aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/src/main/java/com/leonardobishop/quests/common/player/QPlayer.java118
-rw-r--r--common/src/main/java/com/leonardobishop/quests/common/player/QPlayerData.java44
-rw-r--r--common/src/main/java/com/leonardobishop/quests/common/player/QPlayerManager.java143
-rw-r--r--common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/QuestProgress.java344
-rw-r--r--common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/QuestProgressFile.java414
-rw-r--r--common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/TaskProgress.java151
-rw-r--r--common/src/main/java/com/leonardobishop/quests/common/storage/StorageProvider.java82
7 files changed, 885 insertions, 411 deletions
diff --git a/common/src/main/java/com/leonardobishop/quests/common/player/QPlayer.java b/common/src/main/java/com/leonardobishop/quests/common/player/QPlayer.java
index c3e38e67..bcfafdd0 100644
--- a/common/src/main/java/com/leonardobishop/quests/common/player/QPlayer.java
+++ b/common/src/main/java/com/leonardobishop/quests/common/player/QPlayer.java
@@ -18,29 +18,52 @@ import java.util.UUID;
/**
* Represents a player.
*/
-public class QPlayer {
+public final class QPlayer {
private final Quests plugin;
- private final UUID uuid;
- private final QPlayerPreferences playerPreferences;
- private final QuestProgressFile questProgressFile;
+ private final QPlayerData playerData;
private QuestController questController;
- public QPlayer(Quests plugin, UUID uuid, QPlayerPreferences playerPreferences, QuestProgressFile questProgressFile, QuestController questController) {
+ public QPlayer(final @NotNull Quests plugin, final @NotNull QPlayerData playerData, final @NotNull QuestController questController) {
this.plugin = plugin;
- this.uuid = uuid;
- this.playerPreferences = playerPreferences;
- this.questProgressFile = questProgressFile;
+ this.playerData = playerData;
this.questController = questController;
}
/**
+ * Get this players associated {@link QPlayerData}
+ *
+ * @return the players data
+ */
+ public @NotNull QPlayerData getPlayerData() {
+ return this.playerData;
+ }
+
+ /**
* Get the player UUID associated with this quest player. The player may not be online.
*
* @return uuid
*/
public @NotNull UUID getPlayerUUID() {
- return this.uuid;
+ return this.playerData.playerUUID();
+ }
+
+ /**
+ * Get this players associated {@link QPlayerPreferences}
+ *
+ * @return the players preferences
+ */
+ public @NotNull QPlayerPreferences getPlayerPreferences() {
+ return this.playerData.playerPreferences();
+ }
+
+ /**
+ * Get this players associated {@link QuestProgressFile}
+ *
+ * @return the quest progress file
+ */
+ public @NotNull QuestProgressFile getQuestProgressFile() {
+ return this.playerData.questProgressFile();
}
/**
@@ -50,31 +73,32 @@ public class QPlayer {
* @param quest the quest to complete
* @return true (always)
*/
- public boolean completeQuest(@NotNull Quest quest) {
+ @SuppressWarnings("UnusedReturnValue")
+ public boolean completeQuest(final @NotNull Quest quest) {
Objects.requireNonNull(quest, "quest cannot be null");
- return questController.completeQuestForPlayer(this, quest);
+ return this.questController.completeQuestForPlayer(this, quest);
}
/**
* Attempt to track a quest for the player. This will also play all effects (such as titles, messages etc.)
- **
+ *
* @param quest the quest to track
*/
- public void trackQuest(@Nullable Quest quest) {
- questController.trackQuestForPlayer(this, quest);
+ public void trackQuest(final @Nullable Quest quest) {
+ this.questController.trackQuestForPlayer(this, quest);
}
/**
- * Gets whether or not the player has started a specific quest.
+ * Gets whether the player has started a specific quest.
*
* @param quest the quest to test for
* @return true if the quest is started or quest autostart is enabled and the quest is ready to start, false otherwise
*/
- public boolean hasStartedQuest(@NotNull Quest quest) {
+ public boolean hasStartedQuest(final @NotNull Quest quest) {
Objects.requireNonNull(quest, "quest cannot be null");
- return questController.hasPlayerStartedQuest(this, quest);
+ return this.questController.hasPlayerStartedQuest(this, quest);
}
/**
@@ -129,17 +153,16 @@ public class QPlayer {
/**
* Attempt to start a quest for the player. This will also play all effects (such as titles, messages etc.)
- *
* Warning: will fail if the player is not online.
*
* @param quest the quest to start
* @return the quest start result -- {@code QuestStartResult.QUEST_SUCCESS} indicates success
*/
// TODO PlaceholderAPI support
- public @NotNull QuestStartResult startQuest(@NotNull Quest quest) {
+ public @NotNull QuestStartResult startQuest(final @NotNull Quest quest) {
Objects.requireNonNull(quest, "quest cannot be null");
- return questController.startQuestForPlayer(this, quest);
+ return this.questController.startQuestForPlayer(this, quest);
}
/**
@@ -148,10 +171,10 @@ public class QPlayer {
* @param quest the quest to start
* @return true if the quest was cancelled, false otherwise
*/
- public boolean cancelQuest(@NotNull Quest quest) {
+ public boolean cancelQuest(final @NotNull Quest quest) {
Objects.requireNonNull(quest, "quest cannot be null");
- return questController.cancelQuestForPlayer(this, quest);
+ return this.questController.cancelQuestForPlayer(this, quest);
}
/**
@@ -160,52 +183,34 @@ public class QPlayer {
* @param quest the quest to start
* @return true if the quest was expired, false otherwise
*/
- public boolean expireQuest(@NotNull Quest quest) {
+ @SuppressWarnings("UnusedReturnValue")
+ public boolean expireQuest(final @NotNull Quest quest) {
Objects.requireNonNull(quest, "quest cannot be null");
- return questController.expireQuestForPlayer(this, quest);
+ return this.questController.expireQuestForPlayer(this, quest);
}
/**
* Check if the player can start a quest.
- *
* Warning: will fail if the player is not online.
*
* @param quest the quest to check
* @return the quest start result
*/
- public @NotNull QuestStartResult canStartQuest(@NotNull Quest quest) {
+ public @NotNull QuestStartResult canStartQuest(final @NotNull Quest quest) {
Objects.requireNonNull(quest, "quest cannot be null");
- return questController.canPlayerStartQuest(this, quest);
+ return this.questController.canPlayerStartQuest(this, quest);
}
/**
- * Get this players associated {@link QuestProgressFile}
- *
- * @return the quest progress file
- */
- public @NotNull QuestProgressFile getQuestProgressFile() {
- return questProgressFile;
- }
-
- /**
- * Get this players associated {@link QPlayerPreferences}
- *
- * @return the players preferences
- */
- public @NotNull QPlayerPreferences getPlayerPreferences() {
- return playerPreferences;
- }
-
- /**
- * Get this players associated {@link QuestController}, usually the servers active quest controller
+ * Get player's associated {@link QuestController}. It's usually the server's active quest controller.
*
* @see QPlayerManager#getActiveQuestController()
* @return the quest controller for this player
*/
public @NotNull QuestController getQuestController() {
- return questController;
+ return this.questController;
}
/**
@@ -213,21 +218,24 @@ public class QPlayer {
*
* @param questController new quest controller
*/
- public void setQuestController(@NotNull QuestController questController) {
+ public void setQuestController(final @NotNull QuestController questController) {
Objects.requireNonNull(questController, "questController cannot be null");
this.questController = questController;
}
- @Override //Used by java GC
- public boolean equals(Object o) {
- if (!(o instanceof QPlayer)) return false;
- QPlayer qPlayer = (QPlayer) o;
- return this.uuid == qPlayer.getPlayerUUID();
+ @Override
+ public boolean equals(final @Nullable Object o) {
+ if (o instanceof final QPlayer qPlayer) {
+ return this.getPlayerUUID() == qPlayer.getPlayerUUID();
+ } else {
+ return false;
+ }
}
- @Override //Used by java GC
+ @Override
public int hashCode() {
- return uuid.hashCode() * 73; //uuid hash * prime number
+ // uuid hash * prime number
+ return this.getPlayerUUID().hashCode() * 73;
}
}
diff --git a/common/src/main/java/com/leonardobishop/quests/common/player/QPlayerData.java b/common/src/main/java/com/leonardobishop/quests/common/player/QPlayerData.java
new file mode 100644
index 00000000..cdabd385
--- /dev/null
+++ b/common/src/main/java/com/leonardobishop/quests/common/player/QPlayerData.java
@@ -0,0 +1,44 @@
+package com.leonardobishop.quests.common.player;
+
+import com.leonardobishop.quests.common.player.questprogressfile.QuestProgressFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+import java.util.UUID;
+
+public final class QPlayerData {
+
+ private final UUID playerUUID;
+ private final QPlayerPreferences playerPreferences;
+ private final QuestProgressFile questProgressFile;
+
+ public QPlayerData(final @NotNull UUID playerUUID, final @NotNull QPlayerPreferences playerPreferences, final @NotNull QuestProgressFile questProgressFile) {
+ this.playerUUID = Objects.requireNonNull(playerUUID, "playerUUID cannot be null");
+ this.playerPreferences = Objects.requireNonNull(playerPreferences, "playerPreferences cannot be null");
+ this.questProgressFile = Objects.requireNonNull(questProgressFile, "questProgressFile cannot be null");
+ }
+
+ public QPlayerData(final @NotNull QPlayerData playerData) {
+ Objects.requireNonNull(playerData, "playerData cannot be null");
+
+ this.playerUUID = playerData.playerUUID;
+ this.playerPreferences = playerData.playerPreferences;
+ this.questProgressFile = new QuestProgressFile(playerData.questProgressFile);
+ }
+
+ public @NotNull UUID playerUUID() {
+ return this.playerUUID;
+ }
+
+ public @NotNull QPlayerPreferences playerPreferences() {
+ return this.playerPreferences;
+ }
+
+ public @NotNull QuestProgressFile questProgressFile() {
+ return this.questProgressFile;
+ }
+
+ public void setModified(final boolean modified) {
+ this.questProgressFile.setModified(modified);
+ }
+}
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 1475c44a..4c3704de 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
@@ -6,6 +6,7 @@ import com.leonardobishop.quests.common.questcontroller.QuestController;
import com.leonardobishop.quests.common.storage.StorageProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.UnmodifiableView;
import java.util.Collection;
import java.util.Collections;
@@ -19,17 +20,18 @@ import java.util.concurrent.ConcurrentHashMap;
* The QPlayerManager is responsible for keeping a reference to all players on the server and is used to
* obtain an instance of a player, load new players and save current players.
*/
-public class QPlayerManager {
+public final class QPlayerManager {
- private final Map<UUID, QPlayer> qPlayers = new ConcurrentHashMap<>();
private final Quests plugin;
private final StorageProvider storageProvider;
+ private final Map<UUID, QPlayer> qPlayerMap;
private QuestController activeQuestController;
- public QPlayerManager(Quests plugin, StorageProvider storageProvider, QuestController questController) {
- this.plugin = plugin;
- this.storageProvider = storageProvider;
- this.activeQuestController = questController;
+ public QPlayerManager(final @NotNull Quests plugin, final @NotNull StorageProvider storageProvider, final @NotNull QuestController questController) {
+ this.plugin = Objects.requireNonNull(plugin, "plugin cannot be null");
+ this.storageProvider = Objects.requireNonNull(storageProvider, "storageProvider cannot be null");
+ this.activeQuestController = Objects.requireNonNull(questController, "questController cannot be null");
+ this.qPlayerMap = new ConcurrentHashMap<>();
}
/**
@@ -38,7 +40,7 @@ public class QPlayerManager {
* @param uuid the uuid
* @return {@link QPlayer} if they are loaded, otherwise null
*/
- public @Nullable QPlayer getPlayer(@NotNull UUID uuid) {
+ public @Nullable QPlayer getPlayer(final @NotNull UUID uuid) {
Objects.requireNonNull(uuid, "uuid cannot be null");
// QPlayer qPlayer = qPlayers.get(uuid);
@@ -48,7 +50,7 @@ public class QPlayerManager {
// Thread.dumpStack();
// }
// }
- return qPlayers.get(uuid);
+ return this.qPlayerMap.get(uuid);
}
/**
@@ -56,12 +58,12 @@ public class QPlayerManager {
*
* @param uuid the uuid of the player
*/
- public void removePlayer(@NotNull UUID uuid) {
+ public void removePlayer(final @NotNull UUID uuid) {
Objects.requireNonNull(uuid, "uuid cannot be null");
- plugin.getQuestsLogger().debug("Unloading and saving player " + uuid + "...");
- CompletableFuture<Void> future = savePlayer(uuid);
- future.thenAccept((v) -> qPlayers.remove(uuid));
+ this.plugin.getQuestsLogger().debug("Unloading and saving player " + uuid + "...");
+ final CompletableFuture<Void> future = this.savePlayer(uuid);
+ future.thenAccept(unused -> this.qPlayerMap.remove(uuid));
}
/**
@@ -71,32 +73,30 @@ public class QPlayerManager {
* @param uuid the uuid of the player
* @return completable future
*/
- public CompletableFuture<Void> savePlayer(@NotNull UUID uuid) {
+ public @NotNull CompletableFuture<Void> savePlayer(final @NotNull UUID uuid) {
Objects.requireNonNull(uuid, "uuid cannot be null");
- QPlayer qPlayer = getPlayer(uuid);
- if (qPlayer == null) return CompletableFuture.completedFuture(null);
- return savePlayer(uuid, qPlayer.getQuestProgressFile());
+ final QPlayer qPlayer = this.getPlayer(uuid);
+ if (qPlayer == null) {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ return this.savePlayer(qPlayer.getPlayerData());
}
/**
* Schedules a save for the player with a specified {@link QuestProgressFile}. The modified status of the
* specified progress file will be reset.
- *
- * @param uuid the uuid of the player
- * @param originalProgressFile the quest progress file to associate with and save
- * @return completable future
*/
- public CompletableFuture<Void> savePlayer(@NotNull UUID uuid, @NotNull QuestProgressFile originalProgressFile) {
- Objects.requireNonNull(uuid, "uuid cannot be null");
- Objects.requireNonNull(originalProgressFile, "originalProgressFile cannot be null");
+ public @NotNull CompletableFuture<Void> savePlayer(final @NotNull QPlayerData playerData) {
+ Objects.requireNonNull(playerData, "playerData cannot be null");
- CompletableFuture<Void> future = new CompletableFuture<>();
+ final CompletableFuture<Void> future = new CompletableFuture<>();
+ final QPlayerData clonedPlayerData = new QPlayerData(playerData);
+ playerData.setModified(false);
- QuestProgressFile clonedProgressFile = new QuestProgressFile(originalProgressFile);
- originalProgressFile.resetModified();
- plugin.getScheduler().doAsync(() -> {
- save(uuid, clonedProgressFile);
+ this.plugin.getScheduler().doAsync(() -> {
+ this.save(clonedPlayerData);
future.complete(null);
});
@@ -109,34 +109,35 @@ public class QPlayerManager {
*
* @param uuid the uuid of the player
*/
- public void savePlayerSync(@NotNull UUID uuid) {
+ public void savePlayerSync(final @NotNull UUID uuid) {
Objects.requireNonNull(uuid, "uuid cannot be null");
- QPlayer qPlayer = getPlayer(uuid);
- if (qPlayer == null) return;
- savePlayerSync(uuid, qPlayer.getQuestProgressFile());
+ final QPlayer qPlayer = this.getPlayer(uuid);
+ if (qPlayer == null) {
+ return;
+ }
+
+ this.savePlayerSync(qPlayer.getPlayerData());
}
/**
* Immediately saves the player with a specified {@link QuestProgressFile}, on the same thread. The modified status
* of the specified progress file is not changed.
- *
- * @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(final @NotNull QPlayerData playerData) {
+ this.save(playerData);
}
- private void save(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile) {
- Objects.requireNonNull(uuid, "uuid cannot be null");
- Objects.requireNonNull(questProgressFile, "questProgressFile cannot be null");
+ private void save(@NotNull QPlayerData playerData) {
+ Objects.requireNonNull(playerData, "playerData cannot be null");
+
+ final String uuidString = playerData.playerUUID().toString();
+ this.plugin.getQuestsLogger().debug("Saving player " + uuidString + "...");
- plugin.getQuestsLogger().debug("Saving player " + uuid + "...");
- if (storageProvider.saveProgressFile(uuid, questProgressFile)) {
- plugin.getQuestsLogger().debug("Quest progress file saved for player " + uuid + ".");
+ if (this.storageProvider.savePlayerData(playerData)) {
+ this.plugin.getQuestsLogger().debug("Quest progress file saved for player " + uuidString + ".");
} else {
- plugin.getQuestsLogger().severe("Failed to save player " + uuid + "!");
+ this.plugin.getQuestsLogger().severe("Failed to save player " + uuidString + "!");
}
}
@@ -145,20 +146,21 @@ public class QPlayerManager {
*
* @param uuid the uuid of the player
*/
- public void dropPlayer(@NotNull UUID uuid) {
+ public void dropPlayer(final @NotNull UUID uuid) {
Objects.requireNonNull(uuid, "uuid cannot be null");
- plugin.getQuestsLogger().debug("Dropping player " + uuid + ".");
- qPlayers.remove(uuid);
+ this.plugin.getQuestsLogger().debug("Dropping player " + uuid + ".");
+ this.qPlayerMap.remove(uuid);
}
/**
* Gets all QPlayers loaded on the server
*
- * @return immutable map of quest players
+ * @return immutable collection of quest players
*/
- public Collection<QPlayer> getQPlayers() {
- return Collections.unmodifiableCollection(qPlayers.values());
+ @UnmodifiableView
+ public @NotNull Collection<QPlayer> getQPlayers() {
+ return Collections.unmodifiableCollection(this.qPlayerMap.values());
}
/**
@@ -168,20 +170,26 @@ public class QPlayerManager {
* @param uuid the uuid of the player
* @return completable future with the loaded player, or null if there was an error
*/
- public CompletableFuture<QPlayer> loadPlayer(UUID uuid) {
- plugin.getQuestsLogger().debug("Loading player " + uuid + "...");
+ public @NotNull CompletableFuture<QPlayer> loadPlayer(final @NotNull UUID uuid) {
+ Objects.requireNonNull(uuid, "uuid cannot be null");
+
+ final String uuidString = uuid.toString();
+ this.plugin.getQuestsLogger().debug("Loading player " + uuidString + "...");
+ final CompletableFuture<QPlayer> future = new CompletableFuture<>();
- CompletableFuture<QPlayer> future = new CompletableFuture<>();
- plugin.getScheduler().doAsync(() -> {
- QuestProgressFile questProgressFile = storageProvider.loadProgressFile(uuid);
- if (questProgressFile == null) {
- plugin.getQuestsLogger().debug("A problem occurred trying loading player " + uuid + "; quest progress file is null.");
+ this.plugin.getScheduler().doAsync(() -> {
+ final QPlayerData playerData = this.storageProvider.loadPlayerData(uuid);
+
+ if (playerData == null) {
+ this.plugin.getQuestsLogger().debug("A problem occurred trying loading player " + uuidString + "; quest progress file is null.");
future.complete(null);
return;
}
- QPlayer qPlayer = new QPlayer(plugin, uuid, new QPlayerPreferences(null), questProgressFile, activeQuestController);
- qPlayers.computeIfAbsent(uuid, s -> qPlayer);
- plugin.getQuestsLogger().debug("Quest progress file loaded for player " + uuid + ".");
+
+ final QPlayer qPlayer = new QPlayer(this.plugin, playerData, this.activeQuestController);
+ this.qPlayerMap.putIfAbsent(uuid, qPlayer);
+
+ this.plugin.getQuestsLogger().debug("Quest progress file loaded for player " + uuidString + ".");
future.complete(qPlayer);
});
@@ -193,17 +201,18 @@ public class QPlayerManager {
*
* @return {@link StorageProvider}
*/
- public StorageProvider getStorageProvider() {
- return storageProvider;
+ public @NotNull StorageProvider getStorageProvider() {
+ return this.storageProvider;
}
- public QuestController getActiveQuestController() {
- return activeQuestController;
+ public @NotNull QuestController getActiveQuestController() {
+ return this.activeQuestController;
}
- public void setActiveQuestController(QuestController activeQuestController) {
- this.activeQuestController = activeQuestController;
- for (QPlayer qPlayer : qPlayers.values()) {
+ public void setActiveQuestController(final @NotNull QuestController activeQuestController) {
+ this.activeQuestController = Objects.requireNonNull(activeQuestController, "activeQuestController cannot be null");
+
+ for (final QPlayer qPlayer : this.qPlayerMap.values()) {
qPlayer.setQuestController(activeQuestController);
}
}
diff --git a/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/QuestProgress.java b/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/QuestProgress.java
index 8e51d769..d8506b83 100644
--- a/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/QuestProgress.java
+++ b/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/QuestProgress.java
@@ -1,19 +1,23 @@
package com.leonardobishop.quests.common.player.questprogressfile;
import com.leonardobishop.quests.common.plugin.Quests;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
-public class QuestProgress {
+public final class QuestProgress {
private final Quests plugin;
-
- private final Map<String, TaskProgress> taskProgress = new HashMap<>();
- private final String questid;
- private final UUID player;
+ private final String questId;
+ private final UUID playerUUID;
+ private final Map<String, TaskProgress> taskProgressMap;
private boolean started;
private long startedDate;
@@ -22,29 +26,66 @@ public class QuestProgress {
private long completionDate;
private boolean modified;
- public QuestProgress(Quests plugin, String questid, boolean completed, boolean completedBefore, long completionDate, UUID player, boolean started, long startedDate) {
+ /**
+ * Constructs a QuestProgress.
+ *
+ * @param plugin the plugin instance
+ * @param questId the associated quest ID
+ * @param playerUUID the associated player UUID
+ * @param started whether the quest is started
+ * @param startedDate the date of the last quest start
+ * @param completed whether the quest is completed
+ * @param completedBefore whether the quest has been completed before
+ * @param completionDate the date of the last quest completion
+ * @param modified whether the object has been modified and needs to be saved
+ */
+ public QuestProgress(final @NotNull Quests plugin, final @NotNull String questId, final @NotNull UUID playerUUID, final boolean started, final long startedDate, final boolean completed, final boolean completedBefore, final long completionDate, final boolean modified) {
this.plugin = plugin;
- this.questid = questid;
+ this.questId = questId;
+ this.playerUUID = playerUUID;
+ this.taskProgressMap = new HashMap<>();
+ this.started = started;
+ this.startedDate = startedDate;
this.completed = completed;
this.completedBefore = completedBefore;
this.completionDate = completionDate;
- this.player = player;
- this.started = started;
- this.startedDate = startedDate;
+ this.modified = modified;
}
- public QuestProgress(Quests plugin, String questid, boolean completed, boolean completedBefore, long completionDate, UUID player, boolean started, long startedDate, boolean modified) {
- this(plugin, questid, completed, completedBefore, completionDate, player, started, startedDate);
- this.modified = modified;
+ /**
+ * Constructs a QuestProgress with {@link QuestProgress#modified} set to {@code false}.
+ *
+ * @param plugin the plugin instance
+ * @param questId the associated quest ID
+ * @param playerUUID the associated player UUID
+ * @param started whether the quest is started
+ * @param startedDate the date of the last quest start
+ * @param completed whether the quest is completed
+ * @param completedBefore whether the quest has been completed before
+ * @param completionDate the date of the last quest completion
+ */
+ public QuestProgress(final @NotNull Quests plugin, final @NotNull String questId, final @NotNull UUID playerUUID, final boolean started, final long startedDate, final boolean completed, final boolean completedBefore, final long completionDate) {
+ this(plugin, questId, playerUUID, started, startedDate, completed, completedBefore, completionDate, false);
}
- public QuestProgress(QuestProgress questProgress) {
+ /**
+ * Constructs a data-only clone from a QuestProgress instance.
+ *
+ * @param questProgress the quest progress instance
+ */
+ @ApiStatus.Internal
+ public QuestProgress(final @NotNull QuestProgress questProgress) {
+ final Set<Map.Entry<String, TaskProgress>> progressEntries = questProgress.taskProgressMap.entrySet();
+
this.plugin = questProgress.plugin;
- for (Map.Entry<String, TaskProgress> progressEntry : questProgress.taskProgress.entrySet()) {
- taskProgress.put(progressEntry.getKey(), new TaskProgress(progressEntry.getValue()));
+ this.questId = questProgress.questId;
+ this.playerUUID = questProgress.playerUUID;
+ this.taskProgressMap = new HashMap<>(progressEntries.size());
+
+ for (final Map.Entry<String, TaskProgress> progressEntry : progressEntries) {
+ this.taskProgressMap.put(progressEntry.getKey(), new TaskProgress(progressEntry.getValue()));
}
- this.questid = questProgress.questid;
- this.player = questProgress.player;
+
this.started = questProgress.started;
this.startedDate = questProgress.startedDate;
this.completed = questProgress.completed;
@@ -53,120 +94,259 @@ public class QuestProgress {
this.modified = questProgress.modified;
}
- public String getQuestId() {
- return questid;
+ /**
+ * @return the associated quest ID
+ */
+ @Contract(pure = true)
+ public @NotNull String getQuestId() {
+ return this.questId;
}
- public boolean isCompleted() {
- return completed;
+ /**
+ * @return the associated player ID
+ * @see QuestProgress#getPlayerUUID()
+ */
+ @Deprecated(forRemoval = true)
+ @Contract(pure = true)
+ public @NotNull UUID getPlayer() {
+ return this.playerUUID;
}
- public void setCompleted(boolean completed) {
- this.completed = completed;
- this.modified = true;
+ /**
+ * @return the associated player ID
+ */
+ @Contract(pure = true)
+ public @NotNull UUID getPlayerUUID() {
+ return this.playerUUID;
+ }
+
+ /**
+ * @return mutable task progress map
+ */
+ @Contract(pure = true)
+ public @NotNull Map<String, TaskProgress> getTaskProgressMap() {
+ return this.taskProgressMap;
}
+ /**
+ * @return mutable task progress map values collection
+ * @see QuestProgress#getTaskProgress()
+ */
+ @Deprecated(forRemoval = true)
+ @Contract(pure = true)
+ public @NotNull Collection<TaskProgress> getTaskProgress() {
+ return this.taskProgressMap.values();
+ }
+
+ /**
+ * @return mutable task progress map values collection
+ */
+ @SuppressWarnings("unused")
+ @Contract(pure = true)
+ public @NotNull Collection<TaskProgress> getAllTaskProgress() {
+ return this.taskProgressMap.values();
+ }
+
+ /**
+ * Gets the {@link TaskProgress} for a specified task ID. Generates a new one if it does not exist.
+ *
+ * @param taskId the task ID to get the progress for
+ * @return {@link TaskProgress} or a blank generated one if the task does not exist
+ */
+ public @NotNull TaskProgress getTaskProgress(final @NotNull String taskId) {
+ final TaskProgress taskProgress = this.taskProgressMap.get(taskId);
+ if (taskProgress != null) {
+ return taskProgress;
+ }
+
+ final TaskProgress newTaskProgress = new TaskProgress(this, taskId, this.playerUUID, null, false, false);
+ this.addTaskProgress(newTaskProgress);
+ return newTaskProgress;
+ }
+
+ /**
+ * Gets the {@link TaskProgress} for a specified task ID. Returns null if it does not exist.
+ *
+ * @param taskId the task ID to get the progress for
+ * @return {@link TaskProgress} or null if the task does not exist
+ */
+ @SuppressWarnings("unused")
+ @Contract(pure = true)
+ public @Nullable TaskProgress getTaskProgressOrNull(final @NotNull String taskId) {
+ return this.taskProgressMap.get(taskId);
+ }
+
+ /**
+ * @param taskId the task ID to repair the progress for
+ */
+ @Deprecated(forRemoval = true)
+ @ApiStatus.Internal
+ public void repairTaskProgress(final @NotNull String taskId) {
+ final TaskProgress taskProgress = new TaskProgress(this, taskId, this.playerUUID, null, false, false);
+ this.addTaskProgress(taskProgress);
+ }
+
+ /**
+ * @param taskProgress the task progress to put into the task progress map
+ */
+ public void addTaskProgress(final @NotNull TaskProgress taskProgress) {
+ this.taskProgressMap.put(taskProgress.getTaskId(), taskProgress);
+ }
+
+ /**
+ * @return whether the quest is started
+ */
+ @Contract(pure = true)
public boolean isStarted() {
- return started;
+ return this.started;
}
- public void setStarted(boolean started) {
+ /**
+ * @param started whether the quest is started
+ */
+ public void setStarted(final boolean started) {
this.started = started;
this.modified = true;
}
+ /**
+ * @return the date of the last quest start
+ */
+ @Contract(pure = true)
public long getStartedDate() {
- return startedDate;
+ return this.startedDate;
}
- public void setStartedDate(long startedDate) {
+ /**
+ * @param startedDate the date of the last quest start
+ */
+ public void setStartedDate(final long startedDate) {
this.startedDate = startedDate;
this.modified = true;
}
- public long getCompletionDate() {
- return completionDate;
+ /**
+ * @return whether the quest is completed
+ */
+ @Contract(pure = true)
+ public boolean isCompleted() {
+ return this.completed;
}
- public void setCompletionDate(long completionDate) {
- this.completionDate = completionDate;
+ /**
+ * @param completed whether the quest is completed
+ */
+ public void setCompleted(final boolean completed) {
+ this.completed = completed;
this.modified = true;
}
- public UUID getPlayer() {
- return player;
- }
-
+ /**
+ * @return whether the quest has been completed before
+ */
+ @Contract(pure = true)
public boolean isCompletedBefore() {
- return completedBefore;
+ return this.completedBefore;
}
- public void setCompletedBefore(boolean completedBefore) {
+ /**
+ * @param completedBefore whether the quest has been completed before
+ */
+ public void setCompletedBefore(final boolean completedBefore) {
this.completedBefore = completedBefore;
this.modified = true;
}
- public void addTaskProgress(TaskProgress taskProgress) {
- this.taskProgress.put(taskProgress.getTaskId(), taskProgress);
+ /**
+ * @return the date of the last quest completion
+ */
+ @Contract(pure = true)
+ public long getCompletionDate() {
+ return this.completionDate;
}
- public Collection<TaskProgress> getTaskProgress() {
- return taskProgress.values();
+ /**
+ * @param completionDate the date of the last quest completion
+ */
+ public void setCompletionDate(final long completionDate) {
+ this.completionDate = completionDate;
+ this.modified = true;
}
- public Map<String, TaskProgress> getTaskProgressMap() {
- return taskProgress;
- }
+ /**
+ * @return whether the object has been modified and needs to be saved
+ */
+ @SuppressWarnings("unused")
+ @Contract(pure = true)
+ public boolean isModified() {
+ if (this.modified) {
+ return true;
+ }
- public TaskProgress getTaskProgress(String taskId) {
- TaskProgress tP = taskProgress.getOrDefault(taskId, null);
- if (tP == null) {
- repairTaskProgress(taskId);
- tP = taskProgress.getOrDefault(taskId, null);
+ for (final TaskProgress taskProgress : this.taskProgressMap.values()) {
+ if (taskProgress.isModified()) {
+ return true;
+ }
}
- return tP;
+
+ return false;
}
- public void repairTaskProgress(String taskid) {
- TaskProgress taskProgress = new TaskProgress(this, taskid, null, player, false, false);
- this.addTaskProgress(taskProgress);
+ /**
+ * It's equivalent to {@code QuestProgress#setModified(false)}.
+ *
+ * @see QuestProgress#setModified(boolean)
+ */
+ @Deprecated(forRemoval = true)
+ public void resetModified() {
+ this.setModified(false);
}
- public boolean isModified() {
- if (modified) return true;
- else {
- for (TaskProgress progress : this.taskProgress.values()) {
- if (progress.isModified()) return true;
- }
- return false;
+ /**
+ * @param modified whether the object has been modified and needs to be saved
+ */
+ public void setModified(final boolean modified) {
+ this.modified = modified;
+
+ for (final TaskProgress taskProgress : this.taskProgressMap.values()) {
+ taskProgress.setModified(modified);
}
}
+ /**
+ * Gets whether the object has non default values.
+ *
+ * <p>
+ * Fields checked are:<br>
+ * - {@link QuestProgress#started}<br>
+ * - {@link QuestProgress#startedDate}<br>
+ * - {@link QuestProgress#completed}<br>
+ * - {@link QuestProgress#completedBefore}<br>
+ * - {@link QuestProgress#completionDate}
+ * </p>
+ *
+ * @return whether the object has non default values
+ */
+ @Contract(pure = true)
public boolean hasNonDefaultValues() {
- if (this.started || this.startedDate != 0 || this.completed || this.completedBefore || this.completionDate != 0) return true;
- else {
- for (TaskProgress progress : this.taskProgress.values()) {
- if (progress.getProgress() != null || progress.isCompleted()) return true;
- }
- return false;
+ if (this.started || this.startedDate != 0L || this.completed || this.completedBefore || this.completionDate != 0L) {
+ return true;
}
- }
- public void queueForCompletionTest() {
- plugin.getQuestCompleter().queueSingular(this);
- }
-
- public void resetModified() {
- this.modified = false;
- for (TaskProgress progress : this.taskProgress.values()) {
- progress.resetModified();
+ for (final TaskProgress taskProgress : this.taskProgressMap.values()) {
+ if (taskProgress.getProgress() != null || taskProgress.isCompleted()) {
+ return true;
+ }
}
+
+ return false;
}
- public void setModified(boolean modified) {
- this.modified = modified;
- for (TaskProgress progress : this.taskProgress.values()) {
- progress.setModified(modified);
- }
+ /**
+ * Queues the {@link QuestProgress} instance for a completion test.
+ */
+ @SuppressWarnings("unused")
+ public void queueForCompletionTest() {
+ this.plugin.getQuestCompleter().queueSingular(this);
}
}
diff --git a/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/QuestProgressFile.java b/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/QuestProgressFile.java
index 0069eb12..7cadaa9a 100644
--- a/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/QuestProgressFile.java
+++ b/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/QuestProgressFile.java
@@ -4,62 +4,80 @@ import com.leonardobishop.quests.common.player.QPlayer;
import com.leonardobishop.quests.common.plugin.Quests;
import com.leonardobishop.quests.common.quest.Quest;
import com.leonardobishop.quests.common.quest.Task;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* Represents underlying quest progress for a player.
*/
-public class QuestProgressFile {
+public final class QuestProgressFile {
- private final Map<String, QuestProgress> questProgress = new HashMap<>();
- private final UUID playerUUID;
private final Quests plugin;
+ private final UUID playerUUID;
+ private final Map<String, QuestProgress> questProgressMap;
- public QuestProgressFile(UUID playerUUID, Quests plugin) {
- this.playerUUID = playerUUID;
+ /**
+ * Constructs a QuestProgressFile.
+ *
+ * @param plugin the plugin instance
+ * @param playerUUID the associated player UUID
+ */
+ public QuestProgressFile(final @NotNull Quests plugin, final @NotNull UUID playerUUID) {
this.plugin = plugin;
+ this.playerUUID = playerUUID;
+ this.questProgressMap = new HashMap<>();
}
- public QuestProgressFile(QuestProgressFile questProgressFile) {
- for (Map.Entry<String, QuestProgress> progressEntry : questProgressFile.questProgress.entrySet()) {
- questProgress.put(progressEntry.getKey(), new QuestProgress(progressEntry.getValue()));
- }
- this.playerUUID = questProgressFile.playerUUID;
+ /**
+ * Constructs a data-only clone from a QuestProgressFile instance.
+ *
+ * @param questProgressFile the quest progress file instance
+ */
+ @ApiStatus.Internal
+ public QuestProgressFile(final @NotNull QuestProgressFile questProgressFile) {
+ final Set<Map.Entry<String, QuestProgress>> progressEntries = questProgressFile.questProgressMap.entrySet();
+
this.plugin = questProgressFile.plugin;
+ this.playerUUID = questProgressFile.playerUUID;
+ this.questProgressMap = new HashMap<>(progressEntries.size());
+
+ for (final Map.Entry<String, QuestProgress> progressEntry : progressEntries) {
+ this.questProgressMap.put(progressEntry.getKey(), new QuestProgress(progressEntry.getValue()));
+ }
}
- public void addQuestProgress(QuestProgress questProgress) {
- //TODO don't do here
-// if (Options.VERIFY_QUEST_EXISTS_ON_LOAD.getBooleanValue(true) && plugin.getQuestManager().getQuestById(questProgress.getQuestId()) == null) {
-// return;
-// }
- this.questProgress.put(questProgress.getQuestId(), questProgress);
+ /**
+ * @param questProgress the quest progress to put into the quest progress map
+ */
+ public void addQuestProgress(final @NotNull QuestProgress questProgress) {
+ // TODO don't do that here
+ //if (Options.VERIFY_QUEST_EXISTS_ON_LOAD.getBooleanValue(true) && plugin.getQuestManager().getQuestById(questProgress.getQuestId()) == null) {
+ // return;
+ //}
+ this.questProgressMap.put(questProgress.getQuestId(), questProgress);
}
/**
- * Gets all manually started quests.
- * Note: if quest autostart is enabled then this may produce unexpected results as quests are
- * not "started" by the player if autostart is true. Consider {@link QPlayer#hasStartedQuest(Quest)}
- * or {@link QPlayer#getEffectiveStartedQuests()} instead.
+ * Gets all manually started quests. If quest autostart is enabled then this may produce unexpected results as
+ * quests are not "started" by the player if autostart is true. Consider {@link QPlayer#hasStartedQuest(Quest)}
+ * or {@link QPlayer#getEffectiveStartedQuests()} usage instead.
*
* @return list of started quests
*/
- public List<Quest> getStartedQuests() {
- List<Quest> startedQuests = new ArrayList<>();
- for (QuestProgress questProgress : questProgress.values()) {
- Quest quest = plugin.getQuestManager().getQuestById(questProgress.getQuestId());
- if (quest != null && questProgress.isStarted()) {
- startedQuests.add(plugin.getQuestManager().getQuestById(questProgress.getQuestId()));
- }
- }
- return startedQuests;
+ @Contract(pure = true)
+ public @NotNull List<Quest> getStartedQuests() {
+ return this.getAllQuestsFromProgress(QuestsProgressFilter.STARTED);
}
/**
@@ -68,50 +86,86 @@ public class QuestProgressFile {
*
* @return {@code List<Quest>} all quests
*/
- public List<Quest> getAllQuestsFromProgress(QuestsProgressFilter filter) {
- List<Quest> questsProgress = new ArrayList<>();
- for (QuestProgress qProgress : questProgress.values()) {
- boolean condition = false;
- if (filter == QuestsProgressFilter.STARTED) {
- condition = qProgress.isStarted();
- } else if (filter == QuestsProgressFilter.COMPLETED_BEFORE) {
- condition = qProgress.isCompletedBefore();
- } else if (filter == QuestsProgressFilter.COMPLETED) {
- condition = qProgress.isCompleted();
- } else if (filter == QuestsProgressFilter.ALL) {
- condition = true;
+ @Contract(pure = true)
+ public @NotNull List<Quest> getAllQuestsFromProgress(final @NotNull QuestsProgressFilter filter) {
+ final List<Quest> quests = new ArrayList<>();
+
+ for (final QuestProgress questProgress : this.questProgressMap.values()) {
+ final boolean matches = filter.matches(questProgress);
+ if (!matches) {
+ continue;
}
- if (condition) {
- Quest quest = plugin.getQuestManager().getQuestById(qProgress.getQuestId());
- if (quest != null) {
- questsProgress.add(quest);
- }
+
+ final Quest quest = this.plugin.getQuestManager().getQuestById(questProgress.getQuestId());
+ if (quest == null) {
+ continue;
}
+
+ quests.add(quest);
}
- return questsProgress;
+
+ return quests;
}
public enum QuestsProgressFilter {
- ALL("all"),
- COMPLETED("completed"),
- COMPLETED_BEFORE("completedBefore"),
- STARTED("started");
+ ALL("all") {
+ @Override
+ @Contract(pure = true)
+ public boolean matches(final @NotNull QuestProgress questProgress) {
+ return true;
+ }
+ },
+ COMPLETED("completed") {
+ @Override
+ @Contract(pure = true)
+ public boolean matches(final @NotNull QuestProgress questProgress) {
+ return questProgress.isCompleted();
+ }
+ },
+ COMPLETED_BEFORE("completedBefore") {
+ @Override
+ @Contract(pure = true)
+ public boolean matches(final @NotNull QuestProgress questProgress) {
+ return questProgress.isCompletedBefore();
+ }
+ },
+ STARTED("started") {
+ @Override
+ @Contract(pure = true)
+ public boolean matches(final @NotNull QuestProgress questProgress) {
+ return questProgress.isStarted();
+ }
+ };
private final String legacy;
- QuestsProgressFilter(String legacy) {
+ QuestsProgressFilter(final @NotNull String legacy) {
this.legacy = legacy;
}
- public static QuestsProgressFilter fromLegacy(String filter) {
- for (QuestsProgressFilter filterEnum : QuestsProgressFilter.values()) {
- if (filterEnum.getLegacy().equals(filter)) return filterEnum;
- }
- return QuestsProgressFilter.ALL;
+ @SuppressWarnings("unused")
+ @Contract(pure = true)
+ public @NotNull String getLegacy() {
+ return this.legacy;
}
- public String getLegacy() {
- return legacy;
+ @Contract(pure = true)
+ public abstract boolean matches(final @NotNull QuestProgress questProgress);
+
+ // And some static things to improve legacy performance (is it even used?)
+
+ private static final QuestsProgressFilter[] FILTERS = QuestsProgressFilter.values();
+
+ private static final Map<String, QuestsProgressFilter> legacyToFilterMap = new HashMap<>(QuestsProgressFilter.FILTERS.length) {{
+ for (final QuestsProgressFilter questsProgressFilter : QuestsProgressFilter.FILTERS) {
+ this.put(questsProgressFilter.legacy, questsProgressFilter);
+ }
+ }};
+
+ @SuppressWarnings("unused")
+ @Contract(pure = true)
+ public static @NotNull QuestsProgressFilter fromLegacy(final @NotNull String legacy) {
+ return QuestsProgressFilter.legacyToFilterMap.getOrDefault(legacy, QuestsProgressFilter.ALL);
}
}
@@ -120,35 +174,50 @@ public class QuestProgressFile {
*
* @return {@code Collection<QuestProgress>} all quest progresses
*/
- public Collection<QuestProgress> getAllQuestProgress() {
- return questProgress.values();
+ @Contract(pure = true)
+ public @NotNull Collection<QuestProgress> getAllQuestProgress() {
+ return this.questProgressMap.values();
}
/**
- * Checks whether or not the player has {@link QuestProgress} for a specified quest
+ * Checks whether the player has {@link QuestProgress} for a specified quest
*
* @param quest the quest to check for
* @return true if they have quest progress
*/
- public boolean hasQuestProgress(Quest quest) {
- return questProgress.containsKey(quest.getId());
+ @Contract(pure = true)
+ public boolean hasQuestProgress(final @NotNull Quest quest) {
+ return this.questProgressMap.containsKey(quest.getId());
}
/**
* Gets the remaining cooldown before being able to start a specific quest.
*
* @param quest the quest to test for
- * @return 0 if no cooldown remaining or the cooldown is disabled, otherwise the cooldown in milliseconds
+ * @return 0 if no cooldown remaining, -1 if the cooldown is disabled or the quest is not completed,
+ * otherwise the cooldown in milliseconds
*/
- public long getCooldownFor(Quest quest) {
- QuestProgress questProgress = getQuestProgress(quest);
- if (quest.isCooldownEnabled() && questProgress.isCompleted()) {
- if (questProgress.getCompletionDate() > 0) {
- long date = questProgress.getCompletionDate();
- return (date + TimeUnit.MILLISECONDS.convert(quest.getCooldown(), TimeUnit.MINUTES)) - System.currentTimeMillis();
- }
+ @Contract(pure = true)
+ public long getCooldownFor(final @NotNull Quest quest) {
+ if (!quest.isCooldownEnabled()) {
+ return -1;
+ }
+
+ final QuestProgress questProgress = this.getQuestProgressOrNull(quest);
+ if (questProgress == null || !questProgress.isCompleted()) {
+ return -1;
+ }
+
+ final long completionDate = questProgress.getCompletionDate();
+ if (completionDate == 0) {
+ return -1;
}
- return 0;
+
+ final long currentTimeMillis = System.currentTimeMillis();
+ final long cooldownMillis = TimeUnit.MILLISECONDS.convert(quest.getCooldown(), TimeUnit.MINUTES);
+
+ // do the subtraction first to prevent overflow
+ return Math.max(0L, completionDate - currentTimeMillis + cooldownMillis);
}
/**
@@ -158,101 +227,134 @@ public class QuestProgressFile {
* @return 0 if no time remaining, -1 if the time limit is disabled or the quest is not started,
* otherwise the time left in milliseconds
*/
- public long getTimeRemainingFor(Quest quest) {
- QuestProgress questProgress = getQuestProgress(quest);
- if (quest.isTimeLimitEnabled() && questProgress.isStarted()) {
- return Math.max(
- questProgress.getStartedDate()
- + TimeUnit.MILLISECONDS.convert(quest.getTimeLimit(), TimeUnit.MINUTES)
- - System.currentTimeMillis()
- , 0);
+ @Contract(pure = true)
+ public long getTimeRemainingFor(final @NotNull Quest quest) {
+ if (!quest.isTimeLimitEnabled()) {
+ return -1;
+ }
+
+ final QuestProgress questProgress = this.getQuestProgressOrNull(quest);
+ if (questProgress == null || !questProgress.isStarted()) {
+ return -1;
+ }
+
+ final long startedDate = questProgress.getStartedDate();
+ if (startedDate == 0) {
+ return -1;
}
- return -1;
+
+ final long currentTimeMillis = System.currentTimeMillis();
+ final long timeLimitMillis = TimeUnit.MILLISECONDS.convert(quest.getTimeLimit(), TimeUnit.MINUTES);
+
+ // do the subtraction first to prevent overflow
+ return Math.max(0L, startedDate - currentTimeMillis + timeLimitMillis);
}
/**
- * Tests whether or not the player meets the requirements to start a specific quest.
+ * Tests whether the player meets the requirements to start a specific quest.
*
* @param quest the quest to test for
* @return true if they can start the quest
*/
- //TODO possibly move this
- public boolean hasMetRequirements(Quest quest) {
- for (String id : quest.getRequirements()) {
- Quest q = plugin.getQuestManager().getQuestById(id);
- if (q == null) {
- continue;
- }
- if (hasQuestProgress(q) && !getQuestProgress(q).isCompletedBefore()) {
+ // TODO possibly move this
+ @Contract(pure = true)
+ public boolean hasMetRequirements(final @NotNull Quest quest) {
+ for (final String requiredQuestId : quest.getRequirements()) {
+ final QuestProgress requiredQuestProgress = this.questProgressMap.get(requiredQuestId);
+ if (requiredQuestProgress == null || !requiredQuestProgress.isCompletedBefore()) {
+ // if we decide to change the method return type to states like "DOES_NOT_EXIST"
+ // or "COMPLETED_BEFORE" we will need to change the quest existance check order
return false;
- } else if (!hasQuestProgress(q)) {
+ }
+
+ final Quest requiredQuest = this.plugin.getQuestManager().getQuestById(requiredQuestId);
+ if (requiredQuest == null) {
+ // TODO not sure if we actually need this check however probably
+ // forcing the server owner to fix the quest options instead
+ // of just ignoring the fact it's broken is better?
return false;
}
}
+
return true;
}
/**
- * Get the {@link UUID} of the player this QuestProgressFile represents.
- *
- * @return UUID
+ * @return the associated player UUID
*/
- public UUID getPlayerUUID() {
- return playerUUID;
+ @Contract(pure = true)
+ public @NotNull UUID getPlayerUUID() {
+ return this.playerUUID;
}
/**
- * Get the {@link QuestProgress} for a specified {@link Quest}. Generates a new one if it does not exist.
+ * Gets the {@link QuestProgress} for a specified {@link Quest}. Generates a new one if it does not exist.
*
- * @param quest the quest to get progress for
+ * @param quest the quest to get the progress for
* @return {@link QuestProgress} or a blank generated one if the quest does not exist
*/
- public QuestProgress getQuestProgress(Quest quest) {
- QuestProgress qProgress = questProgress.get(quest.getId());
- return qProgress != null ? qProgress : generateBlankQuestProgress(quest);
+ public @NotNull QuestProgress getQuestProgress(final @NotNull Quest quest) {
+ final QuestProgress questProgress = this.getQuestProgressOrNull(quest);
+ return questProgress != null ? questProgress : this.generateBlankQuestProgress(quest);
}
/**
- * Tests whether or not the player has a specified {@link Quest} started.
+ * Gets the {@link QuestProgress} for a specified {@link Quest}. Returns null if it does not exist.
+ *
+ * @param quest the quest to get the progress for
+ * @return {@link QuestProgress} or null if the quest does not exist
+ */
+ @Contract(pure = true)
+ public @Nullable QuestProgress getQuestProgressOrNull(final @NotNull Quest quest) {
+ return this.questProgressMap.get(quest.getId());
+ }
+
+ /**
+ * Tests whether the player has a specified {@link Quest} started.
*
* @param quest the quest to check for
* @return true if player has the quest started
*/
- public boolean hasQuestStarted(Quest quest) {
- QuestProgress qProgress = questProgress.get(quest.getId());
- return qProgress != null && qProgress.isStarted();
+ @Contract(pure = true)
+ public boolean hasQuestStarted(final @NotNull Quest quest) {
+ final QuestProgress questProgress = this.getQuestProgressOrNull(quest);
+ return questProgress != null && questProgress.isStarted();
}
/**
- * Generate a new blank {@link QuestProgress} for a specified {@code quest}.
+ * Generate a new blank {@link QuestProgress} for a specified {@link Quest} with {@link QuestProgress#isModified()} set to {@code false}.
*
- * @param quest the quest to generate progress for
+ * @param quest the quest to generate the progress for
* @return the generated blank {@link QuestProgress}
*/
- public QuestProgress generateBlankQuestProgress(Quest quest) {
- return generateBlankQuestProgress(quest, false);
+ public @NotNull QuestProgress generateBlankQuestProgress(final @NotNull Quest quest) {
+ return this.generateBlankQuestProgress(quest, false);
}
/**
- * Generate a new blank {@link QuestProgress} for a specified {@code quest}.
+ * Generate a new blank {@link QuestProgress} for a specified {@link Quest}.
*
- * @param quest the quest to generate progress for
+ * @param quest the quest to generate the progress for
* @param modified the modified state of the quest
* @return the generated blank {@link QuestProgress}
*/
- public QuestProgress generateBlankQuestProgress(Quest quest, boolean modified) {
- QuestProgress questProgress = new QuestProgress(plugin, quest.getId(), false, false, 0, playerUUID, false, 0, modified);
- for (Task task : quest.getTasks()) {
- TaskProgress taskProgress = new TaskProgress(questProgress, task.getId(), null, playerUUID, false, modified);
+ public @NotNull QuestProgress generateBlankQuestProgress(final @NotNull Quest quest, final boolean modified) {
+ final QuestProgress questProgress = new QuestProgress(this.plugin, quest.getId(), this.playerUUID, false, 0L, false, false, 0L, modified);
+
+ for (final Task task : quest.getTasks()) {
+ final TaskProgress taskProgress = new TaskProgress(questProgress, task.getId(), this.playerUUID, null, false, modified);
questProgress.addTaskProgress(taskProgress);
}
- addQuestProgress(questProgress);
+ this.addQuestProgress(questProgress);
return questProgress;
}
+ /**
+ * Clears quest progress map.
+ */
public void clear() {
- questProgress.clear();
+ this.questProgressMap.clear();
}
/**
@@ -261,15 +363,17 @@ public class QuestProgressFile {
* set the modified flag in that case.
*/
public void reset() {
- for (QuestProgress questProgress : questProgress.values()) {
+ for (final QuestProgress questProgress : this.questProgressMap.values()) {
if (!questProgress.hasNonDefaultValues()) {
continue;
}
- Quest quest = plugin.getQuestManager().getQuestById(questProgress.getQuestId());
+
+ final Quest quest = this.plugin.getQuestManager().getQuestById(questProgress.getQuestId());
if (quest == null) {
continue;
}
- generateBlankQuestProgress(quest, true);
+
+ this.generateBlankQuestProgress(quest, true);
}
}
@@ -278,41 +382,61 @@ public class QuestProgressFile {
*/
@Deprecated
public void clean() {
- plugin.getQuestsLogger().debug("Cleaning file " + playerUUID + ".");
- if (!plugin.getTaskTypeManager().areRegistrationsOpen()) {
- ArrayList<String> invalidQuests = new ArrayList<>();
- for (String questId : this.questProgress.keySet()) {
- Quest q;
- if ((q = plugin.getQuestManager().getQuestById(questId)) == null) {
- invalidQuests.add(questId);
- } else {
- ArrayList<String> invalidTasks = new ArrayList<>();
- for (String taskId : this.questProgress.get(questId).getTaskProgressMap().keySet()) {
- if (q.getTaskById(taskId) == null) {
- invalidTasks.add(taskId);
- }
- }
- for (String taskId : invalidTasks) {
- this.questProgress.get(questId).getTaskProgressMap().remove(taskId);
+ this.plugin.getQuestsLogger().debug("Cleaning file " + this.playerUUID + ".");
+
+ if (!this.plugin.getTaskTypeManager().areRegistrationsOpen()) {
+ final List<String> invalidQuestIds = new ArrayList<>();
+
+ for (final Map.Entry<String, QuestProgress> questProgressEntry : this.questProgressMap.entrySet()) {
+ final String questId = questProgressEntry.getKey();
+
+ final Quest quest = this.plugin.getQuestManager().getQuestById(questId);
+ if (quest == null) {
+ invalidQuestIds.add(questId);
+
+ // tasks will be removed with the quest
+ continue;
+ }
+
+ final QuestProgress questProgress = questProgressEntry.getValue();
+ final Map<String, TaskProgress> taskProgressMap = questProgress.getTaskProgressMap();
+ final List<String> invalidTaskIds = new ArrayList<>();
+
+ for (final String taskId : taskProgressMap.keySet()) {
+ final Task task = quest.getTaskById(taskId);
+
+ if (task == null) {
+ invalidTaskIds.add(taskId);
}
}
+
+ for (final String taskId : invalidTaskIds) {
+ taskProgressMap.remove(taskId);
+ }
}
- for (String questId : invalidQuests) {
- this.questProgress.remove(questId);
+
+ for (final String questId : invalidQuestIds) {
+ this.questProgressMap.remove(questId);
}
}
}
+ /**
+ * It's equivalent to {@code QuestProgressFile#setModified(false)}.
+ *
+ * @see QuestProgressFile#setModified(boolean)
+ */
+ @Deprecated(forRemoval = true)
public void resetModified() {
- for (QuestProgress questProgress : questProgress.values()) {
- questProgress.resetModified();
- }
+ this.setModified(false);
}
- public void setModified(boolean modified) {
- for (QuestProgress questProgress : questProgress.values()) {
+ /**
+ * @param modified whether the object has been modified and needs to be saved
+ */
+ public void setModified(final boolean modified) {
+ for (final QuestProgress questProgress : this.questProgressMap.values()) {
questProgress.setModified(modified);
}
}
-
}
diff --git a/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/TaskProgress.java b/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/TaskProgress.java
index a78a4d25..9395634e 100644
--- a/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/TaskProgress.java
+++ b/common/src/main/java/com/leonardobishop/quests/common/player/questprogressfile/TaskProgress.java
@@ -1,84 +1,163 @@
package com.leonardobishop.quests.common.player.questprogressfile;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Objects;
import java.util.UUID;
-public class TaskProgress {
+public final class TaskProgress {
- private final String taskid;
- private final UUID player;
+ private final QuestProgress questProgress;
+ private final String taskId;
+ private final UUID playerUUID;
- private QuestProgress linkedQuestProgress;
- private boolean modified;
private Object progress;
private boolean completed;
+ private boolean modified;
- public TaskProgress(QuestProgress linkedQuestProgress, String taskid, Object progress, UUID player, boolean completed) {
- this.linkedQuestProgress = linkedQuestProgress;
- this.taskid = taskid;
+ /**
+ * Constructs a TaskProgress.
+ *
+ * @param questProgress the quest progress
+ * @param taskId the associated task ID
+ * @param playerUUID the associated player UUID
+ * @param progress the progress object
+ * @param completed whether the task is completed
+ * @param modified whether the object has been modified and needs to be saved
+ */
+ public TaskProgress(final @Nullable QuestProgress questProgress, final @NotNull String taskId, final @NotNull UUID playerUUID, final @Nullable Object progress, final boolean completed, final boolean modified) {
+ this.questProgress = questProgress;
+ this.taskId = taskId;
+ this.playerUUID = playerUUID;
this.progress = progress;
- this.player = player;
this.completed = completed;
+ this.modified = modified;
}
- public TaskProgress(QuestProgress linkedQuestProgress, String taskid, Object progress, UUID player, boolean completed, boolean modified) {
- this(linkedQuestProgress, taskid, progress, player, completed);
- this.modified = modified;
+ /**
+ * Constructs a TaskProgress with {@link TaskProgress#modified} set to {@code false}.
+ *
+ * @param questProgress the quest progress
+ * @param taskId the associated task ID
+ * @param playerUUID the associated player UUID
+ * @param progress the progress object
+ * @param completed whether the task is completed
+ */
+ public TaskProgress(final @NotNull QuestProgress questProgress, final @NotNull String taskId, final @NotNull UUID playerUUID, final @Nullable Object progress, final boolean completed) {
+ this(questProgress, taskId, playerUUID, progress, completed, false);
}
- public TaskProgress(TaskProgress taskProgress) {
- this.taskid = taskProgress.taskid;
- this.player = taskProgress.player;
- this.modified = taskProgress.modified;
- this.progress = taskProgress.progress;
- this.completed = taskProgress.completed;
+ /**
+ * Constructs a data-only clone from a TaskProgress instance.
+ *
+ * @param taskProgress the task progress instance
+ */
+ @ApiStatus.Internal
+ public TaskProgress(final @NotNull TaskProgress taskProgress) {
+ this(null, taskProgress.taskId, taskProgress.playerUUID, taskProgress.progress, taskProgress.completed, taskProgress.modified);
}
- public String getTaskId() {
- return taskid;
+ /**
+ * @return the associated task ID
+ */
+ @Contract(pure = true)
+ public @NotNull String getTaskId() {
+ return this.taskId;
}
- public Object getProgress() {
- return progress;
+ /**
+ * @return the associated player ID
+ * @see QuestProgress#getPlayerUUID()
+ */
+ @Deprecated(forRemoval = true)
+ @Contract(pure = true)
+ public @NotNull UUID getPlayer() {
+ return this.playerUUID;
}
- public void setProgress(Object progress) {
- if (this.progress != progress) this.modified = true;
+ /**
+ * @return the associated player ID
+ */
+ @Contract(pure = true)
+ public @NotNull UUID getPlayerUUID() {
+ return this.playerUUID;
+ }
- this.progress = progress;
+ /**
+ * @return the progress object
+ */
+ @Contract(pure = true)
+ public @Nullable Object getProgress() {
+ return this.progress;
}
- public UUID getPlayer() {
- return player;
+ /**
+ * @param progress the progress object
+ */
+ public void setProgress(final @Nullable Object progress) {
+ if (Objects.equals(progress, this.progress)) {
+ return;
+ }
+
+ this.progress = progress;
+ this.modified = true;
}
+ /**
+ * @return whether the task is completed
+ */
+ @Contract(pure = true)
public boolean isCompleted() {
- return completed;
+ return this.completed;
}
- public void setCompleted(boolean complete) {
+ /**
+ * @param completed whether the task is completed
+ */
+ public void setCompleted(final boolean completed) {
+ if (this.questProgress == null) {
+ throw new UnsupportedOperationException("associated quest progress cannot be null");
+ }
+
// do not queue completion for already completed quests
// https://github.com/LMBishop/Quests/issues/543
- if (this.completed == complete) {
+ if (this.completed == completed) {
return;
}
- this.completed = complete;
+ this.completed = completed;
this.modified = true;
- if (complete) {
- linkedQuestProgress.queueForCompletionTest();
+ if (completed) {
+ this.questProgress.queueForCompletionTest();
}
}
+ /**
+ * @return whether the object has been modified and needs to be saved
+ */
+ @Contract(pure = true)
public boolean isModified() {
- return modified;
+ return this.modified;
}
+ /**
+ * It's equivalent to {@code TaskProgress#setModified(false)}.
+ *
+ * @see TaskProgress#setModified(boolean)
+ */
+ @Deprecated(forRemoval = true)
public void resetModified() {
- this.modified = false;
+ this.setModified(false);
}
- public void setModified(boolean modified) {
+ /**
+ * @param modified whether the object has been modified and needs to be saved
+ */
+ public void setModified(final boolean modified) {
this.modified = modified;
}
}
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 ba0ee8c8..a9c7b52f 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
@@ -1,61 +1,91 @@
package com.leonardobishop.quests.common.storage;
-import com.leonardobishop.quests.common.player.questprogressfile.QuestProgressFile;
+import com.leonardobishop.quests.common.player.QPlayerData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.io.IOException;
import java.util.List;
+import java.util.Objects;
import java.util.UUID;
/**
- * The storage provider is responsible for obtaining a QuestProgressFile for a specified UUID and for
- * writing a QuestProgressFile.
+ * The StorageProvider interface defines the contract for a storage system that handles the persistence
+ * of player data, such as player preferences and quest progress, for specific players identified by their UUIDs.
+ * Implementations of this interface are responsible for the actual storage and retrieval of this data.
*/
public interface StorageProvider {
- String getName();
+ /**
+ * Retrieves the name of this storage provider.
+ *
+ * @return the name of the storage provider
+ */
+ @NotNull String getName();
- void init();
+ /**
+ * Initializes the storage provider, preparing it for use. This method should be called before any
+ * other operations are performed. Initialization may involve setting up connections or loading necessary resources.
+ */
+ void init() throws IOException;
+ /**
+ * Shuts down the storage provider, ensuring that any open resources are properly closed and that
+ * any pending data is safely stored. This method should be called during the application's shutdown process.
+ */
void shutdown();
/**
- * Load a QuestProgressFile from the data source by a specific UUID
+ * Loads the player data associated with the given UUID from the storage.
*
- * @param uuid the UUID to load
- * @return {@link QuestProgressFile} or null
+ * @param uuid the unique identifier of the player whose data is to be loaded
+ * @return the {@link QPlayerData} for the player, or null if no data is found for the given UUID
*/
- @Nullable QuestProgressFile loadProgressFile(@NotNull UUID uuid);
+ @Nullable QPlayerData loadPlayerData(final @NotNull UUID uuid);
/**
- * Save a QuestProgressFile to the data source with a specific UUID
+ * Saves the given player data to the storage.
*
- * @param uuid the uuid to match the file to
- * @param questProgressFile the file to save
+ * @param playerData the {@link QPlayerData} object containing the player's data to be saved
+ * @return true if the data was successfully saved, false otherwise
*/
- boolean saveProgressFile(@NotNull UUID uuid, @NotNull QuestProgressFile questProgressFile);
+ boolean savePlayerData(final @NotNull QPlayerData playerData);
/**
- * Load all QuestProgressFiles
+ * Loads all player data available in the storage.
*
- * @return {@link List<QuestProgressFile>}
+ * @return a list of {@link QPlayerData} objects
*/
- @NotNull List<QuestProgressFile> loadAllProgressFiles();
+ @NotNull List<QPlayerData> loadAllPlayerData();
/**
- * Save a list of QuestProgressFiles
+ * Saves all provided player data to the storage.
*
- * @param files the list of QuestProgressFile to save
- **/
- void saveAllProgressFiles(List<QuestProgressFile> files);
+ * @param allPlayerData a list of {@link QPlayerData} objects to be saved
+ * @return true if the data was successfully saved, false otherwise
+ */
+ default boolean saveAllPlayerData(final @NotNull List<QPlayerData> allPlayerData) {
+ Objects.requireNonNull(allPlayerData, "allPlayerData cannot be null");
+
+ // fault check is not needed here as the method
+ // saving single player data already handles that,
+ // and it's actually the one we need to check
+
+ boolean result = true;
+
+ for (final QPlayerData playerData : allPlayerData) {
+ result &= this.savePlayerData(playerData);
+ }
+
+ return result;
+ }
/**
- * Whether this provider is 'similar' to another one.
- * Similarity is determined if the provider effectively points to the same data source.
+ * Compares this storage provider with another to determine if they are similar.
+ * Similarity is determined by effectively pointing to the same data source.
*
- * @param provider the provider to compare to
- * @return true if similar, false otherwise
+ * @param otherProvider another StorageProvider to compare against
+ * @return true if the two storage providers are considered similar, false otherwise
*/
- boolean isSimilar(StorageProvider provider);
-
+ boolean isSimilar(final @NotNull StorageProvider otherProvider);
}