summaryrefslogtreecommitdiffstats
path: root/bukkit/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'bukkit/src/main')
-rw-r--r--bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java2
-rw-r--r--bukkit/src/main/java/com/leonardobishop/quests/bukkit/command/AdminCommandSwitcher.java2
-rw-r--r--bukkit/src/main/java/com/leonardobishop/quests/bukkit/command/AdminMigrateCommandHandler.java172
-rw-r--r--bukkit/src/main/java/com/leonardobishop/quests/bukkit/storage/MySqlStorageProvider.java50
-rw-r--r--bukkit/src/main/java/com/leonardobishop/quests/bukkit/storage/YamlStorageProvider.java47
-rw-r--r--bukkit/src/main/resources/resources/bukkit/migrate_data.yml49
6 files changed, 321 insertions, 1 deletions
diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java
index aa202b61..e013cf7f 100644
--- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java
+++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java
@@ -526,7 +526,7 @@ public class BukkitQuestsPlugin extends JavaPlugin implements Quests {
}
}
- private void writeResourceToFile(String resource, File file) {
+ public void writeResourceToFile(String resource, File file) {
try {
file.createNewFile();
try (InputStream in = BukkitQuestsPlugin.class.getClassLoader().getResourceAsStream(resource);
diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/command/AdminCommandSwitcher.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/command/AdminCommandSwitcher.java
index 9afffdf3..be86e6df 100644
--- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/command/AdminCommandSwitcher.java
+++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/command/AdminCommandSwitcher.java
@@ -20,6 +20,7 @@ public class AdminCommandSwitcher extends CommandSwitcher {
super.subcommands.put("reload", new AdminReloadCommandHandler(plugin));
super.subcommands.put("items", new AdminItemsCommandHandler(plugin));
super.subcommands.put("config", new AdminConfigCommandHandler(plugin));
+ super.subcommands.put("migratedata", new AdminMigrateCommandHandler(plugin));
super.subcommands.put("update", new AdminUpdateCommandHandler(plugin));
super.subcommands.put("wiki", new AdminWikiCommandHandler(plugin));
super.subcommands.put("about", new AdminAboutCommandHandler(plugin));
@@ -37,6 +38,7 @@ public class AdminCommandSwitcher extends CommandSwitcher {
sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a items [import <id>] " + ChatColor.DARK_GRAY + ": view registered quest items");
sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a reload " + ChatColor.DARK_GRAY + ": reload Quests configuration");
sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a config " + ChatColor.DARK_GRAY + ": see detected problems in config");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a migratedata " + ChatColor.DARK_GRAY + ": migrate quests data");
sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a update " + ChatColor.DARK_GRAY + ": check for updates");
sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a wiki " + ChatColor.DARK_GRAY + ": get a link to the Quests wiki");
sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a about " + ChatColor.DARK_GRAY + ": get information about Quests");
diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/command/AdminMigrateCommandHandler.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/command/AdminMigrateCommandHandler.java
new file mode 100644
index 00000000..39be3ea8
--- /dev/null
+++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/command/AdminMigrateCommandHandler.java
@@ -0,0 +1,172 @@
+package com.leonardobishop.quests.bukkit.command;
+
+import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin;
+import com.leonardobishop.quests.bukkit.storage.MySqlStorageProvider;
+import com.leonardobishop.quests.bukkit.storage.YamlStorageProvider;
+import com.leonardobishop.quests.common.player.questprogressfile.QuestProgressFile;
+import com.leonardobishop.quests.common.storage.StorageProvider;
+import org.bukkit.ChatColor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class AdminMigrateCommandHandler implements CommandHandler {
+
+ private final BukkitQuestsPlugin plugin;
+
+ private final AtomicBoolean migrationInProgress;
+
+ public AdminMigrateCommandHandler(BukkitQuestsPlugin plugin) {
+ this.plugin = plugin;
+ this.migrationInProgress = new AtomicBoolean(false);
+ }
+
+ @Override
+ public void handle(CommandSender sender, String[] args) {
+ File dataMigrateFile = new File(plugin.getDataFolder(), "migrate_data.yml");
+
+ if (migrationInProgress.get()) {
+ sender.sendMessage(ChatColor.RED + "A migration is already in progress.");
+ }
+
+ if (args.length == 3 && args[2].equalsIgnoreCase("execute")) {
+ if (!dataMigrateFile.exists()) {
+ sender.sendMessage(ChatColor.RED + "Please run '/quests admin migratedata' first.");
+ return;
+ }
+
+ YamlConfiguration configuration;
+ try {
+ configuration = YamlConfiguration.loadConfiguration(dataMigrateFile);
+ } catch (Exception e) {
+ sender.sendMessage(ChatColor.RED + "An error occurred while loading the data migration file.");
+ e.printStackTrace();
+ sender.sendMessage(ChatColor.RED + "See server console for more details.");
+ return;
+ }
+
+ if (!configuration.getBoolean("ready")) {
+ sender.sendMessage(ChatColor.RED + "The 'ready' flag has not been set.");
+ sender.sendMessage(ChatColor.RED + "Please see the migrate_data.yml file, or the wiki, for instructions.");
+ return;
+ }
+
+ ConfigurationSection fromConfiguration = configuration.getConfigurationSection("from");
+ ConfigurationSection toConfiguration = configuration.getConfigurationSection("to");
+
+ if (fromConfiguration == null || toConfiguration == null) {
+ sender.sendMessage(ChatColor.RED + "The 'from' and 'to' sections have not been configured.");
+ sender.sendMessage(ChatColor.RED + "Please see the migrate_data.yml file, or the wiki, for instructions.");
+ return;
+ }
+
+ StorageProvider fromProvider = getStorageProvider(fromConfiguration);
+ StorageProvider toProvider = getStorageProvider(toConfiguration);
+
+ if (fromProvider.getName().equals("yaml") && toProvider.getName().equals("yaml")) {
+ //TODO check mysql databases aren't the same as well
+ sender.sendMessage(ChatColor.RED + "Refusing to migrate from 'yaml' to 'yaml'.");
+ sender.sendMessage(ChatColor.RED + "Please see the migrate_data.yml file, or the wiki, for instructions.");
+ return;
+ }
+
+ long startTime = System.currentTimeMillis();
+ sender.sendMessage(ChatColor.GRAY + "Performing migration...");
+ migrationInProgress.set(true);
+ plugin.getScheduler().doAsync(() -> {
+ try {
+ sender.sendMessage(ChatColor.GRAY + "Initialising storage provider '" + fromProvider.getName() + "'...");
+ fromProvider.init();
+ } catch (Exception e) {
+ sender.sendMessage(ChatColor.RED + "An error occurred while initializing '" + fromProvider.getName() + "' storage provider.");
+ return;
+ }
+
+ try {
+ sender.sendMessage(ChatColor.GRAY + "Initialising storage provider '" + toProvider.getName() + "'...");
+ toProvider.init();
+ } catch (Exception e) {
+ sender.sendMessage(ChatColor.RED + "An error occurred while initializing '" + toProvider.getName() + "' storage provider.");
+ return;
+ }
+
+ sender.sendMessage(ChatColor.GRAY + "Loading quest progress files from '" + fromProvider.getName() + "'...");
+ List<QuestProgressFile> files = fromProvider.loadAllProgressFiles();
+ sender.sendMessage(ChatColor.GRAY.toString() + files.size() + " files loaded.");
+
+ for (QuestProgressFile file : files) {
+ file.setModified(true);
+ }
+
+ sender.sendMessage(ChatColor.GRAY + "Writing quest progress files to '" + toProvider.getName() + "'...");
+ toProvider.saveAllProgressFiles(files);
+ sender.sendMessage(ChatColor.GRAY + "Done.");
+
+ try {
+ sender.sendMessage(ChatColor.GRAY + "Shutting down storage provider '" + fromProvider.getName() + "'...");
+ fromProvider.shutdown();
+ } catch (Exception e) {
+ sender.sendMessage(ChatColor.RED + "An error occurred while shutting down '" + fromProvider.getName() + "' storage provider.");
+ }
+
+ try {
+ sender.sendMessage(ChatColor.GRAY + "Shutting down storage provider '" + toProvider.getName() + "'...");
+ toProvider.shutdown();
+ } catch (Exception e) {
+ sender.sendMessage(ChatColor.RED + "An error occurred while shutting down '" + toProvider.getName() + "' storage provider.");
+ }
+
+ long endTime = System.currentTimeMillis();
+ sender.sendMessage(ChatColor.GREEN + "Migration complete. Took " + String.format("%.3f", (endTime - startTime) / 1000f) + "s.");
+
+ configuration.set("ready", false);
+ try {
+ configuration.save(dataMigrateFile);
+ } catch (IOException ignored) { }
+ migrationInProgress.set(false);
+ });
+ return;
+ }
+
+ if (!dataMigrateFile.exists()) {
+ plugin.writeResourceToFile("resources/bukkit/migrate_data.yml", dataMigrateFile);
+ }
+ sender.sendMessage(ChatColor.GRAY + "A file has been generated at /plugins/Quests/migrate_data.yml.");
+ sender.sendMessage(ChatColor.GRAY + "Please see this file, or the wiki, for further instructions.");
+ }
+
+ private StorageProvider getStorageProvider(ConfigurationSection configurationSection) {
+ String configuredProvider = configurationSection.getString("provider", "yaml");
+ StorageProvider storageProvider;
+ switch (configuredProvider.toLowerCase()) {
+ default:
+ case "yaml":
+ storageProvider = new YamlStorageProvider(plugin);
+ break;
+ case "mysql":
+ ConfigurationSection section = configurationSection.getConfigurationSection("database-settings");
+ storageProvider = new MySqlStorageProvider(plugin, section);
+ }
+ return storageProvider;
+ }
+
+ @Override
+ public List<String> tabComplete(CommandSender sender, String[] args) {
+ if (args.length == 3) {
+ return TabHelper.matchTabComplete(args[2], Collections.singletonList("execute"));
+ }
+ return Collections.emptyList();
+ }
+
+ @Override
+ public @Nullable String getPermission() {
+ return "quests.admin";
+ }
+}
diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/storage/MySqlStorageProvider.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/storage/MySqlStorageProvider.java
index 13b0ac7d..5400afb0 100644
--- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/storage/MySqlStorageProvider.java
+++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/storage/MySqlStorageProvider.java
@@ -41,6 +41,8 @@ public class MySqlStorageProvider implements StorageProvider {
"SELECT quest_id, started, completed, completed_before, completion_date FROM `{prefix}quest_progress` WHERE uuid=?;";
private static final String SELECT_PLAYER_TASK_PROGRESS =
"SELECT quest_id, task_id, completed, progress, data_type FROM `{prefix}task_progress` WHERE uuid=?;";
+ private static final String SELECT_UUID_LIST =
+ "SELECT DISTINCT uuid FROM `{prefix}quest_progress`;";
private static final String SELECT_KNOWN_PLAYER_QUEST_PROGRESS =
"SELECT quest_id FROM `{prefix}quest_progress` WHERE uuid=?;";
private static final String SELECT_KNOWN_PLAYER_TASK_PROGRESS =
@@ -67,6 +69,11 @@ public class MySqlStorageProvider implements StorageProvider {
}
@Override
+ public String getName() {
+ return "mysql";
+ }
+
+ @Override
public void init() {
String address = configuration.getString("network.address", "localhost:3306");
String database = configuration.getString("network.database", "minecraft");
@@ -285,4 +292,47 @@ public class MySqlStorageProvider implements StorageProvider {
e.printStackTrace();
}
}
+
+ @Override
+ public @NotNull List<QuestProgressFile> loadAllProgressFiles() {
+ if (fault) return Collections.emptyList();
+
+ Set<UUID> uuids = new HashSet<>();
+
+ try (Connection connection = hikari.getConnection()) {
+ try (PreparedStatement ps = connection.prepareStatement(this.statementProcessor.apply(SELECT_UUID_LIST))) {
+ try (ResultSet rs = ps.executeQuery()) {
+ while (rs.next()) {
+ String uuidString = rs.getString(1);
+ try {
+ UUID uuid = UUID.fromString(uuidString);
+ uuids.add(uuid);
+ } catch (IllegalArgumentException ignored) { }
+ }
+ }
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return Collections.emptyList();
+ }
+
+ List<QuestProgressFile> files = new ArrayList<>();
+ for (UUID uuid : uuids) {
+ QuestProgressFile file = loadProgressFile(uuid);
+ if (file != null) {
+ files.add(file);
+ }
+ }
+
+ return files;
+ }
+
+ @Override
+ public void saveAllProgressFiles(List<QuestProgressFile> files) {
+ if (fault) return;
+
+ for (QuestProgressFile file : files) {
+ saveProgressFile(file.getPlayerUUID(), file);
+ }
+ }
}
diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/storage/YamlStorageProvider.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/storage/YamlStorageProvider.java
index 0a20f3cf..e6848e06 100644
--- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/storage/YamlStorageProvider.java
+++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/storage/YamlStorageProvider.java
@@ -12,6 +12,8 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
@@ -33,6 +35,11 @@ public class YamlStorageProvider implements StorageProvider {
}
@Override
+ public String getName() {
+ return "yaml";
+ }
+
+ @Override
public void init() {
File directory = new File(plugin.getDataFolder() + File.separator + "playerdata");
directory.mkdirs();
@@ -147,4 +154,44 @@ public class YamlStorageProvider implements StorageProvider {
lock.unlock();
}
}
+
+ public @NotNull List<QuestProgressFile> loadAllProgressFiles() {
+ List<QuestProgressFile> files = new ArrayList<>();
+
+ File directory = new File(plugin.getDataFolder() + File.separator + "playerdata");
+ FileVisitor<Path> fileVisitor = new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path path, BasicFileAttributes attributes) {
+ if (path.toString().endsWith(".yml")) {
+ UUID uuid;
+ try {
+ uuid = UUID.fromString(path.getFileName().toString().replace(".yml", ""));
+ } catch (IllegalArgumentException e) {
+ return FileVisitResult.CONTINUE;
+ }
+
+ QuestProgressFile file = loadProgressFile(uuid);
+ if (file != null) {
+ files.add(file);
+ }
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ };
+
+ try {
+ Files.walkFileTree(directory.toPath(), fileVisitor);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return files;
+ }
+
+ @Override
+ public void saveAllProgressFiles(List<QuestProgressFile> files) {
+ for (QuestProgressFile file : files) {
+ saveProgressFile(file.getPlayerUUID(), file);
+ }
+ }
}
diff --git a/bukkit/src/main/resources/resources/bukkit/migrate_data.yml b/bukkit/src/main/resources/resources/bukkit/migrate_data.yml
new file mode 100644
index 00000000..0c392072
--- /dev/null
+++ b/bukkit/src/main/resources/resources/bukkit/migrate_data.yml
@@ -0,0 +1,49 @@
+# This file was automatically generated by the Quests data migration tool (/q a migratedata).
+# Please read these instructions carefully.
+#
+# The Quests data migration tool is a tool that allows you to migrate your data from one storage
+# system (referred to as 'storage provider') to another. This file serves as a configuration for
+# both storage systems to migrate from.
+#
+# The 'from' section below is the configuration for the storage provider you are migrating from.
+# The 'to' section below is the configuration for the storage provider you are migrating to.
+#
+# The 'from' and 'to' sections are both required.
+#
+# When you have configured both storage providers, you must set the 'ready' flag to true.
+# The command will not work if this is not done.
+#
+# WARNING: This process should be done on a server with no players online, from your
+# server console. Unexpected behaviour or potential data corruption may occur
+# if players are online!
+#
+# One everything is configured, you can execute the migration with the following command:
+# /quests admin migratedata execute
+#
+# When the process has finished, you can remove this file. You must update your main
+# configuration file to point to the new storage provider manually.
+#
+# These instructions are also available on the wiki:
+# https://github.com/LMBishop/Quests/wiki/Data-migration-tool
+
+# Data provider to migrate from
+from:
+ provider: "yaml"
+
+# Data provider to migrate to
+to:
+ provider: "mysql"
+ database-settings:
+ network:
+ database: "minecraft"
+ username: "root"
+ password: ""
+ address: "localhost:3306"
+ connection-pool-settings:
+ maximum-pool-size: 8
+ minimum-idle: 8
+ maximum-lifetime: 1800000
+ connection-timeout: 5000
+ table-prefix: "quests_"
+
+ready: false