Started upgrading player data setup
This commit is contained in:
parent
893b24ed5f
commit
83ecf7717f
5 changed files with 71 additions and 193 deletions
|
@ -35,7 +35,7 @@ public final class IHaveConfig {
|
||||||
*
|
*
|
||||||
* @param saveAction What to do to save the config to disk. Don't use get methods until it's non-null.
|
* @param saveAction What to do to save the config to disk. Don't use get methods until it's non-null.
|
||||||
*/
|
*/
|
||||||
IHaveConfig(Runnable saveAction) {
|
public IHaveConfig(Runnable saveAction) {
|
||||||
this.saveAction = saveAction;
|
this.saveAction = saveAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package buttondevteam.lib.player;
|
|
||||||
|
|
||||||
import buttondevteam.core.component.channel.Channel;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
|
|
||||||
public class ChannelPlayerData { //I just want this to work
|
|
||||||
private final PlayerData<String> data;
|
|
||||||
private final Channel def;
|
|
||||||
|
|
||||||
public ChannelPlayerData(String name, YamlConfiguration yaml, Channel def) {
|
|
||||||
data = new PlayerData<>(name, yaml, "");
|
|
||||||
this.def = def;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Channel get() {
|
|
||||||
String str = data.get();
|
|
||||||
if (str.isEmpty())
|
|
||||||
return def;
|
|
||||||
return Channel.getChannels().filter(c -> str.equals(c.ID)).findAny().orElse(def);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(Channel value) {
|
|
||||||
data.set(value.ID);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,10 @@ package buttondevteam.lib.player;
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin;
|
||||||
import buttondevteam.core.component.channel.Channel;
|
import buttondevteam.core.component.channel.Channel;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
|
import buttondevteam.lib.architecture.IHaveConfig;
|
||||||
import com.google.common.collect.HashBiMap;
|
import com.google.common.collect.HashBiMap;
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
@ -16,17 +19,32 @@ import java.util.HashMap;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@ChromaGamerEnforcer
|
@ChromaGamerEnforcer
|
||||||
public abstract class ChromaGamerBase implements AutoCloseable {
|
public abstract class ChromaGamerBase {
|
||||||
public static final String TBMC_PLAYERS_DIR = "TBMC/players/";
|
private static final String TBMC_PLAYERS_DIR = "TBMC/players/";
|
||||||
|
|
||||||
private static final HashBiMap<Class<? extends ChromaGamerBase>, String> playerTypes = HashBiMap.create();
|
private static final HashBiMap<Class<? extends ChromaGamerBase>, String> playerTypes = HashBiMap.create();
|
||||||
|
private static final HashMap<Class<? extends ChromaGamerBase>, Supplier<? extends ChromaGamerBase>> constructors = new HashMap<>();
|
||||||
|
private static final HashMap<Class<? extends ChromaGamerBase>, HashMap<String, ChromaGamerBase>> userCache = new HashMap<>();
|
||||||
|
private static final ArrayList<Function<CommandSender, ? extends Optional<? extends ChromaGamerBase>>> senderConverters = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for connecting with every type of user ({@link #connectWith(ChromaGamerBase)})
|
* Use {@link #getConfig()} where possible; the 'id' must be always set
|
||||||
*/
|
*/
|
||||||
public static void RegisterPluginUserClass(Class<? extends ChromaGamerBase> userclass) {
|
protected YamlConfiguration plugindata;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
protected final IHaveConfig config = new IHaveConfig(this::save);
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
config.reset(plugindata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for connecting with every type of user ({@link #connectWith(ChromaGamerBase)}) and to init the configs.
|
||||||
|
*/
|
||||||
|
public static <T extends ChromaGamerBase> void RegisterPluginUserClass(Class<T> userclass, Supplier<T> constructor) {
|
||||||
if (userclass.isAnnotationPresent(UserClass.class))
|
if (userclass.isAnnotationPresent(UserClass.class))
|
||||||
playerTypes.put(userclass, userclass.getAnnotation(UserClass.class).foldername());
|
playerTypes.put(userclass, userclass.getAnnotation(UserClass.class).foldername());
|
||||||
else if (userclass.isAnnotationPresent(AbstractUserClass.class))
|
else if (userclass.isAnnotationPresent(AbstractUserClass.class))
|
||||||
|
@ -34,6 +52,7 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
userclass.getAnnotation(AbstractUserClass.class).foldername());
|
userclass.getAnnotation(AbstractUserClass.class).foldername());
|
||||||
else // <-- Really important
|
else // <-- Really important
|
||||||
throw new RuntimeException("Class not registered as a user class! Use @UserClass or TBMCPlayerBase");
|
throw new RuntimeException("Class not registered as a user class! Use @UserClass or TBMCPlayerBase");
|
||||||
|
constructors.put(userclass, constructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -61,19 +80,6 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
return playerTypes.inverse().get(foldername);
|
return playerTypes.inverse().get(foldername);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 final String getFileName() {
|
|
||||||
return plugindata.getString(getFolder() + "_id");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use {@link #data(Object)} or {@link #data(String, Object)} where possible; the 'id' must be always set
|
|
||||||
*/
|
|
||||||
protected YamlConfiguration plugindata;
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 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)}
|
* 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)}
|
||||||
*
|
*
|
||||||
|
@ -82,21 +88,23 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
* @return The user object
|
* @return The user object
|
||||||
*/
|
*/
|
||||||
public static <T extends ChromaGamerBase> T getUser(String fname, Class<T> cl) {
|
public static <T extends ChromaGamerBase> T getUser(String fname, Class<T> cl) {
|
||||||
try {
|
HashMap<String, ? extends ChromaGamerBase> uc;
|
||||||
T obj = cl.newInstance();
|
if (userCache.containsKey(cl)) {
|
||||||
|
uc = userCache.get(cl);
|
||||||
|
if (uc.containsKey(fname))
|
||||||
|
//noinspection unchecked
|
||||||
|
return (T) uc.get(fname);
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked") T obj = (T) constructors.get(cl).get();
|
||||||
final String folder = getFolderForType(cl);
|
final String folder = getFolderForType(cl);
|
||||||
final File file = new File(TBMC_PLAYERS_DIR + folder, fname + ".yml");
|
final File file = new File(TBMC_PLAYERS_DIR + folder, fname + ".yml");
|
||||||
file.getParentFile().mkdirs();
|
file.getParentFile().mkdirs();
|
||||||
obj.plugindata = YamlConfiguration.loadConfiguration(file);
|
obj.plugindata = YamlConfiguration.loadConfiguration(file);
|
||||||
obj.plugindata.set(folder + "_id", fname);
|
obj.plugindata.set(folder + "_id", fname);
|
||||||
|
obj.init();
|
||||||
|
userCache.computeIfAbsent(cl, key -> new HashMap<>()).put(fname, obj);
|
||||||
return obj;
|
return obj;
|
||||||
} catch (Exception e) {
|
|
||||||
TBMCCoreAPI.SendException("An error occured while loading a " + cl.getSimpleName() + "!", e, MainPlugin.Instance);
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ArrayList<Function<CommandSender, ? extends Optional<? extends ChromaGamerBase>>> senderConverters = new ArrayList<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a converter to the start of the list.
|
* Adds a converter to the start of the list.
|
||||||
|
@ -123,20 +131,12 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the player. It'll pass all exceptions to the caller. To automatically handle the exception, use {@link #save()} instead.
|
* Saves the player. It'll handle all exceptions that may happen.
|
||||||
*/
|
*/
|
||||||
@Override
|
private final void save() {
|
||||||
public void close() throws Exception {
|
try {
|
||||||
if (plugindata.getKeys(false).size() > 0)
|
if (plugindata.getKeys(false).size() > 0)
|
||||||
plugindata.save(new File(TBMC_PLAYERS_DIR + getFolder(), getFileName() + ".yml"));
|
plugindata.save(new File(TBMC_PLAYERS_DIR + getFolder(), getFileName() + ".yml"));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the player. It'll handle all exceptions that may happen. To catch the exception, use {@link #close()} instead.
|
|
||||||
*/
|
|
||||||
public void save() {
|
|
||||||
try {
|
|
||||||
close();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Error while saving player to " + getFolder() + "/" + getFileName() + ".yml!", e, MainPlugin.Instance);
|
TBMCCoreAPI.SendException("Error while saving player to " + getFolder() + "/" + getFileName() + ".yml!", e, MainPlugin.Instance);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
*
|
*
|
||||||
* @param user The account to connect with
|
* @param user The account to connect with
|
||||||
*/
|
*/
|
||||||
public <T extends ChromaGamerBase> void connectWith(T user) {
|
public final <T extends ChromaGamerBase> void connectWith(T user) {
|
||||||
// Set the ID, go through all linked files and connect them as well
|
// Set the ID, go through all linked files and connect them as well
|
||||||
if (!playerTypes.containsKey(getClass()))
|
if (!playerTypes.containsKey(getClass()))
|
||||||
throw new RuntimeException("Class not registered as a user class! Use TBMCCoreAPI.RegisterUserClass");
|
throw new RuntimeException("Class not registered as a user class! Use TBMCCoreAPI.RegisterUserClass");
|
||||||
|
@ -166,14 +166,14 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
final String otherid = sourcedata.getString(entry.getValue() + "_id");
|
final String otherid = sourcedata.getString(entry.getValue() + "_id");
|
||||||
if (otherid == null)
|
if (otherid == null)
|
||||||
continue;
|
continue;
|
||||||
try (ChromaGamerBase cg = getUser(otherid, entry.getKey())) {
|
ChromaGamerBase cg = getUser(otherid, entry.getKey());
|
||||||
cg.plugindata.set(sourcefolder + "_id", id); // Set new IDs
|
cg.plugindata.set(sourcefolder + "_id", id); // Set new IDs
|
||||||
for (val item : playerTypes.entrySet())
|
cg.config.signalChange();
|
||||||
if (sourcedata.contains(item.getValue() + "_id"))
|
for (val item : playerTypes.entrySet()) {
|
||||||
|
if (sourcedata.contains(item.getValue() + "_id")) {
|
||||||
cg.plugindata.set(item.getValue() + "_id", sourcedata.getString(item.getValue() + "_id")); // Set all existing IDs
|
cg.plugindata.set(item.getValue() + "_id", sourcedata.getString(item.getValue() + "_id")); // Set all existing IDs
|
||||||
} catch (Exception e) {
|
cg.config.signalChange();
|
||||||
TBMCCoreAPI.SendException("Failed to update " + sourcefolder + " ID in player files for " + id
|
}
|
||||||
+ " in folder with " + entry.getValue() + " id " + otherid + "!", e, MainPlugin.Instance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -187,7 +187,7 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
* @param cl The player class to get the ID from
|
* @param cl The player class to get the ID from
|
||||||
* @return The ID or null if not found
|
* @return The ID or null if not found
|
||||||
*/
|
*/
|
||||||
public <T extends ChromaGamerBase> String getConnectedID(Class<T> cl) {
|
public final <T extends ChromaGamerBase> String getConnectedID(Class<T> cl) {
|
||||||
return plugindata.getString(getFolderForType(cl) + "_id");
|
return plugindata.getString(getFolderForType(cl) + "_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Nullable
|
@Nullable
|
||||||
public <T extends ChromaGamerBase> T getAs(Class<T> cl) { // TODO: Provide a way to use TBMCPlayerBase's loaded players
|
public final <T extends ChromaGamerBase> T getAs(Class<T> cl) {
|
||||||
if (cl.getSimpleName().equals(getClass().getSimpleName()))
|
if (cl.getSimpleName().equals(getClass().getSimpleName()))
|
||||||
return (T) this;
|
return (T) this;
|
||||||
String newfolder = getFolderForType(cl);
|
String newfolder = getFolderForType(cl);
|
||||||
|
@ -213,99 +213,25 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
return getUser(plugindata.getString(newfolder + "_id"), cl);
|
return getUser(plugindata.getString(newfolder + "_id"), cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFolder() {
|
/**
|
||||||
|
* 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 final String getFileName() {
|
||||||
|
return plugindata.getString(getFolder() + "_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getFolder() {
|
||||||
return getFolderForType(getClass());
|
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 final HashMap<String, PlayerData> datamap = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use from a data() method, which is in a method with the name of the key. For example, use flair() for the enclosing method of the outer data() to save to and load from "flair"
|
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T> PlayerData<T> data(String sectionname, T def) {
|
|
||||||
ThrowIfNoUser();
|
|
||||||
String mname = sectionname + "." + new Exception().getStackTrace()[2].getMethodName();
|
|
||||||
if (!datamap.containsKey(mname))
|
|
||||||
datamap.put(mname, new PlayerData<T>(mname, plugindata, def));
|
|
||||||
return datamap.get(mname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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"
|
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T> PlayerData<T> data(T def) {
|
|
||||||
ThrowIfNoUser();
|
|
||||||
String mname = new Exception().getStackTrace()[1].getMethodName();
|
|
||||||
if (!datamap.containsKey(mname))
|
|
||||||
datamap.put(mname, new PlayerData<T>(mname, plugindata, def));
|
|
||||||
return datamap.get(mname);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
private final HashMap<String, EnumPlayerData> dataenummap = new HashMap<>();
|
|
||||||
private ChannelPlayerData datachannel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use from a data() method, which is in a method with the name of the key. For example, use flair() for the enclosing method of the outer data() to save to and load from "flair"
|
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T extends Enum<T>> EnumPlayerData<T> dataEnum(String sectionname, Class<T> cl, T def) {
|
|
||||||
ThrowIfNoUser();
|
|
||||||
String mname = sectionname + "." + new Exception().getStackTrace()[2].getMethodName();
|
|
||||||
if (!dataenummap.containsKey(mname))
|
|
||||||
dataenummap.put(mname, new EnumPlayerData<T>(mname, plugindata, cl, def));
|
|
||||||
return dataenummap.get(mname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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"
|
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T extends Enum<T>> EnumPlayerData<T> dataEnum(Class<T> cl, T def) {
|
|
||||||
ThrowIfNoUser();
|
|
||||||
String mname = new Exception().getStackTrace()[1].getMethodName();
|
|
||||||
if (!dataenummap.containsKey(mname))
|
|
||||||
dataenummap.put(mname, new EnumPlayerData<T>(mname, plugindata, cl, def));
|
|
||||||
return dataenummap.get(mname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Channel
|
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected ChannelPlayerData dataChannel(Channel def) { //TODO: Make interface with fromString() method and require use of that for player data types
|
|
||||||
ThrowIfNoUser();
|
|
||||||
if (datachannel == null)
|
|
||||||
datachannel = new ChannelPlayerData("channel", plugindata, def);
|
|
||||||
return datachannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get player information. This method calls the {@link TBMCPlayerGetInfoEvent} to get all the player information across the TBMC plugins.
|
* Get player information. This method calls the {@link TBMCPlayerGetInfoEvent} to get all the player information across the TBMC plugins.
|
||||||
*
|
*
|
||||||
* @param target The {@link InfoTarget} to return the info for.
|
* @param target The {@link InfoTarget} to return the info for.
|
||||||
* @return The player information.
|
* @return The player information.
|
||||||
*/
|
*/
|
||||||
public String getInfo(InfoTarget target) {
|
public final String getInfo(InfoTarget target) {
|
||||||
TBMCPlayerGetInfoEvent event = new TBMCPlayerGetInfoEvent(this, target);
|
TBMCPlayerGetInfoEvent event = new TBMCPlayerGetInfoEvent(this, target);
|
||||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||||
return event.getResult();
|
return event.getResult();
|
||||||
|
@ -317,7 +243,6 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
|
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
|
|
||||||
public ChannelPlayerData channel() {
|
public final ConfigData<Channel> channel = getConfig().getData("channel", Channel.GlobalChat,
|
||||||
return dataChannel(Channel.GlobalChat);
|
id -> Channel.getChannels().filter(ch -> ch.ID.equalsIgnoreCase((String) id)).findAny().orElse(null), ch -> ch.ID);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
package buttondevteam.lib.player;
|
|
||||||
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
|
|
||||||
public class EnumPlayerData<T extends Enum<T>> {
|
|
||||||
private final PlayerData<String> data;
|
|
||||||
private final Class<T> cl;
|
|
||||||
private final T def;
|
|
||||||
|
|
||||||
public EnumPlayerData(String name, YamlConfiguration yaml, Class<T> cl, T def) {
|
|
||||||
data = new PlayerData<String>(name, yaml, "");
|
|
||||||
this.cl = cl;
|
|
||||||
this.def = def;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T get() {
|
|
||||||
String str = data.get();
|
|
||||||
if (str.isEmpty())
|
|
||||||
return def;
|
|
||||||
return Enum.valueOf(cl, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(T value) {
|
|
||||||
data.set(value.toString());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,6 +23,10 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase {
|
||||||
pluginname = getClass().getAnnotation(PlayerClass.class).pluginname();
|
pluginname = getClass().getAnnotation(PlayerClass.class).pluginname();
|
||||||
else
|
else
|
||||||
throw new RuntimeException("Class not defined as player class! Use @PlayerClass");
|
throw new RuntimeException("Class not defined as player class! Use @PlayerClass");
|
||||||
|
|
||||||
|
var section = plugindata.getConfigurationSection(pluginname);
|
||||||
|
if (section == null) section = plugindata.createSection(pluginname);
|
||||||
|
config.reset(section);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getUUID() {
|
public UUID getUUID() {
|
||||||
|
|
Loading…
Reference in a new issue