diff --git a/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java b/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java index 43b34fda..da655925 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java @@ -19,11 +19,17 @@ package com.sk89q.worldguard.bukkit; +import com.google.common.collect.ImmutableMap; import com.sk89q.commandbook.CommandBook; import com.sk89q.commandbook.GodComponent; import com.sk89q.util.yaml.YAMLFormat; import com.sk89q.util.yaml.YAMLProcessor; import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.protection.managers.storage.driver.DirectoryYamlDriver; +import com.sk89q.worldguard.protection.managers.storage.driver.DriverType; +import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver; +import com.sk89q.worldguard.protection.managers.storage.driver.SQLDriver; +import com.sk89q.worldguard.util.sql.DataSourceConfig; import org.bukkit.World; import org.bukkit.entity.Player; @@ -90,11 +96,8 @@ public class ConfigurationManager { /** * Region Storage Configuration method, and config values */ - public boolean useSqlDatabase = false; - public String sqlDsn; - public String sqlUsername; - public String sqlPassword; - public String sqlTablePrefix; + public RegionStoreDriver selectedRegionStoreDriver; + public Map regionStoreDriverMap; /** * Construct the object. @@ -167,12 +170,25 @@ public void load() { } } - useSqlDatabase = config.getBoolean("regions.sql.use", false); + // ==================================================================== + // Region store drivers + // ==================================================================== - sqlDsn = config.getString("regions.sql.dsn", "jdbc:mysql://localhost/worldguard"); - sqlUsername = config.getString("regions.sql.username", "worldguard"); - sqlPassword = config.getString("regions.sql.password", "worldguard"); - sqlTablePrefix = config.getString("regions.sql.table-prefix", ""); + boolean useSqlDatabase = config.getBoolean("regions.sql.use", false); + String sqlDsn = config.getString("regions.sql.dsn", "jdbc:mysql://localhost/worldguard"); + String sqlUsername = config.getString("regions.sql.username", "worldguard"); + String sqlPassword = config.getString("regions.sql.password", "worldguard"); + String sqlTablePrefix = config.getString("regions.sql.table-prefix", ""); + + DataSourceConfig dataSourceConfig = new DataSourceConfig(sqlDsn, sqlUsername, sqlPassword, sqlTablePrefix); + SQLDriver sqlDriver = new SQLDriver(dataSourceConfig); + DirectoryYamlDriver yamlDriver = new DirectoryYamlDriver(getWorldsDataFolder(), "regions.yml"); + + this.regionStoreDriverMap = ImmutableMap.builder() + .put(DriverType.MYSQL, sqlDriver) + .put(DriverType.YAML, yamlDriver) + .build(); + this.selectedRegionStoreDriver = useSqlDatabase ? sqlDriver : yamlDriver; // Load configurations for each world for (World world : plugin.getServer().getWorlds()) { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java index 660882aa..7093e024 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java @@ -20,7 +20,12 @@ package com.sk89q.worldguard.bukkit; import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldguard.protection.managers.ManagerContainer; import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.migration.Migration; +import com.sk89q.worldguard.protection.managers.migration.MigrationException; +import com.sk89q.worldguard.protection.managers.migration.UUIDMigration; +import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; @@ -35,6 +40,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; @@ -51,6 +58,8 @@ */ public class RegionContainer { + private static final Logger log = Logger.getLogger(RegionContainer.class.getCanonicalName()); + /** * Invalidation frequency in ticks. */ @@ -58,8 +67,8 @@ public class RegionContainer { private final Object lock = new Object(); private final WorldGuardPlugin plugin; - private final ManagerContainer container; private final QueryCache cache = new QueryCache(); + private ManagerContainer container; /** * Create a new instance. @@ -68,16 +77,17 @@ public class RegionContainer { */ RegionContainer(WorldGuardPlugin plugin) { this.plugin = plugin; - - ConfigurationManager config = plugin.getGlobalStateManager(); - container = new ManagerContainer(config); } /** * Initialize the region container. */ void initialize() { - container.initialize(); + ConfigurationManager config = plugin.getGlobalStateManager(); + container = new ManagerContainer(config.selectedRegionStoreDriver); + + // Migrate to UUIDs + autoMigrate(); loadWorlds(); @@ -128,6 +138,15 @@ void unload() { } } + /** + * Get the region store driver. + * + * @return the driver + */ + public RegionStoreDriver getDriver() { + return container.getDriver(); + } + /** * Try loading the region managers for all currently loaded worlds. */ @@ -229,4 +248,49 @@ public RegionQuery createQuery() { return new RegionQuery(plugin, cache); } + /** + * Execute a migration and block any loading of region data during + * the migration. + * + * @param migration the migration + * @throws MigrationException thrown by the migration on error + */ + public void migrate(Migration migration) throws MigrationException { + checkNotNull(migration); + + synchronized (lock) { + try { + log.info("Unloading and saving region data that is currently loaded..."); + unload(); + migration.migrate(); + } finally { + log.info("Loading region data for loaded worlds..."); + loadWorlds(); + } + } + } + + /** + * Execute auto-migration. + */ + private void autoMigrate() { + ConfigurationManager config = plugin.getGlobalStateManager(); + + if (config.migrateRegionsToUuid) { + RegionStoreDriver driver = getDriver(); + UUIDMigration migrator = new UUIDMigration(driver, plugin.getProfileService()); + migrator.setKeepUnresolvedNames(config.keepUnresolvedNames); + try { + migrate(migrator); + + log.info("Regions saved after UUID migration! This won't happen again unless " + + "you change the relevant configuration option in WorldGuard's config."); + + config.disableUuidMigration(); + } catch (MigrationException e) { + log.log(Level.WARNING, "Failed to execute the migration", e); + } + } + } + } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 365d55a5..21f205db 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -30,11 +30,6 @@ import com.sk89q.minecraft.util.commands.MissingNestedCommandException; import com.sk89q.minecraft.util.commands.SimpleInjector; import com.sk89q.minecraft.util.commands.WrappedCommandException; -import com.sk89q.worldguard.bukkit.listener.RegionFlagsListener; -import com.sk89q.worldguard.util.concurrent.EvenMoreExecutors; -import com.sk89q.worldguard.util.task.SimpleSupervisor; -import com.sk89q.worldguard.util.task.Supervisor; -import com.sk89q.worldguard.util.task.Task; import com.sk89q.squirrelid.cache.HashMapCache; import com.sk89q.squirrelid.cache.ProfileCache; import com.sk89q.squirrelid.cache.SQLiteCache; @@ -55,6 +50,7 @@ import com.sk89q.worldguard.bukkit.listener.DebuggingListener; import com.sk89q.worldguard.bukkit.listener.EventAbstractionListener; import com.sk89q.worldguard.bukkit.listener.FlagStateManager; +import com.sk89q.worldguard.bukkit.listener.RegionFlagsListener; import com.sk89q.worldguard.bukkit.listener.RegionProtectionListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardBlockListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardCommandBookListener; @@ -68,9 +64,11 @@ import com.sk89q.worldguard.protection.GlobalRegionManager; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.util.UnresolvedNamesException; -import com.sk89q.worldguard.protection.util.migrator.MigrationException; -import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator; import com.sk89q.worldguard.util.FatalConfigurationLoadingException; +import com.sk89q.worldguard.util.concurrent.EvenMoreExecutors; +import com.sk89q.worldguard.util.task.SimpleSupervisor; +import com.sk89q.worldguard.util.task.Supervisor; +import com.sk89q.worldguard.util.task.Task; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.World; @@ -188,11 +186,6 @@ public void run() { try { // Load the configuration configuration.load(); - - getLogger().info("Loading region data..."); - regionContainer.initialize(); - - migrateRegionUniqueIds(); // Migrate to UUIDs } catch (FatalConfigurationLoadingException e) { getLogger().log(Level.WARNING, "Encountered fatal error while loading configuration", e); getServer().shutdown(); @@ -208,6 +201,9 @@ public void run() { "******************************************************\n"); } + getLogger().info("Loading region data..."); + regionContainer.initialize(); + flagStateManager = new FlagStateManager(this); if (configuration.useRegionsScheduler) { @@ -289,35 +285,6 @@ public void onDisable() { this.getServer().getScheduler().cancelTasks(this); } - private void migrateRegionUniqueIds() { - try { - if (configuration.migrateRegionsToUuid) { - UUIDMigrator migrator = new UUIDMigrator(profileService, getLogger()); - migrator.readConfiguration(configuration); - List managers = globalRegionManager.getLoaded(); - - // Try migration - if (migrator.migrate(managers)) { - getLogger().info("Now saving regions... this may take a while."); - - for (RegionManager manager : managers) { - manager.save(); - } - - getLogger().info( - "Regions saved after UUID migration! This won't happen again unless " + - "you change the relevant configuration option in WorldGuard's config."); - - configuration.disableUuidMigration(); - } - } - } catch (MigrationException e) { - getLogger().log(Level.WARNING, "Failed to migrate regions to use UUIDs instead of player names", e); - } catch (IOException e) { - getLogger().log(Level.WARNING, "Failed to save region data", e); - } - } - @Override public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { try { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommands.java index ba04eda7..f5173421 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommands.java @@ -28,6 +28,8 @@ import com.sk89q.worldedit.Location; import com.sk89q.worldedit.bukkit.BukkitUtil; import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.RegionContainer; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.bukkit.commands.AsyncCommandHelper; @@ -49,17 +51,20 @@ import com.sk89q.worldguard.protection.flags.RegionGroupFlag; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.managers.RemovalStrategy; +import com.sk89q.worldguard.protection.managers.migration.DriverMigration; +import com.sk89q.worldguard.protection.managers.migration.MigrationException; +import com.sk89q.worldguard.protection.managers.migration.UUIDMigration; +import com.sk89q.worldguard.protection.managers.storage.driver.DriverType; +import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; -import com.sk89q.worldguard.protection.util.migrator.MigrationException; -import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator; +import com.sk89q.worldguard.util.Enums; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; @@ -808,60 +813,74 @@ public void save(CommandContext args, final CommandSender sender) throws Command * @throws CommandException any error */ @Command(aliases = {"migratedb"}, usage = " ", - desc = "Migrate from one Protection Database to another.", min = 2, max = 2) + flags = "y", + desc = "Migrate from one Protection Database to another.", min = 2, max = 2) public void migrateDB(CommandContext args, CommandSender sender) throws CommandException { // Check permissions if (!getPermissionModel(sender).mayMigrateRegionStore()) { throw new CommandPermissionsException(); } - /* - String from = args.getString(0).toLowerCase().trim(); - String to = args.getString(1).toLowerCase().trim(); + DriverType from = Enums.findFuzzyByValue(DriverType.class, args.getString(0)); + DriverType to = Enums.findFuzzyByValue(DriverType.class, args.getString(1)); + + if (from == null) { + throw new CommandException("The value of 'from' is not a recognized type of region data database."); + } + + if (to == null) { + throw new CommandException("The value of 'to' is not a recognized type of region region data database."); + } if (from.equals(to)) { - throw new CommandException("Will not migrate with common source and target."); + throw new CommandException("It is not possible to migrate between the same types of region data databases."); } - Map> 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(); - + if (!args.hasFlag('y')) { 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."); + "Please ensure you have made a backup of your data, and then re-enter the command with -y tacked on at the end to proceed."); } - Class cls = migrators.get(key); + ConfigurationManager config = plugin.getGlobalStateManager(); + RegionStoreDriver fromDriver = config.regionStoreDriverMap.get(from); + RegionStoreDriver toDriver = config.regionStoreDriverMap.get(to); + + if (fromDriver == null) { + throw new CommandException("The driver specified as 'from' does not seem to be supported in your version of WorldGuard."); + } + + if (toDriver == null) { + throw new CommandException("The driver specified as 'to' does not seem to be supported in your version of WorldGuard."); + } + + DriverMigration migration = new DriverMigration(fromDriver, toDriver); + + LoggerToChatHandler handler = null; + Logger minecraftLogger = null; + + if (sender instanceof Player) { + handler = new LoggerToChatHandler(sender); + handler.setLevel(Level.ALL); + minecraftLogger = Logger.getLogger("com.sk89q.worldguard"); + minecraftLogger.addHandler(handler); + } try { - AbstractDatabaseMigrator migrator = cls.getConstructor(WorldGuardPlugin.class).newInstance(plugin); - - migrator.migrate(); - } catch (IllegalArgumentException ignore) { - } catch (SecurityException ignore) { - } catch (InstantiationException ignore) { - } catch (IllegalAccessException ignore) { - } catch (InvocationTargetException ignore) { - } catch (NoSuchMethodException ignore) { + RegionContainer container = plugin.getRegionContainer(); + sender.sendMessage(ChatColor.YELLOW + "Now performing migration... this may take a while."); + container.migrate(migration); + sender.sendMessage(ChatColor.YELLOW + + "Migration complete! This only migrated the data. If you already changed your settings to use " + + "the target driver, then WorldGuard is now using the new data. If not, you have to adjust your " + + "configuration to use the new driver and then restart your server."); } catch (MigrationException e) { - throw new CommandException("Error migrating database: " + e.getMessage()); + plugin.getLogger().log(Level.WARNING, "Failed to migrate", e); + throw new CommandException("Error encountered while migrating: " + e.getMessage()); + } finally { + if (minecraftLogger != null) { + minecraftLogger.removeHandler(handler); + } } - - 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."); - */ } /** @@ -885,30 +904,19 @@ public void migrateUuid(CommandContext args, CommandSender sender) throws Comman if (sender instanceof Player) { handler = new LoggerToChatHandler(sender); handler.setLevel(Level.ALL); - minecraftLogger = Logger.getLogger("Minecraft"); + minecraftLogger = Logger.getLogger("com.sk89q.worldguard"); minecraftLogger.addHandler(handler); } try { - UUIDMigrator migrator = new UUIDMigrator(plugin.getProfileService(), plugin.getLogger()); - migrator.readConfiguration(plugin.getGlobalStateManager()); - List managers = plugin.getRegionContainer().getLoaded(); - - // Try migration - if (migrator.migrate(managers)) { - sender.sendMessage(ChatColor.YELLOW + "Now saving regions... this may take a while."); - - for (RegionManager manager : managers) { - manager.save(); - } - - sender.sendMessage(ChatColor.YELLOW + "Migration complete!"); - } else { - sender.sendMessage(ChatColor.YELLOW + "There were no names to migrate."); - } - } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, "Failed to save", e); - throw new CommandException("Error encountered while saving: " + e.getMessage()); + ConfigurationManager config = plugin.getGlobalStateManager(); + RegionContainer container = plugin.getRegionContainer(); + RegionStoreDriver driver = container.getDriver(); + UUIDMigration migration = new UUIDMigration(driver, plugin.getProfileService()); + migration.setKeepUnresolvedNames(config.keepUnresolvedNames); + sender.sendMessage(ChatColor.YELLOW + "Now performing migration... this may take a while."); + container.migrate(migration); + sender.sendMessage(ChatColor.YELLOW + "Migration complete!"); } catch (MigrationException e) { plugin.getLogger().log(Level.WARNING, "Failed to migrate", e); throw new CommandException("Error encountered while migrating: " + e.getMessage()); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/ManagerContainer.java b/src/main/java/com/sk89q/worldguard/protection/managers/ManagerContainer.java similarity index 86% rename from src/main/java/com/sk89q/worldguard/bukkit/ManagerContainer.java rename to src/main/java/com/sk89q/worldguard/protection/managers/ManagerContainer.java index f79f09b7..c877a3b7 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/ManagerContainer.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/ManagerContainer.java @@ -17,15 +17,13 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.bukkit; +package com.sk89q.worldguard.protection.managers; import com.google.common.base.Supplier; -import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.managers.index.ChunkHashTable; 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; @@ -33,7 +31,6 @@ 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; @@ -48,43 +45,35 @@ /** * Manages different {@link RegionManager}s for different worlds or dimensions. */ -class ManagerContainer { +public class ManagerContainer { private static final Logger log = Logger.getLogger(ManagerContainer.class.getCanonicalName()); private static final int SAVE_INTERVAL = 1000 * 30; - private final ConfigurationManager config; private final ConcurrentMap mapping = new ConcurrentHashMap(); private final Object lock = new Object(); - private final EnumMap drivers = new EnumMap(DriverType.class); - private RegionStoreDriver defaultDriver; + private final RegionStoreDriver driver; private final Supplier indexFactory = new ChunkHashTable.Factory(new PriorityRTreeIndex.Factory()); private final Timer timer = new Timer(); /** * Create a new instance. * - * @param config the configuration + * @param driver the region store driver */ - ManagerContainer(ConfigurationManager config) { - checkNotNull(config); - this.config = config; + public ManagerContainer(RegionStoreDriver driver) { + checkNotNull(driver); + this.driver = driver; timer.schedule(new BackgroundSaver(), SAVE_INTERVAL, SAVE_INTERVAL); } /** - * Create drivers from the configuration. + * Get the region store driver. + * + * @return the driver */ - public void initialize() { - 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); - } + public RegionStoreDriver getDriver() { + return driver; } /** @@ -125,7 +114,7 @@ public RegionManager load(String name) { * @throws IOException thrown if loading fals */ private RegionManager createAndLoad(String name) throws IOException { - RegionStore store = defaultDriver.get(name); + RegionStore store = driver.get(name); RegionManager manager = new RegionManager(store, indexFactory); manager.load(); // Try loading, although it may fail return manager; @@ -168,7 +157,7 @@ public void unloadAll() { String name = entry.getKey().toString(); RegionManager manager = entry.getValue(); try { - manager.save(); + manager.saveChanges(); } catch (IOException e) { log.log(Level.WARNING, "Failed to save the region data for '" + name + "' while unloading the data for all worlds", e); } @@ -212,7 +201,9 @@ public void run() { String name = entry.getKey().toString(); RegionManager manager = entry.getValue(); try { - manager.saveChanges(); + if (manager.saveChanges()) { + log.info("Region data changes made in '" + name + "' have been background saved"); + } } catch (IOException e) { log.log(Level.WARNING, "Failed to save the region data for '" + name + "' during a periodical save", e); } catch (Exception e) { diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java index 84a51127..8f222cc4 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java @@ -81,7 +81,11 @@ public RegionManager(RegionStore store, Supplier regions = store.loadAll(); + for (ProtectedRegion region : regions) { + region.setDirty(false); + } + setRegions(regions); } /** @@ -100,9 +104,10 @@ public void save() throws IOException { * *

This method does nothing if there are no changes.

* + * @return true if there were changes to be saved * @throws IOException thrown on save error */ - public void saveChanges() throws IOException { + public boolean saveChanges() throws IOException { RegionDifference diff = index.getAndClearDifference(); if (diff.containsChanges()) { @@ -111,6 +116,9 @@ public void saveChanges() throws IOException { } catch (DifferenceSaveException e) { save(); // Partial save is not supported } + return true; + } else { + return false; } } diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/migration/AbstractMigration.java b/src/main/java/com/sk89q/worldguard/protection/managers/migration/AbstractMigration.java new file mode 100644 index 00000000..b5257d6e --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/migration/AbstractMigration.java @@ -0,0 +1,82 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.migration; + +import com.sk89q.worldguard.protection.managers.storage.RegionStore; +import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * An abstract implementation of a migrator that gets all the worlds in + * a driver and calls a override-able {@code migrate()} method for + * each store. + */ +abstract class AbstractMigration implements Migration { + + private static final Logger log = Logger.getLogger(AbstractMigration.class.getCanonicalName()); + private final RegionStoreDriver driver; + + /** + * Create a new instance. + * + * @param driver the storage driver + */ + public AbstractMigration(RegionStoreDriver driver) { + checkNotNull(driver); + + this.driver = driver; + } + + @Override + public final void migrate() throws MigrationException { + try { + for (RegionStore store : driver.getAll()) { + try { + migrate(store); + } catch (MigrationException e) { + log.log(Level.WARNING, "Migration of one world (" + store.getName() + ") failed with an error", e); + } + } + + postMigration(); + } catch (IOException e) { + throw new MigrationException("Migration failed because the process of getting a list of all the worlds to migrate failed", e); + } + } + + /** + * Called for all the worlds in the driver. + * + * @param store the region store + * @throws MigrationException on migration error + */ + protected abstract void migrate(RegionStore store)throws MigrationException; + + /** + * Called after migration has successfully completed. + */ + protected abstract void postMigration(); + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/migration/DriverMigration.java b/src/main/java/com/sk89q/worldguard/protection/managers/migration/DriverMigration.java new file mode 100644 index 00000000..cb487abb --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/migration/DriverMigration.java @@ -0,0 +1,89 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.migration; + +import com.sk89q.worldguard.protection.managers.storage.RegionStore; +import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.io.IOException; +import java.util.Set; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Handles migration from one region store driver to another. + */ +public class DriverMigration extends AbstractMigration { + + private static final Logger log = Logger.getLogger(DriverMigration.class.getCanonicalName()); + private final RegionStoreDriver target; + + /** + * Create a new instance. + * + * @param driver the source storage driver + * @param target the target storage driver + */ + public DriverMigration(RegionStoreDriver driver, RegionStoreDriver target) { + super(driver); + checkNotNull(target); + this.target = target; + } + + @Override + protected void migrate(RegionStore store) throws MigrationException { + Set regions; + + log.info("Loading the regions for '" + store.getName() + "' with the old driver..."); + + try { + regions = store.loadAll(); + } catch (IOException e) { + throw new MigrationException("Failed to load region data for the world '" + store.getName() + "'", e); + } + + write(store.getName(), regions); + } + + private void write(String name, Set regions) throws MigrationException { + log.info("Saving the data for '" + name + "' with the new driver..."); + + RegionStore store; + + try { + store = target.get(name); + } catch (IOException e) { + throw new MigrationException("The driver to migrate to can't store region data for the world '" + name + "'", e); + } + + try { + store.saveAll(regions); + } catch (IOException e) { + throw new MigrationException("Failed to save region data for '" + store.getName() + "' to the new driver", e); + } + } + + @Override + protected void postMigration() { + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/migration/Migration.java b/src/main/java/com/sk89q/worldguard/protection/managers/migration/Migration.java new file mode 100644 index 00000000..637ffe19 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/migration/Migration.java @@ -0,0 +1,34 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.migration; + +/** + * An object that migrates region data. + */ +public interface Migration { + + /** + * Execute the migration. + * + * @throws MigrationException thrown if the migration fails + */ + void migrate() throws MigrationException; + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/util/migrator/MigrationException.java b/src/main/java/com/sk89q/worldguard/protection/managers/migration/MigrationException.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/protection/util/migrator/MigrationException.java rename to src/main/java/com/sk89q/worldguard/protection/managers/migration/MigrationException.java index 9ea57a4a..c045b003 100644 --- a/src/main/java/com/sk89q/worldguard/protection/util/migrator/MigrationException.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/migration/MigrationException.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection.util.migrator; +package com.sk89q.worldguard.protection.managers.migration; /** * Thrown when a migration fails. diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/migration/UUIDMigration.java b/src/main/java/com/sk89q/worldguard/protection/managers/migration/UUIDMigration.java new file mode 100644 index 00000000..defd3a54 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/migration/UUIDMigration.java @@ -0,0 +1,240 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.migration; + +import com.google.common.base.Predicate; +import com.sk89q.squirrelid.Profile; +import com.sk89q.squirrelid.resolver.ProfileService; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.domains.PlayerDomain; +import com.sk89q.worldguard.protection.managers.storage.RegionStore; +import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; +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; + +/** + * Migrates names to UUIDs for all the worlds in a region store. + */ +public class UUIDMigration extends AbstractMigration { + + private static final Logger log = Logger.getLogger(UUIDMigration.class.getCanonicalName()); + private static final int LOG_DELAY = 5000; + + private final Timer timer = new Timer(); + private final ProfileService profileService; + private final ConcurrentMap resolvedNames = new ConcurrentHashMap(); + private final Set unresolvedNames = new HashSet(); + private boolean keepUnresolvedNames = true; + + /** + * Create a new instance. + * + * @param driver the storage driver + * @param profileService the profile service + */ + public UUIDMigration(RegionStoreDriver driver, ProfileService profileService) { + super(driver); + checkNotNull(profileService); + this.profileService = profileService; + } + + @Override + protected void migrate(RegionStore store) throws MigrationException { + log.log(Level.INFO, "Migrating regions in '" + store.getName() + "' to convert names -> UUIDs..."); + + Set regions; + + try { + regions = store.loadAll(); + } catch (IOException e) { + throw new MigrationException("Failed to load region data for the world '" + store.getName() + "'", e); + } + + migrate(regions); + + try { + store.saveAll(regions); + } catch (IOException e) { + throw new MigrationException("Failed to save region data after migration of the world '" + store.getName() + "'", e); + } + } + + private boolean migrate(Collection regions) throws MigrationException { + // Name scan pass + Set names = getNames(regions); + + if (!names.isEmpty()) { + // This task logs the progress of conversion (% converted...) + // periodically + TimerTask task = new ResolvedNamesTimerTask(); + + try { + timer.schedule(task, LOG_DELAY, LOG_DELAY); + + log.log(Level.INFO, "Resolving " + names.size() + " name(s) into UUIDs... this may take a while."); + + // Don't lookup names that we already looked up for previous + // worlds -- note: all names are lowercase in these collections + Set lookupNames = new HashSet(names); + lookupNames.removeAll(resolvedNames.keySet()); + + // Ask Mojang for names + profileService.findAllByName(lookupNames, new Predicate() { + @Override + public boolean apply(Profile profile) { + resolvedNames.put(profile.getName().toLowerCase(), profile.getUniqueId()); + return true; + } + }); + } catch (IOException e) { + throw new MigrationException("The name -> UUID service failed", e); + } catch (InterruptedException e) { + throw new MigrationException("The migration was interrupted"); + } finally { + // Stop showing the % converted messages + task.cancel(); + } + + // Name -> UUID in all regions + log.log(Level.INFO, "UUIDs resolved... now migrating all regions to UUIDs where possible..."); + convert(regions); + + return true; + } else { + return false; + } + } + + @Override + protected void postMigration() { + if (!unresolvedNames.isEmpty()) { + if (keepUnresolvedNames) { + log.log(Level.WARNING, + "Some member and owner names do not seem to exist or own Minecraft so they " + + "could not be converted into UUIDs. They have been left as names, but the conversion can " + + "be re-run with 'keep-names-that-lack-uuids' set to false in the configuration in " + + "order to remove these names. Leaving the names means that someone can register with one of " + + "these names in the future and become that player."); + } else { + log.log(Level.WARNING, + "Some member and owner names do not seem to exist or own Minecraft so they " + + "could not be converted into UUIDs. These names have been removed."); + } + } + } + + /** + * Grab all the player names from all the regions in the given collection. + * + * @param regions a collection of regions + * @return a set of names + */ + private static Set getNames(Collection regions) { + Set names = new HashSet(); + for (ProtectedRegion region : regions) { + // Names are already lower case + names.addAll(region.getOwners().getPlayers()); + names.addAll(region.getMembers().getPlayers()); + } + return names; + } + + /** + * Convert all the names to UUIDs. + * + * @param regions a collection of regions + */ + private void convert(Collection regions) { + for (ProtectedRegion region : regions) { + convert(region.getOwners()); + convert(region.getMembers()); + } + } + + /** + * Convert all the names to UUIDs. + * + * @param domain the domain + */ + private void convert(DefaultDomain domain) { + PlayerDomain playerDomain = new PlayerDomain(); + for (UUID uuid : domain.getUniqueIds()) { + playerDomain.addPlayer(uuid); + } + + for (String name : domain.getPlayers()) { + UUID uuid = resolvedNames.get(name.toLowerCase()); + if (uuid != null) { + playerDomain.addPlayer(uuid); + } else { + if (keepUnresolvedNames) { + playerDomain.addPlayer(name); + } + unresolvedNames.add(name); + } + } + + domain.setPlayerDomain(playerDomain); + } + + /** + * Get whether names that have no UUID equivalent (i.e. name that is not + * owned) should be kept as names and not removed. + * + * @return true to keep names + */ + public boolean getKeepUnresolvedNames() { + return keepUnresolvedNames; + } + + /** + * Set whether names that have no UUID equivalent (i.e. name that is not + * owned) should be kept as names and not removed. + * + * @param keepUnresolvedNames true to keep names + */ + public void setKeepUnresolvedNames(boolean keepUnresolvedNames) { + this.keepUnresolvedNames = keepUnresolvedNames; + } + + /** + * A task to periodically say how many names have been resolved. + */ + private class ResolvedNamesTimerTask extends TimerTask { + @Override + public void run() { + log.info("UUIDs have been found for " + resolvedNames.size() + " name(s)..."); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/MemoryRegionStore.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/MemoryRegionStore.java index 149634ea..de976390 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/storage/MemoryRegionStore.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/MemoryRegionStore.java @@ -34,6 +34,11 @@ public class MemoryRegionStore implements RegionStore { private Set regions = Collections.emptySet(); + @Override + public String getName() { + return "MEMORY"; + } + @Override public Set loadAll() throws IOException { return regions; diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStore.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStore.java index 2d6da8be..e599c51f 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStore.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStore.java @@ -30,6 +30,11 @@ */ public interface RegionStore { + /** + * Get a displayable name for this store. + */ + String getName(); + /** * Load all regions from storage and place them into the passed map. * diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DirectoryYamlDriver.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DirectoryYamlDriver.java index 28e06bd6..9824b8bc 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DirectoryYamlDriver.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DirectoryYamlDriver.java @@ -81,23 +81,23 @@ public RegionStore get(String id) throws IOException { } } - return new YamlFileStore(file); + return new YamlFileStore(id, file); } @Override - public List fetchAllExisting() { - List names = new ArrayList(); + public List getAll() throws IOException { + List stores = new ArrayList(); File files[] = rootDir.listFiles(); if (files != null) { for (File dir : files) { if (dir.isDirectory() && new File(dir, "regions.yml").isFile()) { - names.add(dir.getName()); + stores.add(new YamlFileStore(dir.getName(), getPath(dir.getName()))); } } } - return names; + return stores; } } diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DriverType.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DriverType.java index 9e45691b..a379b3d2 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DriverType.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DriverType.java @@ -19,34 +19,12 @@ 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); + YAML, + MYSQL } diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/RegionStoreDriver.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/RegionStoreDriver.java index 55ba195a..e57d59eb 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/RegionStoreDriver.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/RegionStoreDriver.java @@ -44,6 +44,6 @@ public interface RegionStoreDriver { * @return a list of names * @throws IOException thrown if the fetch operation fails */ - List fetchAllExisting() throws IOException; + List getAll() throws IOException; } diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/SQLDriver.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/SQLDriver.java index 04c9d25e..58235b84 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/SQLDriver.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/SQLDriver.java @@ -80,17 +80,17 @@ public RegionStore get(String name) throws IOException { } @Override - public List fetchAllExisting() throws IOException { + public List getAll() throws IOException { Closer closer = Closer.create(); try { - List names = new ArrayList(); + List stores = new ArrayList(); 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)); + stores.add(get(rs.getString(1))); } - return names; + return stores; } catch (SQLException e) { throw new IOException("Failed to fetch list of worlds", e); } finally { diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlFileStore.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlFileStore.java index 54851b37..7d4b6e44 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlFileStore.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlFileStore.java @@ -78,6 +78,7 @@ public class YamlFileStore implements RegionStore { "# REMEMBER TO KEEP PERIODICAL BACKUPS.\r\n" + "#"; + private final String name; private final File file; static { @@ -91,11 +92,14 @@ public class YamlFileStore implements RegionStore { /** * Create a new instance. * + * @param name the name of this store * @param file the file * @throws IOException thrown if the file cannot be written to */ - public YamlFileStore(File file) throws IOException { + public YamlFileStore(String name, File file) throws IOException { + checkNotNull(name); checkNotNull(file); + this.name = name; this.file = file; if (!file.exists()) { //noinspection ResultOfMethodCallIgnored @@ -103,6 +107,11 @@ public YamlFileStore(File file) throws IOException { } } + @Override + public String getName() { + return name; + } + @Override public Set loadAll() throws IOException { Map loaded = new HashMap(); diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/SQLRegionStore.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/SQLRegionStore.java index 2da61c0a..8a430c8a 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/SQLRegionStore.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/SQLRegionStore.java @@ -60,6 +60,7 @@ public class SQLRegionStore implements RegionStore { private static final Logger log = Logger.getLogger(SQLRegionStore.class.getCanonicalName()); + private final String name; private final BoneCP connectionPool; private final DataSourceConfig config; private final int worldId; @@ -67,6 +68,7 @@ public class SQLRegionStore implements RegionStore { /** * Create a new instance. * + * @param name the name of this store * @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 @@ -77,6 +79,7 @@ public SQLRegionStore(DataSourceConfig config, BoneCP connectionPool, String wor checkNotNull(connectionPool); checkNotNull(worldName); + this.name = worldName; this.config = config; this.connectionPool = connectionPool; @@ -95,6 +98,11 @@ public SQLRegionStore(DataSourceConfig config, BoneCP connectionPool, String wor } } + @Override + public String getName() { + return name; + } + /** * Attempt to migrate the tables to the latest version. * diff --git a/src/main/java/com/sk89q/worldguard/protection/util/migrator/UUIDMigrator.java b/src/main/java/com/sk89q/worldguard/protection/util/migrator/UUIDMigrator.java deleted file mode 100644 index 1071916a..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/util/migrator/UUIDMigrator.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.util.migrator; - -import com.google.common.base.Predicate; -import com.sk89q.squirrelid.Profile; -import com.sk89q.squirrelid.resolver.ProfileService; -import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.domains.PlayerDomain; -import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import java.io.IOException; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; -import java.util.Timer; -import java.util.TimerTask; -import java.util.UUID; -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; - -public class UUIDMigrator { - - private static final int LOG_DELAY = 5000; - - private final Timer timer = new Timer(); - private final ProfileService profileService; - private final Logger logger; - private final ConcurrentMap resolvedNames = new ConcurrentHashMap(); - private final Set unresolvedNames = new HashSet(); - private boolean keepUnresolvedNames = true; - - public UUIDMigrator(ProfileService profileService, Logger logger) { - checkNotNull(profileService); - checkNotNull(logger); - - this.profileService = profileService; - this.logger = logger; - } - - public boolean migrate(Collection managers) throws MigrationException { - Set names = new HashSet(); - - // Name scan pass - logger.log(Level.INFO, "UUID migrator: Gathering names to convert..."); - for (RegionManager manager : managers) { - scanForNames(manager, names); - } - - if (names.isEmpty()) { - logger.log(Level.INFO, "UUID migrator: No names to convert!"); - return false; - } - - TimerTask resolvedTask = new ResolvedNamesTimerTask(); - try { - timer.schedule(resolvedTask, LOG_DELAY, LOG_DELAY); - logger.log(Level.INFO, "UUID migrator: Resolving " + names.size() + " name(s) into UUIDs... this may take a while."); - profileService.findAllByName(names, new Predicate() { - @Override - public boolean apply(Profile profile) { - resolvedNames.put(profile.getName().toLowerCase(), profile.getUniqueId()); - return true; - } - }); - } catch (IOException e) { - throw new MigrationException("The name -> UUID service failed", e); - } catch (InterruptedException e) { - throw new MigrationException("The migration was interrupted"); - } finally { - resolvedTask.cancel(); - } - - logger.log(Level.INFO, "UUID migrator: UUIDs resolved... now migrating all regions to UUIDs where possible..."); - for (RegionManager manager : managers) { - convertToUniqueIds(manager); - } - - if (!unresolvedNames.isEmpty()) { - if (keepUnresolvedNames) { - logger.log(Level.WARNING, - "UUID migrator: Some member and owner names do not seem to exist or own Minecraft so they " + - "could not be converted into UUIDs. They have been left as names, but the conversion can " + - "be re-run with 'keep-names-that-lack-uuids' set to false in the configuration in " + - "order to remove these names. Leaving the names means that someone can register with one of " + - "these names in the future and become that player."); - } else { - logger.log(Level.WARNING, - "UUID migrator: Some member and owner names do not seem to exist or own Minecraft so they " + - "could not be converted into UUIDs. These names have been removed."); - } - } - - logger.log(Level.INFO, "UUID migrator: Migration finished!"); - - return true; - } - - private void scanForNames(RegionManager manager, Set target) { - for (ProtectedRegion region : manager.getRegions().values()) { - target.addAll(region.getOwners().getPlayers()); - target.addAll(region.getMembers().getPlayers()); - } - } - - private void convertToUniqueIds(RegionManager manager) { - for (ProtectedRegion region : manager.getRegions().values()) { - convertToUniqueIds(region.getOwners()); - convertToUniqueIds(region.getMembers()); - } - } - - private void convertToUniqueIds(DefaultDomain domain) { - PlayerDomain playerDomain = new PlayerDomain(); - for (UUID uuid : domain.getUniqueIds()) { - playerDomain.addPlayer(uuid); - } - - for (String name : domain.getPlayers()) { - UUID uuid = resolvedNames.get(name.toLowerCase()); - if (uuid != null) { - playerDomain.addPlayer(uuid); - } else { - if (keepUnresolvedNames) { - playerDomain.addPlayer(name); - } - unresolvedNames.add(name); - } - } - - domain.setPlayerDomain(playerDomain); - } - - public boolean isKeepUnresolvedNames() { - return keepUnresolvedNames; - } - - public void setKeepUnresolvedNames(boolean keepUnresolvedNames) { - this.keepUnresolvedNames = keepUnresolvedNames; - } - - public void readConfiguration(ConfigurationManager config) { - setKeepUnresolvedNames(config.keepUnresolvedNames); - } - - private class ResolvedNamesTimerTask extends TimerTask { - @Override - public void run() { - logger.info("UUID migrator: UUIDs have been found for " + resolvedNames.size() + " name(s)..."); - } - } - -} diff --git a/src/main/java/com/sk89q/worldguard/util/Enums.java b/src/main/java/com/sk89q/worldguard/util/Enums.java new file mode 100644 index 00000000..1ce7a4d5 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/Enums.java @@ -0,0 +1,80 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helper methods for enums. + */ +public final class Enums { + + private Enums() { + } + + /** + * Search the given enum for a value that is equal to the one of the + * given values, searching in an ascending manner. + * + * @param enumType the enum type + * @param values the list of values + * @param the type of enum + * @return the found value or null + */ + @Nullable + public static > T findByValue(Class enumType, String... values) { + checkNotNull(enumType); + checkNotNull(values); + for (String val : values) { + try { + return Enum.valueOf(enumType, val); + } catch (IllegalArgumentException ignored) {} + } + return null; + } + + /** + * Search the given enum for a value that is equal to the one of the + * given values, searching in an ascending manner. + * + *

Some fuzzy matching of the provided values may be performed.

+ * + * @param enumType the enum type + * @param values the list of values + * @param the type of enum + * @return the found value or null + */ + @Nullable + public static > T findFuzzyByValue(Class enumType, String... values) { + checkNotNull(enumType); + checkNotNull(values); + for (String test : values) { + for (T value : enumType.getEnumConstants()) { + if (value.name().equalsIgnoreCase(test)) { + return value; + } + } + } + return null; + } + +}