diff --git a/src/main/java/org/linia/linizen/Linizen.java b/src/main/java/org/linia/linizen/Linizen.java index e0660e3..7549bba 100644 --- a/src/main/java/org/linia/linizen/Linizen.java +++ b/src/main/java/org/linia/linizen/Linizen.java @@ -1,15 +1,13 @@ package org.linia.linizen; import com.denizenscript.denizencore.utilities.debugging.Debug; -import com.infernalsuite.asp.api.world.SlimeWorldInstance; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.linia.linizen.bridges.ASP.ASPBridge; +import org.linia.linizen.bridges.ASP.SlimeWorldFlagHandler; import org.linia.linizen.extensions.ExtensionsRegistry; import org.linia.linizen.oneblock.OneBlock; -import java.io.IOException; - public class Linizen extends JavaPlugin { public static Plugin instance; @@ -25,16 +23,9 @@ public void onEnable() { @Override public void onDisable() { - Debug.log("Saving slimeworlds..."); - for (SlimeWorldInstance s : ASPBridge.instance.getLoadedWorlds()) { - try { - ASPBridge.instance.saveWorld(s); - } - catch (IOException e) { - Debug.echoError(e); - } - } + SlimeWorldFlagHandler.flushAll(); + ASPBridge.saveAll(); } } diff --git a/src/main/java/org/linia/linizen/bridges/ASP/ASPBridge.java b/src/main/java/org/linia/linizen/bridges/ASP/ASPBridge.java index 0b28ce7..5c877a1 100644 --- a/src/main/java/org/linia/linizen/bridges/ASP/ASPBridge.java +++ b/src/main/java/org/linia/linizen/bridges/ASP/ASPBridge.java @@ -7,6 +7,7 @@ import com.denizenscript.denizencore.objects.core.MapTag; import com.denizenscript.denizencore.tags.PseudoObjectTagBase; import com.denizenscript.denizencore.tags.TagManager; +import com.denizenscript.denizencore.utilities.debugging.Debug; import com.denizenscript.denizencore.utilities.text.StringHolder; import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; @@ -14,6 +15,7 @@ import com.infernalsuite.asp.api.exceptions.UnknownWorldException; import com.infernalsuite.asp.api.loaders.SlimeLoader; import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; import org.linia.linizen.bridges.ASP.commands.FileLoaderCommand; import org.linia.linizen.bridges.ASP.commands.SlimeWorldCommand; @@ -48,7 +50,18 @@ public void init() { }); } - static class ASPTagBase extends PseudoObjectTagBase { + public static void saveAll() { + for (SlimeWorldInstance s : instance.getLoadedWorlds()) { + try { + instance.saveWorld(s); + } + catch (IOException e) { + Debug.echoError(e); + } + } + } + + public static class ASPTagBase extends PseudoObjectTagBase { public static ASPTagBase instance; @@ -71,6 +84,17 @@ public void register() { return new ListTag(ASPBridge.instance.getLoadedWorlds(), SlimeWorldTag::new); }); + // <--[tag] + // @attribute ]> + // @returns ElementTag(Boolean) + // @plugin Linizen, ASP + // @description + // Returns whether the world with a given name is loaded. + // --> + tagProcessor.registerTag(ElementTag.class, ElementTag.class, "is_loaded", (attribute, object, input) -> { + return new ElementTag(ASPBridge.instance.getLoadedWorld(input.asString()) != null); + }); + // <--[tag] // @attribute // @returns ListTag diff --git a/src/main/java/org/linia/linizen/bridges/ASP/SlimeWorldFlagHandler.java b/src/main/java/org/linia/linizen/bridges/ASP/SlimeWorldFlagHandler.java new file mode 100644 index 0000000..3cec1ff --- /dev/null +++ b/src/main/java/org/linia/linizen/bridges/ASP/SlimeWorldFlagHandler.java @@ -0,0 +1,50 @@ +package org.linia.linizen.bridges.ASP; + +import com.denizenscript.denizencore.flags.SavableMapFlagTracker; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.StringBinaryTag; + +import java.util.HashMap; +import java.util.Map; + +public class SlimeWorldFlagHandler { + + static final String EXTRA_DATA_KEY = "denizen_flags"; + + public static final HashMap trackers = new HashMap<>(); + + public static void loadFlags(SlimeWorld world) { + BinaryTag raw = world.getExtraData().get(EXTRA_DATA_KEY); + SavableMapFlagTracker tracker = (raw instanceof StringBinaryTag s) ? new SavableMapFlagTracker(s.value()) : new SavableMapFlagTracker(); + tracker.doTotalClean(); + trackers.put(world.getName(), tracker); + } + + public static void initEmpty(String worldName) { + trackers.put(worldName, new SavableMapFlagTracker()); + } + + public static void flushToWorld(SlimeWorld world) { + SavableMapFlagTracker tracker = trackers.get(world.getName()); + if (tracker == null || !tracker.modified) { + return; + } + world.getExtraData().put(EXTRA_DATA_KEY, StringBinaryTag.stringBinaryTag(tracker.toString())); + tracker.modified = false; + } + + public static void flushAll() { + for (Map.Entry entry : trackers.entrySet()) { + SlimeWorldInstance sw = ASPBridge.instance.getLoadedWorld(entry.getKey()); + if (sw != null) { + flushToWorld(sw); + } + } + } + + public static void unloadFlags(String worldName) { + trackers.remove(worldName); + } +} diff --git a/src/main/java/org/linia/linizen/bridges/ASP/commands/SlimeWorldCommand.java b/src/main/java/org/linia/linizen/bridges/ASP/commands/SlimeWorldCommand.java index 67b079a..a108e14 100644 --- a/src/main/java/org/linia/linizen/bridges/ASP/commands/SlimeWorldCommand.java +++ b/src/main/java/org/linia/linizen/bridges/ASP/commands/SlimeWorldCommand.java @@ -17,6 +17,7 @@ import org.bukkit.Material; import org.bukkit.World; import org.linia.linizen.bridges.ASP.ASPBridge; +import org.linia.linizen.bridges.ASP.SlimeWorldFlagHandler; import org.linia.linizen.bridges.ASP.objects.FileWorldLoaderTag; import org.linia.linizen.bridges.ASP.objects.SlimeWorldTag; import org.linia.linizen.utils.ExecutorUtil; @@ -94,6 +95,7 @@ public static void autoExecute(ScriptEntry scriptEntry, ExecutorUtil.runSyncAndWait(() -> { try { ASPBridge.instance.loadWorld(slimeWorld, true); + SlimeWorldFlagHandler.loadFlags(slimeWorld); World world = Bukkit.getWorld(name); if (world != null) { Location loc = new Location(world, 0, 61, 0); @@ -127,6 +129,7 @@ public static void autoExecute(ScriptEntry scriptEntry, ExecutorUtil.runSyncAndWait(() -> { try { ASPBridge.instance.loadWorld(slimeWorld, true); + SlimeWorldFlagHandler.loadFlags(slimeWorld); scriptEntry.saveObject("loaded_world", new SlimeWorldTag(slimeWorld)); } catch (IllegalArgumentException e) { @@ -161,6 +164,7 @@ public static void autoExecute(ScriptEntry scriptEntry, ExecutorUtil.runSyncAndWait(() -> { try { ASPBridge.instance.loadWorld(cloned, true); + SlimeWorldFlagHandler.initEmpty(cloned.getName()); scriptEntry.saveObject("cloned_world", new SlimeWorldTag(cloned)); } catch (IllegalArgumentException e) { diff --git a/src/main/java/org/linia/linizen/bridges/ASP/objects/SlimeWorldTag.java b/src/main/java/org/linia/linizen/bridges/ASP/objects/SlimeWorldTag.java index ef826c0..c385360 100644 --- a/src/main/java/org/linia/linizen/bridges/ASP/objects/SlimeWorldTag.java +++ b/src/main/java/org/linia/linizen/bridges/ASP/objects/SlimeWorldTag.java @@ -1,6 +1,8 @@ package org.linia.linizen.bridges.ASP.objects; import com.denizenscript.denizen.objects.WorldTag; +import com.denizenscript.denizencore.flags.AbstractFlagTracker; +import com.denizenscript.denizencore.flags.FlaggableObject; import com.denizenscript.denizencore.objects.Adjustable; import com.denizenscript.denizencore.objects.Fetchable; import com.denizenscript.denizencore.objects.Mechanism; @@ -14,10 +16,12 @@ import org.bukkit.Bukkit; import org.bukkit.World; import org.linia.linizen.bridges.ASP.ASPBridge; +import org.linia.linizen.bridges.ASP.SlimeWorldFlagHandler; import java.io.IOException; +import java.lang.ref.WeakReference; -public class SlimeWorldTag implements ObjectTag, Adjustable { +public class SlimeWorldTag implements ObjectTag, Adjustable, FlaggableObject { @Fetchable("sw@") public static SlimeWorldTag valueOf(String string, TagContext context) { @@ -35,43 +39,48 @@ public static boolean matches(String string) { return valueOf(string, null) != null; } - /* - * Maybe in the future if it becomes RAM issue, we could save only worldsName - * and not whole instance. In which case we would need to reconstruct it each - * time we are calling it. (With something like SlimeWorldTag#getSlimeWorld) - */ - public SlimeWorld slimeWorld; + private final String worldName; + private final WeakReference slimeWorldRef; public SlimeWorldTag(SlimeWorld slimeWorld) { - this.slimeWorld = slimeWorld; + this.worldName = slimeWorld.getName(); + this.slimeWorldRef = new WeakReference<>(slimeWorld); + } + + public SlimeWorld getSlimeWorld() { + return slimeWorldRef.get(); + } + + @Override + public AbstractFlagTracker getFlagTracker() { + return SlimeWorldFlagHandler.trackers.get(worldName); + } + + @Override + public void reapplyTracker(AbstractFlagTracker tracker) { + } + + @Override + public String getReasonNotFlaggable() { + return "is the SlimeWorld loaded?"; } public static void register() { - // <--[tag] - // @attribute - // @returns ElementTag(Boolean) - // @plugin Linizen, ASP - // @description - // Returns whether the world is actually loaded. - // --> - tagProcessor.registerTag(ElementTag.class, "is_loaded", (attribute, object) -> { - try { - return new ElementTag(object.slimeWorld.getLoader().worldExists(object.slimeWorld.getName())); - } catch (IOException e) { - return new ElementTag(false); - } - }); + AbstractFlagTracker.registerFlagHandlers(tagProcessor); // <--[tag] - // @attribute + // @attribute // @returns FileWorldLoaderTag // @plugin Linizen, ASP // @description // Returns a file loader which this world is loaded from, if any. // --> tagProcessor.registerTag(FileWorldLoaderTag.class, "get_file_loader", (attribute, object) -> { - return object.slimeWorld.getLoader() instanceof FileWorldLoaderTag f ? f : null; + if (!requireLoadedWorld(object, attribute)) { + return null; + } + return (object.getSlimeWorld().getLoader() instanceof FileWorldLoaderTag f) ? f : null; }); // <--[tag] @@ -82,7 +91,18 @@ public static void register() { // Returns a world representing this slimeworld. // --> tagProcessor.registerTag(WorldTag.class, "as_world", (attribute, object) -> { - return new WorldTag(object.getWorld()); + return requireLoadedWorld(object, attribute) ? new WorldTag(object.getWorld()) : null; + }); + + // <--[tag] + // @attribute + // @returns ElementTag + // @plugin Linizen, ASP + // @description + // Returns the name of the slimeworld. (Works even when unloaded) + // --> + tagProcessor.registerStaticTag(ElementTag.class, "name", (attribute, object) -> { + return new ElementTag(object.worldName); }); // <--[mechanism] @@ -93,8 +113,13 @@ public static void register() { // Saves the world. // --> tagProcessor.registerMechanism("save", false, (object, mechanism) -> { + if (!requireLoadedWorld(object, mechanism)) { + return; + } + SlimeWorld sw = object.getSlimeWorld(); try { - ASPBridge.instance.saveWorld(object.slimeWorld); + SlimeWorldFlagHandler.flushToWorld(sw); + ASPBridge.instance.saveWorld(sw); } catch (IOException e) { mechanism.echoError("Could not save world"); } @@ -109,6 +134,9 @@ public static void register() { // Unloads a world // --> tagProcessor.registerMechanism("unload", false, ElementTag.class, (object, mechanism, input) -> { + if (!requireLoadedWorld(object, mechanism)) { + return; + } if (!input.isBoolean()) { mechanism.echoError("Must provide boolean"); return; @@ -118,14 +146,41 @@ public static void register() { mechanism.echoError("Could not unload world, players are there."); return; } - if (!Bukkit.unloadWorld(world, input.asBoolean())) { + boolean inp = input.asBoolean(); + if (inp) { + SlimeWorldFlagHandler.flushToWorld(object.getSlimeWorld()); + } + if (!Bukkit.unloadWorld(world, inp)) { mechanism.echoError("Saving for SlimeWorld " + world.getName() + " refused by system."); } + else { + SlimeWorldFlagHandler.unloadFlags(object.worldName); + } }); } + public static boolean requireLoadedWorld(SlimeWorldTag slimeWorldTag, Mechanism mechanism) { + if (!slimeWorldTag.isLoaded()) { + mechanism.echoError("World '" + slimeWorldTag.worldName + "' is unloaded, cannot adjust mechanism."); + return false; + } + return true; + } + + public static boolean requireLoadedWorld(SlimeWorldTag slimeWorldTag, Attribute attribute) { + if (!slimeWorldTag.isLoaded()) { + attribute.echoError("World '" + slimeWorldTag.worldName + "' is unloaded, cannot process tag."); + return false; + } + return true; + } + + public boolean isLoaded() { + return getSlimeWorld() == null || !ASPBridge.instance.worldLoaded(getSlimeWorld()); + } + public World getWorld() { - return Bukkit.getWorld(slimeWorld.getName()); + return Bukkit.getWorld(worldName); } public String prefix = "SlimeWorld"; @@ -147,7 +202,7 @@ public boolean isUnique() { @Override public String identify() { - return "sw@" + slimeWorld.getName(); + return "sw@" + worldName; } @Override