Add task management and improve threaded code.

This commit is contained in:
sk89q 2014-08-02 18:14:25 -07:00
parent 32e39f5c7c
commit 471b4496c9
33 changed files with 1344 additions and 1119 deletions

View File

@ -30,6 +30,9 @@
import com.sk89q.minecraft.util.commands.SimpleInjector;
import com.sk89q.minecraft.util.commands.WrappedCommandException;
import com.sk89q.odeum.concurrency.EvenMoreExecutors;
import com.sk89q.odeum.task.SimpleSupervisor;
import com.sk89q.odeum.task.Supervisor;
import com.sk89q.odeum.task.Task;
import com.sk89q.squirrelid.cache.HashMapCache;
import com.sk89q.squirrelid.cache.ProfileCache;
import com.sk89q.squirrelid.cache.SQLiteCache;
@ -49,6 +52,7 @@
import com.sk89q.worldguard.internal.listener.ChestProtectionListener;
import com.sk89q.worldguard.internal.listener.RegionProtectionListener;
import com.sk89q.worldguard.protection.GlobalRegionManager;
import com.sk89q.worldguard.protection.databases.util.UnresolvedNamesException;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.util.FatalConfigurationLoadingException;
import org.bukkit.ChatColor;
@ -64,6 +68,7 @@
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@ -74,6 +79,9 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
@ -88,8 +96,8 @@ public class WorldGuardPlugin extends JavaPlugin {
private final GlobalRegionManager globalRegionManager;
private final ConfigurationManager configuration;
private FlagStateManager flagStateManager;
private final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(
EvenMoreExecutors.newBoundedCachedThreadPool(0, 4, 20));
private final Supervisor supervisor = new SimpleSupervisor();
private ListeningExecutorService executorService;
private ProfileService profileService;
private ProfileCache profileCache;
@ -124,6 +132,7 @@ public static WorldGuardPlugin inst() {
@Override
@SuppressWarnings("deprecation")
public void onEnable() {
executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 20));
// Set the proper command injector
commands.setInjector(new SimpleInjector(this));
@ -226,14 +235,33 @@ public void run() {
@Override
public void onDisable() {
executorService.shutdownNow();
try {
getLogger().log(Level.INFO, "Shutting down executor and waiting for any pending tasks...");
List<Task<?>> tasks = supervisor.getTasks();
if (!tasks.isEmpty()) {
StringBuilder builder = new StringBuilder("Known tasks:");
for (Task<?> task : tasks) {
builder.append("\n");
builder.append(task.getName());
}
getLogger().log(Level.INFO, builder.toString());
}
executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
globalRegionManager.unload();
configuration.unload();
this.getServer().getScheduler().cancelTasks(this);
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label,
String[] args) {
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
try {
commands.execute(cmd.getName(), args, sender, sender);
} catch (CommandPermissionsException e) {
@ -244,12 +272,7 @@ public boolean onCommand(CommandSender sender, Command cmd, String label,
sender.sendMessage(ChatColor.RED + e.getMessage());
sender.sendMessage(ChatColor.RED + e.getUsage());
} catch (WrappedCommandException e) {
if (e.getCause() instanceof NumberFormatException) {
sender.sendMessage(ChatColor.RED + "Number expected, string received instead.");
} else {
sender.sendMessage(ChatColor.RED + "An error has occurred. See console.");
e.printStackTrace();
}
sender.sendMessage(ChatColor.RED + convertThrowable(e.getCause()));
} catch (CommandException e) {
sender.sendMessage(ChatColor.RED + e.getMessage());
}
@ -257,6 +280,31 @@ public boolean onCommand(CommandSender sender, Command cmd, String label,
return true;
}
/**
* Convert the throwable into a somewhat friendly message.
*
* @param throwable the throwable
* @return a message
*/
public String convertThrowable(@Nullable Throwable throwable) {
if (throwable instanceof NumberFormatException) {
return "Number expected, string received instead.";
} else if (throwable instanceof RejectedExecutionException) {
return "There are currently too many tasks queued to add yours. Use /wg running to list queued and running tasks.";
} else 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) {
getLogger().log(Level.WARNING, "WorldGuard encountered an unexpected error", throwable);
return "Unexpected error occurred: " + ((Exception) throwable).getMessage();
} else {
return "Unknown error";
}
}
/**
* Get the GlobalRegionManager.
*
@ -295,6 +343,15 @@ public ConfigurationManager getGlobalStateManager() {
return configuration;
}
/**
* Get the supervisor.
*
* @return the supervisor
*/
public Supervisor getSupervisor() {
return supervisor;
}
/**
* Get the global executor service for internal usage (please use your
* own executor service).

View File

@ -0,0 +1,147 @@
/*
* 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.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.odeum.task.FutureForwardingTask;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.managers.RegionManager;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
class AsyncCommandHelper {
private final ListenableFuture<?> future;
private final WorldGuardPlugin plugin;
private final CommandSender sender;
@Nullable
private Object[] formatArgs;
private AsyncCommandHelper(ListenableFuture<?> future, WorldGuardPlugin plugin, CommandSender sender) {
checkNotNull(future);
checkNotNull(plugin);
checkNotNull(sender);
this.future = future;
this.plugin = plugin;
this.sender = sender;
}
public AsyncCommandHelper formatUsing(Object... args) {
this.formatArgs = args;
return this;
}
private String format(String message) {
if (formatArgs != null) {
return String.format(message, formatArgs);
} else {
return message;
}
}
public AsyncCommandHelper registerWithSupervisor(String description) {
plugin.getSupervisor().monitor(
FutureForwardingTask.create(
future, format(description), sender));
return this;
}
public AsyncCommandHelper sendMessageAfterDelay(String message) {
FutureProgressListener.addProgressListener(future, sender, format(message));
return this;
}
public AsyncCommandHelper thenRespondWith(String success, String failure) {
// Send a response message
Futures.addCallback(
future,
new MessageFutureCallback.Builder(plugin, sender)
.onSuccess(format(success))
.onFailure(format(failure))
.build());
return this;
}
public AsyncCommandHelper thenTellErrorsOnly(String failure) {
// Send a response message
Futures.addCallback(
future,
new MessageFutureCallback.Builder(plugin, sender)
.onFailure(format(failure))
.build());
return this;
}
public AsyncCommandHelper forRegionDataLoad(World world, boolean silent) {
checkNotNull(world);
formatUsing(world.getName());
registerWithSupervisor("Loading region data for '%s'");
if (silent) {
thenTellErrorsOnly("Failed to load regions '%s'");
} else {
sendMessageAfterDelay("(Please wait... loading the region data for '%s')");
thenRespondWith(
"Loaded region data for '%s'",
"Failed to load regions '%s'");
}
return this;
}
public AsyncCommandHelper forRegionDataSave(World world, boolean silent) {
checkNotNull(world);
formatUsing(world.getName());
registerWithSupervisor("Saving region data for '%s'");
if (silent) {
thenTellErrorsOnly("Failed to save regions '%s'");
} else {
sendMessageAfterDelay("(Please wait... saving the region data for '%s')");
thenRespondWith(
"Saved region data for '%s'",
"Failed to load regions '%s'");
}
return this;
}
public AsyncCommandHelper thenSaveRegionData(RegionManager manager, World world) {
checkNotNull(manager);
checkNotNull(world);
ListenableFuture<?> future = manager.save(true);
AsyncCommandHelper.wrap(future, plugin, sender).forRegionDataSave(world, true);
return this;
}
public static AsyncCommandHelper wrap(ListenableFuture<?> future, WorldGuardPlugin plugin, CommandSender sender) {
return new AsyncCommandHelper(future, plugin, sender);
}
}

View File

@ -20,85 +20,39 @@
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.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
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.
* Get the name of the given owner object.
*
* @param future the future
* @param sender the sender to send a message to
* @param message the message to send (will be grey)
* @param owner the owner object
* @return a name
*/
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));
}
};
static String getOwnerName(@Nullable Object owner) {
if (owner == null) {
return "?";
} else if (owner instanceof Player) {
return ((Player) owner).getName();
} else if (owner instanceof ConsoleCommandSender) {
return "*CONSOLE*";
} else if (owner instanceof BlockCommandSender) {
return ((BlockCommandSender) owner).getBlock().getLocation().toString();
} else {
return "?";
}
}
/**
@ -118,71 +72,4 @@ public Object apply(@Nullable String s) {
};
}
/**
* 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,55 @@
/*
* 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.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import java.util.Timer;
import static com.google.common.base.Preconditions.checkNotNull;
class FutureProgressListener implements Runnable {
private static final Timer timer = new Timer();
private static final int MESSAGE_DELAY = 1000;
private final MessageTimerTask task;
FutureProgressListener(CommandSender sender, String message) {
checkNotNull(sender);
checkNotNull(message);
task = new MessageTimerTask(sender, ChatColor.GRAY + message);
timer.schedule(task, MESSAGE_DELAY);
}
@Override
public void run() {
task.cancel();
}
public static void addProgressListener(ListenableFuture<?> future, CommandSender sender, String message) {
future.addListener(new FutureProgressListener(sender, message), MoreExecutors.sameThreadExecutor());
}
}

View File

@ -0,0 +1,103 @@
/*
* 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.util.concurrent.FutureCallback;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
class MessageFutureCallback<V> implements FutureCallback<V> {
private final WorldGuardPlugin plugin;
private final CommandSender sender;
@Nullable
private final String success;
@Nullable
private final String failure;
private MessageFutureCallback(WorldGuardPlugin plugin, CommandSender sender, @Nullable String success, @Nullable String failure) {
this.plugin = plugin;
this.sender = sender;
this.success = success;
this.failure = failure;
}
@Override
public void onSuccess(@Nullable V v) {
if (success != null) {
sender.sendMessage(ChatColor.YELLOW + success);
}
}
@Override
public void onFailure(@Nullable Throwable throwable) {
String failure = this.failure != null ? this.failure : "An error occurred";
sender.sendMessage(ChatColor.RED + failure + ": " + plugin.convertThrowable(throwable));
}
static class Builder {
private final WorldGuardPlugin plugin;
private final CommandSender sender;
@Nullable
private String success;
@Nullable
private String failure;
Builder(WorldGuardPlugin plugin, CommandSender sender) {
checkNotNull(plugin);
checkNotNull(sender);
this.plugin = plugin;
this.sender = sender;
}
public Builder onSuccess(@Nullable String message) {
this.success = message;
return this;
}
public Builder onFailure(@Nullable String message) {
this.failure = message;
return this;
}
public <V> MessageFutureCallback<V> build() {
return new MessageFutureCallback<V>(plugin, sender, success, failure);
}
}
public static <V> MessageFutureCallback<V> createRegionLoadCallback(WorldGuardPlugin plugin, CommandSender sender) {
return new Builder(plugin, sender)
.onSuccess("Successfully load the region data.")
.build();
}
public static <V> MessageFutureCallback<V> createRegionSaveCallback(WorldGuardPlugin plugin, CommandSender sender) {
return new Builder(plugin, sender)
.onSuccess("Successfully saved the region data.")
.build();
}
}

View File

@ -19,7 +19,6 @@
package com.sk89q.worldguard.bukkit.commands;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.minecraft.util.commands.Command;
@ -40,9 +39,9 @@
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.databases.RegionDBUtil;
import com.sk89q.worldguard.protection.databases.migrators.AbstractDatabaseMigrator;
import com.sk89q.worldguard.protection.databases.migrators.MigrationException;
import com.sk89q.worldguard.protection.databases.migrators.MigratorKey;
import com.sk89q.worldguard.protection.databases.migrator.AbstractDatabaseMigrator;
import com.sk89q.worldguard.protection.databases.migrator.MigrationException;
import com.sk89q.worldguard.protection.databases.migrator.MigratorKey;
import com.sk89q.worldguard.protection.flags.DefaultFlag;
import com.sk89q.worldguard.protection.flags.Flag;
import com.sk89q.worldguard.protection.flags.InvalidFlagFormat;
@ -54,6 +53,7 @@
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
@ -65,8 +65,6 @@
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.RejectedExecutionException;
import java.util.logging.Level;
/**
* Implements the /region commands for WorldGuard.
@ -91,7 +89,18 @@ public RegionCommands(WorldGuardPlugin plugin) {
private static RegionPermissionModel getPermissionModel(CommandSender sender) {
return new RegionPermissionModel(WorldGuardPlugin.inst(), sender);
}
/**
* Save the regions asynchronously and alert the sender if any errors
* occur during save.
*
* @param manager the manager to save
* @param sender the sender
*/
private void saveRegionsSilently(RegionManager manager, World world, CommandSender sender) {
AsyncCommandHelper.wrap(Futures.immediateFuture(null), plugin, sender).thenSaveRegionData(manager, world);
}
/**
* Gets the world from the given flag, or falling back to the the current player
* if the sender is a player, otherwise reporting an error.
@ -102,8 +111,7 @@ private static RegionPermissionModel getPermissionModel(CommandSender sender) {
* @return a world
* @throws CommandException on error
*/
private static World getWorld(CommandContext args, CommandSender sender, char flag)
throws CommandException {
private static World getWorld(CommandContext args, CommandSender sender, char flag) throws CommandException {
if (args.hasFlag(flag)) {
return WorldGuardPlugin.inst().matchWorld(sender, args.getFlag(flag));
} else {
@ -150,9 +158,7 @@ private static String validateRegionId(String id, boolean allowGlobal)
* @param allowGlobal true to allow selecting __global__
* @throws CommandException thrown if no region is found by the given name
*/
private static ProtectedRegion findExistingRegion(
RegionManager regionManager, String id, boolean allowGlobal)
throws CommandException {
private static ProtectedRegion findExistingRegion(RegionManager regionManager, String id, boolean allowGlobal) throws CommandException {
// Validate the id
validateRegionId(id, allowGlobal);
@ -186,8 +192,7 @@ private static ProtectedRegion findExistingRegion(
* @return a region
* @throws CommandException thrown if no region was found
*/
private static ProtectedRegion findRegionStandingIn(
RegionManager regionManager, Player player) throws CommandException {
private static ProtectedRegion findRegionStandingIn(RegionManager regionManager, Player player) throws CommandException {
return findRegionStandingIn(regionManager, player, false);
}
@ -206,8 +211,7 @@ private static ProtectedRegion findRegionStandingIn(
* @return a region
* @throws CommandException thrown if no region was found
*/
private static ProtectedRegion findRegionStandingIn(
RegionManager regionManager, Player player, boolean allowGlobal) throws CommandException {
private static ProtectedRegion findRegionStandingIn(RegionManager regionManager, Player player, boolean allowGlobal) throws CommandException {
ApplicableRegionSet set = regionManager.getApplicableRegions(
player.getLocation());
@ -272,9 +276,7 @@ private static Selection getSelection(Player player) throws CommandException {
* @return a new region
* @throws CommandException thrown on an error
*/
private static ProtectedRegion createRegionFromSelection(Player player, String id)
throws CommandException {
private static ProtectedRegion createRegionFromSelection(Player player, String id) throws CommandException {
Selection selection = getSelection(player);
// Detect the type of region from WorldEdit
@ -293,84 +295,6 @@ private static ProtectedRegion createRegionFromSelection(Player player, String i
}
}
/**
* Save the region database.
*
* @param sender the sender
* @param regionManager the region manager
* @param silent whether to suppress messages sent to the player
* @throws CommandException throw on an error
*/
private ListenableFuture<?> commitChanges(final CommandSender sender, RegionManager regionManager, final World world, final boolean silent) throws CommandException {
ListenableFuture<?> future;
try {
future = regionManager.save(true);
} catch (RejectedExecutionException e) {
throw new CommandException("There are too many interleaved load and save tasks currently queued. Please try again later.");
}
if (!silent) {
sender.sendMessage(ChatColor.GRAY + "(A save of the region data has been queued.)");
}
Futures.addCallback(future, new FutureCallback<Object>() {
@Override
public void onSuccess(Object o) {
if (!silent) {
sender.sendMessage(ChatColor.YELLOW + "Successfully saved the region data for the '" + world.getName() + "' world.");
}
}
@Override
public void onFailure(Throwable throwable) {
sender.sendMessage(ChatColor.RED + "Failed to save region data for the '" + world.getName() + "' world: " + throwable.getMessage());
plugin.getLogger().log(Level.WARNING, "Failed to save region data", throwable);
}
});
return future;
}
/**
* Load the region database.
*
* @param sender the sender
* @param regionManager the region manager
* @param silent whether to suppress messages sent to the player
* @throws CommandException throw on an error
*/
private ListenableFuture<?> reloadChanges(final CommandSender sender, RegionManager regionManager, final World world, final boolean silent) throws CommandException {
ListenableFuture<?> future;
try {
future = regionManager.load(true);
} catch (RejectedExecutionException e) {
throw new CommandException("There are too many interleaved load and save tasks currently queued. Please try again later.");
}
if (!silent) {
sender.sendMessage(ChatColor.GRAY + "(A load of the region data has been queued.)");
}
Futures.addCallback(future, new FutureCallback<Object>() {
@Override
public void onSuccess(Object o) {
if (!silent) {
sender.sendMessage(ChatColor.YELLOW + "Successfully loaded the region data for the '" + world.getName() + "' world.");
}
}
@Override
public void onFailure(Throwable throwable) {
sender.sendMessage(ChatColor.RED + "Failed to load region data for the '" + world.getName() + "' world: " + throwable.getMessage());
plugin.getLogger().log(Level.WARNING, "Failed to load region data", throwable);
}
});
return future;
}
/**
* Set a player's selection to a given region.
*
@ -422,9 +346,7 @@ private static void setPlayerSelection(Player player, ProtectedRegion region)
* @param value the value
* @throws InvalidFlagFormat thrown if the value is invalid
*/
private static <V> void setFlag(ProtectedRegion region,
Flag<V> flag, CommandSender sender, String value)
throws InvalidFlagFormat {
private static <V> void setFlag(ProtectedRegion region, Flag<V> flag, CommandSender sender, String value) throws InvalidFlagFormat {
region.setFlag(flag, flag.parseInput(WorldGuardPlugin.inst(), sender, value));
}
@ -439,8 +361,7 @@ private static <V> void setFlag(ProtectedRegion region,
usage = "<id> [<owner1> [<owner2> [<owners...>]]]",
desc = "Defines a region",
min = 1)
public void define(CommandContext args, CommandSender sender)
throws CommandException {
public void define(CommandContext args, CommandSender sender) throws CommandException {
Player player = plugin.checkPlayer(sender);
// Check permissions
@ -466,9 +387,6 @@ public void define(CommandContext args, CommandSender sender)
if (args.argsLength() > 1) {
region.setOwners(RegionDBUtil.parseDomainString(args.getSlice(1), 1));
}
regionManager.addRegion(region);
commitChanges(sender, regionManager, player.getWorld(), true); // Save to disk
// Issue a warning about height
int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY();
@ -488,6 +406,12 @@ public void define(CommandContext args, CommandSender sender)
// Tell the user
sender.sendMessage(ChatColor.YELLOW + "A new region has been made named '" + id + "'.");
// Add region
regionManager.addRegion(region);
// Save regions
saveRegionsSilently(regionManager, player.getWorld(), sender);
}
/**
@ -501,8 +425,7 @@ public void define(CommandContext args, CommandSender sender)
usage = "<id>",
desc = "Re-defines the shape of a region",
min = 1, max = 1)
public void redefine(CommandContext args, CommandSender sender)
throws CommandException {
public void redefine(CommandContext args, CommandSender sender) throws CommandException {
Player player = plugin.checkPlayer(sender);
World world = player.getWorld();
@ -532,9 +455,6 @@ public void redefine(CommandContext args, CommandSender sender)
// This should not be thrown
}
regionManager.addRegion(region); // Replace region
commitChanges(sender, regionManager, player.getWorld(), true); // Save to disk
// Issue a warning about height
int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY();
if (height <= 2) {
@ -543,6 +463,11 @@ public void redefine(CommandContext args, CommandSender sender)
}
sender.sendMessage(ChatColor.YELLOW + "Region '" + id + "' updated with new area.");
regionManager.addRegion(region); // Replace region
// Save regions
saveRegionsSilently(regionManager, world, sender);
}
/**
@ -573,8 +498,8 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep
String id = validateRegionId(args.getString(0), false);
// Can't replace existing regions
RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld());
if (mgr.hasRegion(id)) {
RegionManager regionManager = plugin.getGlobalRegionManager().get(player.getWorld());
if (regionManager.hasRegion(id)) {
throw new CommandException(
"That region already exists. Please choose a different name.");
}
@ -593,13 +518,13 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep
if (!permModel.mayClaimRegionsUnbounded()) {
int maxRegionCount = wcfg.getMaxRegionCount(player);
if (maxRegionCount >= 0
&& mgr.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) {
&& regionManager.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) {
throw new CommandException(
"You own too many regions, delete one first to claim a new one.");
}
}
ProtectedRegion existing = mgr.getRegionExact(id);
ProtectedRegion existing = regionManager.getRegionExact(id);
// Check for an existing region
if (existing != null) {
@ -610,7 +535,7 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep
}
// We have to check whether this region violates the space of any other reion
ApplicableRegionSet regions = mgr.getApplicableRegions(region);
ApplicableRegionSet regions = regionManager.getApplicableRegions(region);
// Check if this region overlaps any other region
if (regions.size() > 0) {
@ -634,34 +559,15 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep
}
}
/*if (plugin.getGlobalConfiguration().getiConomy() != null && wcfg.useiConomy && wcfg.buyOnClaim) {
if (iConomy.getBank().hasAccount(player.getName())) {
Account account = iConomy.getBank().getAccount(player.getName());
double balance = account.getBalance();
double regionCosts = region.countBlocks() * wcfg.buyOnClaimPrice;
if (balance >= regionCosts) {
account.subtract(regionCosts);
player.sendMessage(ChatColor.YELLOW + "You have bought that region for "
+ iConomy.getBank().format(regionCosts));
account.save();
} else {
player.sendMessage(ChatColor.RED + "You have not enough money.");
player.sendMessage(ChatColor.RED + "The region you want to claim costs "
+ iConomy.getBank().format(regionCosts));
player.sendMessage(ChatColor.RED + "You have " + iConomy.getBank().format(balance));
return;
}
} else {
player.sendMessage(ChatColor.YELLOW + "You have not enough money.");
return;
}
}*/
region.getOwners().addPlayer(player.getName());
mgr.addRegion(region);
commitChanges(sender, mgr, player.getWorld(), true); // Save to disk
sender.sendMessage(ChatColor.YELLOW + "Region '" + id + "' updated with new area.");
// Replace region
regionManager.addRegion(region);
// Save regions
saveRegionsSilently(regionManager, player.getWorld(), sender);
}
/**
@ -733,22 +639,31 @@ public void info(CommandContext args, CommandSender sender) throws CommandExcept
throw new CommandPermissionsException();
}
// Print region information
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
// Let the player select the region
if (args.hasFlag('s')) {
// Check permissions
if (!permModel.maySelect(existing)) {
throw new CommandPermissionsException();
}
setPlayerSelection(plugin.checkPlayer(sender), existing);
}
// Print region information
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, args.hasFlag('u') ? null : plugin.getProfileCache());
ListenableFuture<?> future = Futures.transform(
plugin.getExecutorService().submit(printout),
CommandUtils.messageFunction(sender));
// If it takes too long...
FutureProgressListener.addProgressListener(
future, sender, "(Please wait... fetching region information...)");
// Send a response message
Futures.addCallback(future,
new MessageFutureCallback.Builder(plugin, sender)
.onFailure("Failed to fetch region information")
.build());
}
/**
@ -963,8 +878,6 @@ 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, null);
printout.append(ChatColor.GRAY);
@ -972,6 +885,9 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept
printout.appendFlagsList(false);
printout.append(")");
printout.send(sender);
// Save regions
saveRegionsSilently(regionManager, world, sender);
}
/**
@ -1002,11 +918,13 @@ public void setPriority(CommandContext args, CommandSender sender)
}
existing.setPriority(priority);
commitChanges(sender, regionManager, world, true); // Save to disk
sender.sendMessage(ChatColor.YELLOW
+ "Priority of '" + existing.getId() + "' set to "
+ priority + " (higher numbers override).");
// Save regions
saveRegionsSilently(regionManager, world, sender);
}
/**
@ -1058,8 +976,6 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE
printout.send(sender);
return;
}
commitChanges(sender, regionManager, world, true); // Save to disk
// Tell the user the current inheritance
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(child, null);
@ -1073,6 +989,9 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE
printout.append(")");
}
printout.send(sender);
// Save regions
saveRegionsSilently(regionManager, world, sender);
}
/**
@ -1101,10 +1020,11 @@ public void remove(CommandContext args, CommandSender sender) throws CommandExce
}
regionManager.removeRegion(existing.getId());
commitChanges(sender, regionManager, world, true); // Save to disk
sender.sendMessage(ChatColor.YELLOW
+ "Region '" + existing.getId() + "' removed.");
sender.sendMessage(ChatColor.YELLOW + "Region '" + existing.getId() + "' removed.");
// Save regions
saveRegionsSilently(regionManager, world, sender);
}
/**
@ -1132,33 +1052,33 @@ public void load(CommandContext args, final CommandSender sender) throws Command
}
if (world != null) {
RegionManager regionManager = plugin.getGlobalRegionManager().get(world);
if (regionManager == null) {
RegionManager manager = plugin.getGlobalRegionManager().get(world);
if (manager == null) {
throw new CommandException("No region manager exists for world '" + world.getName() + "'.");
}
reloadChanges(sender, regionManager, world, false);
ListenableFuture<?> future = manager.load(true);
AsyncCommandHelper.wrap(future, plugin, sender)
.forRegionDataLoad(world, false);
} else {
// Load regions for all worlds
List<ListenableFuture<?>> futures = new ArrayList<ListenableFuture<?>>();
for (World w : plugin.getServer().getWorlds()) {
RegionManager regionManager = plugin.getGlobalRegionManager().get(w);
if (regionManager == null) {
continue;
for (World w : Bukkit.getServer().getWorlds()) {
RegionManager manager = plugin.getGlobalRegionManager().get(w);
if (manager != null) {
futures.add(manager.load(true));
}
futures.add(reloadChanges(sender, regionManager, world, true));
}
ListenableFuture<?> future = Futures.successfulAsList(futures);
Futures.addCallback(Futures.allAsList(futures), new FutureCallback<Object>() {
@Override
public void onSuccess(Object o) {
sender.sendMessage(ChatColor.YELLOW + "Successfully loaded region data for all worlds.");
}
@Override
public void onFailure(Throwable throwable) {
sender.sendMessage(ChatColor.RED + "Failed to load region data for all worlds: " + throwable.getMessage());
plugin.getLogger().log(Level.WARNING, "Failed to load region data", throwable);
}
});
AsyncCommandHelper.wrap(future, plugin, sender)
.registerWithSupervisor("Loading regions for all worlds")
.sendMessageAfterDelay("(Please wait... loading region data for all worlds...)")
.thenRespondWith(
"Successfully load the region data for all worlds.",
"Failed to load regions for all worlds");
}
}
@ -1187,34 +1107,33 @@ public void save(CommandContext args, final CommandSender sender) throws Command
}
if (world != null) {
RegionManager regionManager = plugin.getGlobalRegionManager().get(world);
if (regionManager == null) {
RegionManager manager = plugin.getGlobalRegionManager().get(world);
if (manager == null) {
throw new CommandException("No region manager exists for world '" + world.getName() + "'.");
}
commitChanges(sender, regionManager, world, false);
ListenableFuture<?> future = manager.save(true);
AsyncCommandHelper.wrap(future, plugin, sender)
.forRegionDataSave(world, false);
} else {
sender.sendMessage(ChatColor.YELLOW + "Saving all region databases... This might take a bit.");
// Save for all worlds
List<ListenableFuture<?>> futures = new ArrayList<ListenableFuture<?>>();
for (World w : plugin.getServer().getWorlds()) {
RegionManager regionManager = plugin.getGlobalRegionManager().get(w);
if (regionManager == null) {
continue;
for (World w : Bukkit.getServer().getWorlds()) {
RegionManager manager = plugin.getGlobalRegionManager().get(w);
if (manager != null) {
futures.add(manager.save(true));
}
futures.add(commitChanges(sender, regionManager, world, true));
}
ListenableFuture<?> future = Futures.successfulAsList(futures);
Futures.addCallback(Futures.allAsList(futures), new FutureCallback<Object>() {
@Override
public void onSuccess(Object o) {
sender.sendMessage(ChatColor.YELLOW + "Successfully saved region data for all worlds.");
}
@Override
public void onFailure(Throwable throwable) {
sender.sendMessage(ChatColor.RED + "Failed to save region data for all worlds: " + throwable.getMessage());
plugin.getLogger().log(Level.WARNING, "Failed to save region data", throwable);
}
});
AsyncCommandHelper.wrap(future, plugin, sender)
.registerWithSupervisor("Saving regions for all worlds")
.sendMessageAfterDelay("(Please wait... saving region data for all worlds...)")
.thenRespondWith(
"Successfully saved the region data for all worlds.",
"Failed to save regions for all worlds");
}
}

View File

@ -36,9 +36,6 @@
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 {
@ -74,8 +71,8 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE
String id = args.getString(0);
RegionManager mgr = plugin.getGlobalRegionManager().get(world);
ProtectedRegion region = mgr.getRegion(id);
RegionManager manager = plugin.getGlobalRegionManager().get(world);
ProtectedRegion region = manager.getRegion(id);
if (region == null) {
throw new CommandException("Could not find a region by that ID.");
@ -103,9 +100,12 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE
plugin.getExecutorService().submit(resolver),
resolver.createAddAllFunction(region.getMembers()));
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));
AsyncCommandHelper.wrap(future, plugin, sender)
.formatUsing(region.getId(), world.getName())
.registerWithSupervisor("Adding members to the region '%s' on '%s'")
.sendMessageAfterDelay("(Please wait... querying player names...)")
.thenRespondWith("Region '%s' updated with new members.", "Failed to add new members")
.thenSaveRegionData(manager, world);
}
@Command(aliases = {"addowner", "addowner", "ao"},
@ -133,8 +133,8 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx
String id = args.getString(0);
RegionManager mgr = plugin.getGlobalRegionManager().get(world);
ProtectedRegion region = mgr.getRegion(id);
RegionManager manager = plugin.getGlobalRegionManager().get(world);
ProtectedRegion region = manager.getRegion(id);
if (region == null) {
throw new CommandException("Could not find a region by that ID.");
@ -148,7 +148,7 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx
if (flag != null && flag && owners != null && owners.size() == 0) {
if (!plugin.hasPermission(player, "worldguard.region.unlimited")) {
int maxRegionCount = plugin.getGlobalStateManager().get(world).getMaxRegionCount(player);
if (maxRegionCount >= 0 && mgr.getRegionCountOfPlayer(localPlayer)
if (maxRegionCount >= 0 && manager.getRegionCountOfPlayer(localPlayer)
>= maxRegionCount) {
throw new CommandException("You already own the maximum allowed amount of regions.");
}
@ -175,9 +175,12 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx
plugin.getExecutorService().submit(resolver),
resolver.createAddAllFunction(region.getOwners()));
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));
AsyncCommandHelper.wrap(future, plugin, sender)
.formatUsing(region.getId(), world.getName())
.registerWithSupervisor("Adding owners to the region '%s' on '%s'")
.sendMessageAfterDelay("(Please wait... querying player names...)")
.thenRespondWith("Region '%s' updated with new owners.", "Failed to add new owners")
.thenSaveRegionData(manager, world);
}
@Command(aliases = {"removemember", "remmember", "removemem", "remmem", "rm"},
@ -205,8 +208,8 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma
String id = args.getString(0);
RegionManager mgr = plugin.getGlobalRegionManager().get(world);
ProtectedRegion region = mgr.getRegion(id);
RegionManager manager = plugin.getGlobalRegionManager().get(world);
ProtectedRegion region = manager.getRegion(id);
if (region == null) {
throw new CommandException("Could not find a region by that ID.");
@ -246,9 +249,12 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma
resolver.createRemoveAllFunction(region.getMembers()));
}
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));
AsyncCommandHelper.wrap(future, plugin, sender)
.formatUsing(region.getId(), world.getName())
.registerWithSupervisor("Removing members from the region '%s' on '%s'")
.sendMessageAfterDelay("(Please wait... querying player names...)")
.thenRespondWith("Region '%s' updated with members removed.", "Failed to remove members")
.thenSaveRegionData(manager, world);
}
@Command(aliases = {"removeowner", "remowner", "ro"},
@ -277,8 +283,8 @@ public void removeOwner(CommandContext args,
String id = args.getString(0);
RegionManager mgr = plugin.getGlobalRegionManager().get(world);
ProtectedRegion region = mgr.getRegion(id);
RegionManager manager = plugin.getGlobalRegionManager().get(world);
ProtectedRegion region = manager.getRegion(id);
if (region == null) {
throw new CommandException("Could not find a region by that ID.");
@ -318,8 +324,11 @@ public void removeOwner(CommandContext args,
resolver.createRemoveAllFunction(region.getOwners()));
}
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));
AsyncCommandHelper.wrap(future, plugin, sender)
.formatUsing(region.getId(), world.getName())
.registerWithSupervisor("Removing owners from the region '%s' on '%s'")
.sendMessageAfterDelay("(Please wait... querying player names...)")
.thenRespondWith("Region '%s' updated with owners removed.", "Failed to remove owners")
.thenSaveRegionData(manager, world);
}
}

View File

@ -19,24 +19,27 @@
package com.sk89q.worldguard.bukkit.commands;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.odeum.task.Task;
import com.sk89q.odeum.task.TaskStateComparator;
import com.sk89q.worldguard.bukkit.LoggerToChatHandler;
import com.sk89q.worldguard.bukkit.ReportWriter;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.util.PastebinPoster;
import com.sk89q.worldguard.util.PastebinPoster.PasteCallback;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class WorldGuardCommands {
private final WorldGuardPlugin plugin;
@ -56,6 +59,11 @@ public void version(CommandContext args, CommandSender sender) throws CommandExc
@Command(aliases = {"reload"}, desc = "Reload WorldGuard configuration", max = 0)
@CommandPermissions({"worldguard.reload"})
public void reload(CommandContext args, CommandSender sender) throws CommandException {
// TODO: This is subject to a race condition, but at least other commands are not being processed concurrently
List<Task<?>> tasks = plugin.getSupervisor().getTasks();
if (!tasks.isEmpty()) {
throw new CommandException("There are currently pending tasks. Use /wg running and /wg cancelall to monitor and cancel these tasks first.");
}
LoggerToChatHandler handler = null;
Logger minecraftLogger = null;
@ -135,4 +143,52 @@ public void flushStates(CommandContext args, CommandSender sender) throws Comman
}
}
@Command(aliases = {"cancelall"}, flags = "f", desc = "Cancel all running tasks", max = 0)
@CommandPermissions("worldguard.cancel.all")
public void cancelAllTasks(CommandContext args, CommandSender sender) throws CommandException {
List<Task<?>> tasks = plugin.getSupervisor().getTasks();
boolean force = args.hasFlag('f');
if (force && !plugin.hasPermission(sender, "worldguard.cancel.force")) {
throw new CommandException("You are not permitted to force cancel.");
}
if (!tasks.isEmpty()) {
for (Task<?> task : tasks) {
task.cancel(force);
sender.sendMessage(ChatColor.YELLOW + "Cancelled: " + ChatColor.WHITE + task.getName());
}
} else {
sender.sendMessage(ChatColor.YELLOW + "There are currently no running tasks.");
}
}
@Command(aliases = {"running", "queue"}, desc = "List running tasks", max = 0)
@CommandPermissions("worldguard.running")
public void listRunningTasks(CommandContext args, CommandSender sender) throws CommandException {
List<Task<?>> tasks = plugin.getSupervisor().getTasks();
if (!tasks.isEmpty()) {
Collections.sort(tasks, new TaskStateComparator());
StringBuilder builder = new StringBuilder();
builder.append(ChatColor.GRAY);
builder.append("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
builder.append(" Running tasks ");
builder.append("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
builder.append("\n").append(ChatColor.GRAY).append("Note: Some 'running' tasks may be waiting to be start.");
for (Task task : tasks) {
builder.append("\n");
builder.append(ChatColor.BLUE).append("(").append(task.getState().name()).append(") ");
builder.append(ChatColor.YELLOW);
builder.append(CommandUtils.getOwnerName(task.getOwner()));
builder.append(": ");
builder.append(ChatColor.WHITE);
builder.append(task.getName());
}
sender.sendMessage(builder.toString());
} else {
sender.sendMessage(ChatColor.YELLOW + "There are currently no running tasks.");
}
}
}

View File

@ -1,181 +0,0 @@
/*
* 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.internal.protection.database.mysql;
import com.google.common.collect.Lists;
import com.sk89q.worldguard.protection.databases.RegionDBUtil;
import com.sk89q.worldguard.util.io.Closer;
import javax.annotation.Nullable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
abstract class UserToIdCache<V> extends AbstractJob {
private static final int MAX_NUMBER_PER_QUERY = 100;
private final Map<V, Integer> cache = new HashMap<V, Integer>();
private final String fieldName;
protected UserToIdCache(MySQLDatabaseImpl database, Connection conn, String fieldName) {
super(database, conn);
this.fieldName = fieldName;
}
protected abstract String fromType(V o);
protected abstract V toType(String o);
protected abstract V toKey(V object);
@Nullable
public Integer find(V object) {
return cache.get(object);
}
public void fetch(Collection<V> entries) throws SQLException {
checkNotNull(entries);
// Get a list of missing entries
List<V> fetchList = new ArrayList<V>();
for (V entry : entries) {
if (!cache.containsKey(toKey(entry))) {
fetchList.add(entry);
}
}
if (fetchList.isEmpty()) {
return; // Nothing to do
}
// Search for entries
for (List<V> partition : Lists.partition(fetchList, MAX_NUMBER_PER_QUERY)) {
Closer closer = Closer.create();
try {
PreparedStatement statement = closer.register(conn.prepareStatement(
String.format(
"SELECT `user`.`id`, `user`.`" + fieldName + "` " +
"FROM `" + config.sqlTablePrefix + "user` AS `user` " +
"WHERE `" + fieldName + "` IN (%s)",
RegionDBUtil.preparePlaceHolders(partition.size()))));
String[] values = new String[partition.size()];
int i = 0;
for (V entry : partition) {
values[i] = fromType(entry);
i++;
}
RegionDBUtil.setValues(statement, values);
ResultSet results = closer.register(statement.executeQuery());
while (results.next()) {
cache.put(toKey(toType(results.getString(fieldName))), results.getInt("id"));
}
} finally {
closer.closeQuietly();
}
}
List<V> missing = new ArrayList<V>();
for (V entry : fetchList) {
if (!cache.containsKey(toKey(entry))) {
missing.add(entry);
}
}
// Insert entries that are missing
if (!missing.isEmpty()) {
Closer closer = Closer.create();
try {
PreparedStatement statement = closer.register(conn.prepareStatement(
"INSERT INTO `" + config.sqlTablePrefix + "user` (`id`, `" + fieldName + "`) VALUES (null, ?)",
Statement.RETURN_GENERATED_KEYS));
for (V entry : missing) {
statement.setString(1, fromType(entry));
statement.execute();
ResultSet generatedKeys = statement.getGeneratedKeys();
if (generatedKeys.first()) {
cache.put(toKey(entry), generatedKeys.getInt(1));
} else {
logger.warning("Could not get the database ID for user " + entry);
}
}
} finally {
closer.closeQuietly();
}
}
}
static class NameToIdCache extends UserToIdCache<String> {
protected NameToIdCache(MySQLDatabaseImpl database, Connection conn) {
super(database, conn, "name");
}
@Override
protected String fromType(String o) {
return o;
}
@Override
protected String toType(String o) {
return o;
}
@Override
protected String toKey(String object) {
return object.toLowerCase();
}
}
static class UUIDToIdCache extends UserToIdCache<UUID> {
protected UUIDToIdCache(MySQLDatabaseImpl database, Connection conn) {
super(database, conn, "uuid");
}
@Override
protected String fromType(UUID o) {
return o.toString();
}
@Override
protected UUID toType(String o) {
return UUID.fromString(o);
}
@Override
protected UUID toKey(UUID object) {
return object;
}
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.internal.util.sql;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class StatementUtils {
/**
* Creates a comma separated list of PreparedStatement place holders
*
* @param length The number of wildcards to create
* @return A string with {@code length} wildcards for usage in a PreparedStatement
*/
public static String preparePlaceHolders(int length) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length;) {
builder.append("?");
if (++i < length) {
builder.append(",");
}
}
return builder.toString();
}
/**
* Adds all of the parsed values to the PreparedStatement
*
* @param preparedStatement The preparedStanement to add to
* @param values The values to set
* @throws SQLException see {@link PreparedStatement#setString(int, String)}
*/
public static void setValues(PreparedStatement preparedStatement, String... values) throws SQLException {
for (int i = 0; i < values.length; i++) {
preparedStatement.setString(i + 1, values[i]);
}
}
}

View File

@ -25,9 +25,9 @@
import com.sk89q.worldguard.bukkit.WorldConfiguration;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.databases.MySQLDatabase;
import com.sk89q.worldguard.protection.databases.YAMLDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
import com.sk89q.worldguard.protection.databases.YAMLDatabase;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.managers.PRTreeRegionManager;
import com.sk89q.worldguard.protection.managers.RegionManager;

View File

@ -19,18 +19,7 @@
package com.sk89q.worldguard.protection.databases;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import au.com.bytecode.opencsv.CSVReader;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldguard.domains.DefaultDomain;
@ -42,11 +31,22 @@
import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException;
import com.sk89q.worldguard.util.ArrayReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Represents a protected area database that uses CSV files.
*
* @author sk89q
* @deprecated no longer maintained - use {@link YAMLDatabase}
*/
@Deprecated
public class CSVDatabase extends AbstractProtectionDatabase {
private static final Map<String, StateFlag> legacyFlagCodes = new HashMap<String, StateFlag>();

View File

@ -1,39 +0,0 @@
/*
* 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;
import com.sk89q.worldguard.util.FatalConfigurationLoadingException;
public class InvalidTableFormatException extends FatalConfigurationLoadingException {
private static final long serialVersionUID = 1L;
protected String updateFile;
public InvalidTableFormatException(String updateFile) {
super();
this.updateFile = updateFile;
}
public String toString() {
return "You need to update your database to the latest version.\n" +
"\t\tPlease see " + this.updateFile;
}
}

View File

@ -19,14 +19,26 @@
package com.sk89q.worldguard.protection.databases;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.sk89q.worldguard.bukkit.ConfigurationManager;
import com.sk89q.worldguard.internal.protection.database.mysql.MySQLDatabaseImpl;
import com.sk89q.worldguard.protection.databases.mysql.MySQLDatabaseImpl;
import java.util.logging.Logger;
/**
* A store that persists regions in a MySQL database.
*/
public class MySQLDatabase extends MySQLDatabaseImpl {
/**
* Create a new instance.
*
* @param executor the executor to perform loads and saves in
* @param config the configuration
* @param worldName the world name
* @param logger a logger
* @throws ProtectionDatabaseException thrown on error
*/
public MySQLDatabase(ConfigurationManager config, String worldName, Logger logger) throws ProtectionDatabaseException {
super(config, worldName, logger);
}

View File

@ -42,7 +42,7 @@ public interface ProtectionDatabase {
/**
* Load the list of regions into a region manager.
*
* <p>This call will block and it must be run from the main server thread.</p>
* <p>This call will block.</p>
*
* @param manager The manager to load regions into
* @throws ProtectionDatabaseException when an error occurs
@ -57,8 +57,6 @@ public interface ProtectionDatabase {
* <p>{@code async} is merely a suggestion and it may be ignored by
* implementations if it is not supported.</p>
*
* <p>This method must be run from the main server thread.</p>
*
* @param manager The manager to load regions into
* @param async true to attempt to save the data asynchronously if it is supported
*/
@ -74,7 +72,7 @@ public interface ProtectionDatabase {
/**
* Save the list of regions from a region manager.
*
* <p>This call will block and it must be run from the main server thread.</p>
* <p>This call will block.</p>
*
* @param manager The manager to load regions into
* @throws ProtectionDatabaseException when an error occurs
@ -89,11 +87,9 @@ public interface ProtectionDatabase {
* <p>{@code async} is merely a suggestion and it may be ignored by
* implementations if it is not supported.</p>
*
* <p>This method must be run from the main server thread.</p>
*
* @param manager The manager to load regions into
* @param async true to attempt to save the data asynchronously if it is supported
* @throws ProtectionDatabaseException when an error occurs
* @throws RejectedExecutionException on rejection
*/
public ListenableFuture<?> save(RegionManager manager, boolean async) throws RejectedExecutionException;

View File

@ -20,18 +20,18 @@
package com.sk89q.worldguard.protection.databases;
import com.sk89q.worldguard.domains.DefaultDomain;
import com.sk89q.worldguard.protection.databases.util.DomainInputResolver;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Various utility functions for parsing region databases.
*
* @author sk89q
*
* @deprecated use {@link DomainInputResolver}
*/
public class RegionDBUtil {
@Deprecated
public final class RegionDBUtil {
private static Pattern groupPattern = Pattern.compile("(?i)^[G]:(.+)$");
@ -45,9 +45,10 @@ private RegionDBUtil() {
* @param domain The domain to add to
* @param split The {@link String[]} containing names to add to {@code domain}
* @param startIndex The beginning index in the array
* @deprecated use {@link DomainInputResolver}
*/
public static void addToDomain(DefaultDomain domain, String[] split,
int startIndex) {
@Deprecated
public static void addToDomain(DefaultDomain domain, String[] split, int startIndex) {
for (int i = startIndex; i < split.length; i++) {
String s = split[i];
Matcher m = groupPattern.matcher(s);
@ -65,9 +66,10 @@ public static void addToDomain(DefaultDomain domain, String[] split,
* @param domain The domain to remove from
* @param split The {@link String[]} containing names to remove from {@code domain}
* @param startIndex The beginning index in the array
* @deprecated use {@link DomainInputResolver}
*/
public static void removeFromDomain(DefaultDomain domain, String[] split,
int startIndex) {
@Deprecated
public static void removeFromDomain(DefaultDomain domain, String[] split, int startIndex) {
for (int i = startIndex; i < split.length; i++) {
String s = split[i];
Matcher m = groupPattern.matcher(s);
@ -85,7 +87,9 @@ public static void removeFromDomain(DefaultDomain domain, String[] split,
* @param split The array of names to add
* @param startIndex The beginning index in the array
* @return The resulting DefaultDomain
* @deprecated use {@link DomainInputResolver}
*/
@Deprecated
public static DefaultDomain parseDomainString(String[] split, int startIndex) {
DefaultDomain domain = new DefaultDomain();
@ -101,34 +105,5 @@ public static DefaultDomain parseDomainString(String[] split, int startIndex) {
return domain;
}
/**
* Creates a comma separated list of PreparedStatement place holders
*
* @param length The number of wildcards to create
* @return A string with {@code length} wildcards for usage in a PreparedStatement
*/
public static String preparePlaceHolders(int length) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length;) {
builder.append("?");
if (++i < length) {
builder.append(",");
}
}
return builder.toString();
}
/**
* Adds all of the parsed values to the PreparedStatement
*
* @param preparedStatement The preparedStanement to add to
* @param values The values to set
* @throws SQLException see {@link PreparedStatement#setString(int, String)}
*/
public static void setValues(PreparedStatement preparedStatement, String... values) throws SQLException {
for (int i = 0; i < values.length; i++) {
preparedStatement.setString(i + 1, values[i]);
}
}
}

View File

@ -52,6 +52,9 @@
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A store that persists regions in a YAML-encoded file.
*/
public class YAMLDatabase extends AbstractAsynchronousDatabase {
/**
@ -62,7 +65,15 @@ public class YAMLDatabase extends AbstractAsynchronousDatabase {
private Map<String, ProtectedRegion> regions;
private final File file;
private final Logger logger;
/**
* Create a new instance.
*
* @param file the file
* @param logger a logger
* @throws ProtectionDatabaseException
* @throws FileNotFoundException
*/
public YAMLDatabase(File file, Logger logger) throws ProtectionDatabaseException, FileNotFoundException {
this.logger = logger;
this.file = file;
@ -89,9 +100,9 @@ public void performLoad() throws ProtectionDatabaseException {
} catch (IOException e) {
throw new ProtectionDatabaseException(e);
}
Map<String, YAMLNode> regionData = config.getNodes("regions");
// No regions are even configured
if (regionData == null) {
this.regions = new HashMap<String, ProtectedRegion>();
@ -108,14 +119,14 @@ public void performLoad() throws ProtectionDatabaseException {
Map<String,ProtectedRegion> regions = new HashMap<String,ProtectedRegion>();
Map<ProtectedRegion,String> parentSets = new LinkedHashMap<ProtectedRegion, String>();
for (Map.Entry<String, YAMLNode> entry : regionData.entrySet()) {
String id = entry.getKey().toLowerCase().replace(".", "");
YAMLNode node = entry.getValue();
String type = node.getString("type");
ProtectedRegion region;
try {
if (type == null) {
logger.warning("Undefined region type for region '" + id + "'!\n" +
@ -139,14 +150,14 @@ public void performLoad() throws ProtectionDatabaseException {
"Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) + "\n");
continue;
}
Integer priority = checkNonNull(node.getInt("priority"));
region.setPriority(priority);
setFlags(region, node.getNode("flags"));
region.setOwners(parseDomain(node.getNode("owners")));
region.setMembers(parseDomain(node.getNode("members")));
regions.put(id, region);
String parentId = node.getString("parent");
if (parentId != null) {
parentSets.put(region, parentId);
@ -154,11 +165,11 @@ public void performLoad() throws ProtectionDatabaseException {
} catch (NullPointerException e) {
logger.log(Level.WARNING,
"Unexpected NullPointerException encountered during parsing for the region '" + id + "'!\n" +
"Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) +
"\n\nNote: This region will disappear as a result!", e);
"Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) +
"\n\nNote: This region will disappear as a result!", e);
}
}
// Relink parents
for (Map.Entry<ProtectedRegion, String> entry : parentSets.entrySet()) {
ProtectedRegion parent = regions.get(entry.getValue());
@ -172,23 +183,23 @@ public void performLoad() throws ProtectionDatabaseException {
logger.warning("Unknown region parent: " + entry.getValue());
}
}
this.regions = regions;
}
private <V> V checkNonNull(V val) throws NullPointerException {
if (val == null) {
throw new NullPointerException();
}
return val;
}
private void setFlags(ProtectedRegion region, YAMLNode flagsData) {
if (flagsData == null) {
return;
}
// @TODO: Make this better
for (Flag<?> flag : DefaultFlag.getFlags()) {
if (flag == null) {
@ -201,16 +212,16 @@ private void setFlags(ProtectedRegion region, YAMLNode flagsData) {
if (o != null) {
setFlag(region, flag, o);
}
if (flag.getRegionGroupFlag() != null) {
Object o2 = flagsData.getProperty(flag.getRegionGroupFlag().getName());
Object o2 = flagsData.getProperty(flag.getRegionGroupFlag().getName());
if (o2 != null) {
setFlag(region, flag.getRegionGroupFlag(), o2);
}
}
}
}
private <T> void setFlag(ProtectedRegion region, Flag<T> flag, Object rawValue) {
T val = flag.unmarshal(rawValue);
if (val == null) {
@ -220,13 +231,13 @@ private <T> void setFlag(ProtectedRegion region, Flag<T> flag, Object rawValue)
}
region.setFlag(flag, val);
}
@SuppressWarnings("deprecation")
private DefaultDomain parseDomain(YAMLNode node) {
if (node == null) {
return new DefaultDomain();
}
DefaultDomain domain = new DefaultDomain();
for (String name : node.getStringList("players", null)) {
@ -240,11 +251,11 @@ private DefaultDomain parseDomain(YAMLNode node) {
logger.log(Level.WARNING, "Failed to parse UUID '" + stringId +"'", e);
}
}
for (String name : node.getStringList("groups", null)) {
domain.addGroup(name);
}
return domain;
}
@ -307,18 +318,18 @@ protected void performSave() {
"#");
config.save();
}
private Map<String, Object> getFlagData(ProtectedRegion region) {
Map<String, Object> flagData = new HashMap<String, Object>();
for (Map.Entry<Flag<?>, Object> entry : region.getFlags().entrySet()) {
Flag<?> flag = entry.getKey();
addMarshalledFlag(flagData, flag, entry.getValue());
}
return flagData;
}
@SuppressWarnings("unchecked")
private <V> void addMarshalledFlag(Map<String, Object> flagData, Flag<V> flag, Object val) {
if (val == null) {
@ -327,7 +338,7 @@ private <V> void addMarshalledFlag(Map<String, Object> flagData, Flag<V> flag, O
flagData.put(flag.getName(), flag.marshal((V) val));
}
@SuppressWarnings("deprecation")
private Map<String, Object> getDomainData(DefaultDomain domain) {
Map<String, Object> domainData = new HashMap<String, Object>();
@ -335,21 +346,21 @@ private Map<String, Object> getDomainData(DefaultDomain domain) {
setDomainData(domainData, "players", domain.getPlayers());
setDomainData(domainData, "unique-ids", domain.getUniqueIds());
setDomainData(domainData, "groups", domain.getGroups());
return domainData;
}
private void setDomainData(Map<String, Object> domainData, String key, Set<?> domain) {
if (domain.isEmpty()) {
return;
}
List<String> list = new ArrayList<String>();
for (Object str : domain) {
list.add(String.valueOf(str));
}
domainData.put(key, list);
}
@ -384,5 +395,5 @@ private static String dumpAsYaml(Object object) {
return "<error while dumping object>";
}
}
}

View File

@ -1,62 +1,62 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.protection.databases.migrators;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
public abstract class AbstractDatabaseMigrator implements DatabaseMigrator {
private static HashMap<MigratorKey, Class<? extends AbstractDatabaseMigrator>> migrators =
new HashMap<MigratorKey, Class<? extends AbstractDatabaseMigrator>>();
public static Map<MigratorKey, Class<? extends AbstractDatabaseMigrator>> getMigrators() {
if (!migrators.isEmpty()) return migrators;
AbstractDatabaseMigrator.migrators.put(new MigratorKey("mysql", "yaml"), MySQLToYAMLMigrator.class);
AbstractDatabaseMigrator.migrators.put(new MigratorKey("yaml", "mysql"), YAMLToMySQLMigrator.class);
return migrators;
}
protected abstract Set<String> getWorldsFromOld() throws MigrationException;
protected abstract Map<String, ProtectedRegion> getRegionsForWorldFromOld(String world) throws MigrationException;
protected abstract ProtectionDatabase getNewWorldStorage(String world) throws MigrationException;
public void migrate() throws MigrationException {
for (String world : this.getWorldsFromOld()) {
ProtectionDatabase database = this.getNewWorldStorage(world);
database.setRegions(this.getRegionsForWorldFromOld(world));
try {
database.save();
} catch (ProtectionDatabaseException e) {
throw new MigrationException(e);
}
}
}
}
/*
* 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.migrator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
public abstract class AbstractDatabaseMigrator implements DatabaseMigrator {
private static HashMap<MigratorKey, Class<? extends AbstractDatabaseMigrator>> migrators =
new HashMap<MigratorKey, Class<? extends AbstractDatabaseMigrator>>();
public static Map<MigratorKey, Class<? extends AbstractDatabaseMigrator>> getMigrators() {
if (!migrators.isEmpty()) return migrators;
AbstractDatabaseMigrator.migrators.put(new MigratorKey("mysql", "yaml"), MySQLToYAMLMigrator.class);
AbstractDatabaseMigrator.migrators.put(new MigratorKey("yaml", "mysql"), YAMLToMySQLMigrator.class);
return migrators;
}
protected abstract Set<String> getWorldsFromOld() throws MigrationException;
protected abstract Map<String, ProtectedRegion> getRegionsForWorldFromOld(String world) throws MigrationException;
protected abstract ProtectionDatabase getNewWorldStorage(String world) throws MigrationException;
public void migrate() throws MigrationException {
for (String world : this.getWorldsFromOld()) {
ProtectionDatabase database = this.getNewWorldStorage(world);
database.setRegions(this.getRegionsForWorldFromOld(world));
try {
database.save();
} catch (ProtectionDatabaseException e) {
throw new MigrationException(e);
}
}
}
}

View File

@ -1,31 +1,31 @@
/*
* 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.migrators;
/**
* Represents a method of converting from one database to another.
*
* @author Nicholas Steicke
*/
public interface DatabaseMigrator {
public void migrate() throws MigrationException;
}
/*
* 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.migrator;
/**
* Represents a method of converting from one database to another.
*
* @author Nicholas Steicke
*/
public interface DatabaseMigrator {
public void migrate() throws MigrationException;
}

View File

@ -1,40 +1,40 @@
/*
* 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.migrators;
public class MigrationException extends Exception {
private static final long serialVersionUID = 1L;
public MigrationException() {
super();
}
public MigrationException(String message) {
super(message);
}
public MigrationException(String message, Throwable cause) {
super(message, cause);
}
public MigrationException(Throwable cause) {
super(cause);
}
}
/*
* 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.migrator;
public class MigrationException extends Exception {
private static final long serialVersionUID = 1L;
public MigrationException() {
super();
}
public MigrationException(String message) {
super(message);
}
public MigrationException(String message, Throwable cause) {
super(message, cause);
}
public MigrationException(Throwable cause) {
super(cause);
}
}

View File

@ -1,43 +1,43 @@
/*
* 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.migrators;
public class MigratorKey {
public final String from;
public final String to;
public MigratorKey(String from, String to) {
this.from = from;
this.to = to;
}
public boolean equals(Object o) {
MigratorKey other = (MigratorKey) o;
return other.from.equals(this.from) && other.to.equals(this.to);
}
public int hashCode() {
int hash = 17;
hash = hash * 31 + this.from.hashCode();
hash = hash * 31 + this.to.hashCode();
return hash;
}
/*
* 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.migrator;
public class MigratorKey {
public final String from;
public final String to;
public MigratorKey(String from, String to) {
this.from = from;
this.to = to;
}
public boolean equals(Object o) {
MigratorKey other = (MigratorKey) o;
return other.from.equals(this.from) && other.to.equals(this.to);
}
public int hashCode() {
int hash = 17;
hash = hash * 31 + this.from.hashCode();
hash = hash * 31 + this.to.hashCode();
return hash;
}
}

View File

@ -1,97 +1,97 @@
/*
* 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.migrators;
import com.sk89q.worldguard.bukkit.ConfigurationManager;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.databases.MySQLDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
import com.sk89q.worldguard.protection.databases.YAMLDatabase;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import java.io.File;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class MySQLToYAMLMigrator extends AbstractDatabaseMigrator {
private WorldGuardPlugin plugin;
private Set<String> worlds;
public MySQLToYAMLMigrator(WorldGuardPlugin plugin) throws MigrationException {
this.plugin = plugin;
this.worlds = new HashSet<String>();
ConfigurationManager config = plugin.getGlobalStateManager();
try {
Connection conn = DriverManager.getConnection(config.sqlDsn, config.sqlUsername, config.sqlPassword);
ResultSet worlds = conn.prepareStatement("SELECT `name` FROM `world`;").executeQuery();
while(worlds.next()) {
this.worlds.add(worlds.getString(1));
}
conn.close();
} catch (SQLException e) {
throw new MigrationException(e);
}
}
@Override
protected Set<String> getWorldsFromOld() {
return this.worlds;
}
@Override
protected Map<String, ProtectedRegion> getRegionsForWorldFromOld(String world) throws MigrationException {
ProtectionDatabase oldDatabase;
try {
oldDatabase = new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger());
oldDatabase.load();
} catch (ProtectionDatabaseException e) {
throw new MigrationException(e);
}
return oldDatabase.getRegions();
}
@Override
protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException {
try {
File file = new File(plugin.getDataFolder(),
"worlds" + File.separator + world + File.separator + "regions.yml");
return new YAMLDatabase(file, plugin.getLogger());
} catch (FileNotFoundException e) {
throw new MigrationException(e);
} catch (ProtectionDatabaseException e) {
throw new MigrationException(e);
}
}
}
/*
* 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.migrator;
import com.sk89q.worldguard.bukkit.ConfigurationManager;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.databases.MySQLDatabase;
import com.sk89q.worldguard.protection.databases.YAMLDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import java.io.File;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class MySQLToYAMLMigrator extends AbstractDatabaseMigrator {
private WorldGuardPlugin plugin;
private Set<String> worlds;
public MySQLToYAMLMigrator(WorldGuardPlugin plugin) throws MigrationException {
this.plugin = plugin;
this.worlds = new HashSet<String>();
ConfigurationManager config = plugin.getGlobalStateManager();
try {
Connection conn = DriverManager.getConnection(config.sqlDsn, config.sqlUsername, config.sqlPassword);
ResultSet worlds = conn.prepareStatement("SELECT `name` FROM `world`;").executeQuery();
while(worlds.next()) {
this.worlds.add(worlds.getString(1));
}
conn.close();
} catch (SQLException e) {
throw new MigrationException(e);
}
}
@Override
protected Set<String> getWorldsFromOld() {
return this.worlds;
}
@Override
protected Map<String, ProtectedRegion> getRegionsForWorldFromOld(String world) throws MigrationException {
ProtectionDatabase oldDatabase;
try {
oldDatabase = new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger());
oldDatabase.load();
} catch (ProtectionDatabaseException e) {
throw new MigrationException(e);
}
return oldDatabase.getRegions();
}
@Override
protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException {
try {
File file = new File(plugin.getDataFolder(),
"worlds" + File.separator + world + File.separator + "regions.yml");
return new YAMLDatabase(file, plugin.getLogger());
} catch (FileNotFoundException e) {
throw new MigrationException(e);
} catch (ProtectionDatabaseException e) {
throw new MigrationException(e);
}
}
}

View File

@ -1,86 +1,86 @@
/*
* 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.migrators;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.databases.MySQLDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
import com.sk89q.worldguard.protection.databases.YAMLDatabase;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class YAMLToMySQLMigrator extends AbstractDatabaseMigrator {
private WorldGuardPlugin plugin;
private HashMap<String,File> regionYamlFiles;
public YAMLToMySQLMigrator(WorldGuardPlugin plugin) {
this.plugin = plugin;
this.regionYamlFiles = new HashMap<String,File>();
File files[] = new File(plugin.getDataFolder(), "worlds" + File.separator).listFiles();
for (File item : files) {
if (item.isDirectory()) {
for (File subItem : item.listFiles()) {
if (subItem.getName().equals("regions.yml")) {
this.regionYamlFiles.put(item.getName(), subItem);
}
}
}
}
}
@Override
protected Set<String> getWorldsFromOld() {
return this.regionYamlFiles.keySet();
}
@Override
protected Map<String, ProtectedRegion> getRegionsForWorldFromOld(String world) throws MigrationException {
ProtectionDatabase oldDatabase;
try {
oldDatabase = new YAMLDatabase(this.regionYamlFiles.get(world), plugin.getLogger());
oldDatabase.load();
} catch (FileNotFoundException e) {
throw new MigrationException(e);
} catch (ProtectionDatabaseException e) {
throw new MigrationException(e);
}
return oldDatabase.getRegions();
}
@Override
protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException {
try {
return new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger());
} catch (ProtectionDatabaseException e) {
throw new MigrationException(e);
}
}
}
/*
* 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.migrator;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.databases.MySQLDatabase;
import com.sk89q.worldguard.protection.databases.YAMLDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class YAMLToMySQLMigrator extends AbstractDatabaseMigrator {
private WorldGuardPlugin plugin;
private HashMap<String,File> regionYamlFiles;
public YAMLToMySQLMigrator(WorldGuardPlugin plugin) {
this.plugin = plugin;
this.regionYamlFiles = new HashMap<String,File>();
File files[] = new File(plugin.getDataFolder(), "worlds" + File.separator).listFiles();
for (File item : files) {
if (item.isDirectory()) {
for (File subItem : item.listFiles()) {
if (subItem.getName().equals("regions.yml")) {
this.regionYamlFiles.put(item.getName(), subItem);
}
}
}
}
}
@Override
protected Set<String> getWorldsFromOld() {
return this.regionYamlFiles.keySet();
}
@Override
protected Map<String, ProtectedRegion> getRegionsForWorldFromOld(String world) throws MigrationException {
ProtectionDatabase oldDatabase;
try {
oldDatabase = new YAMLDatabase(this.regionYamlFiles.get(world), plugin.getLogger());
oldDatabase.load();
} catch (FileNotFoundException e) {
throw new MigrationException(e);
} catch (ProtectionDatabaseException e) {
throw new MigrationException(e);
}
return oldDatabase.getRegions();
}
@Override
protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException {
try {
return new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger());
} catch (ProtectionDatabaseException e) {
throw new MigrationException(e);
}
}
}

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.internal.protection.database.mysql;
package com.sk89q.worldguard.protection.databases.mysql;
import com.sk89q.worldguard.bukkit.ConfigurationManager;
import org.yaml.snakeyaml.error.YAMLException;

View File

@ -17,8 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.internal.protection.database.mysql;
package com.sk89q.worldguard.protection.databases.mysql;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.jolbox.bonecp.BoneCP;
import com.jolbox.bonecp.BoneCPConfig;
import com.sk89q.worldguard.bukkit.ConfigurationManager;
@ -270,7 +271,7 @@ protected void performLoad() throws ProtectionDatabaseException {
Connection connection = null;
try {
connection = getConnection();
setRegions(new RegionLoader(this, connection).call());
setRegions(new RegionLoader(this, connection).load());
} catch (SQLException e) {
throw new ProtectionDatabaseException("Failed to load regions database", e);
} finally {
@ -288,7 +289,7 @@ protected void performSave() throws ProtectionDatabaseException {
Connection connection = null;
try {
connection = getConnection();
new RegionCommitter(this, connection, getRegions()).call();
new RegionWriter(this, connection, getRegions()).save();
} catch (SQLException e) {
throw new ProtectionDatabaseException("Failed to save regions database", e);
} finally {

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.internal.protection.database.mysql;
package com.sk89q.worldguard.protection.databases.mysql;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.BlockVector2D;
@ -40,9 +40,8 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
class RegionLoader extends AbstractJob implements Callable<Map<String, ProtectedRegion>> {
class RegionLoader extends AbstractJob {
/*
========= Everything below is a nightmare. =========
@ -60,8 +59,7 @@ class RegionLoader extends AbstractJob implements Callable<Map<String, Protected
this.worldId = database.getWorldId();
}
@Override
public Map<String, ProtectedRegion> call() {
public Map<String, ProtectedRegion> load() {
parentSets = new HashMap<ProtectedRegion, String>();
// We load the cuboid regions first, as this is likely to be the

View File

@ -17,15 +17,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.internal.protection.database.mysql;
package com.sk89q.worldguard.protection.databases.mysql;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.BlockVector2D;
import com.sk89q.worldguard.domains.DefaultDomain;
import com.sk89q.worldguard.internal.protection.database.mysql.UserToIdCache.NameToIdCache;
import com.sk89q.worldguard.internal.protection.database.mysql.UserToIdCache.UUIDToIdCache;
import com.sk89q.worldguard.internal.util.sql.StatementUtils;
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
import com.sk89q.worldguard.protection.databases.RegionDBUtil;
import com.sk89q.worldguard.protection.flags.Flag;
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
@ -43,33 +41,33 @@
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldguard.protection.databases.mysql.UserRowCache.NameRowCache;
import static com.sk89q.worldguard.protection.databases.mysql.UserRowCache.UUIDRowCache;
class RegionCommitter extends AbstractJob implements Callable<Object> {
class RegionWriter extends AbstractJob {
/*
========= Everything below is a nightmare. =========
*/
private final Map<String, ProtectedRegion> regions;
private final NameToIdCache usernameCache;
private final UUIDToIdCache uuidCache;
private final NameRowCache usernameCache;
private final UUIDRowCache uuidCache;
private final int worldId;
RegionCommitter(MySQLDatabaseImpl database, Connection conn, Map<String, ProtectedRegion> regions) {
RegionWriter(MySQLDatabaseImpl database, Connection conn, Map<String, ProtectedRegion> regions) {
super(database, conn);
checkNotNull(regions);
this.regions = regions;
this.worldId = database.getWorldId();
usernameCache = new NameToIdCache(database, conn);
uuidCache = new UUIDToIdCache(database, conn);
usernameCache = new NameRowCache(database, conn);
uuidCache = new UUIDRowCache(database, conn);
}
@Override
public Object call() throws SQLException, ProtectionDatabaseException {
public void save() throws SQLException, ProtectionDatabaseException {
/*
* As we don't get notified on the creation/removal of regions:
@ -183,8 +181,6 @@ public Object call() throws SQLException, ProtectionDatabaseException {
closeQuietly(removeRegion);
}
}
return null;
}
/*
@ -207,11 +203,11 @@ private Map<String,Integer> getGroupIds(String... groupnames) {
"`group`.`name` " +
"FROM `" + config.sqlTablePrefix + "group` AS `group` " +
"WHERE `name` IN (%s)",
RegionDBUtil.preparePlaceHolders(groupnames.length)
StatementUtils.preparePlaceHolders(groupnames.length)
)
);
RegionDBUtil.setValues(findGroupsStatement, groupnames);
StatementUtils.setValues(findGroupsStatement, groupnames);
findGroupsResults = findGroupsStatement.executeQuery();

View File

@ -0,0 +1,184 @@
/*
* 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.mysql;
import com.google.common.collect.Lists;
import com.sk89q.worldguard.internal.util.sql.StatementUtils;
import com.sk89q.worldguard.util.io.Closer;
import javax.annotation.Nullable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
abstract class UserRowCache<V> extends AbstractJob {
private static final int MAX_NUMBER_PER_QUERY = 100;
private static final Object LOCK = new Object();
private final Map<V, Integer> cache = new HashMap<V, Integer>();
private final String fieldName;
protected UserRowCache(MySQLDatabaseImpl database, Connection conn, String fieldName) {
super(database, conn);
this.fieldName = fieldName;
}
protected abstract String fromType(V o);
protected abstract V toType(String o);
protected abstract V toKey(V object);
@Nullable
public Integer find(V object) {
return cache.get(object);
}
public void fetch(Collection<V> entries) throws SQLException {
synchronized (LOCK) { // Lock across all cache instances
checkNotNull(entries);
// Get a list of missing entries
List<V> fetchList = new ArrayList<V>();
for (V entry : entries) {
if (!cache.containsKey(toKey(entry))) {
fetchList.add(entry);
}
}
if (fetchList.isEmpty()) {
return; // Nothing to do
}
// Search for entries
for (List<V> partition : Lists.partition(fetchList, MAX_NUMBER_PER_QUERY)) {
Closer closer = Closer.create();
try {
PreparedStatement statement = closer.register(conn.prepareStatement(
String.format(
"SELECT `user`.`id`, `user`.`" + fieldName + "` " +
"FROM `" + config.sqlTablePrefix + "user` AS `user` " +
"WHERE `" + fieldName + "` IN (%s)",
StatementUtils.preparePlaceHolders(partition.size()))));
String[] values = new String[partition.size()];
int i = 0;
for (V entry : partition) {
values[i] = fromType(entry);
i++;
}
StatementUtils.setValues(statement, values);
ResultSet results = closer.register(statement.executeQuery());
while (results.next()) {
cache.put(toKey(toType(results.getString(fieldName))), results.getInt("id"));
}
} finally {
closer.closeQuietly();
}
}
List<V> missing = new ArrayList<V>();
for (V entry : fetchList) {
if (!cache.containsKey(toKey(entry))) {
missing.add(entry);
}
}
// Insert entries that are missing
if (!missing.isEmpty()) {
Closer closer = Closer.create();
try {
PreparedStatement statement = closer.register(conn.prepareStatement(
"INSERT INTO `" + config.sqlTablePrefix + "user` (`id`, `" + fieldName + "`) VALUES (null, ?)",
Statement.RETURN_GENERATED_KEYS));
for (V entry : missing) {
statement.setString(1, fromType(entry));
statement.execute();
ResultSet generatedKeys = statement.getGeneratedKeys();
if (generatedKeys.first()) {
cache.put(toKey(entry), generatedKeys.getInt(1));
} else {
logger.warning("Could not get the database ID for user " + entry);
}
}
} finally {
closer.closeQuietly();
}
}
}
}
static class NameRowCache extends UserRowCache<String> {
protected NameRowCache(MySQLDatabaseImpl database, Connection conn) {
super(database, conn, "name");
}
@Override
protected String fromType(String o) {
return o;
}
@Override
protected String toType(String o) {
return o;
}
@Override
protected String toKey(String object) {
return object.toLowerCase();
}
}
static class UUIDRowCache extends UserRowCache<UUID> {
protected UUIDRowCache(MySQLDatabaseImpl database, Connection conn) {
super(database, conn, "uuid");
}
@Override
protected String fromType(UUID o) {
return o.toString();
}
@Override
protected UUID toType(String o) {
return UUID.fromString(o);
}
@Override
protected UUID toKey(UUID object) {
return object;
}
}
}

View File

@ -26,21 +26,24 @@
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A very simple implementation of the region manager that uses a flat list
* and iterates through the list to identify applicable regions. This method
* is not very efficient.
*
* @author sk89q
*/
public class FlatRegionManager extends RegionManager {
/**
* List of protected regions.
*/
private Map<String, ProtectedRegion> regions;
private ConcurrentMap<String, ProtectedRegion> regions = new ConcurrentHashMap<String, ProtectedRegion>();
/**
* Construct the manager.
@ -49,7 +52,6 @@ public class FlatRegionManager extends RegionManager {
*/
public FlatRegionManager(ProtectionDatabase regionLoader) {
super(regionLoader);
regions = new TreeMap<String, ProtectedRegion>();
}
@Override
@ -59,7 +61,7 @@ public Map<String, ProtectedRegion> getRegions() {
@Override
public void setRegions(Map<String, ProtectedRegion> regions) {
this.regions = new TreeMap<String, ProtectedRegion>(regions);
this.regions = new ConcurrentHashMap<String, ProtectedRegion>(regions);
}
@Override
@ -185,7 +187,7 @@ public boolean overlapsUnownedRegion(ProtectedRegion checkRegion, LocalPlayer pl
intersectRegions = new ArrayList<ProtectedRegion>();
}
return intersectRegions.size() > 0;
return !intersectRegions.isEmpty();
}
@Override

View File

@ -30,7 +30,12 @@
import org.khelekore.prtree.PRTree;
import org.khelekore.prtree.SimpleMBR;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class PRTreeRegionManager extends RegionManager {
@ -55,7 +60,7 @@ public Map<String, ProtectedRegion> getRegions() {
@Override
public void setRegions(Map<String, ProtectedRegion> regions) {
Map<String, ProtectedRegion> newRegions = new TreeMap<String, ProtectedRegion>(regions);
ConcurrentMap<String, ProtectedRegion> newRegions = new ConcurrentHashMap<String, ProtectedRegion>(regions);
PRTree<ProtectedRegion> tree = new PRTree<ProtectedRegion>(converter, BRANCH_FACTOR);
tree.load(newRegions.values());
this.data = new RegionsContainer(newRegions, tree);
@ -65,8 +70,9 @@ public void setRegions(Map<String, ProtectedRegion> regions) {
public void addRegion(ProtectedRegion region) {
RegionsContainer data = this.data;
data.regions.put(region.getId().toLowerCase(), region);
data.tree = new PRTree<ProtectedRegion>(converter, BRANCH_FACTOR);
data.tree.load(data.regions.values());
PRTree<ProtectedRegion> tree = new PRTree<ProtectedRegion>(converter, BRANCH_FACTOR);
tree.load(data.regions.values());
this.data = new RegionsContainer(data.regions, tree);
}
@Override
@ -95,8 +101,9 @@ public void removeRegion(String id) {
}
}
data.tree = new PRTree<ProtectedRegion>(converter, BRANCH_FACTOR);
data.tree.load(data.regions.values());
PRTree<ProtectedRegion> tree = new PRTree<ProtectedRegion>(converter, BRANCH_FACTOR);
tree.load(data.regions.values());
this.data = new RegionsContainer(data.regions, tree);
}
@Override
@ -197,7 +204,7 @@ public boolean overlapsUnownedRegion(ProtectedRegion checkRegion, LocalPlayer pl
intersectRegions = new ArrayList<ProtectedRegion>();
}
return intersectRegions.size() > 0;
return !intersectRegions.isEmpty();
}
@Override
@ -219,15 +226,15 @@ public int getRegionCountOfPlayer(LocalPlayer player) {
}
private class RegionsContainer {
private final Map<String, ProtectedRegion> regions;
private PRTree<ProtectedRegion> tree;
private final ConcurrentMap<String, ProtectedRegion> regions;
private final PRTree<ProtectedRegion> tree;
private RegionsContainer() {
regions = new TreeMap<String, ProtectedRegion>();
regions = new ConcurrentHashMap<String, ProtectedRegion>();
tree = new PRTree<ProtectedRegion>(converter, BRANCH_FACTOR);
}
private RegionsContainer(Map<String, ProtectedRegion> regions, PRTree<ProtectedRegion> tree) {
private RegionsContainer(ConcurrentMap<String, ProtectedRegion> regions, PRTree<ProtectedRegion> tree) {
this.regions = regions;
this.tree = tree;
}

View File

@ -60,6 +60,7 @@ public RegionManager(ProtectionDatabase loader) {
public void load() throws ProtectionDatabaseException {
loader.load(this);
}
/**
* Load the list of regions. If the regions do not load properly, then
* the existing list should be used (as stored previously).

View File

@ -35,8 +35,6 @@
/**
* Represents a region of any shape and size that can be protected.
*
* @author sk89q
*/
public abstract class ProtectedRegion implements Comparable<ProtectedRegion> {
@ -45,34 +43,11 @@ public abstract class ProtectedRegion implements Comparable<ProtectedRegion> {
private static final Pattern idPattern = Pattern.compile("^[A-Za-z0-9_,'\\-\\+/]{1,}$");
/**
* Holds the region's ID.
*/
private String id;
/**
* Priority.
*/
private int priority = 0;
/**
* Holds the curParent.
*/
private ProtectedRegion parent;
/**
* List of owners.
*/
private DefaultDomain owners = new DefaultDomain();
/**
* List of members.
*/
private DefaultDomain members = new DefaultDomain();
/**
* List of flags.
*/
private Map<Flag<?>, Object> flags = new ConcurrentHashMap<Flag<?>, Object>();
/**
@ -457,8 +432,8 @@ public boolean contains(int x, int y, int z) {
/**
* Check to see if any of the 2D points are inside this region.
*
* @param pts
* @return
* @param pts a list positions
* @return true if contained
*/
public boolean containsAny(List<BlockVector2D> pts) {
for (BlockVector2D pt : pts) {
@ -477,6 +452,7 @@ public boolean containsAny(List<BlockVector2D> pts) {
*
* @param other The region to compare to
*/
@Override
public int compareTo(ProtectedRegion other) {
if (priority > other.priority) {
return -1;
@ -598,6 +574,6 @@ public boolean equals(Object obj) {
* situation.
*/
public static class CircularInheritanceException extends Exception {
private static final long serialVersionUID = 7479613488496776022L;
}
}

View File

@ -24,11 +24,9 @@
/**
* Various utility functions for regions.
*
* @author sk89q
*/
@Deprecated
public class RegionUtil {
public final class RegionUtil {
private RegionUtil() {
}