diff --git a/src/main/java/buttondevteam/lib/player/AbstractUserClass.java b/src/main/java/buttondevteam/lib/player/AbstractUserClass.java new file mode 100644 index 0000000..f6554e1 --- /dev/null +++ b/src/main/java/buttondevteam/lib/player/AbstractUserClass.java @@ -0,0 +1,28 @@ +package buttondevteam.lib.player; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies a {@link ChromaGamerBase} direct subclass which's abstract. For Minecraft data, use {@link PlayerClass} + * + * @author NorbiPeti + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface AbstractUserClass { + /** + * Indicates which folder should the player files be saved in. + */ + String foldername(); + + /** + * Indicates the class to create when connecting accounts. + */ + Class prototype(); +} diff --git a/src/main/java/buttondevteam/lib/player/ChromaGamerBase.java b/src/main/java/buttondevteam/lib/player/ChromaGamerBase.java index 89cca93..0daf543 100644 --- a/src/main/java/buttondevteam/lib/player/ChromaGamerBase.java +++ b/src/main/java/buttondevteam/lib/player/ChromaGamerBase.java @@ -21,7 +21,10 @@ public abstract class ChromaGamerBase implements AutoCloseable { public static void RegisterPluginUserClass(Class userclass) { if (userclass.isAnnotationPresent(UserClass.class)) playerTypes.put(userclass, userclass.getAnnotation(UserClass.class).foldername()); - else //<-- Really important + else if (userclass.isAnnotationPresent(AbstractUserClass.class)) + playerTypes.put(userclass.getAnnotation(AbstractUserClass.class).prototype(), + userclass.getAnnotation(AbstractUserClass.class).foldername()); + else // <-- Really important throw new RuntimeException("Class not registered as a user class! Use @UserClass or TBMCPlayerBase"); } @@ -37,24 +40,24 @@ public abstract class ChromaGamerBase implements AutoCloseable { public static String getFolderForType(Class cl) { if (cl.isAnnotationPresent(UserClass.class)) return cl.getAnnotation(UserClass.class).foldername(); - throw new RuntimeException("Class not registered as a user class! Use @UserClass"); + else if (cl.isAnnotationPresent(AbstractUserClass.class)) + return cl.getAnnotation(AbstractUserClass.class).foldername(); + throw new RuntimeException("Class not registered as a user class! Use @UserClass or @AbstractUserClass"); } /** - * This method returns the filename for this player data. For example, for Minecraft-related data, use MC UUIDs, for Discord data, use Discord IDs, etc.
+ * This method returns the filename for this player data. For example, for Minecraft-related data, MC UUIDs, for Discord data, use Discord IDs, etc.
* Does not include .yml */ - public abstract String getFileName(); + public final String getFileName() { + return plugindata.getString(getFolder() + "_id"); + } /** * Use {@link #data()} or {@link #data(String)} where possible; the 'id' must be always set */ protected YamlConfiguration plugindata; - public String getID() { - return plugindata != null ? plugindata.getString("id") : null; - } - /*** * Loads a user from disk and returns the user object. Make sure to use the subclasses' methods, where possible, like {@link TBMCPlayerBase#getPlayer(java.util.UUID, Class)} * @@ -69,7 +72,7 @@ public abstract class ChromaGamerBase implements AutoCloseable { final File file = new File(TBMC_PLAYERS_DIR + folder, fname + ".yml"); file.getParentFile().mkdirs(); obj.plugindata = YamlConfiguration.loadConfiguration(file); - obj.plugindata.set(folder + "_id", obj.getID()); + obj.plugindata.set(folder + "_id", fname); return obj; } catch (Exception e) { TBMCCoreAPI.SendException("An error occured while loading a " + cl.getSimpleName() + "!", e); @@ -110,13 +113,16 @@ public abstract class ChromaGamerBase implements AutoCloseable { // Set the ID, go through all linked files and connect them as well if (!playerTypes.containsKey(getClass())) throw new RuntimeException("Class not registered as a user class! Use TBMCCoreAPI.RegisterUserClass"); - plugindata.set(user.getFolder() + "_id", user.plugindata.getString("id")); - final String ownFolder = user.getFolder(); - user.plugindata.set(ownFolder + "_id", plugindata.getString("id")); + final String ownFolder = getFolder(); + user.plugindata.set(ownFolder + "_id", plugindata.getString(ownFolder + "_id")); + final String userFolder = user.getFolder(); + plugindata.set(userFolder + "_id", user.plugindata.getString(userFolder + "_id")); Consumer sync = sourcedata -> { - final String sourcefolder = sourcedata == plugindata ? ownFolder : user.getFolder(); - final String id = sourcedata.getString("id"); + final String sourcefolder = sourcedata == plugindata ? ownFolder : userFolder; + final String id = sourcedata.getString(sourcefolder + "_id"); for (Entry, String> entry : playerTypes.entrySet()) { // Set our ID in all files we can find, both from our connections and the new ones + if (entry.getKey() == getClass() || entry.getKey() == user.getClass()) + continue; final String otherid = sourcedata.getString(entry.getValue() + "_id"); if (otherid == null) continue; @@ -157,13 +163,13 @@ public abstract class ChromaGamerBase implements AutoCloseable { */ @SuppressWarnings("unchecked") public T getAs(Class cl) { // TODO: Provide a way to use TBMCPlayerBase's loaded players - //System.out.println("getAs cls: " + cl.getSimpleName() + " =? " + getClass().getSimpleName()); // TODO: TMP - Don't be tired when programming if (cl.getSimpleName().equals(getClass().getSimpleName())) return (T) this; String newfolder = getFolderForType(cl); if (newfolder == null) throw new RuntimeException("The specified class " + cl.getSimpleName() + " isn't registered!"); - System.out.println("getAs newfolder: " + newfolder); + if (newfolder.equals(getFolder())) // If in the same folder, the same filename is used + return getUser(getFileName(), cl); if (!plugindata.contains(newfolder + "_id")) return null; return getUser(plugindata.getString(newfolder + "_id"), cl); @@ -173,6 +179,12 @@ public abstract class ChromaGamerBase implements AutoCloseable { return getFolderForType(getClass()); } + private void ThrowIfNoUser() { + if (!getClass().isAnnotationPresent(UserClass.class) + && !getClass().isAnnotationPresent(AbstractUserClass.class)) + throw new RuntimeException("Class not registered as a user class! Use @UserClass"); + } + @SuppressWarnings("rawtypes") private HashMap datamap = new HashMap<>(); @@ -183,8 +195,7 @@ public abstract class ChromaGamerBase implements AutoCloseable { */ @SuppressWarnings("unchecked") protected PlayerData data(String sectionname) { - if (!getClass().isAnnotationPresent(UserClass.class)) - throw new RuntimeException("Class not registered as a user class! Use @UserClass"); + ThrowIfNoUser(); String mname = sectionname + "." + new Exception().getStackTrace()[2].getMethodName(); if (!datamap.containsKey(mname)) datamap.put(mname, new PlayerData(mname, plugindata)); @@ -198,9 +209,7 @@ public abstract class ChromaGamerBase implements AutoCloseable { */ @SuppressWarnings("unchecked") protected PlayerData data() { - //System.out.println("Calling ChromaGamerBase data"); // TODO: TMP - Debugged for hours - if (!getClass().isAnnotationPresent(UserClass.class)) - throw new RuntimeException("Class not registered as a user class! Use @UserClass"); + ThrowIfNoUser(); String mname = new Exception().getStackTrace()[1].getMethodName(); if (!datamap.containsKey(mname)) datamap.put(mname, new PlayerData(mname, plugindata)); @@ -217,8 +226,7 @@ public abstract class ChromaGamerBase implements AutoCloseable { */ @SuppressWarnings("unchecked") protected > EnumPlayerData dataEnum(String sectionname, Class cl) { - if (!getClass().isAnnotationPresent(UserClass.class)) - throw new RuntimeException("Class not registered as a user class! Use @UserClass"); + ThrowIfNoUser(); String mname = sectionname + "." + new Exception().getStackTrace()[2].getMethodName(); if (!dataenummap.containsKey(mname)) dataenummap.put(mname, new EnumPlayerData(mname, plugindata, cl)); @@ -232,8 +240,7 @@ public abstract class ChromaGamerBase implements AutoCloseable { */ @SuppressWarnings("unchecked") protected > EnumPlayerData dataEnum(Class cl) { - if (!getClass().isAnnotationPresent(UserClass.class)) - throw new RuntimeException("Class not registered as a user class! Use @UserClass"); + ThrowIfNoUser(); String mname = new Exception().getStackTrace()[1].getMethodName(); if (!dataenummap.containsKey(mname)) dataenummap.put(mname, new EnumPlayerData(mname, plugindata, cl)); diff --git a/src/main/java/buttondevteam/lib/player/TBMCPlayerBase.java b/src/main/java/buttondevteam/lib/player/TBMCPlayerBase.java index 269a41f..0ff465d 100644 --- a/src/main/java/buttondevteam/lib/player/TBMCPlayerBase.java +++ b/src/main/java/buttondevteam/lib/player/TBMCPlayerBase.java @@ -17,7 +17,7 @@ import com.palmergames.bukkit.towny.object.TownyUniverse; import buttondevteam.lib.TBMCCoreAPI; -@UserClass(foldername = "minecraft") +@AbstractUserClass(foldername = "minecraft", prototype = TBMCPlayer.class) public abstract class TBMCPlayerBase extends ChromaGamerBase { protected UUID uuid; @@ -31,19 +31,15 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase { } public UUID getUUID() { + if (uuid == null) + uuid = UUID.fromString(getFileName()); return uuid; } public PlayerData PlayerName() { - //System.out.println("Calling playername"); // TODO: TMP - The data will only get stored if it's changed return super.data(); } - @Override - public String getFileName() { - return getUUID().toString(); - } - /** * Use from a method with the name of the key. For example, use flair() for the enclosing method to save to and load from "flair" * @@ -51,7 +47,6 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase { */ @Override protected PlayerData data() { - //System.out.println("Calling TMBCPlayerBase data"); // TODO: TMP - Sigh return super.data(pluginname); } @@ -78,17 +73,17 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase { public static T getPlayer(UUID uuid, Class cl) { if (playermap.containsKey(uuid + "-" + cl.getSimpleName())) return (T) playermap.get(uuid + "-" + cl.getSimpleName()); - //System.out.println("A"); + // System.out.println("A"); try { T player; if (playermap.containsKey(uuid + "-" + TBMCPlayer.class.getSimpleName())) { - //System.out.println("B"); - Don't program when tired + // System.out.println("B"); - Don't program when tired player = cl.newInstance(); player.plugindata = playermap.get(uuid + "-" + TBMCPlayer.class.getSimpleName()).plugindata; playermap.put(uuid + "-" + cl.getSimpleName(), player); // It will get removed on player quit } else player = ChromaGamerBase.getUser(uuid.toString(), cl); - //System.out.println("C"); + // System.out.println("C"); player.uuid = uuid; return player; } catch (Exception e) { diff --git a/src/main/java/buttondevteam/lib/player/UserClass.java b/src/main/java/buttondevteam/lib/player/UserClass.java index bbe9b59..cae2326 100644 --- a/src/main/java/buttondevteam/lib/player/UserClass.java +++ b/src/main/java/buttondevteam/lib/player/UserClass.java @@ -7,7 +7,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Specifies a {@link ChromaGamerBase} direct subclass. For Minecraft data, use {@link PlayerClass} + * Specifies a {@link ChromaGamerBase} direct subclass which can be instantiated. For Minecraft data, use {@link PlayerClass} * * @author NorbiPeti * diff --git a/src/test/java/buttondevteam/core/PlayerDataTest.java b/src/test/java/buttondevteam/core/PlayerDataTest.java index 87fa5f7..a512e82 100644 --- a/src/test/java/buttondevteam/core/PlayerDataTest.java +++ b/src/test/java/buttondevteam/core/PlayerDataTest.java @@ -48,7 +48,7 @@ public class PlayerDataTest extends TestCase { try (TestPlayerClass p = TBMCPlayerBase.getPlayer(uuid, TestPlayerClass.class)) { p.PlayerName().set("Test"); assertEquals("Test", p.PlayerName().get()); - p.testenum().set(TestEnum.A); // TODO: Fix enum saving + p.testenum().set(TestEnum.A); assertEquals(TestEnum.A, p.testenum().get()); // p.TestShort().set((short) 5); // assertEquals((short) 5, (short) (int) p.TestShort().get());