mirror of
https://github.com/EngineHub/WorldGuard.git
synced 2024-12-18 15:17:36 +01:00
Added MySQL region storage method.
Also added ProtectionDatabaseException, and made use of it across the project Closes #180
This commit is contained in:
parent
2351ade6fe
commit
f45464b0d9
184
region_storage.sql
Executable file
184
region_storage.sql
Executable file
@ -0,0 +1,184 @@
|
||||
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
|
||||
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
|
||||
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';
|
||||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `user`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `user` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
|
||||
`name` VARCHAR(64) NOT NULL UNIQUE,
|
||||
PRIMARY KEY (`id`))
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `group`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `group` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
|
||||
`name` VARCHAR(64) NOT NULL UNIQUE,
|
||||
PRIMARY KEY (`id`))
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `world`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `world` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
|
||||
`name` VARCHAR(128) NOT NULL UNIQUE,
|
||||
PRIMARY KEY (`id`))
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `region`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `region` (
|
||||
`id` VARCHAR(128) NOT NULL ,
|
||||
`world_id` INT UNSIGNED NOT NULL ,
|
||||
`type` ENUM('cuboid','poly2d','global') NOT NULL ,
|
||||
`priority` SMALLINT NOT NULL DEFAULT 0 ,
|
||||
`parent` VARCHAR(128) NULL ,
|
||||
PRIMARY KEY (`id`, `world_id`) ,
|
||||
INDEX `parent` (`parent` ASC) ,
|
||||
INDEX `fk_region_world` (`world_id` ASC) ,
|
||||
CONSTRAINT `parent`
|
||||
FOREIGN KEY (`parent` )
|
||||
REFERENCES `region` (`id` )
|
||||
ON DELETE SET NULL
|
||||
ON UPDATE CASCADE,
|
||||
CONSTRAINT `fk_region_world1`
|
||||
FOREIGN KEY (`world_id` )
|
||||
REFERENCES `world` (`id` )
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE)
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `region_flag`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `region_flag` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
|
||||
`region_id` VARCHAR(128) NOT NULL ,
|
||||
`flag` VARCHAR(64) NOT NULL ,
|
||||
`value` VARCHAR(256) NOT NULL ,
|
||||
INDEX `fk_flags_region` (`region_id` ASC) ,
|
||||
PRIMARY KEY (`id`) ,
|
||||
CONSTRAINT `fk_flags_region1`
|
||||
FOREIGN KEY (`region_id` )
|
||||
REFERENCES `region` (`id` )
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE)
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `region_cuboid`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `region_cuboid` (
|
||||
`region_id` VARCHAR(128) NOT NULL ,
|
||||
`min_z` BIGINT NOT NULL ,
|
||||
`min_y` BIGINT NOT NULL ,
|
||||
`min_x` BIGINT NOT NULL ,
|
||||
`max_z` BIGINT NOT NULL ,
|
||||
`max_y` BIGINT NOT NULL ,
|
||||
`max_x` BIGINT NOT NULL ,
|
||||
PRIMARY KEY (`region_id`) ,
|
||||
INDEX `fk_region_cuboid_region` (`region_id` ASC) ,
|
||||
CONSTRAINT `fk_region_cuboid_region`
|
||||
FOREIGN KEY (`region_id` )
|
||||
REFERENCES `region` (`id` )
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE)
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `region_poly2d`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `region_poly2d` (
|
||||
`region_id` VARCHAR(128) NOT NULL ,
|
||||
`max_y` INT NOT NULL ,
|
||||
`min_y` INT NOT NULL ,
|
||||
PRIMARY KEY (`region_id`) ,
|
||||
INDEX `fk_region_poly2d_region` (`region_id` ASC) ,
|
||||
CONSTRAINT `fk_region_poly2d_region`
|
||||
FOREIGN KEY (`region_id` )
|
||||
REFERENCES `region` (`id` )
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE)
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `region_players`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `region_players` (
|
||||
`region_id` VARCHAR(128) NOT NULL ,
|
||||
`user_id` INT UNSIGNED NOT NULL ,
|
||||
`owner` TINYINT(1) NOT NULL ,
|
||||
PRIMARY KEY (`region_id`, `user_id`) ,
|
||||
INDEX `fk_region_players_region` (`region_id` ASC) ,
|
||||
INDEX `fk_region_players_user` (`user_id` ASC) ,
|
||||
CONSTRAINT `fk_region_players_region`
|
||||
FOREIGN KEY (`region_id` )
|
||||
REFERENCES `region` (`id` )
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
CONSTRAINT `fk_region_players_user`
|
||||
FOREIGN KEY (`user_id` )
|
||||
REFERENCES `user` (`id` )
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE)
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `region_groups`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `region_groups` (
|
||||
`region_id` VARCHAR(128) NOT NULL ,
|
||||
`group_id` INT UNSIGNED NOT NULL ,
|
||||
`owner` TINYINT(1) NOT NULL ,
|
||||
PRIMARY KEY (`region_id`, `group_id`) ,
|
||||
INDEX `fk_region_groups_region` (`region_id` ASC) ,
|
||||
INDEX `fk_region_groups_group` (`group_id` ASC) ,
|
||||
CONSTRAINT `fk_region_groups_region`
|
||||
FOREIGN KEY (`region_id` )
|
||||
REFERENCES `region` (`id` )
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
CONSTRAINT `fk_region_groups_group`
|
||||
FOREIGN KEY (`group_id` )
|
||||
REFERENCES `group` (`id` )
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE)
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `region_poly2d_point`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `region_poly2d_point` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ,
|
||||
`region_id` VARCHAR(128) NOT NULL ,
|
||||
`z` BIGINT NOT NULL ,
|
||||
`x` BIGINT NOT NULL ,
|
||||
PRIMARY KEY (`id`) ,
|
||||
INDEX `fk_region_poly2d_point_region_poly2d` (`region_id` ASC) ,
|
||||
CONSTRAINT `fk_region_poly2d_point_region_poly2d`
|
||||
FOREIGN KEY (`region_id` )
|
||||
REFERENCES `region_poly2d` (`region_id` )
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE)
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
|
||||
SET SQL_MODE=@OLD_SQL_MODE;
|
||||
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
|
||||
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
|
@ -98,6 +98,14 @@ public class ConfigurationManager {
|
||||
public boolean activityHaltToggle = false;
|
||||
public boolean autoGodMode;
|
||||
public boolean usePlayerMove;
|
||||
|
||||
/**
|
||||
* Region Storage Configuration method, and config values
|
||||
*/
|
||||
public boolean useSqlDatabase = false;
|
||||
public String sqlDsn;
|
||||
public String sqlUsername;
|
||||
public String sqlPassword;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
@ -134,6 +142,13 @@ public void load() {
|
||||
usePlayerMove = config.getBoolean(
|
||||
"use-player-move-event", true);
|
||||
|
||||
useSqlDatabase = config.getBoolean(
|
||||
"regions.sql.use", false);
|
||||
|
||||
sqlDsn = config.getString("regions.sql.dsn", "jdbc:mysql://localhost/worldguard");
|
||||
sqlUsername = config.getString("regions.sql.username", "worldguard");
|
||||
sqlPassword = config.getString("regions.sql.password", "worldguard");
|
||||
|
||||
// Load configurations for each world
|
||||
for (World world : plugin.getServer().getWorlds()) {
|
||||
get(world);
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
import org.bukkit.World;
|
||||
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.databases.CSVDatabase;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
|
||||
@ -105,8 +106,7 @@ public static void migrateRegions(WorldGuardPlugin plugin) {
|
||||
oldDatabase.renameTo(new File(plugin.getDataFolder(), "regions.txt.old"));
|
||||
|
||||
logger.info("WorldGuard: Regions database converted!");
|
||||
} catch (FileNotFoundException e) {
|
||||
} catch (IOException e) {
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
logger.warning("WorldGuard: Failed to load regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
|
104
src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java
Executable file → Normal file
104
src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java
Executable file → Normal file
@ -19,8 +19,9 @@
|
||||
|
||||
package com.sk89q.worldguard.bukkit.commands;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
@ -53,11 +54,18 @@
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.databases.migrators.AbstractDatabaseMigrator;
|
||||
import com.sk89q.worldguard.protection.databases.migrators.MigrationException;
|
||||
import com.sk89q.worldguard.protection.databases.migrators.MigratorKey;
|
||||
import com.sk89q.worldguard.util.RegionUtil;
|
||||
|
||||
public class RegionCommands {
|
||||
private final WorldGuardPlugin plugin;
|
||||
|
||||
private MigratorKey migrateDBRequest;
|
||||
private Date migrateDBRequestDate;
|
||||
|
||||
public RegionCommands(WorldGuardPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@ -118,8 +126,8 @@ public void define(CommandContext args, CommandSender sender) throws CommandExce
|
||||
try {
|
||||
mgr.save();
|
||||
sender.sendMessage(ChatColor.YELLOW + "Region saved as " + id + ".");
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -192,8 +200,8 @@ public void redefine(CommandContext args, CommandSender sender) throws CommandEx
|
||||
|
||||
try {
|
||||
mgr.save();
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -323,8 +331,8 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep
|
||||
try {
|
||||
mgr.save();
|
||||
sender.sendMessage(ChatColor.YELLOW + "Region saved as " + id + ".");
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -681,8 +689,8 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept
|
||||
|
||||
try {
|
||||
mgr.save();
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -730,8 +738,8 @@ public void setPriority(CommandContext args, CommandSender sender) throws Comman
|
||||
|
||||
try {
|
||||
mgr.save();
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -801,8 +809,8 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE
|
||||
|
||||
try {
|
||||
mgr.save();
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -839,8 +847,8 @@ public void remove(CommandContext args, CommandSender sender) throws CommandExce
|
||||
|
||||
try {
|
||||
mgr.save();
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -862,8 +870,8 @@ public void load(CommandContext args, CommandSender sender) throws CommandExcept
|
||||
mgr.load();
|
||||
sender.sendMessage(ChatColor.YELLOW
|
||||
+ "Regions for '" + world.getName() + "' load.");
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to read regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to read regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
} else {
|
||||
@ -872,8 +880,8 @@ public void load(CommandContext args, CommandSender sender) throws CommandExcept
|
||||
|
||||
try {
|
||||
mgr.load();
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to read regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to read regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -900,8 +908,8 @@ public void save(CommandContext args, CommandSender sender) throws CommandExcept
|
||||
mgr.save();
|
||||
sender.sendMessage(ChatColor.YELLOW
|
||||
+ "Regions for '" + world.getName() + "' saved.");
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
} else {
|
||||
@ -910,8 +918,8 @@ public void save(CommandContext args, CommandSender sender) throws CommandExcept
|
||||
|
||||
try {
|
||||
mgr.save();
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -920,4 +928,54 @@ public void save(CommandContext args, CommandSender sender) throws CommandExcept
|
||||
+ "Region databases saved.");
|
||||
}
|
||||
}
|
||||
|
||||
@Command(aliases = {"migratedb"}, usage = "<from> <to>",
|
||||
desc = "Migrate from one Protection Database to another.", min = 1)
|
||||
@CommandPermissions({"worldguard.region.migratedb"})
|
||||
public void migratedb(CommandContext args, CommandSender sender) throws CommandException {
|
||||
String from = args.getString(0).toLowerCase().trim();
|
||||
String to = args.getString(1).toLowerCase().trim();
|
||||
|
||||
if (from.equals(to)) {
|
||||
throw new CommandException("Will not migrate with common source and target.");
|
||||
}
|
||||
|
||||
Map<MigratorKey, Class<? extends AbstractDatabaseMigrator>> migrators = AbstractDatabaseMigrator.getMigrators();
|
||||
MigratorKey key = new MigratorKey(from,to);
|
||||
|
||||
if (!migrators.containsKey(key)) {
|
||||
throw new CommandException("No migrator found for that combination and direction.");
|
||||
}
|
||||
|
||||
long lastRequest = 10000000;
|
||||
if (this.migrateDBRequestDate != null) {
|
||||
lastRequest = new Date().getTime() - this.migrateDBRequestDate.getTime();
|
||||
}
|
||||
if (this.migrateDBRequest == null || lastRequest > 60000) {
|
||||
this.migrateDBRequest = key;
|
||||
this.migrateDBRequestDate = new Date();
|
||||
|
||||
throw new CommandException("This command is potentially dangerous.\n" +
|
||||
"Please ensure you have made a backup of your data, and then re-enter the command exactly to procede.");
|
||||
}
|
||||
|
||||
Class<? extends AbstractDatabaseMigrator> cls = migrators.get(key);
|
||||
|
||||
try {
|
||||
AbstractDatabaseMigrator migrator = cls.getConstructor(WorldGuardPlugin.class).newInstance(plugin);
|
||||
|
||||
migrator.migrate();
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (SecurityException e) {
|
||||
} catch (InstantiationException e) {
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (InvocationTargetException e) {
|
||||
} catch (NoSuchMethodException e) {
|
||||
} catch (MigrationException e) {
|
||||
throw new CommandException("Error migrating database: " + e.getMessage());
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.YELLOW + "Regions have been migrated successfuly.\n" +
|
||||
"If you wish to use the destination format as your new backend, please update your config and reload WorldGuard.");
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
package com.sk89q.worldguard.bukkit.commands;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -35,6 +33,7 @@
|
||||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.util.RegionUtil;
|
||||
|
||||
// @TODO: A lot of code duplication here! Need to fix.
|
||||
@ -78,8 +77,8 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE
|
||||
|
||||
try {
|
||||
mgr.save();
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -129,8 +128,8 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx
|
||||
|
||||
try {
|
||||
mgr.save();
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -167,8 +166,8 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma
|
||||
|
||||
try {
|
||||
mgr.save();
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -206,8 +205,8 @@ public void removeOwner(CommandContext args,
|
||||
|
||||
try {
|
||||
mgr.save();
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("Failed to write regions file: "
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new CommandException("Failed to write regions: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,6 @@
|
||||
import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -36,7 +34,10 @@
|
||||
import com.sk89q.worldguard.bukkit.ConfigurationManager;
|
||||
import com.sk89q.worldguard.bukkit.WorldConfiguration;
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
|
||||
import com.sk89q.worldguard.protection.databases.YAMLDatabase;
|
||||
import com.sk89q.worldguard.protection.databases.MySQLDatabase;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||
import com.sk89q.worldguard.protection.managers.FlatRegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
@ -134,30 +135,44 @@ public void unloadAll() {
|
||||
*/
|
||||
public RegionManager load(World world) {
|
||||
String name = world.getName();
|
||||
File file = getPath(name);
|
||||
|
||||
ProtectionDatabase database = null;
|
||||
File file = null;
|
||||
|
||||
try {
|
||||
if (!config.useSqlDatabase) {
|
||||
file = getPath(name);
|
||||
database = new YAMLDatabase(file);
|
||||
|
||||
// Store the last modification date so we can track changes
|
||||
lastModified.put(name, file.lastModified());
|
||||
} else {
|
||||
database = new MySQLDatabase(config, name);
|
||||
}
|
||||
|
||||
// Create a manager
|
||||
RegionManager manager = new FlatRegionManager(new YAMLDatabase(file));
|
||||
RegionManager manager = new FlatRegionManager(database);
|
||||
|
||||
managers.put(name, manager);
|
||||
manager.load();
|
||||
|
||||
logger.info("WorldGuard: " + manager.getRegions().size()
|
||||
+ " regions loaded for '" + name + "'");
|
||||
|
||||
// Store the last modification date so we can track changes
|
||||
lastModified.put(name, file.lastModified());
|
||||
|
||||
return manager;
|
||||
} catch (FileNotFoundException e) { // this should no longer happen hopefully
|
||||
logger.info("WorldGuard: Region file for world \""
|
||||
+ name + "\" missing or inaccessible.");
|
||||
} catch (IOException e) {
|
||||
logger.info("WorldGuard: Failed to load regions from file "
|
||||
+ file.getAbsolutePath() + " : " + e.getMessage());
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
String logStr = "WorldGuard: Failed to load regions from ";
|
||||
if (config.useSqlDatabase) {
|
||||
logStr += "SQL Database <" + config.sqlDsn + "> ";
|
||||
} else {
|
||||
logStr += "file \"" + file + "\" ";
|
||||
}
|
||||
|
||||
logger.info(logStr + " : " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
logger.info("WorldGuard: Error loading regions for world \""
|
||||
+ name + "\":" + e.getMessage());
|
||||
+ name + "\": " + e.toString() + "\n\t" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// @TODO: THIS CREATES PROBLEMS!!one!!1!!eleven!!1!!!
|
||||
@ -179,6 +194,8 @@ public void preload() {
|
||||
* have changed.
|
||||
*/
|
||||
public void reloadChanged() {
|
||||
if (config.useSqlDatabase) return;
|
||||
|
||||
for (String name : managers.keySet()) {
|
||||
File file = getPath(name);
|
||||
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
package com.sk89q.worldguard.protection.databases;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
|
||||
public abstract class AbstractProtectionDatabase implements ProtectionDatabase {
|
||||
@ -28,9 +26,9 @@ public abstract class AbstractProtectionDatabase implements ProtectionDatabase {
|
||||
/**
|
||||
* Load the list of regions into a region manager.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws ProtectionDatabaseException
|
||||
*/
|
||||
public void load(RegionManager manager) throws IOException {
|
||||
public void load(RegionManager manager) throws ProtectionDatabaseException {
|
||||
load();
|
||||
manager.setRegions(getRegions());
|
||||
}
|
||||
@ -38,9 +36,9 @@ public void load(RegionManager manager) throws IOException {
|
||||
/**
|
||||
* Save the list of regions from a region manager.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws ProtectionDatabaseException
|
||||
*/
|
||||
public void save(RegionManager manager) throws IOException {
|
||||
public void save(RegionManager manager) throws ProtectionDatabaseException {
|
||||
setRegions(manager.getRegions());
|
||||
save();
|
||||
}
|
||||
|
@ -72,22 +72,23 @@ public CSVDatabase(File file) {
|
||||
/**
|
||||
* Saves the database.
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
public void save() throws ProtectionDatabaseException {
|
||||
throw new UnsupportedOperationException("CSV format is no longer implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the database from file.
|
||||
*/
|
||||
public void load() throws IOException {
|
||||
public void load() throws ProtectionDatabaseException {
|
||||
Map<String,ProtectedRegion> regions =
|
||||
new HashMap<String,ProtectedRegion>();
|
||||
Map<ProtectedRegion,String> parentSets =
|
||||
new LinkedHashMap<ProtectedRegion, String>();
|
||||
|
||||
CSVReader reader = new CSVReader(new FileReader(file));
|
||||
|
||||
CSVReader reader = null;
|
||||
try {
|
||||
reader = new CSVReader(new FileReader(file));
|
||||
|
||||
String[] line;
|
||||
|
||||
while ((line = reader.readNext()) != null) {
|
||||
@ -162,6 +163,8 @@ public void load() throws IOException {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ProtectionDatabaseException(e);
|
||||
} finally {
|
||||
try {
|
||||
reader.close();
|
||||
|
966
src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java
Executable file
966
src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java
Executable file
@ -0,0 +1,966 @@
|
||||
// $Id$
|
||||
/*
|
||||
* MySQL WordGuard Region Database
|
||||
* Copyright (C) 2011 Nicholas Steicke <http://narthollis.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.PreparedStatement;
|
||||
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.bukkit.ConfigurationManager;
|
||||
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;
|
||||
|
||||
public class MySQLDatabase extends AbstractProtectionDatabase {
|
||||
|
||||
private static Logger logger = Logger.getLogger("Minecraft.WorldGuard");
|
||||
|
||||
private Map<String, ProtectedRegion> regions;
|
||||
|
||||
private Map<String, ProtectedRegion> cuboidRegions;
|
||||
private Map<String, ProtectedRegion> poly2dRegions;
|
||||
private Map<String, ProtectedRegion> globalRegions;
|
||||
private Map<ProtectedRegion, String> parentSets;
|
||||
|
||||
private ConfigurationManager config;
|
||||
|
||||
private Connection conn;
|
||||
private String world;
|
||||
private int worldDbId = -1; // The database will never have an id of -1;
|
||||
|
||||
public MySQLDatabase(ConfigurationManager config, String world) throws ProtectionDatabaseException {
|
||||
this.config = config;
|
||||
this.world = world;
|
||||
|
||||
try {
|
||||
connect();
|
||||
|
||||
PreparedStatement worldStmt = conn.prepareStatement(
|
||||
"SELECT `id` FROM " +
|
||||
"`world` " +
|
||||
"WHERE `name` = ? LIMIT 0,1"
|
||||
);
|
||||
|
||||
worldStmt.setString(1, this.world);
|
||||
ResultSet worldResult = worldStmt.executeQuery();
|
||||
|
||||
if (worldResult.first()) {
|
||||
this.worldDbId = worldResult.getInt("id");
|
||||
} else {
|
||||
PreparedStatement insertWorldStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO " +
|
||||
"`world` " +
|
||||
"(`id`, `name`) VALUES (null, ?)",
|
||||
Statement.RETURN_GENERATED_KEYS
|
||||
);
|
||||
|
||||
insertWorldStatement.setString(1, world);
|
||||
insertWorldStatement.execute();
|
||||
ResultSet generatedKeys = insertWorldStatement.getGeneratedKeys();
|
||||
if (generatedKeys.first()) {
|
||||
this.worldDbId = generatedKeys.getInt(1);
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.SEVERE, ex.getMessage(), ex);
|
||||
// We havn't connected to the databases, or there was an error
|
||||
// initialising the world record, so there is no point continuing
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.worldDbId <= 0) {
|
||||
logger.log(Level.SEVERE, "Could not find or create the world");
|
||||
// There was an error initialising the world record, so there is
|
||||
// no point continuing
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void connect() throws SQLException {
|
||||
if (conn == null || conn.isClosed()) {
|
||||
conn = DriverManager.getConnection(config.sqlDsn, config.sqlUsername, config.sqlPassword);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFlags(ProtectedRegion region) {
|
||||
// @TODO: Iterate _ONCE_
|
||||
try {
|
||||
PreparedStatement flagsStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`region_flag`.`flag`, " +
|
||||
"`region_flag`.`value` " +
|
||||
"FROM `region_flag` " +
|
||||
"WHERE `region_flag`.`region_id` = ? "
|
||||
);
|
||||
|
||||
flagsStatement.setString(1, region.getId().toLowerCase());
|
||||
ResultSet flagsResultSet = flagsStatement.executeQuery();
|
||||
|
||||
Map<String,Object> regionFlags = new HashMap<String,Object>();
|
||||
while (flagsResultSet.next()) {
|
||||
regionFlags.put(
|
||||
flagsResultSet.getString("flag"),
|
||||
flagsResultSet.getObject("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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private void loadOwnersAndMembers(ProtectedRegion region) {
|
||||
DefaultDomain owners = new DefaultDomain();
|
||||
DefaultDomain members = new DefaultDomain();
|
||||
|
||||
try {
|
||||
PreparedStatement usersStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`user`.`name`, " +
|
||||
"`region_players`.`owner` " +
|
||||
"FROM `region_players` " +
|
||||
"LEFT JOIN `user` ON ( " +
|
||||
"`region_players`.`user_id` = " +
|
||||
"`user`.`id`) " +
|
||||
"WHERE `region_players`.`region_id` = ? "
|
||||
);
|
||||
|
||||
usersStatement.setString(1, region.getId().toLowerCase());
|
||||
ResultSet userSet = usersStatement.executeQuery();
|
||||
while(userSet.next()) {
|
||||
if (userSet.getBoolean("owner")) {
|
||||
owners.addPlayer(userSet.getString("name"));
|
||||
} else {
|
||||
members.addPlayer(userSet.getString("name"));
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.warning("Unable to load users for region " + region.getId().toLowerCase() + ": " + ex.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
PreparedStatement groupsStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`group`.`name`, " +
|
||||
"`region_groups`.`owner` " +
|
||||
"FROM `region_groups` " +
|
||||
"LEFT JOIN `group` ON ( " +
|
||||
"`region_groups`.`group_id` = " +
|
||||
"`group`.`id`) " +
|
||||
"WHERE `region_groups`.`region_id` = ? "
|
||||
);
|
||||
|
||||
groupsStatement.setString(1, region.getId().toLowerCase());
|
||||
ResultSet 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());
|
||||
}
|
||||
|
||||
region.setOwners(owners);
|
||||
region.setMembers(members);
|
||||
}
|
||||
|
||||
private void loadGlobal() {
|
||||
Map<String,ProtectedRegion> regions =
|
||||
new HashMap<String,ProtectedRegion>();
|
||||
|
||||
try {
|
||||
PreparedStatement globalRegionStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`region`.`id`, " +
|
||||
"`region`.`priority`, " +
|
||||
"`parent`.`id` AS `parent` " +
|
||||
"FROM `region` " +
|
||||
"LEFT JOIN `region` AS `parent` " +
|
||||
"ON (`region`.`parent` = `parent`.`id`) " +
|
||||
"WHERE `region`.`type` = 'global' " +
|
||||
"AND `region`.`world_id` = ? "
|
||||
);
|
||||
|
||||
globalRegionStatement.setInt(1, this.worldDbId);
|
||||
ResultSet 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());
|
||||
}
|
||||
}
|
||||
|
||||
globalRegions = regions;
|
||||
}
|
||||
|
||||
private void loadCuboid() {
|
||||
Map<String,ProtectedRegion> regions =
|
||||
new HashMap<String,ProtectedRegion>();
|
||||
|
||||
try {
|
||||
PreparedStatement 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 `region_cuboid` " +
|
||||
"LEFT JOIN `region` " +
|
||||
"ON (`region_cuboid`.`region_id` = `region`.`id`) " +
|
||||
"LEFT JOIN `region` AS `parent` " +
|
||||
"ON (`region`.`parent` = `parent`.`id`) " +
|
||||
"WHERE `region`.`world_id` = ? "
|
||||
);
|
||||
|
||||
cuboidRegionStatement.setInt(1, this.worldDbId);
|
||||
ResultSet 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());
|
||||
}
|
||||
}
|
||||
|
||||
cuboidRegions = regions;
|
||||
}
|
||||
|
||||
private void loadPoly2d() {
|
||||
Map<String,ProtectedRegion> regions =
|
||||
new HashMap<String,ProtectedRegion>();
|
||||
|
||||
try {
|
||||
PreparedStatement poly2dRegionStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`region_poly2d`.`min_y`, " +
|
||||
"`region_poly2d`.`max_y`, " +
|
||||
"`region`.`id`, " +
|
||||
"`region`.`priority`, " +
|
||||
"`parent`.`id` AS `parent` " +
|
||||
"FROM `region_poly2d` " +
|
||||
"LEFT JOIN `region` " +
|
||||
"ON (`region_poly2d`.`region_id` = `region`.`id`) " +
|
||||
"LEFT JOIN `region` AS `parent` " +
|
||||
"ON (`region`.`parent` = `parent`.`id`) " +
|
||||
"WHERE `region`.`world_id` = ? "
|
||||
);
|
||||
|
||||
poly2dRegionStatement.setInt(1, this.worldDbId);
|
||||
ResultSet poly2dResultSet = poly2dRegionStatement.executeQuery();
|
||||
|
||||
PreparedStatement poly2dVectorStatement = this.conn.prepareStatement(
|
||||
"SELECT " +
|
||||
"`region_poly2d_point`.`x`, " +
|
||||
"`region_poly2d_point`.`z` " +
|
||||
"FROM `region_poly2d_point` " +
|
||||
"WHERE `region_poly2d_point`.`region_id` = ? "
|
||||
);
|
||||
|
||||
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")
|
||||
));
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
poly2dRegions = regions;
|
||||
}
|
||||
|
||||
public void load() throws ProtectionDatabaseException {
|
||||
try {
|
||||
connect();
|
||||
} catch (SQLException ex) {
|
||||
throw new ProtectionDatabaseException(ex);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
this.regions = regions;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns the database id for the user
|
||||
* If it doesn't exits it adds the user and returns the id.
|
||||
*/
|
||||
private Map<String,Integer> getUserIds(String... usernames) {
|
||||
Map<String,Integer> users = new HashMap<String,Integer>();
|
||||
|
||||
if (usernames.length < 1) return users;
|
||||
|
||||
try {
|
||||
PreparedStatement findUsersStatement = this.conn.prepareStatement(
|
||||
String.format(
|
||||
"SELECT " +
|
||||
"`user`.`id`, " +
|
||||
"`user`.`name` " +
|
||||
"FROM `user` " +
|
||||
"WHERE `name` IN (%s)",
|
||||
RegionDBUtil.preparePlaceHolders(usernames.length)
|
||||
)
|
||||
);
|
||||
|
||||
RegionDBUtil.setValues(findUsersStatement, usernames);
|
||||
|
||||
ResultSet findUsersResults = findUsersStatement.executeQuery();
|
||||
|
||||
while(findUsersResults.next()) {
|
||||
users.put(findUsersResults.getString("name"), findUsersResults.getInt("id"));
|
||||
}
|
||||
|
||||
PreparedStatement insertUserStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO " +
|
||||
"`user` ( " +
|
||||
"`id`, " +
|
||||
"`name`" +
|
||||
") VALUES (null, ?)",
|
||||
Statement.RETURN_GENERATED_KEYS
|
||||
);
|
||||
|
||||
for(int i=0, len=usernames.length; i<len; i++) {
|
||||
if (!users.containsKey(usernames[i])) {
|
||||
insertUserStatement.setString(1, usernames[i]);
|
||||
insertUserStatement.execute();
|
||||
ResultSet generatedKeys = insertUserStatement.getGeneratedKeys();
|
||||
if (generatedKeys.first()) {
|
||||
users.put(usernames[i], generatedKeys.getInt(1));
|
||||
} else {
|
||||
logger.warning("Could not get the database id for user " + usernames[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
logger.warning("Could not get the database id for the users " + usernames.toString() + "\n\t" + ex.getMessage());
|
||||
Throwable t = ex.getCause();
|
||||
while (t != null) {
|
||||
logger.warning(t.getMessage());
|
||||
t = t.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
try {
|
||||
PreparedStatement findGroupsStatement = this.conn.prepareStatement(
|
||||
String.format(
|
||||
"SELECT " +
|
||||
"`group`.`id`, " +
|
||||
"`group`.`name` " +
|
||||
"FROM `group` " +
|
||||
"WHERE `name` IN (%s)",
|
||||
RegionDBUtil.preparePlaceHolders(groupnames.length)
|
||||
)
|
||||
);
|
||||
|
||||
RegionDBUtil.setValues(findGroupsStatement, groupnames);
|
||||
|
||||
ResultSet findGroupsResults = findGroupsStatement.executeQuery();
|
||||
|
||||
while(findGroupsResults.next()) {
|
||||
groups.put(findGroupsResults.getString("name"), findGroupsResults.getInt("id"));
|
||||
}
|
||||
|
||||
PreparedStatement insertGroupStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO " +
|
||||
"`group` ( " +
|
||||
"`id`, " +
|
||||
"`name`" +
|
||||
") VALUES (null, ?)",
|
||||
Statement.RETURN_GENERATED_KEYS
|
||||
);
|
||||
|
||||
for(int i=0, len=groupnames.length; i<len; i++) {
|
||||
if (!groups.containsKey(groupnames[i])) {
|
||||
insertGroupStatement.setString(1, groupnames[i]);
|
||||
insertGroupStatement.execute();
|
||||
ResultSet generatedKeys = insertGroupStatement.getGeneratedKeys();
|
||||
if (generatedKeys.first()) {
|
||||
groups.put(groupnames[i], generatedKeys.getInt(1));
|
||||
} else {
|
||||
logger.warning("Could not get the database id for user " + groupnames[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.warning("Could not get the database id for the groups " + groupnames.toString() + ex.getMessage());
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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()
|
||||
*/
|
||||
public void save() throws ProtectionDatabaseException {
|
||||
try {
|
||||
connect();
|
||||
} catch (SQLException ex) {
|
||||
throw new ProtectionDatabaseException(ex);
|
||||
}
|
||||
|
||||
List<String> regionsInDatabase = new ArrayList<String>();
|
||||
|
||||
try {
|
||||
PreparedStatement getAllRegionsStatement = this.conn.prepareStatement(
|
||||
"SELECT `region`.`id` FROM " +
|
||||
"`region` " +
|
||||
"WHERE `world_id` = ? "
|
||||
);
|
||||
|
||||
getAllRegionsStatement.setInt(1, this.worldDbId);
|
||||
ResultSet 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());
|
||||
}
|
||||
|
||||
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()) {
|
||||
try {
|
||||
if (entry.getValue().getParent() == null) continue;
|
||||
|
||||
PreparedStatement setParentStatement = this.conn.prepareStatement(
|
||||
"UPDATE `region` SET " +
|
||||
"`parent` = ? " +
|
||||
"WHERE `id` = ? "
|
||||
);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
for (String name : regionsInDatabase) {
|
||||
try {
|
||||
PreparedStatement removeRegion = this.conn.prepareStatement(
|
||||
"DELETE FROM `region` WHERE `id` = ? "
|
||||
);
|
||||
|
||||
removeRegion.setString(1, name);
|
||||
removeRegion.execute();
|
||||
} catch (SQLException ex) {
|
||||
logger.warning("Could not remove region from database " + name + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateFlags(ProtectedRegion region) throws SQLException {
|
||||
PreparedStatement clearCurrentFlagStatement = this.conn.prepareStatement(
|
||||
"DELETE FROM `region_flag` " +
|
||||
"WHERE `region_id` = ? "
|
||||
);
|
||||
|
||||
clearCurrentFlagStatement.setString(1, region.getId().toLowerCase());
|
||||
clearCurrentFlagStatement.execute();
|
||||
|
||||
for (Map.Entry<Flag<?>, Object> entry : region.getFlags().entrySet()) {
|
||||
if (entry.getValue() == null) continue;
|
||||
|
||||
Object flag = marshalFlag(entry.getKey(), entry.getValue());
|
||||
|
||||
PreparedStatement insertFlagStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO `region_flag` ( " +
|
||||
"`id`, " +
|
||||
"`region_id`, " +
|
||||
"`flag`, " +
|
||||
"`value` " +
|
||||
") VALUES (null, ?, ?, ?)"
|
||||
);
|
||||
|
||||
insertFlagStatement.setString(1, region.getId().toLowerCase());
|
||||
insertFlagStatement.setString(2, entry.getKey().getName());
|
||||
insertFlagStatement.setObject(3, flag);
|
||||
|
||||
insertFlagStatement.execute();
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePlayerAndGroups(ProtectedRegion region, Boolean owners) throws SQLException {
|
||||
DefaultDomain domain;
|
||||
|
||||
if (owners) {
|
||||
domain = region.getOwners();
|
||||
} else {
|
||||
domain = region.getMembers();
|
||||
}
|
||||
|
||||
PreparedStatement deleteUsersForRegion = this.conn.prepareStatement(
|
||||
"DELETE FROM `region_players` " +
|
||||
"WHERE `region_id` = ? " +
|
||||
"AND `owner` = ?"
|
||||
);
|
||||
|
||||
deleteUsersForRegion.setString(1, region.getId().toLowerCase());
|
||||
deleteUsersForRegion.setBoolean(2, owners);
|
||||
deleteUsersForRegion.execute();
|
||||
|
||||
PreparedStatement insertUsersForRegion = this.conn.prepareStatement(
|
||||
"INSERT INTO `region_players` " +
|
||||
"(`region_id`, `user_id`, `owner`) VALUES (?, ?, ?)"
|
||||
);
|
||||
|
||||
Map<String,Integer> players = getUserIds(domain.getPlayers().toArray(new String[0]));
|
||||
|
||||
for (Integer player : players.values()) {
|
||||
insertUsersForRegion.setString(1, region.getId().toLowerCase());
|
||||
insertUsersForRegion.setInt(2, player);
|
||||
insertUsersForRegion.setBoolean(3, owners);
|
||||
|
||||
insertUsersForRegion.execute();
|
||||
}
|
||||
|
||||
PreparedStatement deleteGroupsForRegion = this.conn.prepareStatement(
|
||||
"DELETE FROM `region_groups` " +
|
||||
"WHERE `region_id` = ? " +
|
||||
"AND `owner` = ?"
|
||||
);
|
||||
|
||||
deleteGroupsForRegion.setString(1, region.getId().toLowerCase());
|
||||
deleteGroupsForRegion.setBoolean(2, owners);
|
||||
deleteGroupsForRegion.execute();
|
||||
|
||||
PreparedStatement insertGroupsForRegion = this.conn.prepareStatement(
|
||||
"INSERT INTO `region_groups` " +
|
||||
"(`region_id`, `group_id`, `owner`) VALUES (?, ?, ?)"
|
||||
);
|
||||
|
||||
Map<String,Integer> groups = getGroupIds(domain.getGroups().toArray(new String[0]));
|
||||
|
||||
for (Integer group : groups.values()) {
|
||||
insertGroupsForRegion.setString(1, region.getId().toLowerCase());
|
||||
insertGroupsForRegion.setInt(2, group);
|
||||
insertGroupsForRegion.setBoolean(3, owners);
|
||||
|
||||
insertGroupsForRegion.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@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 = this.conn.prepareStatement(
|
||||
"INSERT INTO `region` (" +
|
||||
"`id`, " +
|
||||
"`world_id`, " +
|
||||
"`type`, " +
|
||||
"`priority`, " +
|
||||
"`parent` " +
|
||||
") VALUES (?, " + this.worldDbId + ", ?, ?, null)"
|
||||
);
|
||||
|
||||
insertRegionStatement.setString(1, region.getId().toLowerCase());
|
||||
insertRegionStatement.setString(2, type);
|
||||
insertRegionStatement.setInt(3, region.getPriority());
|
||||
|
||||
insertRegionStatement.execute();
|
||||
|
||||
updateFlags(region);
|
||||
|
||||
updatePlayerAndGroups(region, false);
|
||||
updatePlayerAndGroups(region, true);
|
||||
}
|
||||
|
||||
private void insertRegionCuboid(ProtectedCuboidRegion region) throws SQLException {
|
||||
insertRegion(region, "cuboid");
|
||||
|
||||
PreparedStatement insertCuboidRegionStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO `region_cuboid` (" +
|
||||
"`region_id`, " +
|
||||
"`min_z`, " +
|
||||
"`min_y`, " +
|
||||
"`min_x`, " +
|
||||
"`max_z`, " +
|
||||
"`max_y`, " +
|
||||
"`max_x` " +
|
||||
") VALUES (?, ?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
private void insertRegionPoly2D(ProtectedPolygonalRegion region) throws SQLException {
|
||||
insertRegion(region, "poly2d");
|
||||
|
||||
PreparedStatement insertPoly2dRegionStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO `region_poly2d` (" +
|
||||
"`region_id`, " +
|
||||
"`max_y`, " +
|
||||
"`min_y` " +
|
||||
") VALUES (?, ?, ?)"
|
||||
);
|
||||
|
||||
insertPoly2dRegionStatement.setString(1, region.getId().toLowerCase());
|
||||
insertPoly2dRegionStatement.setInt(2, region.getMaximumPoint().getBlockY());
|
||||
insertPoly2dRegionStatement.setInt(3, region.getMinimumPoint().getBlockY());
|
||||
|
||||
insertPoly2dRegionStatement.execute();
|
||||
|
||||
updatePoly2dPoints(region);
|
||||
}
|
||||
|
||||
private void updatePoly2dPoints(ProtectedPolygonalRegion region) throws SQLException {
|
||||
PreparedStatement clearPoly2dPointsForRegionStatement = this.conn.prepareStatement(
|
||||
"DELETE FROM `region_poly2d_point` " +
|
||||
"WHERE `region_id` = ? "
|
||||
);
|
||||
|
||||
clearPoly2dPointsForRegionStatement.setString(1, region.getId().toLowerCase());
|
||||
|
||||
clearPoly2dPointsForRegionStatement.execute();
|
||||
|
||||
PreparedStatement insertPoly2dPointStatement = this.conn.prepareStatement(
|
||||
"INSERT INTO `region_poly2d_point` (" +
|
||||
"`id`, " +
|
||||
"`region_id`, " +
|
||||
"`z`, " +
|
||||
"`x` " +
|
||||
") VALUES (null, ?, ?, ?)"
|
||||
);
|
||||
|
||||
for (BlockVector2D point : region.getPoints()) {
|
||||
insertPoly2dPointStatement.setString(1, region.getId().toLowerCase());
|
||||
insertPoly2dPointStatement.setInt(2, point.getBlockZ());
|
||||
insertPoly2dPointStatement.setInt(3, point.getBlockX());
|
||||
|
||||
insertPoly2dPointStatement.execute();
|
||||
}
|
||||
}
|
||||
|
||||
private void insertRegionGlobal(GlobalProtectedRegion region) throws SQLException {
|
||||
insertRegion(region, "global");
|
||||
}
|
||||
|
||||
private void updateRegion(ProtectedRegion region, String type) throws SQLException {
|
||||
PreparedStatement updateRegionStatement = this.conn.prepareStatement(
|
||||
"UPDATE `region` SET " +
|
||||
"`priority` = ? WHERE `id` = ? "
|
||||
);
|
||||
|
||||
updateRegionStatement.setInt(1, region.getPriority());
|
||||
updateRegionStatement.setString(2, region.getId().toLowerCase());
|
||||
|
||||
updateRegionStatement.execute();
|
||||
|
||||
updateFlags(region);
|
||||
|
||||
updatePlayerAndGroups(region, false);
|
||||
updatePlayerAndGroups(region, true);
|
||||
}
|
||||
|
||||
private void updateRegionCuboid(ProtectedCuboidRegion region) throws SQLException {
|
||||
updateRegion(region, "cuboid");
|
||||
|
||||
PreparedStatement updateCuboidRegionStatement = this.conn.prepareStatement(
|
||||
"UPDATE `region_cuboid` SET " +
|
||||
"`min_z` = ?, " +
|
||||
"`min_y` = ?, " +
|
||||
"`min_x` = ?, " +
|
||||
"`max_z` = ?, " +
|
||||
"`max_y` = ?, " +
|
||||
"`max_x` = ? " +
|
||||
"WHERE `region_id` = ? "
|
||||
);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
private void updateRegionPoly2D(ProtectedPolygonalRegion region) throws SQLException {
|
||||
updateRegion(region, "poly2d");
|
||||
|
||||
PreparedStatement updatePoly2dRegionStatement = this.conn.prepareStatement(
|
||||
"UPDATE `region_poly2d` SET " +
|
||||
"`max_y` = ?, " +
|
||||
"`min_y` = ? " +
|
||||
"WHERE `region_id` = ? "
|
||||
);
|
||||
|
||||
updatePoly2dRegionStatement.setInt(1, region.getMaximumPoint().getBlockY());
|
||||
updatePoly2dRegionStatement.setInt(2, region.getMinimumPoint().getBlockY());
|
||||
updatePoly2dRegionStatement.setString(3, region.getId().toLowerCase());
|
||||
|
||||
updatePoly2dRegionStatement.execute();
|
||||
|
||||
updatePoly2dPoints(region);
|
||||
}
|
||||
|
||||
private void updateRegionGlobal(GlobalProtectedRegion region) throws SQLException {
|
||||
updateRegion(region, "global");
|
||||
}
|
||||
|
||||
public Map<String, ProtectedRegion> getRegions() {
|
||||
return regions;
|
||||
}
|
||||
|
||||
public void setRegions(Map<String, ProtectedRegion> regions) {
|
||||
this.regions = regions;
|
||||
}
|
||||
}
|
@ -19,7 +19,6 @@
|
||||
|
||||
package com.sk89q.worldguard.protection.databases;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
@ -35,29 +34,29 @@ public interface ProtectionDatabase {
|
||||
* Load the list of regions. The method should not modify the list returned
|
||||
* by getRegions() unless the load finishes successfully.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws ProtectionDatabaseException
|
||||
*/
|
||||
public void load() throws IOException;
|
||||
public void load() throws ProtectionDatabaseException;
|
||||
/**
|
||||
* Save the list of regions.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws ProtectionDatabaseException
|
||||
*/
|
||||
public void save() throws IOException;
|
||||
public void save() throws ProtectionDatabaseException;
|
||||
/**
|
||||
* Load the list of regions into a region manager.
|
||||
*
|
||||
* @param manager
|
||||
* @throws IOException
|
||||
* @throws ProtectionDatabaseException
|
||||
*/
|
||||
public void load(RegionManager manager) throws IOException;
|
||||
public void load(RegionManager manager) throws ProtectionDatabaseException;
|
||||
/**
|
||||
* Save the list of regions from a region manager.
|
||||
*
|
||||
* @param manager
|
||||
* @throws IOException
|
||||
* @throws ProtectionDatabaseException
|
||||
*/
|
||||
public void save(RegionManager manager) throws IOException;
|
||||
public void save(RegionManager manager) throws ProtectionDatabaseException;
|
||||
/**
|
||||
* Get a list of regions.
|
||||
*
|
||||
|
@ -0,0 +1,42 @@
|
||||
// $Id$
|
||||
/*
|
||||
* WorldGuard ProtectionDatabaseException
|
||||
* Copyright (C) 2011 Nicholas Steicke <http://narthollis.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases;
|
||||
|
||||
import java.lang.Exception;
|
||||
|
||||
public class ProtectionDatabaseException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ProtectionDatabaseException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ProtectionDatabaseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ProtectionDatabaseException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ProtectionDatabaseException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -19,6 +19,8 @@
|
||||
|
||||
package com.sk89q.worldguard.protection.databases;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -98,4 +100,34 @@ public static DefaultDomain parseDomainString(String[] split, int startIndex) {
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a comma separated list of PreparedStatement place holders
|
||||
*
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
public static String preparePlaceHolders(int length) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < length;) {
|
||||
builder.append("?");
|
||||
if (++i < length) {
|
||||
builder.append(",");
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all of the parsed values to the PreparedStatement
|
||||
*
|
||||
* @param preparedStatement
|
||||
* @param values
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static void setValues(PreparedStatement preparedStatement, String... values) throws SQLException {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
preparedStatement.setString(i + 1, values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -51,18 +52,23 @@ public class YAMLDatabase extends AbstractProtectionDatabase {
|
||||
private YAMLProcessor config;
|
||||
private Map<String, ProtectedRegion> regions;
|
||||
|
||||
public YAMLDatabase(File file) {
|
||||
public YAMLDatabase(File file) throws ProtectionDatabaseException, FileNotFoundException {
|
||||
if (!file.exists()) { // shouldn't be necessary, but check anyways
|
||||
try {
|
||||
file.createNewFile();
|
||||
} catch (IOException e) {}
|
||||
// if this is thrown, we can't do anything (caught elsewhere anyway)
|
||||
} catch (IOException e) {
|
||||
throw new FileNotFoundException(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
config = new YAMLProcessor(file, false, YAMLFormat.COMPACT);
|
||||
}
|
||||
|
||||
public void load() throws IOException {
|
||||
config.load();
|
||||
public void load() throws ProtectionDatabaseException {
|
||||
try {
|
||||
config.load();
|
||||
} catch (IOException e) {
|
||||
throw new ProtectionDatabaseException(e);
|
||||
}
|
||||
|
||||
Map<String, YAMLNode> regionData = config.getNodes("regions");
|
||||
|
||||
@ -190,7 +196,7 @@ private DefaultDomain parseDomain(YAMLNode node) {
|
||||
return domain;
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
public void save() throws ProtectionDatabaseException {
|
||||
config.clear();
|
||||
|
||||
for (Map.Entry<String, ProtectedRegion> entry : regions.entrySet()) {
|
||||
|
@ -0,0 +1,62 @@
|
||||
// $Id$
|
||||
/*
|
||||
* MySQL WordGuard Region Database
|
||||
* Copyright (C) 2011 Nicholas Steicke <http://narthollis.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.migrators;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
public abstract class AbstractDatabaseMigrator implements DatabaseMigrator {
|
||||
|
||||
private static HashMap<MigratorKey, Class<? extends AbstractDatabaseMigrator>> migrators =
|
||||
new HashMap<MigratorKey, Class<? extends AbstractDatabaseMigrator>>();
|
||||
|
||||
public static Map<MigratorKey, Class<? extends AbstractDatabaseMigrator>> getMigrators() {
|
||||
if (!migrators.isEmpty()) return migrators;
|
||||
|
||||
AbstractDatabaseMigrator.migrators.put(new MigratorKey("mysql", "yaml"), MySQLToYAMLMigrator.class);
|
||||
AbstractDatabaseMigrator.migrators.put(new MigratorKey("yaml", "mysql"), YAMLToMySQLMigrator.class);
|
||||
|
||||
return migrators;
|
||||
}
|
||||
|
||||
protected abstract Set<String> getWorldsFromOld() throws MigrationException;
|
||||
|
||||
protected abstract Map<String, ProtectedRegion> getRegionsForWorldFromOld(String world) throws MigrationException;
|
||||
|
||||
protected abstract ProtectionDatabase getNewWorldStorage(String world) throws MigrationException;
|
||||
|
||||
public void migrate() throws MigrationException {
|
||||
for (String world : this.getWorldsFromOld()) {
|
||||
ProtectionDatabase database = this.getNewWorldStorage(world);
|
||||
database.setRegions(this.getRegionsForWorldFromOld(world));
|
||||
|
||||
try {
|
||||
database.save();
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new MigrationException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
// $Id$
|
||||
/*
|
||||
* MySQL WordGuard Region Database
|
||||
* Copyright (C) 2011 Nicholas Steicke <http://narthollis.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.migrators;
|
||||
|
||||
/**
|
||||
* Represents a method of converting from one database to another.
|
||||
*
|
||||
* @author Nicholas Steicke
|
||||
*/
|
||||
public interface DatabaseMigrator {
|
||||
|
||||
public void migrate() throws MigrationException;
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
// $Id$
|
||||
/*
|
||||
* MySQL WordGuard Region Database
|
||||
* Copyright (C) 2011 Nicholas Steicke <http://narthollis.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.migrators;
|
||||
|
||||
public class MigrationException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MigrationException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public MigrationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MigrationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public MigrationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
// $Id$
|
||||
/*
|
||||
* MySQL WordGuard Region Database
|
||||
* Copyright (C) 2011 Nicholas Steicke <http://narthollis.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.migrators;
|
||||
|
||||
public class MigratorKey {
|
||||
public final String from;
|
||||
public final String to;
|
||||
|
||||
public MigratorKey(String from, String to) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
MigratorKey other = (MigratorKey) o;
|
||||
|
||||
return other.from.equals(this.from) && other.to.equals(this.to);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hash = 17;
|
||||
hash = hash * 31 + this.from.hashCode();
|
||||
hash = hash * 31 + this.to.hashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
// $Id$
|
||||
/*
|
||||
* MySQL WordGuard Region Database
|
||||
* Copyright (C) 2011 Nicholas Steicke <http://narthollis.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.migrators;
|
||||
|
||||
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;
|
||||
|
||||
import com.sk89q.worldguard.bukkit.ConfigurationManager;
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
import com.sk89q.worldguard.protection.databases.MySQLDatabase;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.databases.YAMLDatabase;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
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((Exception) 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);
|
||||
oldDatabase.load();
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new MigrationException((Exception) 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);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new MigrationException((Exception) e);
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new MigrationException((Exception) e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
// $Id$
|
||||
/*
|
||||
* MySQL WordGuard Region Database
|
||||
* Copyright (C) 2011 Nicholas Steicke <http://narthollis.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.databases.migrators;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
import com.sk89q.worldguard.protection.databases.MySQLDatabase;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.databases.YAMLDatabase;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
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));
|
||||
oldDatabase.load();
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new MigrationException((Exception) e);
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new MigrationException((Exception) e);
|
||||
}
|
||||
|
||||
return oldDatabase.getRegions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException {
|
||||
try {
|
||||
return new MySQLDatabase(plugin.getGlobalStateManager(), world);
|
||||
} catch (ProtectionDatabaseException e) {
|
||||
throw new MigrationException((Exception) e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -32,6 +32,7 @@
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegionMBRConverter;
|
||||
@ -58,7 +59,7 @@ public class PRTreeRegionManager extends RegionManager {
|
||||
* @param regionloader
|
||||
* @throws IOException
|
||||
*/
|
||||
public PRTreeRegionManager(ProtectionDatabase regionloader) throws IOException {
|
||||
public PRTreeRegionManager(ProtectionDatabase regionloader) throws ProtectionDatabaseException {
|
||||
super(regionloader);
|
||||
regions = new TreeMap<String, ProtectedRegion>();
|
||||
tree = new PRTree<ProtectedRegion>(converter, BRANCH_FACTOR);
|
||||
|
@ -26,6 +26,7 @@
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
|
||||
import com.sk89q.worldguard.protection.databases.ProtectionDatabase;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
@ -56,7 +57,7 @@ public RegionManager(ProtectionDatabase loader) {
|
||||
*
|
||||
* @throws IOException thrown on load error
|
||||
*/
|
||||
public void load() throws IOException {
|
||||
public void load() throws ProtectionDatabaseException {
|
||||
loader.load(this);
|
||||
}
|
||||
|
||||
@ -65,7 +66,7 @@ public void load() throws IOException {
|
||||
*
|
||||
* @throws IOException thrown on save eIf checking multiple flags for a single locationror
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
public void save() throws ProtectionDatabaseException {
|
||||
loader.save(this);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user