Remade user data managing, added a test #30

Merged
NorbiPeti merged 22 commits from dev into master 2017-04-15 11:54:06 +00:00
5 changed files with 68 additions and 38 deletions
Showing only changes of commit 1497a487a4 - Show all commits

View file

@ -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();
}

View file

@ -21,7 +21,10 @@ public abstract class ChromaGamerBase implements AutoCloseable {
public static void RegisterPluginUserClass(Class<? extends ChromaGamerBase> 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 <T extends ChromaGamerBase> String getFolderForType(Class<T> 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.<br>
* This method returns the filename for this player data. For example, for Minecraft-related data, MC UUIDs, for Discord data, use Discord IDs, etc.<br>
* <b>Does not include .yml</b>
*/
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<YamlConfiguration> 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<Class<?>, 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 extends ChromaGamerBase> T getAs(Class<T> 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<String, PlayerData> datamap = new HashMap<>();
@ -183,8 +195,7 @@ public abstract class ChromaGamerBase implements AutoCloseable {
*/
@SuppressWarnings("unchecked")
protected <T> PlayerData<T> 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<T>(mname, plugindata));
@ -198,9 +209,7 @@ public abstract class ChromaGamerBase implements AutoCloseable {
*/
@SuppressWarnings("unchecked")
protected <T> PlayerData<T> 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<T>(mname, plugindata));
@ -217,8 +226,7 @@ public abstract class ChromaGamerBase implements AutoCloseable {
*/
@SuppressWarnings("unchecked")
protected <T extends Enum<T>> EnumPlayerData<T> dataEnum(String sectionname, Class<T> 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<T>(mname, plugindata, cl));
@ -232,8 +240,7 @@ public abstract class ChromaGamerBase implements AutoCloseable {
*/
@SuppressWarnings("unchecked")
protected <T extends Enum<T>> EnumPlayerData<T> dataEnum(Class<T> 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<T>(mname, plugindata, cl));

View file

@ -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<String> 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 <T> PlayerData<T> 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 extends TBMCPlayerBase> T getPlayer(UUID uuid, Class<T> 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) {

View file

@ -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
*

View file

@ -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());