| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- package me.lethunderhawk.fluxapi.profile.representation;
- import me.lethunderhawk.fluxapi.profile.event.FluxProfileLoadEvent;
- import me.lethunderhawk.fluxapi.profile.event.FluxProfileUpdateEvent;
- import org.bukkit.Bukkit;
- import org.bukkit.configuration.serialization.ConfigurationSerializable;
- import org.bukkit.configuration.serialization.ConfigurationSerialization;
- import org.jetbrains.annotations.NotNull;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.concurrent.ConcurrentHashMap;
- public class FluxProfile implements ConfigurationSerializable {
- /* ------------------------------------------------ */
- /* Static Registry */
- /* ------------------------------------------------ */
- private static final Map<String, Class<? extends ConfigurationSerializable>> REGISTERED_TYPES = new ConcurrentHashMap<>();
- private static final Map<String, SubProfileFactory<?>> DEFAULT_FACTORIES = new HashMap<>();
- /**
- * Register a sub profile type.
- *
- * @param id The yaml key (e.g. "customdata1")
- * @param clazz The serializable class
- */
- public static void registerSubProfile(
- @NotNull String id,
- @NotNull Class<? extends ConfigurationSerializable> clazz
- ) {
- REGISTERED_TYPES.put(id, clazz);
- ConfigurationSerialization.registerClass(clazz);
- }
- /**
- * Register a sub profile type with a default Object.
- *
- * @param id The yaml key (e.g. "customdata1")
- * @param clazz The serializable class
- * @param factory The default factory used to create a subConfig if there is nothing saved yet
- */
- public static <T extends ConfigurationSerializable> void registerSubProfile(
- @NotNull String id,
- @NotNull Class<T> clazz,
- @NotNull SubProfileFactory<T> factory
- ) {
- REGISTERED_TYPES.put(id, clazz);
- DEFAULT_FACTORIES.put(id, factory);
- ConfigurationSerialization.registerClass(clazz);
- }
- /* ------------------------------------------------ */
- /* Instance Data */
- /* ------------------------------------------------ */
- private final String playerUUID;
- private boolean dirty = false;
- // Fully deserialized objects
- private final Map<String, ConfigurationSerializable> subProfiles = new HashMap<>();
- // Raw maps (used if plugin not yet registered)
- private final Map<String, Map<String, Object>> unresolvedData = new HashMap<>();
- /* ------------------------------------------------ */
- /* Constructors */
- /* ------------------------------------------------ */
- public FluxProfile(@NotNull String playerUUID) {
- this.playerUUID = playerUUID;
- }
- /**
- * Bukkit deserialization constructor
- */
- public FluxProfile(@NotNull Map<String, Object> map) {
- this.playerUUID = (String) map.get("uuid");
- if (playerUUID == null) {
- throw new IllegalStateException("Profile missing UUID.");
- }
- for (Map.Entry<String, Object> entry : map.entrySet()) {
- String key = entry.getKey();
- if (key.equals("uuid")) continue;
- Object value = entry.getValue();
- // CASE 1: Already deserialized by Bukkit
- if (value instanceof ConfigurationSerializable serializable) {
- subProfiles.put(key, serializable);
- continue;
- }
- // CASE 2: Raw map (class not registered yet)
- if (value instanceof Map<?, ?> rawMap) {
- @SuppressWarnings("unchecked")
- Map<String, Object> section =
- new HashMap<>((Map<String, Object>) rawMap);
- Object typeKey = section.get("==");
- if (typeKey instanceof String className) {
- try {
- Class<?> clazz = Class.forName(className);
- if (ConfigurationSerializable.class.isAssignableFrom(clazz)) {
- ConfigurationSerializable obj =
- ConfigurationSerialization.deserializeObject(
- section,
- clazz.asSubclass(ConfigurationSerializable.class)
- );
- subProfiles.put(key, obj);
- continue;
- }
- } catch (ClassNotFoundException ignored) {}
- }
- unresolvedData.put(key, section);
- }
- System.out.println("Loaded key: " + key + " type: " + value.getClass().getName());
- }
- Bukkit.getPluginManager().callEvent(new FluxProfileLoadEvent(this));
- }
- /* ------------------------------------------------ */
- /* Public API */
- /* ------------------------------------------------ */
- public @NotNull String getPlayerUUID() {
- return playerUUID;
- }
- /**
- * Add or replace a sub profile.
- */
- public void addSubProfile(
- @NotNull String id,
- @NotNull ConfigurationSerializable profile
- ) {
- subProfiles.put(id, profile);
- Bukkit.getPluginManager().callEvent(
- new FluxProfileUpdateEvent(this, id)
- );
- }
- /**
- * Type-safe retrieval.
- * Automatically resolves unresolved raw data if class gets registered later.
- */
- @SuppressWarnings("unchecked")
- public synchronized <T extends ConfigurationSerializable> T getSubProfile(
- @NotNull String id,
- @NotNull Class<T> expectedType
- ) {
- // Already deserialized
- ConfigurationSerializable existing = subProfiles.get(id);
- if (existing != null) {
- return expectedType.cast(existing);
- }
- // Raw saved data exists → deserialize FIRST
- Map<String, Object> raw = unresolvedData.remove(id);
- if (raw != null) {
- Class<? extends ConfigurationSerializable> clazz = REGISTERED_TYPES.get(id);
- if (clazz != null) {
- ConfigurationSerializable obj =
- ConfigurationSerialization.deserializeObject(raw, clazz);
- subProfiles.put(id, obj);
- return expectedType.cast(obj);
- } else {
- // Class not yet registered → put raw back
- unresolvedData.put(id, raw);
- return null;
- }
- }
- // Only if NO SAVED DATA exists → create default
- SubProfileFactory<?> factory = DEFAULT_FACTORIES.get(id);
- if (factory != null) {
- T created = (T) factory.create(playerUUID);
- subProfiles.put(id, created);
- return created;
- }
- return null;
- }
- /* ------------------------------------------------ */
- /* Serialization */
- /* ------------------------------------------------ */
- @Override
- public @NotNull Map<String, Object> serialize() {
- Map<String, Object> data = new HashMap<>();
- data.put("uuid", playerUUID);
- // Store resolved profiles
- data.putAll(subProfiles);
- // Preserve unresolved raw data
- data.putAll(unresolvedData);
- return data;
- }
- public static FluxProfile deserialize(Map<String, Object> map) {
- return new FluxProfile(map);
- }
- }
|