From c8a6d01b0bdf708cd24c7212fa31b68e820ae3c1 Mon Sep 17 00:00:00 2001 From: TomTom <93038247+AverageGithub@users.noreply.github.com> Date: Mon, 19 Aug 2024 18:52:11 +0200 Subject: [PATCH] Add reloading --- .../axminions/AxMinionsPlugin.java | 12 ++---- .../axminions/command/AxMinionsCommand.java | 43 +++++++++++++++---- .../axminions/config/Minions.java | 5 ++- .../axminions/listeners/WorldListener.java | 8 +--- .../axminions/minions/Minion.java | 6 +++ .../axminions/minions/MinionArea.java | 18 ++++++++ .../axminions/minions/MinionSaver.java | 6 ++- .../axminions/minions/MinionWorldCache.java | 27 ++++++++++++ 8 files changed, 97 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/artillexstudios/axminions/AxMinionsPlugin.java b/src/main/java/com/artillexstudios/axminions/AxMinionsPlugin.java index 46742b0..b4c4e8e 100644 --- a/src/main/java/com/artillexstudios/axminions/AxMinionsPlugin.java +++ b/src/main/java/com/artillexstudios/axminions/AxMinionsPlugin.java @@ -76,7 +76,9 @@ public final class AxMinionsPlugin extends AxPlugin { DataHandler.setup().thenRun(() -> LogUtils.debug("Loaded database!")); - this.reload(); + Language.reload(); + Skins.reload(); + Minions.reload(); for (World world : Bukkit.getWorlds()) { MinionWorldCache.loadArea(world); @@ -124,12 +126,4 @@ public final class AxMinionsPlugin extends AxPlugin { AsyncUtils.stop(); DatabaseConnector.getInstance().close(); } - - @Override - public void reload() { - Config.reload(); - Language.reload(); - Skins.reload(); - Minions.reload(); - } } diff --git a/src/main/java/com/artillexstudios/axminions/command/AxMinionsCommand.java b/src/main/java/com/artillexstudios/axminions/command/AxMinionsCommand.java index 2be1707..413cc56 100644 --- a/src/main/java/com/artillexstudios/axminions/command/AxMinionsCommand.java +++ b/src/main/java/com/artillexstudios/axminions/command/AxMinionsCommand.java @@ -9,6 +9,7 @@ import com.artillexstudios.axminions.config.Config; import com.artillexstudios.axminions.config.Language; import com.artillexstudios.axminions.config.Minions; import com.artillexstudios.axminions.config.Skins; +import com.artillexstudios.axminions.database.DataHandler; import com.artillexstudios.axminions.minions.Level; import com.artillexstudios.axminions.minions.Minion; import com.artillexstudios.axminions.minions.MinionArea; @@ -18,6 +19,7 @@ import com.artillexstudios.axminions.minions.MinionWorldCache; import com.artillexstudios.axminions.utils.Direction; import com.artillexstudios.axminions.utils.FileUtils; import com.artillexstudios.axminions.utils.LocationUtils; +import com.artillexstudios.axminions.utils.LogUtils; import dev.jorel.commandapi.CommandTree; import dev.jorel.commandapi.arguments.IntegerArgument; import dev.jorel.commandapi.arguments.LiteralArgument; @@ -26,6 +28,7 @@ import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -34,6 +37,8 @@ import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; public final class AxMinionsCommand { @@ -75,6 +80,10 @@ public final class AxMinionsCommand { long start = System.nanoTime(); List failed = new ArrayList<>(); + for (World world : Bukkit.getWorlds()) { + MinionWorldCache.clear(world); + } + if (!Config.reload()) { failed.add(FileUtils.PLUGIN_DIRECTORY.resolve("config.yml").toFile()); } @@ -89,15 +98,31 @@ public final class AxMinionsCommand { Minions.reload(); failed.addAll(Minions.failedToLoad()); - // TODO: refresh minions - if (failed.isEmpty()) { - MessageUtils.sendMessage(sender, Language.PREFIX, Language.RELOAD_SUCCESS, Placeholder.parsed("time", Long.toString((System.nanoTime() - start) / 1_000_000))); - } else { - MessageUtils.sendMessage(sender, Language.PREFIX, Language.RELOAD_FAIL, Placeholder.parsed("time", Long.toString((System.nanoTime() - start) / 1_000_000)), Placeholder.parsed("files", String.join(", ", failed.stream() - .map(File::getName) - .toList()) - )); - } + + CompletableFuture[] futures = Minions.loadingMinions().toArray(new CompletableFuture[0]); + List worlds = Bukkit.getWorlds(); + AtomicInteger counter = new AtomicInteger(); + CompletableFuture.allOf(futures).thenRun(() -> { + Minions.loadingMinions().clear(); + + for (World world : worlds) { + DataHandler.loadMinions(world).toCompletableFuture().thenAccept(loaded -> { + LogUtils.debug("Loaded {} minions in world {} in {} ms!", loaded.firstInt(), world.getName(), loaded.secondLong() / 1_000_000); + if (counter.incrementAndGet() != futures.length * worlds.size()) { + return; + } + + if (failed.isEmpty()) { + MessageUtils.sendMessage(sender, Language.PREFIX, Language.RELOAD_SUCCESS, Placeholder.parsed("time", Long.toString((System.nanoTime() - start) / 1_000_000))); + } else { + MessageUtils.sendMessage(sender, Language.PREFIX, Language.RELOAD_FAIL, Placeholder.parsed("time", Long.toString((System.nanoTime() - start) / 1_000_000)), Placeholder.parsed("files", String.join(", ", failed.stream() + .map(File::getName) + .toList()) + )); + } + }); + } + }); }) ) .then(new LiteralArgument("debug") diff --git a/src/main/java/com/artillexstudios/axminions/config/Minions.java b/src/main/java/com/artillexstudios/axminions/config/Minions.java index 61fc861..751bcda 100644 --- a/src/main/java/com/artillexstudios/axminions/config/Minions.java +++ b/src/main/java/com/artillexstudios/axminions/config/Minions.java @@ -13,12 +13,13 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; public final class Minions { private static final Minions INSTANCE = new Minions(); private final File minionsDirectory = com.artillexstudios.axminions.utils.FileUtils.PLUGIN_DIRECTORY.resolve("minions").toFile(); private final ObjectArrayList failedToLoad = new ObjectArrayList<>(); - private final ObjectArrayList> loadingMinions = new ObjectArrayList<>(); + private final ConcurrentLinkedQueue> loadingMinions = new ConcurrentLinkedQueue<>(); private final List failedToLoadImmutable = Collections.unmodifiableList(failedToLoad); public static void reload() { @@ -61,7 +62,7 @@ public final class Minions { } } - public static ObjectArrayList> loadingMinions() { + public static ConcurrentLinkedQueue> loadingMinions() { return INSTANCE.loadingMinions; } diff --git a/src/main/java/com/artillexstudios/axminions/listeners/WorldListener.java b/src/main/java/com/artillexstudios/axminions/listeners/WorldListener.java index 8993ece..0fb6f4e 100644 --- a/src/main/java/com/artillexstudios/axminions/listeners/WorldListener.java +++ b/src/main/java/com/artillexstudios/axminions/listeners/WorldListener.java @@ -1,6 +1,5 @@ package com.artillexstudios.axminions.listeners; -import com.artillexstudios.axminions.minions.MinionArea; import com.artillexstudios.axminions.minions.MinionWorldCache; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -16,10 +15,7 @@ public final class WorldListener implements Listener { @EventHandler public void onWorldUnloadEvent(WorldUnloadEvent event) { - MinionArea area = MinionWorldCache.remove(event.getWorld()); - - if (area != null) { - // TODO: Remove all minions from the area - } + MinionWorldCache.clear(event.getWorld()); + MinionWorldCache.remove(event.getWorld()); } } diff --git a/src/main/java/com/artillexstudios/axminions/minions/Minion.java b/src/main/java/com/artillexstudios/axminions/minions/Minion.java index 9e721c0..32976ee 100644 --- a/src/main/java/com/artillexstudios/axminions/minions/Minion.java +++ b/src/main/java/com/artillexstudios/axminions/minions/Minion.java @@ -142,7 +142,13 @@ public final class Minion { this.entity.spawn(); } + // Remove removes the minion from the database, + // while destroy removes the minion from the world public void remove() { + this.destroy(); + } + + public void destroy() { this.entity.remove(); } diff --git a/src/main/java/com/artillexstudios/axminions/minions/MinionArea.java b/src/main/java/com/artillexstudios/axminions/minions/MinionArea.java index 83100c4..0046f10 100644 --- a/src/main/java/com/artillexstudios/axminions/minions/MinionArea.java +++ b/src/main/java/com/artillexstudios/axminions/minions/MinionArea.java @@ -227,6 +227,24 @@ public final class MinionArea { } } + public void clear() { + this.writeLock.lock(); + try { + this.writeCount++; + // Load the list into stack memory for faster access + ObjectArrayList positions = this.positions; + + // We don't want to reevaluate the size of the list + int size = positions.size(); + for (int i = 0; i < size; i++) { + ChunkPos pos = positions.get(i); + pos.minions().clear(); + } + } finally { + this.writeLock.unlock(); + } + } + public long readCount() { return this.readCount; } diff --git a/src/main/java/com/artillexstudios/axminions/minions/MinionSaver.java b/src/main/java/com/artillexstudios/axminions/minions/MinionSaver.java index d9c6c22..baf7607 100644 --- a/src/main/java/com/artillexstudios/axminions/minions/MinionSaver.java +++ b/src/main/java/com/artillexstudios/axminions/minions/MinionSaver.java @@ -33,7 +33,9 @@ public final class MinionSaver { } public void stop() { - this.future.cancel(false); - this.future = null; + if (this.future != null && !this.future.isCancelled()) { + this.future.cancel(false); + this.future = null; + } } } diff --git a/src/main/java/com/artillexstudios/axminions/minions/MinionWorldCache.java b/src/main/java/com/artillexstudios/axminions/minions/MinionWorldCache.java index 4b2a131..f8c008f 100644 --- a/src/main/java/com/artillexstudios/axminions/minions/MinionWorldCache.java +++ b/src/main/java/com/artillexstudios/axminions/minions/MinionWorldCache.java @@ -23,12 +23,22 @@ public final class MinionWorldCache { public static void add(Minion minion) { minions.add(minion); MinionArea area = worlds.get(minion.location().getWorld()); + if (area == null) { + LogUtils.error("Tried to add minion to unknown world! {}", minion); + return; + } + area.load(minion); } public static void remove(Minion minion) { minions.remove(minion); MinionArea area = worlds.get(minion.location().getWorld()); + if (area == null) { + LogUtils.error("Tried to remove minion from unknown world! {}", minion); + return; + } + area.remove(minion); } @@ -40,6 +50,23 @@ public final class MinionWorldCache { return worlds.remove(world); } + public static void clear(World world) { + LogUtils.debug("Worlds map pre clear: {}", worlds); + MinionArea area = getArea(world); + if (area == null) { + LogUtils.error("Tried to remove minion from unknown world {}! Map: {}", world.getName(), worlds); + return; + } + + area.forEachPos(position -> { + for (Minion minion : position.minions()) { + minion.destroy(); + } + }); + + area.clear(); + } + public static ObjectArrayList minions() { return minions; }