Add reloading

This commit is contained in:
TomTom 2024-08-19 18:52:11 +02:00
parent ea55ef432b
commit c8a6d01b0b
8 changed files with 97 additions and 28 deletions

View File

@ -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();
}
}

View File

@ -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<File> 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<World> 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")

View File

@ -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<File> failedToLoad = new ObjectArrayList<>();
private final ObjectArrayList<CompletableFuture<Void>> loadingMinions = new ObjectArrayList<>();
private final ConcurrentLinkedQueue<CompletableFuture<Void>> loadingMinions = new ConcurrentLinkedQueue<>();
private final List<File> failedToLoadImmutable = Collections.unmodifiableList(failedToLoad);
public static void reload() {
@ -61,7 +62,7 @@ public final class Minions {
}
}
public static ObjectArrayList<CompletableFuture<Void>> loadingMinions() {
public static ConcurrentLinkedQueue<CompletableFuture<Void>> loadingMinions() {
return INSTANCE.loadingMinions;
}

View File

@ -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());
}
}

View File

@ -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();
}

View File

@ -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<ChunkPos> 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;
}

View File

@ -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;
}
}
}

View File

@ -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<Minion> minions() {
return minions;
}