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 index 2f3ad0af5..1cbfcaca6 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/command/AdminMigrateCommandHandler.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/command/AdminMigrateCommandHandler.java @@ -15,6 +15,7 @@ import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; public class AdminMigrateCommandHandler implements CommandHandler { @@ -87,11 +88,11 @@ public void handle(CommandSender sender, String[] args) { } sender.sendMessage(ChatColor.GRAY + "Loading quest progress files from '" + fromProvider.getName() + "'..."); - List files = fromProvider.loadAllProgressFiles(); + List> files = fromProvider.loadAllProgressFiles(); sender.sendMessage(ChatColor.GRAY.toString() + files.size() + " files loaded."); - for (QuestProgressFile file : files) { - file.setModified(true); + for (Map.Entry file : files) { + file.getKey().setModified(true); } sender.sendMessage(ChatColor.GRAY + "Writing quest progress files to '" + toProvider.getName() + "'..."); diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/listener/PlayerJoinListener.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/listener/PlayerJoinListener.java index 13f2566a1..b4f440213 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/listener/PlayerJoinListener.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/listener/PlayerJoinListener.java @@ -41,11 +41,17 @@ public void onEvent(PlayerJoinEvent event) { plugin.getQuestCompleter().queueFullCheck(qPlayer.getQuestProgressFile()); // track first quest - if (plugin.getConfig().getBoolean("options.allow-quest-track") && plugin.getConfig().getBoolean("options.quest-autotrack")) { - for (Quest quest : plugin.getQuestManager().getQuests().values()) { - if (qPlayer.hasStartedQuest(quest)) { - qPlayer.trackQuest(quest); - break; + if (plugin.getConfig().getBoolean("options.allow-quest-track")) { + if (qPlayer.getPlayerPreferences().getTrackedQuestId() != null) { + return; + } + + if (plugin.getConfig().getBoolean("options.quest-autotrack")) { + for (Quest quest : plugin.getQuestManager().getQuests().values()) { + if (qPlayer.hasStartedQuest(quest)) { + qPlayer.trackQuest(quest); + break; + } } } } 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 f8eaefbab..93b60e573 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 @@ -19,6 +19,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -51,6 +52,11 @@ public class MySqlStorageProvider implements StorageProvider { " `progress` VARCHAR(64) NULL," + " `data_type` VARCHAR(10) NULL," + " PRIMARY KEY (`uuid`, `quest_id`, `task_id`));"; + private static final String CREATE_TABLE_PLAYER_PREFERENCES = + "CREATE TABLE IF NOT EXISTS `{prefix}player_preferences` (" + + " `uuid` VARCHAR(36) NOT NULL," + + " `tracked_quest_id` VARCHAR(50) NOT NULL," + + " PRIMARY KEY (`uuid`));"; private static final String CREATE_TABLE_DATABASE_INFORMATION = "CREATE TABLE IF NOT EXISTS `{prefix}database_information` (" + " `key` VARCHAR(255) NOT NULL," + @@ -60,6 +66,8 @@ public class MySqlStorageProvider implements StorageProvider { "SELECT quest_id, started, started_date, 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_PLAYER_PLAYER_PREFERENCES = + "SELECT tracked_quest_id FROM `{prefix}player_preferences` 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 = @@ -70,6 +78,8 @@ public class MySqlStorageProvider implements StorageProvider { "INSERT INTO `{prefix}quest_progress` (uuid, quest_id, started, started_date, completed, completed_before, completion_date) VALUES (?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE started=?, started_date=?, completed=?, completed_before=?, completion_date=?"; private static final String WRITE_PLAYER_TASK_PROGRESS = "INSERT INTO `{prefix}task_progress` (uuid, quest_id, task_id, completed, progress, data_type) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE completed=?, progress=?, data_type=?"; + private static final String WRITE_PLAYER_PLAYER_PREFERENCES = + "INSERT INTO `{prefix}player_preferences` (uuid, tracked_quest_id) VALUES (?, ?) ON DUPLICATE KEY UPDATE tracked_quest_id=?"; private final ConfigurationSection configuration; private final BukkitQuestsPlugin plugin; @@ -134,6 +144,7 @@ public void init() { plugin.getQuestsLogger().debug("Creating default tables"); s.addBatch(this.statementProcessor.apply(CREATE_TABLE_QUEST_PROGRESS)); s.addBatch(this.statementProcessor.apply(CREATE_TABLE_TASK_PROGRESS)); + s.addBatch(this.statementProcessor.apply(CREATE_TABLE_PLAYER_PREFERENCES)); s.addBatch(this.statementProcessor.apply(CREATE_TABLE_DATABASE_INFORMATION)); s.executeBatch(); @@ -158,7 +169,7 @@ public void shutdown() { @Override @Nullable - public QuestProgressFile loadProgressFile(@NotNull UUID uuid) { + public Map.Entry loadProgressFile(@NotNull UUID uuid) { Objects.requireNonNull(uuid, "uuid cannot be null"); if (fault) return null; @@ -166,6 +177,8 @@ public QuestProgressFile loadProgressFile(@NotNull UUID uuid) { boolean validateQuests = plugin.getQuestsConfig().getBoolean("options.verify-quest-exists-on-load", true); QuestProgressFile questProgressFile = new QuestProgressFile(uuid, plugin); + String trackedQuestId = null; + try (Connection connection = hikari.getConnection()) { plugin.getQuestsLogger().debug("Querying player " + uuid); Map questProgressMap = new HashMap<>(); @@ -242,22 +255,33 @@ public QuestProgressFile loadProgressFile(@NotNull UUID uuid) { for (QuestProgress questProgress : questProgressMap.values()) { questProgressFile.addQuestProgress(questProgress); } + try (PreparedStatement ps = connection.prepareStatement(this.statementProcessor.apply(SELECT_PLAYER_PLAYER_PREFERENCES))) { + ps.setString(1, uuid.toString()); + + try (ResultSet rs = ps.executeQuery()) { + if (rs.first()) { + trackedQuestId = rs.getString("tracked_quest_id"); + } + } + } } catch (SQLException e) { e.printStackTrace(); return null; } - return questProgressFile; + + return new AbstractMap.SimpleImmutableEntry<>(questProgressFile, trackedQuestId); } @Override - public boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile) { + public boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile, @Nullable String trackedQuestId) { Objects.requireNonNull(uuid, "uuid cannot be null"); Objects.requireNonNull(questProgressFile, "questProgressFile cannot be null"); if (fault) return false; try (Connection connection = hikari.getConnection()) { try (PreparedStatement writeQuestProgress = connection.prepareStatement(this.statementProcessor.apply(WRITE_PLAYER_QUEST_PROGRESS)); - PreparedStatement writeTaskProgress = connection.prepareStatement(this.statementProcessor.apply(WRITE_PLAYER_TASK_PROGRESS))) { + PreparedStatement writeTaskProgress = connection.prepareStatement(this.statementProcessor.apply(WRITE_PLAYER_TASK_PROGRESS)); + PreparedStatement writePlayerPreferences = connection.prepareStatement(this.statementProcessor.apply(WRITE_PLAYER_PLAYER_PREFERENCES))) { List questProgressValues = new ArrayList<>(questProgressFile.getAllQuestProgress()); for (QuestProgress questProgress : questProgressValues) { @@ -318,8 +342,12 @@ public boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile q } } + writePlayerPreferences.setString(1, uuid.toString()); + writePlayerPreferences.setString(2, trackedQuestId); + writeQuestProgress.executeBatch(); writeTaskProgress.executeBatch(); + writePlayerPreferences.executeUpdate(); } return true; } catch (SQLException e) { @@ -329,7 +357,7 @@ public boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile q } @Override - public @NotNull List loadAllProgressFiles() { + public @NotNull List> loadAllProgressFiles() { if (fault) return Collections.emptyList(); Set uuids = new HashSet<>(); @@ -351,9 +379,9 @@ public boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile q return Collections.emptyList(); } - List files = new ArrayList<>(); + List> files = new ArrayList<>(); for (UUID uuid : uuids) { - QuestProgressFile file = loadProgressFile(uuid); + Map.Entry file = loadProgressFile(uuid); if (file != null) { files.add(file); } @@ -363,11 +391,11 @@ public boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile q } @Override - public void saveAllProgressFiles(List files) { + public void saveAllProgressFiles(List> files) { if (fault) return; - for (QuestProgressFile file : files) { - saveProgressFile(file.getPlayerUUID(), file); + for (Map.Entry file : files) { + saveProgressFile(file.getKey().getPlayerUUID(), file.getKey(), file.getValue()); } } 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 e74212af1..9ee9398e4 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 @@ -6,6 +6,7 @@ import com.leonardobishop.quests.common.player.questprogressfile.TaskProgress; import com.leonardobishop.quests.common.quest.Quest; import com.leonardobishop.quests.common.storage.StorageProvider; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -18,6 +19,7 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -59,14 +61,16 @@ public void shutdown() { // no impl } - public @Nullable QuestProgressFile loadProgressFile(@NotNull UUID uuid) { + public @Nullable Map.Entry loadProgressFile(@NotNull UUID uuid) { Objects.requireNonNull(uuid, "uuid cannot be null"); ReentrantLock lock = lock(uuid); Map presentQuests = new HashMap<>(plugin.getQuestManager().getQuests()); boolean validateQuests = plugin.getQuestsConfig().getBoolean("options.verify-quest-exists-on-load", true); - + QuestProgressFile questProgressFile = new QuestProgressFile(uuid, plugin); + String trackedQuestId = null; + try { File directory = new File(plugin.getDataFolder() + File.separator + "playerdata"); if (directory.exists() && directory.isDirectory()) { @@ -101,6 +105,11 @@ public void shutdown() { questProgressFile.addQuestProgress(questProgress); } } + + final ConfigurationSection preferencesSection = data.getConfigurationSection("player-preferences"); + if (preferencesSection != null) { + trackedQuestId = preferencesSection.getString("tracked-quest-id"); + } } else { plugin.getQuestsLogger().debug("Player " + uuid + " does not have a quest progress file."); } @@ -112,10 +121,10 @@ public void shutdown() { lock.unlock(); } - return questProgressFile; + return new AbstractMap.SimpleEntry<>(questProgressFile, trackedQuestId); } - public boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile) { + public boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile, @Nullable String trackedQuestId) { Objects.requireNonNull(uuid, "uuid cannot be null"); Objects.requireNonNull(questProgressFile, "questProgressFile cannot be null"); @@ -152,6 +161,9 @@ public boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile q } } + // Player preferences + data.set("player-preferences.tracked-quest-id", trackedQuestId); + plugin.getQuestsLogger().debug("Writing player " + uuid + " to disk."); try { data.save(file); @@ -165,8 +177,8 @@ public boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile q } } - public @NotNull List loadAllProgressFiles() { - List files = new ArrayList<>(); + public @NotNull List> loadAllProgressFiles() { + List> files = new ArrayList<>(); File directory = new File(plugin.getDataFolder() + File.separator + "playerdata"); FileVisitor fileVisitor = new SimpleFileVisitor() { @@ -180,7 +192,7 @@ public FileVisitResult visitFile(Path path, BasicFileAttributes attributes) { return FileVisitResult.CONTINUE; } - QuestProgressFile file = loadProgressFile(uuid); + Map.Entry file = loadProgressFile(uuid); if (file != null) { files.add(file); } @@ -199,9 +211,9 @@ public FileVisitResult visitFile(Path path, BasicFileAttributes attributes) { } @Override - public void saveAllProgressFiles(List files) { - for (QuestProgressFile file : files) { - saveProgressFile(file.getPlayerUUID(), file); + public void saveAllProgressFiles(List> files) { + for (Map.Entry file : files) { + saveProgressFile(file.getKey().getPlayerUUID(), file.getKey(), file.getValue()); } } diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CommandUtils.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CommandUtils.java index b17c63251..60a29e291 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CommandUtils.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CommandUtils.java @@ -173,7 +173,7 @@ public static void useOtherPlayer(CommandSender sender, String name, BukkitQuest public static void doSafeSave(QPlayer qPlayer, QuestProgressFile questProgressFile, BukkitQuestsPlugin plugin) { if (Bukkit.getPlayer(qPlayer.getPlayerUUID()) == null) { plugin.getScheduler().doAsync(() -> { - plugin.getPlayerManager().savePlayerSync(qPlayer.getPlayerUUID(), questProgressFile); + plugin.getPlayerManager().savePlayerSync(qPlayer.getPlayerUUID(), questProgressFile, qPlayer.getPlayerPreferences()); plugin.getScheduler().doSync(() -> { if (Bukkit.getPlayer(qPlayer.getPlayerUUID()) == null) { plugin.getPlayerManager().dropPlayer(qPlayer.getPlayerUUID()); diff --git a/common/src/main/java/com/leonardobishop/quests/common/player/QPlayerManager.java b/common/src/main/java/com/leonardobishop/quests/common/player/QPlayerManager.java index 1475c44a8..4c75a5544 100644 --- a/common/src/main/java/com/leonardobishop/quests/common/player/QPlayerManager.java +++ b/common/src/main/java/com/leonardobishop/quests/common/player/QPlayerManager.java @@ -76,7 +76,7 @@ public CompletableFuture savePlayer(@NotNull UUID uuid) { QPlayer qPlayer = getPlayer(uuid); if (qPlayer == null) return CompletableFuture.completedFuture(null); - return savePlayer(uuid, qPlayer.getQuestProgressFile()); + return savePlayer(uuid, qPlayer.getQuestProgressFile(), qPlayer.getPlayerPreferences()); } /** @@ -87,7 +87,7 @@ public CompletableFuture savePlayer(@NotNull UUID uuid) { * @param originalProgressFile the quest progress file to associate with and save * @return completable future */ - public CompletableFuture savePlayer(@NotNull UUID uuid, @NotNull QuestProgressFile originalProgressFile) { + public CompletableFuture savePlayer(@NotNull UUID uuid, @NotNull QuestProgressFile originalProgressFile, @NotNull QPlayerPreferences preferences) { Objects.requireNonNull(uuid, "uuid cannot be null"); Objects.requireNonNull(originalProgressFile, "originalProgressFile cannot be null"); @@ -96,7 +96,7 @@ public CompletableFuture savePlayer(@NotNull UUID uuid, @NotNull QuestProg QuestProgressFile clonedProgressFile = new QuestProgressFile(originalProgressFile); originalProgressFile.resetModified(); plugin.getScheduler().doAsync(() -> { - save(uuid, clonedProgressFile); + save(uuid, clonedProgressFile, preferences); future.complete(null); }); @@ -114,7 +114,7 @@ public void savePlayerSync(@NotNull UUID uuid) { QPlayer qPlayer = getPlayer(uuid); if (qPlayer == null) return; - savePlayerSync(uuid, qPlayer.getQuestProgressFile()); + savePlayerSync(uuid, qPlayer.getQuestProgressFile(), qPlayer.getPlayerPreferences()); } /** @@ -124,16 +124,16 @@ public void savePlayerSync(@NotNull UUID uuid) { * @param uuid the uuid of the player * @param questProgressFile the quest progress file to associate with and save */ - public void savePlayerSync(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile) { - save(uuid, questProgressFile); + public void savePlayerSync(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile, @NotNull QPlayerPreferences preferences) { + save(uuid, questProgressFile, preferences); } - private void save(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile) { + private void save(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile, @NotNull QPlayerPreferences preferences) { Objects.requireNonNull(uuid, "uuid cannot be null"); Objects.requireNonNull(questProgressFile, "questProgressFile cannot be null"); plugin.getQuestsLogger().debug("Saving player " + uuid + "..."); - if (storageProvider.saveProgressFile(uuid, questProgressFile)) { + if (storageProvider.saveProgressFile(uuid, questProgressFile, preferences.getTrackedQuestId())) { plugin.getQuestsLogger().debug("Quest progress file saved for player " + uuid + "."); } else { plugin.getQuestsLogger().severe("Failed to save player " + uuid + "!"); @@ -173,13 +173,13 @@ public CompletableFuture loadPlayer(UUID uuid) { CompletableFuture future = new CompletableFuture<>(); plugin.getScheduler().doAsync(() -> { - QuestProgressFile questProgressFile = storageProvider.loadProgressFile(uuid); - if (questProgressFile == null) { + Map.Entry playerData = storageProvider.loadProgressFile(uuid); + if (playerData == null) { plugin.getQuestsLogger().debug("A problem occurred trying loading player " + uuid + "; quest progress file is null."); future.complete(null); return; } - QPlayer qPlayer = new QPlayer(plugin, uuid, new QPlayerPreferences(null), questProgressFile, activeQuestController); + QPlayer qPlayer = new QPlayer(plugin, uuid, new QPlayerPreferences(playerData.getValue()), playerData.getKey(), activeQuestController); qPlayers.computeIfAbsent(uuid, s -> qPlayer); plugin.getQuestsLogger().debug("Quest progress file loaded for player " + uuid + "."); future.complete(qPlayer); diff --git a/common/src/main/java/com/leonardobishop/quests/common/storage/StorageProvider.java b/common/src/main/java/com/leonardobishop/quests/common/storage/StorageProvider.java index ba0ee8c88..45cf36bcd 100644 --- a/common/src/main/java/com/leonardobishop/quests/common/storage/StorageProvider.java +++ b/common/src/main/java/com/leonardobishop/quests/common/storage/StorageProvider.java @@ -5,6 +5,7 @@ import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Map; import java.util.UUID; /** @@ -25,7 +26,7 @@ public interface StorageProvider { * @param uuid the UUID to load * @return {@link QuestProgressFile} or null */ - @Nullable QuestProgressFile loadProgressFile(@NotNull UUID uuid); + @Nullable Map.Entry loadProgressFile(@NotNull UUID uuid); /** * Save a QuestProgressFile to the data source with a specific UUID @@ -33,21 +34,21 @@ public interface StorageProvider { * @param uuid the uuid to match the file to * @param questProgressFile the file to save */ - boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile); + boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile, @Nullable String trackedQuestId); /** * Load all QuestProgressFiles * * @return {@link List} */ - @NotNull List loadAllProgressFiles(); + @NotNull List> loadAllProgressFiles(); /** * Save a list of QuestProgressFiles * * @param files the list of QuestProgressFile to save **/ - void saveAllProgressFiles(List files); + void saveAllProgressFiles(List> files); /** * Whether this provider is 'similar' to another one.