From 252239ebe580e6789210a4fad3761c3e410f7979 Mon Sep 17 00:00:00 2001 From: LMBishop <13875753+LMBishop@users.noreply.github.com> Date: Thu, 16 Jun 2022 16:20:57 +0100 Subject: Use computeIfAbsent exclsively on async thread (closes #404) --- .../quests/bukkit/listener/PlayerJoinListener.java | 28 ++++++------- .../quests/bukkit/util/CommandUtils.java | 30 +++++++------- .../quests/common/player/QPlayerManager.java | 46 +++++++++++++++------- 3 files changed, 60 insertions(+), 44 deletions(-) diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/listener/PlayerJoinListener.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/listener/PlayerJoinListener.java index 2dff4913..bdce5260 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/listener/PlayerJoinListener.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/listener/PlayerJoinListener.java @@ -33,24 +33,24 @@ public class PlayerJoinListener implements Listener { } final Player player = event.getPlayer(); - plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, () -> { + plugin.getServer().getScheduler().runTaskLater(plugin, () -> { if (!player.isOnline()) return; - plugin.getPlayerManager().loadPlayer(player.getUniqueId()); - plugin.getServer().getScheduler().runTask(plugin, () -> { - QPlayer qPlayer = plugin.getPlayerManager().getPlayer(player.getUniqueId()); + plugin.getPlayerManager().loadPlayer(player.getUniqueId()).thenAccept(qPlayer -> { if (qPlayer == null) return; - // run a full check to check for any missed quest completions - plugin.getQuestCompleter().queueFullCheck(qPlayer.getQuestProgressFile()); - - // track first quest - if (plugin.getConfig().getBoolean("options.allow-quest-track") && plugin.getConfig().getBoolean("options.quest-autotrack")) { - for (Quest quest : plugin.getQuestManager().getQuests().values()) { - if (qPlayer.hasStartedQuest(quest)) { - qPlayer.trackQuest(quest); - break; + plugin.getScheduler().doSync(() -> { + // run a full check to check for any missed quest completions + plugin.getQuestCompleter().queueFullCheck(qPlayer.getQuestProgressFile()); + + // track first quest + if (plugin.getConfig().getBoolean("options.allow-quest-track") && plugin.getConfig().getBoolean("options.quest-autotrack")) { + for (Quest quest : plugin.getQuestManager().getQuests().values()) { + if (qPlayer.hasStartedQuest(quest)) { + qPlayer.trackQuest(quest); + break; + } } } - } + }); }); }, plugin.getQuestsConfig().getInt("options.storage.synchronisation.delay-loading", 0)); } diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CommandUtils.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CommandUtils.java index ca14e6ac..89c5418c 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CommandUtils.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CommandUtils.java @@ -12,6 +12,7 @@ import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import java.util.*; +import java.util.concurrent.ExecutionException; import java.util.function.Consumer; public class CommandUtils { @@ -104,8 +105,9 @@ public class CommandUtils { QPlayer qPlayer = plugin.getPlayerManager().getPlayer(uuid); if (qPlayer == null) { Messages.COMMAND_QUEST_ADMIN_LOADDATA.send(sender, "{player}", username); - plugin.getPlayerManager().loadPlayer(uuid); - qPlayer = plugin.getPlayerManager().getPlayer(uuid); + try { + qPlayer = plugin.getPlayerManager().loadPlayer(uuid).get(); + } catch (InterruptedException | ExecutionException ignored) {} } if (qPlayer == null) { Messages.COMMAND_QUEST_ADMIN_NODATA.send(sender, "{player}", username); @@ -134,21 +136,17 @@ public class CommandUtils { } } - plugin.getScheduler().doAsync(() -> { - if (plugin.getPlayerManager().getPlayer(uuid) == null) { - Messages.COMMAND_QUEST_ADMIN_LOADDATA.send(sender, "{player}", username); - plugin.getPlayerManager().loadPlayer(uuid); - } - - final QPlayer qPlayer = plugin.getPlayerManager().getPlayer(uuid); - - if (qPlayer == null) { - Messages.COMMAND_QUEST_ADMIN_NODATA.send(sender, "{player}", username); - return; - } + if (plugin.getPlayerManager().getPlayer(uuid) == null) { + Messages.COMMAND_QUEST_ADMIN_LOADDATA.send(sender, "{player}", username); + plugin.getPlayerManager().loadPlayer(uuid).thenAccept((qPlayer -> { + if (qPlayer == null) { + Messages.COMMAND_QUEST_ADMIN_NODATA.send(sender, "{player}", username); + return; + } - plugin.getScheduler().doSync(() -> callback.accept(qPlayer)); - }); + plugin.getScheduler().doSync(() -> callback.accept(qPlayer)); + })); + } } public static void doSafeSave(QPlayer qPlayer, QuestProgressFile questProgressFile, BukkitQuestsPlugin plugin) { 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 aeaf225c..b9a814a0 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 @@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; /** @@ -56,10 +57,8 @@ public class QPlayerManager { Objects.requireNonNull(uuid, "uuid cannot be null"); plugin.getQuestsLogger().debug("Unloading and saving player " + uuid + "."); - qPlayers.computeIfPresent(uuid, (mapUUID, qPlayer) -> { - savePlayer(uuid); - return null; - }); + CompletableFuture future = savePlayer(uuid); + future.thenAccept((v) -> qPlayers.remove(uuid)); } /** @@ -67,13 +66,14 @@ public class QPlayerManager { * The modified status of the progress file will be reset. * * @param uuid the uuid of the player + * @return completable future */ - public void savePlayer(@NotNull UUID uuid) { + public CompletableFuture savePlayer(@NotNull UUID uuid) { Objects.requireNonNull(uuid, "uuid cannot be null"); QPlayer qPlayer = getPlayer(uuid); - if (qPlayer == null) return; - savePlayer(uuid, qPlayer.getQuestProgressFile()); + if (qPlayer == null) return CompletableFuture.completedFuture(null); + return savePlayer(uuid, qPlayer.getQuestProgressFile()); } /** @@ -82,14 +82,22 @@ public class QPlayerManager { * * @param uuid the uuid of the player * @param originalProgressFile the quest progress file to associate with and save + * @return completable future */ - public void savePlayer(@NotNull UUID uuid, @NotNull QuestProgressFile originalProgressFile) { + public CompletableFuture savePlayer(@NotNull UUID uuid, @NotNull QuestProgressFile originalProgressFile) { Objects.requireNonNull(uuid, "uuid cannot be null"); Objects.requireNonNull(originalProgressFile, "originalProgressFile cannot be null"); + CompletableFuture future = new CompletableFuture<>(); + QuestProgressFile clonedProgressFile = new QuestProgressFile(originalProgressFile); originalProgressFile.resetModified(); - plugin.getScheduler().doAsync(() -> save(uuid, clonedProgressFile)); + plugin.getScheduler().doAsync(() -> { + save(uuid, clonedProgressFile); + future.complete(null); + }); + + return future; } /** @@ -148,17 +156,27 @@ public class QPlayerManager { /** * Load the player if they exist, otherwise create a new {@link QuestProgressFile}. - * This will have no effect if player is already loaded. Can be invoked asynchronously. + * This will have no effect if player is already loaded. * * @param uuid the uuid of the player + * @return completable future with the loaded player, or null if there was an error */ - public void loadPlayer(UUID uuid) { + public CompletableFuture loadPlayer(UUID uuid) { plugin.getQuestsLogger().debug("Loading player " + uuid + "."); - qPlayers.computeIfAbsent(uuid, s -> { + + CompletableFuture future = new CompletableFuture<>(); + plugin.getScheduler().doAsync(() -> { QuestProgressFile questProgressFile = storageProvider.loadProgressFile(uuid); - if (questProgressFile == null) return null; - return new QPlayer(plugin, uuid, new QPlayerPreferences(null), questProgressFile, activeQuestController); + if (questProgressFile == null) { + future.complete(null); + return; + } + QPlayer qPlayer = new QPlayer(plugin, uuid, new QPlayerPreferences(null), questProgressFile, activeQuestController); + qPlayers.computeIfAbsent(uuid, s -> qPlayer); + future.complete(qPlayer); }); + + return future; } /** -- cgit v1.2.3-70-g09d2