mirror of
https://github.com/EngineHub/WorldGuard.git
synced 2024-12-25 18:47:44 +01:00
Rewrite region API. Migrations need to be re-implemented.
This commit, however, adds: * Better support for background saves and loads. * Periodical writes of region changes rather than explicit save() calls. * Rewrite of the MySQL region code. * Support for partial saves when using MySQL. * Decoupling of region index implementations and RegionManager. What still needs to be done includes: * Background region modification operations (addmember, etc.). * Unit tests to verify correctness of the partial saving. * Migration support (to be re-added). * Better handling when regions are failing to save. * Testing of the /rg load and /rg save commands. * Verification of how server shutdown / world unload is handled. * Verification that thread-unsafe concurrent saves of data is not happening.
This commit is contained in:
parent
b5dfed9e01
commit
7a01168781
@ -66,30 +66,11 @@ public class ConfigurationManager {
|
||||
"# - Lines starting with # are comments and so they are ignored.\r\n" +
|
||||
"#\r\n";
|
||||
|
||||
/**
|
||||
* Reference to the plugin.
|
||||
*/
|
||||
private WorldGuardPlugin plugin;
|
||||
|
||||
/**
|
||||
* Holds configurations for different worlds.
|
||||
*/
|
||||
private ConcurrentMap<String, WorldConfiguration> worlds;
|
||||
|
||||
/**
|
||||
* The global configuration for use when loading worlds
|
||||
*/
|
||||
private YAMLProcessor config;
|
||||
|
||||
/**
|
||||
* List of people with god mode.
|
||||
*/
|
||||
@Deprecated
|
||||
private Set<String> hasGodMode = new HashSet<String>();
|
||||
|
||||
/**
|
||||
* List of people who can breathe underwater.
|
||||
*/
|
||||
private Set<String> hasAmphibious = new HashSet<String>();
|
||||
|
||||
private boolean hasCommandBookGodMode = false;
|
||||
@ -125,6 +106,25 @@ public ConfigurationManager(WorldGuardPlugin plugin) {
|
||||
this.worlds = new ConcurrentHashMap<String, WorldConfiguration>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the folder for storing data files and configuration.
|
||||
*
|
||||
* @return the data folder
|
||||
*/
|
||||
public File getDataFolder() {
|
||||
return plugin.getDataFolder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the folder for storing data files and configuration for each
|
||||
* world.
|
||||
*
|
||||
* @return the data folder
|
||||
*/
|
||||
public File getWorldsDataFolder() {
|
||||
return new File(getDataFolder(), "worlds");
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the configuration.
|
||||
*/
|
||||
@ -167,8 +167,7 @@ public void load() {
|
||||
}
|
||||
}
|
||||
|
||||
useSqlDatabase = config.getBoolean(
|
||||
"regions.sql.use", false);
|
||||
useSqlDatabase = config.getBoolean("regions.sql.use", false);
|
||||
|
||||
sqlDsn = config.getString("regions.sql.dsn", "jdbc:mysql://localhost/worldguard");
|
||||
sqlUsername = config.getString("regions.sql.username", "worldguard");
|
||||
|
@ -1,155 +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.bukkit;
|
||||
|
||||
import com.sk89q.worldguard.protection.databases.CSVDatabase;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Utility methods for porting from legacy versions.
|
||||
*/
|
||||
public final class LegacyWorldGuardMigration {
|
||||
|
||||
private LegacyWorldGuardMigration() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Port over the blacklist.
|
||||
*
|
||||
* @param plugin The plugin instance
|
||||
*/
|
||||
public static void migrateBlacklist(WorldGuardPlugin plugin) {
|
||||
World mainWorld = plugin.getServer().getWorlds().get(0);
|
||||
String mainWorldName = mainWorld.getName();
|
||||
String newPath = "worlds/" + mainWorldName + "/blacklist.txt";
|
||||
|
||||
File oldFile = new File(plugin.getDataFolder(), "blacklist.txt");
|
||||
File newFile = new File(plugin.getDataFolder(), newPath);
|
||||
|
||||
if (!newFile.exists() && oldFile.exists()) {
|
||||
plugin.getLogger().warning("WorldGuard will now update your blacklist "
|
||||
+ "from an older version of WorldGuard.");
|
||||
|
||||
// Need to make root directories
|
||||
newFile.getParentFile().mkdirs();
|
||||
|
||||
if (copyFile(oldFile, newFile)) {
|
||||
oldFile.renameTo(new File(plugin.getDataFolder(),
|
||||
"blacklist.txt.old"));
|
||||
} else {
|
||||
plugin.getLogger().warning("blacklist.txt has been converted " +
|
||||
"for the main world at " + newPath + "");
|
||||
plugin.getLogger().warning("Your other worlds currently have no " +
|
||||
"blacklist defined!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate region settings.
|
||||
*
|
||||
* @param plugin The plugin instance
|
||||
*/
|
||||
public static void migrateRegions(WorldGuardPlugin plugin) {
|
||||
try {
|
||||
File oldDatabase = new File(plugin.getDataFolder(), "regions.txt");
|
||||
if (!oldDatabase.exists()) return;
|
||||
|
||||
plugin.getLogger().info("The regions database has changed in 5.x. "
|
||||
+ "Your old regions database will be converted to the new format "
|
||||
+ "and set as your primary world's database.");
|
||||
|
||||
World w = plugin.getServer().getWorlds().get(0);
|
||||
RegionManager mgr = plugin.getGlobalRegionManager().get(w);
|
||||
|
||||
// First load up the old database using the CSV loader
|
||||
CSVDatabase db = new CSVDatabase(oldDatabase, plugin.getLogger());
|
||||
db.load();
|
||||
|
||||
// Then save the new database
|
||||
mgr.setRegions(db.getRegions());
|
||||
mgr.save();
|
||||
|
||||
oldDatabase.renameTo(new File(plugin.getDataFolder(), "regions.txt.old"));
|
||||
|
||||
plugin.getLogger().info("Regions database converted!");
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
plugin.getLogger().warning("Failed to load regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a file.
|
||||
*
|
||||
* @param from The source file
|
||||
* @param to The destination file
|
||||
* @return true if successful
|
||||
*/
|
||||
private static boolean copyFile(File from, File to) {
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
|
||||
try {
|
||||
in = new FileInputStream(from);
|
||||
out = new FileOutputStream(to);
|
||||
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
|
||||
in.close();
|
||||
out.close();
|
||||
|
||||
return true;
|
||||
} catch (FileNotFoundException ignore) {
|
||||
} catch (IOException ignore) {
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -300,7 +300,7 @@ private void appendWorldConfigurations(WorldGuardPlugin plugin, List<World> worl
|
||||
regionsLog.put("Number of regions", worldRegions.getRegions().size());
|
||||
LogListBlock globalRegionLog = regionsLog.putChild("Global region");
|
||||
|
||||
ProtectedRegion globalRegion = worldRegions.getRegion("__global__");
|
||||
ProtectedRegion globalRegion = worldRegions.matchRegion("__global__");
|
||||
if (globalRegion == null) {
|
||||
globalRegionLog.put("Status", "UNDEFINED");
|
||||
} else {
|
||||
|
@ -53,10 +53,9 @@
|
||||
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.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.databases.migrator.MigrationException;
|
||||
import com.sk89q.worldguard.protection.databases.migrator.UUIDMigrator;
|
||||
import com.sk89q.worldguard.protection.databases.util.UnresolvedNamesException;
|
||||
import com.sk89q.worldguard.protection.util.migrator.MigrationException;
|
||||
import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator;
|
||||
import com.sk89q.worldguard.protection.util.UnresolvedNamesException;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.util.FatalConfigurationLoadingException;
|
||||
import org.bukkit.ChatColor;
|
||||
@ -98,8 +97,8 @@ public class WorldGuardPlugin extends JavaPlugin {
|
||||
|
||||
private static WorldGuardPlugin inst;
|
||||
private final CommandsManager<CommandSender> commands;
|
||||
private final GlobalRegionManager globalRegionManager;
|
||||
private final ConfigurationManager configuration;
|
||||
private GlobalRegionManager globalRegionManager;
|
||||
private ConfigurationManager configuration;
|
||||
private FlagStateManager flagStateManager;
|
||||
private final Supervisor supervisor = new SimpleSupervisor();
|
||||
private ListeningExecutorService executorService;
|
||||
@ -111,9 +110,6 @@ public class WorldGuardPlugin extends JavaPlugin {
|
||||
* this merely instantiates the objects.
|
||||
*/
|
||||
public WorldGuardPlugin() {
|
||||
configuration = new ConfigurationManager(this);
|
||||
globalRegionManager = new GlobalRegionManager(this);
|
||||
|
||||
final WorldGuardPlugin plugin = inst = this;
|
||||
commands = new CommandsManager<CommandSender>() {
|
||||
@Override
|
||||
@ -137,6 +133,7 @@ public static WorldGuardPlugin inst() {
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void onEnable() {
|
||||
configuration = new ConfigurationManager(this);
|
||||
executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 20));
|
||||
|
||||
// Set the proper command injector
|
||||
@ -176,14 +173,12 @@ public void run() {
|
||||
|
||||
PermissionsResolverManager.initialize(this);
|
||||
|
||||
// This must be done before configuration is loaded
|
||||
LegacyWorldGuardMigration.migrateBlacklist(this);
|
||||
|
||||
try {
|
||||
// Load the configuration
|
||||
configuration.load();
|
||||
|
||||
getLogger().info("Loading region data...");
|
||||
globalRegionManager = new GlobalRegionManager(this);
|
||||
globalRegionManager.preload();
|
||||
|
||||
migrateRegionUniqueIds(); // Migrate to UUIDs
|
||||
@ -202,10 +197,6 @@ public void run() {
|
||||
"******************************************************\n");
|
||||
}
|
||||
|
||||
// Migrate regions after the regions were loaded because
|
||||
// the migration code reuses the loaded region managers
|
||||
LegacyWorldGuardMigration.migrateRegions(this);
|
||||
|
||||
flagStateManager = new FlagStateManager(this);
|
||||
|
||||
if (configuration.useRegionsScheduler) {
|
||||
@ -306,8 +297,8 @@ private void migrateRegionUniqueIds() {
|
||||
}
|
||||
} catch (MigrationException e) {
|
||||
getLogger().log(Level.WARNING, "Failed to migrate regions to use UUIDs instead of player names", e);
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
getLogger().log(Level.WARNING, "Failed to save regions after UUID conversion", e);
|
||||
} catch (IOException e) {
|
||||
getLogger().log(Level.WARNING, "Failed to save region data", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,16 +334,14 @@ public String convertThrowable(@Nullable Throwable throwable) {
|
||||
} 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";
|
||||
return "WorldGuard: Task was cancelled";
|
||||
} else if (throwable instanceof InterruptedException) {
|
||||
return "Task was interrupted";
|
||||
return "WorldGuard: 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";
|
||||
getLogger().log(Level.WARNING, "WorldGuard encountered an unexpected error", throwable);
|
||||
return "WorldGuard: An unexpected error occurred! Please see the server console.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@
|
||||
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;
|
||||
|
||||
@ -129,17 +128,6 @@ public AsyncCommandHelper forRegionDataSave(World world, boolean silent) {
|
||||
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);
|
||||
}
|
||||
|
@ -39,18 +39,15 @@
|
||||
import com.sk89q.worldguard.bukkit.WorldConfiguration;
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.databases.RegionDBUtil;
|
||||
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.databases.migrator.UUIDMigrator;
|
||||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.protection.flags.InvalidFlagFormat;
|
||||
import com.sk89q.worldguard.protection.flags.RegionGroup;
|
||||
import com.sk89q.worldguard.protection.flags.RegionGroupFlag;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.util.migrator.MigrationException;
|
||||
import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator;
|
||||
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
@ -62,10 +59,9 @@
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
@ -78,9 +74,6 @@ public final class RegionCommands {
|
||||
|
||||
private final WorldGuardPlugin plugin;
|
||||
|
||||
private MigratorKey migrateDBRequest;
|
||||
private Date migrateDBRequestDate;
|
||||
|
||||
public RegionCommands(WorldGuardPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@ -95,17 +88,6 @@ 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.
|
||||
@ -167,7 +149,7 @@ private static ProtectedRegion findExistingRegion(RegionManager regionManager, S
|
||||
// Validate the id
|
||||
validateRegionId(id, allowGlobal);
|
||||
|
||||
ProtectedRegion region = regionManager.getRegionExact(id);
|
||||
ProtectedRegion region = regionManager.getRegion(id);
|
||||
|
||||
// No region found!
|
||||
if (region == null) {
|
||||
@ -414,9 +396,6 @@ public void define(CommandContext args, CommandSender sender) throws CommandExce
|
||||
|
||||
// Add region
|
||||
regionManager.addRegion(region);
|
||||
|
||||
// Save regions
|
||||
saveRegionsSilently(regionManager, player.getWorld(), sender);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -470,9 +449,6 @@ public void redefine(CommandContext args, CommandSender sender) throws CommandEx
|
||||
sender.sendMessage(ChatColor.YELLOW + "Region '" + id + "' updated with new area.");
|
||||
|
||||
regionManager.addRegion(region); // Replace region
|
||||
|
||||
// Save regions
|
||||
saveRegionsSilently(regionManager, world, sender);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -529,7 +505,7 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep
|
||||
}
|
||||
}
|
||||
|
||||
ProtectedRegion existing = regionManager.getRegionExact(id);
|
||||
ProtectedRegion existing = regionManager.getRegion(id);
|
||||
|
||||
// Check for an existing region
|
||||
if (existing != null) {
|
||||
@ -570,9 +546,6 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep
|
||||
|
||||
// Replace region
|
||||
regionManager.addRegion(region);
|
||||
|
||||
// Save regions
|
||||
saveRegionsSilently(regionManager, player.getWorld(), sender);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -890,9 +863,6 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept
|
||||
printout.appendFlagsList(false);
|
||||
printout.append(")");
|
||||
printout.send(sender);
|
||||
|
||||
// Save regions
|
||||
saveRegionsSilently(regionManager, world, sender);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -927,9 +897,6 @@ public void setPriority(CommandContext args, CommandSender sender)
|
||||
sender.sendMessage(ChatColor.YELLOW
|
||||
+ "Priority of '" + existing.getId() + "' set to "
|
||||
+ priority + " (higher numbers override).");
|
||||
|
||||
// Save regions
|
||||
saveRegionsSilently(regionManager, world, sender);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -994,9 +961,6 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE
|
||||
printout.append(")");
|
||||
}
|
||||
printout.send(sender);
|
||||
|
||||
// Save regions
|
||||
saveRegionsSilently(regionManager, world, sender);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1027,9 +991,6 @@ public void remove(CommandContext args, CommandSender sender) throws CommandExce
|
||||
regionManager.removeRegion(existing.getId());
|
||||
|
||||
sender.sendMessage(ChatColor.YELLOW + "Region '" + existing.getId() + "' removed.");
|
||||
|
||||
// Save regions
|
||||
saveRegionsSilently(regionManager, world, sender);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1063,20 +1024,22 @@ public void load(CommandContext args, final CommandSender sender) throws Command
|
||||
throw new CommandException("No region manager exists for world '" + world.getName() + "'.");
|
||||
}
|
||||
|
||||
ListenableFuture<?> future = manager.load(true);
|
||||
ListenableFuture<?> future = plugin.getExecutorService().submit(new RegionManagerLoad(manager));
|
||||
|
||||
AsyncCommandHelper.wrap(future, plugin, sender)
|
||||
.forRegionDataLoad(world, false);
|
||||
} else {
|
||||
// Load regions for all worlds
|
||||
List<ListenableFuture<?>> futures = new ArrayList<ListenableFuture<?>>();
|
||||
List<RegionManager> managers = new ArrayList<RegionManager>();
|
||||
|
||||
for (World w : Bukkit.getServer().getWorlds()) {
|
||||
RegionManager manager = plugin.getGlobalRegionManager().get(w);
|
||||
if (manager != null) {
|
||||
futures.add(manager.load(true));
|
||||
managers.add(manager);
|
||||
}
|
||||
}
|
||||
ListenableFuture<?> future = Futures.successfulAsList(futures);
|
||||
|
||||
ListenableFuture<?> future = plugin.getExecutorService().submit(new RegionManagerLoad(managers));
|
||||
|
||||
AsyncCommandHelper.wrap(future, plugin, sender)
|
||||
.registerWithSupervisor("Loading regions for all worlds")
|
||||
@ -1118,20 +1081,22 @@ public void save(CommandContext args, final CommandSender sender) throws Command
|
||||
throw new CommandException("No region manager exists for world '" + world.getName() + "'.");
|
||||
}
|
||||
|
||||
ListenableFuture<?> future = manager.save(true);
|
||||
ListenableFuture<?> future = plugin.getExecutorService().submit(new RegionManagerSave(manager));
|
||||
|
||||
AsyncCommandHelper.wrap(future, plugin, sender)
|
||||
.forRegionDataSave(world, false);
|
||||
} else {
|
||||
// Save for all worlds
|
||||
List<ListenableFuture<?>> futures = new ArrayList<ListenableFuture<?>>();
|
||||
List<RegionManager> managers = new ArrayList<RegionManager>();
|
||||
|
||||
for (World w : Bukkit.getServer().getWorlds()) {
|
||||
RegionManager manager = plugin.getGlobalRegionManager().get(w);
|
||||
if (manager != null) {
|
||||
futures.add(manager.save(true));
|
||||
managers.add(manager);
|
||||
}
|
||||
}
|
||||
ListenableFuture<?> future = Futures.successfulAsList(futures);
|
||||
|
||||
ListenableFuture<?> future = plugin.getExecutorService().submit(new RegionManagerSave(managers));
|
||||
|
||||
AsyncCommandHelper.wrap(future, plugin, sender)
|
||||
.registerWithSupervisor("Saving regions for all worlds")
|
||||
@ -1156,7 +1121,8 @@ public void migrateDB(CommandContext args, CommandSender sender) throws CommandE
|
||||
if (!getPermissionModel(sender).mayMigrateRegionStore()) {
|
||||
throw new CommandPermissionsException();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
String from = args.getString(0).toLowerCase().trim();
|
||||
String to = args.getString(1).toLowerCase().trim();
|
||||
|
||||
@ -1202,6 +1168,7 @@ public void migrateDB(CommandContext args, CommandSender sender) throws CommandE
|
||||
|
||||
sender.sendMessage(ChatColor.YELLOW + "Regions have been migrated successfully.\n" +
|
||||
"If you wish to use the destination format as your new backend, please update your config and reload WorldGuard.");
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1246,7 +1213,7 @@ public void migrateUuid(CommandContext args, CommandSender sender) throws Comman
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.YELLOW + "There were no names to migrate.");
|
||||
}
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to save", e);
|
||||
throw new CommandException("Error encountered while saving: " + e.getMessage());
|
||||
} catch (MigrationException e) {
|
||||
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.sk89q.worldguard.protection.managers.RegionManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class RegionManagerLoad implements Callable<Collection<RegionManager>> {
|
||||
|
||||
private final Collection<RegionManager> managers;
|
||||
|
||||
RegionManagerLoad(Collection<RegionManager> managers) {
|
||||
checkNotNull(managers);
|
||||
this.managers = managers;
|
||||
}
|
||||
|
||||
RegionManagerLoad(RegionManager... manager) {
|
||||
this(Arrays.asList(manager));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RegionManager> call() throws IOException {
|
||||
for (RegionManager manager : managers) {
|
||||
manager.load();
|
||||
}
|
||||
|
||||
return managers;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.sk89q.worldguard.protection.managers.RegionManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
class RegionManagerSave implements Callable<Collection<RegionManager>> {
|
||||
|
||||
private final Collection<RegionManager> managers;
|
||||
|
||||
RegionManagerSave(Collection<RegionManager> managers) {
|
||||
checkNotNull(managers);
|
||||
this.managers = managers;
|
||||
}
|
||||
|
||||
RegionManagerSave(RegionManager... manager) {
|
||||
this(Arrays.asList(manager));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RegionManager> call() throws IOException {
|
||||
for (RegionManager manager : managers) {
|
||||
manager.save();
|
||||
}
|
||||
|
||||
return managers;
|
||||
}
|
||||
|
||||
}
|
@ -27,8 +27,8 @@
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
import com.sk89q.worldguard.domains.DefaultDomain;
|
||||
import com.sk89q.worldguard.protection.databases.util.DomainInputResolver;
|
||||
import com.sk89q.worldguard.protection.databases.util.DomainInputResolver.UserLocatorPolicy;
|
||||
import com.sk89q.worldguard.protection.util.DomainInputResolver;
|
||||
import com.sk89q.worldguard.protection.util.DomainInputResolver.UserLocatorPolicy;
|
||||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
@ -72,7 +72,7 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE
|
||||
String id = args.getString(0);
|
||||
|
||||
RegionManager manager = plugin.getGlobalRegionManager().get(world);
|
||||
ProtectedRegion region = manager.getRegion(id);
|
||||
ProtectedRegion region = manager.matchRegion(id);
|
||||
|
||||
if (region == null) {
|
||||
throw new CommandException("Could not find a region by that ID.");
|
||||
@ -104,8 +104,7 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE
|
||||
.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);
|
||||
.thenRespondWith("Region '%s' updated with new members.", "Failed to add new members");
|
||||
}
|
||||
|
||||
@Command(aliases = {"addowner", "addowner", "ao"},
|
||||
@ -134,7 +133,7 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx
|
||||
String id = args.getString(0);
|
||||
|
||||
RegionManager manager = plugin.getGlobalRegionManager().get(world);
|
||||
ProtectedRegion region = manager.getRegion(id);
|
||||
ProtectedRegion region = manager.matchRegion(id);
|
||||
|
||||
if (region == null) {
|
||||
throw new CommandException("Could not find a region by that ID.");
|
||||
@ -179,8 +178,7 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx
|
||||
.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);
|
||||
.thenRespondWith("Region '%s' updated with new owners.", "Failed to add new owners");
|
||||
}
|
||||
|
||||
@Command(aliases = {"removemember", "remmember", "removemem", "remmem", "rm"},
|
||||
@ -209,7 +207,7 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma
|
||||
String id = args.getString(0);
|
||||
|
||||
RegionManager manager = plugin.getGlobalRegionManager().get(world);
|
||||
ProtectedRegion region = manager.getRegion(id);
|
||||
ProtectedRegion region = manager.matchRegion(id);
|
||||
|
||||
if (region == null) {
|
||||
throw new CommandException("Could not find a region by that ID.");
|
||||
@ -253,8 +251,7 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma
|
||||
.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);
|
||||
.thenRespondWith("Region '%s' updated with members removed.", "Failed to remove members");
|
||||
}
|
||||
|
||||
@Command(aliases = {"removeowner", "remowner", "ro"},
|
||||
@ -284,7 +281,7 @@ public void removeOwner(CommandContext args,
|
||||
String id = args.getString(0);
|
||||
|
||||
RegionManager manager = plugin.getGlobalRegionManager().get(world);
|
||||
ProtectedRegion region = manager.getRegion(id);
|
||||
ProtectedRegion region = manager.matchRegion(id);
|
||||
|
||||
if (region == null) {
|
||||
throw new CommandException("Could not find a region by that ID.");
|
||||
@ -328,7 +325,6 @@ public void removeOwner(CommandContext args,
|
||||
.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);
|
||||
.thenRespondWith("Region '%s' updated with owners removed.", "Failed to remove owners");
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ public void appendBasics() {
|
||||
|
||||
builder.append(ChatColor.GRAY);
|
||||
builder.append(" (type=");
|
||||
builder.append(region.getTypeName());
|
||||
builder.append(region.getType().getName());
|
||||
|
||||
builder.append(ChatColor.GRAY);
|
||||
builder.append(", priority=");
|
||||
|
@ -23,6 +23,7 @@
|
||||
import com.sk89q.squirrelid.Profile;
|
||||
import com.sk89q.squirrelid.cache.ProfileCache;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.util.ChangeTracked;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
@ -37,7 +38,7 @@
|
||||
/**
|
||||
* A combination of a {@link PlayerDomain} and a {@link GroupDomain}.
|
||||
*/
|
||||
public class DefaultDomain implements Domain {
|
||||
public class DefaultDomain implements Domain, ChangeTracked {
|
||||
|
||||
private PlayerDomain playerDomain = new PlayerDomain();
|
||||
private GroupDomain groupDomain = new GroupDomain();
|
||||
@ -84,9 +85,7 @@ public void setGroupDomain(GroupDomain groupDomain) {
|
||||
* Add the given player to the domain, identified by the player's name.
|
||||
*
|
||||
* @param name the name of the player
|
||||
* @deprecated names are deprecated in favor of UUIDs in MC 1.7+
|
||||
*/
|
||||
@Deprecated
|
||||
public void addPlayer(String name) {
|
||||
playerDomain.addPlayer(name);
|
||||
}
|
||||
@ -95,9 +94,7 @@ public void addPlayer(String name) {
|
||||
* Remove the given player from the domain, identified by the player's name.
|
||||
*
|
||||
* @param name the name of the player
|
||||
* @deprecated names are deprecated in favor of UUIDs in MC 1.7+
|
||||
*/
|
||||
@Deprecated
|
||||
public void removePlayer(String name) {
|
||||
playerDomain.removePlayer(name);
|
||||
}
|
||||
@ -179,9 +176,7 @@ public void removeAll(DefaultDomain other) {
|
||||
* Get the set of player names.
|
||||
*
|
||||
* @return the set of player names
|
||||
* @deprecated names are deprecated in favor of UUIDs in MC 1.7+
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<String> getPlayers() {
|
||||
return playerDomain.getPlayers();
|
||||
}
|
||||
@ -252,7 +247,6 @@ public void removeAll() {
|
||||
clear();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public String toPlayersString() {
|
||||
return toPlayersString(null);
|
||||
}
|
||||
@ -340,4 +334,14 @@ public String toUserFriendlyString(ProfileCache cache) {
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return playerDomain.isDirty() || groupDomain.isDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirty(boolean dirty) {
|
||||
playerDomain.setDirty(dirty);
|
||||
groupDomain.setDirty(dirty);
|
||||
}
|
||||
}
|
||||
|
@ -20,19 +20,23 @@
|
||||
package com.sk89q.worldguard.domains;
|
||||
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.util.ChangeTracked;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Contains groups in a domain.
|
||||
*/
|
||||
public class GroupDomain implements Domain {
|
||||
public class GroupDomain implements Domain, ChangeTracked {
|
||||
|
||||
private final Set<String> groups = new CopyOnWriteArraySet<String>();
|
||||
private boolean dirty = true;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
@ -43,11 +47,11 @@ public GroupDomain() {
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param groupsy an array of groups
|
||||
* @param groups an array of groups
|
||||
*/
|
||||
public GroupDomain(String[] groupsy) {
|
||||
checkNotNull(groupsy);
|
||||
for (String group : groupsy) {
|
||||
public GroupDomain(String[] groups) {
|
||||
checkNotNull(groups);
|
||||
for (String group : groups) {
|
||||
addGroup(group);
|
||||
}
|
||||
}
|
||||
@ -59,7 +63,9 @@ public GroupDomain(String[] groupsy) {
|
||||
*/
|
||||
public void addGroup(String name) {
|
||||
checkNotNull(name);
|
||||
groups.add(name.toLowerCase());
|
||||
checkArgument(!name.trim().isEmpty(), "Can't add empty group name");
|
||||
setDirty(true);
|
||||
groups.add(name.trim().toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,7 +75,8 @@ public void addGroup(String name) {
|
||||
*/
|
||||
public void removeGroup(String name) {
|
||||
checkNotNull(name);
|
||||
groups.remove(name.toLowerCase());
|
||||
setDirty(true);
|
||||
groups.remove(name.trim().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -90,7 +97,7 @@ public boolean contains(LocalPlayer player) {
|
||||
* @return the set of group names
|
||||
*/
|
||||
public Set<String> getGroups() {
|
||||
return groups;
|
||||
return Collections.unmodifiableSet(groups);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -113,4 +120,14 @@ public void clear() {
|
||||
groups.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirty(boolean dirty) {
|
||||
this.dirty = dirty;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,20 +20,24 @@
|
||||
package com.sk89q.worldguard.domains;
|
||||
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.util.ChangeTracked;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Stores players (only) in a domain.
|
||||
*/
|
||||
public class PlayerDomain implements Domain {
|
||||
public class PlayerDomain implements Domain, ChangeTracked {
|
||||
|
||||
private final Set<UUID> uniqueIds = new CopyOnWriteArraySet<UUID>();
|
||||
private final Set<String> names = new CopyOnWriteArraySet<String>();
|
||||
private boolean dirty = true;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
@ -58,12 +62,15 @@ public PlayerDomain(String[] names) {
|
||||
* Add the given player to the domain, identified by the player's name.
|
||||
*
|
||||
* @param name the name of the player
|
||||
* @deprecated names are deprecated in favor of UUIDs in MC 1.7+
|
||||
*/
|
||||
@Deprecated
|
||||
public void addPlayer(String name) {
|
||||
checkNotNull(name);
|
||||
names.add(name.toLowerCase());
|
||||
checkArgument(!name.trim().isEmpty(), "Can't add empty player name");
|
||||
setDirty(true);
|
||||
names.add(name.trim().toLowerCase());
|
||||
// Trim because some names contain spaces (previously valid Minecraft
|
||||
// names) and we cannot store these correctly in the SQL storage
|
||||
// implementations
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,6 +80,7 @@ public void addPlayer(String name) {
|
||||
*/
|
||||
public void addPlayer(UUID uniqueId) {
|
||||
checkNotNull(uniqueId);
|
||||
setDirty(true);
|
||||
uniqueIds.add(uniqueId);
|
||||
}
|
||||
|
||||
@ -83,6 +91,7 @@ public void addPlayer(UUID uniqueId) {
|
||||
*/
|
||||
public void addPlayer(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
setDirty(true);
|
||||
addPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
@ -90,12 +99,11 @@ public void addPlayer(LocalPlayer player) {
|
||||
* Remove the given player from the domain, identified by the player's name.
|
||||
*
|
||||
* @param name the name of the player
|
||||
* @deprecated names are deprecated in favor of UUIDs in MC 1.7+
|
||||
*/
|
||||
@Deprecated
|
||||
public void removePlayer(String name) {
|
||||
checkNotNull(name);
|
||||
names.remove(name.toLowerCase());
|
||||
setDirty(true);
|
||||
names.remove(name.trim().toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -116,8 +124,8 @@ public void removePlayer(UUID uuid) {
|
||||
*/
|
||||
public void removePlayer(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
names.remove(player.getName().toLowerCase());
|
||||
uniqueIds.remove(player.getUniqueId());
|
||||
removePlayer(player.getName());
|
||||
removePlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -130,11 +138,9 @@ public boolean contains(LocalPlayer player) {
|
||||
* Get the set of player names.
|
||||
*
|
||||
* @return the set of player names
|
||||
* @deprecated names are deprecated in favor of UUIDs in MC 1.7+
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<String> getPlayers() {
|
||||
return names;
|
||||
return Collections.unmodifiableSet(names);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,7 +149,7 @@ public Set<String> getPlayers() {
|
||||
* @return the set of player UUIDs
|
||||
*/
|
||||
public Set<UUID> getUniqueIds() {
|
||||
return uniqueIds;
|
||||
return Collections.unmodifiableSet(uniqueIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -155,7 +161,7 @@ public boolean contains(UUID uniqueId) {
|
||||
@Override
|
||||
public boolean contains(String playerName) {
|
||||
checkNotNull(playerName);
|
||||
return names.contains(playerName.toLowerCase());
|
||||
return names.contains(playerName.trim().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -169,4 +175,14 @@ public void clear() {
|
||||
names.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirty(boolean dirty) {
|
||||
this.dirty = dirty;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,67 +20,87 @@
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.flags.*;
|
||||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.protection.flags.RegionGroup;
|
||||
import com.sk89q.worldguard.protection.flags.RegionGroupFlag;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag.State;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import java.util.*;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Represents a set of regions for a particular point or area and the rules
|
||||
* that are represented by that set. An instance of this can be used to
|
||||
* query the value of a flag or check if a player can build in the respective
|
||||
* region or point. This object contains the list of applicable regions and so
|
||||
* the expensive search of regions that are in the desired area has already
|
||||
* been completed.
|
||||
*
|
||||
* @author sk89q
|
||||
* Represents the effective set of flags, owners, and members for a given
|
||||
* spatial query.
|
||||
*
|
||||
* <p>An instance of this can be created using the spatial query methods
|
||||
* available on {@link RegionManager}.</p>
|
||||
*/
|
||||
public class ApplicableRegionSet implements Iterable<ProtectedRegion> {
|
||||
|
||||
private Collection<ProtectedRegion> applicable;
|
||||
private ProtectedRegion globalRegion;
|
||||
private final SortedSet<ProtectedRegion> applicable;
|
||||
@Nullable
|
||||
private final ProtectedRegion globalRegion;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
*
|
||||
* <p>A sorted set will be created to include the collection of regions.</p>
|
||||
*
|
||||
* @param applicable the regions contained in this set
|
||||
* @param globalRegion the global region, set aside for special handling.
|
||||
*/
|
||||
public ApplicableRegionSet(Collection<ProtectedRegion> applicable, @Nullable ProtectedRegion globalRegion) {
|
||||
this(new TreeSet<ProtectedRegion>(checkNotNull(applicable)), globalRegion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
*
|
||||
* @param applicable The regions contained in this set
|
||||
* @param globalRegion The global region, set aside for special handling.
|
||||
* @param applicable the regions contained in this set
|
||||
* @param globalRegion the global region, set aside for special handling.
|
||||
*/
|
||||
public ApplicableRegionSet(Collection<ProtectedRegion> applicable,
|
||||
ProtectedRegion globalRegion) {
|
||||
public ApplicableRegionSet(SortedSet<ProtectedRegion> applicable, @Nullable ProtectedRegion globalRegion) {
|
||||
checkNotNull(applicable);
|
||||
this.applicable = applicable;
|
||||
this.globalRegion = globalRegion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player can build in an area.
|
||||
* Test whether a player can build in an area.
|
||||
*
|
||||
* @param player The player to check
|
||||
* @return build ability
|
||||
*/
|
||||
public boolean canBuild(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
return internalGetState(DefaultFlag.BUILD, player, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether the construct flag evaluates true for the given player.
|
||||
*
|
||||
* @param player the player
|
||||
* @return true if true
|
||||
*/
|
||||
public boolean canConstruct(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
final RegionGroup flag = getFlag(DefaultFlag.CONSTRUCT, player);
|
||||
return RegionGroupFlag.isMember(this, flag, player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player can use buttons and such in an area.
|
||||
*
|
||||
* @param player The player to check
|
||||
* @return able to use items
|
||||
* @deprecated This method seems to be the opposite of its name
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean canUse(LocalPlayer player) {
|
||||
return !allows(DefaultFlag.USE, player)
|
||||
&& !canBuild(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of a state flag. This cannot be used for the build flag.
|
||||
*
|
||||
@ -89,9 +109,12 @@ public boolean canUse(LocalPlayer player) {
|
||||
* @throws IllegalArgumentException if the build flag is given
|
||||
*/
|
||||
public boolean allows(StateFlag flag) {
|
||||
checkNotNull(flag);
|
||||
|
||||
if (flag == DefaultFlag.BUILD) {
|
||||
throw new IllegalArgumentException("Can't use build flag with allows()");
|
||||
}
|
||||
|
||||
return internalGetState(flag, null, null);
|
||||
}
|
||||
|
||||
@ -103,7 +126,9 @@ public boolean allows(StateFlag flag) {
|
||||
* @return whether the state is allows for it
|
||||
* @throws IllegalArgumentException if the build flag is given
|
||||
*/
|
||||
public boolean allows(StateFlag flag, LocalPlayer player) {
|
||||
public boolean allows(StateFlag flag, @Nullable LocalPlayer player) {
|
||||
checkNotNull(flag);
|
||||
|
||||
if (flag == DefaultFlag.BUILD) {
|
||||
throw new IllegalArgumentException("Can't use build flag with allows()");
|
||||
}
|
||||
@ -111,12 +136,14 @@ public boolean allows(StateFlag flag, LocalPlayer player) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a player is an owner of all regions in this set.
|
||||
* Test whether a player is an owner of all regions in this set.
|
||||
*
|
||||
* @param player player
|
||||
* @param player the player
|
||||
* @return whether the player is an owner of all regions
|
||||
*/
|
||||
public boolean isOwnerOfAll(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
|
||||
for (ProtectedRegion region : applicable) {
|
||||
if (!region.isOwner(player)) {
|
||||
return false;
|
||||
@ -127,13 +154,14 @@ public boolean isOwnerOfAll(LocalPlayer player) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a player is an owner or member of all regions in
|
||||
* this set.
|
||||
* Test whether a player is an owner or member of all regions in this set.
|
||||
*
|
||||
* @param player player
|
||||
* @param player the player
|
||||
* @return whether the player is a member of all regions
|
||||
*/
|
||||
public boolean isMemberOfAll(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
|
||||
for (ProtectedRegion region : applicable) {
|
||||
if (!region.isMember(player)) {
|
||||
return false;
|
||||
@ -144,15 +172,16 @@ public boolean isMemberOfAll(LocalPlayer player) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a flag is permitted.
|
||||
* Test whether a flag tests true.
|
||||
*
|
||||
* @param flag flag to check
|
||||
* @param player null to not check owners and members
|
||||
* @param groupPlayer player to use for the group flag check
|
||||
* @param flag the flag to check
|
||||
* @param player the player, or null to not check owners and members
|
||||
* @param groupPlayer a player to use for the group flag check
|
||||
* @return the allow/deny state for the flag
|
||||
*/
|
||||
private boolean internalGetState(StateFlag flag, LocalPlayer player,
|
||||
LocalPlayer groupPlayer) {
|
||||
private boolean internalGetState(StateFlag flag, @Nullable LocalPlayer player, @Nullable LocalPlayer groupPlayer) {
|
||||
checkNotNull(flag);
|
||||
|
||||
boolean found = false;
|
||||
boolean hasFlagDefined = false;
|
||||
boolean allowed = false; // Used for ALLOW override
|
||||
@ -254,6 +283,7 @@ private boolean internalGetState(StateFlag flag, LocalPlayer player,
|
||||
if (player != null) {
|
||||
hasFlagDefined = true;
|
||||
|
||||
//noinspection StatementWithEmptyBody
|
||||
if (hasCleared.contains(region)) {
|
||||
// Already cleared, so do nothing
|
||||
} else {
|
||||
@ -269,19 +299,17 @@ private boolean internalGetState(StateFlag flag, LocalPlayer player,
|
||||
found = true;
|
||||
}
|
||||
|
||||
return !found ? def :
|
||||
(allowed || (player != null && needsClear.size() == 0));
|
||||
return !found ? def : (allowed || (player != null && needsClear.isEmpty()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a region's parents for isFlagAllowed().
|
||||
*
|
||||
* @param needsClear The regions that should be cleared
|
||||
* @param hasCleared The regions already cleared
|
||||
* @param region The region to start from
|
||||
* @param needsClear the regions that should be cleared
|
||||
* @param hasCleared the regions already cleared
|
||||
* @param region the region to start from
|
||||
*/
|
||||
private void clearParents(Set<ProtectedRegion> needsClear,
|
||||
Set<ProtectedRegion> hasCleared, ProtectedRegion region) {
|
||||
private void clearParents(Set<ProtectedRegion> needsClear, Set<ProtectedRegion> hasCleared, ProtectedRegion region) {
|
||||
ProtectedRegion parent = region.getParent();
|
||||
|
||||
while (parent != null) {
|
||||
@ -294,10 +322,13 @@ private void clearParents(Set<ProtectedRegion> needsClear,
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getFlag(com.sk89q.worldguard.protection.flags.Flag, com.sk89q.worldguard.LocalPlayer)
|
||||
* @param flag flag to check
|
||||
* @return value of the flag
|
||||
* Gets the value of a flag. Do not use this for state flags
|
||||
* (use {@link #allows(StateFlag, LocalPlayer)} for that).
|
||||
*
|
||||
* @param flag the flag to check
|
||||
* @return value of the flag, which may be null
|
||||
*/
|
||||
@Nullable
|
||||
public <T extends Flag<V>, V> V getFlag(T flag) {
|
||||
return getFlag(flag, null);
|
||||
}
|
||||
@ -308,10 +339,13 @@ public <T extends Flag<V>, V> V getFlag(T flag) {
|
||||
*
|
||||
* @param flag flag to check
|
||||
* @param groupPlayer player to check {@link RegionGroup}s against
|
||||
* @return value of the flag
|
||||
* @return value of the flag, which may be null
|
||||
* @throws IllegalArgumentException if a StateFlag is given
|
||||
*/
|
||||
public <T extends Flag<V>, V> V getFlag(T flag, LocalPlayer groupPlayer) {
|
||||
@Nullable
|
||||
public <T extends Flag<V>, V> V getFlag(T flag, @Nullable LocalPlayer groupPlayer) {
|
||||
checkNotNull(flag);
|
||||
|
||||
/*
|
||||
if (flag instanceof StateFlag) {
|
||||
throw new IllegalArgumentException("Cannot use StateFlag with getFlag()");
|
||||
@ -341,6 +375,7 @@ public <T extends Flag<V>, V> V getFlag(T flag, LocalPlayer groupPlayer) {
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection StatementWithEmptyBody
|
||||
if (hasCleared.contains(region)) {
|
||||
// Already cleared, so do nothing
|
||||
} else if (region.getFlag(flag) != null) {
|
||||
@ -388,16 +423,15 @@ private void clearParents(Map<ProtectedRegion, ?> needsClear,
|
||||
/**
|
||||
* Get the number of regions that are included.
|
||||
*
|
||||
* @return the size of this ApplicbleRegionSet
|
||||
* @return the number of contained regions
|
||||
*/
|
||||
public int size() {
|
||||
return applicable.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator of affected regions.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public Iterator<ProtectedRegion> iterator() {
|
||||
return applicable.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,289 +24,74 @@
|
||||
import com.sk89q.worldguard.bukkit.ConfigurationManager;
|
||||
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.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;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector;
|
||||
|
||||
/**
|
||||
* This class keeps track of region information for every world. It loads
|
||||
* world region information as needed.
|
||||
*
|
||||
* @author sk89q
|
||||
* @author Redecouverte
|
||||
*/
|
||||
public class GlobalRegionManager {
|
||||
|
||||
/**
|
||||
* Reference to the plugin.
|
||||
*/
|
||||
private WorldGuardPlugin plugin;
|
||||
private final WorldGuardPlugin plugin;
|
||||
private final ConfigurationManager config;
|
||||
private final ManagerContainer container;
|
||||
|
||||
/**
|
||||
* Reference to the global configuration.
|
||||
*/
|
||||
private ConfigurationManager config;
|
||||
|
||||
/**
|
||||
* Map of managers per-world.
|
||||
*/
|
||||
private ConcurrentHashMap<String, RegionManager> managers;
|
||||
|
||||
/**
|
||||
* Stores the list of modification dates for the world files. This allows
|
||||
* WorldGuard to reload files as needed.
|
||||
*/
|
||||
private HashMap<String, Long> lastModified;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
*
|
||||
* @param plugin The plugin instance
|
||||
*/
|
||||
public GlobalRegionManager(WorldGuardPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
config = plugin.getGlobalStateManager();
|
||||
managers = new ConcurrentHashMap<String, RegionManager>();
|
||||
lastModified = new HashMap<String, Long>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload region information.
|
||||
*/
|
||||
public void unload() {
|
||||
managers.clear();
|
||||
lastModified.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path for a world's regions file.
|
||||
*
|
||||
* @param name The name of the world
|
||||
* @return The region file path for a world's region file
|
||||
*/
|
||||
protected File getPath(String name) {
|
||||
return new File(plugin.getDataFolder(),
|
||||
"worlds" + File.separator + name + File.separator + "regions.yml");
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload region information for a world.
|
||||
*
|
||||
* @param name The name of the world to unload
|
||||
*/
|
||||
public void unload(String name) {
|
||||
RegionManager manager = managers.remove(name);
|
||||
|
||||
if (manager != null) {
|
||||
lastModified.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload all region information.
|
||||
*/
|
||||
public void unloadAll() {
|
||||
managers.clear();
|
||||
lastModified.clear();
|
||||
container = new ManagerContainer(config);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RegionManager load(World world) {
|
||||
RegionManager manager = create(world);
|
||||
if (manager != null) {
|
||||
managers.put(world.getName(), manager);
|
||||
}
|
||||
return manager;
|
||||
return container.load(world.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load region information for a world.
|
||||
*
|
||||
* @param world The world to load a RegionManager for
|
||||
* @return the loaded RegionManager
|
||||
*/
|
||||
public RegionManager create(World world) {
|
||||
String name = world.getName();
|
||||
boolean sql = config.useSqlDatabase;
|
||||
ProtectionDatabase database;
|
||||
File file = null;
|
||||
|
||||
try {
|
||||
if (!sql) {
|
||||
file = getPath(name);
|
||||
database = new YAMLDatabase(file, plugin.getLogger());
|
||||
|
||||
// Store the last modification date so we can track changes
|
||||
lastModified.put(name, file.lastModified());
|
||||
} else {
|
||||
database = new MySQLDatabase(config, name, plugin.getLogger());
|
||||
}
|
||||
|
||||
// Create a manager
|
||||
RegionManager manager = new PRTreeRegionManager(database);
|
||||
manager.load();
|
||||
|
||||
if (plugin.getGlobalStateManager().get(world).summaryOnStart) {
|
||||
plugin.getLogger().info(manager.getRegions().size()
|
||||
+ " regions loaded for '" + name + "'");
|
||||
}
|
||||
|
||||
return manager;
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
String logStr = "Failed to load regions from ";
|
||||
if (sql) {
|
||||
logStr += "SQL Database <" + config.sqlDsn + "> ";
|
||||
} else {
|
||||
logStr += "file \"" + file + "\" ";
|
||||
}
|
||||
|
||||
plugin.getLogger().log(Level.SEVERE, logStr + " : " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
} catch (FileNotFoundException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Error loading regions for world \""
|
||||
+ name + "\": " + e.toString() + "\n\t" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// @TODO: THIS CREATES PROBLEMS!!one!!1!!eleven!!1!!!
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preloads region managers for all worlds.
|
||||
*/
|
||||
public void preload() {
|
||||
// Load regions
|
||||
for (World world : plugin.getServer().getWorlds()) {
|
||||
load(world);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the region information from file when region databases
|
||||
* have changed.
|
||||
*/
|
||||
public void reloadChanged() {
|
||||
if (config.useSqlDatabase) return;
|
||||
|
||||
for (String name : managers.keySet()) {
|
||||
File file = getPath(name);
|
||||
|
||||
Long oldDate = lastModified.get(name);
|
||||
|
||||
if (oldDate == null) {
|
||||
oldDate = 0L;
|
||||
}
|
||||
|
||||
try {
|
||||
if (file.lastModified() > oldDate) {
|
||||
World world = plugin.getServer().getWorld(name);
|
||||
|
||||
if (world != null) {
|
||||
load(world);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
public void unload(String name) {
|
||||
container.unload(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the region manager for a particular world.
|
||||
*
|
||||
* @param world The world to get a RegionManager for
|
||||
* @return The region manager.
|
||||
*/
|
||||
public void unload() {
|
||||
container.unloadAll();
|
||||
}
|
||||
|
||||
public void unloadAll() {
|
||||
container.unloadAll();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RegionManager get(World world) {
|
||||
RegionManager manager = managers.get(world.getName());
|
||||
RegionManager newManager = null;
|
||||
|
||||
while (manager == null) {
|
||||
if (newManager == null) {
|
||||
newManager = create(world);
|
||||
}
|
||||
managers.putIfAbsent(world.getName(), newManager);
|
||||
manager = managers.get(world.getName());
|
||||
}
|
||||
|
||||
return manager;
|
||||
return container.get(world.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of loaded region managers.
|
||||
*
|
||||
* @return an unmodifiable list
|
||||
*/
|
||||
public List<RegionManager> getLoaded() {
|
||||
List<RegionManager> list = new ArrayList<RegionManager>();
|
||||
for (RegionManager manager : managers.values()) {
|
||||
if (manager != null) {
|
||||
list.add(manager);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
return container.getLoaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the player can bypass.
|
||||
*
|
||||
* @param player The player to check
|
||||
* @param world The world to check for
|
||||
* @return Whether {@code player} has bypass permission for {@code world}
|
||||
*/
|
||||
public boolean hasBypass(LocalPlayer player, World world) {
|
||||
return player.hasPermission("worldguard.region.bypass."
|
||||
+ world.getName());
|
||||
return player.hasPermission("worldguard.region.bypass." + world.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the player can bypass.
|
||||
*
|
||||
* @param player The player to check
|
||||
* @param world The world to check
|
||||
* @return Whether {@code player} has bypass permission for {@code world}
|
||||
*/
|
||||
public boolean hasBypass(Player player, World world) {
|
||||
return plugin.hasPermission(player, "worldguard.region.bypass."
|
||||
+ world.getName());
|
||||
return plugin.hasPermission(player, "worldguard.region.bypass." + world.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a player has permission to build at a block.
|
||||
*
|
||||
* @param player The player to check
|
||||
* @param block The block to check at
|
||||
* @return Whether {@code player} can build at {@code block}'s location
|
||||
*/
|
||||
public boolean canBuild(Player player, Block block) {
|
||||
return canBuild(player, block.getLocation());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a player has permission to build at a location.
|
||||
*
|
||||
* @param player The player to check
|
||||
* @param loc The location to check
|
||||
* @return Whether {@code player} can build at {@code loc}
|
||||
*/
|
||||
public boolean canBuild(Player player, Location loc) {
|
||||
World world = loc.getWorld();
|
||||
WorldConfiguration worldConfig = config.get(world);
|
||||
@ -320,8 +105,7 @@ public boolean canBuild(Player player, Location loc) {
|
||||
if (!hasBypass(player, world)) {
|
||||
RegionManager mgr = get(world);
|
||||
|
||||
if (!mgr.getApplicableRegions(BukkitUtil.toVector(loc))
|
||||
.canBuild(localPlayer)) {
|
||||
if (mgr != null && !mgr.getApplicableRegions(BukkitUtil.toVector(loc)).canBuild(localPlayer)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -346,39 +130,25 @@ public boolean canConstruct(Player player, Location loc) {
|
||||
if (!hasBypass(player, world)) {
|
||||
RegionManager mgr = get(world);
|
||||
|
||||
final ApplicableRegionSet applicableRegions = mgr.getApplicableRegions(BukkitUtil.toVector(loc));
|
||||
if (!applicableRegions.canBuild(localPlayer)) {
|
||||
return false;
|
||||
}
|
||||
if (!applicableRegions.canConstruct(localPlayer)) {
|
||||
return false;
|
||||
if (mgr != null) {
|
||||
final ApplicableRegionSet applicableRegions = mgr.getApplicableRegions(BukkitUtil.toVector(loc));
|
||||
if (!applicableRegions.canBuild(localPlayer)) {
|
||||
return false;
|
||||
}
|
||||
if (!applicableRegions.canConstruct(localPlayer)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see whether a flag is allowed.
|
||||
*
|
||||
* @see #allows(com.sk89q.worldguard.protection.flags.StateFlag, org.bukkit.Location, com.sk89q.worldguard.LocalPlayer)
|
||||
* @param flag The flag to check
|
||||
* @param loc The location to check the flag at
|
||||
* @return Whether the flag is allowed
|
||||
*/
|
||||
public boolean allows(StateFlag flag, Location loc) {
|
||||
return allows(flag, loc, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see whether a flag is allowed.
|
||||
*
|
||||
* @param flag The flag to check
|
||||
* @param loc The location to check the flag at
|
||||
* @param player The player to check for the flag's {@link com.sk89q.worldguard.protection.flags.RegionGroup}
|
||||
* @return Whether the flag is allowed
|
||||
*/
|
||||
public boolean allows(StateFlag flag, Location loc, LocalPlayer player) {
|
||||
public boolean allows(StateFlag flag, Location loc, @Nullable LocalPlayer player) {
|
||||
World world = loc.getWorld();
|
||||
WorldConfiguration worldConfig = config.get(world);
|
||||
|
||||
@ -387,6 +157,7 @@ public boolean allows(StateFlag flag, Location loc, LocalPlayer player) {
|
||||
}
|
||||
|
||||
RegionManager mgr = get(world);
|
||||
return mgr.getApplicableRegions(toVector(loc)).allows(flag, player);
|
||||
return mgr == null || mgr.getApplicableRegions(toVector(loc)).allows(flag, player);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.sk89q.worldguard.bukkit.ConfigurationManager;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex;
|
||||
import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.DriverType;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver;
|
||||
import com.sk89q.worldguard.util.Normal;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Manages different {@link RegionManager}s for different worlds or dimensions.
|
||||
*/
|
||||
class ManagerContainer {
|
||||
|
||||
private static final Logger log = Logger.getLogger(ManagerContainer.class.getCanonicalName());
|
||||
private static final int SAVE_INTERVAL = 1000 * 30;
|
||||
|
||||
private final ConcurrentMap<Normal, RegionManager> mapping = new ConcurrentHashMap<Normal, RegionManager>();
|
||||
private final Object lock = new Object();
|
||||
private final EnumMap<DriverType, RegionStoreDriver> drivers = new EnumMap<DriverType, RegionStoreDriver>(DriverType.class);
|
||||
private final RegionStoreDriver defaultDriver;
|
||||
private final Supplier<? extends ConcurrentRegionIndex> indexFactory = new PriorityRTreeIndex.Factory();
|
||||
private final Timer timer = new Timer();
|
||||
|
||||
ManagerContainer(ConfigurationManager config) {
|
||||
checkNotNull(config);
|
||||
|
||||
for (DriverType type : DriverType.values()) {
|
||||
drivers.put(type, type.create(config));
|
||||
}
|
||||
|
||||
if (config.useSqlDatabase) {
|
||||
defaultDriver = drivers.get(DriverType.SQL);
|
||||
} else {
|
||||
defaultDriver = drivers.get(DriverType.YAML);
|
||||
}
|
||||
|
||||
timer.schedule(new BackgroundSaver(), SAVE_INTERVAL, SAVE_INTERVAL);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RegionManager load(String name) {
|
||||
checkNotNull(name);
|
||||
|
||||
Normal normal = Normal.normal(name);
|
||||
|
||||
synchronized (lock) {
|
||||
RegionManager manager = mapping.get(normal);
|
||||
if (manager != null) {
|
||||
return manager;
|
||||
} else {
|
||||
try {
|
||||
manager = createAndLoad(name);
|
||||
mapping.put(normal, manager);
|
||||
return manager;
|
||||
} catch (IOException e) {
|
||||
log.log(Level.WARNING, "Failed to load the region data for '" + name + "'", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RegionManager createAndLoad(String name) throws IOException {
|
||||
RegionStore store = defaultDriver.get(name);
|
||||
RegionManager manager = new RegionManager(store, indexFactory);
|
||||
manager.load(); // Try loading, although it may fail
|
||||
return manager;
|
||||
}
|
||||
|
||||
public void unload(String name) {
|
||||
checkNotNull(name);
|
||||
|
||||
Normal normal = Normal.normal(name);
|
||||
|
||||
synchronized (lock) {
|
||||
RegionManager manager = mapping.get(normal);
|
||||
if (manager != null) {
|
||||
try {
|
||||
manager.save();
|
||||
} catch (IOException e) {
|
||||
log.log(Level.WARNING, "Failed to save the region data for '" + name + "'", e);
|
||||
}
|
||||
}
|
||||
mapping.remove(normal);
|
||||
}
|
||||
}
|
||||
|
||||
public void unloadAll() {
|
||||
synchronized (lock) {
|
||||
for (Map.Entry<Normal, RegionManager> entry : mapping.entrySet()) {
|
||||
String name = entry.getKey().toString();
|
||||
RegionManager manager = entry.getValue();
|
||||
try {
|
||||
manager.save();
|
||||
} catch (IOException e) {
|
||||
log.log(Level.WARNING, "Failed to save the region data for '" + name + "' while unloading the data for all worlds", e);
|
||||
}
|
||||
}
|
||||
|
||||
mapping.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RegionManager get(String name) {
|
||||
checkNotNull(name);
|
||||
return mapping.get(Normal.normal(name));
|
||||
}
|
||||
|
||||
public List<RegionManager> getLoaded() {
|
||||
return Collections.unmodifiableList(new ArrayList<RegionManager>(mapping.values()));
|
||||
}
|
||||
|
||||
private class BackgroundSaver extends TimerTask {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (lock) {
|
||||
// Block loading of new region managers
|
||||
|
||||
for (Map.Entry<Normal, RegionManager> entry : mapping.entrySet()) {
|
||||
String name = entry.getKey().toString();
|
||||
RegionManager manager = entry.getValue();
|
||||
try {
|
||||
manager.saveChanges();
|
||||
} catch (IOException e) {
|
||||
log.log(Level.WARNING, "Failed to save the region data for '" + name + "' during a periodical save", e);
|
||||
} catch (Exception e) {
|
||||
log.log(Level.WARNING, "An expected error occurred during a periodical save", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -19,6 +19,12 @@
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
/**
|
||||
* Thrown when an intersection between two different types of regions is not
|
||||
* supported.
|
||||
*
|
||||
* @deprecated no longer utilized
|
||||
*/
|
||||
@Deprecated
|
||||
public class UnsupportedIntersectionException extends Exception {
|
||||
private static final long serialVersionUID = 6423189392345575148L;
|
||||
}
|
||||
|
@ -1,226 +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.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.sk89q.odeum.concurrent.EvenMoreExecutors;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* An abstract implementation of a {@code RegionManager} that supports
|
||||
* asynchronously loading and saving region data while only allowing one
|
||||
* single operation (either load or save) occurring at a given time.
|
||||
*/
|
||||
public abstract class AbstractAsynchronousDatabase extends AbstractProtectionDatabase {
|
||||
|
||||
private static final Logger log = Logger.getLogger(AbstractAsynchronousDatabase.class.getName());
|
||||
|
||||
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 4));
|
||||
private final Object lock = new Object();
|
||||
private QueuedTask lastSave;
|
||||
private QueuedTask lastLoad;
|
||||
|
||||
@Override
|
||||
public final void load() throws ProtectionDatabaseException, RejectedExecutionException {
|
||||
blockOnSave(submitLoadTask(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ListenableFuture<?> load(RegionManager manager, boolean async) throws RejectedExecutionException {
|
||||
ListenableFuture<?> future = submitLoadTask(manager);
|
||||
if (!async) {
|
||||
blockOnLoad(future);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void save() throws ProtectionDatabaseException, RejectedExecutionException {
|
||||
blockOnSave(submitSaveTask(new HashMap<String, ProtectedRegion>(getRegions())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ListenableFuture<?> save(RegionManager manager, boolean async) throws RejectedExecutionException {
|
||||
ListenableFuture<?> future = submitSaveTask(new HashMap<String, ProtectedRegion>(manager.getRegions()));
|
||||
if (!async) {
|
||||
blockOnSave(future);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a load task and return a future for it.
|
||||
*
|
||||
* <p>If a load task is already queued then that load task's future will
|
||||
* be returned.</p>
|
||||
*
|
||||
* @param manager the manager
|
||||
* @return a future
|
||||
* @throws RejectedExecutionException thrown if there are too many load/save tasks queued
|
||||
*/
|
||||
private ListenableFuture<?> submitLoadTask(@Nullable final RegionManager manager) throws RejectedExecutionException {
|
||||
synchronized (lock) {
|
||||
lastSave = null; // Void the pending queued save so that any future
|
||||
// save() calls will submit a brand new save task
|
||||
|
||||
QueuedTask last = lastLoad;
|
||||
|
||||
// Check if there is already a queued task that has not yet started
|
||||
// that we can return, rather than queue yet another task
|
||||
if (last != null && !last.started) {
|
||||
return last.future;
|
||||
} else {
|
||||
// Submit the task
|
||||
final QueuedTask task = new QueuedTask();
|
||||
task.future = executor.submit(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
task.started = true;
|
||||
|
||||
performLoad();
|
||||
if (manager != null) {
|
||||
manager.setRegions(getRegions());
|
||||
}
|
||||
return AbstractAsynchronousDatabase.this;
|
||||
}
|
||||
});
|
||||
|
||||
this.lastLoad = task;
|
||||
|
||||
return task.future;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a save task and return a future for it.
|
||||
*
|
||||
* <p>If a save task is already queued then that save task's future will
|
||||
* be returned.</p>
|
||||
*
|
||||
* @param entries a map of regions
|
||||
* @return a future
|
||||
* @throws RejectedExecutionException thrown if there are too many load/save tasks queued
|
||||
*/
|
||||
private ListenableFuture<?> submitSaveTask(final Map<String, ProtectedRegion> entries) throws RejectedExecutionException {
|
||||
checkNotNull(entries);
|
||||
|
||||
synchronized (lock) {
|
||||
lastLoad = null; // Void the pending queued load so that any future
|
||||
// load() calls will submit a brand new load task
|
||||
|
||||
QueuedTask last = lastSave;
|
||||
|
||||
// Check if there is already a queued task that has not yet started
|
||||
// that we can return, rather than queue yet another task
|
||||
if (last != null && !last.started) {
|
||||
return last.future;
|
||||
} else {
|
||||
// Submit the task
|
||||
final QueuedTask task = new QueuedTask();
|
||||
task.future = executor.submit(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
task.started = true;
|
||||
|
||||
setRegions(entries);
|
||||
performSave();
|
||||
return AbstractAsynchronousDatabase.this;
|
||||
}
|
||||
});
|
||||
|
||||
this.lastSave = task;
|
||||
|
||||
return task.future;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Block on the given future and print error messages about failing to
|
||||
* load the database on error.
|
||||
*
|
||||
* @param future the future
|
||||
*/
|
||||
private void blockOnLoad(Future<?> future) {
|
||||
try {
|
||||
future.get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (ExecutionException e) {
|
||||
log.log(Level.WARNING, "Failed to load the region database", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Block on the given future and print error messages about failing to
|
||||
* save the database on error.
|
||||
*
|
||||
* @param future the future
|
||||
*/
|
||||
private void blockOnSave(Future<?> future) {
|
||||
try {
|
||||
future.get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (ExecutionException e) {
|
||||
log.log(Level.WARNING, "Failed to save the region database", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to execute a load that may occur in any thread. However, no
|
||||
* other save or load operation will be simultaneously ongoing.
|
||||
*/
|
||||
protected abstract void performLoad() throws ProtectionDatabaseException;
|
||||
|
||||
/**
|
||||
* Call to execute a save that may occur in any thread. However, no
|
||||
* other save or load operation will be simultaneously ongoing.
|
||||
*
|
||||
* <p>{@link #setRegions(Map)} must not be called until loading
|
||||
* has completed and the provided map is in its completed state.</p>
|
||||
*/
|
||||
protected abstract void performSave() throws ProtectionDatabaseException;
|
||||
|
||||
/**
|
||||
* Stores information about the a queued task.
|
||||
*/
|
||||
private static class QueuedTask {
|
||||
private boolean started = false;
|
||||
private ListenableFuture<?> future;
|
||||
}
|
||||
|
||||
}
|
@ -1,60 +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.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
|
||||
public abstract class AbstractProtectionDatabase implements ProtectionDatabase {
|
||||
|
||||
@Override
|
||||
public void load(RegionManager manager) throws ProtectionDatabaseException {
|
||||
load();
|
||||
manager.setRegions(getRegions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void save(RegionManager manager) throws ProtectionDatabaseException {
|
||||
save(manager, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<?> load(RegionManager manager, boolean async) {
|
||||
try {
|
||||
load(manager);
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
return Futures.immediateFailedFuture(e);
|
||||
}
|
||||
return Futures.immediateCheckedFuture(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<?> save(RegionManager manager, boolean async) {
|
||||
setRegions(manager.getRegions());
|
||||
try {
|
||||
save();
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
return Futures.immediateFailedFuture(e);
|
||||
}
|
||||
return Futures.immediateCheckedFuture(this);
|
||||
}
|
||||
|
||||
}
|
@ -1,357 +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 au.com.bytecode.opencsv.CSVReader;
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.domains.DefaultDomain;
|
||||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag.State;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
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.
|
||||
*
|
||||
* @deprecated no longer maintained - use {@link YAMLDatabase}
|
||||
*/
|
||||
@Deprecated
|
||||
public class CSVDatabase extends AbstractProtectionDatabase {
|
||||
|
||||
private static final Map<String, StateFlag> legacyFlagCodes = new HashMap<String, StateFlag>();
|
||||
static {
|
||||
legacyFlagCodes.put("z", DefaultFlag.PASSTHROUGH);
|
||||
legacyFlagCodes.put("b", DefaultFlag.BUILD);
|
||||
legacyFlagCodes.put("p", DefaultFlag.PVP);
|
||||
legacyFlagCodes.put("m", DefaultFlag.MOB_DAMAGE);
|
||||
legacyFlagCodes.put("c", DefaultFlag.CREEPER_EXPLOSION);
|
||||
legacyFlagCodes.put("t", DefaultFlag.TNT);
|
||||
legacyFlagCodes.put("l", DefaultFlag.LIGHTER);
|
||||
legacyFlagCodes.put("f", DefaultFlag.FIRE_SPREAD);
|
||||
legacyFlagCodes.put("F", DefaultFlag.LAVA_FIRE);
|
||||
legacyFlagCodes.put("C", DefaultFlag.CHEST_ACCESS);
|
||||
}
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
/**
|
||||
* References the CSV file.
|
||||
*/
|
||||
private final File file;
|
||||
/**
|
||||
* Holds the list of regions.
|
||||
*/
|
||||
private Map<String,ProtectedRegion> regions;
|
||||
|
||||
/**
|
||||
* Construct the database with a path to a file. No file is read or
|
||||
* written at this time.
|
||||
*
|
||||
* @param file The file in CSV format containing the region database
|
||||
* @param logger The logger to log errors to
|
||||
*/
|
||||
public CSVDatabase(File file, Logger logger) {
|
||||
this.file = file;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the database.
|
||||
*/
|
||||
public void save() throws ProtectionDatabaseException {
|
||||
throw new UnsupportedOperationException("CSV format is no longer implemented");
|
||||
}
|
||||
|
||||
public void load() throws ProtectionDatabaseException {
|
||||
Map<String,ProtectedRegion> regions =
|
||||
new HashMap<String,ProtectedRegion>();
|
||||
Map<ProtectedRegion,String> parentSets =
|
||||
new LinkedHashMap<ProtectedRegion, String>();
|
||||
|
||||
CSVReader reader = null;
|
||||
try {
|
||||
reader = new CSVReader(new FileReader(file));
|
||||
|
||||
String[] line;
|
||||
|
||||
while ((line = reader.readNext()) != null) {
|
||||
if (line.length < 2) {
|
||||
logger.warning("Invalid region definition: " + line);
|
||||
continue;
|
||||
}
|
||||
|
||||
String id = line[0].toLowerCase().replace(".", "");
|
||||
String type = line[1];
|
||||
ArrayReader<String> entries = new ArrayReader<String>(line);
|
||||
|
||||
if (type.equalsIgnoreCase("cuboid")) {
|
||||
if (line.length < 8) {
|
||||
logger.warning("Invalid region definition: " + line);
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector pt1 = new Vector(
|
||||
Integer.parseInt(line[2]),
|
||||
Integer.parseInt(line[3]),
|
||||
Integer.parseInt(line[4]));
|
||||
Vector pt2 = new Vector(
|
||||
Integer.parseInt(line[5]),
|
||||
Integer.parseInt(line[6]),
|
||||
Integer.parseInt(line[7]));
|
||||
|
||||
BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector();
|
||||
BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector();
|
||||
|
||||
int priority = entries.get(8) == null ? 0 : Integer.parseInt(entries.get(8));
|
||||
String ownersData = entries.get(9);
|
||||
String flagsData = entries.get(10);
|
||||
//String enterMessage = nullEmptyString(entries.get(11));
|
||||
|
||||
ProtectedRegion region = new ProtectedCuboidRegion(id, min, max);
|
||||
region.setPriority(priority);
|
||||
parseFlags(region, flagsData);
|
||||
region.setOwners(this.parseDomains(ownersData));
|
||||
regions.put(id, region);
|
||||
} else if (type.equalsIgnoreCase("cuboid.2")) {
|
||||
Vector pt1 = new Vector(
|
||||
Integer.parseInt(line[2]),
|
||||
Integer.parseInt(line[3]),
|
||||
Integer.parseInt(line[4]));
|
||||
Vector pt2 = new Vector(
|
||||
Integer.parseInt(line[5]),
|
||||
Integer.parseInt(line[6]),
|
||||
Integer.parseInt(line[7]));
|
||||
|
||||
BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector();
|
||||
BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector();
|
||||
|
||||
int priority = entries.get(8) == null ? 0 : Integer.parseInt(entries.get(8));
|
||||
String parentId = entries.get(9);
|
||||
String ownersData = entries.get(10);
|
||||
String membersData = entries.get(11);
|
||||
String flagsData = entries.get(12);
|
||||
//String enterMessage = nullEmptyString(entries.get(13));
|
||||
//String leaveMessage = nullEmptyString(entries.get(14));
|
||||
|
||||
ProtectedRegion region = new ProtectedCuboidRegion(id, min, max);
|
||||
region.setPriority(priority);
|
||||
parseFlags(region, flagsData);
|
||||
region.setOwners(this.parseDomains(ownersData));
|
||||
region.setMembers(this.parseDomains(membersData));
|
||||
regions.put(id, region);
|
||||
|
||||
// Link children to parents later
|
||||
if (parentId.length() > 0) {
|
||||
parentSets.put(region, parentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ProtectionDatabaseException(e);
|
||||
} finally {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<ProtectedRegion, String> entry : parentSets.entrySet()) {
|
||||
ProtectedRegion parent = regions.get(entry.getValue());
|
||||
if (parent != null) {
|
||||
try {
|
||||
entry.getKey().setParent(parent);
|
||||
} catch (CircularInheritanceException e) {
|
||||
logger.warning("Circular inheritance detect with '"
|
||||
+ entry.getValue() + "' detected as a parent");
|
||||
}
|
||||
} else {
|
||||
logger.warning("Unknown region parent: " + entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
this.regions = regions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to parse the specified domain in the CSV file.
|
||||
*
|
||||
* @param data The domain data as a string
|
||||
* @return The domain data as a DefaultDomain
|
||||
*/
|
||||
private DefaultDomain parseDomains(String data) {
|
||||
if (data == null) {
|
||||
return new DefaultDomain();
|
||||
}
|
||||
|
||||
DefaultDomain domain = new DefaultDomain();
|
||||
Pattern pattern = Pattern.compile("^([A-Za-z]):(.*)$");
|
||||
|
||||
String[] parts = data.split(",");
|
||||
|
||||
for (String part : parts) {
|
||||
if (part.trim().length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Matcher matcher = pattern.matcher(part);
|
||||
|
||||
if (!matcher.matches()) {
|
||||
logger.warning("Invalid owner specification: " + part);
|
||||
continue;
|
||||
}
|
||||
|
||||
String type = matcher.group(1);
|
||||
String id = matcher.group(2);
|
||||
|
||||
if (type.equals("u")) {
|
||||
domain.addPlayer(id);
|
||||
} else if (type.equals("g")) {
|
||||
domain.addGroup(id);
|
||||
} else {
|
||||
logger.warning("Unknown owner specification: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to parse the list of flags.
|
||||
*
|
||||
* @param data The flag data in string format
|
||||
*/
|
||||
private void parseFlags(ProtectedRegion region, String data) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
State curState = State.ALLOW;
|
||||
|
||||
for (int i = 0; i < data.length(); i++) {
|
||||
char k = data.charAt(i);
|
||||
if (k == '+') {
|
||||
curState = State.ALLOW;
|
||||
} else if (k == '-') {
|
||||
curState = State.DENY;
|
||||
} else {
|
||||
String flagStr;
|
||||
if (k == '_') {
|
||||
if (i == data.length() - 1) {
|
||||
logger.warning("_ read ahead fail");
|
||||
break;
|
||||
}
|
||||
flagStr = "_" + data.charAt(i + 1);
|
||||
i++;
|
||||
|
||||
logger.warning("_? custom flags are no longer supported");
|
||||
continue;
|
||||
} else {
|
||||
flagStr = String.valueOf(k);
|
||||
}
|
||||
|
||||
StateFlag flag = legacyFlagCodes.get(flagStr);
|
||||
if (flag != null) {
|
||||
region.setFlag(flag, curState);
|
||||
} else {
|
||||
logger.warning("Legacy flag '" + flagStr + "' is unsupported");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to write the list of domains.
|
||||
*
|
||||
* @param domain
|
||||
* @return
|
||||
*/
|
||||
/* private String writeDomains(DefaultDomain domain) {
|
||||
StringBuilder str = new StringBuilder();
|
||||
|
||||
for (String player : domain.getPlayers()) {
|
||||
str.append("u:" + player + ",");
|
||||
}
|
||||
|
||||
for (String group : domain.getGroups()) {
|
||||
str.append("g:" + group + ",");
|
||||
}
|
||||
|
||||
return str.length() > 0 ?
|
||||
str.toString().substring(0, str.length() - 1) : "";
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Helper method to prepend '+' or '-' in front of a flag according
|
||||
* to the flag's state.
|
||||
*
|
||||
* @param state
|
||||
* @param flag
|
||||
* @return
|
||||
*/
|
||||
/*
|
||||
private String writeFlag(State state, String flag) {
|
||||
if (state == State.ALLOW) {
|
||||
return "+" + flag;
|
||||
} else if (state == State.DENY) {
|
||||
return "-" + flag;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a null if a string is null or empty.
|
||||
*
|
||||
* @param str The string to format
|
||||
* @return null if the string is empty or null, otherwise the provided string
|
||||
*/
|
||||
protected String nullEmptyString(String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
} else if (str.length() == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String,ProtectedRegion> getRegions() {
|
||||
return regions;
|
||||
}
|
||||
|
||||
public void setRegions(Map<String,ProtectedRegion> regions) {
|
||||
this.regions = regions;
|
||||
}
|
||||
}
|
@ -1,110 +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.google.common.util.concurrent.ListenableFuture;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Represents a database to read and write lists of regions from and to.
|
||||
*/
|
||||
public interface ProtectionDatabase {
|
||||
|
||||
/**
|
||||
* Load the list of regions. The method should not modify the list returned
|
||||
* by getRegions() unless the load finishes successfully.
|
||||
*
|
||||
* @throws ProtectionDatabaseException when an error occurs
|
||||
*/
|
||||
public void load() throws ProtectionDatabaseException, RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Load the list of regions into a region manager.
|
||||
*
|
||||
* <p>This call will block.</p>
|
||||
*
|
||||
* @param manager The manager to load regions into
|
||||
* @throws ProtectionDatabaseException when an error occurs
|
||||
*/
|
||||
public void load(RegionManager manager) throws ProtectionDatabaseException, RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Load the list of regions into a region manager, optionally to suggest
|
||||
* that the data be load in another thread without blocking the thread
|
||||
* from which the call is made.
|
||||
*
|
||||
* <p>{@code async} is merely a suggestion and it may be ignored by
|
||||
* implementations if it is not supported.</p>
|
||||
*
|
||||
* @param manager The manager to load regions into
|
||||
* @param async true to attempt to save the data asynchronously if it is supported
|
||||
*/
|
||||
public ListenableFuture<?> load(RegionManager manager, boolean async) throws RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Save the list of regions.
|
||||
*
|
||||
* @throws ProtectionDatabaseException when an error occurs
|
||||
*/
|
||||
public void save() throws ProtectionDatabaseException, RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Save the list of regions from a region manager.
|
||||
*
|
||||
* <p>This call will block.</p>
|
||||
*
|
||||
* @param manager The manager to load regions into
|
||||
* @throws ProtectionDatabaseException when an error occurs
|
||||
*/
|
||||
public void save(RegionManager manager) throws ProtectionDatabaseException, RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Save the list of regions from a region manager, optionally to suggest
|
||||
* that the data be saved in another thread without blocking the thread
|
||||
* from which the call is made.
|
||||
*
|
||||
* <p>{@code async} is merely a suggestion and it may be ignored by
|
||||
* implementations if it is not supported.</p>
|
||||
*
|
||||
* @param manager The manager to load regions into
|
||||
* @param async true to attempt to save the data asynchronously if it is supported
|
||||
* @throws RejectedExecutionException on rejection
|
||||
*/
|
||||
public ListenableFuture<?> save(RegionManager manager, boolean async) throws RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Get a list of regions.
|
||||
*
|
||||
* @return the regions loaded by this ProtectionDatabase
|
||||
*/
|
||||
public Map<String,ProtectedRegion> getRegions();
|
||||
|
||||
/**
|
||||
* Set the list of regions.
|
||||
*
|
||||
* @param regions The regions to be applied to this ProtectionDatabase
|
||||
*/
|
||||
public void setRegions(Map<String,ProtectedRegion> regions);
|
||||
|
||||
}
|
@ -20,7 +20,7 @@
|
||||
package com.sk89q.worldguard.protection.databases;
|
||||
|
||||
import com.sk89q.worldguard.domains.DefaultDomain;
|
||||
import com.sk89q.worldguard.protection.databases.util.DomainInputResolver;
|
||||
import com.sk89q.worldguard.protection.util.DomainInputResolver;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -1,62 +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.migrator;
|
||||
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,97 +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.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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,86 +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.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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,78 +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.mysql;
|
||||
|
||||
import com.sk89q.worldguard.bukkit.ConfigurationManager;
|
||||
import org.yaml.snakeyaml.error.YAMLException;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@SuppressWarnings("ProtectedField")
|
||||
abstract class AbstractJob {
|
||||
|
||||
protected final MySQLDatabaseImpl database;
|
||||
protected final ConfigurationManager config;
|
||||
protected final Connection conn;
|
||||
protected final Logger logger;
|
||||
|
||||
protected AbstractJob(MySQLDatabaseImpl database, Connection conn) {
|
||||
checkNotNull(database);
|
||||
checkNotNull(conn);
|
||||
this.database = database;
|
||||
this.config = database.getConfiguration();
|
||||
this.conn = conn;
|
||||
this.logger = database.getLogger();
|
||||
}
|
||||
|
||||
static void closeQuietly(ResultSet rs) {
|
||||
if (rs != null) {
|
||||
try {
|
||||
rs.close();
|
||||
} catch (SQLException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
static void closeQuietly(Statement st) {
|
||||
if (st != null) {
|
||||
try {
|
||||
st.close();
|
||||
} catch (SQLException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
protected Object sqlUnmarshal(String rawValue) {
|
||||
try {
|
||||
return database.getYaml().load(rawValue);
|
||||
} catch (YAMLException e) {
|
||||
return String.valueOf(rawValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected String sqlMarshal(Object rawObject) {
|
||||
return database.getYaml().dump(rawObject);
|
||||
}
|
||||
|
||||
}
|
@ -1,305 +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.mysql;
|
||||
|
||||
import com.jolbox.bonecp.BoneCP;
|
||||
import com.jolbox.bonecp.BoneCPConfig;
|
||||
import com.sk89q.worldguard.bukkit.ConfigurationManager;
|
||||
import com.sk89q.worldguard.protection.databases.AbstractAsynchronousDatabase;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.util.io.Closer;
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.flywaydb.core.api.FlywayException;
|
||||
import org.flywaydb.core.api.MigrationVersion;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* For internal use. Do not subclass.
|
||||
*/
|
||||
public class MySQLDatabaseImpl extends AbstractAsynchronousDatabase {
|
||||
|
||||
private final ConfigurationManager config;
|
||||
private final Logger logger;
|
||||
|
||||
private final BoneCP connectionPool;
|
||||
private final Yaml yaml = createYaml();
|
||||
private final int worldId;
|
||||
|
||||
private Map<String, ProtectedRegion> regions = new HashMap<String, ProtectedRegion>();
|
||||
|
||||
public MySQLDatabaseImpl(ConfigurationManager config, String worldName, Logger logger) throws ProtectionDatabaseException {
|
||||
checkNotNull(config);
|
||||
checkNotNull(worldName);
|
||||
checkNotNull(logger);
|
||||
|
||||
this.config = config;
|
||||
this.logger = logger;
|
||||
|
||||
BoneCPConfig poolConfig = new BoneCPConfig();
|
||||
poolConfig.setJdbcUrl(config.sqlDsn);
|
||||
poolConfig.setUsername(config.sqlUsername);
|
||||
poolConfig.setPassword(config.sqlPassword);
|
||||
|
||||
try {
|
||||
connectionPool = new BoneCP(poolConfig);
|
||||
} catch (SQLException e) {
|
||||
throw new ProtectionDatabaseException("Failed to connect to the database", e);
|
||||
}
|
||||
|
||||
try {
|
||||
migrate();
|
||||
} catch (FlywayException e) {
|
||||
throw new ProtectionDatabaseException("Failed to migrate tables", e);
|
||||
} catch (SQLException e) {
|
||||
throw new ProtectionDatabaseException("Failed to migrate tables", e);
|
||||
}
|
||||
|
||||
try {
|
||||
worldId = chooseWorldId(worldName);
|
||||
} catch (SQLException e) {
|
||||
throw new ProtectionDatabaseException("Failed to choose the ID for this world", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean tryQuery(Connection conn, String sql) throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
Statement statement = closer.register(conn.createStatement());
|
||||
statement.executeQuery(sql);
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
return false;
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the tables to the latest version.
|
||||
*
|
||||
* @throws SQLException thrown if a connection can't be opened
|
||||
* @throws ProtectionDatabaseException thrown on other error
|
||||
*/
|
||||
private void migrate() throws SQLException, ProtectionDatabaseException {
|
||||
Closer closer = Closer.create();
|
||||
Connection conn = closer.register(getConnection());
|
||||
|
||||
// Check some tables
|
||||
boolean tablesExist;
|
||||
boolean isRecent;
|
||||
boolean isBeforeMigrations;
|
||||
boolean hasMigrations;
|
||||
|
||||
try {
|
||||
tablesExist = tryQuery(conn, "SELECT * FROM `" + config.sqlTablePrefix + "region_cuboid` LIMIT 1");
|
||||
isRecent = tryQuery(conn, "SELECT `world_id` FROM `" + config.sqlTablePrefix + "region_cuboid` LIMIT 1");
|
||||
isBeforeMigrations = !tryQuery(conn, "SELECT `uuid` FROM `" + config.sqlTablePrefix + "user` LIMIT 1");
|
||||
hasMigrations = tryQuery(conn, "SELECT * FROM `" + config.sqlTablePrefix + "migrations` LIMIT 1");
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
|
||||
// We don't bother with migrating really old tables
|
||||
if (tablesExist && !isRecent) {
|
||||
throw new ProtectionDatabaseException(
|
||||
"Sorry, your tables are too old for the region SQL auto-migration system. " +
|
||||
"Please run region_manual_update_20110325.sql on your database, which comes " +
|
||||
"with WorldGuard or can be found in http://github.com/sk89q/worldguard");
|
||||
}
|
||||
|
||||
// Our placeholders
|
||||
Map<String, String> placeHolders = new HashMap<String, String>();
|
||||
placeHolders.put("tablePrefix", config.sqlTablePrefix);
|
||||
|
||||
BoneCPConfig boneConfig = connectionPool.getConfig();
|
||||
|
||||
Flyway flyway = new Flyway();
|
||||
|
||||
// The MySQL support predates the usage of Flyway, so let's do some
|
||||
// checks and issue messages appropriately
|
||||
if (!hasMigrations) {
|
||||
flyway.setInitOnMigrate(true);
|
||||
|
||||
if (tablesExist) {
|
||||
// Detect if this is before migrations
|
||||
if (isBeforeMigrations) {
|
||||
flyway.setInitVersion(MigrationVersion.fromVersion("1"));
|
||||
}
|
||||
|
||||
logger.log(Level.INFO, "The MySQL region tables exist but the migrations table seems to not exist yet. Creating the migrations table...");
|
||||
} else {
|
||||
// By default, if Flyway sees any tables at all in the schema, it
|
||||
// will assume that we are up to date, so we have to manually
|
||||
// check ourselves and then ask Flyway to start from the beginning
|
||||
// if our test table doesn't exist
|
||||
flyway.setInitVersion(MigrationVersion.fromVersion("0"));
|
||||
|
||||
logger.log(Level.INFO, "MySQL region tables do not exist: creating...");
|
||||
}
|
||||
}
|
||||
|
||||
flyway.setClassLoader(getClass().getClassLoader());
|
||||
flyway.setLocations("migrations/region/mysql");
|
||||
flyway.setDataSource(boneConfig.getJdbcUrl(), boneConfig.getUser(), boneConfig.getPassword());
|
||||
flyway.setTable(config.sqlTablePrefix + "migrations");
|
||||
flyway.setPlaceholders(placeHolders);
|
||||
flyway.setValidateOnMigrate(false);
|
||||
flyway.migrate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID for this world from the database or pick a new one if
|
||||
* an entry does not exist yet.
|
||||
*
|
||||
* @param worldName the world name
|
||||
* @return a world ID
|
||||
* @throws SQLException on a database access error
|
||||
*/
|
||||
private int chooseWorldId(String worldName) throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
Connection conn = closer.register(getConnection());
|
||||
PreparedStatement worldStmt = closer.register(conn.prepareStatement(
|
||||
"SELECT `id` FROM `" + config.sqlTablePrefix + "world` WHERE `name` = ? LIMIT 0, 1"
|
||||
));
|
||||
|
||||
worldStmt.setString(1, worldName);
|
||||
ResultSet worldResult = closer.register(worldStmt.executeQuery());
|
||||
|
||||
if (worldResult.first()) {
|
||||
return worldResult.getInt("id");
|
||||
} else {
|
||||
PreparedStatement insertWorldStatement = closer.register(conn.prepareStatement(
|
||||
"INSERT INTO " +
|
||||
"`" + config.sqlTablePrefix + "world` " +
|
||||
"(`id`, `name`) VALUES (null, ?)",
|
||||
Statement.RETURN_GENERATED_KEYS
|
||||
));
|
||||
|
||||
insertWorldStatement.setString(1, worldName);
|
||||
insertWorldStatement.execute();
|
||||
ResultSet generatedKeys = insertWorldStatement.getGeneratedKeys();
|
||||
|
||||
if (generatedKeys.first()) {
|
||||
return generatedKeys.getInt(1);
|
||||
} else {
|
||||
throw new SQLException("Expected result, got none");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private static Yaml createYaml() {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setIndent(2);
|
||||
options.setDefaultFlowStyle(FlowStyle.FLOW);
|
||||
Representer representer = new Representer();
|
||||
representer.setDefaultFlowStyle(FlowStyle.FLOW);
|
||||
|
||||
// We have to use this in order to properly save non-string values
|
||||
return new Yaml(new SafeConstructor(), new Representer(), options);
|
||||
}
|
||||
|
||||
ConfigurationManager getConfiguration() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
Yaml getYaml() {
|
||||
return yaml;
|
||||
}
|
||||
|
||||
int getWorldId() {
|
||||
return worldId;
|
||||
}
|
||||
|
||||
private Connection getConnection() throws SQLException {
|
||||
return connectionPool.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ProtectedRegion> getRegions() {
|
||||
return regions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegions(Map<String, ProtectedRegion> regions) {
|
||||
this.regions = regions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performLoad() throws ProtectionDatabaseException {
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = getConnection();
|
||||
setRegions(new RegionLoader(this, connection).load());
|
||||
} catch (SQLException e) {
|
||||
throw new ProtectionDatabaseException("Failed to load regions database", e);
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performSave() throws ProtectionDatabaseException {
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = getConnection();
|
||||
new RegionWriter(this, connection, getRegions()).save();
|
||||
} catch (SQLException e) {
|
||||
throw new ProtectionDatabaseException("Failed to save regions database", e);
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,456 +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.mysql;
|
||||
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.domains.DefaultDomain;
|
||||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
class RegionLoader extends AbstractJob {
|
||||
|
||||
/*
|
||||
========= Everything below is a nightmare. =========
|
||||
*/
|
||||
|
||||
private final int worldId;
|
||||
|
||||
private Map<String, ProtectedRegion> cuboidRegions;
|
||||
private Map<String, ProtectedRegion> poly2dRegions;
|
||||
private Map<String, ProtectedRegion> globalRegions;
|
||||
private Map<ProtectedRegion, String> parentSets;
|
||||
|
||||
RegionLoader(MySQLDatabaseImpl database, Connection conn) {
|
||||
super(database, conn);
|
||||
this.worldId = database.getWorldId();
|
||||
}
|
||||
|
||||
public Map<String, ProtectedRegion> load() {
|
||||
parentSets = new HashMap<ProtectedRegion, String>();
|
||||
|
||||
// We load the cuboid regions first, as this is likely to be the
|
||||
// largest dataset. This should save time in regards to the putAll()s
|
||||
this.loadCuboid();
|
||||
Map<String, ProtectedRegion> regions = this.cuboidRegions;
|
||||
this.cuboidRegions = null;
|
||||
|
||||
this.loadPoly2d();
|
||||
regions.putAll(this.poly2dRegions);
|
||||
this.poly2dRegions = null;
|
||||
|
||||
this.loadGlobal();
|
||||
regions.putAll(this.globalRegions);
|
||||
this.globalRegions = null;
|
||||
|
||||
// Relink parents // Taken verbatim from YAMLDatabase
|
||||
for (Map.Entry<ProtectedRegion, String> entry : parentSets.entrySet()) {
|
||||
ProtectedRegion parent = regions.get(entry.getValue());
|
||||
if (parent != null) {
|
||||
try {
|
||||
entry.getKey().setParent(parent);
|
||||
} catch (CircularInheritanceException e) {
|
||||
logger.warning("Circular inheritance detect with '"
|
||||
+ entry.getValue() + "' detected as a parent");
|
||||
}
|
||||
} else {
|
||||
logger.warning("Unknown region parent: " + entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
private void loadFlags(ProtectedRegion region) {
|
||||
// @TODO: Iterate _ONCE_
|
||||
PreparedStatement flagsStatement = null;
|
||||
ResultSet flagsResultSet = null;
|
||||
try {
|
||||
flagsStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`region_flag`.`flag`, " +
|
||||
"`region_flag`.`value` " +
|
||||
"FROM `" + config.sqlTablePrefix + "region_flag` AS `region_flag` " +
|
||||
"WHERE `region_id` = ? " +
|
||||
"AND `world_id` = " + this.worldId
|
||||
);
|
||||
|
||||
flagsStatement.setString(1, region.getId().toLowerCase());
|
||||
flagsResultSet = flagsStatement.executeQuery();
|
||||
|
||||
Map<String, Object> regionFlags = new HashMap<String, Object>();
|
||||
while (flagsResultSet.next()) {
|
||||
regionFlags.put(
|
||||
flagsResultSet.getString("flag"),
|
||||
sqlUnmarshal(flagsResultSet.getString("value"))
|
||||
);
|
||||
}
|
||||
|
||||
// @TODO: Make this better
|
||||
for (Flag<?> flag : DefaultFlag.getFlags()) {
|
||||
Object o = regionFlags.get(flag.getName());
|
||||
if (o != null) {
|
||||
setFlag(region, flag, o);
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.warning(
|
||||
"Unable to load flags for region "
|
||||
+ region.getId().toLowerCase() + ": " + ex.getMessage()
|
||||
);
|
||||
} finally {
|
||||
closeQuietly(flagsResultSet);
|
||||
closeQuietly(flagsStatement);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void setFlag(ProtectedRegion region, Flag<T> flag, Object rawValue) {
|
||||
T val = flag.unmarshal(rawValue);
|
||||
if (val == null) {
|
||||
logger.warning("Failed to parse flag '" + flag.getName() + "' with value '" + rawValue + "'");
|
||||
return;
|
||||
}
|
||||
region.setFlag(flag, val);
|
||||
}
|
||||
|
||||
private void loadOwnersAndMembers(ProtectedRegion region) {
|
||||
DefaultDomain owners = new DefaultDomain();
|
||||
DefaultDomain members = new DefaultDomain();
|
||||
|
||||
ResultSet userSet = null;
|
||||
PreparedStatement usersStatement = null;
|
||||
try {
|
||||
usersStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`user`.`name`, `user`.`uuid`, " +
|
||||
"`region_players`.`owner` " +
|
||||
"FROM `" + config.sqlTablePrefix + "region_players` AS `region_players` " +
|
||||
"LEFT JOIN `" + config.sqlTablePrefix + "user` AS `user` ON ( " +
|
||||
"`region_players`.`user_id` = " +
|
||||
"`user`.`id`) " +
|
||||
"WHERE `region_players`.`region_id` = ? " +
|
||||
"AND `region_players`.`world_id` = " + this.worldId
|
||||
);
|
||||
|
||||
usersStatement.setString(1, region.getId().toLowerCase());
|
||||
userSet = usersStatement.executeQuery();
|
||||
while (userSet.next()) {
|
||||
DefaultDomain domain;
|
||||
if (userSet.getBoolean("owner")) {
|
||||
domain = owners;
|
||||
} else {
|
||||
domain = members;
|
||||
}
|
||||
|
||||
String name = userSet.getString("name");
|
||||
String uuid = userSet.getString("uuid");
|
||||
if (name != null) {
|
||||
domain.addPlayer(name);
|
||||
} else if (uuid != null) {
|
||||
try {
|
||||
domain.addPlayer(UUID.fromString(uuid));
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warning("Invalid UUID in database: " + uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.warning("Unable to load users for region " + region.getId().toLowerCase() + ": " + ex.getMessage());
|
||||
} finally {
|
||||
closeQuietly(userSet);
|
||||
closeQuietly(usersStatement);
|
||||
}
|
||||
|
||||
PreparedStatement groupsStatement = null;
|
||||
ResultSet groupSet = null;
|
||||
try {
|
||||
groupsStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`group`.`name`, " +
|
||||
"`region_groups`.`owner` " +
|
||||
"FROM `" + config.sqlTablePrefix + "region_groups` AS `region_groups` " +
|
||||
"LEFT JOIN `" + config.sqlTablePrefix + "group` AS `group` ON ( " +
|
||||
"`region_groups`.`group_id` = " +
|
||||
"`group`.`id`) " +
|
||||
"WHERE `region_groups`.`region_id` = ? " +
|
||||
"AND `region_groups`.`world_id` = " + this.worldId
|
||||
);
|
||||
|
||||
groupsStatement.setString(1, region.getId().toLowerCase());
|
||||
groupSet = groupsStatement.executeQuery();
|
||||
while (groupSet.next()) {
|
||||
if (groupSet.getBoolean("owner")) {
|
||||
owners.addGroup(groupSet.getString("name"));
|
||||
} else {
|
||||
members.addGroup(groupSet.getString("name"));
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.warning("Unable to load groups for region " + region.getId().toLowerCase() + ": " + ex.getMessage());
|
||||
} finally {
|
||||
closeQuietly(groupSet);
|
||||
closeQuietly(groupsStatement);
|
||||
}
|
||||
|
||||
region.setOwners(owners);
|
||||
region.setMembers(members);
|
||||
}
|
||||
|
||||
private void loadGlobal() {
|
||||
Map<String, ProtectedRegion> regions =
|
||||
new HashMap<String, ProtectedRegion>();
|
||||
|
||||
PreparedStatement globalRegionStatement = null;
|
||||
ResultSet globalResultSet = null;
|
||||
try {
|
||||
globalRegionStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`region`.`id`, " +
|
||||
"`region`.`priority`, " +
|
||||
"`parent`.`id` AS `parent` " +
|
||||
"FROM `" + config.sqlTablePrefix + "region` AS `region` " +
|
||||
"LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " +
|
||||
"ON (`region`.`parent` = `parent`.`id` " +
|
||||
"AND `region`.`world_id` = `parent`.`world_id`) " +
|
||||
"WHERE `region`.`type` = 'global' " +
|
||||
"AND `region`.`world_id` = ? "
|
||||
);
|
||||
|
||||
globalRegionStatement.setInt(1, this.worldId);
|
||||
globalResultSet = globalRegionStatement.executeQuery();
|
||||
|
||||
while (globalResultSet.next()) {
|
||||
ProtectedRegion region = new GlobalProtectedRegion(globalResultSet.getString("id"));
|
||||
|
||||
region.setPriority(globalResultSet.getInt("priority"));
|
||||
|
||||
this.loadFlags(region);
|
||||
this.loadOwnersAndMembers(region);
|
||||
|
||||
regions.put(globalResultSet.getString("id"), region);
|
||||
|
||||
String parentId = globalResultSet.getString("parent");
|
||||
if (parentId != null) {
|
||||
parentSets.put(region, parentId);
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
logger.warning("Unable to load regions from sql database: " + ex.getMessage());
|
||||
Throwable t = ex.getCause();
|
||||
while (t != null) {
|
||||
logger.warning("\t\tCause: " + t.getMessage());
|
||||
t = t.getCause();
|
||||
}
|
||||
} finally {
|
||||
closeQuietly(globalResultSet);
|
||||
closeQuietly(globalRegionStatement);
|
||||
}
|
||||
|
||||
globalRegions = regions;
|
||||
}
|
||||
|
||||
private void loadCuboid() {
|
||||
Map<String, ProtectedRegion> regions = new HashMap<String, ProtectedRegion>();
|
||||
|
||||
PreparedStatement cuboidRegionStatement = null;
|
||||
ResultSet cuboidResultSet = null;
|
||||
try {
|
||||
cuboidRegionStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`region_cuboid`.`min_z`, " +
|
||||
"`region_cuboid`.`min_y`, " +
|
||||
"`region_cuboid`.`min_x`, " +
|
||||
"`region_cuboid`.`max_z`, " +
|
||||
"`region_cuboid`.`max_y`, " +
|
||||
"`region_cuboid`.`max_x`, " +
|
||||
"`region`.`id`, " +
|
||||
"`region`.`priority`, " +
|
||||
"`parent`.`id` AS `parent` " +
|
||||
"FROM `" + config.sqlTablePrefix + "region_cuboid` AS `region_cuboid` " +
|
||||
"LEFT JOIN `" + config.sqlTablePrefix + "region` AS `region` " +
|
||||
"ON (`region_cuboid`.`region_id` = `region`.`id` " +
|
||||
"AND `region_cuboid`.`world_id` = `region`.`world_id`) " +
|
||||
"LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " +
|
||||
"ON (`region`.`parent` = `parent`.`id` " +
|
||||
"AND `region`.`world_id` = `parent`.`world_id`) " +
|
||||
"WHERE `region`.`world_id` = ? "
|
||||
);
|
||||
|
||||
cuboidRegionStatement.setInt(1, this.worldId);
|
||||
cuboidResultSet = cuboidRegionStatement.executeQuery();
|
||||
|
||||
while (cuboidResultSet.next()) {
|
||||
Vector pt1 = new Vector(
|
||||
cuboidResultSet.getInt("min_x"),
|
||||
cuboidResultSet.getInt("min_y"),
|
||||
cuboidResultSet.getInt("min_z")
|
||||
);
|
||||
Vector pt2 = new Vector(
|
||||
cuboidResultSet.getInt("max_x"),
|
||||
cuboidResultSet.getInt("max_y"),
|
||||
cuboidResultSet.getInt("max_z")
|
||||
);
|
||||
|
||||
BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector();
|
||||
BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector();
|
||||
ProtectedRegion region = new ProtectedCuboidRegion(
|
||||
cuboidResultSet.getString("id"),
|
||||
min,
|
||||
max
|
||||
);
|
||||
|
||||
region.setPriority(cuboidResultSet.getInt("priority"));
|
||||
|
||||
this.loadFlags(region);
|
||||
this.loadOwnersAndMembers(region);
|
||||
|
||||
regions.put(cuboidResultSet.getString("id"), region);
|
||||
|
||||
String parentId = cuboidResultSet.getString("parent");
|
||||
if (parentId != null) {
|
||||
parentSets.put(region, parentId);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
logger.warning("Unable to load regions from sql database: " + ex.getMessage());
|
||||
Throwable t = ex.getCause();
|
||||
while (t != null) {
|
||||
logger.warning("\t\tCause: " + t.getMessage());
|
||||
t = t.getCause();
|
||||
}
|
||||
} finally {
|
||||
closeQuietly(cuboidResultSet);
|
||||
closeQuietly(cuboidRegionStatement);
|
||||
}
|
||||
|
||||
cuboidRegions = regions;
|
||||
}
|
||||
|
||||
private void loadPoly2d() {
|
||||
Map<String, ProtectedRegion> regions = new HashMap<String, ProtectedRegion>();
|
||||
|
||||
PreparedStatement poly2dRegionStatement = null;
|
||||
ResultSet poly2dResultSet = null;
|
||||
PreparedStatement poly2dVectorStatement = null;
|
||||
try {
|
||||
poly2dRegionStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`region_poly2d`.`min_y`, " +
|
||||
"`region_poly2d`.`max_y`, " +
|
||||
"`region`.`id`, " +
|
||||
"`region`.`priority`, " +
|
||||
"`parent`.`id` AS `parent` " +
|
||||
"FROM `" + config.sqlTablePrefix + "region_poly2d` AS `region_poly2d` " +
|
||||
"LEFT JOIN `" + config.sqlTablePrefix + "region` AS `region` " +
|
||||
"ON (`region_poly2d`.`region_id` = `region`.`id` " +
|
||||
"AND `region_poly2d`.`world_id` = `region`.`world_id`) " +
|
||||
"LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " +
|
||||
"ON (`region`.`parent` = `parent`.`id` " +
|
||||
"AND `region`.`world_id` = `parent`.`world_id`) " +
|
||||
"WHERE `region`.`world_id` = ? "
|
||||
);
|
||||
|
||||
poly2dRegionStatement.setInt(1, this.worldId);
|
||||
poly2dResultSet = poly2dRegionStatement.executeQuery();
|
||||
|
||||
poly2dVectorStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`region_poly2d_point`.`x`, " +
|
||||
"`region_poly2d_point`.`z` " +
|
||||
"FROM `" + config.sqlTablePrefix + "region_poly2d_point` AS `region_poly2d_point` " +
|
||||
"WHERE `region_poly2d_point`.`region_id` = ? " +
|
||||
"AND `region_poly2d_point`.`world_id` = " + this.worldId
|
||||
);
|
||||
|
||||
while (poly2dResultSet.next()) {
|
||||
String id = poly2dResultSet.getString("id");
|
||||
|
||||
Integer minY = poly2dResultSet.getInt("min_y");
|
||||
Integer maxY = poly2dResultSet.getInt("max_y");
|
||||
List<BlockVector2D> points = new ArrayList<BlockVector2D>();
|
||||
|
||||
poly2dVectorStatement.setString(1, id);
|
||||
ResultSet poly2dVectorResultSet = poly2dVectorStatement.executeQuery();
|
||||
|
||||
while (poly2dVectorResultSet.next()) {
|
||||
points.add(new BlockVector2D(
|
||||
poly2dVectorResultSet.getInt("x"),
|
||||
poly2dVectorResultSet.getInt("z")
|
||||
));
|
||||
}
|
||||
|
||||
if (points.size() < 3) {
|
||||
logger.warning(String.format("Invalid polygonal region '%s': region only has %d point(s). Ignoring.", id, points.size()));
|
||||
continue;
|
||||
}
|
||||
|
||||
closeQuietly(poly2dVectorResultSet);
|
||||
|
||||
ProtectedRegion region = new ProtectedPolygonalRegion(id, points, minY, maxY);
|
||||
|
||||
region.setPriority(poly2dResultSet.getInt("priority"));
|
||||
|
||||
this.loadFlags(region);
|
||||
this.loadOwnersAndMembers(region);
|
||||
|
||||
regions.put(poly2dResultSet.getString("id"), region);
|
||||
|
||||
String parentId = poly2dResultSet.getString("parent");
|
||||
if (parentId != null) {
|
||||
parentSets.put(region, parentId);
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
logger.warning("Unable to load regions from sql database: " + ex.getMessage());
|
||||
Throwable t = ex.getCause();
|
||||
while (t != null) {
|
||||
logger.warning("\t\tCause: " + t.getMessage());
|
||||
t = t.getCause();
|
||||
}
|
||||
} finally {
|
||||
closeQuietly(poly2dResultSet);
|
||||
closeQuietly(poly2dRegionStatement);
|
||||
closeQuietly(poly2dVectorStatement);
|
||||
}
|
||||
|
||||
poly2dRegions = regions;
|
||||
}
|
||||
|
||||
}
|
@ -1,608 +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.mysql;
|
||||
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldguard.domains.DefaultDomain;
|
||||
import com.sk89q.worldguard.internal.util.sql.StatementUtils;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
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 RegionWriter extends AbstractJob {
|
||||
|
||||
/*
|
||||
========= Everything below is a nightmare. =========
|
||||
*/
|
||||
|
||||
private final Map<String, ProtectedRegion> regions;
|
||||
private final NameRowCache usernameCache;
|
||||
private final UUIDRowCache uuidCache;
|
||||
private final int worldId;
|
||||
|
||||
RegionWriter(MySQLDatabaseImpl database, Connection conn, Map<String, ProtectedRegion> regions) {
|
||||
super(database, conn);
|
||||
checkNotNull(regions);
|
||||
this.regions = regions;
|
||||
this.worldId = database.getWorldId();
|
||||
usernameCache = new NameRowCache(database, conn);
|
||||
uuidCache = new UUIDRowCache(database, conn);
|
||||
}
|
||||
|
||||
public void save() throws SQLException, ProtectionDatabaseException {
|
||||
|
||||
/*
|
||||
* As we don't get notified on the creation/removal of regions:
|
||||
* 1) We get a list of all of the in-database regions
|
||||
* 2) We iterate over all of the in-memory regions
|
||||
* 2a) If the region is in the database, we update the database and
|
||||
* remove the region from the in-database list
|
||||
* b) If the region is not in the database, we insert it
|
||||
* 3) We iterate over what remains of the in-database list and remove
|
||||
* them from the database
|
||||
*
|
||||
* TODO: Look at adding/removing/updating the database when the in
|
||||
* memory region is created/remove/updated
|
||||
*
|
||||
* @see com.sk89q.worldguard.protection.databases.ProtectionDatabase#save()
|
||||
*/
|
||||
|
||||
List<String> regionsInDatabase = new ArrayList<String>();
|
||||
|
||||
PreparedStatement getAllRegionsStatement = null;
|
||||
ResultSet getAllRegionsResult = null;
|
||||
try {
|
||||
getAllRegionsStatement = this.conn.prepareStatement(
|
||||
"SELECT `region`.`id` FROM " +
|
||||
"`" + config.sqlTablePrefix + "region` AS `region` " +
|
||||
"WHERE `world_id` = ? "
|
||||
);
|
||||
|
||||
getAllRegionsStatement.setInt(1, this.worldId);
|
||||
getAllRegionsResult = getAllRegionsStatement.executeQuery();
|
||||
|
||||
while(getAllRegionsResult.next()) {
|
||||
regionsInDatabase.add(getAllRegionsResult.getString("id"));
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.warning("Could not get region list for save comparison: " + ex.getMessage());
|
||||
} finally {
|
||||
closeQuietly(getAllRegionsResult);
|
||||
closeQuietly(getAllRegionsStatement);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, ProtectedRegion> entry : regions.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
ProtectedRegion region = entry.getValue();
|
||||
|
||||
try {
|
||||
if (regionsInDatabase.contains(name)) {
|
||||
regionsInDatabase.remove(name);
|
||||
|
||||
if (region instanceof ProtectedCuboidRegion) {
|
||||
updateRegionCuboid( (ProtectedCuboidRegion) region );
|
||||
} else if (region instanceof ProtectedPolygonalRegion) {
|
||||
updateRegionPoly2D( (ProtectedPolygonalRegion) region );
|
||||
} else if (region instanceof GlobalProtectedRegion) {
|
||||
updateRegionGlobal( (GlobalProtectedRegion) region );
|
||||
} else {
|
||||
this.updateRegion(region, region.getClass().getCanonicalName());
|
||||
}
|
||||
} else {
|
||||
if (region instanceof ProtectedCuboidRegion) {
|
||||
insertRegionCuboid( (ProtectedCuboidRegion) region );
|
||||
} else if (region instanceof ProtectedPolygonalRegion) {
|
||||
insertRegionPoly2D( (ProtectedPolygonalRegion) region );
|
||||
} else if (region instanceof GlobalProtectedRegion) {
|
||||
insertRegionGlobal( (GlobalProtectedRegion) region );
|
||||
} else {
|
||||
this.insertRegion(region, region.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.warning("Could not save region " + region.getId().toLowerCase() + ": " + ex.getMessage());
|
||||
throw new ProtectionDatabaseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, ProtectedRegion> entry : regions.entrySet()) {
|
||||
PreparedStatement setParentStatement = null;
|
||||
try {
|
||||
if (entry.getValue().getParent() == null) continue;
|
||||
|
||||
setParentStatement = this.conn.prepareStatement(
|
||||
"UPDATE `" + config.sqlTablePrefix + "region` SET " +
|
||||
"`parent` = ? " +
|
||||
"WHERE `id` = ? AND `world_id` = " + this.worldId
|
||||
);
|
||||
|
||||
setParentStatement.setString(1, entry.getValue().getParent().getId().toLowerCase());
|
||||
setParentStatement.setString(2, entry.getValue().getId().toLowerCase());
|
||||
|
||||
setParentStatement.execute();
|
||||
} catch (SQLException ex) {
|
||||
logger.warning("Could not save region parents " + entry.getValue().getId().toLowerCase() + ": " + ex.getMessage());
|
||||
throw new ProtectionDatabaseException(ex);
|
||||
} finally {
|
||||
closeQuietly(setParentStatement);
|
||||
}
|
||||
}
|
||||
|
||||
for (String name : regionsInDatabase) {
|
||||
PreparedStatement removeRegion = null;
|
||||
try {
|
||||
removeRegion = this.conn.prepareStatement(
|
||||
"DELETE FROM `" + config.sqlTablePrefix + "region` WHERE `id` = ? "
|
||||
);
|
||||
|
||||
removeRegion.setString(1, name);
|
||||
removeRegion.execute();
|
||||
} catch (SQLException ex) {
|
||||
logger.warning("Could not remove region from database " + name + ": " + ex.getMessage());
|
||||
} finally {
|
||||
closeQuietly(removeRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the database id for the groups
|
||||
* If it doesn't exits it adds the group and returns the id.
|
||||
*/
|
||||
private Map<String,Integer> getGroupIds(String... groupnames) {
|
||||
Map<String,Integer> groups = new HashMap<String,Integer>();
|
||||
|
||||
if (groupnames.length < 1) return groups;
|
||||
|
||||
PreparedStatement findGroupsStatement = null;
|
||||
ResultSet findGroupsResults = null;
|
||||
PreparedStatement insertGroupStatement = null;
|
||||
try {
|
||||
findGroupsStatement = this.conn.prepareStatement(
|
||||
String.format(
|
||||
"SELECT " +
|
||||
"`group`.`id`, " +
|
||||
"`group`.`name` " +
|
||||
"FROM `" + config.sqlTablePrefix + "group` AS `group` " +
|
||||
"WHERE `name` IN (%s)",
|
||||
StatementUtils.preparePlaceHolders(groupnames.length)
|
||||
)
|
||||
);
|
||||
|
||||
StatementUtils.setValues(findGroupsStatement, groupnames);
|
||||
|
||||
findGroupsResults = findGroupsStatement.executeQuery();
|
||||
|
||||
while(findGroupsResults.next()) {
|
||||
groups.put(findGroupsResults.getString("name"), findGroupsResults.getInt("id"));
|
||||
}
|
||||
|
||||
insertGroupStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO " +
|
||||
"`" + config.sqlTablePrefix + "group` ( " +
|
||||
"`id`, " +
|
||||
"`name`" +
|
||||
") VALUES (null, ?)",
|
||||
Statement.RETURN_GENERATED_KEYS
|
||||
);
|
||||
|
||||
for (String groupname : groupnames) {
|
||||
if (!groups.containsKey(groupname)) {
|
||||
insertGroupStatement.setString(1, groupname);
|
||||
insertGroupStatement.execute();
|
||||
ResultSet generatedKeys = insertGroupStatement.getGeneratedKeys();
|
||||
if (generatedKeys.first()) {
|
||||
groups.put(groupname, generatedKeys.getInt(1));
|
||||
} else {
|
||||
logger.warning("Could not get the database id for user " + groupname);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.warning("Could not get the database id for the groups " + groupnames.toString() + ex.getMessage());
|
||||
} finally {
|
||||
closeQuietly(findGroupsResults);
|
||||
closeQuietly(findGroupsStatement);
|
||||
closeQuietly(insertGroupStatement);
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
private void updateFlags(ProtectedRegion region) throws SQLException {
|
||||
PreparedStatement clearCurrentFlagStatement = null;
|
||||
try {
|
||||
clearCurrentFlagStatement = this.conn.prepareStatement(
|
||||
"DELETE FROM `" + config.sqlTablePrefix + "region_flag` " +
|
||||
"WHERE `region_id` = ? " +
|
||||
"AND `world_id` = " + this.worldId
|
||||
);
|
||||
|
||||
clearCurrentFlagStatement.setString(1, region.getId().toLowerCase());
|
||||
clearCurrentFlagStatement.execute();
|
||||
|
||||
for (Map.Entry<Flag<?>, Object> entry : region.getFlags().entrySet()) {
|
||||
if (entry.getValue() == null) continue;
|
||||
|
||||
Object flag = sqlMarshal(marshalFlag(entry.getKey(), entry.getValue()));
|
||||
|
||||
PreparedStatement insertFlagStatement = null;
|
||||
try {
|
||||
insertFlagStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO `" + config.sqlTablePrefix + "region_flag` ( " +
|
||||
"`id`, " +
|
||||
"`region_id`, " +
|
||||
"`world_id`, " +
|
||||
"`flag`, " +
|
||||
"`value` " +
|
||||
") VALUES (null, ?, " + this.worldId + ", ?, ?)"
|
||||
);
|
||||
|
||||
insertFlagStatement.setString(1, region.getId().toLowerCase());
|
||||
insertFlagStatement.setString(2, entry.getKey().getName());
|
||||
insertFlagStatement.setObject(3, flag);
|
||||
|
||||
insertFlagStatement.execute();
|
||||
} finally {
|
||||
closeQuietly(insertFlagStatement);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closeQuietly(clearCurrentFlagStatement);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePlayerAndGroups(ProtectedRegion region, Boolean owners) throws SQLException {
|
||||
DefaultDomain domain;
|
||||
|
||||
if (owners) {
|
||||
domain = region.getOwners();
|
||||
} else {
|
||||
domain = region.getMembers();
|
||||
}
|
||||
|
||||
PreparedStatement deleteUsersForRegion = null;
|
||||
PreparedStatement insertUsersForRegion = null;
|
||||
PreparedStatement deleteGroupsForRegion = null;
|
||||
PreparedStatement insertGroupsForRegion = null;
|
||||
|
||||
try {
|
||||
deleteUsersForRegion = this.conn.prepareStatement(
|
||||
"DELETE FROM `" + config.sqlTablePrefix + "region_players` " +
|
||||
"WHERE `region_id` = ? " +
|
||||
"AND `world_id` = " + this.worldId + " " +
|
||||
"AND `owner` = ?"
|
||||
);
|
||||
|
||||
deleteUsersForRegion.setString(1, region.getId().toLowerCase());
|
||||
deleteUsersForRegion.setBoolean(2, owners);
|
||||
deleteUsersForRegion.execute();
|
||||
|
||||
insertUsersForRegion = this.conn.prepareStatement(
|
||||
"INSERT INTO `" + config.sqlTablePrefix + "region_players` " +
|
||||
"(`region_id`, `world_id`, `user_id`, `owner`) " +
|
||||
"VALUES (?, " + this.worldId + ", ?, ?)"
|
||||
);
|
||||
|
||||
// Map players to IDs
|
||||
usernameCache.fetch(domain.getPlayers());
|
||||
uuidCache.fetch(domain.getUniqueIds());
|
||||
|
||||
for (String name : domain.getPlayers()) {
|
||||
Integer id = usernameCache.find(name);
|
||||
if (id != null) {
|
||||
insertUsersForRegion.setString(1, region.getId().toLowerCase());
|
||||
insertUsersForRegion.setInt(2, id);
|
||||
insertUsersForRegion.setBoolean(3, owners);
|
||||
insertUsersForRegion.execute();
|
||||
} else {
|
||||
logger.log(Level.WARNING, "Did not find an ID for the user identified as '" + name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
for (UUID uuid : domain.getUniqueIds()) {
|
||||
Integer id = uuidCache.find(uuid);
|
||||
if (id != null) {
|
||||
insertUsersForRegion.setString(1, region.getId().toLowerCase());
|
||||
insertUsersForRegion.setInt(2, id);
|
||||
insertUsersForRegion.setBoolean(3, owners);
|
||||
insertUsersForRegion.execute();
|
||||
} else {
|
||||
logger.log(Level.WARNING, "Did not find an ID for the user identified by '" + uuid + "'");
|
||||
}
|
||||
}
|
||||
|
||||
deleteGroupsForRegion = this.conn.prepareStatement(
|
||||
"DELETE FROM `" + config.sqlTablePrefix + "region_groups` " +
|
||||
"WHERE `region_id` = ? " +
|
||||
"AND `world_id` = " + this.worldId + " " +
|
||||
"AND `owner` = ?"
|
||||
);
|
||||
|
||||
deleteGroupsForRegion.setString(1, region.getId().toLowerCase());
|
||||
deleteGroupsForRegion.setBoolean(2, owners);
|
||||
deleteGroupsForRegion.execute();
|
||||
|
||||
insertGroupsForRegion = this.conn.prepareStatement(
|
||||
"INSERT INTO `" + config.sqlTablePrefix + "region_groups` " +
|
||||
"(`region_id`, `world_id`, `group_id`, `owner`) " +
|
||||
"VALUES (?, " + this.worldId + ", ?, ?)"
|
||||
);
|
||||
|
||||
Set<String> groupVar = domain.getGroups();
|
||||
for (Integer group : getGroupIds(groupVar.toArray(new String[groupVar.size()])).values()) {
|
||||
insertGroupsForRegion.setString(1, region.getId().toLowerCase());
|
||||
insertGroupsForRegion.setInt(2, group);
|
||||
insertGroupsForRegion.setBoolean(3, owners);
|
||||
|
||||
insertGroupsForRegion.execute();
|
||||
}
|
||||
} finally {
|
||||
closeQuietly(deleteGroupsForRegion);
|
||||
closeQuietly(deleteUsersForRegion);
|
||||
closeQuietly(insertGroupsForRegion);
|
||||
closeQuietly(insertUsersForRegion);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <V> Object marshalFlag(Flag<V> flag, Object val) {
|
||||
return flag.marshal( (V) val );
|
||||
}
|
||||
|
||||
private void insertRegion(ProtectedRegion region, String type) throws SQLException {
|
||||
PreparedStatement insertRegionStatement = null;
|
||||
try {
|
||||
insertRegionStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO `" + config.sqlTablePrefix + "region` (" +
|
||||
"`id`, " +
|
||||
"`world_id`, " +
|
||||
"`type`, " +
|
||||
"`priority`, " +
|
||||
"`parent` " +
|
||||
") VALUES (?, ?, ?, ?, null)"
|
||||
);
|
||||
|
||||
insertRegionStatement.setString(1, region.getId().toLowerCase());
|
||||
insertRegionStatement.setInt(2, this.worldId);
|
||||
insertRegionStatement.setString(3, type);
|
||||
insertRegionStatement.setInt(4, region.getPriority());
|
||||
|
||||
insertRegionStatement.execute();
|
||||
} finally {
|
||||
closeQuietly(insertRegionStatement);
|
||||
}
|
||||
|
||||
updateFlags(region);
|
||||
|
||||
updatePlayerAndGroups(region, false);
|
||||
updatePlayerAndGroups(region, true);
|
||||
}
|
||||
|
||||
private void insertRegionCuboid(ProtectedCuboidRegion region) throws SQLException {
|
||||
insertRegion(region, "cuboid");
|
||||
|
||||
PreparedStatement insertCuboidRegionStatement = null;
|
||||
try {
|
||||
insertCuboidRegionStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO `" + config.sqlTablePrefix + "region_cuboid` (" +
|
||||
"`region_id`, " +
|
||||
"`world_id`, " +
|
||||
"`min_z`, " +
|
||||
"`min_y`, " +
|
||||
"`min_x`, " +
|
||||
"`max_z`, " +
|
||||
"`max_y`, " +
|
||||
"`max_x` " +
|
||||
") VALUES (?, " + this.worldId + ", ?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
|
||||
BlockVector min = region.getMinimumPoint();
|
||||
BlockVector max = region.getMaximumPoint();
|
||||
|
||||
insertCuboidRegionStatement.setString(1, region.getId().toLowerCase());
|
||||
insertCuboidRegionStatement.setInt(2, min.getBlockZ());
|
||||
insertCuboidRegionStatement.setInt(3, min.getBlockY());
|
||||
insertCuboidRegionStatement.setInt(4, min.getBlockX());
|
||||
insertCuboidRegionStatement.setInt(5, max.getBlockZ());
|
||||
insertCuboidRegionStatement.setInt(6, max.getBlockY());
|
||||
insertCuboidRegionStatement.setInt(7, max.getBlockX());
|
||||
|
||||
insertCuboidRegionStatement.execute();
|
||||
} finally {
|
||||
closeQuietly(insertCuboidRegionStatement);
|
||||
}
|
||||
}
|
||||
|
||||
private void insertRegionPoly2D(ProtectedPolygonalRegion region) throws SQLException {
|
||||
insertRegion(region, "poly2d");
|
||||
|
||||
PreparedStatement insertPoly2dRegionStatement = null;
|
||||
try {
|
||||
insertPoly2dRegionStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO `" + config.sqlTablePrefix + "region_poly2d` (" +
|
||||
"`region_id`, " +
|
||||
"`world_id`, " +
|
||||
"`max_y`, " +
|
||||
"`min_y` " +
|
||||
") VALUES (?, " + this.worldId + ", ?, ?)"
|
||||
);
|
||||
|
||||
insertPoly2dRegionStatement.setString(1, region.getId().toLowerCase());
|
||||
insertPoly2dRegionStatement.setInt(2, region.getMaximumPoint().getBlockY());
|
||||
insertPoly2dRegionStatement.setInt(3, region.getMinimumPoint().getBlockY());
|
||||
|
||||
insertPoly2dRegionStatement.execute();
|
||||
} finally {
|
||||
closeQuietly(insertPoly2dRegionStatement);
|
||||
}
|
||||
|
||||
updatePoly2dPoints(region);
|
||||
}
|
||||
|
||||
private void updatePoly2dPoints(ProtectedPolygonalRegion region) throws SQLException {
|
||||
PreparedStatement clearPoly2dPointsForRegionStatement = null;
|
||||
PreparedStatement insertPoly2dPointStatement = null;
|
||||
|
||||
try {
|
||||
clearPoly2dPointsForRegionStatement = this.conn.prepareStatement(
|
||||
"DELETE FROM `" + config.sqlTablePrefix + "region_poly2d_point` " +
|
||||
"WHERE `region_id` = ? " +
|
||||
"AND `world_id` = " + this.worldId
|
||||
);
|
||||
|
||||
clearPoly2dPointsForRegionStatement.setString(1, region.getId().toLowerCase());
|
||||
|
||||
clearPoly2dPointsForRegionStatement.execute();
|
||||
|
||||
insertPoly2dPointStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO `" + config.sqlTablePrefix + "region_poly2d_point` (" +
|
||||
"`id`, " +
|
||||
"`region_id`, " +
|
||||
"`world_id`, " +
|
||||
"`z`, " +
|
||||
"`x` " +
|
||||
") VALUES (null, ?, " + this.worldId + ", ?, ?)"
|
||||
);
|
||||
|
||||
String lowerId = region.getId().toLowerCase();
|
||||
for (BlockVector2D point : region.getPoints()) {
|
||||
insertPoly2dPointStatement.setString(1, lowerId);
|
||||
insertPoly2dPointStatement.setInt(2, point.getBlockZ());
|
||||
insertPoly2dPointStatement.setInt(3, point.getBlockX());
|
||||
|
||||
insertPoly2dPointStatement.execute();
|
||||
}
|
||||
} finally {
|
||||
closeQuietly(clearPoly2dPointsForRegionStatement);
|
||||
closeQuietly(insertPoly2dPointStatement);
|
||||
}
|
||||
}
|
||||
|
||||
private void insertRegionGlobal(GlobalProtectedRegion region) throws SQLException {
|
||||
insertRegion(region, "global");
|
||||
}
|
||||
|
||||
private void updateRegion(ProtectedRegion region, String type) throws SQLException {
|
||||
PreparedStatement updateRegionStatement = null;
|
||||
try {
|
||||
updateRegionStatement = this.conn.prepareStatement(
|
||||
"UPDATE `" + config.sqlTablePrefix + "region` SET " +
|
||||
"`priority` = ? WHERE `id` = ? AND `world_id` = " + this.worldId
|
||||
);
|
||||
|
||||
updateRegionStatement.setInt(1, region.getPriority());
|
||||
updateRegionStatement.setString(2, region.getId().toLowerCase());
|
||||
|
||||
updateRegionStatement.execute();
|
||||
} finally {
|
||||
closeQuietly(updateRegionStatement);
|
||||
}
|
||||
|
||||
updateFlags(region);
|
||||
|
||||
updatePlayerAndGroups(region, false);
|
||||
updatePlayerAndGroups(region, true);
|
||||
}
|
||||
|
||||
private void updateRegionCuboid(ProtectedCuboidRegion region) throws SQLException {
|
||||
updateRegion(region, "cuboid");
|
||||
|
||||
PreparedStatement updateCuboidRegionStatement = null;
|
||||
try {
|
||||
updateCuboidRegionStatement = this.conn.prepareStatement(
|
||||
"UPDATE `" + config.sqlTablePrefix + "region_cuboid` SET " +
|
||||
"`min_z` = ?, " +
|
||||
"`min_y` = ?, " +
|
||||
"`min_x` = ?, " +
|
||||
"`max_z` = ?, " +
|
||||
"`max_y` = ?, " +
|
||||
"`max_x` = ? " +
|
||||
"WHERE `region_id` = ? " +
|
||||
"AND `world_id` = " + this.worldId
|
||||
);
|
||||
|
||||
BlockVector min = region.getMinimumPoint();
|
||||
BlockVector max = region.getMaximumPoint();
|
||||
|
||||
updateCuboidRegionStatement.setInt(1, min.getBlockZ());
|
||||
updateCuboidRegionStatement.setInt(2, min.getBlockY());
|
||||
updateCuboidRegionStatement.setInt(3, min.getBlockX());
|
||||
updateCuboidRegionStatement.setInt(4, max.getBlockZ());
|
||||
updateCuboidRegionStatement.setInt(5, max.getBlockY());
|
||||
updateCuboidRegionStatement.setInt(6, max.getBlockX());
|
||||
updateCuboidRegionStatement.setString(7, region.getId().toLowerCase());
|
||||
|
||||
updateCuboidRegionStatement.execute();
|
||||
} finally {
|
||||
closeQuietly(updateCuboidRegionStatement);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRegionPoly2D(ProtectedPolygonalRegion region) throws SQLException {
|
||||
updateRegion(region, "poly2d");
|
||||
|
||||
PreparedStatement updatePoly2dRegionStatement = null;
|
||||
try {
|
||||
updatePoly2dRegionStatement = this.conn.prepareStatement(
|
||||
"UPDATE `" + config.sqlTablePrefix + "region_poly2d` SET " +
|
||||
"`max_y` = ?, " +
|
||||
"`min_y` = ? " +
|
||||
"WHERE `region_id` = ? " +
|
||||
"AND `world_id` = " + this.worldId
|
||||
);
|
||||
|
||||
updatePoly2dRegionStatement.setInt(1, region.getMaximumPoint().getBlockY());
|
||||
updatePoly2dRegionStatement.setInt(2, region.getMinimumPoint().getBlockY());
|
||||
updatePoly2dRegionStatement.setString(3, region.getId().toLowerCase());
|
||||
|
||||
updatePoly2dRegionStatement.execute();
|
||||
} finally {
|
||||
closeQuietly(updatePoly2dRegionStatement);
|
||||
}
|
||||
updatePoly2dPoints(region);
|
||||
}
|
||||
|
||||
private void updateRegionGlobal(GlobalProtectedRegion region) throws SQLException {
|
||||
updateRegion(region, "global");
|
||||
}
|
||||
|
||||
}
|
@ -41,6 +41,15 @@ public EnumFlag(String name, Class<T> enumClass) {
|
||||
this.enumClass = enumClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enum class.
|
||||
*
|
||||
* @return the enum class
|
||||
*/
|
||||
public Class<T> getEnumClass() {
|
||||
return enumClass;
|
||||
}
|
||||
|
||||
private T findValue(String input) throws IllegalArgumentException {
|
||||
if (input != null) {
|
||||
input = input.toUpperCase();
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author sk89q
|
||||
@ -56,7 +58,14 @@ public RegionGroupFlag getRegionGroupFlag() {
|
||||
public abstract T parseInput(WorldGuardPlugin plugin, CommandSender sender,
|
||||
String input) throws InvalidFlagFormat;
|
||||
|
||||
public abstract T unmarshal(Object o);
|
||||
public abstract T unmarshal(@Nullable Object o);
|
||||
|
||||
public abstract Object marshal(T o);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + "{" +
|
||||
"name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author sk89q
|
||||
@ -85,8 +87,7 @@ public static boolean isMember(ProtectedRegion region, RegionGroup group, LocalP
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isMember(ApplicableRegionSet set,
|
||||
RegionGroup group, LocalPlayer player) {
|
||||
public static boolean isMember(ApplicableRegionSet set, @Nullable RegionGroup group, LocalPlayer player) {
|
||||
if (group == null || group == RegionGroup.ALL) {
|
||||
return true;
|
||||
} else if (group == RegionGroup.OWNERS) {
|
||||
|
@ -19,16 +19,15 @@
|
||||
|
||||
package com.sk89q.worldguard.protection.flags;
|
||||
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
|
||||
/**
|
||||
* Represents a flag that consists of a set.
|
||||
*
|
||||
@ -48,6 +47,15 @@ public SetFlag(String name, Flag<T> subFlag) {
|
||||
this.subFlag = subFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the flag that is stored in this flag.
|
||||
*
|
||||
* @return the stored flag type
|
||||
*/
|
||||
public Flag<T> getType() {
|
||||
return subFlag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> parseInput(WorldGuardPlugin plugin, CommandSender sender,
|
||||
String input) throws InvalidFlagFormat {
|
||||
|
@ -1,210 +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.managers;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.UnsupportedIntersectionException;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
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.
|
||||
*/
|
||||
public class FlatRegionManager extends RegionManager {
|
||||
|
||||
/**
|
||||
* List of protected regions.
|
||||
*/
|
||||
private ConcurrentMap<String, ProtectedRegion> regions = new ConcurrentHashMap<String, ProtectedRegion>();
|
||||
|
||||
/**
|
||||
* Construct the manager.
|
||||
*
|
||||
* @param regionLoader The loader for regions
|
||||
*/
|
||||
public FlatRegionManager(ProtectionDatabase regionLoader) {
|
||||
super(regionLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ProtectedRegion> getRegions() {
|
||||
return regions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegions(Map<String, ProtectedRegion> regions) {
|
||||
this.regions = new ConcurrentHashMap<String, ProtectedRegion>(regions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRegion(ProtectedRegion region) {
|
||||
regions.put(region.getId().toLowerCase(), region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRegion(String id) {
|
||||
ProtectedRegion region = regions.get(id.toLowerCase());
|
||||
regions.remove(id.toLowerCase());
|
||||
|
||||
if (region != null) {
|
||||
List<String> removeRegions = new ArrayList<String>();
|
||||
for (ProtectedRegion curRegion : regions.values()) {
|
||||
if (curRegion.getParent() == region) {
|
||||
removeRegions.add(curRegion.getId().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
for (String remId : removeRegions) {
|
||||
removeRegion(remId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRegion(String id) {
|
||||
return regions.containsKey(id.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicableRegionSet getApplicableRegions(Vector pt) {
|
||||
TreeSet<ProtectedRegion> appRegions =
|
||||
new TreeSet<ProtectedRegion>();
|
||||
|
||||
for (ProtectedRegion region : regions.values()) {
|
||||
if (region.contains(pt)) {
|
||||
appRegions.add(region);
|
||||
|
||||
ProtectedRegion parent = region.getParent();
|
||||
|
||||
while (parent != null) {
|
||||
if (!appRegions.contains(parent)) {
|
||||
appRegions.add(parent);
|
||||
}
|
||||
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ApplicableRegionSet(appRegions, regions.get("__global__"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object for a region for rules to be applied with.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/*@Override
|
||||
public ApplicableRegionSet getApplicableRegions(ProtectedRegion checkRegion) {
|
||||
|
||||
List<ProtectedRegion> appRegions = new ArrayList<ProtectedRegion>();
|
||||
appRegions.addAll(regions.values());
|
||||
|
||||
List<ProtectedRegion> intersectRegions;
|
||||
try {
|
||||
intersectRegions = checkRegion.getIntersectingRegions(appRegions);
|
||||
} catch (Exception e) {
|
||||
intersectRegions = new ArrayList<ProtectedRegion>();
|
||||
}
|
||||
|
||||
return new ApplicableRegionSet(intersectRegions, regions.get("__global__"));
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public List<String> getApplicableRegionsIDs(Vector pt) {
|
||||
List<String> applicable = new ArrayList<String>();
|
||||
|
||||
for (Map.Entry<String, ProtectedRegion> entry : regions.entrySet()) {
|
||||
if (entry.getValue().contains(pt)) {
|
||||
applicable.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
return applicable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicableRegionSet getApplicableRegions(ProtectedRegion checkRegion) {
|
||||
|
||||
List<ProtectedRegion> appRegions = new ArrayList<ProtectedRegion>();
|
||||
appRegions.addAll(regions.values());
|
||||
|
||||
List<ProtectedRegion> intersectRegions;
|
||||
|
||||
try {
|
||||
intersectRegions = checkRegion.getIntersectingRegions(appRegions);
|
||||
} catch (Exception e) {
|
||||
intersectRegions = new ArrayList<ProtectedRegion>();
|
||||
}
|
||||
|
||||
return new ApplicableRegionSet(intersectRegions, regions.get("__global__"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean overlapsUnownedRegion(ProtectedRegion checkRegion, LocalPlayer player) {
|
||||
List<ProtectedRegion> appRegions = new ArrayList<ProtectedRegion>();
|
||||
|
||||
for (ProtectedRegion other : regions.values()) {
|
||||
if (other.getOwners().contains(player)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
appRegions.add(other);
|
||||
}
|
||||
|
||||
List<ProtectedRegion> intersectRegions;
|
||||
try {
|
||||
intersectRegions = checkRegion.getIntersectingRegions(appRegions);
|
||||
} catch (UnsupportedIntersectionException e) {
|
||||
intersectRegions = new ArrayList<ProtectedRegion>();
|
||||
}
|
||||
|
||||
return !intersectRegions.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return regions.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionCountOfPlayer(LocalPlayer player) {
|
||||
int count = 0;
|
||||
|
||||
for (ProtectedRegion region : regions.values()) {
|
||||
if (region.getOwners().contains(player)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
@ -1,243 +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.managers;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegionMBRConverter;
|
||||
import org.khelekore.prtree.MBR;
|
||||
import org.khelekore.prtree.MBRConverter;
|
||||
import org.khelekore.prtree.PRTree;
|
||||
import org.khelekore.prtree.SimpleMBR;
|
||||
|
||||
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 {
|
||||
|
||||
private static final int BRANCH_FACTOR = 30;
|
||||
|
||||
private MBRConverter<ProtectedRegion> converter = new ProtectedRegionMBRConverter();
|
||||
private RegionsContainer data = new RegionsContainer();
|
||||
|
||||
/**
|
||||
* Construct the manager.
|
||||
*
|
||||
* @param regionLoader The region loader to use
|
||||
*/
|
||||
public PRTreeRegionManager(ProtectionDatabase regionLoader) {
|
||||
super(regionLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ProtectedRegion> getRegions() {
|
||||
return data.regions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegions(Map<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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRegion(ProtectedRegion region) {
|
||||
RegionsContainer data = this.data;
|
||||
data.regions.put(region.getId().toLowerCase(), region);
|
||||
PRTree<ProtectedRegion> tree = new PRTree<ProtectedRegion>(converter, BRANCH_FACTOR);
|
||||
tree.load(data.regions.values());
|
||||
this.data = new RegionsContainer(data.regions, tree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRegion(String id) {
|
||||
return data.regions.containsKey(id.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRegion(String id) {
|
||||
RegionsContainer data = this.data;
|
||||
|
||||
ProtectedRegion region = data.regions.get(id.toLowerCase());
|
||||
|
||||
data.regions.remove(id.toLowerCase());
|
||||
|
||||
if (region != null) {
|
||||
List<String> removeRegions = new ArrayList<String>();
|
||||
for (ProtectedRegion curRegion : data.regions.values()) {
|
||||
if (curRegion.getParent() == region) {
|
||||
removeRegions.add(curRegion.getId().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
for (String remId : removeRegions) {
|
||||
removeRegion(remId);
|
||||
}
|
||||
}
|
||||
|
||||
PRTree<ProtectedRegion> tree = new PRTree<ProtectedRegion>(converter, BRANCH_FACTOR);
|
||||
tree.load(data.regions.values());
|
||||
this.data = new RegionsContainer(data.regions, tree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicableRegionSet getApplicableRegions(Vector pt) {
|
||||
RegionsContainer data = this.data;
|
||||
|
||||
// Floor the vector to ensure we get accurate points
|
||||
pt = pt.floor();
|
||||
|
||||
List<ProtectedRegion> appRegions = new ArrayList<ProtectedRegion>();
|
||||
MBR pointMBR = new SimpleMBR(pt.getX(), pt.getX(), pt.getY(), pt.getY(), pt.getZ(), pt.getZ());
|
||||
|
||||
for (ProtectedRegion region : data.tree.find(pointMBR)) {
|
||||
if (region.contains(pt) && !appRegions.contains(region)) {
|
||||
appRegions.add(region);
|
||||
|
||||
ProtectedRegion parent = region.getParent();
|
||||
|
||||
while (parent != null) {
|
||||
if (!appRegions.contains(parent)) {
|
||||
appRegions.add(parent);
|
||||
}
|
||||
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(appRegions);
|
||||
|
||||
return new ApplicableRegionSet(appRegions, data.regions.get("__global__"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicableRegionSet getApplicableRegions(ProtectedRegion checkRegion) {
|
||||
RegionsContainer data = this.data;
|
||||
|
||||
List<ProtectedRegion> appRegions = new ArrayList<ProtectedRegion>();
|
||||
appRegions.addAll(data.regions.values());
|
||||
|
||||
List<ProtectedRegion> intersectRegions;
|
||||
try {
|
||||
intersectRegions = checkRegion.getIntersectingRegions(appRegions);
|
||||
} catch (Exception e) {
|
||||
intersectRegions = new ArrayList<ProtectedRegion>();
|
||||
}
|
||||
|
||||
return new ApplicableRegionSet(intersectRegions, data.regions.get("__global__"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getApplicableRegionsIDs(Vector pt) {
|
||||
RegionsContainer data = this.data;
|
||||
|
||||
// Floor the vector to ensure we get accurate points
|
||||
pt = pt.floor();
|
||||
|
||||
List<String> applicable = new ArrayList<String>();
|
||||
MBR pointMBR = new SimpleMBR(pt.getX(), pt.getX(), pt.getY(), pt.getY(), pt.getZ(), pt.getZ());
|
||||
|
||||
for (ProtectedRegion region : data.tree.find(pointMBR)) {
|
||||
if (region.contains(pt) && !applicable.contains(region.getId())) {
|
||||
applicable.add(region.getId());
|
||||
|
||||
ProtectedRegion parent = region.getParent();
|
||||
|
||||
while (parent != null) {
|
||||
if (!applicable.contains(parent.getId())) {
|
||||
applicable.add(parent.getId());
|
||||
}
|
||||
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return applicable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean overlapsUnownedRegion(ProtectedRegion checkRegion, LocalPlayer player) {
|
||||
RegionsContainer data = this.data;
|
||||
|
||||
List<ProtectedRegion> appRegions = new ArrayList<ProtectedRegion>();
|
||||
|
||||
for (ProtectedRegion other : data.regions.values()) {
|
||||
if (other.getOwners().contains(player)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
appRegions.add(other);
|
||||
}
|
||||
|
||||
List<ProtectedRegion> intersectRegions;
|
||||
try {
|
||||
intersectRegions = checkRegion.getIntersectingRegions(appRegions);
|
||||
} catch (Exception e) {
|
||||
intersectRegions = new ArrayList<ProtectedRegion>();
|
||||
}
|
||||
|
||||
return !intersectRegions.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return data.regions.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegionCountOfPlayer(LocalPlayer player) {
|
||||
int count = 0;
|
||||
|
||||
for (Map.Entry<String, ProtectedRegion> entry : data.regions.entrySet()) {
|
||||
if (entry.getValue().getOwners().contains(player)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private class RegionsContainer {
|
||||
private final ConcurrentMap<String, ProtectedRegion> regions;
|
||||
private final PRTree<ProtectedRegion> tree;
|
||||
|
||||
private RegionsContainer() {
|
||||
regions = new ConcurrentHashMap<String, ProtectedRegion>();
|
||||
tree = new PRTree<ProtectedRegion>(converter, BRANCH_FACTOR);
|
||||
}
|
||||
|
||||
private RegionsContainer(ConcurrentMap<String, ProtectedRegion> regions, PRTree<ProtectedRegion> tree) {
|
||||
this.regions = regions;
|
||||
this.tree = tree;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.managers;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A consumer predicate that adds regions to a collection.
|
||||
*
|
||||
* <p>This class can also add the parents of regions that are visited
|
||||
* to the collection, although it may result in duplicates in the collection
|
||||
* if the collection is not a set.</p>
|
||||
*/
|
||||
class RegionCollectionConsumer implements Predicate<ProtectedRegion> {
|
||||
|
||||
private final Collection<ProtectedRegion> collection;
|
||||
private final boolean addParents;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param collection the collection to add regions to
|
||||
* @param addParents true to also add the parents to the collection
|
||||
*/
|
||||
RegionCollectionConsumer(Collection<ProtectedRegion> collection, boolean addParents) {
|
||||
checkNotNull(collection);
|
||||
|
||||
this.collection = collection;
|
||||
this.addParents = addParents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(ProtectedRegion region) {
|
||||
collection.add(region);
|
||||
|
||||
if (addParents) {
|
||||
ProtectedRegion parent = region.getParent();
|
||||
|
||||
while (parent != null) {
|
||||
collection.add(parent);
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.managers;
|
||||
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Describes the difference in region data.
|
||||
*/
|
||||
public final class RegionDifference {
|
||||
|
||||
private final Set<ProtectedRegion> changed;
|
||||
private final Set<ProtectedRegion> removed;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param changed a set of regions that were changed or added
|
||||
* @param removed a set of regions that were removed
|
||||
*/
|
||||
public RegionDifference(Set<ProtectedRegion> changed, Set<ProtectedRegion> removed) {
|
||||
checkNotNull(changed);
|
||||
checkNotNull(removed);
|
||||
|
||||
this.changed = changed;
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the regions that were changed or added.
|
||||
*
|
||||
* @return regions
|
||||
*/
|
||||
public Set<ProtectedRegion> getChanged() {
|
||||
return Collections.unmodifiableSet(changed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the regions that were removed.
|
||||
*
|
||||
* @return regions
|
||||
*/
|
||||
public Set<ProtectedRegion> getRemoved() {
|
||||
return Collections.unmodifiableSet(removed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether there are changes or removals.
|
||||
*
|
||||
* @return true if there are changes
|
||||
*/
|
||||
public boolean containsChanges() {
|
||||
return !changed.isEmpty() || !removed.isEmpty();
|
||||
}
|
||||
|
||||
public void addAll(RegionDifference diff) {
|
||||
changed.addAll(diff.getChanged());
|
||||
removed.addAll(diff.getRemoved());
|
||||
}
|
||||
|
||||
}
|
@ -19,123 +19,204 @@
|
||||
|
||||
package com.sk89q.worldguard.protection.managers;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex;
|
||||
import com.sk89q.worldguard.protection.managers.index.RegionIndex;
|
||||
import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.util.Normal;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* An abstract class for getting, setting, and looking up regions. The most
|
||||
* simple implementation uses a flat list and iterates through the entire list
|
||||
* to look for applicable regions, but a more complicated (and more efficient)
|
||||
* implementation may use space partitioning techniques.
|
||||
*
|
||||
* @author sk89q
|
||||
*/
|
||||
public abstract class RegionManager {
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
protected ProtectionDatabase loader;
|
||||
public final class RegionManager {
|
||||
|
||||
private final RegionStore store;
|
||||
private final Supplier<? extends ConcurrentRegionIndex> indexFactory;
|
||||
private ConcurrentRegionIndex index;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
* Create a new index.
|
||||
*
|
||||
* @param loader The loader for this region
|
||||
* @param store the region store
|
||||
* @param indexFactory the factory for creating new instances of the index
|
||||
*/
|
||||
public RegionManager(ProtectionDatabase loader) {
|
||||
this.loader = loader;
|
||||
public RegionManager(RegionStore store, Supplier<? extends ConcurrentRegionIndex> indexFactory) {
|
||||
checkNotNull(store);
|
||||
checkNotNull(indexFactory);
|
||||
|
||||
this.store = store;
|
||||
this.indexFactory = indexFactory;
|
||||
this.index = indexFactory.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the list of regions. If the regions do not load properly, then
|
||||
* the existing list should be used (as stored previously).
|
||||
* Load regions from storage and replace the index on this manager with
|
||||
* the regions loaded from the store.
|
||||
*
|
||||
* @throws ProtectionDatabaseException when an error occurs
|
||||
* <p>This method will block until the save completes, but it will
|
||||
* not block access to the region data from other threads, nor will it
|
||||
* prevent the creation or modification of regions in the index while
|
||||
* a new collection of regions is loaded from storage.</p>
|
||||
*
|
||||
* @throws IOException thrown when loading fails
|
||||
*/
|
||||
public void load() throws ProtectionDatabaseException {
|
||||
loader.load(this);
|
||||
public void load() throws IOException {
|
||||
setRegions(store.loadAll());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the list of regions. If the regions do not load properly, then
|
||||
* the existing list should be used (as stored previously).
|
||||
* Save a snapshot of all the regions as it is right now to storage.
|
||||
*
|
||||
* @param async true to attempt to save the data asynchronously
|
||||
* @throws IOException
|
||||
*/
|
||||
public ListenableFuture<?> load(boolean async) {
|
||||
return loader.load(this, async);
|
||||
public void save() throws IOException {
|
||||
store.saveAll(new HashSet<ProtectedRegion>(getValuesCopy()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the list of regions.
|
||||
* Save changes to the region index to disk, preferring to only save
|
||||
* the changes (rather than the whole index), but choosing to save the
|
||||
* whole index if the underlying store does not support partial saves.
|
||||
*
|
||||
* @throws ProtectionDatabaseException when an error occurs while saving
|
||||
* <p>This method does nothing if there are no changes.</p>
|
||||
*
|
||||
* @throws IOException thrown on save error
|
||||
*/
|
||||
public void save() throws ProtectionDatabaseException {
|
||||
loader.save(this);
|
||||
public void saveChanges() throws IOException {
|
||||
RegionDifference diff = index.getAndClearDifference();
|
||||
|
||||
if (diff.containsChanges()) {
|
||||
try {
|
||||
store.saveChanges(diff);
|
||||
} catch (DifferenceSaveException e) {
|
||||
save(); // Partial save is not supported
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the list of regions.
|
||||
* Get an unmodifiable map of regions containing the state of the
|
||||
* index at the time of call.
|
||||
*
|
||||
* @param async true to attempt to save the data asynchronously
|
||||
* <p>This call is relatively heavy (and may block other threads),
|
||||
* so refrain from calling it frequently.</p>
|
||||
*
|
||||
* @return a map of regions
|
||||
*/
|
||||
public ListenableFuture<?> save(boolean async) {
|
||||
return loader.save(this, async);
|
||||
public Map<String, ProtectedRegion> getRegions() {
|
||||
Map<String, ProtectedRegion> map = new HashMap<String, ProtectedRegion>();
|
||||
for (ProtectedRegion region : index.values()) {
|
||||
map.put(Normal.normalize(region.getId()), region);
|
||||
}
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map of protected regions. Use one of the region manager methods
|
||||
* if possible if working with regions.
|
||||
* Replace the index with the regions in the given map.
|
||||
*
|
||||
* @return map of regions, with keys being region IDs (lowercase)
|
||||
* <p>The parents of the regions will also be added to the index, even
|
||||
* if they are not in the provided map.</p>
|
||||
*
|
||||
* @param regions a map of regions
|
||||
*/
|
||||
public abstract Map<String, ProtectedRegion> getRegions();
|
||||
public void setRegions(Map<String, ProtectedRegion> regions) {
|
||||
checkNotNull(regions);
|
||||
|
||||
setRegions(regions.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of protected regions. Keys should be lowercase in the given
|
||||
* map fo regions.
|
||||
* Replace the index with the regions in the given collection.
|
||||
*
|
||||
* @param regions map of regions
|
||||
* <p>The parents of the regions will also be added to the index, even
|
||||
* if they are not in the provided map.</p>
|
||||
*
|
||||
* @param regions a collection of regions
|
||||
*/
|
||||
public abstract void setRegions(Map<String, ProtectedRegion> regions);
|
||||
public void setRegions(Collection<ProtectedRegion> regions) {
|
||||
checkNotNull(regions);
|
||||
|
||||
ConcurrentRegionIndex newIndex = indexFactory.get();
|
||||
newIndex.addAll(regions);
|
||||
newIndex.getAndClearDifference(); // Clear changes
|
||||
this.index = newIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a region. If a region by the given name already exists, then
|
||||
* the existing region will be replaced.
|
||||
* Aad a region to the manager.
|
||||
*
|
||||
* @param region region to add
|
||||
* <p>The parents of the region will also be added to the index.</p>
|
||||
*
|
||||
* @param region the region
|
||||
*/
|
||||
public abstract void addRegion(ProtectedRegion region);
|
||||
public void addRegion(ProtectedRegion region) {
|
||||
checkNotNull(region);
|
||||
index.add(region);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether a region exists by an ID.
|
||||
* Return whether the index contains a region by the given name,
|
||||
* with equality determined by {@link Normal}.
|
||||
*
|
||||
* @param id id of the region, can be mixed-case
|
||||
* @return whether the region exists
|
||||
* @param id the name of the region
|
||||
* @return true if this index contains the region
|
||||
*/
|
||||
public abstract boolean hasRegion(String id);
|
||||
public boolean hasRegion(String id) {
|
||||
return index.contains(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a region by its ID. Includes symbolic names like #<index>
|
||||
* Get the region named by the given name (equality determined using
|
||||
* {@link Normal}).
|
||||
*
|
||||
* @param id id of the region, can be mixed-case
|
||||
* @return region or null if it doesn't exist
|
||||
* @param id the name of the region
|
||||
* @return a region or {@code null}
|
||||
* @deprecated use {@link #matchRegion(String)}
|
||||
*/
|
||||
@Nullable
|
||||
public ProtectedRegion getRegion(String id) {
|
||||
if (id.startsWith("#")) {
|
||||
checkNotNull(id);
|
||||
return index.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a region using either the pattern {@code #{region_index}} or
|
||||
* simply by the exact name of the region.
|
||||
*
|
||||
* @param pattern the pattern
|
||||
* @return a region
|
||||
*/
|
||||
@Nullable
|
||||
public ProtectedRegion matchRegion(String pattern) {
|
||||
checkNotNull(pattern);
|
||||
|
||||
if (pattern.startsWith("#")) {
|
||||
int index;
|
||||
try {
|
||||
index = Integer.parseInt(id.substring(1)) - 1;
|
||||
index = Integer.parseInt(pattern.substring(1)) - 1;
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
for (ProtectedRegion region : getRegions().values()) {
|
||||
for (ProtectedRegion region : this.index.values()) {
|
||||
if (index == 0) {
|
||||
return region;
|
||||
}
|
||||
@ -144,87 +225,167 @@ public ProtectedRegion getRegion(String id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getRegionExact(id);
|
||||
return getRegion(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a region by its ID.
|
||||
* Remove a region from the index with the given name, opting to remove
|
||||
* the children of the removed region.
|
||||
*
|
||||
* @param id id of the region, can be mixed-case
|
||||
* @return region or null if it doesn't exist
|
||||
* @param id the name of the region
|
||||
* @return a list of removed regions where the first entry is the region specified by {@code id}
|
||||
*/
|
||||
public ProtectedRegion getRegionExact(String id) {
|
||||
return getRegions().get(id.toLowerCase());
|
||||
@Nullable
|
||||
public Set<ProtectedRegion> removeRegion(String id) {
|
||||
return removeRegion(id, RemovalStrategy.REMOVE_CHILDREN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a region, including inheriting children.
|
||||
* Remove a region from the index with the given name.
|
||||
*
|
||||
* @param id id of the region, can be mixed-case
|
||||
* @param id the name of the region
|
||||
* @param strategy what to do with children
|
||||
* @return a list of removed regions where the first entry is the region specified by {@code id}
|
||||
*/
|
||||
public abstract void removeRegion(String id);
|
||||
@Nullable
|
||||
public Set<ProtectedRegion> removeRegion(String id, RemovalStrategy strategy) {
|
||||
return index.remove(id, strategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object for a point for rules to be applied with. Use this in order
|
||||
* to query for flag data or membership data for a given point.
|
||||
* Query for effective flags and owners for the given positive.
|
||||
*
|
||||
* @param loc Bukkit location
|
||||
* @return applicable region set
|
||||
* @param position the position
|
||||
* @return the query object
|
||||
*/
|
||||
public ApplicableRegionSet getApplicableRegions(Vector position) {
|
||||
checkNotNull(position);
|
||||
|
||||
TreeSet<ProtectedRegion> regions = new TreeSet<ProtectedRegion>();
|
||||
index.applyContaining(position, new RegionCollectionConsumer(regions, true));
|
||||
return new ApplicableRegionSet(regions, index.get("__global__"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for effective flags and owners for the area represented
|
||||
* by the given region.
|
||||
*
|
||||
* @param region the region
|
||||
* @return the query object
|
||||
*/
|
||||
public ApplicableRegionSet getApplicableRegions(ProtectedRegion region) {
|
||||
checkNotNull(region);
|
||||
|
||||
TreeSet<ProtectedRegion> regions = new TreeSet<ProtectedRegion>();
|
||||
index.applyIntersecting(region, new RegionCollectionConsumer(regions, true));
|
||||
return new ApplicableRegionSet(regions, index.get("__global__"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of region names for regions that contain the given position.
|
||||
*
|
||||
* @param position the position
|
||||
* @return a list of names
|
||||
*/
|
||||
public List<String> getApplicableRegionsIDs(Vector position) {
|
||||
checkNotNull(position);
|
||||
|
||||
final List<String> names = new ArrayList<String>();
|
||||
|
||||
index.applyContaining(position, new Predicate<ProtectedRegion>() {
|
||||
@Override
|
||||
public boolean apply(ProtectedRegion region) {
|
||||
return names.add(region.getId());
|
||||
}
|
||||
});
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether there are any regions intersecting the given region that
|
||||
* are not owned by the given player.
|
||||
*
|
||||
* @param region the region
|
||||
* @param player the player
|
||||
* @return true if there are such intersecting regions
|
||||
*/
|
||||
public boolean overlapsUnownedRegion(ProtectedRegion region, final LocalPlayer player) {
|
||||
checkNotNull(region);
|
||||
checkNotNull(player);
|
||||
|
||||
RegionIndex index = this.index;
|
||||
|
||||
final AtomicBoolean overlapsUnowned = new AtomicBoolean();
|
||||
|
||||
index.applyIntersecting(region, new Predicate<ProtectedRegion>() {
|
||||
@Override
|
||||
public boolean apply(ProtectedRegion test) {
|
||||
if (!test.getOwners().contains(player)) {
|
||||
overlapsUnowned.set(true);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return overlapsUnowned.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of regions.
|
||||
*
|
||||
* @return the number of regions
|
||||
*/
|
||||
public int size() {
|
||||
return index.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of regions that are owned by the given player.
|
||||
*
|
||||
* @param player the player
|
||||
* @return name number of regions that a player owns
|
||||
*/
|
||||
public int getRegionCountOfPlayer(final LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
|
||||
final AtomicInteger count = new AtomicInteger();
|
||||
|
||||
index.apply(new Predicate<ProtectedRegion>() {
|
||||
@Override
|
||||
public boolean apply(ProtectedRegion test) {
|
||||
if (test.getOwners().contains(player)) {
|
||||
count.incrementAndGet();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return count.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an {@link ArrayList} copy of regions in the index.
|
||||
*
|
||||
* @return a list
|
||||
*/
|
||||
private List<ProtectedRegion> getValuesCopy() {
|
||||
return new ArrayList<ProtectedRegion>(index.values());
|
||||
}
|
||||
|
||||
// =============== HELPER METHODS ===============
|
||||
|
||||
/**
|
||||
* Helper method for {@link #getApplicableRegions(Vector)} using Bukkit
|
||||
* locations.
|
||||
*
|
||||
* @param loc the location
|
||||
* @return an {@code ApplicableRegionSet}
|
||||
*/
|
||||
public ApplicableRegionSet getApplicableRegions(org.bukkit.Location loc) {
|
||||
return getApplicableRegions(com.sk89q.worldedit.bukkit.BukkitUtil.toVector(loc));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object for a point for rules to be applied with. Use this in order
|
||||
* to query for flag data or membership data for a given point.
|
||||
*
|
||||
* @param pt point
|
||||
* @return applicable region set
|
||||
*/
|
||||
public abstract ApplicableRegionSet getApplicableRegions(Vector pt);
|
||||
|
||||
/**
|
||||
* Get an object for a point for rules to be applied with. This gets
|
||||
* a set for the given reason.
|
||||
*
|
||||
* @param region region
|
||||
* @return regino set
|
||||
*/
|
||||
public abstract ApplicableRegionSet getApplicableRegions(
|
||||
ProtectedRegion region);
|
||||
|
||||
/**
|
||||
* Get a list of region IDs that contain a point.
|
||||
*
|
||||
* @param pt point
|
||||
* @return list of region Ids
|
||||
*/
|
||||
public abstract List<String> getApplicableRegionsIDs(Vector pt);
|
||||
|
||||
/**
|
||||
* Returns true if the provided region overlaps with any other region that
|
||||
* is not owned by the player.
|
||||
*
|
||||
* @param region region to check
|
||||
* @param player player to check against
|
||||
* @return whether there is an overlap
|
||||
*/
|
||||
public abstract boolean overlapsUnownedRegion(ProtectedRegion region,
|
||||
LocalPlayer player);
|
||||
|
||||
/**
|
||||
* Get the number of regions.
|
||||
*
|
||||
* @return number of regions
|
||||
*/
|
||||
public abstract int size();
|
||||
|
||||
/**
|
||||
* Get the number of regions for a player.
|
||||
*
|
||||
* @param player player
|
||||
* @return name number of regions that a player owns
|
||||
*/
|
||||
public abstract int getRegionCountOfPlayer(LocalPlayer player);
|
||||
}
|
||||
|
@ -0,0 +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.managers;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.index.RegionIndex;
|
||||
|
||||
/**
|
||||
* Determines the strategy regarding child regions when regions are
|
||||
* removed from a {@link RegionIndex}.
|
||||
*/
|
||||
public enum RemovalStrategy {
|
||||
|
||||
/**
|
||||
* Unset the parent in children regions.
|
||||
*/
|
||||
UNSET_PARENT_IN_CHILDREN,
|
||||
|
||||
/**
|
||||
* Remove any children under the removed regions.
|
||||
*/
|
||||
REMOVE_CHILDREN
|
||||
|
||||
}
|
@ -17,15 +17,11 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.migrator;
|
||||
package com.sk89q.worldguard.protection.managers.index;
|
||||
|
||||
/**
|
||||
* Represents a method of converting from one database to another.
|
||||
*
|
||||
* @author Nicholas Steicke
|
||||
* An abstract implementation of a region index to make implementations easier.
|
||||
*/
|
||||
public interface DatabaseMigrator {
|
||||
abstract class AbstractRegionIndex implements RegionIndex {
|
||||
|
||||
public void migrate() throws MigrationException;
|
||||
}
|
||||
|
@ -17,13 +17,16 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
package com.sk89q.worldguard.protection.managers.index;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.FlatRegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
public class FlatRegionManagerTest extends RegionOverlapTest {
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new FlatRegionManager(null);
|
||||
}
|
||||
/**
|
||||
* An implementation of a region index that supports concurrent access.
|
||||
*
|
||||
* <p>The mechanics of concurrent access should be similar to that of
|
||||
* {@link ConcurrentMap}. Spatial queries can lag behind changes on the data
|
||||
* for brief periods of time.</p>
|
||||
*/
|
||||
public interface ConcurrentRegionIndex extends RegionIndex {
|
||||
}
|
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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.managers.index;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.protection.managers.RegionDifference;
|
||||
import com.sk89q.worldguard.protection.managers.RemovalStrategy;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldguard.util.Normal.normalize;
|
||||
|
||||
/**
|
||||
* An index that stores regions in a hash map, which allows for fast lookup
|
||||
* by ID but O(n) performance for spatial queries.
|
||||
*
|
||||
* <p>This implementation supports concurrency to the extent that
|
||||
* a {@link ConcurrentMap} does.</p>
|
||||
*/
|
||||
public class HashMapIndex extends AbstractRegionIndex implements ConcurrentRegionIndex {
|
||||
|
||||
private final ConcurrentMap<String, ProtectedRegion> regions = new ConcurrentHashMap<String, ProtectedRegion>();
|
||||
private Set<ProtectedRegion> removed = new HashSet<ProtectedRegion>();
|
||||
private final Object lock = new Object();
|
||||
|
||||
/**
|
||||
* Called to rebuild the index after changes.
|
||||
*/
|
||||
protected void rebuildIndex() {
|
||||
// Can be implemented by subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the add operation.
|
||||
*
|
||||
* @param region the region
|
||||
*/
|
||||
private void performAdd(ProtectedRegion region) {
|
||||
checkNotNull(region);
|
||||
|
||||
region.setDirty(true);
|
||||
|
||||
synchronized (lock) {
|
||||
String normalId = normalize(region.getId());
|
||||
|
||||
ProtectedRegion existing = regions.get(normalId);
|
||||
|
||||
// Casing / form of ID has changed
|
||||
if (existing != null && !existing.getId().equals(region.getId())) {
|
||||
removed.add(existing);
|
||||
}
|
||||
|
||||
regions.put(normalId, region);
|
||||
|
||||
removed.remove(region);
|
||||
|
||||
ProtectedRegion parent = region.getParent();
|
||||
if (parent != null) {
|
||||
performAdd(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(Collection<ProtectedRegion> regions) {
|
||||
checkNotNull(regions);
|
||||
|
||||
synchronized (lock) {
|
||||
for (ProtectedRegion region : regions) {
|
||||
performAdd(region);
|
||||
}
|
||||
|
||||
rebuildIndex();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(ProtectedRegion region) {
|
||||
synchronized (lock) {
|
||||
performAdd(region);
|
||||
|
||||
rebuildIndex();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtectedRegion> remove(String id, RemovalStrategy strategy) {
|
||||
checkNotNull(id);
|
||||
checkNotNull(strategy);
|
||||
|
||||
Set<ProtectedRegion> removedSet = new HashSet<ProtectedRegion>();
|
||||
|
||||
synchronized (lock) {
|
||||
ProtectedRegion removed = regions.remove(normalize(id));
|
||||
|
||||
if (removed != null) {
|
||||
removedSet.add(removed);
|
||||
|
||||
Iterator<ProtectedRegion> it = regions.values().iterator();
|
||||
|
||||
// Handle children
|
||||
while (it.hasNext()) {
|
||||
ProtectedRegion current = it.next();
|
||||
ProtectedRegion parent = current.getParent();
|
||||
|
||||
if (parent != null && parent == removed) {
|
||||
switch (strategy) {
|
||||
case REMOVE_CHILDREN:
|
||||
removedSet.add(current);
|
||||
it.remove();
|
||||
break;
|
||||
case UNSET_PARENT_IN_CHILDREN:
|
||||
current.clearParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.removed.addAll(removedSet);
|
||||
|
||||
rebuildIndex();
|
||||
}
|
||||
|
||||
return removedSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String id) {
|
||||
return regions.containsKey(normalize(id));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ProtectedRegion get(String id) {
|
||||
return regions.get(normalize(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Predicate<ProtectedRegion> consumer) {
|
||||
for (ProtectedRegion region : regions.values()) {
|
||||
if (!consumer.apply(region)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyContaining(final Vector position, final Predicate<ProtectedRegion> consumer) {
|
||||
apply(new Predicate<ProtectedRegion>() {
|
||||
@Override
|
||||
public boolean apply(ProtectedRegion region) {
|
||||
return !region.contains(position) || consumer.apply(region);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyIntersecting(ProtectedRegion region, Predicate<ProtectedRegion> consumer) {
|
||||
for (ProtectedRegion found : region.getIntersectingRegions(regions.values())) {
|
||||
if (!consumer.apply(found)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return regions.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegionDifference getAndClearDifference() {
|
||||
synchronized (lock) {
|
||||
Set<ProtectedRegion> changed = new HashSet<ProtectedRegion>();
|
||||
Set<ProtectedRegion> removed = this.removed;
|
||||
|
||||
for (ProtectedRegion region : regions.values()) {
|
||||
if (region.isDirty()) {
|
||||
changed.add(region);
|
||||
region.setDirty(false);
|
||||
}
|
||||
}
|
||||
|
||||
this.removed = new HashSet<ProtectedRegion>();
|
||||
|
||||
return new RegionDifference(changed, removed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ProtectedRegion> values() {
|
||||
return Collections.unmodifiableCollection(regions.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
synchronized (lock) {
|
||||
if (!removed.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (ProtectedRegion region : regions.values()) {
|
||||
if (region.isDirty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirty(boolean dirty) {
|
||||
synchronized (lock) {
|
||||
if (!dirty) {
|
||||
removed.clear();
|
||||
}
|
||||
|
||||
for (ProtectedRegion region : regions.values()) {
|
||||
region.setDirty(dirty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory for new instances using this index.
|
||||
*/
|
||||
public static final class Factory implements Supplier<HashMapIndex> {
|
||||
@Override
|
||||
public HashMapIndex get() {
|
||||
return new HashMapIndex();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.managers.index;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegionMBRConverter;
|
||||
import org.khelekore.prtree.MBR;
|
||||
import org.khelekore.prtree.MBRConverter;
|
||||
import org.khelekore.prtree.PRTree;
|
||||
import org.khelekore.prtree.SimpleMBR;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An implementation of an index that uses {@link HashMapIndex} for queries
|
||||
* by region name and a priority R-tree for spatial queries.
|
||||
*
|
||||
* <p>At the moment, the R-tree is only utilized for the
|
||||
* {@link #applyContaining(Vector, Predicate)} method, and the underlying
|
||||
* hash map-based index is used for the other spatial queries. In addition,
|
||||
* every modification to the index requires the entire R-tree to be rebuilt,
|
||||
* although this operation is reasonably quick.</p>
|
||||
*
|
||||
* <p>This implementation is as thread-safe as the underlying
|
||||
* {@link HashMapIndex}, although spatial queries may lag behind changes
|
||||
* for very brief periods of time as the tree is rebuilt.</p>
|
||||
*/
|
||||
public class PriorityRTreeIndex extends HashMapIndex {
|
||||
|
||||
private static final int BRANCH_FACTOR = 30;
|
||||
private static final MBRConverter<ProtectedRegion> CONVERTER = new ProtectedRegionMBRConverter();
|
||||
|
||||
private PRTree<ProtectedRegion> tree;
|
||||
|
||||
public PriorityRTreeIndex() {
|
||||
tree = new PRTree<ProtectedRegion>(CONVERTER, BRANCH_FACTOR);
|
||||
tree.load(Collections.<ProtectedRegion>emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void rebuildIndex() {
|
||||
PRTree<ProtectedRegion> newTree = new PRTree<ProtectedRegion>(CONVERTER, BRANCH_FACTOR);
|
||||
newTree.load(values());
|
||||
this.tree = newTree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyContaining(Vector position, Predicate<ProtectedRegion> consumer) {
|
||||
// Floor the vector to ensure we get accurate points
|
||||
position = position.floor();
|
||||
|
||||
Set<ProtectedRegion> seen = new HashSet<ProtectedRegion>();
|
||||
MBR pointMBR = new SimpleMBR(position.getX(), position.getX(), position.getY(), position.getY(), position.getZ(), position.getZ());
|
||||
|
||||
for (ProtectedRegion region : tree.find(pointMBR)) {
|
||||
if (region.contains(position) && !seen.contains(region)) {
|
||||
seen.add(region);
|
||||
if (!consumer.apply(region)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyIntersecting(ProtectedRegion region, Predicate<ProtectedRegion> consumer) {
|
||||
super.applyIntersecting(region, consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory for new instances using this index.
|
||||
*/
|
||||
public static final class Factory implements Supplier<PriorityRTreeIndex> {
|
||||
@Override
|
||||
public PriorityRTreeIndex get() {
|
||||
return new PriorityRTreeIndex();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.managers.index;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.protection.managers.RegionDifference;
|
||||
import com.sk89q.worldguard.protection.managers.RemovalStrategy;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.util.ChangeTracked;
|
||||
import com.sk89q.worldguard.util.Normal;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An index of regions to allow for fast lookups of regions by their ID and
|
||||
* through spatial queries.
|
||||
*
|
||||
* <p>Indexes may be thread-unsafe.</p>
|
||||
*/
|
||||
public interface RegionIndex extends ChangeTracked {
|
||||
|
||||
/**
|
||||
* Add a region to this index, replacing any existing one with the same
|
||||
* name (equality determined using {@link Normal}).
|
||||
*
|
||||
* <p>The parents of the region will also be added to the index.</p>
|
||||
*
|
||||
* @param region the region
|
||||
*/
|
||||
void add(ProtectedRegion region);
|
||||
|
||||
/**
|
||||
* Add a list of regions to this index, replacing any existing one
|
||||
* with the same name (equality determined using {@link Normal}).
|
||||
*
|
||||
* <p>The parents of the region will also be added to the index.</p>
|
||||
*
|
||||
* @param regions a collections of regions
|
||||
*/
|
||||
void addAll(Collection<ProtectedRegion> regions);
|
||||
|
||||
/**
|
||||
* Remove a region from the index with the given name.
|
||||
*
|
||||
* @param id the name of the region
|
||||
* @param strategy what to do with children
|
||||
* @return a list of removed regions where the first entry is the region specified by {@code id}
|
||||
*/
|
||||
Set<ProtectedRegion> remove(String id, RemovalStrategy strategy);
|
||||
|
||||
/**
|
||||
* Test whether the index contains a region named by the given name
|
||||
* (equality determined using {@link Normal}).
|
||||
*
|
||||
* @param id the name of the region
|
||||
* @return true if the index contains the region
|
||||
*/
|
||||
boolean contains(String id);
|
||||
|
||||
/**
|
||||
* Get the region named by the given name (equality determined using
|
||||
* {@link Normal}).
|
||||
*
|
||||
* @param id the name of the region
|
||||
* @return a region or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
ProtectedRegion get(String id);
|
||||
|
||||
/**
|
||||
* Apply the given predicate to all the regions in the index
|
||||
* until there are no more regions or the predicate returns false.
|
||||
*
|
||||
* @param consumer a predicate that returns true to continue iterating
|
||||
*/
|
||||
void apply(Predicate<ProtectedRegion> consumer);
|
||||
|
||||
/**
|
||||
* Apply the given predicate to all regions that contain the given
|
||||
* position until there are no more regions or the predicate returns false.
|
||||
*
|
||||
* @param position the position
|
||||
* @param consumer a predicate that returns true to continue iterating
|
||||
*/
|
||||
void applyContaining(Vector position, Predicate<ProtectedRegion> consumer);
|
||||
|
||||
/**
|
||||
* Apply the given predicate to all regions that intersect the given
|
||||
* region until there are no more regions or the predicate returns false.
|
||||
*
|
||||
* @param region the intersecting region
|
||||
* @param consumer a predicate that returns true to continue iterating
|
||||
*/
|
||||
void applyIntersecting(ProtectedRegion region, Predicate<ProtectedRegion> consumer);
|
||||
|
||||
/**
|
||||
* Return the number of regions in the index.
|
||||
*
|
||||
* @return the number of regions
|
||||
*/
|
||||
int size();
|
||||
|
||||
/**
|
||||
* Get the list of changed or removed regions since last call and
|
||||
* clear those lists.
|
||||
*
|
||||
* @return the difference
|
||||
*/
|
||||
RegionDifference getAndClearDifference();
|
||||
|
||||
/**
|
||||
* Get an unmodifiable collection of regions stored in this index.
|
||||
*
|
||||
* @return a collection of regions
|
||||
*/
|
||||
Collection<ProtectedRegion> values();
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldGuard team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.managers.storage;
|
||||
|
||||
/**
|
||||
* Thrown when a partial save is not supported.
|
||||
*/
|
||||
public class DifferenceSaveException extends RegionStoreException {
|
||||
|
||||
public DifferenceSaveException() {
|
||||
}
|
||||
|
||||
public DifferenceSaveException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DifferenceSaveException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public DifferenceSaveException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.managers.storage;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.RegionDifference;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A memory store that saves the memory to a temporary variable.
|
||||
*/
|
||||
public class MemoryRegionStore implements RegionStore {
|
||||
|
||||
private Set<ProtectedRegion> regions = Collections.emptySet();
|
||||
|
||||
@Override
|
||||
public Set<ProtectedRegion> loadAll() throws IOException {
|
||||
return regions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll(Set<ProtectedRegion> regions) throws IOException {
|
||||
this.regions = Collections.unmodifiableSet(new HashSet<ProtectedRegion>(regions));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveChanges(RegionDifference difference) throws DifferenceSaveException, IOException {
|
||||
throw new DifferenceSaveException();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.managers.storage;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.RegionDifference;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An object that persists region data to a persistent store.
|
||||
*/
|
||||
public interface RegionStore {
|
||||
|
||||
/**
|
||||
* Load all regions from storage and place them into the passed map.
|
||||
*
|
||||
* <p>The map will only be modified from one thread. The keys of
|
||||
* each map entry will be in respect to the ID of the region but
|
||||
* transformed to be lowercase. Until this method returns, the map may not
|
||||
* be modified by any other thread simultaneously. If an exception is
|
||||
* thrown, then the state in which the map is left is undefined.</p>
|
||||
*
|
||||
* <p>The provided map should have reasonably efficient
|
||||
* {@code get()} and {@code put()} calls in order to maximize performance.
|
||||
* </p>
|
||||
*
|
||||
* @return a setf loaded regions
|
||||
* @throws IOException thrown on read error
|
||||
*/
|
||||
Set<ProtectedRegion> loadAll() throws IOException;
|
||||
|
||||
/**
|
||||
* Replace all the data in the store with the given collection of regions.
|
||||
*
|
||||
* @param regions a set of regions
|
||||
* @throws IOException thrown on write error
|
||||
*/
|
||||
void saveAll(Set<ProtectedRegion> regions) throws IOException;
|
||||
|
||||
/**
|
||||
* Perform a partial save that only commits changes, rather than the
|
||||
* entire region index.
|
||||
*
|
||||
* @param difference the difference
|
||||
* @throws DifferenceSaveException thrown if partial saves are not supported
|
||||
* @throws IOException thrown on write error
|
||||
*/
|
||||
void saveChanges(RegionDifference difference) throws DifferenceSaveException, IOException;
|
||||
|
||||
}
|
@ -17,26 +17,26 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases;
|
||||
package com.sk89q.worldguard.protection.managers.storage;
|
||||
|
||||
import java.lang.Exception;
|
||||
/**
|
||||
* Exceptions related to region stores inherit from this exception.
|
||||
*/
|
||||
public class RegionStoreException extends Exception {
|
||||
|
||||
public class ProtectionDatabaseException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ProtectionDatabaseException() {
|
||||
super();
|
||||
public RegionStoreException() {
|
||||
}
|
||||
|
||||
public ProtectionDatabaseException(String message) {
|
||||
public RegionStoreException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ProtectionDatabaseException(String message, Throwable cause) {
|
||||
public RegionStoreException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ProtectionDatabaseException(Throwable cause) {
|
||||
public RegionStoreException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.managers.storage;
|
||||
|
||||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Utility methods for region stores.
|
||||
*/
|
||||
public final class RegionStoreUtils {
|
||||
|
||||
private static final Logger log = Logger.getLogger(RegionStoreUtils.class.getCanonicalName());
|
||||
|
||||
private RegionStoreUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Try setting the given map of flags onto the region.
|
||||
*
|
||||
* @param region the region
|
||||
* @param flagData the map of flag data
|
||||
*/
|
||||
public static void trySetFlagMap(ProtectedRegion region, Map<String, Object> flagData) {
|
||||
checkNotNull(region);
|
||||
checkNotNull(flagData);
|
||||
|
||||
for (Flag<?> flag : DefaultFlag.getFlags()) {
|
||||
if (flag == null) {
|
||||
// Some plugins that add custom flags to WorldGuard are doing
|
||||
// something very wrong -- see WORLDGUARD-3094
|
||||
continue;
|
||||
}
|
||||
|
||||
Object o = flagData.get(flag.getName());
|
||||
if (o != null) {
|
||||
RegionStoreUtils.trySetFlag(region, flag, o);
|
||||
}
|
||||
|
||||
// Set group
|
||||
if (flag.getRegionGroupFlag() != null) {
|
||||
Object o2 = flagData.get(flag.getRegionGroupFlag().getName());
|
||||
if (o2 != null) {
|
||||
RegionStoreUtils.trySetFlag(region, flag.getRegionGroupFlag(), o2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to set a flag on the region.
|
||||
*
|
||||
* @param region the region
|
||||
* @param flag the flag
|
||||
* @param value the value of the flag, which may be {@code null}
|
||||
* @param <T> the flag's type
|
||||
* @return true if the set succeeded
|
||||
*/
|
||||
public static <T> boolean trySetFlag(ProtectedRegion region, Flag<T> flag, @Nullable Object value) {
|
||||
checkNotNull(region);
|
||||
checkNotNull(flag);
|
||||
|
||||
T val = flag.unmarshal(value);
|
||||
|
||||
if (val != null) {
|
||||
region.setFlag(flag, val);
|
||||
return true;
|
||||
} else {
|
||||
log.warning("Failed to parse flag '" + flag.getName() + "' with value '" + value + "'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-link parent regions on each provided region using the two
|
||||
* provided maps.
|
||||
*
|
||||
* @param regions the map of regions from which parent regions are found
|
||||
* @param parentSets a mapping of region to parent name
|
||||
*/
|
||||
public static void relinkParents(Map<String, ProtectedRegion> regions, Map<ProtectedRegion, String> parentSets) {
|
||||
checkNotNull(regions);
|
||||
checkNotNull(parentSets);
|
||||
|
||||
for (Map.Entry<ProtectedRegion, String> entry : parentSets.entrySet()) {
|
||||
ProtectedRegion target = entry.getKey();
|
||||
ProtectedRegion parent = regions.get(entry.getValue());
|
||||
if (parent != null) {
|
||||
try {
|
||||
target.setParent(parent);
|
||||
} catch (CircularInheritanceException e) {
|
||||
log.warning("Circular inheritance detected! Can't set the parent of '" + target + "' to parent '" + parent.getId() + "'");
|
||||
}
|
||||
} else {
|
||||
log.warning("Unknown region parent: " + entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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.protection.managers.storage.driver;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.file.YamlFileStore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Stores region data in a {root_dir}/{id}/{filename} pattern on disk
|
||||
* using {@link YamlFileStore}.
|
||||
*/
|
||||
public class DirectoryYamlDriver implements RegionStoreDriver {
|
||||
|
||||
private final File rootDir;
|
||||
private final String filename;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param rootDir the directory where the world folders reside
|
||||
* @param filename the filename (i.e. "regions.yml")
|
||||
*/
|
||||
public DirectoryYamlDriver(File rootDir, String filename) {
|
||||
checkNotNull(rootDir);
|
||||
checkNotNull(filename);
|
||||
this.rootDir = rootDir;
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path for the given ID.
|
||||
*
|
||||
* @param id the ID
|
||||
* @return the file path
|
||||
*/
|
||||
private File getPath(String id) {
|
||||
checkNotNull(id);
|
||||
|
||||
File f = new File(rootDir, id + File.separator + filename);
|
||||
try {
|
||||
f.getCanonicalPath();
|
||||
return f;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("Invalid file path for the world's regions file");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegionStore get(String id) throws IOException {
|
||||
checkNotNull(id);
|
||||
|
||||
File file = getPath(id);
|
||||
File parentDir = file.getParentFile();
|
||||
if (!parentDir.exists()) {
|
||||
if (!parentDir.mkdirs()) {
|
||||
throw new IOException("Failed to create the parent directory (" + parentDir.getAbsolutePath() + ") to store the regions file in");
|
||||
}
|
||||
}
|
||||
|
||||
return new YamlFileStore(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> fetchAllExisting() {
|
||||
List<String> names = new ArrayList<String>();
|
||||
|
||||
File files[] = rootDir.listFiles();
|
||||
if (files != null) {
|
||||
for (File dir : files) {
|
||||
if (dir.isDirectory() && new File(dir, "regions.yml").isFile()) {
|
||||
names.add(dir.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.managers.storage.driver;
|
||||
|
||||
import com.sk89q.worldguard.bukkit.ConfigurationManager;
|
||||
import com.sk89q.worldguard.util.sql.DataSourceConfig;
|
||||
|
||||
/**
|
||||
* An enumeration of supported drivers.
|
||||
*/
|
||||
public enum DriverType {
|
||||
|
||||
YAML {
|
||||
@Override
|
||||
public RegionStoreDriver create(ConfigurationManager config) {
|
||||
return new DirectoryYamlDriver(config.getWorldsDataFolder(), "regions.yml");
|
||||
}
|
||||
},
|
||||
SQL {
|
||||
@Override
|
||||
public RegionStoreDriver create(ConfigurationManager config) {
|
||||
DataSourceConfig dataSourceConfig = new DataSourceConfig(config.sqlDsn, config.sqlUsername, config.sqlPassword, config.sqlTablePrefix);
|
||||
return new SQLDriver(dataSourceConfig);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a driver instance from a configuration.
|
||||
*
|
||||
* @param config a configuration
|
||||
* @return a driver
|
||||
*/
|
||||
public abstract RegionStoreDriver create(ConfigurationManager config);
|
||||
|
||||
}
|
@ -17,30 +17,33 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases;
|
||||
package com.sk89q.worldguard.protection.managers.storage.driver;
|
||||
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.sk89q.worldguard.bukkit.ConfigurationManager;
|
||||
import com.sk89q.worldguard.protection.databases.mysql.MySQLDatabaseImpl;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A store that persists regions in a MySQL database.
|
||||
* A driver is able to create new {@code RegionStore}s for named worlds.
|
||||
*/
|
||||
public class MySQLDatabase extends MySQLDatabaseImpl {
|
||||
public interface RegionStoreDriver {
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* Get a region store for the named world.
|
||||
*
|
||||
* @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
|
||||
* @param name the name
|
||||
* @return the world
|
||||
* @throws IOException thrown if the region store can't be created due to an I/O error
|
||||
*/
|
||||
public MySQLDatabase(ConfigurationManager config, String worldName, Logger logger) throws ProtectionDatabaseException {
|
||||
super(config, worldName, logger);
|
||||
}
|
||||
RegionStore get(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Fetch the names of all worlds that are stored with this driver.
|
||||
*
|
||||
* @return a list of names
|
||||
* @throws IOException thrown if the fetch operation fails
|
||||
*/
|
||||
List<String> fetchAllExisting() throws IOException;
|
||||
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.managers.storage.driver;
|
||||
|
||||
import com.jolbox.bonecp.BoneCP;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.sql.SQLRegionStore;
|
||||
import com.sk89q.worldguard.util.io.Closer;
|
||||
import com.sk89q.worldguard.util.sql.DataSourceConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Stores regions using {@link SQLRegionStore}.
|
||||
*/
|
||||
public class SQLDriver implements RegionStoreDriver {
|
||||
|
||||
private final DataSourceConfig config;
|
||||
private final Object lock = new Object();
|
||||
private BoneCP connectionPool;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param config a configuration
|
||||
*/
|
||||
public SQLDriver(DataSourceConfig config) {
|
||||
checkNotNull(config);
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the connection pool.
|
||||
*
|
||||
* @return the connection pool
|
||||
* @throws SQLException occurs when the connection pool can't be created
|
||||
*/
|
||||
protected BoneCP getConnectionPool() throws SQLException {
|
||||
synchronized (lock) {
|
||||
if (connectionPool == null) {
|
||||
connectionPool = new BoneCP(config.createBoneCPConfig());
|
||||
}
|
||||
|
||||
return connectionPool;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegionStore get(String name) throws IOException {
|
||||
try {
|
||||
return new SQLRegionStore(config, getConnectionPool(), name);
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Failed to get a connection pool for storing regions (are the SQL details correct?)");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> fetchAllExisting() throws IOException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
List<String> names = new ArrayList<String>();
|
||||
Connection connection = closer.register(getConnectionPool().getConnection());
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = closer.register(stmt.executeQuery("SELECT name FROM world"));
|
||||
while (rs.next()) {
|
||||
names.add(rs.getString(1));
|
||||
}
|
||||
return names;
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Failed to fetch list of worlds", e);
|
||||
} finally {
|
||||
closer.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,405 +1,349 @@
|
||||
/*
|
||||
* 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.util.yaml.YAMLFormat;
|
||||
import com.sk89q.util.yaml.YAMLNode;
|
||||
import com.sk89q.util.yaml.YAMLProcessor;
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.domains.DefaultDomain;
|
||||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
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 {
|
||||
|
||||
/**
|
||||
* Used to dump YAML when an error occurs
|
||||
*/
|
||||
private static Yaml yaml;
|
||||
|
||||
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;
|
||||
|
||||
if (!file.exists()) { // shouldn't be necessary, but check anyways
|
||||
try {
|
||||
file.createNewFile();
|
||||
} catch (IOException e) {
|
||||
throw new FileNotFoundException(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private YAMLProcessor createYamlProcessor(File file) {
|
||||
return new YAMLProcessor(file, false, YAMLFormat.COMPACT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performLoad() throws ProtectionDatabaseException {
|
||||
YAMLProcessor config = createYamlProcessor(file);
|
||||
|
||||
try {
|
||||
config.load();
|
||||
} 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>();
|
||||
return;
|
||||
}
|
||||
|
||||
// Warning for WORLDGUARD-3094
|
||||
for (Flag<?> flag : DefaultFlag.getFlags()) {
|
||||
if (flag == null) {
|
||||
logger.severe("Some 3rd-party plugin has registered an invalid 'null' custom flag with WorldGuard, though we can't tell you which plugin did it - this may cause major problems in other places");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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" +
|
||||
"Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) + "\n");
|
||||
continue;
|
||||
} else if (type.equals("cuboid")) {
|
||||
Vector pt1 = checkNonNull(node.getVector("min"));
|
||||
Vector pt2 = checkNonNull(node.getVector("max"));
|
||||
BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector();
|
||||
BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector();
|
||||
region = new ProtectedCuboidRegion(id, min, max);
|
||||
} else if (type.equals("poly2d")) {
|
||||
Integer minY = checkNonNull(node.getInt("min-y"));
|
||||
Integer maxY = checkNonNull(node.getInt("max-y"));
|
||||
List<BlockVector2D> points = node.getBlockVector2dList("points", null);
|
||||
region = new ProtectedPolygonalRegion(id, points, minY, maxY);
|
||||
} else if (type.equals("global")) {
|
||||
region = new GlobalProtectedRegion(id);
|
||||
} else {
|
||||
logger.warning("Unknown region type for region '" + id + "'!\n" +
|
||||
"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);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Relink parents
|
||||
for (Map.Entry<ProtectedRegion, String> entry : parentSets.entrySet()) {
|
||||
ProtectedRegion parent = regions.get(entry.getValue());
|
||||
if (parent != null) {
|
||||
try {
|
||||
entry.getKey().setParent(parent);
|
||||
} catch (CircularInheritanceException e) {
|
||||
logger.warning("Circular inheritance detect with '" + entry.getValue() + "' detected as a parent");
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
// Some plugins that add custom flags to WorldGuard are doing
|
||||
// something very wrong -- see WORLDGUARD-3094
|
||||
continue;
|
||||
}
|
||||
|
||||
Object o = flagsData.getProperty(flag.getName());
|
||||
if (o != null) {
|
||||
setFlag(region, flag, o);
|
||||
}
|
||||
|
||||
if (flag.getRegionGroupFlag() != null) {
|
||||
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) {
|
||||
logger.warning("Failed to parse flag '" + flag.getName()
|
||||
+ "' with value '" + rawValue.toString() + "'");
|
||||
return;
|
||||
}
|
||||
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)) {
|
||||
domain.addPlayer(name);
|
||||
}
|
||||
|
||||
for (String stringId : node.getStringList("unique-ids", null)) {
|
||||
try {
|
||||
domain.addPlayer(UUID.fromString(stringId));
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.log(Level.WARNING, "Failed to parse UUID '" + stringId +"'", e);
|
||||
}
|
||||
}
|
||||
|
||||
for (String name : node.getStringList("groups", null)) {
|
||||
domain.addGroup(name);
|
||||
}
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performSave() throws ProtectionDatabaseException {
|
||||
File tempFile = new File(file.getParentFile(), file.getName() + ".tmp");
|
||||
YAMLProcessor config = createYamlProcessor(tempFile);
|
||||
|
||||
config.clear();
|
||||
|
||||
for (Map.Entry<String, ProtectedRegion> entry : regions.entrySet()) {
|
||||
ProtectedRegion region = entry.getValue();
|
||||
YAMLNode node = config.addNode("regions." + entry.getKey());
|
||||
|
||||
if (region instanceof ProtectedCuboidRegion) {
|
||||
ProtectedCuboidRegion cuboid = (ProtectedCuboidRegion) region;
|
||||
node.setProperty("type", "cuboid");
|
||||
node.setProperty("min", cuboid.getMinimumPoint());
|
||||
node.setProperty("max", cuboid.getMaximumPoint());
|
||||
} else if (region instanceof ProtectedPolygonalRegion) {
|
||||
ProtectedPolygonalRegion poly = (ProtectedPolygonalRegion) region;
|
||||
node.setProperty("type", "poly2d");
|
||||
node.setProperty("min-y", poly.getMinimumPoint().getBlockY());
|
||||
node.setProperty("max-y", poly.getMaximumPoint().getBlockY());
|
||||
|
||||
List<Map<String, Object>> points = new ArrayList<Map<String,Object>>();
|
||||
for (BlockVector2D point : poly.getPoints()) {
|
||||
Map<String, Object> data = new HashMap<String, Object>();
|
||||
data.put("x", point.getBlockX());
|
||||
data.put("z", point.getBlockZ());
|
||||
points.add(data);
|
||||
}
|
||||
|
||||
node.setProperty("points", points);
|
||||
} else if (region instanceof GlobalProtectedRegion) {
|
||||
node.setProperty("type", "global");
|
||||
} else {
|
||||
node.setProperty("type", region.getClass().getCanonicalName());
|
||||
}
|
||||
|
||||
node.setProperty("priority", region.getPriority());
|
||||
node.setProperty("flags", getFlagData(region));
|
||||
node.setProperty("owners", getDomainData(region.getOwners()));
|
||||
node.setProperty("members", getDomainData(region.getMembers()));
|
||||
ProtectedRegion parent = region.getParent();
|
||||
if (parent != null) {
|
||||
node.setProperty("parent", parent.getId());
|
||||
}
|
||||
}
|
||||
|
||||
config.setHeader("#\r\n" +
|
||||
"# WorldGuard regions file\r\n" +
|
||||
"#\r\n" +
|
||||
"# WARNING: THIS FILE IS AUTOMATICALLY GENERATED. If you modify this file by\r\n" +
|
||||
"# hand, be aware that A SINGLE MISTYPED CHARACTER CAN CORRUPT THE FILE. If\r\n" +
|
||||
"# WorldGuard is unable to parse the file, your regions will FAIL TO LOAD and\r\n" +
|
||||
"# the contents of this file will reset. Please use a YAML validator such as\r\n" +
|
||||
"# http://yaml-online-parser.appspot.com (for smaller files).\r\n" +
|
||||
"#\r\n" +
|
||||
"# REMEMBER TO KEEP PERIODICAL BACKUPS.\r\n" +
|
||||
"#");
|
||||
config.save();
|
||||
|
||||
file.delete();
|
||||
if (!tempFile.renameTo(file)) {
|
||||
throw new ProtectionDatabaseException("Failed to rename temporary regions file to " + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
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>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ProtectedRegion> getRegions() {
|
||||
return regions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegions(Map<String, ProtectedRegion> regions) {
|
||||
this.regions = regions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the given object as YAML for debugging purposes.
|
||||
*
|
||||
* @param object the object
|
||||
* @return the YAML string or an error string if dumping fals
|
||||
*/
|
||||
private static String dumpAsYaml(Object object) {
|
||||
if (yaml == null) {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setIndent(4);
|
||||
options.setDefaultFlowStyle(FlowStyle.AUTO);
|
||||
|
||||
yaml = new Yaml(new SafeConstructor(), new Representer(), options);
|
||||
}
|
||||
|
||||
try {
|
||||
return yaml.dump(object).replaceAll("(?m)^", "\t");
|
||||
} catch (Throwable t) {
|
||||
return "<error while dumping object>";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.managers.storage.file;
|
||||
|
||||
import com.sk89q.util.yaml.YAMLFormat;
|
||||
import com.sk89q.util.yaml.YAMLNode;
|
||||
import com.sk89q.util.yaml.YAMLProcessor;
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.domains.DefaultDomain;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.protection.managers.RegionDifference;
|
||||
import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStoreUtils;
|
||||
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A store that persists regions in a YAML-encoded file.
|
||||
*/
|
||||
public class YamlFileStore implements RegionStore {
|
||||
|
||||
private static final Logger log = Logger.getLogger(YamlFileStore.class.getCanonicalName());
|
||||
private static final Yaml ERROR_DUMP_YAML;
|
||||
|
||||
private static final String FILE_HEADER = "#\r\n" +
|
||||
"# WorldGuard regions file\r\n" +
|
||||
"#\r\n" +
|
||||
"# WARNING: THIS FILE IS AUTOMATICALLY GENERATED. If you modify this file by\r\n" +
|
||||
"# hand, be aware that A SINGLE MISTYPED CHARACTER CAN CORRUPT THE FILE. If\r\n" +
|
||||
"# WorldGuard is unable to parse the file, your regions will FAIL TO LOAD and\r\n" +
|
||||
"# the contents of this file will reset. Please use a YAML validator such as\r\n" +
|
||||
"# http://yaml-online-parser.appspot.com (for smaller files).\r\n" +
|
||||
"#\r\n" +
|
||||
"# REMEMBER TO KEEP PERIODICAL BACKUPS.\r\n" +
|
||||
"#";
|
||||
|
||||
private final File file;
|
||||
|
||||
static {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setIndent(4);
|
||||
options.setDefaultFlowStyle(FlowStyle.AUTO);
|
||||
|
||||
ERROR_DUMP_YAML = new Yaml(new SafeConstructor(), new Representer(), options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param file the file
|
||||
* @throws IOException thrown if the file cannot be written to
|
||||
*/
|
||||
public YamlFileStore(File file) throws IOException {
|
||||
checkNotNull(file);
|
||||
this.file = file;
|
||||
if (!file.exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.createNewFile();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtectedRegion> loadAll() throws IOException {
|
||||
Map<String, ProtectedRegion> loaded = new HashMap<String, ProtectedRegion>();
|
||||
|
||||
YAMLProcessor config = createYamlProcessor(file);
|
||||
config.load();
|
||||
|
||||
Map<String, YAMLNode> regionData = config.getNodes("regions");
|
||||
|
||||
if (regionData == null) {
|
||||
return Collections.emptySet(); // No regions are even configured
|
||||
}
|
||||
|
||||
Map<ProtectedRegion, String> parentSets = new LinkedHashMap<ProtectedRegion, String>();
|
||||
|
||||
for (Map.Entry<String, YAMLNode> entry : regionData.entrySet()) {
|
||||
String id = entry.getKey();
|
||||
YAMLNode node = entry.getValue();
|
||||
|
||||
String type = node.getString("type");
|
||||
ProtectedRegion region;
|
||||
|
||||
try {
|
||||
if (type == null) {
|
||||
log.warning("Undefined region type for region '" + id + "'!\n" +
|
||||
"Here is what the region data looks like:\n\n" + toYamlOutput(entry.getValue().getMap()) + "\n");
|
||||
continue;
|
||||
} else if (type.equals("cuboid")) {
|
||||
Vector pt1 = checkNotNull(node.getVector("min"));
|
||||
Vector pt2 = checkNotNull(node.getVector("max"));
|
||||
BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector();
|
||||
BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector();
|
||||
region = new ProtectedCuboidRegion(id, min, max);
|
||||
} else if (type.equals("poly2d")) {
|
||||
Integer minY = checkNotNull(node.getInt("min-y"));
|
||||
Integer maxY = checkNotNull(node.getInt("max-y"));
|
||||
List<BlockVector2D> points = node.getBlockVector2dList("points", null);
|
||||
region = new ProtectedPolygonalRegion(id, points, minY, maxY);
|
||||
} else if (type.equals("global")) {
|
||||
region = new GlobalProtectedRegion(id);
|
||||
} else {
|
||||
log.warning("Unknown region type for region '" + id + "'!\n" +
|
||||
"Here is what the region data looks like:\n\n" + toYamlOutput(entry.getValue().getMap()) + "\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer priority = checkNotNull(node.getInt("priority"));
|
||||
region.setPriority(priority);
|
||||
setFlags(region, node.getNode("flags"));
|
||||
region.setOwners(parseDomain(node.getNode("owners")));
|
||||
region.setMembers(parseDomain(node.getNode("members")));
|
||||
|
||||
loaded.put(id, region);
|
||||
|
||||
String parentId = node.getString("parent");
|
||||
if (parentId != null) {
|
||||
parentSets.put(region, parentId);
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
log.log(Level.WARNING,
|
||||
"Unexpected NullPointerException encountered during parsing for the region '" + id + "'!\n" +
|
||||
"Here is what the region data looks like:\n\n" + toYamlOutput(entry.getValue().getMap()) +
|
||||
"\n\nNote: This region will disappear as a result!", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Relink parents
|
||||
RegionStoreUtils.relinkParents(loaded, parentSets);
|
||||
|
||||
return new HashSet<ProtectedRegion>(loaded.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll(Set<ProtectedRegion> regions) throws IOException {
|
||||
checkNotNull(regions);
|
||||
|
||||
File tempFile = new File(file.getParentFile(), file.getName() + ".tmp");
|
||||
YAMLProcessor config = createYamlProcessor(tempFile);
|
||||
|
||||
config.clear();
|
||||
|
||||
YAMLNode regionsNode = config.addNode("regions");
|
||||
Map<String, Object> map = regionsNode.getMap();
|
||||
|
||||
for (ProtectedRegion region : regions) {
|
||||
Map<String, Object> nodeMap = new HashMap<String, Object>();
|
||||
map.put(region.getId(), nodeMap);
|
||||
YAMLNode node = new YAMLNode(nodeMap, false);
|
||||
|
||||
if (region instanceof ProtectedCuboidRegion) {
|
||||
ProtectedCuboidRegion cuboid = (ProtectedCuboidRegion) region;
|
||||
node.setProperty("type", "cuboid");
|
||||
node.setProperty("min", cuboid.getMinimumPoint());
|
||||
node.setProperty("max", cuboid.getMaximumPoint());
|
||||
} else if (region instanceof ProtectedPolygonalRegion) {
|
||||
ProtectedPolygonalRegion poly = (ProtectedPolygonalRegion) region;
|
||||
node.setProperty("type", "poly2d");
|
||||
node.setProperty("min-y", poly.getMinimumPoint().getBlockY());
|
||||
node.setProperty("max-y", poly.getMaximumPoint().getBlockY());
|
||||
|
||||
List<Map<String, Object>> points = new ArrayList<Map<String, Object>>();
|
||||
for (BlockVector2D point : poly.getPoints()) {
|
||||
Map<String, Object> data = new HashMap<String, Object>();
|
||||
data.put("x", point.getBlockX());
|
||||
data.put("z", point.getBlockZ());
|
||||
points.add(data);
|
||||
}
|
||||
|
||||
node.setProperty("points", points);
|
||||
} else if (region instanceof GlobalProtectedRegion) {
|
||||
node.setProperty("type", "global");
|
||||
} else {
|
||||
node.setProperty("type", region.getClass().getCanonicalName());
|
||||
}
|
||||
|
||||
node.setProperty("priority", region.getPriority());
|
||||
node.setProperty("flags", getFlagData(region));
|
||||
node.setProperty("owners", getDomainData(region.getOwners()));
|
||||
node.setProperty("members", getDomainData(region.getMembers()));
|
||||
|
||||
ProtectedRegion parent = region.getParent();
|
||||
if (parent != null) {
|
||||
node.setProperty("parent", parent.getId());
|
||||
}
|
||||
}
|
||||
|
||||
config.setHeader(FILE_HEADER);
|
||||
config.save();
|
||||
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.delete();
|
||||
if (!tempFile.renameTo(file)) {
|
||||
throw new IOException("Failed to rename temporary regions file to " + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveChanges(RegionDifference difference) throws DifferenceSaveException {
|
||||
throw new DifferenceSaveException("Not supported");
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private DefaultDomain parseDomain(YAMLNode node) {
|
||||
if (node == null) {
|
||||
return new DefaultDomain();
|
||||
}
|
||||
|
||||
DefaultDomain domain = new DefaultDomain();
|
||||
|
||||
for (String name : node.getStringList("players", null)) {
|
||||
domain.addPlayer(name);
|
||||
}
|
||||
|
||||
for (String stringId : node.getStringList("unique-ids", null)) {
|
||||
try {
|
||||
domain.addPlayer(UUID.fromString(stringId));
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.log(Level.WARNING, "Failed to parse UUID '" + stringId + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
for (String name : node.getStringList("groups", null)) {
|
||||
domain.addGroup(name);
|
||||
}
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private void setFlags(ProtectedRegion region, YAMLNode flagsData) {
|
||||
if (flagsData != null) {
|
||||
RegionStoreUtils.trySetFlagMap(region, flagsData.getMap());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <V> void addMarshalledFlag(Map<String, Object> flagData, Flag<V> flag, Object val) {
|
||||
if (val == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
flagData.put(flag.getName(), flag.marshal((V) val));
|
||||
}
|
||||
|
||||
private Map<String, Object> getDomainData(DefaultDomain domain) {
|
||||
Map<String, Object> domainData = new HashMap<String, Object>();
|
||||
|
||||
//noinspection deprecation
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a YAML processer instance.
|
||||
*
|
||||
* @param file the file
|
||||
* @return a processor instance
|
||||
*/
|
||||
private YAMLProcessor createYamlProcessor(File file) {
|
||||
checkNotNull(file);
|
||||
return new YAMLProcessor(file, false, YAMLFormat.COMPACT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the given object as YAML for debugging purposes.
|
||||
*
|
||||
* @param object the object
|
||||
* @return the YAML string or an error string if dumping fals
|
||||
*/
|
||||
private static String toYamlOutput(Object object) {
|
||||
try {
|
||||
return ERROR_DUMP_YAML.dump(object).replaceAll("(?m)^", "\t");
|
||||
} catch (Throwable t) {
|
||||
return "<error while dumping object>";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* 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.managers.storage.sql;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Table;
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.domains.DefaultDomain;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStoreUtils;
|
||||
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.util.io.Closer;
|
||||
import com.sk89q.worldguard.util.sql.DataSourceConfig;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.error.YAMLException;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
class DataLoader {
|
||||
|
||||
private static final Logger log = Logger.getLogger(DataLoader.class.getCanonicalName());
|
||||
|
||||
final Connection conn;
|
||||
final DataSourceConfig config;
|
||||
final int worldId;
|
||||
|
||||
private final Map<String, ProtectedRegion> loaded = new HashMap<String, ProtectedRegion>();
|
||||
private final Map<ProtectedRegion, String> parentSets = new HashMap<ProtectedRegion, String>();
|
||||
private final Yaml yaml = SQLRegionStore.createYaml();
|
||||
|
||||
DataLoader(SQLRegionStore regionStore, Connection conn) throws SQLException {
|
||||
checkNotNull(regionStore);
|
||||
|
||||
this.conn = conn;
|
||||
this.config = regionStore.getDataSourceConfig();
|
||||
this.worldId = regionStore.getWorldId();
|
||||
}
|
||||
|
||||
public Set<ProtectedRegion> load() throws SQLException {
|
||||
loadCuboids();
|
||||
loadPolygons();
|
||||
loadGlobals();
|
||||
|
||||
loadFlags();
|
||||
loadDomainUsers();
|
||||
loadDomainGroups();
|
||||
|
||||
RegionStoreUtils.relinkParents(loaded, parentSets);
|
||||
|
||||
return new HashSet<ProtectedRegion>(loaded.values());
|
||||
}
|
||||
|
||||
private void loadCuboids() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"SELECT g.min_z, g.min_y, g.min_x, " +
|
||||
" g.max_z, g.max_y, g.max_x, " +
|
||||
" r.id, r.priority, p.id AS parent " +
|
||||
"FROM " + config.getTablePrefix() + "region_cuboid AS g " +
|
||||
"LEFT JOIN " + config.getTablePrefix() + "region AS r " +
|
||||
" ON (g.region_id = r.id AND g.world_id = r.world_id) " +
|
||||
"LEFT JOIN " + config.getTablePrefix() + "region AS p " +
|
||||
" ON (r.parent = p.id AND r.world_id = p.world_id) " +
|
||||
"WHERE r.world_id = " + worldId));
|
||||
|
||||
ResultSet rs = closer.register(stmt.executeQuery());
|
||||
|
||||
while (rs.next()) {
|
||||
Vector pt1 = new Vector(rs.getInt("min_x"), rs.getInt("min_y"), rs.getInt("min_z"));
|
||||
Vector pt2 = new Vector(rs.getInt("max_x"), rs.getInt("max_y"), rs.getInt("max_z"));
|
||||
|
||||
BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector();
|
||||
BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector();
|
||||
ProtectedRegion region = new ProtectedCuboidRegion(rs.getString("id"), min, max);
|
||||
|
||||
region.setPriority(rs.getInt("priority"));
|
||||
|
||||
loaded.put(rs.getString("id"), region);
|
||||
|
||||
String parentId = rs.getString("parent");
|
||||
if (parentId != null) {
|
||||
parentSets.put(region, parentId);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadGlobals() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"SELECT r.id, r.priority, p.id AS parent " +
|
||||
"FROM " + config.getTablePrefix() + "region AS r " +
|
||||
"LEFT JOIN " + config.getTablePrefix() + "region AS p " +
|
||||
" ON (r.parent = p.id AND r.world_id = p.world_id) " +
|
||||
"WHERE r.type = 'global' AND r.world_id = " + worldId));
|
||||
|
||||
ResultSet rs = closer.register(stmt.executeQuery());
|
||||
|
||||
while (rs.next()) {
|
||||
ProtectedRegion region = new GlobalProtectedRegion(rs.getString("id"));
|
||||
|
||||
region.setPriority(rs.getInt("priority"));
|
||||
|
||||
loaded.put(rs.getString("id"), region);
|
||||
|
||||
String parentId = rs.getString("parent");
|
||||
if (parentId != null) {
|
||||
parentSets.put(region, parentId);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadPolygons() throws SQLException {
|
||||
ListMultimap<String, BlockVector2D> pointsCache = ArrayListMultimap.create();
|
||||
|
||||
// First get all the vertices and store them in memory
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"SELECT region_id, x, z " +
|
||||
"FROM " + config.getTablePrefix() + "region_poly2d_point " +
|
||||
"WHERE world_id = " + worldId));
|
||||
|
||||
ResultSet rs = closer.register(stmt.executeQuery());
|
||||
|
||||
while (rs.next()) {
|
||||
pointsCache.put(rs.getString("region_id"), new BlockVector2D(rs.getInt("x"), rs.getInt("z")));
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
|
||||
// Now we pull the regions themselves
|
||||
closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"SELECT g.min_y, g.max_y, r.id, r.priority, p.id AS parent " +
|
||||
"FROM " + config.getTablePrefix() + "region_poly2d AS g " +
|
||||
"LEFT JOIN " + config.getTablePrefix() + "region AS r " +
|
||||
" ON (g.region_id = r.id AND g.world_id = r.world_id) " +
|
||||
"LEFT JOIN " + config.getTablePrefix() + "region AS p " +
|
||||
" ON (r.parent = p.id AND r.world_id = p.world_id) " +
|
||||
"WHERE r.world_id = " + worldId
|
||||
));
|
||||
|
||||
ResultSet rs = closer.register(stmt.executeQuery());
|
||||
|
||||
while (rs.next()) {
|
||||
String id = rs.getString("id");
|
||||
|
||||
// Get the points from the cache
|
||||
List<BlockVector2D> points = pointsCache.get(id);
|
||||
|
||||
if (points.size() < 3) {
|
||||
log.log(Level.WARNING, "Invalid polygonal region '" + id + "': region has " + points.size() + " point(s) (less than the required 3). Skipping this region.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer minY = rs.getInt("min_y");
|
||||
Integer maxY = rs.getInt("max_y");
|
||||
|
||||
ProtectedRegion region = new ProtectedPolygonalRegion(id, points, minY, maxY);
|
||||
region.setPriority(rs.getInt("priority"));
|
||||
|
||||
loaded.put(id, region);
|
||||
|
||||
String parentId = rs.getString("parent");
|
||||
if (parentId != null) {
|
||||
parentSets.put(region, parentId);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFlags() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"SELECT region_id, flag, value " +
|
||||
"FROM " + config.getTablePrefix() + "region_flag " +
|
||||
"WHERE world_id = " + worldId));
|
||||
|
||||
ResultSet rs = closer.register(stmt.executeQuery());
|
||||
|
||||
Table<String, String, Object> data = HashBasedTable.create();
|
||||
while (rs.next()) {
|
||||
data.put(
|
||||
rs.getString("region_id"),
|
||||
rs.getString("flag"),
|
||||
unmarshalFlagValue(rs.getString("value")));
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Map<String, Object>> entry : data.rowMap().entrySet()) {
|
||||
ProtectedRegion region = loaded.get(entry.getKey());
|
||||
if (region != null) {
|
||||
RegionStoreUtils.trySetFlagMap(region, entry.getValue());
|
||||
} else {
|
||||
throw new RuntimeException("An unexpected error occurred (loaded.get() returned null)");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadDomainUsers() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"SELECT p.region_id, u.name, u.uuid, p.owner " +
|
||||
"FROM " + config.getTablePrefix() + "region_players AS p " +
|
||||
"LEFT JOIN " + config.getTablePrefix() + "user AS u " +
|
||||
" ON (p.user_id = u.id) " +
|
||||
"WHERE p.world_id = " + worldId));
|
||||
|
||||
ResultSet rs = closer.register(stmt.executeQuery());
|
||||
|
||||
while (rs.next()) {
|
||||
ProtectedRegion region = loaded.get(rs.getString("region_id"));
|
||||
|
||||
if (region != null) {
|
||||
DefaultDomain domain;
|
||||
|
||||
if (rs.getBoolean("owner")) {
|
||||
domain = region.getOwners();
|
||||
} else {
|
||||
domain = region.getMembers();
|
||||
}
|
||||
|
||||
String name = rs.getString("name");
|
||||
String uuid = rs.getString("uuid");
|
||||
|
||||
if (name != null) {
|
||||
//noinspection deprecation
|
||||
domain.addPlayer(name);
|
||||
} else if (uuid != null) {
|
||||
try {
|
||||
domain.addPlayer(UUID.fromString(uuid));
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warning("Invalid UUID '" + uuid + "' for region '" + region.getId() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadDomainGroups() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"SELECT rg.region_id, g.name, rg.owner " +
|
||||
"FROM `" + config.getTablePrefix() + "region_groups` AS rg " +
|
||||
"INNER JOIN `" + config.getTablePrefix() + "group` AS g ON (rg.group_id = g.id) " +
|
||||
// LEFT JOIN is returning NULLS for reasons unknown
|
||||
"AND rg.world_id = " + this.worldId));
|
||||
|
||||
ResultSet rs = closer.register(stmt.executeQuery());
|
||||
|
||||
while (rs.next()) {
|
||||
ProtectedRegion region = loaded.get(rs.getString("region_id"));
|
||||
|
||||
if (region != null) {
|
||||
DefaultDomain domain;
|
||||
|
||||
if (rs.getBoolean("owner")) {
|
||||
domain = region.getOwners();
|
||||
} else {
|
||||
domain = region.getMembers();
|
||||
}
|
||||
|
||||
domain.addGroup(rs.getString("name"));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private Object unmarshalFlagValue(String rawValue) {
|
||||
try {
|
||||
return yaml.load(rawValue);
|
||||
} catch (YAMLException e) {
|
||||
return String.valueOf(rawValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.managers.storage.sql;
|
||||
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.util.io.Closer;
|
||||
import com.sk89q.worldguard.util.sql.DataSourceConfig;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
class DataUpdater {
|
||||
|
||||
final Connection conn;
|
||||
final DataSourceConfig config;
|
||||
final int worldId;
|
||||
final DomainTableCache domainTableCache;
|
||||
|
||||
DataUpdater(SQLRegionStore regionStore, Connection conn) throws SQLException {
|
||||
checkNotNull(regionStore);
|
||||
|
||||
this.conn = conn;
|
||||
this.config = regionStore.getDataSourceConfig();
|
||||
this.worldId = regionStore.getWorldId();
|
||||
this.domainTableCache = new DomainTableCache(config, conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the given set of regions to the database.
|
||||
*
|
||||
* @param regions a set of regions to save
|
||||
* @throws SQLException thrown on a fatal SQL error
|
||||
*/
|
||||
public void saveAll(Set<ProtectedRegion> regions) throws SQLException {
|
||||
executeSave(regions, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the given set of regions to the database.
|
||||
*
|
||||
* @param changed a set of changed regions
|
||||
* @param removed a set of removed regions
|
||||
* @throws SQLException thrown on a fatal SQL error
|
||||
*/
|
||||
public void saveChanges(Set<ProtectedRegion> changed, Set<ProtectedRegion> removed) throws SQLException {
|
||||
executeSave(changed, removed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the save operation.
|
||||
*
|
||||
* @param toUpdate a list of regions to update
|
||||
* @param toRemove a list of regions to remove, or {@code null} to remove
|
||||
* regions in the database that were not in {@code toUpdate}
|
||||
* @throws SQLException thrown on a fatal SQL error
|
||||
*/
|
||||
private void executeSave(Set<ProtectedRegion> toUpdate, @Nullable Set<ProtectedRegion> toRemove) throws SQLException {
|
||||
Map<String, String> existing = getExistingRegions(); // Map of regions that already exist in the database
|
||||
|
||||
// WARNING: The database uses utf8_bin for its collation, so
|
||||
// we have to remove the exact same ID (it is case-sensitive!)
|
||||
|
||||
try {
|
||||
conn.setAutoCommit(false);
|
||||
|
||||
RegionUpdater updater = new RegionUpdater(this);
|
||||
RegionInserter inserter = new RegionInserter(this);
|
||||
RegionRemover remover = new RegionRemover(this);
|
||||
|
||||
for (ProtectedRegion region : toUpdate) {
|
||||
if (toRemove != null && toRemove.contains(region)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String currentType = existing.get(region.getId());
|
||||
|
||||
// Check if the region
|
||||
if (currentType != null) { // Region exists in the database
|
||||
existing.remove(region.getId());
|
||||
|
||||
updater.updateRegionType(region);
|
||||
remover.removeGeometry(region, currentType);
|
||||
} else {
|
||||
inserter.insertRegionType(region);
|
||||
}
|
||||
|
||||
inserter.insertGeometry(region);
|
||||
updater.updateRegionProperties(region);
|
||||
}
|
||||
|
||||
if (toRemove != null) {
|
||||
List<String> removeNames = new ArrayList<String>();
|
||||
for (ProtectedRegion region : toRemove) {
|
||||
removeNames.add(region.getId());
|
||||
}
|
||||
remover.removeRegionsEntirely(removeNames);
|
||||
} else {
|
||||
remover.removeRegionsEntirely(existing.keySet());
|
||||
}
|
||||
|
||||
remover.apply();
|
||||
inserter.apply();
|
||||
updater.apply();
|
||||
|
||||
conn.commit();
|
||||
} catch (SQLException e) {
|
||||
conn.rollback();
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
conn.rollback();
|
||||
throw e;
|
||||
} finally {
|
||||
conn.setAutoCommit(true);
|
||||
}
|
||||
|
||||
// Connection to be closed by caller
|
||||
}
|
||||
|
||||
private Map<String, String> getExistingRegions() throws SQLException {
|
||||
Map<String, String> existing = new HashMap<String, String>();
|
||||
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"SELECT id, type " +
|
||||
"FROM " + config.getTablePrefix() + "region " +
|
||||
"WHERE world_id = " + worldId));
|
||||
|
||||
ResultSet resultSet = closer.register(stmt.executeQuery());
|
||||
|
||||
while (resultSet.next()) {
|
||||
existing.put(resultSet.getString("id"), resultSet.getString("type"));
|
||||
}
|
||||
|
||||
return existing;
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.managers.storage.sql;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.storage.sql.TableCache.GroupNameCache;
|
||||
import com.sk89q.worldguard.protection.managers.storage.sql.TableCache.UserNameCache;
|
||||
import com.sk89q.worldguard.protection.managers.storage.sql.TableCache.UserUuidCache;
|
||||
import com.sk89q.worldguard.util.sql.DataSourceConfig;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
class DomainTableCache {
|
||||
|
||||
private final UserNameCache userNameCache;
|
||||
private final UserUuidCache userUuidCache;
|
||||
private final GroupNameCache groupNameCache;
|
||||
|
||||
DomainTableCache(DataSourceConfig config, Connection conn) {
|
||||
userNameCache = new UserNameCache(config, conn);
|
||||
userUuidCache = new UserUuidCache(config, conn);
|
||||
groupNameCache = new GroupNameCache(config, conn);
|
||||
}
|
||||
|
||||
public UserNameCache getUserNameCache() {
|
||||
return userNameCache;
|
||||
}
|
||||
|
||||
public UserUuidCache getUserUuidCache() {
|
||||
return userUuidCache;
|
||||
}
|
||||
|
||||
public GroupNameCache getGroupNameCache() {
|
||||
return groupNameCache;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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.managers.storage.sql;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.util.io.Closer;
|
||||
import com.sk89q.worldguard.util.sql.DataSourceConfig;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Insert regions that don't exist in the database yet.
|
||||
*/
|
||||
class RegionInserter {
|
||||
|
||||
private final DataSourceConfig config;
|
||||
private final Connection conn;
|
||||
private final int worldId;
|
||||
private final List<ProtectedRegion> all = new ArrayList<ProtectedRegion>();
|
||||
private final List<ProtectedCuboidRegion> cuboids = new ArrayList<ProtectedCuboidRegion>();
|
||||
private final List<ProtectedPolygonalRegion> polygons = new ArrayList<ProtectedPolygonalRegion>();
|
||||
|
||||
RegionInserter(DataUpdater updater) {
|
||||
this.config = updater.config;
|
||||
this.conn = updater.conn;
|
||||
this.worldId = updater.worldId;
|
||||
}
|
||||
|
||||
public void insertRegionType(ProtectedRegion region) throws SQLException {
|
||||
all.add(region);
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
public void insertGeometry(ProtectedRegion region) throws SQLException {
|
||||
if (region instanceof ProtectedCuboidRegion) {
|
||||
cuboids.add((ProtectedCuboidRegion) region);
|
||||
|
||||
} else if (region instanceof ProtectedPolygonalRegion) {
|
||||
polygons.add((ProtectedPolygonalRegion) region);
|
||||
|
||||
} else if (region instanceof GlobalProtectedRegion) {
|
||||
// Nothing special to do about them
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown type of region: " + region.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void insertRegionTypes() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"INSERT INTO " + config.getTablePrefix() + "region " +
|
||||
"(id, world_id, type, priority, parent) " +
|
||||
"VALUES " +
|
||||
"(?, ?, ?, ?, NULL)"));
|
||||
|
||||
for (List<ProtectedRegion> partition : Lists.partition(all, StatementBatch.MAX_BATCH_SIZE)) {
|
||||
for (ProtectedRegion region : partition) {
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.setInt(2, worldId);
|
||||
stmt.setString(3, SQLRegionStore.getRegionTypeName(region));
|
||||
stmt.setInt(4, region.getPriority());
|
||||
stmt.addBatch();
|
||||
}
|
||||
|
||||
stmt.executeBatch();
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void insertCuboids() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"INSERT INTO " + config.getTablePrefix() + "region_cuboid " +
|
||||
"(region_id, world_id, min_z, min_y, min_x, max_z, max_y, max_x ) " +
|
||||
"VALUES " +
|
||||
"(?, " + worldId + ", ?, ?, ?, ?, ?, ?)"));
|
||||
|
||||
for (List<ProtectedCuboidRegion> partition : Lists.partition(cuboids, StatementBatch.MAX_BATCH_SIZE)) {
|
||||
for (ProtectedCuboidRegion region : partition) {
|
||||
BlockVector min = region.getMinimumPoint();
|
||||
BlockVector max = region.getMaximumPoint();
|
||||
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.setInt(2, min.getBlockZ());
|
||||
stmt.setInt(3, min.getBlockY());
|
||||
stmt.setInt(4, min.getBlockX());
|
||||
stmt.setInt(5, max.getBlockZ());
|
||||
stmt.setInt(6, max.getBlockY());
|
||||
stmt.setInt(7, max.getBlockX());
|
||||
stmt.addBatch();
|
||||
}
|
||||
|
||||
stmt.executeBatch();
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void insertPolygons() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"INSERT INTO " + config.getTablePrefix() + "region_poly2d " +
|
||||
"(region_id, world_id, max_y, min_y) " +
|
||||
"VALUES " +
|
||||
"(?, " + worldId + ", ?, ?)"));
|
||||
|
||||
for (List<ProtectedPolygonalRegion> partition : Lists.partition(polygons, StatementBatch.MAX_BATCH_SIZE)) {
|
||||
for (ProtectedPolygonalRegion region : partition) {
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.setInt(2, region.getMaximumPoint().getBlockY());
|
||||
stmt.setInt(3, region.getMinimumPoint().getBlockY());
|
||||
stmt.addBatch();
|
||||
}
|
||||
|
||||
stmt.executeBatch();
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void insertPolygonVertices() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"INSERT INTO " + config.getTablePrefix() + "region_poly2d_point" +
|
||||
"(region_id, world_id, z, x) " +
|
||||
"VALUES " +
|
||||
"(?, " + worldId + ", ?, ?)"));
|
||||
|
||||
StatementBatch batch = new StatementBatch(stmt, StatementBatch.MAX_BATCH_SIZE);
|
||||
|
||||
for (ProtectedPolygonalRegion region : polygons) {
|
||||
for (BlockVector2D point : region.getPoints()) {
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.setInt(2, point.getBlockZ());
|
||||
stmt.setInt(3, point.getBlockX());
|
||||
batch.addBatch();
|
||||
}
|
||||
}
|
||||
|
||||
batch.executeRemaining();
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
public void apply() throws SQLException {
|
||||
insertRegionTypes();
|
||||
insertCuboids();
|
||||
insertPolygons();
|
||||
insertPolygonVertices();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.managers.storage.sql;
|
||||
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.util.io.Closer;
|
||||
import com.sk89q.worldguard.util.sql.DataSourceConfig;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
class RegionRemover {
|
||||
|
||||
private final DataSourceConfig config;
|
||||
private final Connection conn;
|
||||
private final int worldId;
|
||||
private final List<String> regionQueue = new ArrayList<String>();
|
||||
private final List<String> cuboidGeometryQueue = new ArrayList<String>();
|
||||
private final List<String> polygonGeometryQueue = new ArrayList<String>();
|
||||
|
||||
RegionRemover(DataUpdater updater) {
|
||||
this.config = updater.config;
|
||||
this.conn = updater.conn;
|
||||
this.worldId = updater.worldId;
|
||||
}
|
||||
|
||||
public void removeRegionsEntirely(Collection<String> names) {
|
||||
regionQueue.addAll(names);
|
||||
}
|
||||
|
||||
public void removeGeometry(ProtectedRegion region, String currentType) {
|
||||
if (currentType.equals("cuboid")) {
|
||||
cuboidGeometryQueue.add(region.getId());
|
||||
} else if (currentType.equals("poly2d")) {
|
||||
polygonGeometryQueue.add(region.getId());
|
||||
} else if (currentType.equals("global")) {
|
||||
// Nothing to do
|
||||
} else {
|
||||
throw new RuntimeException("Unknown type of region in the database: " + currentType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void removeRows(Collection<String> names, String table, String field) throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"DELETE FROM " + config.getTablePrefix() + table + " WHERE " + field + " = ? AND world_id = " + worldId));
|
||||
|
||||
StatementBatch batch = new StatementBatch(stmt, StatementBatch.MAX_BATCH_SIZE);
|
||||
for (String name : names) {
|
||||
stmt.setString(1, name);
|
||||
batch.addBatch();
|
||||
}
|
||||
|
||||
batch.executeRemaining();
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
public void apply() throws SQLException {
|
||||
removeRows(regionQueue, "region", "id");
|
||||
removeRows(cuboidGeometryQueue, "region_cuboid", "region_id");
|
||||
removeRows(polygonGeometryQueue, "region_poly2d", "region_id");
|
||||
}
|
||||
}
|
@ -0,0 +1,341 @@
|
||||
/*
|
||||
* 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.managers.storage.sql;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sk89q.worldguard.domains.DefaultDomain;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.util.io.Closer;
|
||||
import com.sk89q.worldguard.util.sql.DataSourceConfig;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Updates region data that needs to be updated for both inserts and updates.
|
||||
*/
|
||||
class RegionUpdater {
|
||||
|
||||
private static final Logger log = Logger.getLogger(RegionUpdater.class.getCanonicalName());
|
||||
private final DataSourceConfig config;
|
||||
private final Connection conn;
|
||||
private final int worldId;
|
||||
private final DomainTableCache domainTableCache;
|
||||
|
||||
private final Set<String> userNames = new HashSet<String>();
|
||||
private final Set<UUID> userUuids = new HashSet<UUID>();
|
||||
private final Set<String> groupNames = new HashSet<String>();
|
||||
|
||||
private final Yaml yaml = SQLRegionStore.createYaml();
|
||||
|
||||
private final List<ProtectedRegion> typesToUpdate = new ArrayList<ProtectedRegion>();
|
||||
private final List<ProtectedRegion> parentsToSet = new ArrayList<ProtectedRegion>();
|
||||
private final List<ProtectedRegion> flagsToReplace = new ArrayList<ProtectedRegion>();
|
||||
private final List<ProtectedRegion> domainsToReplace = new ArrayList<ProtectedRegion>();
|
||||
|
||||
RegionUpdater(DataUpdater updater) {
|
||||
this.config = updater.config;
|
||||
this.conn = updater.conn;
|
||||
this.worldId = updater.worldId;
|
||||
this.domainTableCache = updater.domainTableCache;
|
||||
}
|
||||
|
||||
public void updateRegionType(ProtectedRegion region) {
|
||||
typesToUpdate.add(region);
|
||||
}
|
||||
|
||||
public void updateRegionProperties(ProtectedRegion region) {
|
||||
if (region.getParent() != null) {
|
||||
parentsToSet.add(region);
|
||||
}
|
||||
|
||||
flagsToReplace.add(region);
|
||||
domainsToReplace.add(region);
|
||||
|
||||
addDomain(region.getOwners());
|
||||
addDomain(region.getMembers());
|
||||
}
|
||||
|
||||
private void addDomain(DefaultDomain domain) {
|
||||
//noinspection deprecation
|
||||
for (String name : domain.getPlayers()) {
|
||||
userNames.add(name.toLowerCase());
|
||||
}
|
||||
|
||||
for (UUID uuid : domain.getUniqueIds()) {
|
||||
userUuids.add(uuid);
|
||||
}
|
||||
|
||||
for (String name : domain.getGroups()) {
|
||||
groupNames.add(name.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
private void setParents() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"UPDATE " + config.getTablePrefix() + "region " +
|
||||
"SET parent = ? " +
|
||||
"WHERE id = ? AND world_id = " + worldId));
|
||||
|
||||
for (List<ProtectedRegion> partition : Lists.partition(parentsToSet, StatementBatch.MAX_BATCH_SIZE)) {
|
||||
for (ProtectedRegion region : partition) {
|
||||
ProtectedRegion parent = region.getParent();
|
||||
if (parent != null) { // Parent would be null due to a race condition
|
||||
stmt.setString(1, parent.getId());
|
||||
stmt.setString(2, region.getId());
|
||||
stmt.addBatch();
|
||||
}
|
||||
}
|
||||
|
||||
stmt.executeBatch();
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void replaceFlags() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"DELETE FROM " + config.getTablePrefix() + "region_flag " +
|
||||
"WHERE region_id = ? " +
|
||||
"AND world_id = " + worldId));
|
||||
|
||||
for (List<ProtectedRegion> partition : Lists.partition(flagsToReplace, StatementBatch.MAX_BATCH_SIZE)) {
|
||||
for (ProtectedRegion region : partition) {
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.addBatch();
|
||||
}
|
||||
|
||||
stmt.executeBatch();
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
|
||||
closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"INSERT INTO " + config.getTablePrefix() + "region_flag " +
|
||||
"(id, region_id, world_id, flag, value) " +
|
||||
"VALUES " +
|
||||
"(null, ?, " + worldId + ", ?, ?)"));
|
||||
|
||||
StatementBatch batch = new StatementBatch(stmt, StatementBatch.MAX_BATCH_SIZE);
|
||||
|
||||
for (ProtectedRegion region : flagsToReplace) {
|
||||
for (Map.Entry<Flag<?>, Object> entry : region.getFlags().entrySet()) {
|
||||
if (entry.getValue() == null) continue;
|
||||
|
||||
Object flag = marshalFlagValue(entry.getKey(), entry.getValue());
|
||||
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.setString(2, entry.getKey().getName());
|
||||
stmt.setObject(3, flag);
|
||||
batch.addBatch();
|
||||
}
|
||||
}
|
||||
|
||||
batch.executeRemaining();
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void replaceDomainUsers() throws SQLException {
|
||||
// Remove users
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"DELETE FROM " + config.getTablePrefix() + "region_players " +
|
||||
"WHERE region_id = ? " +
|
||||
"AND world_id = " + worldId));
|
||||
|
||||
for (List<ProtectedRegion> partition : Lists.partition(domainsToReplace, StatementBatch.MAX_BATCH_SIZE)) {
|
||||
for (ProtectedRegion region : partition) {
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.addBatch();
|
||||
}
|
||||
|
||||
stmt.executeBatch();
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
|
||||
// Add users
|
||||
closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"INSERT INTO " + config.getTablePrefix() + "region_players " +
|
||||
"(region_id, world_id, user_id, owner) " +
|
||||
"VALUES (?, " + worldId + ", ?, ?)"));
|
||||
|
||||
StatementBatch batch = new StatementBatch(stmt, StatementBatch.MAX_BATCH_SIZE);
|
||||
|
||||
for (ProtectedRegion region : domainsToReplace) {
|
||||
insertDomainUsers(stmt, batch, region, region.getMembers(), false); // owner = false
|
||||
insertDomainUsers(stmt, batch, region, region.getOwners(), true); // owner = true
|
||||
}
|
||||
|
||||
batch.executeRemaining();
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void insertDomainUsers(PreparedStatement stmt, StatementBatch batch, ProtectedRegion region, DefaultDomain domain, boolean owner) throws SQLException {
|
||||
//noinspection deprecation
|
||||
for (String name : domain.getPlayers()) {
|
||||
Integer id = domainTableCache.getUserNameCache().find(name);
|
||||
if (id != null) {
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.setInt(2, id);
|
||||
stmt.setBoolean(3, owner);
|
||||
batch.addBatch();
|
||||
} else {
|
||||
log.log(Level.WARNING, "Did not find an ID for the user identified as '" + name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
for (UUID uuid : domain.getUniqueIds()) {
|
||||
Integer id = domainTableCache.getUserUuidCache().find(uuid);
|
||||
if (id != null) {
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.setInt(2, id);
|
||||
stmt.setBoolean(3, owner);
|
||||
batch.addBatch();
|
||||
} else {
|
||||
log.log(Level.WARNING, "Did not find an ID for the user identified by '" + uuid + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void replaceDomainGroups() throws SQLException {
|
||||
// Remove groups
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"DELETE FROM " + config.getTablePrefix() + "region_groups " +
|
||||
"WHERE region_id = ? " +
|
||||
"AND world_id = " + worldId));
|
||||
|
||||
for (List<ProtectedRegion> partition : Lists.partition(domainsToReplace, StatementBatch.MAX_BATCH_SIZE)) {
|
||||
for (ProtectedRegion region : partition) {
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.addBatch();
|
||||
}
|
||||
|
||||
stmt.executeBatch();
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
|
||||
// Add groups
|
||||
closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"INSERT INTO " + config.getTablePrefix() + "region_groups " +
|
||||
"(region_id, world_id, group_id, owner) " +
|
||||
"VALUES (?, " + worldId + ", ?, ?)"));
|
||||
|
||||
StatementBatch batch = new StatementBatch(stmt, StatementBatch.MAX_BATCH_SIZE);
|
||||
|
||||
for (ProtectedRegion region : domainsToReplace) {
|
||||
insertDomainGroups(stmt, batch, region, region.getMembers(), false); // owner = false
|
||||
insertDomainGroups(stmt, batch, region, region.getOwners(), true); // owner = true
|
||||
}
|
||||
|
||||
batch.executeRemaining();
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
private void insertDomainGroups(PreparedStatement stmt, StatementBatch batch, ProtectedRegion region, DefaultDomain domain, boolean owner) throws SQLException {
|
||||
for (String name : domain.getGroups()) {
|
||||
Integer id = domainTableCache.getGroupNameCache().find(name);
|
||||
if (id != null) {
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.setInt(2, id);
|
||||
stmt.setBoolean(3, owner);
|
||||
batch.addBatch();
|
||||
} else {
|
||||
log.log(Level.WARNING, "Did not find an ID for the group identified as '" + name + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRegionTypes() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"UPDATE " + config.getTablePrefix() + "region " +
|
||||
"SET type = ?, priority = ?, parent = NULL " +
|
||||
"WHERE id = ? AND world_id = " + worldId));
|
||||
|
||||
for (List<ProtectedRegion> partition : Lists.partition(typesToUpdate, StatementBatch.MAX_BATCH_SIZE)) {
|
||||
for (ProtectedRegion region : partition) {
|
||||
stmt.setString(1, SQLRegionStore.getRegionTypeName(region));
|
||||
stmt.setInt(2, region.getPriority());
|
||||
stmt.setString(3, region.getId());
|
||||
stmt.addBatch();
|
||||
}
|
||||
|
||||
stmt.executeBatch();
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
public void apply() throws SQLException {
|
||||
domainTableCache.getUserNameCache().fetch(userNames);
|
||||
domainTableCache.getUserUuidCache().fetch(userUuids);
|
||||
domainTableCache.getGroupNameCache().fetch(groupNames);
|
||||
|
||||
updateRegionTypes();
|
||||
setParents();
|
||||
replaceFlags();
|
||||
replaceDomainUsers();
|
||||
replaceDomainGroups();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <V> Object marshalFlagValue(Flag<V> flag, Object val) {
|
||||
return yaml.dump(flag.marshal((V) val));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,375 @@
|
||||
/*
|
||||
* 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.managers.storage.sql;
|
||||
|
||||
import com.jolbox.bonecp.BoneCP;
|
||||
import com.jolbox.bonecp.BoneCPConfig;
|
||||
import com.sk89q.worldguard.protection.managers.RegionDifference;
|
||||
import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.util.io.Closer;
|
||||
import com.sk89q.worldguard.util.sql.DataSourceConfig;
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.flywaydb.core.api.FlywayException;
|
||||
import org.flywaydb.core.api.MigrationVersion;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Stores region data into a SQL database in a highly normalized fashion.
|
||||
*/
|
||||
public class SQLRegionStore implements RegionStore {
|
||||
|
||||
private static final Logger log = Logger.getLogger(SQLRegionStore.class.getCanonicalName());
|
||||
|
||||
private final BoneCP connectionPool;
|
||||
private final DataSourceConfig config;
|
||||
private final int worldId;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param config a configuration object that configures a {@link Connection}
|
||||
* @param connectionPool a connection pool
|
||||
* @param worldName the name of the world to store regions by
|
||||
* @throws IOException thrown on error
|
||||
*/
|
||||
public SQLRegionStore(DataSourceConfig config, BoneCP connectionPool, String worldName) throws IOException {
|
||||
checkNotNull(config);
|
||||
checkNotNull(connectionPool);
|
||||
checkNotNull(worldName);
|
||||
|
||||
this.config = config;
|
||||
this.connectionPool = connectionPool;
|
||||
|
||||
try {
|
||||
migrate();
|
||||
} catch (FlywayException e) {
|
||||
throw new IOException("Failed to migrate tables", e);
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Failed to migrate tables", e);
|
||||
}
|
||||
|
||||
try {
|
||||
worldId = chooseWorldId(worldName);
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Failed to choose the ID for this world", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to migrate the tables to the latest version.
|
||||
*
|
||||
* @throws SQLException thrown on SQL errors
|
||||
*/
|
||||
private void migrate() throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
Connection conn = closer.register(getConnection());
|
||||
|
||||
try {
|
||||
// Check some tables
|
||||
boolean tablesExist;
|
||||
boolean isRecent;
|
||||
boolean isBeforeMigrations;
|
||||
boolean hasMigrations;
|
||||
|
||||
try {
|
||||
tablesExist = tryQuery(conn, "SELECT * FROM " + config.getTablePrefix() + "region_cuboid LIMIT 1");
|
||||
isRecent = tryQuery(conn, "SELECT world_id FROM " + config.getTablePrefix() + "region_cuboid LIMIT 1");
|
||||
isBeforeMigrations = !tryQuery(conn, "SELECT uuid FROM " + config.getTablePrefix() + "user LIMIT 1");
|
||||
hasMigrations = tryQuery(conn, "SELECT * FROM " + config.getTablePrefix() + "migrations LIMIT 1");
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
|
||||
// We don't bother with migrating really old tables
|
||||
if (tablesExist && !isRecent) {
|
||||
throw new SQLException(
|
||||
"Sorry, your tables are too old for the region SQL auto-migration system. " +
|
||||
"Please run region_manual_update_20110325.sql on your database, which comes " +
|
||||
"with WorldGuard or can be found in http://github.com/sk89q/worldguard");
|
||||
}
|
||||
|
||||
// Our placeholders
|
||||
Map<String, String> placeHolders = new HashMap<String, String>();
|
||||
placeHolders.put("tablePrefix", config.getTablePrefix());
|
||||
|
||||
BoneCPConfig boneConfig = connectionPool.getConfig();
|
||||
|
||||
Flyway flyway = new Flyway();
|
||||
|
||||
// The SQL support predates the usage of Flyway, so let's do some
|
||||
// checks and issue messages appropriately
|
||||
if (!hasMigrations) {
|
||||
flyway.setInitOnMigrate(true);
|
||||
|
||||
if (tablesExist) {
|
||||
// Detect if this is before migrations
|
||||
if (isBeforeMigrations) {
|
||||
flyway.setInitVersion(MigrationVersion.fromVersion("1"));
|
||||
}
|
||||
|
||||
log.log(Level.INFO, "The SQL region tables exist but the migrations table seems to not exist yet. Creating the migrations table...");
|
||||
} else {
|
||||
// By default, if Flyway sees any tables at all in the schema, it
|
||||
// will assume that we are up to date, so we have to manually
|
||||
// check ourselves and then ask Flyway to start from the beginning
|
||||
// if our test table doesn't exist
|
||||
flyway.setInitVersion(MigrationVersion.fromVersion("0"));
|
||||
|
||||
log.log(Level.INFO, "SQL region tables do not exist: creating...");
|
||||
}
|
||||
}
|
||||
|
||||
flyway.setClassLoader(getClass().getClassLoader());
|
||||
flyway.setLocations("migrations/region/" + getMigrationFolderName());
|
||||
flyway.setDataSource(boneConfig.getJdbcUrl(), boneConfig.getUser(), boneConfig.getPassword());
|
||||
flyway.setTable(config.getTablePrefix() + "migrations");
|
||||
flyway.setPlaceholders(placeHolders);
|
||||
flyway.setValidateOnMigrate(false);
|
||||
flyway.migrate();
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID for this world from the database or pick a new one if
|
||||
* an entry does not exist yet.
|
||||
*
|
||||
* @param worldName the world name
|
||||
* @return a world ID
|
||||
* @throws SQLException on a database access error
|
||||
*/
|
||||
private int chooseWorldId(String worldName) throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
Connection conn = closer.register(getConnection());
|
||||
|
||||
PreparedStatement stmt = closer.register(conn.prepareStatement(
|
||||
"SELECT id FROM " + config.getTablePrefix() + "world WHERE name = ? LIMIT 0, 1"));
|
||||
|
||||
stmt.setString(1, worldName);
|
||||
ResultSet worldResult = closer.register(stmt.executeQuery());
|
||||
|
||||
if (worldResult.next()) {
|
||||
return worldResult.getInt("id");
|
||||
} else {
|
||||
PreparedStatement stmt2 = closer.register(conn.prepareStatement(
|
||||
"INSERT INTO " + config.getTablePrefix() + "world (id, name) VALUES (null, ?)",
|
||||
Statement.RETURN_GENERATED_KEYS));
|
||||
|
||||
stmt2.setString(1, worldName);
|
||||
stmt2.execute();
|
||||
ResultSet generatedKeys = stmt2.getGeneratedKeys();
|
||||
|
||||
if (generatedKeys.next()) {
|
||||
return generatedKeys.getInt(1);
|
||||
} else {
|
||||
throw new SQLException("Expected result, got none");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new database connection.
|
||||
*
|
||||
* @return a connection
|
||||
* @throws SQLException thrown if the connection could not be created
|
||||
*/
|
||||
private Connection getConnection() throws SQLException {
|
||||
return connectionPool.getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data source config.
|
||||
*
|
||||
* @return the data source config
|
||||
*/
|
||||
public DataSourceConfig getDataSourceConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the world ID.
|
||||
*
|
||||
* @return the world ID
|
||||
*/
|
||||
public int getWorldId() {
|
||||
return worldId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to execute a query and return true if it did not fail.
|
||||
*
|
||||
* @param conn the connection to run the query on
|
||||
* @param sql the SQL query
|
||||
* @return true if the query did not end in error
|
||||
*/
|
||||
private boolean tryQuery(Connection conn, String sql) {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
Statement statement = closer.register(conn.createStatement());
|
||||
statement.executeQuery(sql);
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
return false;
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identifier string for a region's type.
|
||||
*
|
||||
* @param region the region
|
||||
* @return the ID of the region type
|
||||
*/
|
||||
static String getRegionTypeName(ProtectedRegion region) {
|
||||
if (region instanceof ProtectedCuboidRegion) {
|
||||
return "cuboid";
|
||||
} else if (region instanceof ProtectedPolygonalRegion) {
|
||||
return "poly2d"; // Differs from getTypeName() on ProtectedRegion
|
||||
} else if (region instanceof GlobalProtectedRegion) {
|
||||
return "global";
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unexpected region type: " + region.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a YAML dumper / parser.
|
||||
*
|
||||
* @return a YAML dumper / parser
|
||||
*/
|
||||
static Yaml createYaml() {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setIndent(2);
|
||||
options.setDefaultFlowStyle(FlowStyle.FLOW);
|
||||
Representer representer = new Representer();
|
||||
representer.setDefaultFlowStyle(FlowStyle.FLOW);
|
||||
|
||||
// We have to use this in order to properly save non-string values
|
||||
return new Yaml(new SafeConstructor(), new Representer(), options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the folder in migrations/region containing the migration files.
|
||||
*
|
||||
* @return the migration folder name
|
||||
*/
|
||||
public String getMigrationFolderName() {
|
||||
return "mysql";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtectedRegion> loadAll() throws IOException {
|
||||
Closer closer = Closer.create();
|
||||
DataLoader loader;
|
||||
|
||||
try {
|
||||
try {
|
||||
loader = new DataLoader(this, closer.register(getConnection()));
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Failed to get a connection to the database", e);
|
||||
}
|
||||
|
||||
try {
|
||||
return loader.load();
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Failed to save the region data to the database", e);
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll(Set<ProtectedRegion> regions) throws IOException {
|
||||
checkNotNull(regions);
|
||||
|
||||
Closer closer = Closer.create();
|
||||
DataUpdater updater;
|
||||
|
||||
try {
|
||||
try {
|
||||
updater = new DataUpdater(this, closer.register(getConnection()));
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Failed to get a connection to the database", e);
|
||||
}
|
||||
|
||||
try {
|
||||
updater.saveAll(regions);
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Failed to save the region data to the database", e);
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveChanges(RegionDifference difference) throws DifferenceSaveException, IOException {
|
||||
checkNotNull(difference);
|
||||
|
||||
Closer closer = Closer.create();
|
||||
DataUpdater updater;
|
||||
|
||||
try {
|
||||
try {
|
||||
updater = new DataUpdater(this, closer.register(getConnection()));
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Failed to get a connection to the database", e);
|
||||
}
|
||||
|
||||
try {
|
||||
updater.saveChanges(difference.getChanged(), difference.getRemoved());
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Failed to save the region data to the database", e);
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
}
|
@ -17,27 +17,38 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.migrator;
|
||||
package com.sk89q.worldguard.protection.managers.storage.sql;
|
||||
|
||||
public class MigratorKey {
|
||||
public final String from;
|
||||
public final String to;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public MigratorKey(String from, String to) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
class StatementBatch {
|
||||
|
||||
public static final int MAX_BATCH_SIZE = 100;
|
||||
|
||||
private final PreparedStatement stmt;
|
||||
private final int batchSize;
|
||||
private int count = 0;
|
||||
|
||||
StatementBatch(PreparedStatement stmt, int batchSize) {
|
||||
this.stmt = stmt;
|
||||
this.batchSize = batchSize;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
MigratorKey other = (MigratorKey) o;
|
||||
|
||||
return other.from.equals(this.from) && other.to.equals(this.to);
|
||||
public void addBatch() throws SQLException {
|
||||
stmt.addBatch();
|
||||
count++;
|
||||
if (count > batchSize) {
|
||||
stmt.executeBatch();
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hash = 17;
|
||||
hash = hash * 31 + this.from.hashCode();
|
||||
hash = hash * 31 + this.to.hashCode();
|
||||
return hash;
|
||||
public void executeRemaining() throws SQLException {
|
||||
if (count > 0) {
|
||||
count = 0;
|
||||
stmt.executeBatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -17,11 +17,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.mysql;
|
||||
package com.sk89q.worldguard.protection.managers.storage.sql;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sk89q.worldguard.internal.util.sql.StatementUtils;
|
||||
import com.sk89q.worldguard.util.io.Closer;
|
||||
import com.sk89q.worldguard.util.sql.DataSourceConfig;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.sql.Connection;
|
||||
@ -35,26 +36,66 @@
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
abstract class UserRowCache<V> extends AbstractJob {
|
||||
/**
|
||||
* Stores a cache of entries from a table for fast lookup and
|
||||
* creates new rows whenever required.
|
||||
*
|
||||
* @param <V> the type of entry
|
||||
*/
|
||||
abstract class TableCache<V> {
|
||||
|
||||
private static final Logger log = Logger.getLogger(TableCache.class.getCanonicalName());
|
||||
|
||||
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 DataSourceConfig config;
|
||||
private final Connection conn;
|
||||
private final String tableName;
|
||||
private final String fieldName;
|
||||
|
||||
protected UserRowCache(MySQLDatabaseImpl database, Connection conn, String fieldName) {
|
||||
super(database, conn);
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param config the data source config
|
||||
* @param conn the connection to use
|
||||
* @param tableName the table name
|
||||
* @param fieldName the field name
|
||||
*/
|
||||
protected TableCache(DataSourceConfig config, Connection conn, String tableName, String fieldName) {
|
||||
this.config = config;
|
||||
this.conn = conn;
|
||||
this.tableName = tableName;
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from the type to the string representation.
|
||||
*
|
||||
* @param o the object
|
||||
* @return the string representation
|
||||
*/
|
||||
protected abstract String fromType(V o);
|
||||
|
||||
/**
|
||||
* Convert from the string representation to the type.
|
||||
*
|
||||
* @param o the string
|
||||
* @return the object
|
||||
*/
|
||||
protected abstract V toType(String o);
|
||||
|
||||
/**
|
||||
* Convert the object to the version that is stored as a key in the map.
|
||||
*
|
||||
* @param object the object
|
||||
* @return the key version
|
||||
*/
|
||||
protected abstract V toKey(V object);
|
||||
|
||||
@Nullable
|
||||
@ -62,6 +103,13 @@ public Integer find(V object) {
|
||||
return cache.get(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch from the database rows that match the given entries, otherwise
|
||||
* create new entries and assign them an ID.
|
||||
*
|
||||
* @param entries a list of entries
|
||||
* @throws SQLException thrown on SQL error
|
||||
*/
|
||||
public void fetch(Collection<V> entries) throws SQLException {
|
||||
synchronized (LOCK) { // Lock across all cache instances
|
||||
checkNotNull(entries);
|
||||
@ -84,9 +132,9 @@ public void fetch(Collection<V> entries) throws SQLException {
|
||||
try {
|
||||
PreparedStatement statement = closer.register(conn.prepareStatement(
|
||||
String.format(
|
||||
"SELECT `user`.`id`, `user`.`" + fieldName + "` " +
|
||||
"FROM `" + config.sqlTablePrefix + "user` AS `user` " +
|
||||
"WHERE `" + fieldName + "` IN (%s)",
|
||||
"SELECT id, " + fieldName + " " +
|
||||
"FROM `" + config.getTablePrefix() + tableName + "` " +
|
||||
"WHERE " + fieldName + " IN (%s)",
|
||||
StatementUtils.preparePlaceHolders(partition.size()))));
|
||||
|
||||
String[] values = new String[partition.size()];
|
||||
@ -118,7 +166,7 @@ public void fetch(Collection<V> entries) throws SQLException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
PreparedStatement statement = closer.register(conn.prepareStatement(
|
||||
"INSERT INTO `" + config.sqlTablePrefix + "user` (`id`, `" + fieldName + "`) VALUES (null, ?)",
|
||||
"INSERT INTO `" + config.getTablePrefix() + tableName + "` (id, " + fieldName + ") VALUES (null, ?)",
|
||||
Statement.RETURN_GENERATED_KEYS));
|
||||
|
||||
for (V entry : missing) {
|
||||
@ -126,10 +174,10 @@ public void fetch(Collection<V> entries) throws SQLException {
|
||||
statement.execute();
|
||||
|
||||
ResultSet generatedKeys = statement.getGeneratedKeys();
|
||||
if (generatedKeys.first()) {
|
||||
if (generatedKeys.next()) {
|
||||
cache.put(toKey(entry), generatedKeys.getInt(1));
|
||||
} else {
|
||||
logger.warning("Could not get the database ID for user " + entry);
|
||||
log.warning("Could not get the database ID for entry " + entry);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@ -139,9 +187,12 @@ public void fetch(Collection<V> entries) throws SQLException {
|
||||
}
|
||||
}
|
||||
|
||||
static class NameRowCache extends UserRowCache<String> {
|
||||
protected NameRowCache(MySQLDatabaseImpl database, Connection conn) {
|
||||
super(database, conn, "name");
|
||||
/**
|
||||
* An index of user rows that utilize the name field.
|
||||
*/
|
||||
static class UserNameCache extends TableCache<String> {
|
||||
protected UserNameCache(DataSourceConfig config, Connection conn) {
|
||||
super(config, conn, "user", "name");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -160,9 +211,12 @@ protected String toKey(String object) {
|
||||
}
|
||||
}
|
||||
|
||||
static class UUIDRowCache extends UserRowCache<UUID> {
|
||||
protected UUIDRowCache(MySQLDatabaseImpl database, Connection conn) {
|
||||
super(database, conn, "uuid");
|
||||
/**
|
||||
* An index of user rows that utilize the UUID field.
|
||||
*/
|
||||
static class UserUuidCache extends TableCache<UUID> {
|
||||
protected UserUuidCache(DataSourceConfig config, Connection conn) {
|
||||
super(config, conn, "user", "uuid");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -181,4 +235,28 @@ protected UUID toKey(UUID object) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An index of group rows.
|
||||
*/
|
||||
static class GroupNameCache extends TableCache<String> {
|
||||
protected GroupNameCache(DataSourceConfig config, Connection conn) {
|
||||
super(config, conn, "group", "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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -19,25 +19,39 @@
|
||||
|
||||
package com.sk89q.worldguard.protection.regions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.protection.UnsupportedIntersectionException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A special region that is not quite "anywhere" (its volume is 0, it
|
||||
* contains no positions, and it does not intersect with any other region).
|
||||
*
|
||||
* <p>Global regions, however, are used to specify a region with flags that
|
||||
* are applied with the lowest priority.</p>
|
||||
*/
|
||||
public class GlobalProtectedRegion extends ProtectedRegion {
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param id the ID
|
||||
*/
|
||||
public GlobalProtectedRegion(String id) {
|
||||
super(id);
|
||||
min = new BlockVector(0, 0, 0);
|
||||
max = new BlockVector(0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BlockVector2D> getPoints() {
|
||||
// This doesn't make sense
|
||||
List<BlockVector2D> pts = new ArrayList<BlockVector2D>();
|
||||
pts.add(new BlockVector2D(min.getBlockX(),min.getBlockZ()));
|
||||
pts.add(new BlockVector2D(min.getBlockX(), min.getBlockZ()));
|
||||
return pts;
|
||||
}
|
||||
|
||||
@ -48,18 +62,18 @@ public int volume() {
|
||||
|
||||
@Override
|
||||
public boolean contains(Vector pt) {
|
||||
// Global regions are handled separately so it must not contain any positions
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return "global";
|
||||
public RegionType getType() {
|
||||
return RegionType.GLOBAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProtectedRegion> getIntersectingRegions(
|
||||
List<ProtectedRegion> regions)
|
||||
throws UnsupportedIntersectionException {
|
||||
public List<ProtectedRegion> getIntersectingRegions(Collection<ProtectedRegion> regions) {
|
||||
// Global regions are handled separately so it must not contain any positions
|
||||
return new ArrayList<ProtectedRegion>();
|
||||
}
|
||||
|
||||
|
@ -19,13 +19,15 @@
|
||||
|
||||
package com.sk89q.worldguard.protection.regions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.protection.UnsupportedIntersectionException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Represents a cuboid region that can be protected.
|
||||
@ -37,9 +39,9 @@ public class ProtectedCuboidRegion extends ProtectedRegion {
|
||||
/**
|
||||
* Construct a new instance of this cuboid region.
|
||||
*
|
||||
* @param id The region id
|
||||
* @param pt1 The first point of this region
|
||||
* @param pt2 The second point of this region
|
||||
* @param id the region id
|
||||
* @param pt1 the first point of this region
|
||||
* @param pt2 the second point of this region
|
||||
*/
|
||||
public ProtectedCuboidRegion(String id, BlockVector pt1, BlockVector pt2) {
|
||||
super(id);
|
||||
@ -47,34 +49,37 @@ public ProtectedCuboidRegion(String id, BlockVector pt1, BlockVector pt2) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given any two points, sets the minimum and maximum points
|
||||
* Given any two points, sets the minimum and maximum points.
|
||||
*
|
||||
* @param pt1 The first point of this region
|
||||
* @param pt2 The second point of this region
|
||||
* @param position1 the first point of this region
|
||||
* @param position2 the second point of this region
|
||||
*/
|
||||
private void setMinMaxPoints(BlockVector pt1, BlockVector pt2) {
|
||||
private void setMinMaxPoints(BlockVector position1, BlockVector position2) {
|
||||
checkNotNull(position1);
|
||||
checkNotNull(position2);
|
||||
|
||||
List<Vector> points = new ArrayList<Vector>();
|
||||
points.add(pt1);
|
||||
points.add(pt2);
|
||||
points.add(position1);
|
||||
points.add(position2);
|
||||
setMinMaxPoints(points);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the lower point of the cuboid.
|
||||
*
|
||||
* @param pt The point to set as the minimum point
|
||||
* @param position the point to set as the minimum point
|
||||
*/
|
||||
public void setMinimumPoint(BlockVector pt) {
|
||||
setMinMaxPoints(pt, max);
|
||||
public void setMinimumPoint(BlockVector position) {
|
||||
setMinMaxPoints(position, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upper point of the cuboid.
|
||||
*
|
||||
* @param pt The point to set as the maximum point
|
||||
* @param position the point to set as the maximum point
|
||||
*/
|
||||
public void setMaximumPoint(BlockVector pt) {
|
||||
setMinMaxPoints(min, pt);
|
||||
public void setMaximumPoint(BlockVector position) {
|
||||
setMinMaxPoints(min, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -103,34 +108,15 @@ public boolean contains(Vector pt) {
|
||||
&& z >= min.getBlockZ() && z < max.getBlockZ()+1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public boolean intersectsWith(ProtectedRegion region) throws UnsupportedIntersectionException {
|
||||
|
||||
if (region instanceof ProtectedCuboidRegion) {
|
||||
ProtectedCuboidRegion r1 = (ProtectedCuboidRegion) this;
|
||||
ProtectedCuboidRegion r2 = (ProtectedCuboidRegion) region;
|
||||
BlockVector min1 = r1.getMinimumPoint();
|
||||
BlockVector max1 = r1.getMaximumPoint();
|
||||
BlockVector min2 = r2.getMinimumPoint();
|
||||
BlockVector max2 = r2.getMaximumPoint();
|
||||
|
||||
return !(min1.getBlockX() > max2.getBlockX()
|
||||
|| min1.getBlockY() > max2.getBlockY()
|
||||
|| min1.getBlockZ() > max2.getBlockZ()
|
||||
|| max1.getBlockX() < min2.getBlockX()
|
||||
|| max1.getBlockY() < min2.getBlockY()
|
||||
|| max1.getBlockZ() < min2.getBlockZ());
|
||||
} else if (region instanceof ProtectedPolygonalRegion) {
|
||||
throw new UnsupportedIntersectionException();
|
||||
} else {
|
||||
throw new UnsupportedIntersectionException();
|
||||
}
|
||||
@Override
|
||||
public RegionType getType() {
|
||||
return RegionType.CUBOID;
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public List<ProtectedRegion> getIntersectingRegions(List<ProtectedRegion> regions) throws UnsupportedIntersectionException {
|
||||
public List<ProtectedRegion> getIntersectingRegions(Collection<ProtectedRegion> regions) {
|
||||
checkNotNull(regions);
|
||||
|
||||
List<ProtectedRegion> intersectingRegions = new ArrayList<ProtectedRegion>();
|
||||
|
||||
for (ProtectedRegion region : regions) {
|
||||
@ -139,28 +125,21 @@ public List<ProtectedRegion> getIntersectingRegions(List<ProtectedRegion> region
|
||||
// If both regions are Cuboids and their bounding boxes intersect, they intersect
|
||||
if (region instanceof ProtectedCuboidRegion) {
|
||||
intersectingRegions.add(region);
|
||||
continue;
|
||||
} else if (region instanceof ProtectedPolygonalRegion) {
|
||||
// If either region contains the points of the other,
|
||||
// or if any edges intersect, the regions intersect
|
||||
if (containsAny(region.getPoints())
|
||||
|| region.containsAny(getPoints())
|
||||
|| intersectsEdges(region)) {
|
||||
if (containsAny(region.getPoints()) || region.containsAny(getPoints()) || intersectsEdges(region)) {
|
||||
intersectingRegions.add(region);
|
||||
continue;
|
||||
}
|
||||
} else if (region instanceof GlobalProtectedRegion) {
|
||||
// Never intersects
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
throw new IllegalArgumentException("Not supported yet.");
|
||||
}
|
||||
}
|
||||
return intersectingRegions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return "cuboid";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int volume() {
|
||||
int xLength = max.getBlockX() - min.getBlockX() + 1;
|
||||
@ -169,4 +148,5 @@ public int volume() {
|
||||
|
||||
return xLength * yLength * zLength;
|
||||
}
|
||||
|
||||
}
|
@ -19,23 +19,25 @@
|
||||
|
||||
package com.sk89q.worldguard.protection.regions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.protection.UnsupportedIntersectionException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class ProtectedPolygonalRegion extends ProtectedRegion {
|
||||
|
||||
protected List<BlockVector2D> points;
|
||||
protected int minY;
|
||||
protected int maxY;
|
||||
private List<BlockVector2D> points;
|
||||
private int minY;
|
||||
private int maxY;
|
||||
|
||||
public ProtectedPolygonalRegion(String id, List<BlockVector2D> points, int minY, int maxY) {
|
||||
super(id);
|
||||
this.points = points;
|
||||
setMinMaxPoints(points, minY, maxY);
|
||||
this.points = points;
|
||||
this.minY = min.getBlockY();
|
||||
this.maxY = max.getBlockY();
|
||||
}
|
||||
@ -48,6 +50,8 @@ public ProtectedPolygonalRegion(String id, List<BlockVector2D> points, int minY,
|
||||
* @param maxY The maximum y coordinate
|
||||
*/
|
||||
private void setMinMaxPoints(List<BlockVector2D> points2D, int minY, int maxY) {
|
||||
checkNotNull(points2D);
|
||||
|
||||
List<Vector> points = new ArrayList<Vector>();
|
||||
int y = minY;
|
||||
for (BlockVector2D point2D : points2D) {
|
||||
@ -57,18 +61,18 @@ private void setMinMaxPoints(List<BlockVector2D> points2D, int minY, int maxY) {
|
||||
setMinMaxPoints(points);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BlockVector2D> getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a point is inside this region.
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(Vector pt) {
|
||||
int targetX = pt.getBlockX(); //wide
|
||||
int targetY = pt.getBlockY(); //height
|
||||
int targetZ = pt.getBlockZ(); //depth
|
||||
public boolean contains(Vector position) {
|
||||
checkNotNull(position);
|
||||
|
||||
int targetX = position.getBlockX(); // Width
|
||||
int targetY = position.getBlockY(); // Height
|
||||
int targetZ = position.getBlockZ(); // Depth
|
||||
|
||||
if (targetY < minY || targetY > maxY) {
|
||||
return false;
|
||||
@ -124,7 +128,14 @@ public boolean contains(Vector pt) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProtectedRegion> getIntersectingRegions(List<ProtectedRegion> regions) throws UnsupportedIntersectionException {
|
||||
public RegionType getType() {
|
||||
return RegionType.POLYGON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProtectedRegion> getIntersectingRegions(Collection<ProtectedRegion> regions) {
|
||||
checkNotNull(regions);
|
||||
|
||||
List<ProtectedRegion> intersectingRegions = new ArrayList<ProtectedRegion>();
|
||||
|
||||
for (ProtectedRegion region : regions) {
|
||||
@ -133,67 +144,22 @@ public List<ProtectedRegion> getIntersectingRegions(List<ProtectedRegion> region
|
||||
if (region instanceof ProtectedPolygonalRegion || region instanceof ProtectedCuboidRegion) {
|
||||
// If either region contains the points of the other,
|
||||
// or if any edges intersect, the regions intersect
|
||||
if (containsAny(region.getPoints())
|
||||
|| region.containsAny(getPoints())
|
||||
|| intersectsEdges(region)) {
|
||||
if (containsAny(region.getPoints()) || region.containsAny(getPoints()) || intersectsEdges(region)) {
|
||||
intersectingRegions.add(region);
|
||||
continue;
|
||||
}
|
||||
} else if (region instanceof GlobalProtectedRegion) {
|
||||
// Never intersects
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
throw new IllegalArgumentException("Not supported yet.");
|
||||
}
|
||||
}
|
||||
return intersectingRegions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the type of region as a user-friendly name.
|
||||
*
|
||||
* @return type of region
|
||||
*/
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return "polygon";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int volume() {
|
||||
int volume = 0;
|
||||
// TODO: Fix this
|
||||
/*int numPoints = points.size();
|
||||
if (numPoints < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double area = 0;
|
||||
int xa, z1, z2;
|
||||
|
||||
for (int i = 0; i < numPoints; i++) {
|
||||
xa = points.get(i).getBlockX();
|
||||
//za = points.get(i).getBlockZ();
|
||||
|
||||
if (points.get(i + 1) == null) {
|
||||
z1 = points.get(0).getBlockZ();
|
||||
} else {
|
||||
z1 = points.get(i + 1).getBlockZ();
|
||||
}
|
||||
if (points.get(i - 1) == null) {
|
||||
z2 = points.get(numPoints - 1).getBlockZ();
|
||||
} else {
|
||||
z2 = points.get(i - 1).getBlockZ();
|
||||
}
|
||||
|
||||
area = area + (xa * (z1 - z2));
|
||||
}
|
||||
|
||||
xa = points.get(0).getBlockX();
|
||||
//za = points.get(0).getBlockZ();
|
||||
|
||||
area = area + (xa * (points.get(1).getBlockZ() - points.get(numPoints - 1).getBlockZ()));
|
||||
|
||||
volume = (Math.abs(maxY - minY) + 1) * (int) Math.ceil((Math.abs(area) / 2));*/
|
||||
|
||||
return volume;
|
||||
// TODO: Fix this -- the previous algorithm returned incorrect results, but the current state of this method is even worse
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,45 +24,62 @@
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.domains.DefaultDomain;
|
||||
import com.sk89q.worldguard.protection.UnsupportedIntersectionException;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.util.ChangeTracked;
|
||||
import com.sk89q.worldguard.util.Normal;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Represents a region of any shape and size that can be protected.
|
||||
* Represents a region that can be indexed and have spatial queries performed
|
||||
* against it.
|
||||
*
|
||||
* <p>Instances can be modified and access from several threads at a time.</p>
|
||||
*/
|
||||
public abstract class ProtectedRegion implements Comparable<ProtectedRegion> {
|
||||
public abstract class ProtectedRegion implements ChangeTracked, Comparable<ProtectedRegion> {
|
||||
|
||||
private static final Pattern VALID_ID_PATTERN = Pattern.compile("^[A-Za-z0-9_,'\\-\\+/]{1,}$");
|
||||
|
||||
protected BlockVector min;
|
||||
protected BlockVector max;
|
||||
|
||||
private static final Pattern idPattern = Pattern.compile("^[A-Za-z0-9_,'\\-\\+/]{1,}$");
|
||||
|
||||
private String id;
|
||||
private final String id;
|
||||
private int priority = 0;
|
||||
private ProtectedRegion parent;
|
||||
private DefaultDomain owners = new DefaultDomain();
|
||||
private DefaultDomain members = new DefaultDomain();
|
||||
private Map<Flag<?>, Object> flags = new ConcurrentHashMap<Flag<?>, Object>();
|
||||
private ConcurrentMap<Flag<?>, Object> flags = new ConcurrentHashMap<Flag<?>, Object>();
|
||||
private boolean dirty = true;
|
||||
|
||||
/**
|
||||
* Construct a new instance of this region.
|
||||
*
|
||||
* @param id The id (name) of this region.
|
||||
* @param id the name of this region
|
||||
* @throws IllegalArgumentException thrown if the ID is invalid (see {@link #isValidId(String)}
|
||||
*/
|
||||
public ProtectedRegion(String id) {
|
||||
this.id = id;
|
||||
ProtectedRegion(String id) { // Package private because we can't have people creating their own region types
|
||||
checkNotNull(id);
|
||||
|
||||
if (!isValidId(id)) {
|
||||
throw new IllegalArgumentException("Invalid region ID: " + id);
|
||||
}
|
||||
|
||||
this.id = Normal.normalize(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum and maximum points of the bounding box for a region
|
||||
* Set the minimum and maximum points of the bounding box for a region
|
||||
*
|
||||
* @param points The points to set. Must have at least one element.
|
||||
* @param points the points to set with at least one entry
|
||||
*/
|
||||
protected void setMinMaxPoints(List<Vector> points) {
|
||||
int minX = points.get(0).getBlockX();
|
||||
@ -85,39 +102,45 @@ protected void setMinMaxPoints(List<Vector> points) {
|
||||
if (y > maxY) maxY = y;
|
||||
if (z > maxZ) maxZ = z;
|
||||
}
|
||||
|
||||
|
||||
setDirty(true);
|
||||
min = new BlockVector(minX, minY, minZ);
|
||||
max = new BlockVector(maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id of this region
|
||||
* Gets the name of this region
|
||||
*
|
||||
* @return the id
|
||||
* @return the name
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lower point of the cuboid.
|
||||
* Get a vector containing the smallest X, Y, and Z components for the
|
||||
* corner of the axis-aligned bounding box that contains this region.
|
||||
*
|
||||
* @return min point
|
||||
* @return the minimum point
|
||||
*/
|
||||
public BlockVector getMinimumPoint() {
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the upper point of the cuboid.
|
||||
* Get a vector containing the highest X, Y, and Z components for the
|
||||
* corner of the axis-aligned bounding box that contains this region.
|
||||
*
|
||||
* @return max point
|
||||
* @return the maximum point
|
||||
*/
|
||||
public BlockVector getMaximumPoint() {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the priority of the region, where higher numbers indicate a higher
|
||||
* priority.
|
||||
*
|
||||
* @return the priority
|
||||
*/
|
||||
public int getPriority() {
|
||||
@ -125,27 +148,36 @@ public int getPriority() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param priority the priority to setFlag
|
||||
* Set the priority of the region, where higher numbers indicate a higher
|
||||
* priority.
|
||||
*
|
||||
* @param priority the priority to set
|
||||
*/
|
||||
public void setPriority(int priority) {
|
||||
setDirty(true);
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the curParent
|
||||
* Get the parent of the region, if one exists.
|
||||
*
|
||||
* @return the parent, or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public ProtectedRegion getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the curParent. This checks to make sure that it will not result
|
||||
* in circular inheritance.
|
||||
* Set the parent of this region. This checks to make sure that it will
|
||||
* not result in circular inheritance.
|
||||
*
|
||||
* @param parent the curParent to setFlag
|
||||
* @param parent the new parent
|
||||
* @throws CircularInheritanceException when circular inheritance is detected
|
||||
*/
|
||||
public void setParent(ProtectedRegion parent) throws CircularInheritanceException {
|
||||
public void setParent(@Nullable ProtectedRegion parent) throws CircularInheritanceException {
|
||||
setDirty(true);
|
||||
|
||||
if (parent == null) {
|
||||
this.parent = null;
|
||||
return;
|
||||
@ -166,23 +198,38 @@ public void setParent(ProtectedRegion parent) throws CircularInheritanceExceptio
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the parent (set the parent to {@code null}).
|
||||
*/
|
||||
public void clearParent() {
|
||||
setDirty(true);
|
||||
this.parent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the owners
|
||||
* Get the domain that contains the owners of this region.
|
||||
*
|
||||
* @return the domain
|
||||
*/
|
||||
public DefaultDomain getOwners() {
|
||||
|
||||
return owners;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param owners the owners to setFlag
|
||||
* Set the owner domain.
|
||||
*
|
||||
* @param owners the new domain
|
||||
*/
|
||||
public void setOwners(DefaultDomain owners) {
|
||||
checkNotNull(owners);
|
||||
setDirty(true);
|
||||
this.owners = owners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the domain that contains the members of this region, which does
|
||||
* not automatically include the owners.
|
||||
*
|
||||
* @return the members
|
||||
*/
|
||||
public DefaultDomain getMembers() {
|
||||
@ -190,9 +237,13 @@ public DefaultDomain getMembers() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param members the members to setFlag
|
||||
* Set the members domain.
|
||||
*
|
||||
* @param members the new domain
|
||||
*/
|
||||
public void setMembers(DefaultDomain members) {
|
||||
checkNotNull(members);
|
||||
setDirty(true);
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
@ -212,6 +263,8 @@ public boolean hasMembersOrOwners() {
|
||||
* @return whether an owner
|
||||
*/
|
||||
public boolean isOwner(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
|
||||
if (owners.contains(player)) {
|
||||
return true;
|
||||
}
|
||||
@ -233,8 +286,12 @@ public boolean isOwner(LocalPlayer player) {
|
||||
*
|
||||
* @param playerName player name to check
|
||||
* @return whether an owner
|
||||
* @deprecated Names are deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isOwner(String playerName) {
|
||||
checkNotNull(playerName);
|
||||
|
||||
if (owners.contains(playerName)) {
|
||||
return true;
|
||||
}
|
||||
@ -259,6 +316,8 @@ public boolean isOwner(String playerName) {
|
||||
* @return whether an owner or member
|
||||
*/
|
||||
public boolean isMember(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
|
||||
if (isOwner(player)) {
|
||||
return true;
|
||||
}
|
||||
@ -285,8 +344,12 @@ public boolean isMember(LocalPlayer player) {
|
||||
*
|
||||
* @param playerName player name to check
|
||||
* @return whether an owner or member
|
||||
* @deprecated Names are deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isMember(String playerName) {
|
||||
checkNotNull(playerName);
|
||||
|
||||
if (isOwner(playerName)) {
|
||||
return true;
|
||||
}
|
||||
@ -308,13 +371,14 @@ public boolean isMember(String playerName) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a player is a member of the region
|
||||
* or any of its parents.
|
||||
* Checks whether a player is a member of the region or any of its parents.
|
||||
*
|
||||
* @param player player to check
|
||||
* @return whether an member
|
||||
*/
|
||||
public boolean isMemberOnly(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
|
||||
if (members.contains(player)) {
|
||||
return true;
|
||||
}
|
||||
@ -334,32 +398,40 @@ public boolean isMemberOnly(LocalPlayer player) {
|
||||
/**
|
||||
* Get a flag's value.
|
||||
*
|
||||
* @param <T> The flag type
|
||||
* @param <V> The type of the flag's value
|
||||
* @param flag The flag to check
|
||||
* @return value or null if isn't defined
|
||||
* @param flag the flag to check
|
||||
* @return the value or null if isn't defined
|
||||
* @param <T> the flag type
|
||||
* @param <V> the type of the flag's value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public <T extends Flag<V>, V> V getFlag(T flag) {
|
||||
checkNotNull(flag);
|
||||
|
||||
Object obj = flags.get(flag);
|
||||
V val;
|
||||
|
||||
if (obj != null) {
|
||||
val = (V) obj;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a flag's value.
|
||||
*
|
||||
* @param <T> The flag type
|
||||
* @param <V> The type of the flag's value
|
||||
* @param flag The flag to check
|
||||
* @param val The value to set
|
||||
* @param flag the flag to check
|
||||
* @param val the value to set
|
||||
* @param <T> the flag type
|
||||
* @param <V> the type of the flag's value
|
||||
*/
|
||||
public <T extends Flag<V>, V> void setFlag(T flag, V val) {
|
||||
public <T extends Flag<V>, V> void setFlag(T flag, @Nullable V val) {
|
||||
checkNotNull(flag);
|
||||
setDirty(true);
|
||||
|
||||
if (val == null) {
|
||||
flags.remove(flag);
|
||||
} else {
|
||||
@ -370,30 +442,35 @@ public <T extends Flag<V>, V> void setFlag(T flag, V val) {
|
||||
/**
|
||||
* Get the map of flags.
|
||||
*
|
||||
* @return The map of flags currently used for this region
|
||||
* @return the map of flags currently used for this region
|
||||
*/
|
||||
public Map<Flag<?>, Object> getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the map of flags.
|
||||
* Set the map of flags.
|
||||
*
|
||||
* @param flags The flags to set
|
||||
* <p>A copy of the map will be used.</p>
|
||||
*
|
||||
* @param flags the flags to set
|
||||
*/
|
||||
public void setFlags(Map<Flag<?>, Object> flags) {
|
||||
this.flags = flags;
|
||||
checkNotNull(flags);
|
||||
|
||||
setDirty(true);
|
||||
this.flags = new ConcurrentHashMap<Flag<?>, Object>(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 2D points for this region
|
||||
* Get points of the region projected onto the X-Z plane.
|
||||
*
|
||||
* @return The points for this region as (x, z) coordinates
|
||||
* @return the points
|
||||
*/
|
||||
public abstract List<BlockVector2D> getPoints();
|
||||
|
||||
/**
|
||||
* Get the number of blocks in this region
|
||||
* Get the number of blocks in this region.
|
||||
*
|
||||
* @return the volume of this region in blocks
|
||||
*/
|
||||
@ -408,84 +485,79 @@ public void setFlags(Map<Flag<?>, Object> flags) {
|
||||
public abstract boolean contains(Vector pt);
|
||||
|
||||
/**
|
||||
* Check to see if a point is inside this region.
|
||||
* Check to see if a position is contained within this region.
|
||||
*
|
||||
* @param pt The point to check
|
||||
* @return Whether {@code pt} is in this region
|
||||
* @param position the position to check
|
||||
* @return whether {@code position} is in this region
|
||||
*/
|
||||
public boolean contains(BlockVector2D pt) {
|
||||
return contains(new Vector(pt.getBlockX(), min.getBlockY(), pt.getBlockZ()));
|
||||
public boolean contains(BlockVector2D position) {
|
||||
checkNotNull(position);
|
||||
return contains(new Vector(position.getBlockX(), min.getBlockY(), position.getBlockZ()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a point is inside this region.
|
||||
*
|
||||
* @param x The x coordinate to check
|
||||
* @param y The y coordinate to check
|
||||
* @param z The z coordinate to check
|
||||
* @return Whether this region contains the points at the given coordinate
|
||||
* @param x the x coordinate to check
|
||||
* @param y the y coordinate to check
|
||||
* @param z the z coordinate to check
|
||||
* @return whether this region contains the point
|
||||
*/
|
||||
public boolean contains(int x, int y, int z) {
|
||||
return contains(new Vector(x, y, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if any of the 2D points are inside this region.
|
||||
* Check to see if any of the points are inside this region projected
|
||||
* onto the X-Z plane.
|
||||
*
|
||||
* @param pts a list positions
|
||||
* @param positions a list of positions
|
||||
* @return true if contained
|
||||
*/
|
||||
public boolean containsAny(List<BlockVector2D> pts) {
|
||||
for (BlockVector2D pt : pts) {
|
||||
public boolean containsAny(List<BlockVector2D> positions) {
|
||||
checkNotNull(positions);
|
||||
|
||||
for (BlockVector2D pt : positions) {
|
||||
if (contains(pt)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares to another region.<br>
|
||||
*<br>
|
||||
* Orders primarily by the priority, descending<br>
|
||||
* Orders secondarily by the id, ascending
|
||||
* Get the type of region.
|
||||
*
|
||||
* @param other The region to compare to
|
||||
* @return the type
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(ProtectedRegion other) {
|
||||
if (priority > other.priority) {
|
||||
return -1;
|
||||
} else if (priority < other.priority) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return id.compareTo(other.id);
|
||||
}
|
||||
public abstract RegionType getType();
|
||||
|
||||
/**
|
||||
* Return the type of region as a user-friendly, lowercase name.
|
||||
*
|
||||
* @return type of region
|
||||
* @deprecated use {@link #getType()}
|
||||
*/
|
||||
public abstract String getTypeName();
|
||||
@Deprecated
|
||||
public final String getTypeName() {
|
||||
return getType().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of intersecting regions.
|
||||
* Return a list of regions from the given list of regions that intersect
|
||||
* with this region.
|
||||
*
|
||||
* @param regions The list of regions to source from
|
||||
* @return The elements of {@code regions} that intersect with this region
|
||||
* @throws UnsupportedIntersectionException if an invalid intersection is detected
|
||||
* @param regions a list of regions to source from
|
||||
* @return the elements of {@code regions} that intersect with this region
|
||||
*/
|
||||
public abstract List<ProtectedRegion> getIntersectingRegions(
|
||||
List<ProtectedRegion> regions)
|
||||
throws UnsupportedIntersectionException;
|
||||
public abstract List<ProtectedRegion> getIntersectingRegions(Collection<ProtectedRegion> regions);
|
||||
|
||||
/**
|
||||
* Checks if the bounding box of a region intersects with with the bounding
|
||||
* box of this region
|
||||
* box of this region.
|
||||
*
|
||||
* @param region The region to check
|
||||
* @param region the region to check
|
||||
* @return whether the given region intersects
|
||||
*/
|
||||
protected boolean intersectsBoundingBox(ProtectedRegion region) {
|
||||
@ -507,9 +579,9 @@ protected boolean intersectsBoundingBox(ProtectedRegion region) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares all edges of two regions to see if any of them intersect
|
||||
* Compares all edges of two regions to see if any of them intersect.
|
||||
*
|
||||
* @param region The region to check
|
||||
* @param region the region to check
|
||||
* @return whether any edges of a region intersect
|
||||
*/
|
||||
protected boolean intersectsEdges(ProtectedRegion region) {
|
||||
@ -540,15 +612,27 @@ protected boolean intersectsEdges(ProtectedRegion region) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the given ID is accurate.
|
||||
*
|
||||
* @param id The id to check
|
||||
* @see #idPattern
|
||||
* @return Whether the region id given is valid
|
||||
*/
|
||||
public static boolean isValidId(String id) {
|
||||
return idPattern.matcher(id).matches();
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return dirty || owners.isDirty() || members.isDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirty(boolean dirty) {
|
||||
this.dirty = dirty;
|
||||
owners.setDirty(dirty);
|
||||
members.setDirty(dirty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ProtectedRegion other) {
|
||||
if (getPriority() > other.getPriority()) {
|
||||
return -1;
|
||||
} else if (getPriority() < other.getPriority()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return getId().compareTo(other.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -556,9 +640,6 @@ public int hashCode(){
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this region has the same ID as another region.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof ProtectedRegion)) {
|
||||
@ -569,8 +650,27 @@ public boolean equals(Object obj) {
|
||||
return other.getId().equals(getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProtectedRegion{" +
|
||||
"id='" + id + "', " +
|
||||
"type='" + getType() + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when setting a curParent would create a circular inheritance
|
||||
* Checks to see if the given ID is a valid ID.
|
||||
*
|
||||
* @param id the id to check
|
||||
* @return whether the region id given is valid
|
||||
*/
|
||||
public static boolean isValidId(String id) {
|
||||
checkNotNull(id);
|
||||
return VALID_ID_PATTERN.matcher(id).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when setting a parent would create a circular inheritance
|
||||
* situation.
|
||||
*/
|
||||
public static class CircularInheritanceException extends Exception {
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.regions;
|
||||
|
||||
/**
|
||||
* An enum of supported region types.
|
||||
*/
|
||||
public enum RegionType {
|
||||
|
||||
// Do not change the names
|
||||
CUBOID("cuboid"),
|
||||
POLYGON("poly2d"),
|
||||
GLOBAL("global");
|
||||
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param name the region name
|
||||
*/
|
||||
RegionType(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the region.
|
||||
*
|
||||
* @return the name of the region
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.util;
|
||||
package com.sk89q.worldguard.protection.util;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.util;
|
||||
package com.sk89q.worldguard.protection.util;
|
||||
|
||||
/**
|
||||
* Thrown when there are unresolved names.
|
@ -17,10 +17,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.migrator;
|
||||
package com.sk89q.worldguard.protection.util.migrator;
|
||||
|
||||
/**
|
||||
* Thrown when a migration fails.
|
||||
*/
|
||||
public class MigrationException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MigrationException() {
|
||||
super();
|
||||
@ -37,4 +39,5 @@ public MigrationException(String message, Throwable cause) {
|
||||
public MigrationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.migrator;
|
||||
package com.sk89q.worldguard.protection.util.migrator;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.sk89q.squirrelid.Profile;
|
42
src/main/java/com/sk89q/worldguard/util/ChangeTracked.java
Normal file
42
src/main/java/com/sk89q/worldguard/util/ChangeTracked.java
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
/**
|
||||
* An object that keeps track of a dirty flag that is set to true when changes
|
||||
* are made to this object.
|
||||
*/
|
||||
public interface ChangeTracked {
|
||||
|
||||
/**
|
||||
* Tests whether changes have been made.
|
||||
*
|
||||
* @return true if changes have been made
|
||||
*/
|
||||
boolean isDirty();
|
||||
|
||||
/**
|
||||
* Set whether changes have been made.
|
||||
*
|
||||
* @param dirty a new dirty state
|
||||
*/
|
||||
void setDirty(boolean dirty);
|
||||
|
||||
}
|
119
src/main/java/com/sk89q/worldguard/util/Normal.java
Normal file
119
src/main/java/com/sk89q/worldguard/util/Normal.java
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.text.Normalizer;
|
||||
import java.text.Normalizer.Form;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Normal names are strings that are considered equal after they have been
|
||||
* normalized using Unicode's NFC form and made lowercase.
|
||||
*/
|
||||
public final class Normal {
|
||||
|
||||
private final String name;
|
||||
@Nullable
|
||||
private final String normal;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param name a new instance
|
||||
*/
|
||||
private Normal(String name) {
|
||||
checkNotNull(name);
|
||||
|
||||
this.name = name;
|
||||
String normal = normalize(name);
|
||||
if (!normal.equals(name)) { // Simple comparison
|
||||
this.normal = normal;
|
||||
} else {
|
||||
this.normal = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original name before normalization.
|
||||
*
|
||||
* @return the original name before normalization
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the normalized name.
|
||||
*
|
||||
* @return the normal name
|
||||
*/
|
||||
public String getNormal() {
|
||||
return normal != null ? normal : name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a string according to the rules of this class.
|
||||
*
|
||||
* @param name an string
|
||||
* @return the normalized string
|
||||
*/
|
||||
public static String normalize(String name) {
|
||||
return Normalizer.normalize(name.toLowerCase(), Form.NFC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param name the name
|
||||
* @return an instance
|
||||
*/
|
||||
public static Normal normal(String name) {
|
||||
return new Normal(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Normal that = (Normal) o;
|
||||
|
||||
return getNormal().equals(that.getNormal());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getNormal().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the un-normalized name.
|
||||
*
|
||||
* @return the un-normalized name
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.util.sql;
|
||||
|
||||
import com.jolbox.bonecp.BoneCPConfig;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Describes a data source.
|
||||
*/
|
||||
public class DataSourceConfig {
|
||||
|
||||
private final String dsn;
|
||||
private final String username;
|
||||
private final String password;
|
||||
private final String tablePrefix;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param dsn the DSN
|
||||
* @param username the username
|
||||
* @param password the password
|
||||
* @param tablePrefix the table prefix
|
||||
*/
|
||||
public DataSourceConfig(String dsn, String username, String password, String tablePrefix) {
|
||||
checkNotNull(dsn);
|
||||
checkNotNull(username);
|
||||
checkNotNull(password);
|
||||
checkNotNull(tablePrefix);
|
||||
|
||||
this.dsn = dsn;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.tablePrefix = tablePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DSN.
|
||||
*
|
||||
* @return the DSN
|
||||
*/
|
||||
public String getDsn() {
|
||||
return dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the username.
|
||||
*
|
||||
* @return the username
|
||||
*/
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the password.
|
||||
*
|
||||
* @return the password
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table prefix.
|
||||
*
|
||||
* @return the table prefix
|
||||
*/
|
||||
public String getTablePrefix() {
|
||||
return tablePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with a new DSN.
|
||||
*
|
||||
* @param dsn a new DSN string
|
||||
* @return a new instance
|
||||
*/
|
||||
public DataSourceConfig setDsn(String dsn) {
|
||||
return new DataSourceConfig(dsn, username, password, tablePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with a new username.
|
||||
*
|
||||
* @param username a new username
|
||||
* @return a new instance
|
||||
*/
|
||||
public DataSourceConfig setUsername(String username) {
|
||||
return new DataSourceConfig(dsn, username, password, tablePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with a new password.
|
||||
*
|
||||
* @param password a new password
|
||||
* @return a new instance
|
||||
*/
|
||||
public DataSourceConfig setPassword(String password) {
|
||||
return new DataSourceConfig(dsn, username, password, tablePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with a new table prefix.
|
||||
*
|
||||
* @param tablePrefix the new table prefix
|
||||
* @return a new instance
|
||||
*/
|
||||
public DataSourceConfig setTablePrefix(String tablePrefix) {
|
||||
return new DataSourceConfig(dsn, username, password, tablePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BoneCP configuration object.
|
||||
*
|
||||
* @return a new configuration object
|
||||
*/
|
||||
public BoneCPConfig createBoneCPConfig() {
|
||||
BoneCPConfig config = new BoneCPConfig();
|
||||
config.setJdbcUrl(dsn);
|
||||
config.setUsername(username);
|
||||
config.setPassword(password);
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
@ -5,6 +5,10 @@ ALTER TABLE `${tablePrefix}region_players`
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`region_id`, `world_id`, `user_id`, `owner`);
|
||||
|
||||
ALTER TABLE `${tablePrefix}region_groups`
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`region_id`, `world_id`, `group_id`, `owner`);
|
||||
|
||||
-- Fix WORLDGUARD-3030
|
||||
-- Adds UUID support
|
||||
|
||||
@ -14,4 +18,10 @@ ALTER TABLE `${tablePrefix}user`
|
||||
ALTER TABLE `${tablePrefix}user`
|
||||
CHANGE COLUMN `name` `name` VARCHAR(64) NULL COLLATE 'utf8_bin' AFTER `id`,
|
||||
ADD COLUMN `uuid` CHAR(36) NULL AFTER `name`,
|
||||
ADD UNIQUE INDEX `uuid` (`uuid`);
|
||||
ADD UNIQUE INDEX `uuid` (`uuid`);
|
||||
|
||||
-- Strings with differing numbers of trailing spaces are equal in MySQL
|
||||
-- The domains have been updated to trim strings
|
||||
|
||||
UPDATE `${tablePrefix}user` SET `name` = TRIM(`name`);
|
||||
UPDATE `${tablePrefix}group` SET `name` = TRIM(`name`);
|
160
src/main/resources/migrations/region/sqlite/V1__Initial.sql
Normal file
160
src/main/resources/migrations/region/sqlite/V1__Initial.sql
Normal file
@ -0,0 +1,160 @@
|
||||
|
||||
CREATE TABLE "${tablePrefix}world" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
NOT NULL,
|
||||
name TEXT NOT NULL
|
||||
UNIQUE
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE "${tablePrefix}region" (
|
||||
id TEXT NOT NULL,
|
||||
world_id INTEGER NOT NULL
|
||||
REFERENCES "${tablePrefix}world" ( id ) ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
type TEXT NOT NULL,
|
||||
priority INTEGER NOT NULL,
|
||||
parent TEXT DEFAULT ( NULL )
|
||||
--REFERENCES "${tablePrefix}region" ( id ) ON DELETE SET NULL
|
||||
-- ON UPDATE CASCADE -- Not supported
|
||||
,
|
||||
PRIMARY KEY ( id, world_id )
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE "${tablePrefix}user" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
NOT NULL,
|
||||
name TEXT UNIQUE
|
||||
DEFAULT ( NULL ),
|
||||
uuid TEXT UNIQUE
|
||||
DEFAULT ( NULL )
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE "${tablePrefix}group" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
NOT NULL,
|
||||
name TEXT NOT NULL
|
||||
UNIQUE
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE "${tablePrefix}region_cuboid" (
|
||||
region_id TEXT NOT NULL,
|
||||
world_id INTEGER NOT NULL,
|
||||
min_x INTEGER NOT NULL,
|
||||
min_y INTEGER NOT NULL,
|
||||
min_z INTEGER NOT NULL,
|
||||
max_x INTEGER NOT NULL,
|
||||
max_y INTEGER NOT NULL,
|
||||
max_z INTEGER NOT NULL,
|
||||
PRIMARY KEY ( region_id, world_id ),
|
||||
FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region" ( id, world_id ) ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE "${tablePrefix}region_poly2d" (
|
||||
region_id TEXT NOT NULL,
|
||||
world_id INTEGER NOT NULL,
|
||||
min_y INTEGER NOT NULL,
|
||||
max_y INTEGER NOT NULL,
|
||||
PRIMARY KEY ( region_id, world_id ),
|
||||
FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region" ( id, world_id ) ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE "${tablePrefix}region_poly2d_point" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
NOT NULL,
|
||||
region_id TEXT NOT NULL,
|
||||
world_id INTEGER NOT NULL,
|
||||
x INTEGER NOT NULL,
|
||||
z INTEGER NOT NULL,
|
||||
FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region_poly2d" ( region_id, world_id ) ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE "${tablePrefix}region_groups" (
|
||||
region_id TEXT NOT NULL,
|
||||
world_id INTEGER NOT NULL,
|
||||
group_id INTEGER NOT NULL
|
||||
REFERENCES "${tablePrefix}group" ( id ) ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
owner BOOLEAN NOT NULL,
|
||||
PRIMARY KEY ( region_id, world_id, group_id ),
|
||||
FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region" ( id, world_id ) ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE "${tablePrefix}region_flag" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
NOT NULL,
|
||||
region_id TEXT NOT NULL,
|
||||
world_id INTEGER NOT NULL,
|
||||
flag TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region" ( id, world_id ) ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE "${tablePrefix}region_players" (
|
||||
region_id TEXT NOT NULL,
|
||||
world_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL
|
||||
REFERENCES "${tablePrefix}user" ( id ) ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
owner BOOLEAN NOT NULL,
|
||||
PRIMARY KEY ( region_id, world_id, user_id, owner ),
|
||||
FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region" ( id, world_id ) ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
|
||||
CREATE INDEX "idx_${tablePrefix}region_cuboid_region_id" ON "${tablePrefix}region_cuboid" (
|
||||
region_id
|
||||
);
|
||||
|
||||
|
||||
CREATE INDEX "idx_${tablePrefix}region_world_id" ON "${tablePrefix}region" (
|
||||
world_id
|
||||
);
|
||||
|
||||
|
||||
CREATE INDEX "idx_${tablePrefix}region_parent" ON "${tablePrefix}region" (
|
||||
parent
|
||||
);
|
||||
|
||||
|
||||
CREATE INDEX "idx_${tablePrefix}region_poly2d_region_id" ON "${tablePrefix}region_poly2d" (
|
||||
region_id
|
||||
);
|
||||
|
||||
|
||||
CREATE INDEX "idx_${tablePrefix}region_poly2d_point_region_world_id" ON "${tablePrefix}region_poly2d_point" (
|
||||
region_id,
|
||||
world_id
|
||||
);
|
||||
|
||||
|
||||
CREATE INDEX "idx_${tablePrefix}region_groups_region_id" ON "${tablePrefix}region_groups" (
|
||||
region_id
|
||||
);
|
||||
|
||||
|
||||
CREATE INDEX "idx_${tablePrefix}region_groups_group_id" ON "${tablePrefix}region_groups" (
|
||||
group_id
|
||||
);
|
||||
|
||||
|
||||
CREATE INDEX "idx_${tablePrefix}region_flag_region_world_id" ON "${tablePrefix}region_flag" (
|
||||
region_id,
|
||||
world_id,
|
||||
flag
|
||||
);
|
||||
|
@ -1,29 +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;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.FlatRegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
|
||||
public class FlatRegionOverlapTest extends RegionOverlapTest {
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new FlatRegionManager(null);
|
||||
}
|
||||
}
|
@ -19,11 +19,15 @@
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.PRTreeRegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.index.HashMapIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
|
||||
public class PRTreeRegionPriorityTest extends RegionPriorityTest {
|
||||
public class HashMapIndexPriorityTest extends RegionPriorityTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new PRTreeRegionManager(null);
|
||||
return new RegionManager(new MemoryRegionStore(), new HashMapIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
@ -19,11 +19,15 @@
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.PRTreeRegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.index.HashMapIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
|
||||
public class PRTreeRegionManagerTest extends RegionOverlapTest {
|
||||
public class HashMapIndexRegionOverlapTest extends RegionOverlapTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new PRTreeRegionManager(null);
|
||||
return new RegionManager(new MemoryRegionStore(), new HashMapIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
@ -19,12 +19,15 @@
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.PRTreeRegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.index.HashMapIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
|
||||
public class HashMapIndexTest extends RegionOverlapTest {
|
||||
|
||||
public class PRTreeRegionEntryExitTest extends RegionEntryExitTest {
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new PRTreeRegionManager(null);
|
||||
return new RegionManager(new MemoryRegionStore(), new HashMapIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
@ -79,7 +79,7 @@ public ApplicableRegionSet getApplicableSet() {
|
||||
|
||||
private String getNextId() {
|
||||
id++;
|
||||
return "#REGION_" + id;
|
||||
return "REGION_" + id;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,11 +19,15 @@
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.FlatRegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
|
||||
public class FlatRegionPriorityTest extends RegionPriorityTest {
|
||||
public class PriorityRTreeIndexTest extends RegionOverlapTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new FlatRegionManager(null);
|
||||
return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
|
||||
public class PriorityRTreeRegionEntryExitTest extends RegionEntryExitTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
@ -19,11 +19,15 @@
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.PRTreeRegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
|
||||
public class PRTreeRegionOverlapTest extends RegionOverlapTest {
|
||||
public class PriorityRTreeRegionOverlapTest extends RegionOverlapTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new PRTreeRegionManager(null);
|
||||
return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
|
||||
public class PriorityRTreeRegionPriorityTest extends RegionPriorityTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
@ -37,6 +37,7 @@
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public abstract class RegionEntryExitTest {
|
||||
|
||||
static String ENTRY_ID = "entry_rg";
|
||||
static String EXIT_ID = "exit_rg";
|
||||
static String BUILDER_GROUP = "builder";
|
||||
|
Loading…
Reference in New Issue
Block a user