From 343aed44a398911495c05d91ff9d18857eda3329 Mon Sep 17 00:00:00 2001 From: LMBishop <13875753+LMBishop@users.noreply.github.com> Date: Wed, 2 Jun 2021 23:35:56 +0100 Subject: Add SQL options --- src/main/resources/config.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src/main/resources') diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 0386fa98..b4c94332 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -234,6 +234,34 @@ options: global-task-configuration-override: false # Whether or not the global display configuration will override per-quest display settins global-quest-display-configuration-override: false + # Storage options + storage: + # Either 'yaml' (flatfile) or 'mysql' (network) + provider: "yaml" + # The following is only applicable for database storage providers (e.g. mysql) + database-settings: + # The name of the database. This database should already exist! + database-name: "minecraft" + network: + username: "root" + password: "" + address: "localhost" + port: 3306 + # This plugin uses 'HikariCP' for connection management, the pooling configuration can be changed here + connection-pool-settings: + # The maximum number of connections to keep open with the database (def=8) + maximum-pool-size: 8 + # The minimum number of connections to keep open with the database (def=8) + minimum-pool-size: 8 + # The maximum time (in milliseconds) to keep a single connection open (def=1800000 - 30 min) + connection-lifetime: 1800000 + # The time (in milliseconds) to ping the database (0 to disable, def=0) + heartbeat-time: 0 + # The time (in milliseconds) the plugin will wait for a response by the database (def=0) + timeout: 5000 + # The prefix each table will use + table-prefix: "quests_" + # This switches up the entire quest system. # By enabling daily-quests, players will no longer be presented with the standard Quest GUI. -- cgit v1.2.3-70-g09d2 From e5a28b13be1e71eda33a1e3f5e8e56e5dad6c063 Mon Sep 17 00:00:00 2001 From: LMBishop <13875753+LMBishop@users.noreply.github.com> Date: Thu, 3 Jun 2021 18:04:28 +0100 Subject: Initial SQL storage implementation --- build.gradle | 2 + .../java/com/leonardobishop/quests/Quests.java | 5 +- .../quests/player/QPlayerManager.java | 4 +- .../quests/storage/MySqlStorageProvider.java | 258 +++++++++++++++++++++ src/main/resources/config.yml | 18 +- 5 files changed, 274 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java (limited to 'src/main/resources') diff --git a/build.gradle b/build.gradle index d9f61c7b..a6a0f745 100644 --- a/build.gradle +++ b/build.gradle @@ -85,6 +85,8 @@ dependencies { implementation 'org.bstats:bstats-bukkit-lite:1.8' // HikariCP implementation 'com.zaxxer:HikariCP:4.0.3' + // slf4j + implementation 'org.slf4j:slf4j-simple:1.7.30' compileOnly fileTree(dir: 'libs', includes: ['*.jar']) } diff --git a/src/main/java/com/leonardobishop/quests/Quests.java b/src/main/java/com/leonardobishop/quests/Quests.java index cab89f6a..7d74283f 100644 --- a/src/main/java/com/leonardobishop/quests/Quests.java +++ b/src/main/java/com/leonardobishop/quests/Quests.java @@ -166,13 +166,14 @@ public class Quests extends JavaPlugin { questsLogger = new QuestsLogger(this, QuestsLogger.LoggingLevel.INFO); questCompleter = new QuestCompleter(this); + this.generateConfigurations(); + this.setupVersionSpecific(); + taskTypeManager = new TaskTypeManager(this); questManager = new QuestManager(this); qPlayerManager = new QPlayerManager(this); menuController = new MenuController(this); - this.generateConfigurations(); - this.setupVersionSpecific(); super.getCommand("quests").setExecutor(new QuestsCommand(this)); Bukkit.getPluginManager().registerEvents(new PlayerJoinListener(this), this); diff --git a/src/main/java/com/leonardobishop/quests/player/QPlayerManager.java b/src/main/java/com/leonardobishop/quests/player/QPlayerManager.java index f521d2de..bd7ccf37 100644 --- a/src/main/java/com/leonardobishop/quests/player/QPlayerManager.java +++ b/src/main/java/com/leonardobishop/quests/player/QPlayerManager.java @@ -6,6 +6,7 @@ import com.leonardobishop.quests.player.questprogressfile.QPlayerPreferences; import com.leonardobishop.quests.player.questprogressfile.QuestProgress; import com.leonardobishop.quests.player.questprogressfile.QuestProgressFile; import com.leonardobishop.quests.player.questprogressfile.TaskProgress; +import com.leonardobishop.quests.storage.MySqlStorageProvider; import com.leonardobishop.quests.storage.StorageProvider; import com.leonardobishop.quests.storage.YamlStorageProvider; import com.leonardobishop.quests.util.Options; @@ -24,7 +25,8 @@ public class QPlayerManager { private StorageProvider storageProvider; public QPlayerManager(Quests plugin) { - this.storageProvider = new YamlStorageProvider(plugin); + this.storageProvider = new MySqlStorageProvider(plugin, plugin.getConfig().getConfigurationSection("options.storage.database-settings")); + storageProvider.init(); this.plugin = plugin; } diff --git a/src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java b/src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java new file mode 100644 index 00000000..83147a19 --- /dev/null +++ b/src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java @@ -0,0 +1,258 @@ +package com.leonardobishop.quests.storage; + +import com.leonardobishop.quests.Quests; +import com.leonardobishop.quests.player.questprogressfile.QuestProgress; +import com.leonardobishop.quests.player.questprogressfile.QuestProgressFile; +import com.leonardobishop.quests.player.questprogressfile.TaskProgress; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.MemorySection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; + +public class MySqlStorageProvider implements StorageProvider { + + private static final String CREATE_TABLE_QUEST_PROGRESS = + "CREATE TABLE IF NOT EXISTS `{prefix}quest_progress` (" + + " `id` INT NOT NULL AUTO_INCREMENT," + + " `uuid` VARCHAR(36) NOT NULL," + + " `quest_id` VARCHAR(50) NOT NULL," + + " `started` BOOL NOT NULL," + + " `completed` BOOL NOT NULL," + + " `completed_before` BOOL NOT NULL," + + " `completion_date` BIGINT NOT NULL," + + " PRIMARY KEY (`id`));"; + private static final String CREATE_TABLE_TASK_PROGRESS = + "CREATE TABLE IF NOT EXISTS `{prefix}task_progress` (" + + " `id` INT NOT NULL AUTO_INCREMENT," + + " `uuid` VARCHAR(36) NOT NULL," + + " `quest_id` VARCHAR(50) NOT NULL," + + " `task_id` VARCHAR(50) NOT NULL," + + " `completed` BOOL NOT NULL," + + " `progress` VARCHAR(64) NULL," + + " PRIMARY KEY (`id`));"; + private static final String SELECT_PLAYER_QUEST_PROGRESS = + "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 FROM `{prefix}task_progress` WHERE uuid=?;"; + 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 = + "SELECT quest_id, task_id FROM `{prefix}task_progress` WHERE uuid=?;"; + private static final String INSERT_PLAYER_QUEST_PROGRESS = + "INSERT INTO `{prefix}quest_progress` (uuid, quest_id, started, completed, completed_before, completion_date) VALUES (?,?,?,?,?,?)"; + private static final String INSERT_PLAYER_TASK_PROGRESS = + "INSERT INTO `{prefix}task_progress` (uuid, quest_id, task_id, completed, progress) VALUES (?,?,?,?,?)"; + private static final String UPDATE_PLAYER_QUEST_PROGRESS = + "UPDATE `{prefix}quest_progress` SET started=?, completed=?, completed_before=?, completion_date=? WHERE uuid=? AND quest_id=?"; + private static final String UPDATE_PLAYER_TASK_PROGRESS = + "UPDATE `{prefix}task_progress` SET completed=?, progress=? WHERE uuid=? AND quest_id=? AND task_id=?"; + + private final HikariDataSource hikari; + private final String prefix; + private final Quests plugin; + private final Function statementProcessor; + + public MySqlStorageProvider(Quests plugin, ConfigurationSection configuration) { + this.plugin = plugin; + if (configuration == null) { + configuration = new YamlConfiguration(); + } + + String address = configuration.getString("network.address", "localhost:3306"); + String database = configuration.getString("network.database", "minecraft"); + String url = "jdbc:mysql://" + address + "/" + database; + + HikariConfig config = new HikariConfig(); + config.setPoolName("quests-hikari"); + + config.setUsername(configuration.getString("network.username", "root")); + config.setPassword(configuration.getString("network.password", "")); + config.setJdbcUrl(url); + config.setMaximumPoolSize(configuration.getInt("connection-pool-settings.maximum-pool-size", 8)); + config.setMinimumIdle(configuration.getInt("connection-pool-settings.minimum-idle", 8)); + config.setMaxLifetime(configuration.getInt("connection-pool-settings.maximum-lifetime", 1800000)); + config.setConnectionTimeout(configuration.getInt("connection-pool-settings.connection-timeout", 5000)); + + config.addDataSourceProperty("cachePrepStmts", true); + config.addDataSourceProperty("prepStmtCacheSize", 250); + config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048); + config.addDataSourceProperty("useServerPrepStmts", true); + config.addDataSourceProperty("useLocalSessionState", true); + config.addDataSourceProperty("rewriteBatchedStatements", true); + config.addDataSourceProperty("cacheResultSetMetadata", true); + config.addDataSourceProperty("cacheServerConfiguration", true); + config.addDataSourceProperty("elideSetAutoCommits", true); + config.addDataSourceProperty("maintainTimeStats", false); + + this.hikari = new HikariDataSource(config); + this.prefix = configuration.getString("database-settings.table-prefix", "quests_"); + this.statementProcessor = s -> s.replace("{prefix}", prefix); + } + + @Override + public void init() { + try (Connection connection = hikari.getConnection()) { + try (Statement s = connection.createStatement()) { + 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.executeBatch(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + @Override + public QuestProgressFile loadProgressFile(UUID uuid) { + QuestProgressFile questProgressFile = new QuestProgressFile(uuid, plugin); + try (Connection connection = hikari.getConnection()) { + plugin.getQuestsLogger().debug("Querying player " + uuid); + Map questProgressMap = new HashMap<>(); + try (PreparedStatement ps = connection.prepareStatement(this.statementProcessor.apply(SELECT_PLAYER_QUEST_PROGRESS))) { + ps.setString(1, uuid.toString()); + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + String questId = rs.getString(1); + boolean started = rs.getBoolean(2); + boolean completed = rs.getBoolean(3); + boolean completedBefore = rs.getBoolean(4); + long completionDate = rs.getLong(5); + + QuestProgress questProgress = new QuestProgress(plugin, questId, completed, completedBefore, completionDate, uuid, started); + questProgressMap.put(questId, questProgress); + } + } + } + try (PreparedStatement ps = connection.prepareStatement(this.statementProcessor.apply(SELECT_PLAYER_TASK_PROGRESS))) { + ps.setString(1, uuid.toString()); + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + String questId = rs.getString(1); + String taskId = rs.getString(2); + boolean completed = rs.getBoolean(3); + Object progress = rs.getObject(4); + + QuestProgress linkedQuestProgress = questProgressMap.get(questId); + if (linkedQuestProgress == null) continue; // lost quest progress ? + TaskProgress questProgress = new TaskProgress(linkedQuestProgress, taskId, progress, uuid, completed); + linkedQuestProgress.addTaskProgress(questProgress); + } + } + } + for (QuestProgress questProgress : questProgressMap.values()) { + questProgressFile.addQuestProgress(questProgress); + } + } catch (SQLException e) { + plugin.getQuestsLogger().severe("Failed to load player: " + uuid + "!"); + e.printStackTrace(); + } + return questProgressFile; + } + + @Override + public void saveProgressFile(UUID uuid, QuestProgressFile questProgressFile) { + try (Connection connection = hikari.getConnection()) { + plugin.getQuestsLogger().debug("Saving player " + uuid); + List knownQuestIds = new ArrayList<>(); + Map> knownTaskIds = new HashMap<>(); + try (PreparedStatement ps = connection.prepareStatement(this.statementProcessor.apply(SELECT_KNOWN_PLAYER_QUEST_PROGRESS))) { + ps.setString(1, uuid.toString()); + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + knownQuestIds.add(rs.getString(0)); + } + } + } + try (PreparedStatement ps = connection.prepareStatement(this.statementProcessor.apply(SELECT_KNOWN_PLAYER_TASK_PROGRESS))) { + ps.setString(1, uuid.toString()); + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + String questId = rs.getString(0); + String taskId = rs.getString(1); + + knownTaskIds.putIfAbsent(questId, new ArrayList<>()); + knownTaskIds.get(questId).add(taskId); + } + } + } + + try (PreparedStatement insertQuestProgress = connection.prepareStatement(this.statementProcessor.apply(INSERT_PLAYER_QUEST_PROGRESS)); + PreparedStatement insertTaskProgress = connection.prepareStatement(this.statementProcessor.apply(INSERT_PLAYER_TASK_PROGRESS)); + PreparedStatement updateQuestProgress = connection.prepareStatement(this.statementProcessor.apply(UPDATE_PLAYER_QUEST_PROGRESS)); + PreparedStatement updateTaskProgress = connection.prepareStatement(this.statementProcessor.apply(UPDATE_PLAYER_TASK_PROGRESS))) { + + List questProgressValues = new ArrayList<>(questProgressFile.getAllQuestProgress()); + for (QuestProgress questProgress : questProgressValues) { + String questId = questProgress.getQuestId(); + if (knownQuestIds.contains(questId)) { + updateQuestProgress.setBoolean(1, questProgress.isStarted()); + updateQuestProgress.setBoolean(2, questProgress.isCompleted()); + updateQuestProgress.setBoolean(3, questProgress.isCompletedBefore()); + updateQuestProgress.setLong(4, questProgress.getCompletionDate()); + updateQuestProgress.setString(5, uuid.toString()); + updateQuestProgress.setString(6, questId); + updateQuestProgress.addBatch(); + } else { + insertQuestProgress.setString(1, uuid.toString()); + insertQuestProgress.setString(2, questProgress.getQuestId()); + insertQuestProgress.setBoolean(3, questProgress.isStarted()); + insertQuestProgress.setBoolean(4, questProgress.isCompleted()); + insertQuestProgress.setBoolean(5, questProgress.isCompletedBefore()); + insertQuestProgress.setLong(6, questProgress.getCompletionDate()); + insertQuestProgress.addBatch(); + } + List taskIds = knownTaskIds.getOrDefault(questProgress.getQuestId(), Collections.emptyList()); + for (TaskProgress taskProgress : questProgress.getTaskProgress()) { + if (taskIds.contains(taskProgress.getTaskId())) { + updateTaskProgress.setBoolean(1, taskProgress.isCompleted()); + updateTaskProgress.setObject(2, taskProgress.getProgress()); + updateTaskProgress.setString(3, uuid.toString()); + updateTaskProgress.setString(4, questProgress.getQuestId()); + updateTaskProgress.setString(5, taskProgress.getTaskId()); + updateTaskProgress.addBatch(); + } else { + insertTaskProgress.setString(1, uuid.toString()); + insertTaskProgress.setString(2, questProgress.getQuestId()); + insertTaskProgress.setString(3, taskProgress.getTaskId()); + insertTaskProgress.setBoolean(4, taskProgress.isCompleted()); + insertTaskProgress.setObject(5, taskProgress.getProgress()); + insertTaskProgress.addBatch(); + } + } + } + + System.out.println(insertQuestProgress); + insertQuestProgress.executeBatch(); + System.out.println(insertTaskProgress); + insertTaskProgress.executeBatch(); + System.out.println(updateQuestProgress); + updateQuestProgress.executeBatch(); + System.out.println(updateTaskProgress); + updateTaskProgress.executeBatch(); + } + } catch (SQLException e) { + plugin.getQuestsLogger().severe("Failed to save player: " + uuid + "!"); + e.printStackTrace(); + } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b4c94332..b2ab5871 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -240,25 +240,23 @@ options: provider: "yaml" # The following is only applicable for database storage providers (e.g. mysql) database-settings: - # The name of the database. This database should already exist! - database-name: "minecraft" network: + # The name of the database. This database should already exist! + database: "minecraft" username: "root" password: "" - address: "localhost" - port: 3306 + # Address should be in the format ip:port (just like the game itself) + address: "localhost:3306" # This plugin uses 'HikariCP' for connection management, the pooling configuration can be changed here connection-pool-settings: # The maximum number of connections to keep open with the database (def=8) maximum-pool-size: 8 # The minimum number of connections to keep open with the database (def=8) - minimum-pool-size: 8 + minimum-idle: 8 # The maximum time (in milliseconds) to keep a single connection open (def=1800000 - 30 min) - connection-lifetime: 1800000 - # The time (in milliseconds) to ping the database (0 to disable, def=0) - heartbeat-time: 0 - # The time (in milliseconds) the plugin will wait for a response by the database (def=0) - timeout: 5000 + maximum-lifetime: 1800000 + # The time (in milliseconds) the plugin will wait for a response by the database (def=500) + connection-timeout: 5000 # The prefix each table will use table-prefix: "quests_" -- cgit v1.2.3-70-g09d2 From bf1a2b93a512ca7aa8208191d9db49be731ec280 Mon Sep 17 00:00:00 2001 From: LMBishop <13875753+LMBishop@users.noreply.github.com> Date: Sat, 5 Jun 2021 22:01:03 +0100 Subject: Remove clean option since it is stupid --- src/main/java/com/leonardobishop/quests/Quests.java | 6 ------ .../leonardobishop/quests/listener/PlayerJoinListener.java | 7 ------- .../quests/player/questprogressfile/QuestProgressFile.java | 4 ++++ .../leonardobishop/quests/storage/MySqlStorageProvider.java | 2 ++ .../leonardobishop/quests/storage/YamlStorageProvider.java | 2 +- src/main/java/com/leonardobishop/quests/util/Options.java | 3 +-- src/main/resources/config.yml | 12 ++++++------ 7 files changed, 14 insertions(+), 22 deletions(-) (limited to 'src/main/resources') diff --git a/src/main/java/com/leonardobishop/quests/Quests.java b/src/main/java/com/leonardobishop/quests/Quests.java index 21cd8a6e..bfbc0e60 100644 --- a/src/main/java/com/leonardobishop/quests/Quests.java +++ b/src/main/java/com/leonardobishop/quests/Quests.java @@ -248,12 +248,6 @@ public class Quests extends JavaPlugin { taskTypeManager.closeRegistrations(); reloadQuests(); -// if (!questsConfigLoader.getBrokenFiles().isEmpty()) { -// this.getQuestsLogger().severe("Quests has failed to load the following files:"); -// for (Map.Entry entry : questsConfigLoader.getBrokenFiles().entrySet()) { -// this.getQuestsLogger().severe(" - " + entry.getKey() + ": " + entry.getValue().getMessage()); -// } -// } for (Player player : Bukkit.getOnlinePlayers()) { qPlayerManager.loadPlayer(player.getUniqueId()); diff --git a/src/main/java/com/leonardobishop/quests/listener/PlayerJoinListener.java b/src/main/java/com/leonardobishop/quests/listener/PlayerJoinListener.java index fcee6b40..5cdb465e 100644 --- a/src/main/java/com/leonardobishop/quests/listener/PlayerJoinListener.java +++ b/src/main/java/com/leonardobishop/quests/listener/PlayerJoinListener.java @@ -30,13 +30,6 @@ public class PlayerJoinListener implements Listener { public void onEvent(PlayerJoinEvent event) { UUID playerUuid = event.getPlayer().getUniqueId(); plugin.getPlayerManager().loadPlayer(playerUuid); - if (Options.SOFT_CLEAN_QUESTSPROGRESSFILE_ON_JOIN.getBooleanValue()) { - QPlayer qPlayer = plugin.getPlayerManager().getPlayer(playerUuid); - qPlayer.getQuestProgressFile().clean(); - if (Options.PUSH_SOFT_CLEAN_TO_DISK.getBooleanValue()) { - plugin.getPlayerManager().savePlayer(playerUuid, qPlayer.getQuestProgressFile()); - } - } if (plugin.getDescription().getVersion().contains("beta") && event.getPlayer().hasPermission("quests.admin")) { event.getPlayer().sendMessage(Messages.BETA_REMINDER.getMessage()); } diff --git a/src/main/java/com/leonardobishop/quests/player/questprogressfile/QuestProgressFile.java b/src/main/java/com/leonardobishop/quests/player/questprogressfile/QuestProgressFile.java index ccdb1db1..3f6d9900 100644 --- a/src/main/java/com/leonardobishop/quests/player/questprogressfile/QuestProgressFile.java +++ b/src/main/java/com/leonardobishop/quests/player/questprogressfile/QuestProgressFile.java @@ -4,6 +4,7 @@ import com.leonardobishop.quests.Quests; import com.leonardobishop.quests.player.QPlayer; import com.leonardobishop.quests.quest.Quest; import com.leonardobishop.quests.quest.Task; +import com.leonardobishop.quests.util.Options; import org.bukkit.Bukkit; import org.bukkit.configuration.file.YamlConfiguration; @@ -40,6 +41,9 @@ public class QuestProgressFile { } public void addQuestProgress(QuestProgress questProgress) { + if (Options.VERIFY_QUEST_EXISTS_ON_LOAD.getBooleanValue(true) && plugin.getQuestManager().getQuestById(questProgress.getQuestId()) == null) { + return; + } this.questProgress.put(questProgress.getQuestId(), questProgress); } diff --git a/src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java b/src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java index bac0b5c9..c2b30734 100644 --- a/src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java +++ b/src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java @@ -4,6 +4,7 @@ import com.leonardobishop.quests.Quests; import com.leonardobishop.quests.player.questprogressfile.QuestProgress; import com.leonardobishop.quests.player.questprogressfile.QuestProgressFile; import com.leonardobishop.quests.player.questprogressfile.TaskProgress; +import com.leonardobishop.quests.util.Options; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.bukkit.configuration.ConfigurationSection; @@ -17,6 +18,7 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.UUID; diff --git a/src/main/java/com/leonardobishop/quests/storage/YamlStorageProvider.java b/src/main/java/com/leonardobishop/quests/storage/YamlStorageProvider.java index 4c803baa..8ba30a4e 100644 --- a/src/main/java/com/leonardobishop/quests/storage/YamlStorageProvider.java +++ b/src/main/java/com/leonardobishop/quests/storage/YamlStorageProvider.java @@ -108,8 +108,8 @@ public class YamlStorageProvider implements StorageProvider { } YamlConfiguration data = YamlConfiguration.loadConfiguration(file); - data.set("quest-progress", null); for (QuestProgress questProgress : questProgressValues) { + if (!questProgress.isModified()) continue; data.set("quest-progress." + questProgress.getQuestId() + ".started", questProgress.isStarted()); data.set("quest-progress." + questProgress.getQuestId() + ".completed", questProgress.isCompleted()); data.set("quest-progress." + questProgress.getQuestId() + ".completed-before", questProgress.isCompletedBefore()); diff --git a/src/main/java/com/leonardobishop/quests/util/Options.java b/src/main/java/com/leonardobishop/quests/util/Options.java index 34922f18..0ad0a41d 100644 --- a/src/main/java/com/leonardobishop/quests/util/Options.java +++ b/src/main/java/com/leonardobishop/quests/util/Options.java @@ -25,8 +25,7 @@ public enum Options { GUITITLE_QUEST_CANCEL("options.guinames.quest-cancel"), ALLOW_QUEST_CANCEL("options.allow-quest-cancel"), ALLOW_QUEST_TRACK("options.allow-quest-track"), - SOFT_CLEAN_QUESTSPROGRESSFILE_ON_JOIN("options.soft-clean-questsprogressfile-on-join"), - PUSH_SOFT_CLEAN_TO_DISK("options.tab-completion.push-soft-clean-to-disk"), + VERIFY_QUEST_EXISTS_ON_LOAD("options.verify-quest-exists-on-load"), TAB_COMPLETE_ENABLED("options.tab-completion.enabled"), ERROR_CHECKING_OVERRIDE("options.error-checking.override-errors"), QUEST_AUTOSTART("options.quest-autostart"), diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b2ab5871..a23c98dd 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -213,12 +213,12 @@ options: quest-autotrack: true # How much quests should log, 0 = errors only, 1 = warnings, 2 = info, 3 = debug verbose-logging-level: 2 - # Automatically clean player's quest progress files when they join. - # These changes will not be reflected to disk. - # Useful if you frequently add and remove quests on a production server. Equivalent of executing /q a moddata clean, without overwriting the file. - soft-clean-questsprogressfile-on-join: false - # The above, but overwriting the file on disk with the cleaned version, so it does not soft clean on every join. - push-soft-clean-to-disk: false + # Verify quests exist when a player's data is loaded - inconsistencies may arise when + # players progress on specific quests and those quests are later removed. The problem is that their progress + # is still kept in the quest progress file, which may lead to issues such as players reaching a quest started + # limit when the quests they had active no longer exist - having this option enabled prevents + # non-existent quests from being loaded + verify-quest-exists-on-load: true performance-tweaking: # The following are measured in server ticks, multiply SECONDS by 20 to get the number of ticks. quest-queue-executor-interval: 1 # how frequently Quests should execute the next check in the completion queue (def=1 - 0.05s) - increase this value if you are struggling with performance quest-autosave-interval: 12000 # how frequently online players data will be autosaved (def=12000 - 10 minutes) -- cgit v1.2.3-70-g09d2 From c9682c6a23348afc80053f4a0f156556ba3e4ee4 Mon Sep 17 00:00:00 2001 From: LMBishop <13875753+LMBishop@users.noreply.github.com> Date: Tue, 8 Jun 2021 19:39:49 +0100 Subject: Add warning --- src/main/resources/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/main/resources') diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index a23c98dd..4b8a4f2b 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -234,9 +234,10 @@ options: global-task-configuration-override: false # Whether or not the global display configuration will override per-quest display settins global-quest-display-configuration-override: false - # Storage options + # Storage options - please see the following: https://github.com/LMBishop/Quests/wiki/Storage-Providers storage: # Either 'yaml' (flatfile) or 'mysql' (network) + # Please read the following before using MySQL https://github.com/LMBishop/Quests/wiki/Storage-Providers#network provider: "yaml" # The following is only applicable for database storage providers (e.g. mysql) database-settings: -- cgit v1.2.3-70-g09d2 From 73cb35b98774ca3668f9fcb49e75e8f3ae2ed56f Mon Sep 17 00:00:00 2001 From: LMBishop <13875753+LMBishop@users.noreply.github.com> Date: Tue, 8 Jun 2021 21:46:06 +0100 Subject: Allow qPlayer to be null --- .../com/leonardobishop/quests/QuestCompleter.java | 3 + .../java/com/leonardobishop/quests/Quests.java | 1 - .../quests/api/QuestsPlaceholders.java | 1 + .../quests/command/QuestsCommand.java | 132 ++++++++++----------- .../quests/listener/PlayerJoinListener.java | 5 +- .../com/leonardobishop/quests/player/QPlayer.java | 11 +- .../quests/player/QPlayerManager.java | 15 ++- .../quest/tasktype/type/MiningCertainTaskType.java | 3 + .../type/dependent/EssentialsBalanceTaskType.java | 3 + .../quests/storage/MySqlStorageProvider.java | 26 ++-- .../com/leonardobishop/quests/util/Messages.java | 1 + src/main/resources/config.yml | 1 + 12 files changed, 117 insertions(+), 85 deletions(-) (limited to 'src/main/resources') diff --git a/src/main/java/com/leonardobishop/quests/QuestCompleter.java b/src/main/java/com/leonardobishop/quests/QuestCompleter.java index f31e5daa..d3ff8cf1 100644 --- a/src/main/java/com/leonardobishop/quests/QuestCompleter.java +++ b/src/main/java/com/leonardobishop/quests/QuestCompleter.java @@ -35,6 +35,8 @@ public class QuestCompleter implements Runnable { Player player = Bukkit.getPlayer(questProgress.getPlayer()); if (player != null && player.isOnline()) { QPlayer qPlayer = plugin.getPlayerManager().getPlayer(player.getUniqueId()); + if (qPlayer == null) return; + Quest quest = plugin.getQuestManager().getQuestById(questProgress.getQuestId()); if (!qPlayer.hasStartedQuest(quest)) return; @@ -52,6 +54,7 @@ public class QuestCompleter implements Runnable { Player player = Bukkit.getPlayer(questProgressFile.getPlayerUUID()); if (player != null && player.isOnline()) { QPlayer qPlayer = plugin.getPlayerManager().getPlayer(player.getUniqueId()); + if (qPlayer == null) return; for (QuestProgress questProgress : questProgressFile.getAllQuestProgress()) { Quest quest = plugin.getQuestManager().getQuestById(questProgress.getQuestId()); if (quest == null) continue; diff --git a/src/main/java/com/leonardobishop/quests/Quests.java b/src/main/java/com/leonardobishop/quests/Quests.java index bfbc0e60..6308f9de 100644 --- a/src/main/java/com/leonardobishop/quests/Quests.java +++ b/src/main/java/com/leonardobishop/quests/Quests.java @@ -174,7 +174,6 @@ public class Quests extends JavaPlugin { qPlayerManager = new QPlayerManager(this); menuController = new MenuController(this); - super.getCommand("quests").setExecutor(new QuestsCommand(this)); Bukkit.getPluginManager().registerEvents(new PlayerJoinListener(this), this); Bukkit.getPluginManager().registerEvents(menuController, this); diff --git a/src/main/java/com/leonardobishop/quests/api/QuestsPlaceholders.java b/src/main/java/com/leonardobishop/quests/api/QuestsPlaceholders.java index 9a402ed5..5c82cf6b 100644 --- a/src/main/java/com/leonardobishop/quests/api/QuestsPlaceholders.java +++ b/src/main/java/com/leonardobishop/quests/api/QuestsPlaceholders.java @@ -70,6 +70,7 @@ public class QuestsPlaceholders extends PlaceholderExpansion implements Cacheabl if (save) args = Arrays.copyOf(args, args.length - 1); final QPlayer qPlayer = plugin.getPlayerManager().getPlayer(p.getUniqueId()); + if (qPlayer == null) return "Data not loaded"; String split = args[args.length - 1]; String result = "null"; diff --git a/src/main/java/com/leonardobishop/quests/command/QuestsCommand.java b/src/main/java/com/leonardobishop/quests/command/QuestsCommand.java index d33baa65..d18f977c 100644 --- a/src/main/java/com/leonardobishop/quests/command/QuestsCommand.java +++ b/src/main/java/com/leonardobishop/quests/command/QuestsCommand.java @@ -78,6 +78,10 @@ public class QuestsCommand implements TabExecutor { if (args.length == 0 && sender instanceof Player) { Player player = (Player) sender; QPlayer qPlayer = plugin.getPlayerManager().getPlayer(player.getUniqueId()); + if (qPlayer == null) { + player.sendMessage(Messages.COMMAND_DATA_NOT_LOADED.getMessage()); + return true; + } qPlayer.openQuests(); return true; } else if (args.length >= 1) { @@ -90,10 +94,11 @@ public class QuestsCommand implements TabExecutor { showAdminHelp(sender, "moddata"); return true; } else if (args[1].equalsIgnoreCase("reload")) { + sender.sendMessage(ChatColor.GRAY + "Please note that some options, such as storage, require a full restart for chances to take effect."); plugin.reloadConfig(); plugin.reloadQuests(); - showProblems(sender); - sender.sendMessage(ChatColor.GRAY + "Quests successfully reloaded."); + if (!plugin.getQuestsConfigLoader().getFilesWithProblems().isEmpty()) showProblems(sender); + sender.sendMessage(ChatColor.GREEN + "Quests successfully reloaded."); return true; } else if (args[1].equalsIgnoreCase("config")) { showProblems(sender); @@ -156,6 +161,7 @@ public class QuestsCommand implements TabExecutor { showAdminHelp(sender, "opengui"); return true; } else if (args[1].equalsIgnoreCase("moddata")) { + // TODO remove me if (args[2].equalsIgnoreCase("clean")) { FileVisitor fileVisitor = new SimpleFileVisitor() { @Override @@ -260,36 +266,16 @@ public class QuestsCommand implements TabExecutor { showAdminHelp(sender, "opengui"); return true; } else if (args[1].equalsIgnoreCase("moddata")) { - OfflinePlayer ofp = Bukkit.getOfflinePlayer(args[3]); - UUID uuid; - String name; - // Player.class is a superclass for OfflinePlayer. - // getofflinePlayer return a player regardless if exists or not - if (ofp.hasPlayedBefore()) { - uuid = ofp.getUniqueId(); - name = ofp.getName(); - } else { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_PLAYERNOTFOUND.getMessage().replace("{player}", args[3])); - return true; - } + QPlayer qPlayer = getOtherPlayer(sender, args[3]); + if (qPlayer == null) return true; if (args[2].equalsIgnoreCase("fullreset")) { - QPlayer qPlayer = plugin.getPlayerManager().getPlayer(uuid); - if (qPlayer == null) { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_LOADDATA.getMessage().replace("{player}", name)); - plugin.getPlayerManager().loadPlayer(uuid); - qPlayer = plugin.getPlayerManager().getPlayer(uuid); //get again - } - if (qPlayer == null) { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_NODATA.getMessage().replace("{player}", name)); - return true; - } QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile(); questProgressFile.clear(); - plugin.getPlayerManager().savePlayerSync(uuid, questProgressFile); - if (Bukkit.getPlayer(uuid) == null) { - plugin.getPlayerManager().dropPlayer(uuid); + plugin.getPlayerManager().savePlayerSync(qPlayer.getPlayerUUID(), questProgressFile); + if (Bukkit.getPlayer(qPlayer.getPlayerUUID()) == null) { + plugin.getPlayerManager().dropPlayer(qPlayer.getPlayerUUID()); } - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_FULLRESET.getMessage().replace("{player}", name)); + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_FULLRESET.getMessage().replace("{player}", args[3])); return true; } showAdminHelp(sender, "moddata"); @@ -326,26 +312,8 @@ public class QuestsCommand implements TabExecutor { } } else if (args[1].equalsIgnoreCase("moddata")) { boolean success = false; - OfflinePlayer ofp = Bukkit.getOfflinePlayer(args[3]); - UUID uuid; - String name; - if (ofp.hasPlayedBefore()) { - uuid = ofp.getUniqueId(); - name = ofp.getName(); - } else { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_PLAYERNOTFOUND.getMessage().replace("{player}", args[3])); - return true; - } - QPlayer qPlayer = plugin.getPlayerManager().getPlayer(uuid); - if (qPlayer == null) { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_LOADDATA.getMessage().replace("{player}", name)); - plugin.getPlayerManager().loadPlayer(uuid); - } - if (qPlayer == null) { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_NODATA.getMessage().replace("{player}", name)); - success = true; - } - qPlayer = plugin.getPlayerManager().getPlayer(uuid); //get again + QPlayer qPlayer = getOtherPlayer(sender, args[3]); + if (qPlayer == null) return true; QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile(); Quest quest = plugin.getQuestManager().getQuestById(args[4]); if (quest == null) { @@ -355,47 +323,47 @@ public class QuestsCommand implements TabExecutor { } if (args[2].equalsIgnoreCase("reset")) { questProgressFile.generateBlankQuestProgress(quest); - plugin.getPlayerManager().savePlayerSync(uuid, questProgressFile); - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_RESET_SUCCESS.getMessage().replace("{player}", name).replace("{quest}", quest.getId())); + plugin.getPlayerManager().savePlayerSync(qPlayer.getPlayerUUID(), questProgressFile); + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_RESET_SUCCESS.getMessage().replace("{player}", args[3]).replace("{quest}", quest.getId())); success = true; } else if (args[2].equalsIgnoreCase("start")) { QuestStartResult response = qPlayer.startQuest(quest); if (response == QuestStartResult.QUEST_LIMIT_REACHED) { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILLIMIT.getMessage().replace("{player}", name).replace("{quest}", quest.getId())); + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILLIMIT.getMessage().replace("{player}", args[3]).replace("{quest}", quest.getId())); return true; } else if (response == QuestStartResult.QUEST_ALREADY_COMPLETED) { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILCOMPLETE.getMessage().replace("{player}", name).replace("{quest}", quest.getId())); + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILCOMPLETE.getMessage().replace("{player}", args[3]).replace("{quest}", quest.getId())); return true; } else if (response == QuestStartResult.QUEST_COOLDOWN) { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILCOOLDOWN.getMessage().replace("{player}", name).replace("{quest}", quest.getId())); + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILCOOLDOWN.getMessage().replace("{player}", args[3]).replace("{quest}", quest.getId())); return true; } else if (response == QuestStartResult.QUEST_LOCKED) { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILLOCKED.getMessage().replace("{player}", name).replace("{quest}", quest.getId())); + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILLOCKED.getMessage().replace("{player}", args[3]).replace("{quest}", quest.getId())); return true; } else if (response == QuestStartResult.QUEST_ALREADY_STARTED) { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILSTARTED.getMessage().replace("{player}", name).replace("{quest}", quest.getId())); + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILSTARTED.getMessage().replace("{player}", args[3]).replace("{quest}", quest.getId())); return true; } else if (response == QuestStartResult.QUEST_NO_PERMISSION) { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILPERMISSION.getMessage().replace("{player}", name).replace("{quest}", quest.getId())); + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILPERMISSION.getMessage().replace("{player}", args[3]).replace("{quest}", quest.getId())); return true; } else if (response == QuestStartResult.NO_PERMISSION_FOR_CATEGORY) { - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILCATEGORYPERMISSION.getMessage().replace("{player}", name).replace("{quest}", quest.getId())); + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_FAILCATEGORYPERMISSION.getMessage().replace("{player}", args[3]).replace("{quest}", quest.getId())); return true; } - plugin.getPlayerManager().savePlayerSync(uuid, questProgressFile); - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_SUCCESS.getMessage().replace("{player}", name).replace("{quest}", quest.getId())); + plugin.getPlayerManager().savePlayerSync(qPlayer.getPlayerUUID(), questProgressFile); + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_START_SUCCESS.getMessage().replace("{player}", args[3]).replace("{quest}", quest.getId())); success = true; } else if (args[2].equalsIgnoreCase("complete")) { qPlayer.completeQuest(quest); - plugin.getPlayerManager().savePlayerSync(uuid, questProgressFile); - sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_COMPLETE_SUCCESS.getMessage().replace("{player}", name).replace("{quest}", quest.getId())); + plugin.getPlayerManager().savePlayerSync(qPlayer.getPlayerUUID(), questProgressFile); + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_COMPLETE_SUCCESS.getMessage().replace("{player}", args[3]).replace("{quest}", quest.getId())); success = true; } if (!success) { showAdminHelp(sender, "moddata"); } - if (Bukkit.getPlayer(uuid) == null) { - plugin.getPlayerManager().dropPlayer(uuid); + if (Bukkit.getPlayer(qPlayer.getPlayerUUID()) == null) { + plugin.getPlayerManager().dropPlayer(qPlayer.getPlayerUUID()); } return true; } @@ -409,7 +377,7 @@ public class QuestsCommand implements TabExecutor { Quest quest = plugin.getQuestManager().getQuestById(args[1]); QPlayer qPlayer = plugin.getPlayerManager().getPlayer(player.getUniqueId()); if (qPlayer == null) { - sender.sendMessage(ChatColor.RED + "Your quest progress file has not been loaded yet."); + player.sendMessage(Messages.COMMAND_DATA_NOT_LOADED.getMessage()); return true; } if (quest == null) { @@ -434,10 +402,14 @@ public class QuestsCommand implements TabExecutor { Player player = (Player) sender; if (args.length >= 2) { Category category = plugin.getQuestManager().getCategoryById(args[1]); + QPlayer qPlayer = plugin.getPlayerManager().getPlayer(player.getUniqueId()); + if (qPlayer == null) { + player.sendMessage(Messages.COMMAND_DATA_NOT_LOADED.getMessage()); + return true; + } if (category == null) { sender.sendMessage(Messages.COMMAND_CATEGORY_OPEN_DOESNTEXIST.getMessage().replace("{category}", args[1])); } else { - QPlayer qPlayer = plugin.getPlayerManager().getPlayer(player.getUniqueId()); qPlayer.openCategory(category, null, false); return true; } @@ -447,7 +419,7 @@ public class QuestsCommand implements TabExecutor { Player player = (Player) sender; QPlayer qPlayer = plugin.getPlayerManager().getPlayer(player.getUniqueId()); if (qPlayer == null) { - sender.sendMessage(ChatColor.RED + "Your quest progress file has not been loaded yet."); + player.sendMessage(Messages.COMMAND_DATA_NOT_LOADED.getMessage()); return true; } List validQuests = new ArrayList<>(); @@ -467,6 +439,10 @@ public class QuestsCommand implements TabExecutor { } else if (sender instanceof Player && (args[0].equalsIgnoreCase("started"))) { Player player = (Player) sender; QPlayer qPlayer = plugin.getPlayerManager().getPlayer(player.getUniqueId()); + if (qPlayer == null) { + player.sendMessage(Messages.COMMAND_DATA_NOT_LOADED.getMessage()); + return true; + } qPlayer.openStartedQuests(); return true; } @@ -477,6 +453,30 @@ public class QuestsCommand implements TabExecutor { return true; } + private QPlayer getOtherPlayer(CommandSender sender, String name) { + OfflinePlayer ofp = Bukkit.getOfflinePlayer(name); + UUID uuid; + String username; + if (ofp.hasPlayedBefore()) { + uuid = ofp.getUniqueId(); + username = ofp.getName(); + } else { + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_PLAYERNOTFOUND.getMessage().replace("{player}", name)); + return null; + } + QPlayer qPlayer = plugin.getPlayerManager().getPlayer(uuid); + if (qPlayer == null) { + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_LOADDATA.getMessage().replace("{player}", username)); + plugin.getPlayerManager().loadPlayer(uuid); + qPlayer = plugin.getPlayerManager().getPlayer(uuid); + } + if (qPlayer == null) { + sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_NODATA.getMessage().replace("{player}", username)); + return null; + } + return qPlayer; + } + private void showProblems(CommandSender sender) { if (!plugin.getQuestsConfigLoader().getFilesWithProblems().isEmpty()) { // sender.sendMessage(ChatColor.DARK_GRAY.toString() + "----"); diff --git a/src/main/java/com/leonardobishop/quests/listener/PlayerJoinListener.java b/src/main/java/com/leonardobishop/quests/listener/PlayerJoinListener.java index 5cdb465e..53916c32 100644 --- a/src/main/java/com/leonardobishop/quests/listener/PlayerJoinListener.java +++ b/src/main/java/com/leonardobishop/quests/listener/PlayerJoinListener.java @@ -38,8 +38,11 @@ public class PlayerJoinListener implements Listener { Bukkit.getScheduler().runTaskLater(this.plugin, () -> event.getPlayer().sendMessage(plugin.getUpdater().getMessage()), 50L); } + QPlayer qPlayer = plugin.getPlayerManager().getPlayer(playerUuid); + if (qPlayer == null) return; + // run a full check to check for any missed quest completions - plugin.getQuestCompleter().queueFullCheck(plugin.getPlayerManager().getPlayer(playerUuid).getQuestProgressFile()); + plugin.getQuestCompleter().queueFullCheck(qPlayer.getQuestProgressFile()); } } diff --git a/src/main/java/com/leonardobishop/quests/player/QPlayer.java b/src/main/java/com/leonardobishop/quests/player/QPlayer.java index 872b02d2..4e04566b 100644 --- a/src/main/java/com/leonardobishop/quests/player/QPlayer.java +++ b/src/main/java/com/leonardobishop/quests/player/QPlayer.java @@ -78,10 +78,9 @@ public class QPlayer { } Player player = Bukkit.getPlayer(uuid); if (player != null) { - QPlayer questPlayer = QuestsAPI.getPlayerManager().getPlayer(uuid); String questFinishMessage = Messages.QUEST_COMPLETE.getMessage().replace("{quest}", quest.getDisplayNameStripped()); // PlayerFinishQuestEvent -- start - PlayerFinishQuestEvent questFinishEvent = new PlayerFinishQuestEvent(player, questPlayer, questProgress, questFinishMessage); + PlayerFinishQuestEvent questFinishEvent = new PlayerFinishQuestEvent(player, this, questProgress, questFinishMessage); Bukkit.getPluginManager().callEvent(questFinishEvent); // PlayerFinishQuestEvent -- end Bukkit.getServer().getScheduler().runTask(plugin, () -> { @@ -375,10 +374,10 @@ public class QPlayer { } if (Options.CATEGORIES_ENABLED.getBooleanValue()) { - CategoryQMenu categoryQMenu = new CategoryQMenu(plugin, plugin.getPlayerManager().getPlayer(player.getUniqueId())); + CategoryQMenu categoryQMenu = new CategoryQMenu(plugin, this); List questMenus = new ArrayList<>(); for (Category category : plugin.getQuestManager().getCategories()) { - QuestQMenu questQMenu = new QuestQMenu(plugin, plugin.getPlayerManager().getPlayer(player.getUniqueId()), category.getId(), categoryQMenu); + QuestQMenu questQMenu = new QuestQMenu(plugin, this, category.getId(), categoryQMenu); List quests = new ArrayList<>(); for (String questid : category.getRegisteredQuestIds()) { Quest quest = plugin.getQuestManager().getQuestById(questid); @@ -393,7 +392,7 @@ public class QPlayer { plugin.getMenuController().openMenu(player, categoryQMenu, 1); } else { - QuestQMenu questQMenu = new QuestQMenu(plugin, plugin.getPlayerManager().getPlayer(player.getUniqueId()), "", null); + QuestQMenu questQMenu = new QuestQMenu(plugin, this, "", null); List quests = new ArrayList<>(); for (Map.Entry entry : plugin.getQuestManager().getQuests().entrySet()) { quests.add(entry.getValue()); @@ -414,7 +413,7 @@ public class QPlayer { return; } - StartedQMenu startedQMenu = new StartedQMenu(plugin, plugin.getPlayerManager().getPlayer(player.getUniqueId())); + StartedQMenu startedQMenu = new StartedQMenu(plugin, this); List quests = new ArrayList<>(); for (Map.Entry entry : plugin.getQuestManager().getQuests().entrySet()) { quests.add(new QuestSortWrapper(plugin, entry.getValue())); diff --git a/src/main/java/com/leonardobishop/quests/player/QPlayerManager.java b/src/main/java/com/leonardobishop/quests/player/QPlayerManager.java index 2cd9cbfd..41c704e3 100644 --- a/src/main/java/com/leonardobishop/quests/player/QPlayerManager.java +++ b/src/main/java/com/leonardobishop/quests/player/QPlayerManager.java @@ -36,7 +36,11 @@ public class QPlayerManager { plugin.getQuestsLogger().warning("No valid storage provider is configured - Quests will use YAML storage as a default"); this.storageProvider = new YamlStorageProvider(plugin); } - storageProvider.init(); + try { + storageProvider.init(); + } catch (Exception ignored) { + plugin.getQuestsLogger().severe("An error occurred initialising the storage provider."); + } } private final Map qPlayers = new ConcurrentHashMap<>(); @@ -78,7 +82,9 @@ public class QPlayerManager { * @param uuid the uuid of the player */ public void savePlayer(UUID uuid) { - savePlayer(uuid, getPlayer(uuid).getQuestProgressFile()); + QPlayer qPlayer = getPlayer(uuid); + if (qPlayer == null) return; + savePlayer(uuid, qPlayer.getQuestProgressFile()); } /** @@ -101,7 +107,9 @@ public class QPlayerManager { * @param uuid the uuid of the player */ public void savePlayerSync(UUID uuid) { - savePlayerSync(uuid, getPlayer(uuid).getQuestProgressFile()); + QPlayer qPlayer = getPlayer(uuid); + if (qPlayer == null) return; + savePlayerSync(uuid, qPlayer.getQuestProgressFile()); } /** @@ -144,6 +152,7 @@ public class QPlayerManager { plugin.getQuestsLogger().debug("Loading player " + uuid + ". Main thread: " + Bukkit.isPrimaryThread()); qPlayers.computeIfAbsent(uuid, s -> { QuestProgressFile questProgressFile = storageProvider.loadProgressFile(uuid); + if (questProgressFile == null) return null; return new QPlayer(uuid, questProgressFile, new QPlayerPreferences(null), plugin); }); } diff --git a/src/main/java/com/leonardobishop/quests/quest/tasktype/type/MiningCertainTaskType.java b/src/main/java/com/leonardobishop/quests/quest/tasktype/type/MiningCertainTaskType.java index da497808..125bfecc 100644 --- a/src/main/java/com/leonardobishop/quests/quest/tasktype/type/MiningCertainTaskType.java +++ b/src/main/java/com/leonardobishop/quests/quest/tasktype/type/MiningCertainTaskType.java @@ -127,6 +127,9 @@ public final class MiningCertainTaskType extends TaskType { if (event.getPlayer().hasMetadata("NPC")) return; QPlayer qPlayer = QuestsAPI.getPlayerManager().getPlayer(event.getPlayer().getUniqueId()); + if (qPlayer == null) { + return; + } for (Quest quest : super.getRegisteredQuests()) { if (qPlayer.hasStartedQuest(quest)) { diff --git a/src/main/java/com/leonardobishop/quests/quest/tasktype/type/dependent/EssentialsBalanceTaskType.java b/src/main/java/com/leonardobishop/quests/quest/tasktype/type/dependent/EssentialsBalanceTaskType.java index 581cbafb..22cef8a2 100644 --- a/src/main/java/com/leonardobishop/quests/quest/tasktype/type/dependent/EssentialsBalanceTaskType.java +++ b/src/main/java/com/leonardobishop/quests/quest/tasktype/type/dependent/EssentialsBalanceTaskType.java @@ -47,6 +47,9 @@ public class EssentialsBalanceTaskType extends TaskType { Essentials ess = (Essentials) Bukkit.getPluginManager().getPlugin("Essentials"); if (player != null && player.isOnline() && ess != null) { QPlayer qPlayer = QuestsAPI.getPlayerManager().getPlayer(playerUUID); + if (qPlayer == null) { + return; + } QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile(); QuestProgress questProgress = questProgressFile.getQuestProgress(quest); TaskProgress taskProgress = questProgress.getTaskProgress(task.getId()); diff --git a/src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java b/src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java index fefd53ba..02692919 100644 --- a/src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java +++ b/src/main/java/com/leonardobishop/quests/storage/MySqlStorageProvider.java @@ -57,17 +57,23 @@ public class MySqlStorageProvider implements StorageProvider { 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 final HikariDataSource hikari; - private final String prefix; + private final ConfigurationSection configuration; private final Quests plugin; - private final Function statementProcessor; + private HikariDataSource hikari; + private String prefix; + private Function statementProcessor; + private boolean fault; public MySqlStorageProvider(Quests plugin, ConfigurationSection configuration) { this.plugin = plugin; if (configuration == null) { configuration = new YamlConfiguration(); } + this.configuration = configuration; + } + @Override + public void init() { String address = configuration.getString("network.address", "localhost:3306"); String database = configuration.getString("network.database", "minecraft"); String url = "jdbc:mysql://" + address + "/" + database; @@ -94,13 +100,14 @@ public class MySqlStorageProvider implements StorageProvider { config.addDataSourceProperty("elideSetAutoCommits", true); config.addDataSourceProperty("maintainTimeStats", false); - this.hikari = new HikariDataSource(config); + try { + this.hikari = new HikariDataSource(config); + } catch (Exception e) { + e.printStackTrace(); + fault = true; + } this.prefix = configuration.getString("database-settings.table-prefix", "quests_"); this.statementProcessor = s -> s.replace("{prefix}", prefix); - } - - @Override - public void init() { try (Connection connection = hikari.getConnection()) { try (Statement s = connection.createStatement()) { plugin.getQuestsLogger().debug("Creating default tables"); @@ -121,6 +128,7 @@ public class MySqlStorageProvider implements StorageProvider { @Override public QuestProgressFile loadProgressFile(UUID uuid) { + if (fault) return null; QuestProgressFile questProgressFile = new QuestProgressFile(uuid, plugin); try (Connection connection = hikari.getConnection()) { plugin.getQuestsLogger().debug("Querying player " + uuid); @@ -193,12 +201,14 @@ public class MySqlStorageProvider implements StorageProvider { } catch (SQLException e) { plugin.getQuestsLogger().severe("Failed to load player: " + uuid + "!"); e.printStackTrace(); + return null; } return questProgressFile; } @Override public void saveProgressFile(UUID uuid, QuestProgressFile questProgressFile) { + if (fault) return; 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))) { diff --git a/src/main/java/com/leonardobishop/quests/util/Messages.java b/src/main/java/com/leonardobishop/quests/util/Messages.java index ba9e42da..a86a4144 100644 --- a/src/main/java/com/leonardobishop/quests/util/Messages.java +++ b/src/main/java/com/leonardobishop/quests/util/Messages.java @@ -22,6 +22,7 @@ public enum Messages { QUEST_CATEGORY_PERMISSION("messages.quest-category-permission"), QUEST_CANCEL_NOTSTARTED("messages.quest-cancel-notstarted"), QUEST_UPDATER("messages.quest-updater"), + COMMAND_DATA_NOT_LOADED("messages.command-data-not-loaded"), COMMAND_SUB_DOESNTEXIST("messages.command-sub-doesntexist"), COMMAND_QUEST_START_DOESNTEXIST("messages.command-quest-start-doesntexist"), COMMAND_QUEST_GENERAL_DOESNTEXIST("messages.command-quest-general-doesntexist"), diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 4b8a4f2b..2c2e09c7 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -334,6 +334,7 @@ messages: quest-category-quest-permission: "&7You do not have permission to start this quest since it is in a category you do not have permission to view." quest-cancel-notstarted: "&7You have not started this quest." quest-updater: "&cQuests > &7A new version &c{newver} &7was found on Spigot (your version: &c{oldver}&7). Please update me! <3 - Link: {link}" + command-data-not-loaded: "&4Your quests progress file has not been loaded; you cannot use quests. If this issue persists, contact an admin." command-sub-doesntexist: "&7The specified subcommand '&c{sub}' &7does not exist." command-quest-start-doesntexist: "&7The specified quest '&c{quest}&7' does not exist." command-quest-general-doesntexist: "&7The specified quest '&c{quest}&7' does not exist." -- cgit v1.2.3-70-g09d2