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!")); DataHandler.setup().thenRun(() -> LogUtils.debug("Loaded database!"));
this.reload(); Language.reload();
Skins.reload();
Minions.reload();
for (World world : Bukkit.getWorlds()) { for (World world : Bukkit.getWorlds()) {
MinionWorldCache.loadArea(world); MinionWorldCache.loadArea(world);
@ -124,12 +126,4 @@ public final class AxMinionsPlugin extends AxPlugin {
AsyncUtils.stop(); AsyncUtils.stop();
DatabaseConnector.getInstance().close(); 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.Language;
import com.artillexstudios.axminions.config.Minions; import com.artillexstudios.axminions.config.Minions;
import com.artillexstudios.axminions.config.Skins; import com.artillexstudios.axminions.config.Skins;
import com.artillexstudios.axminions.database.DataHandler;
import com.artillexstudios.axminions.minions.Level; import com.artillexstudios.axminions.minions.Level;
import com.artillexstudios.axminions.minions.Minion; import com.artillexstudios.axminions.minions.Minion;
import com.artillexstudios.axminions.minions.MinionArea; 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.Direction;
import com.artillexstudios.axminions.utils.FileUtils; import com.artillexstudios.axminions.utils.FileUtils;
import com.artillexstudios.axminions.utils.LocationUtils; import com.artillexstudios.axminions.utils.LocationUtils;
import com.artillexstudios.axminions.utils.LogUtils;
import dev.jorel.commandapi.CommandTree; import dev.jorel.commandapi.CommandTree;
import dev.jorel.commandapi.arguments.IntegerArgument; import dev.jorel.commandapi.arguments.IntegerArgument;
import dev.jorel.commandapi.arguments.LiteralArgument; 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.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -34,6 +37,8 @@ import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
public final class AxMinionsCommand { public final class AxMinionsCommand {
@ -75,6 +80,10 @@ public final class AxMinionsCommand {
long start = System.nanoTime(); long start = System.nanoTime();
List<File> failed = new ArrayList<>(); List<File> failed = new ArrayList<>();
for (World world : Bukkit.getWorlds()) {
MinionWorldCache.clear(world);
}
if (!Config.reload()) { if (!Config.reload()) {
failed.add(FileUtils.PLUGIN_DIRECTORY.resolve("config.yml").toFile()); failed.add(FileUtils.PLUGIN_DIRECTORY.resolve("config.yml").toFile());
} }
@ -89,15 +98,31 @@ public final class AxMinionsCommand {
Minions.reload(); Minions.reload();
failed.addAll(Minions.failedToLoad()); failed.addAll(Minions.failedToLoad());
// TODO: refresh minions
if (failed.isEmpty()) { CompletableFuture<?>[] futures = Minions.loadingMinions().toArray(new CompletableFuture[0]);
MessageUtils.sendMessage(sender, Language.PREFIX, Language.RELOAD_SUCCESS, Placeholder.parsed("time", Long.toString((System.nanoTime() - start) / 1_000_000))); List<World> worlds = Bukkit.getWorlds();
} else { AtomicInteger counter = new AtomicInteger();
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() CompletableFuture.allOf(futures).thenRun(() -> {
.map(File::getName) Minions.loadingMinions().clear();
.toList())
)); 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") .then(new LiteralArgument("debug")

View File

@ -13,12 +13,13 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
public final class Minions { public final class Minions {
private static final Minions INSTANCE = new Minions(); private static final Minions INSTANCE = new Minions();
private final File minionsDirectory = com.artillexstudios.axminions.utils.FileUtils.PLUGIN_DIRECTORY.resolve("minions").toFile(); private final File minionsDirectory = com.artillexstudios.axminions.utils.FileUtils.PLUGIN_DIRECTORY.resolve("minions").toFile();
private final ObjectArrayList<File> failedToLoad = new ObjectArrayList<>(); 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); private final List<File> failedToLoadImmutable = Collections.unmodifiableList(failedToLoad);
public static void reload() { 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; return INSTANCE.loadingMinions;
} }

View File

@ -1,6 +1,5 @@
package com.artillexstudios.axminions.listeners; package com.artillexstudios.axminions.listeners;
import com.artillexstudios.axminions.minions.MinionArea;
import com.artillexstudios.axminions.minions.MinionWorldCache; import com.artillexstudios.axminions.minions.MinionWorldCache;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -16,10 +15,7 @@ public final class WorldListener implements Listener {
@EventHandler @EventHandler
public void onWorldUnloadEvent(WorldUnloadEvent event) { public void onWorldUnloadEvent(WorldUnloadEvent event) {
MinionArea area = MinionWorldCache.remove(event.getWorld()); MinionWorldCache.clear(event.getWorld());
MinionWorldCache.remove(event.getWorld());
if (area != null) {
// TODO: Remove all minions from the area
}
} }
} }

View File

@ -142,7 +142,13 @@ public final class Minion {
this.entity.spawn(); this.entity.spawn();
} }
// Remove removes the minion from the database,
// while destroy removes the minion from the world
public void remove() { public void remove() {
this.destroy();
}
public void destroy() {
this.entity.remove(); 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() { public long readCount() {
return this.readCount; return this.readCount;
} }

View File

@ -33,7 +33,9 @@ public final class MinionSaver {
} }
public void stop() { public void stop() {
this.future.cancel(false); if (this.future != null && !this.future.isCancelled()) {
this.future = null; this.future.cancel(false);
this.future = null;
}
} }
} }

View File

@ -23,12 +23,22 @@ public final class MinionWorldCache {
public static void add(Minion minion) { public static void add(Minion minion) {
minions.add(minion); minions.add(minion);
MinionArea area = worlds.get(minion.location().getWorld()); MinionArea area = worlds.get(minion.location().getWorld());
if (area == null) {
LogUtils.error("Tried to add minion to unknown world! {}", minion);
return;
}
area.load(minion); area.load(minion);
} }
public static void remove(Minion minion) { public static void remove(Minion minion) {
minions.remove(minion); minions.remove(minion);
MinionArea area = worlds.get(minion.location().getWorld()); MinionArea area = worlds.get(minion.location().getWorld());
if (area == null) {
LogUtils.error("Tried to remove minion from unknown world! {}", minion);
return;
}
area.remove(minion); area.remove(minion);
} }
@ -40,6 +50,23 @@ public final class MinionWorldCache {
return worlds.remove(world); 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() { public static ObjectArrayList<Minion> minions() {
return minions; return minions;
} }