浏览代码

NPCs are now properly stored and loaded upon world loading etc

Jan 3 周之前
父节点
当前提交
cbf8df3d63
共有 22 个文件被更改,包括 299 次插入260 次删除
  1. 1 1
      pom.xml
  2. 3 4
      src/main/java/me/lethunderhawk/fluxapi/main/FluxAPI.java
  3. 55 18
      src/main/java/me/lethunderhawk/fluxapi/npc/NPCModule.java
  4. 10 4
      src/main/java/me/lethunderhawk/fluxapi/npc/abstraction/NPC.java
  5. 84 0
      src/main/java/me/lethunderhawk/fluxapi/npc/abstraction/NPCLocation.java
  6. 19 9
      src/main/java/me/lethunderhawk/fluxapi/npc/abstraction/NPCOptions.java
  7. 2 3
      src/main/java/me/lethunderhawk/fluxapi/npc/command/NPCCommand.java
  8. 2 2
      src/main/java/me/lethunderhawk/fluxapi/npc/listener/NPCListener.java
  9. 20 0
      src/main/java/me/lethunderhawk/fluxapi/npc/listener/WorldLoadListener.java
  10. 60 9
      src/main/java/me/lethunderhawk/fluxapi/npc/manager/NPCManager.java
  11. 32 0
      src/main/java/me/lethunderhawk/fluxapi/npc/registry/NPCRegistry.java
  12. 6 23
      src/main/java/me/lethunderhawk/fluxapi/profile/ProfileManager.java
  13. 1 1
      src/main/java/me/lethunderhawk/fluxapi/profile/ProfileModule.java
  14. 0 8
      src/main/java/me/lethunderhawk/fluxapi/profile/config/abstraction/ConfigFactory.java
  15. 0 10
      src/main/java/me/lethunderhawk/fluxapi/profile/config/abstraction/PersistentConfig.java
  16. 0 18
      src/main/java/me/lethunderhawk/fluxapi/profile/config/abstraction/subconfig/ProfileSubConfig.java
  17. 0 9
      src/main/java/me/lethunderhawk/fluxapi/profile/config/abstraction/subconfig/ProfileSubConfigFactory.java
  18. 0 65
      src/main/java/me/lethunderhawk/fluxapi/profile/config/registry/ConfigService.java
  19. 0 31
      src/main/java/me/lethunderhawk/fluxapi/profile/config/registry/ProfileModuleRegistry.java
  20. 1 0
      src/main/java/me/lethunderhawk/fluxapi/profile/listener/JoinListener.java
  21. 2 44
      src/main/java/me/lethunderhawk/fluxapi/profile/representation/FluxProfile.java
  22. 1 1
      src/main/java/me/lethunderhawk/fluxapi/util/config/ConfigLoader.java

+ 1 - 1
pom.xml

@@ -6,7 +6,7 @@
 
     <groupId>me.lethunderhawk</groupId>
     <artifactId>FluxAPI</artifactId>
-    <version>1.2.0</version>
+    <version>1.2.1-SNAPSHOT.2</version>
     <packaging>jar</packaging>
 
     <name>FluxAPI</name>

+ 3 - 4
src/main/java/me/lethunderhawk/fluxapi/main/FluxAPI.java

@@ -2,8 +2,7 @@ package me.lethunderhawk.fluxapi.main;
 
 import me.lethunderhawk.fluxapi.FluxService;
 import me.lethunderhawk.fluxapi.npc.NPCModule;
-import me.lethunderhawk.fluxapi.profile.ProfileModule;
-import me.lethunderhawk.fluxapi.profile.config.ConfigLoader;
+import me.lethunderhawk.fluxapi.util.config.ConfigLoader;
 import org.bukkit.plugin.java.JavaPlugin;
 
 public class FluxAPI extends JavaPlugin {
@@ -15,8 +14,8 @@ public class FluxAPI extends JavaPlugin {
         ConfigLoader configLoader = new ConfigLoader(this);
         FluxService.register(ConfigLoader.class, configLoader);
 
-        ProfileModule profile = new ProfileModule(this);
-        profile.onEnable();
+        //ProfileModule profile = new ProfileModule(this);
+        //profile.onEnable();
 
         NPCModule module = new NPCModule(this);
         module.onEnable();

+ 55 - 18
src/main/java/me/lethunderhawk/fluxapi/npc/NPCModule.java

@@ -2,21 +2,25 @@ package me.lethunderhawk.fluxapi.npc;
 
 import me.lethunderhawk.fluxapi.FluxService;
 import me.lethunderhawk.fluxapi.npc.abstraction.DummyNPC;
+import me.lethunderhawk.fluxapi.npc.abstraction.NPCLocation;
 import me.lethunderhawk.fluxapi.npc.abstraction.NPCOptions;
 import me.lethunderhawk.fluxapi.npc.command.NPCCommand;
 import me.lethunderhawk.fluxapi.npc.listener.NPCListener;
+import me.lethunderhawk.fluxapi.npc.listener.WorldLoadListener;
 import me.lethunderhawk.fluxapi.npc.manager.NPCManager;
-import me.lethunderhawk.fluxapi.profile.config.ConfigLoader;
+import me.lethunderhawk.fluxapi.npc.registry.NPCRegistry;
+import me.lethunderhawk.fluxapi.util.config.ConfigLoader;
 import me.lethunderhawk.fluxapi.util.interfaces.BazaarFluxModule;
+import org.bukkit.Bukkit;
 import org.bukkit.configuration.serialization.ConfigurationSerialization;
 import org.bukkit.event.HandlerList;
 import org.bukkit.plugin.java.JavaPlugin;
 
-public class NPCModule extends BazaarFluxModule {
+public final class NPCModule extends BazaarFluxModule {
     private final String saveLocation = "/npc/npcs.yml";
     private NPCManager npcManager;
     private NPCListener listener;
-
+    private WorldLoadListener worldLoadListener;
     public NPCModule(JavaPlugin plugin) {
         super(plugin);
     }
@@ -27,40 +31,73 @@ public class NPCModule extends BazaarFluxModule {
 
     @Override
     public void onEnable() {
+        FluxService.registerModule(NPCModule.class, this);
+
+        registerBaseNPCSerializables();
+        if(NPCRegistry.isRegistered()){
+            finalizeRegistration();
+        }else{
+            printWaitingNotif();
+        }
+        registerListeners();
+
+
+        NPCCommand npcCommand = new NPCCommand(this);
+        plugin.getCommand("npc").setExecutor(npcCommand);
+        plugin.getCommand("npc").setTabCompleter(npcCommand);
+    }
+
+    private void registerListeners() {
+        this.listener = new NPCListener();
+        plugin.getServer().getPluginManager().registerEvents(listener, plugin);
+        this.worldLoadListener = new WorldLoadListener();
+        plugin.getServer().getPluginManager().registerEvents(worldLoadListener, plugin);
+    }
+
+    private void printWaitingNotif() {
+        Bukkit.getLogger().info("FluxAPI enabled. Waiting for NPC registration...");
+    }
+
+    private void registerBaseNPCSerializables() {
         ConfigurationSerialization.registerClass(NPCOptions.class);
         ConfigurationSerialization.registerClass(NPCManager.class);
         ConfigurationSerialization.registerClass(DummyNPC.class);
+        ConfigurationSerialization.registerClass(NPCLocation.class);
+    }
 
+    public void finalizeRegistration() {
         this.npcManager = FluxService.get(ConfigLoader.class).loadObject(
                 saveLocation,
                 "manager",
                 NPCManager.class
         );
 
-
+        if(npcManager == null) {
+            npcManager = new NPCManager();
+        }
 
         FluxService.register(NPCManager.class, npcManager);
         npcManager.initializeSpawning();
-
-        this.listener = new NPCListener();
-        plugin.getServer().getPluginManager().registerEvents(listener, plugin);
-
-        NPCCommand npcCommand = new NPCCommand(this);
-        plugin.getCommand("npc").setExecutor(npcCommand);
-        plugin.getCommand("npc").setTabCompleter(npcCommand);
-
-
-        plugin.getLogger().info("NPC Extension Enabled");
+        plugin.getLogger().info("NPC Extension Enabled and fully registered!");
     }
 
     @Override
     public void onDisable() {
-        npcManager.saveNPCsToFile(saveLocation);
-        npcManager.deleteAllNpcEntities();
+        if(npcManager != null) {
+            npcManager.saveNPCsToFile(saveLocation);
+            npcManager.deleteAllNpcEntities();
+        }
+        unregisterListeners();
 
-        HandlerList.unregisterAll(listener);
         FluxService.unregister(NPCManager.class);
-        listener = null;
+
         npcManager = null;
     }
+
+    private void unregisterListeners() {
+        HandlerList.unregisterAll(listener);
+        listener = null;
+        HandlerList.unregisterAll(worldLoadListener);
+        worldLoadListener = null;
+    }
 }

+ 10 - 4
src/main/java/me/lethunderhawk/fluxapi/npc/abstraction/NPC.java

@@ -37,7 +37,7 @@ public abstract class NPC implements ConfigurationSerializable {
         Entity entity = npcOptions.getEntity();
         if(entity != null) {
             entity.remove();
-            npcOptions.setEntityUUID(null);
+            npcOptions.setEntity(null);
         }
     }
 
@@ -56,9 +56,7 @@ public abstract class NPC implements ConfigurationSerializable {
 
     public void lookAt(Location target) {
         Entity entity = npcOptions.getEntity();
-        entity = Bukkit.getEntity(entity.getUniqueId());
         if (target == null || entity == null) {
-            Bukkit.getLogger().info("HEY I SHOULD LOOK!!");
             return;
         }
 
@@ -95,10 +93,17 @@ public abstract class NPC implements ConfigurationSerializable {
         if (yaw < -180.0F) yaw += 360.0F;
         return yaw;
     }
+    private boolean isSpawned(){
+        return npcOptions.getEntity() != null;
+    }
 
     public Entity spawn(){
+        if(!npcOptions.getNPCLocation().isWorldLoaded() || isSpawned()){
+            return null;
+        }
         Entity entity = npcOptions.getLocation().getWorld().spawnEntity(npcOptions.getLocation(), npcOptions.getEntityType());
 
+
         if(npcOptions.getEntityType() == EntityType.MANNEQUIN){
             Mannequin mannequin = (Mannequin) entity;
 
@@ -116,12 +121,13 @@ public abstract class NPC implements ConfigurationSerializable {
 
         entity.customName(Component.text(npcOptions.getName()));
         entity.setCustomNameVisible(npcOptions.isNameVisible());
+        entity.setPersistent(false);
         entity.setGravity(false);
 
         if(entity instanceof LivingEntity livingEntity){
             livingEntity.setAI(false);
         }
-        npcOptions.setEntityUUID(entity.getUniqueId());
+        npcOptions.setEntity(entity);
         Bukkit.getLogger().info("Spawned entity UUID: " + entity.getUniqueId());
         return entity;
     }

+ 84 - 0
src/main/java/me/lethunderhawk/fluxapi/npc/abstraction/NPCLocation.java

@@ -0,0 +1,84 @@
+package me.lethunderhawk.fluxapi.npc.abstraction;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
+import org.bukkit.util.NumberConversions;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.Nullable;
+
+import java.util.Map;
+
+public final class NPCLocation implements ConfigurationSerializable {
+
+    private final String worldName;
+    private final double x;
+    private final double y;
+    private final double z;
+    private final float yaw;
+    private final float pitch;
+
+    public NPCLocation(String worldName, double x, double y, double z, float yaw, float pitch) {
+        this.worldName = worldName;
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        this.yaw = yaw;
+        this.pitch = pitch;
+    }
+
+    public NPCLocation(Location location) {
+        this(
+                location.getWorld().getName(),
+                location.getX(),
+                location.getY(),
+                location.getZ(),
+                location.getYaw(),
+                location.getPitch()
+        );
+    }
+
+    /**
+     * Returns a Bukkit Location if the world is loaded.
+     * Otherwise, returns null safely.
+     */
+    public @Nullable Location toBukkitLocation() {
+        World world = Bukkit.getWorld(worldName);
+        if (world == null) {
+            return null;
+        }
+        return new Location(world, x, y, z, yaw, pitch);
+    }
+
+    public boolean isWorldLoaded() {
+        return Bukkit.getWorld(worldName) != null;
+    }
+
+    @Override
+    public @NotNull Map<String, Object> serialize() {
+        return Map.of(
+                "world", worldName,
+                "x", x,
+                "y", y,
+                "z", z,
+                "yaw", yaw,
+                "pitch", pitch
+        );
+    }
+
+    public static @NotNull NPCLocation deserialize(@NotNull Map<String, Object> args) {
+        return new NPCLocation(
+                (String) args.get("world"),
+                NumberConversions.toDouble(args.get("x")),
+                NumberConversions.toDouble(args.get("y")),
+                NumberConversions.toDouble(args.get("z")),
+                NumberConversions.toFloat(args.get("yaw")),
+                NumberConversions.toFloat(args.get("pitch"))
+        );
+    }
+
+    public String getWorldName() {
+        return worldName;
+    }
+}

+ 19 - 9
src/main/java/me/lethunderhawk/fluxapi/npc/abstraction/NPCOptions.java

@@ -1,6 +1,5 @@
 package me.lethunderhawk.fluxapi.npc.abstraction;
 
-import org.bukkit.Bukkit;
 import org.bukkit.Location;
 import org.bukkit.configuration.serialization.ConfigurationSerializable;
 import org.bukkit.entity.Entity;
@@ -17,13 +16,14 @@ public class NPCOptions implements ConfigurationSerializable {
     private String name;
     private String texture;
     private String signature;
-    private Location location;
+    private NPCLocation location;
     private boolean visibleName = true;
     private boolean listed;
     private EntityType entityType = EntityType.MANNEQUIN;
     private String description = "";
     private boolean lookingAtNearest = false;
-    private UUID npcUUID, entityUUID;
+    private UUID npcUUID;
+    private Entity entity = null;
 
     public NPCOptions(UUID uuid){
         this.npcUUID = uuid;
@@ -42,6 +42,10 @@ public class NPCOptions implements ConfigurationSerializable {
     }
 
     public Location getLocation() {
+        return location.toBukkitLocation();
+    }
+
+    public NPCLocation getNPCLocation() {
         return location;
     }
 
@@ -86,7 +90,11 @@ public class NPCOptions implements ConfigurationSerializable {
         return this;
     }
 
-    public NPCOptions setLocation(Location location) {
+    public NPCOptions setBukkitLocation(Location location) {
+        this.location = new NPCLocation(location);
+        return this;
+    }
+    public NPCOptions setNPCLocation(NPCLocation location) {
         this.location = location;
         return this;
     }
@@ -122,15 +130,16 @@ public class NPCOptions implements ConfigurationSerializable {
 
 
     public Entity getEntity() {
-        return Bukkit.getEntity(entityUUID);
+        return entity;
     }
 
-    public void setEntityUUID(UUID entityUUID) {
-        this.entityUUID = entityUUID;
+    public void setEntity(Entity entity) {
+        this.entity = entity;
     }
 
     public @Nullable UUID getEntityUUID() {
-        return entityUUID;
+        if(entity == null) return null;
+        return entity.getUniqueId();
     }
 
     @Override
@@ -162,7 +171,7 @@ public class NPCOptions implements ConfigurationSerializable {
         options.setTexture((String) map.get("texture"));
         options.setSignature((String) map.get("signature"));
 
-        options.setLocation((Location) map.get("location"));
+        options.setNPCLocation((NPCLocation) map.get("location"));
 
         options.setVisibleName((Boolean) map.getOrDefault("visibleName", false));
         options.setListed((Boolean) map.getOrDefault("listed", false));
@@ -176,4 +185,5 @@ public class NPCOptions implements ConfigurationSerializable {
 
         return options;
     }
+
 }

+ 2 - 3
src/main/java/me/lethunderhawk/fluxapi/npc/command/NPCCommand.java

@@ -19,11 +19,9 @@ import java.util.UUID;
 import java.util.stream.Collectors;
 
 public class NPCCommand extends CustomCommand {
-    private final NPCManager manager;
 
     public NPCCommand(BazaarFluxModule module) {
         super(new CommandNode("npc", "Base npc command", null), module);
-        this.manager = FluxService.get(NPCManager.class);
     }
 
     @Override
@@ -88,12 +86,13 @@ public class NPCCommand extends CustomCommand {
         }
         NPCOptions options = new NPCOptions(UUID.randomUUID())
                 .setName(name)
-                .setLocation(p.getLocation())
+                .setBukkitLocation(p.getLocation())
                 .setTexture(stringdata[0])
                 .setSignature(stringdata[1])
                 .setLookingAtNearest(true);
 
         NPC npc = new DummyNPC(options);
+        NPCManager manager = FluxService.get(NPCManager.class);
         manager.registerNPC(npc);
         manager.spawnNpc(npc);
     }

+ 2 - 2
src/main/java/me/lethunderhawk/fluxapi/npc/listener/NPCListener.java

@@ -3,7 +3,6 @@ package me.lethunderhawk.fluxapi.npc.listener;
 import me.lethunderhawk.fluxapi.FluxService;
 import me.lethunderhawk.fluxapi.npc.abstraction.NPC;
 import me.lethunderhawk.fluxapi.npc.manager.NPCManager;
-import org.bukkit.Bukkit;
 import org.bukkit.Location;
 import org.bukkit.entity.Entity;
 import org.bukkit.entity.Player;
@@ -69,7 +68,7 @@ public final class NPCListener implements Listener {
         for (NPC npc : getRegisteredNPCs()) {
             if(!npc.getOptions().isLookingAtNearest()) continue;
             if(npc.getOptions().getEntity() == null){
-                Bukkit.getLogger().info("Entity is null for NPC UUID: " +  npc.getNpcUUID());
+                //Bukkit.getLogger().info("Entity is null for NPC UUID: " +  npc.getNpcUUID());
                 continue;
             }
             Location npcLocation = npc.getOptions().getEntity().getLocation();
@@ -96,6 +95,7 @@ public final class NPCListener implements Listener {
     }
     private NPC getNPC(@NotNull Entity entity) {
         for(NPC npc : FluxService.get(NPCManager.class).getRegisteredNPCs()){
+            if(npc.getEntityUUID() == null) continue;
             if(npc.getEntityUUID().equals(entity.getUniqueId())){
                 return npc;
             }

+ 20 - 0
src/main/java/me/lethunderhawk/fluxapi/npc/listener/WorldLoadListener.java

@@ -0,0 +1,20 @@
+package me.lethunderhawk.fluxapi.npc.listener;
+
+import me.lethunderhawk.fluxapi.FluxService;
+import me.lethunderhawk.fluxapi.npc.manager.NPCManager;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.world.WorldLoadEvent;
+import org.bukkit.event.world.WorldUnloadEvent;
+
+public class WorldLoadListener implements Listener {
+    @EventHandler
+    public void onWorldLoad(WorldLoadEvent event) {
+        FluxService.get(NPCManager.class).resolvePendingLocations(event.getWorld());
+    }
+
+    @EventHandler
+    public void onWorldUnload(WorldUnloadEvent event) {
+        FluxService.get(NPCManager.class).makeUnloadWorldRequest(event.getWorld());
+    }
+}

+ 60 - 9
src/main/java/me/lethunderhawk/fluxapi/npc/manager/NPCManager.java

@@ -2,21 +2,20 @@ package me.lethunderhawk.fluxapi.npc.manager;
 
 import me.lethunderhawk.fluxapi.FluxService;
 import me.lethunderhawk.fluxapi.npc.abstraction.NPC;
-import me.lethunderhawk.fluxapi.profile.config.ConfigLoader;
+import me.lethunderhawk.fluxapi.util.config.ConfigLoader;
 import org.bukkit.Bukkit;
+import org.bukkit.World;
 import org.bukkit.configuration.ConfigurationSection;
 import org.bukkit.configuration.file.YamlConfiguration;
 import org.bukkit.configuration.serialization.ConfigurationSerializable;
 import org.bukkit.entity.Entity;
 import org.jetbrains.annotations.NotNull;
 
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
+import java.util.*;
 
 public class NPCManager implements ConfigurationSerializable {
     private final HashMap<UUID, NPC> npcsById = new HashMap<>(); // NPCs UUID to NPC representation
+    private final Map<String, Map<UUID, NPC>> unresolvedNPCs = new HashMap<>(); // World name or path
     public NPCManager() {
     }
 
@@ -74,13 +73,23 @@ public class NPCManager implements ConfigurationSerializable {
     public void initializeSpawning() {
         Bukkit.getLogger().info("Initializing spawning. NPC count: " + npcsById.size());
         for (NPC npc : npcsById.values()) {
-            spawnNpc(npc);
+            if(spawnNpc(npc)){
+                Bukkit.getLogger().info("NPC with UUID: " + npc.getNpcUUID().toString() + " was spawned");
+            }else{
+                Bukkit.getLogger().info("NPC with UUID: " + npc.getNpcUUID().toString() + " was stored for spawning on world load");
+            }
         }
     }
 
-    public void spawnNpc(NPC npc) {
+    public boolean spawnNpc(NPC npc) {
         Bukkit.getLogger().info("Spawning NPC " + npc.getNpcUUID());
-        npc.spawn();
+        if(npc.spawn() != null){
+            return true;
+        }else{
+            String worldName = npc.getOptions().getNPCLocation().getWorldName();
+            unresolvedNPCs.computeIfAbsent(worldName, k -> new HashMap<>()).put(npc.getNpcUUID(), npc);
+            return false;
+        }
     }
 
     public Collection<NPC> getRegisteredNPCs() {
@@ -95,8 +104,50 @@ public class NPCManager implements ConfigurationSerializable {
         );
     }
 
-    public void loadNPCsFromFile(String saveLocation) {
+    public void resolvePendingLocations(@NotNull World world) {
+
+        String worldName = world.getName();
+        Map<UUID, NPC> worldMap = unresolvedNPCs.get(worldName);
+
+        if (worldMap == null) return;
+
+        Iterator<Map.Entry<UUID, NPC>> iterator = worldMap.entrySet().iterator();
+
+        while (iterator.hasNext()) {
+            NPC npc = iterator.next().getValue();
+
+            if (npc.spawn() != null) {
+                iterator.remove();
+            }
+        }
+
+        if (worldMap.isEmpty()) {
+            unresolvedNPCs.remove(worldName);
+        }
+    }
+
+    public List<NPC> getNPCsInWorld(@NotNull World world) {
+        String worldName = world.getName();
+        List<NPC> list = new ArrayList<>(unresolvedNPCs.get(worldName).values().stream().toList());
+        getRegisteredNPCs().forEach(npc -> {
+            if(npc.getOptions().getNPCLocation().getWorldName().equals(worldName)) {
+                list.add(npc);
+            }
+        });
+        return list;
+    }
+
+    public void makeUnloadWorldRequest(@NotNull World world) {
+
+        String worldName = world.getName();
+
+        Map<UUID, NPC> worldMap =
+                unresolvedNPCs.computeIfAbsent(worldName, k -> new HashMap<>());
 
+        for (NPC npc : getNPCsInWorld(world)) {
+            worldMap.put(npc.getNpcUUID(), npc); // prevents duplicates
+            npc.deleteEntity();
+        }
     }
 
     @Override public @NotNull Map<String, Object> serialize() {

+ 32 - 0
src/main/java/me/lethunderhawk/fluxapi/npc/registry/NPCRegistry.java

@@ -0,0 +1,32 @@
+package me.lethunderhawk.fluxapi.npc.registry;
+
+import me.lethunderhawk.fluxapi.FluxService;
+import me.lethunderhawk.fluxapi.npc.NPCModule;
+import me.lethunderhawk.fluxapi.npc.abstraction.NPC;
+import org.bukkit.Bukkit;
+import org.bukkit.configuration.serialization.ConfigurationSerialization;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public final class NPCRegistry {
+
+    private static final Set<Class<? extends NPC>> REGISTERED = new HashSet<>();
+    private static boolean registered = false;
+    public static void registerNPC(Class<? extends NPC> clazz) {
+        ConfigurationSerialization.registerClass(clazz);
+        REGISTERED.add(clazz);
+    }
+
+    public static boolean isRegistered() {
+        return registered;
+    }
+
+    public static void finalizeRegistration() {
+        for(Class<? extends NPC> clazz : REGISTERED){
+            Bukkit.getLogger().info("Class: " +clazz.getSimpleName() + " registered for deserialization");
+        }
+        registered = true;
+        FluxService.get(NPCModule.class).finalizeRegistration();
+    }
+}

+ 6 - 23
src/main/java/me/lethunderhawk/fluxapi/profile/ProfileManager.java

@@ -1,6 +1,6 @@
 package me.lethunderhawk.fluxapi.profile;
 
-import me.lethunderhawk.fluxapi.profile.config.ConfigLoader;
+import me.lethunderhawk.fluxapi.util.config.ConfigLoader;
 import me.lethunderhawk.fluxapi.profile.representation.FluxProfile;
 import org.bukkit.entity.Player;
 
@@ -17,9 +17,6 @@ public class ProfileManager {
         this.loader = loader;
     }
 
-    /*
-     * Lazy-load profile.
-     */
     public FluxProfile loadProfile(UUID uuid) {
 
         if (profiles.containsKey(uuid)) {
@@ -31,29 +28,16 @@ public class ProfileManager {
                 uuid.toString(),
                 FluxProfile.class
         );
+        if(profile == null) {
+            profile = createDefaultProfile(uuid);
+        }
 
         profiles.put(uuid, profile);
         return profile;
     }
 
-    /*
-     * Creates default profile with default modules.
-     */
     private FluxProfile createDefaultProfile(UUID uuid) {
-
-        FluxProfile profile = new FluxProfile(uuid.toString());
-
-        injectDefaultModules(profile);
-
-        return profile;
-    }
-
-    /*
-     * Injects default modules for new players.
-     * This is where API modules can define defaults.
-     */
-    private void injectDefaultModules(FluxProfile profile) {
-
+        return new FluxProfile(uuid.toString());
     }
 
     /*
@@ -88,6 +72,7 @@ public class ProfileManager {
 
         saveProfile(profile);
     }
+
     public void saveProfile(FluxProfile profile) {
 
         if (profile == null) return;
@@ -102,7 +87,6 @@ public class ProfileManager {
      * Save all loaded profiles.
      */
     public void saveAllProfiles() {
-
         for (FluxProfile profile : profiles.values()) {
             saveProfile(profile);
         }
@@ -112,7 +96,6 @@ public class ProfileManager {
      * Unload profile (save before removal).
      */
     public void unloadProfile(UUID uuid) {
-
         FluxProfile profile = profiles.remove(uuid);
 
         if (profile != null) {

+ 1 - 1
src/main/java/me/lethunderhawk/fluxapi/profile/ProfileModule.java

@@ -2,7 +2,7 @@ package me.lethunderhawk.fluxapi.profile;
 
 import me.lethunderhawk.fluxapi.FluxService;
 import me.lethunderhawk.fluxapi.util.interfaces.BazaarFluxModule;
-import me.lethunderhawk.fluxapi.profile.config.ConfigLoader;
+import me.lethunderhawk.fluxapi.util.config.ConfigLoader;
 import me.lethunderhawk.fluxapi.profile.listener.JoinListener;
 import org.bukkit.event.HandlerList;
 import org.bukkit.plugin.java.JavaPlugin;

+ 0 - 8
src/main/java/me/lethunderhawk/fluxapi/profile/config/abstraction/ConfigFactory.java

@@ -1,8 +0,0 @@
-package me.lethunderhawk.fluxapi.profile.config.abstraction;
-
-import org.bukkit.configuration.file.YamlConfiguration;
-
-@FunctionalInterface
-public interface ConfigFactory<T extends PersistentConfig> {
-    T deserialize(YamlConfiguration cfg);
-}

+ 0 - 10
src/main/java/me/lethunderhawk/fluxapi/profile/config/abstraction/PersistentConfig.java

@@ -1,10 +0,0 @@
-package me.lethunderhawk.fluxapi.profile.config.abstraction;
-
-import org.bukkit.configuration.file.YamlConfiguration;
-
-public interface PersistentConfig {
-
-    void serialize(YamlConfiguration cfg);
-
-    String getFileName();
-}

+ 0 - 18
src/main/java/me/lethunderhawk/fluxapi/profile/config/abstraction/subconfig/ProfileSubConfig.java

@@ -1,18 +0,0 @@
-package me.lethunderhawk.fluxapi.profile.config.abstraction.subconfig;
-
-import org.bukkit.configuration.ConfigurationSection;
-import org.bukkit.configuration.serialization.ConfigurationSerializable;
-
-public interface ProfileSubConfig extends ConfigurationSerializable {
-
-    /**
-     * Unique ID of this sub config.
-     * Used as YAML section name.
-     */
-    String getId();
-
-    /**
-     * Serialize into given section.
-     */
-    void serialize(ConfigurationSection section);
-}

+ 0 - 9
src/main/java/me/lethunderhawk/fluxapi/profile/config/abstraction/subconfig/ProfileSubConfigFactory.java

@@ -1,9 +0,0 @@
-package me.lethunderhawk.fluxapi.profile.config.abstraction.subconfig;
-
-import org.bukkit.configuration.ConfigurationSection;
-
-@FunctionalInterface
-public interface ProfileSubConfigFactory<T extends ProfileSubConfig> {
-
-    T deserialize(ConfigurationSection section);
-}

+ 0 - 65
src/main/java/me/lethunderhawk/fluxapi/profile/config/registry/ConfigService.java

@@ -1,65 +0,0 @@
-package me.lethunderhawk.fluxapi.profile.config.registry;
-
-import me.lethunderhawk.fluxapi.profile.config.abstraction.ConfigFactory;
-import me.lethunderhawk.fluxapi.profile.config.abstraction.PersistentConfig;
-import org.bukkit.configuration.file.YamlConfiguration;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-public final class ConfigService {
-
-    private static final Map<Class<? extends PersistentConfig>, ConfigFactory<?>> factories = new ConcurrentHashMap<>();
-    private static final Map<Class<? extends PersistentConfig>, PersistentConfig> loadedConfigs = new ConcurrentHashMap<>();
-
-    private ConfigService() {}
-
-    public static <T extends PersistentConfig> void register(
-            Class<T> type,
-            ConfigFactory<T> factory
-    ) {
-        factories.put(type, factory);
-    }
-
-    @SuppressWarnings("unchecked")
-    public static <T extends PersistentConfig> T load(
-            Class<T> type,
-            File file
-    ) {
-        ConfigFactory<T> factory = (ConfigFactory<T>) factories.get(type);
-
-        if (factory == null) {
-            throw new IllegalStateException("No factory registered for " + type.getName());
-        }
-
-        YamlConfiguration cfg = YamlConfiguration.loadConfiguration(file);
-        T config = factory.deserialize(cfg);
-
-        loadedConfigs.put(type, config);
-        return config;
-    }
-
-    @SuppressWarnings("unchecked")
-    public static <T extends PersistentConfig> T get(Class<T> type) {
-        return (T) loadedConfigs.get(type);
-    }
-
-    public static <T extends PersistentConfig> void save(Class<T> type, File file) {
-        T config = get(type);
-
-        if (config == null) {
-            throw new IllegalStateException("Config not loaded: " + type.getName());
-        }
-
-        YamlConfiguration cfg = new YamlConfiguration();
-        config.serialize(cfg);
-
-        try {
-            cfg.save(file);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-}

+ 0 - 31
src/main/java/me/lethunderhawk/fluxapi/profile/config/registry/ProfileModuleRegistry.java

@@ -1,31 +0,0 @@
-package me.lethunderhawk.fluxapi.profile.config.registry;
-
-import me.lethunderhawk.fluxapi.profile.config.abstraction.subconfig.ProfileSubConfig;
-import me.lethunderhawk.fluxapi.profile.config.abstraction.subconfig.ProfileSubConfigFactory;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-public final class ProfileModuleRegistry {
-
-    private static final Map<String, ProfileSubConfigFactory<?>> factories = new HashMap<>();
-
-    private ProfileModuleRegistry() {}
-
-    public static <T extends ProfileSubConfig> void register(
-            String id,
-            ProfileSubConfigFactory<T> factory
-    ) {
-        factories.put(id, factory);
-    }
-
-    @SuppressWarnings("unchecked")
-    public static <T extends ProfileSubConfig> ProfileSubConfigFactory<T> getFactory(String id) {
-        return (ProfileSubConfigFactory<T>) factories.get(id);
-    }
-
-    public static Set<String> getRegisteredIds() {
-        return factories.keySet();
-    }
-}

+ 1 - 0
src/main/java/me/lethunderhawk/fluxapi/profile/listener/JoinListener.java

@@ -9,6 +9,7 @@ import org.bukkit.event.player.PlayerQuitEvent;
 public class JoinListener implements Listener {
 
     ProfileManager profileManager;
+
     public JoinListener(ProfileManager profileManager) {
         this.profileManager = profileManager;
     }

+ 2 - 44
src/main/java/me/lethunderhawk/fluxapi/profile/representation/FluxProfile.java

@@ -1,9 +1,5 @@
 package me.lethunderhawk.fluxapi.profile.representation;
 
-import me.lethunderhawk.fluxapi.profile.config.abstraction.subconfig.ProfileSubConfig;
-import me.lethunderhawk.fluxapi.profile.config.abstraction.subconfig.ProfileSubConfigFactory;
-import me.lethunderhawk.fluxapi.profile.config.registry.ProfileModuleRegistry;
-import org.bukkit.configuration.file.YamlConfiguration;
 import org.bukkit.configuration.serialization.ConfigurationSerializable;
 import org.jetbrains.annotations.NotNull;
 
@@ -13,7 +9,7 @@ import java.util.Map;
 public class FluxProfile implements ConfigurationSerializable {
 
     private final String playerUUID;
-    private final Map<String, ProfileSubConfig> subConfigs = new HashMap<>();
+    private final Map<String, ConfigurationSerializable> subConfigs = new HashMap<>();
 
     public FluxProfile(String playerUUID) {
         this.playerUUID = playerUUID;
@@ -27,33 +23,6 @@ public class FluxProfile implements ConfigurationSerializable {
         if (playerUUID == null) {
             throw new IllegalStateException("Profile missing UUID.");
         }
-
-        // Load sub configs
-        for (String id : ProfileModuleRegistry.getRegisteredIds()) {
-
-            Object raw = map.get(id);
-
-            if (raw instanceof ProfileSubConfig sub) {
-                subConfigs.put(id, sub);
-            }
-        }
-
-        // Inject missing modules
-        for (String id : ProfileModuleRegistry.getRegisteredIds()) {
-
-            if (!subConfigs.containsKey(id)) {
-
-                ProfileSubConfigFactory<?> factory =
-                        ProfileModuleRegistry.getFactory(id);
-
-                if (factory != null) {
-                    ProfileSubConfig defaultModule =
-                            factory.deserialize(new YamlConfiguration());
-
-                    subConfigs.put(id, defaultModule);
-                }
-            }
-        }
     }
 
     @Override
@@ -62,9 +31,7 @@ public class FluxProfile implements ConfigurationSerializable {
         Map<String, Object> data = new HashMap<>();
         data.put("uuid", playerUUID);
         data.put("ein", 1);
-        for (Map.Entry<String, ProfileSubConfig> entry : subConfigs.entrySet()) {
-            data.put(entry.getKey(), entry.getValue());
-        }
+        data.putAll(subConfigs);
 
         return data;
     }
@@ -76,13 +43,4 @@ public class FluxProfile implements ConfigurationSerializable {
     public String getPlayerUUID() {
         return playerUUID;
     }
-
-    public void addSubConfig(ProfileSubConfig subConfig) {
-        subConfigs.put(subConfig.getId(), subConfig);
-    }
-
-    @SuppressWarnings("unchecked")
-    public <T extends ProfileSubConfig> T getSubConfig(String id, Class<T> type) {
-        return (T) subConfigs.get(id);
-    }
 }

+ 1 - 1
src/main/java/me/lethunderhawk/fluxapi/profile/config/ConfigLoader.java → src/main/java/me/lethunderhawk/fluxapi/util/config/ConfigLoader.java

@@ -1,4 +1,4 @@
-package me.lethunderhawk.fluxapi.profile.config;
+package me.lethunderhawk.fluxapi.util.config;
 
 import org.bukkit.configuration.ConfigurationSection;
 import org.bukkit.configuration.file.YamlConfiguration;