Fix and improve user class, convert player class
And other stuff Reworked ChromaGamerBase.connectWith() Converted other stuff too
This commit is contained in:
parent
113d975986
commit
39424fa92b
15 changed files with 307 additions and 313 deletions
|
@ -92,11 +92,11 @@ class MainPlugin : ButtonPlugin() {
|
||||||
}
|
}
|
||||||
TBMCCoreAPI.RegisterUserClass(TBMCPlayerBase::class.java) { TBMCPlayer() }
|
TBMCCoreAPI.RegisterUserClass(TBMCPlayerBase::class.java) { TBMCPlayer() }
|
||||||
TBMCChatAPI.RegisterChatChannel(Channel("§fg§f", Color.White, "g", null)
|
TBMCChatAPI.RegisterChatChannel(Channel("§fg§f", Color.White, "g", null)
|
||||||
.also { Channel.GlobalChat = it }) //The /ooc ID has moved to the config
|
.also { Channel.globalChat = it }) //The /ooc ID has moved to the config
|
||||||
TBMCChatAPI.RegisterChatChannel(Channel("§cADMIN§f", Color.Red, "a", Channel.inGroupFilter(null))
|
TBMCChatAPI.RegisterChatChannel(Channel("§cADMIN§f", Color.Red, "a", Channel.inGroupFilter(null))
|
||||||
.also { Channel.AdminChat = it })
|
.also { Channel.adminChat = it })
|
||||||
TBMCChatAPI.RegisterChatChannel(Channel("§9MOD§f", Color.Blue, "mod", Channel.inGroupFilter("mod"))
|
TBMCChatAPI.RegisterChatChannel(Channel("§9MOD§f", Color.Blue, "mod", Channel.inGroupFilter("mod"))
|
||||||
.also { Channel.ModChat = it })
|
.also { Channel.modChat = it })
|
||||||
TBMCChatAPI.RegisterChatChannel(
|
TBMCChatAPI.RegisterChatChannel(
|
||||||
Channel(
|
Channel(
|
||||||
"§6DEV§f",
|
"§6DEV§f",
|
||||||
|
|
|
@ -26,13 +26,13 @@ class PlayerListener(val plugin: MainPlugin) : Listener {
|
||||||
fun onPlayerJoin(event: PlayerJoinEvent) {
|
fun onPlayerJoin(event: PlayerJoinEvent) {
|
||||||
val p = event.player
|
val p = event.player
|
||||||
val player = TBMCPlayerBase.getPlayer(p.uniqueId, TBMCPlayer::class.java)
|
val player = TBMCPlayerBase.getPlayer(p.uniqueId, TBMCPlayer::class.java)
|
||||||
val pname = player.PlayerName.get()
|
val pname = player.playerName.get()
|
||||||
if (pname.isEmpty()) {
|
if (pname.isEmpty()) {
|
||||||
player.PlayerName.set(p.name)
|
player.playerName.set(p.name)
|
||||||
plugin.logger.info("Player name saved: " + player.PlayerName.get())
|
plugin.logger.info("Player name saved: " + player.playerName.get())
|
||||||
} else if (p.name != pname) {
|
} else if (p.name != pname) {
|
||||||
plugin.logger.info(pname + " renamed to " + p.name)
|
plugin.logger.info(pname + " renamed to " + p.name)
|
||||||
player.PlayerName.set(p.name)
|
player.playerName.set(p.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import java.util.logging.Logger;
|
||||||
public class TestPrepare {
|
public class TestPrepare {
|
||||||
|
|
||||||
public static void PrepareServer() {
|
public static void PrepareServer() {
|
||||||
ChromaUtils.setTest(); //Needs to be in a separate class because of the potential lack of Mockito
|
ChromaUtils.setTest(true); //Needs to be in a separate class because of the potential lack of Mockito
|
||||||
Bukkit.setServer(Mockito.mock(Server.class, new Answer<Object>() {
|
Bukkit.setServer(Mockito.mock(Server.class, new Answer<Object>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -45,6 +45,6 @@ public class TestPrepare {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
Component.registerComponent(Mockito.mock(JavaPlugin.class), new ChannelComponent());
|
Component.registerComponent(Mockito.mock(JavaPlugin.class), new ChannelComponent());
|
||||||
TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fg§f", Color.White, "g", null));
|
TBMCChatAPI.RegisterChatChannel(Channel.globalChat = new Channel("§fg§f", Color.White, "g", null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,10 +211,9 @@ open class Channel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmField
|
lateinit var globalChat: Channel
|
||||||
var GlobalChat: Channel? = null
|
lateinit var adminChat: Channel
|
||||||
var AdminChat: Channel? = null
|
lateinit var modChat: Channel
|
||||||
var ModChat: Channel? = null
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun registerChannel(channel: Channel) {
|
fun registerChannel(channel: Channel) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package buttondevteam.core.component.channel
|
package buttondevteam.core.component.channel
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin
|
|
||||||
import buttondevteam.lib.ChromaUtils
|
import buttondevteam.lib.ChromaUtils
|
||||||
import buttondevteam.lib.TBMCSystemChatEvent.BroadcastTarget
|
import buttondevteam.lib.TBMCSystemChatEvent.BroadcastTarget
|
||||||
import buttondevteam.lib.architecture.Component
|
import buttondevteam.lib.architecture.Component
|
||||||
|
@ -12,7 +11,7 @@ import org.bukkit.plugin.java.JavaPlugin
|
||||||
/**
|
/**
|
||||||
* Manages chat channels. If disabled, only global channels will be registered.
|
* Manages chat channels. If disabled, only global channels will be registered.
|
||||||
*/
|
*/
|
||||||
class ChannelComponent : Component<MainPlugin>() {
|
class ChannelComponent : Component<JavaPlugin>() {
|
||||||
override fun register(plugin: JavaPlugin) {
|
override fun register(plugin: JavaPlugin) {
|
||||||
super.register(plugin)
|
super.register(plugin)
|
||||||
roomJoinLeave = BroadcastTarget.add("roomJoinLeave") //Even if it's disabled, global channels continue to work
|
roomJoinLeave = BroadcastTarget.add("roomJoinLeave") //Even if it's disabled, global channels continue to work
|
||||||
|
@ -27,7 +26,7 @@ class ChannelComponent : Component<MainPlugin>() {
|
||||||
override fun enable() {}
|
override fun enable() {}
|
||||||
override fun disable() {}
|
override fun disable() {}
|
||||||
fun registerChannelCommand(channel: Channel) {
|
fun registerChannelCommand(channel: Channel) {
|
||||||
if (!ChromaUtils.isTest()) registerCommand(ChannelCommand(channel))
|
if (!ChromaUtils.isTest) registerCommand(ChannelCommand(channel))
|
||||||
}
|
}
|
||||||
|
|
||||||
@CommandClass
|
@CommandClass
|
||||||
|
@ -51,7 +50,7 @@ class ChannelComponent : Component<MainPlugin>() {
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
val oldch = user.channel.get()
|
val oldch = user.channel.get()
|
||||||
if (oldch is ChatRoom) oldch.leaveRoom(sender)
|
if (oldch is ChatRoom) oldch.leaveRoom(sender)
|
||||||
if (oldch == channel) user.channel.set(Channel.GlobalChat) else {
|
if (oldch == channel) user.channel.set(Channel.globalChat) else {
|
||||||
user.channel.set(channel)
|
user.channel.set(channel)
|
||||||
if (channel is ChatRoom) channel.joinRoom(sender)
|
if (channel is ChatRoom) channel.joinRoom(sender)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,12 @@ public class PrimeRestartCommand extends ICommand2MC {
|
||||||
if (Bukkit.getOnlinePlayers().size() > 0) {
|
if (Bukkit.getOnlinePlayers().size() > 0) {
|
||||||
sender.sendMessage("§bPlayers online, restart delayed.");
|
sender.sendMessage("§bPlayers online, restart delayed.");
|
||||||
if (loud)
|
if (loud)
|
||||||
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, ChatColor.DARK_RED + "The server will restart as soon as nobody is online.", component.getRestartBroadcast());
|
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, ChatColor.DARK_RED + "The server will restart as soon as nobody is online.", component.getRestartBroadcast());
|
||||||
plsrestart = true;
|
plsrestart = true;
|
||||||
} else {
|
} else {
|
||||||
sender.sendMessage("§bNobody is online. Restarting now.");
|
sender.sendMessage("§bNobody is online. Restarting now.");
|
||||||
if (loud)
|
if (loud)
|
||||||
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§cNobody is online. Restarting server.", component.getRestartBroadcast());
|
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, "§cNobody is online. Restarting server.", component.getRestartBroadcast());
|
||||||
Bukkit.spigot().restart();
|
Bukkit.spigot().restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,12 +74,12 @@ public class RestartComponent extends Component<MainPlugin> implements Listener
|
||||||
&& !event.getQuitMessage().equalsIgnoreCase("Server is restarting")) {
|
&& !event.getQuitMessage().equalsIgnoreCase("Server is restarting")) {
|
||||||
if (Bukkit.getOnlinePlayers().size() <= 1) {
|
if (Bukkit.getOnlinePlayers().size() <= 1) {
|
||||||
if (PrimeRestartCommand.isLoud())
|
if (PrimeRestartCommand.isLoud())
|
||||||
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§cNobody is online anymore. Restarting.", restartBroadcast);
|
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, "§cNobody is online anymore. Restarting.", restartBroadcast);
|
||||||
Bukkit.spigot().restart();
|
Bukkit.spigot().restart();
|
||||||
} else if (!(event.getPlayer() instanceof IFakePlayer) && System.nanoTime() - 10 * 60 * 1000000000L - lasttime > 0) { //10 minutes passed since last reminder
|
} else if (!(event.getPlayer() instanceof IFakePlayer) && System.nanoTime() - 10 * 60 * 1000000000L - lasttime > 0) { //10 minutes passed since last reminder
|
||||||
lasttime = System.nanoTime();
|
lasttime = System.nanoTime();
|
||||||
if (PrimeRestartCommand.isLoud())
|
if (PrimeRestartCommand.isLoud())
|
||||||
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, ChatColor.DARK_RED + "The server will restart as soon as nobody is online.", restartBroadcast);
|
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, ChatColor.DARK_RED + "The server will restart as soon as nobody is online.", restartBroadcast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class ScheduledRestartCommand extends ICommand2MC {
|
||||||
Bukkit.spigot().restart();
|
Bukkit.spigot().restart();
|
||||||
}
|
}
|
||||||
if (restartCounter % 200 == 0 && Bukkit.getOnlinePlayers().size() > 0)
|
if (restartCounter % 200 == 0 && Bukkit.getOnlinePlayers().size() > 0)
|
||||||
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§c-- The server is restarting in " + restartCounter / 20 + " seconds!", component.getRestartBroadcast());
|
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, "§c-- The server is restarting in " + restartCounter / 20 + " seconds!", component.getRestartBroadcast());
|
||||||
restartbar.setProgress(restartCounter / (double) restarttime);
|
restartbar.setProgress(restartCounter / (double) restarttime);
|
||||||
restartbar.setTitle(String.format("Server restart in %.2f", restartCounter / 20f));
|
restartbar.setTitle(String.format("Server restart in %.2f", restartCounter / 20f));
|
||||||
restartCounter--;
|
restartCounter--;
|
||||||
|
|
|
@ -1,101 +1,90 @@
|
||||||
package buttondevteam.lib;
|
package buttondevteam.lib
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player
|
||||||
import org.bukkit.event.Cancellable;
|
import org.bukkit.event.Cancellable
|
||||||
import org.bukkit.event.Event;
|
import org.bukkit.event.Event
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
object ChromaUtils {
|
||||||
|
fun getDisplayName(sender: CommandSender): String {
|
||||||
|
return when (sender) {
|
||||||
|
is IHaveFancyName -> sender.fancyName
|
||||||
|
is Player -> sender.displayName
|
||||||
|
else -> sender.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class ChromaUtils {
|
fun getFullDisplayName(sender: CommandSender): String {
|
||||||
private ChromaUtils() {}
|
return when (sender) {
|
||||||
|
is IHaveFancyName -> sender.fancyFullName
|
||||||
|
else -> getDisplayName(sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String getDisplayName(CommandSender sender) {
|
fun convertNumber(number: Number, targetcl: Class<out Number>): Number {
|
||||||
if (sender instanceof IHaveFancyName)
|
return when {
|
||||||
return ((IHaveFancyName) sender).getFancyName();
|
targetcl == Long::class.javaPrimitiveType || Long::class.java.isAssignableFrom(targetcl) -> number.toLong()
|
||||||
if (sender instanceof Player)
|
targetcl == Int::class.javaPrimitiveType || Int::class.java.isAssignableFrom(targetcl) -> number.toInt() //Needed because the parser can get longs
|
||||||
return ((Player) sender).getDisplayName();
|
targetcl == Short::class.javaPrimitiveType || Short::class.java.isAssignableFrom(targetcl) -> number.toShort()
|
||||||
return sender.getName();
|
targetcl == Byte::class.javaPrimitiveType || Byte::class.java.isAssignableFrom(targetcl) -> number.toByte()
|
||||||
}
|
targetcl == Float::class.javaPrimitiveType || Float::class.java.isAssignableFrom(targetcl) -> number.toFloat()
|
||||||
|
targetcl == Double::class.javaPrimitiveType || Double::class.java.isAssignableFrom(targetcl) -> number.toDouble()
|
||||||
|
else -> number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String getFullDisplayName(CommandSender sender) {
|
/**
|
||||||
if (sender instanceof IHaveFancyName)
|
* Calls the event always asynchronously. The return value is always false if async.
|
||||||
return ((IHaveFancyName) sender).getFancyFullName();
|
*
|
||||||
return getDisplayName(sender);
|
* @param event The event to call
|
||||||
}
|
* @return The event cancelled state or false if async.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun <T> callEventAsync(event: T): Boolean where T : Event, T : Cancellable {
|
||||||
|
val task = Supplier {
|
||||||
|
Bukkit.getPluginManager().callEvent(event)
|
||||||
|
event.isCancelled
|
||||||
|
}
|
||||||
|
return doItAsync(task, false)
|
||||||
|
}
|
||||||
|
|
||||||
public interface IHaveFancyName {
|
/**
|
||||||
/**
|
* Does something always asynchronously. It will execute in the same thread if it's not the server thread.
|
||||||
* May not be null.
|
*
|
||||||
*
|
* @param what What to do
|
||||||
* @return The name to be displayed in most places.
|
* @param def Default if async
|
||||||
*/
|
* @return The value supplied by the action or def if async.
|
||||||
String getFancyName();
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun <T> doItAsync(what: Supplier<T>, def: T): T {
|
||||||
|
return if (Bukkit.isPrimaryThread()) {
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.instance, Runnable { what.get() })
|
||||||
|
def
|
||||||
|
} else what.get()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* May return null.
|
* Returns true while unit testing.
|
||||||
*
|
*/
|
||||||
* @return The full name that can be used to uniquely identify the user.
|
@JvmStatic
|
||||||
*/
|
var isTest = false
|
||||||
String getFancyFullName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Number convertNumber(Number number, Class<? extends Number> targetcl) {
|
interface IHaveFancyName {
|
||||||
if (targetcl == long.class || Long.class.isAssignableFrom(targetcl))
|
/**
|
||||||
return number.longValue();
|
* May not be null.
|
||||||
else if (targetcl == int.class || Integer.class.isAssignableFrom(targetcl))
|
*
|
||||||
return number.intValue(); //Needed because the parser can get longs
|
* @return The name to be displayed in most places.
|
||||||
else if (targetcl == short.class || Short.class.isAssignableFrom(targetcl))
|
*/
|
||||||
return number.shortValue();
|
val fancyName: String
|
||||||
else if (targetcl == byte.class || Byte.class.isAssignableFrom(targetcl))
|
|
||||||
return number.byteValue();
|
|
||||||
else if (targetcl == float.class || Float.class.isAssignableFrom(targetcl))
|
|
||||||
return number.floatValue();
|
|
||||||
else if (targetcl == double.class || Double.class.isAssignableFrom(targetcl))
|
|
||||||
return number.doubleValue();
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls the event always asynchronously. The return value is always false if async.
|
* May return null.
|
||||||
*
|
*
|
||||||
* @param event The event to call
|
* @return The full name that can be used to uniquely identify the user.
|
||||||
* @return The event cancelled state or false if async.
|
*/
|
||||||
*/
|
val fancyFullName: String
|
||||||
public static <T extends Event & Cancellable> boolean callEventAsync(T event) {
|
}
|
||||||
Supplier<Boolean> task = () -> {
|
}
|
||||||
Bukkit.getPluginManager().callEvent(event);
|
|
||||||
return event.isCancelled();
|
|
||||||
};
|
|
||||||
return doItAsync(task, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does something always asynchronously. It will execute in the same thread if it's not the server thread.
|
|
||||||
*
|
|
||||||
* @param what What to do
|
|
||||||
* @param def Default if async
|
|
||||||
* @return The value supplied by the action or def if async.
|
|
||||||
*/
|
|
||||||
public static <T> T doItAsync(Supplier<T> what, T def) {
|
|
||||||
if (Bukkit.isPrimaryThread())
|
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.instance, what::get);
|
|
||||||
else
|
|
||||||
return what.get();
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean test = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true while unit testing.
|
|
||||||
*/
|
|
||||||
public static boolean isTest() { return test; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call when unit testing.
|
|
||||||
*/
|
|
||||||
public static void setTest() { test = true; }
|
|
||||||
}
|
|
|
@ -131,7 +131,7 @@ public class TBMCCoreAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends ChromaGamerBase> void RegisterUserClass(Class<T> userclass, Supplier<T> constructor) {
|
public static <T extends ChromaGamerBase> void RegisterUserClass(Class<T> userclass, Supplier<T> constructor) {
|
||||||
ChromaGamerBase.RegisterPluginUserClass(userclass, constructor);
|
ChromaGamerBase.registerPluginUserClass(userclass, constructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,7 +12,6 @@ import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.configuration.file.YamlConfiguration
|
import org.bukkit.configuration.file.YamlConfiguration
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Consumer
|
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
@ -20,30 +19,21 @@ import java.util.function.Supplier
|
||||||
abstract class ChromaGamerBase {
|
abstract class ChromaGamerBase {
|
||||||
lateinit var config: IHaveConfig
|
lateinit var config: IHaveConfig
|
||||||
|
|
||||||
@JvmField
|
protected lateinit var commonUserData: CommonUserData<out ChromaGamerBase>
|
||||||
protected var commonUserData: CommonUserData<*>? = null
|
|
||||||
protected open fun init() {
|
protected open fun init() {
|
||||||
config.reset(commonUserData!!.playerData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun updateUserConfig() {}
|
protected fun updateUserConfig() {} // TODO: Use this instead of reset()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the player. It'll handle all exceptions that may happen. Called automatically.
|
* Saves the player. It'll handle all exceptions that may happen. Called automatically.
|
||||||
*/
|
*/
|
||||||
protected open fun save() {
|
protected open fun save() {
|
||||||
try {
|
try {
|
||||||
if (commonUserData!!.playerData.getKeys(false).size > 0) commonUserData!!.playerData.save(
|
if (commonUserData.playerData.getKeys(false).size > 0)
|
||||||
File(
|
commonUserData.playerData.save(File(TBMC_PLAYERS_DIR + folder, "$fileName.yml"))
|
||||||
TBMC_PLAYERS_DIR + folder, fileName + ".yml"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
TBMCCoreAPI.SendException(
|
TBMCCoreAPI.SendException("Error while saving player to $folder/$fileName.yml!", e, MainPlugin.instance)
|
||||||
"Error while saving player to " + folder + "/" + fileName + ".yml!",
|
|
||||||
e,
|
|
||||||
MainPlugin.instance
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,8 +41,11 @@ abstract class ChromaGamerBase {
|
||||||
* Removes the user from the cache. This will be called automatically after some time by default.
|
* Removes the user from the cache. This will be called automatically after some time by default.
|
||||||
*/
|
*/
|
||||||
fun uncache() {
|
fun uncache() {
|
||||||
val userCache: HashMap<Class<out Any?>, out ChromaGamerBase> = commonUserData!!.userCache
|
val userCache = commonUserData.userCache
|
||||||
synchronized(userCache) { if (userCache.containsKey(javaClass)) check(userCache.remove(javaClass) === this) { "A different player instance was cached!" } }
|
synchronized(userCache) {
|
||||||
|
if (userCache.containsKey(javaClass))
|
||||||
|
check(userCache.remove(javaClass) === this) { "A different player instance was cached!" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun scheduleUncache() {
|
protected open fun scheduleUncache() {
|
||||||
|
@ -68,42 +61,33 @@ abstract class ChromaGamerBase {
|
||||||
*
|
*
|
||||||
* @param user The account to connect with
|
* @param user The account to connect with
|
||||||
*/
|
*/
|
||||||
fun <T : ChromaGamerBase?> connectWith(user: T) {
|
fun <T : ChromaGamerBase> connectWith(user: T) {
|
||||||
// Set the ID, go through all linked files and connect them as well
|
synchronized(staticDataMap) {
|
||||||
val ownFolder = folder
|
// Set the ID, go through all linked files and connect them as well
|
||||||
val userFolder = user!!.folder
|
if (folder.equals(user.folder, ignoreCase = true))
|
||||||
if (ownFolder.equals(
|
throw RuntimeException("Do not connect two accounts of the same type! Type: $folder")
|
||||||
userFolder,
|
// This method acts from the point of view of the source, the target is the other way around
|
||||||
ignoreCase = true
|
fun sync(us: ChromaGamerBase, them: ChromaGamerBase) {
|
||||||
)
|
val ourData = us.commonUserData.playerData
|
||||||
) throw RuntimeException("Do not connect two accounts of the same type! Type: $ownFolder")
|
// Iterate through the target and their connections and set all our IDs in them
|
||||||
val ownData = commonUserData!!.playerData
|
for (theirOtherClass in registeredClasses) {
|
||||||
val userData = user.commonUserData!!.playerData
|
// Skip our own class because we have the IDs
|
||||||
userData[ownFolder + "_id"] = ownData.getString(ownFolder + "_id")
|
if (theirOtherClass == javaClass) continue
|
||||||
ownData[userFolder + "_id"] = userData.getString(userFolder + "_id")
|
val theirConnected = them.getAs(theirOtherClass) ?: continue
|
||||||
config.signalChange()
|
val theirConnectedData = theirConnected.commonUserData.playerData
|
||||||
user.config.signalChange()
|
// Set new IDs
|
||||||
val sync = Consumer { sourcedata: YamlConfiguration ->
|
for (ourOtherFolder in registeredFolders) {
|
||||||
val sourcefolder = if (sourcedata === ownData) ownFolder else userFolder
|
if (ourData.contains(ourOtherFolder + "_id")) {
|
||||||
val id = sourcedata.getString(sourcefolder + "_id")!!
|
theirConnectedData[ourOtherFolder + "_id"] =
|
||||||
for ((key, value) in staticDataMap) { // Set our ID in all files we can find, both from our connections and the new ones
|
ourData.getString(ourOtherFolder + "_id") // Set all existing IDs
|
||||||
if (key == javaClass || key == user.javaClass) continue
|
}
|
||||||
val entryFolder = value.folder
|
|
||||||
val otherid = sourcedata.getString(entryFolder + "_id") ?: continue
|
|
||||||
val cg = getUser(otherid, key)!!
|
|
||||||
val cgData = cg.commonUserData!!.playerData
|
|
||||||
cgData[sourcefolder + "_id"] = id // Set new IDs
|
|
||||||
for ((_, value1) in staticDataMap) {
|
|
||||||
val itemFolder = value1.folder
|
|
||||||
if (sourcedata.contains(itemFolder + "_id")) {
|
|
||||||
cgData[itemFolder + "_id"] = sourcedata.getString(itemFolder + "_id") // Set all existing IDs
|
|
||||||
}
|
}
|
||||||
|
theirConnected.config.signalChange()
|
||||||
}
|
}
|
||||||
cg.config.signalChange()
|
|
||||||
}
|
}
|
||||||
|
sync(this, user)
|
||||||
|
sync(user, this)
|
||||||
}
|
}
|
||||||
sync.accept(ownData)
|
|
||||||
sync.accept(userData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,8 +96,9 @@ abstract class ChromaGamerBase {
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
fun <T : ChromaGamerBase?> getConnectedID(cl: Class<T>): String {
|
fun <T : ChromaGamerBase> getConnectedID(cl: Class<T>): String? {
|
||||||
return commonUserData!!.playerData.getString(getFolderForType(cl) + "_id")!!
|
val data = staticDataMap[cl] ?: throw RuntimeException("Class $cl is not registered!")
|
||||||
|
return commonUserData.playerData.getString(data.folder + "_id")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,17 +108,16 @@ abstract class ChromaGamerBase {
|
||||||
* @param cl The target player class
|
* @param cl The target player class
|
||||||
* @return The player as a [T] object or null if the user doesn't have an account there
|
* @return The player as a [T] object or null if the user doesn't have an account there
|
||||||
*/
|
*/
|
||||||
fun <T : ChromaGamerBase?> getAs(cl: Class<T>): T? {
|
fun <T : ChromaGamerBase> getAs(cl: Class<T>): T? {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
if (cl.simpleName == javaClass.simpleName) return this as T
|
if (cl.simpleName == javaClass.simpleName) return this as T
|
||||||
val newfolder = getFolderForType(cl)
|
val newfolder = getFolderForType(cl)
|
||||||
?: throw RuntimeException("The specified class " + cl.simpleName + " isn't registered!")
|
|
||||||
if (newfolder == folder) // If in the same folder, the same filename is used
|
if (newfolder == folder) // If in the same folder, the same filename is used
|
||||||
return getUser(fileName, cl)
|
return getUser(fileName, cl)
|
||||||
val playerData = commonUserData!!.playerData
|
val playerData = commonUserData.playerData
|
||||||
return if (!playerData.contains(newfolder + "_id")) null else getUser(
|
return if (playerData.contains(newfolder + "_id"))
|
||||||
playerData.getString(newfolder + "_id")!!,
|
getUser(playerData.getString(newfolder + "_id")!!, cl)
|
||||||
cl
|
else null
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val fileName: String
|
val fileName: String
|
||||||
|
@ -141,7 +125,7 @@ abstract class ChromaGamerBase {
|
||||||
* This method returns the filename for this player data. For example, for Minecraft-related data, MC UUIDs, for Discord data, Discord IDs, etc.<br></br>
|
* This method returns the filename for this player data. For example, for Minecraft-related data, MC UUIDs, for Discord data, Discord IDs, etc.<br></br>
|
||||||
* **Does not include .yml**
|
* **Does not include .yml**
|
||||||
*/
|
*/
|
||||||
get() = commonUserData!!.playerData.getString(folder + "_id")!!
|
get() = commonUserData.playerData.getString(folder + "_id")!!
|
||||||
val folder: String
|
val folder: String
|
||||||
/**
|
/**
|
||||||
* This method returns the folder that this player data is stored in. For example: "minecraft".
|
* This method returns the folder that this player data is stored in. For example: "minecraft".
|
||||||
|
@ -166,11 +150,11 @@ abstract class ChromaGamerBase {
|
||||||
|
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
@JvmField
|
@JvmField
|
||||||
val channel: ConfigData<Channel> = config.getData("channel", Channel.GlobalChat,
|
val channel: ConfigData<Channel> = config.getData("channel", Channel.globalChat,
|
||||||
{ id ->
|
{ id ->
|
||||||
getChannels().filter { ch: Channel -> ch.identifier.equals(id as String, ignoreCase = true) }
|
getChannels().filter { ch: Channel -> ch.identifier.equals(id as String, ignoreCase = true) }
|
||||||
.findAny().orElse(null)
|
.findAny().orElseThrow { RuntimeException("Channel $id not found!") }
|
||||||
}) { ch -> ch.ID }
|
}, { ch -> ch.identifier })
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TBMC_PLAYERS_DIR = "TBMC/players/"
|
private const val TBMC_PLAYERS_DIR = "TBMC/players/"
|
||||||
|
@ -179,31 +163,31 @@ abstract class ChromaGamerBase {
|
||||||
/**
|
/**
|
||||||
* Holds data per user class
|
* Holds data per user class
|
||||||
*/
|
*/
|
||||||
private val staticDataMap = HashMap<Class<out ChromaGamerBase>, StaticUserData<*>>()
|
private val staticDataMap = HashMap<Class<out ChromaGamerBase>, StaticUserData<out ChromaGamerBase>>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for connecting with every type of user ([.connectWith]) and to init the configs.
|
* Used for connecting with every type of user ([.connectWith]) and to init the configs.
|
||||||
* Also, to construct an instance if an abstract class is provided.
|
* Also, to construct an instance if an abstract class is provided.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun <T : ChromaGamerBase?> RegisterPluginUserClass(userclass: Class<T>, constructor: Supplier<T>?) {
|
fun <T : ChromaGamerBase> registerPluginUserClass(userclass: Class<T>, constructor: Supplier<T>) {
|
||||||
val cl: Class<out T>
|
val prototype: Class<out T>
|
||||||
val folderName: String
|
val folderName: String
|
||||||
if (userclass.isAnnotationPresent(UserClass::class.java)) {
|
if (userclass.isAnnotationPresent(UserClass::class.java)) {
|
||||||
cl = userclass
|
prototype = userclass
|
||||||
folderName = userclass.getAnnotation(UserClass::class.java).foldername
|
folderName = userclass.getAnnotation(UserClass::class.java).foldername
|
||||||
} else if (userclass.isAnnotationPresent(AbstractUserClass::class.java)) {
|
} else if (userclass.isAnnotationPresent(AbstractUserClass::class.java)) {
|
||||||
val ucl: Class<out ChromaGamerBase> = userclass.getAnnotation(
|
val ucl = userclass.getAnnotation(AbstractUserClass::class.java).prototype.java
|
||||||
AbstractUserClass::class.java
|
if (!userclass.isAssignableFrom(ucl))
|
||||||
).prototype
|
throw RuntimeException("The prototype class (${ucl.simpleName}) must be a subclass of the userclass parameter (${userclass.simpleName})!")
|
||||||
if (!userclass.isAssignableFrom(ucl)) throw RuntimeException("The prototype class (" + ucl.simpleName + ") must be a subclass of the userclass parameter (" + userclass.simpleName + ")!")
|
@Suppress("UNCHECKED_CAST")
|
||||||
cl = ucl as Class<out T>
|
prototype = ucl as Class<out T>
|
||||||
folderName = userclass.getAnnotation(AbstractUserClass::class.java).foldername
|
folderName = userclass.getAnnotation(AbstractUserClass::class.java).foldername
|
||||||
} else throw RuntimeException("Class not registered as a user class! Use @UserClass or TBMCPlayerBase")
|
} else throw RuntimeException("Class not registered as a user class! Use @UserClass or TBMCPlayerBase")
|
||||||
val sud = StaticUserData<T>(folderName)
|
val sud = StaticUserData<T>(folderName)
|
||||||
sud.constructors[cl] = constructor
|
// Alawys register abstract and prototype class (TBMCPlayerBase and TBMCPlayer)
|
||||||
sud.constructors[userclass] =
|
sud.constructors[prototype] = constructor
|
||||||
constructor // Alawys register abstract and prototype class (TBMCPlayerBase and TBMCPlayer)
|
sud.constructors[userclass] = constructor
|
||||||
staticDataMap[userclass] = sud
|
staticDataMap[userclass] = sud
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,30 +198,42 @@ abstract class ChromaGamerBase {
|
||||||
* @return The folder name for the given type
|
* @return The folder name for the given type
|
||||||
* @throws RuntimeException If the class doesn't have the [UserClass] annotation.
|
* @throws RuntimeException If the class doesn't have the [UserClass] annotation.
|
||||||
*/
|
*/
|
||||||
fun <T : ChromaGamerBase?> getFolderForType(cl: Class<T>): String {
|
fun <T : ChromaGamerBase> getFolderForType(cl: Class<T>): String {
|
||||||
if (cl.isAnnotationPresent(UserClass::class.java)) return cl.getAnnotation(UserClass::class.java).foldername else if (cl.isAnnotationPresent(
|
if (cl.isAnnotationPresent(UserClass::class.java))
|
||||||
AbstractUserClass::class.java
|
return cl.getAnnotation(UserClass::class.java).foldername
|
||||||
)
|
else if (cl.isAnnotationPresent(AbstractUserClass::class.java))
|
||||||
) return cl.getAnnotation(AbstractUserClass::class.java).foldername
|
return cl.getAnnotation(AbstractUserClass::class.java).foldername
|
||||||
throw RuntimeException("Class not registered as a user class! Use @UserClass or @AbstractUserClass")
|
throw RuntimeException("Class not registered as a user class! Use @UserClass or @AbstractUserClass")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline val registeredFolders: Iterable<String> get() = staticDataMap.values.map { it.folder }
|
||||||
|
|
||||||
|
private inline val registeredClasses get() = staticDataMap.keys
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the (per-user) static data for the given player class.
|
||||||
|
* The static data is only stored once per user class so for example
|
||||||
|
* if you have two different [TBMCPlayerBase] classes, the static data is the same for both.
|
||||||
|
*
|
||||||
|
* @param cl The class to get the data from (like [TBMCPlayerBase] or one of its subclasses)
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private fun <T : ChromaGamerBase> getStaticData(cl: Class<out T>) = staticDataMap.entries
|
||||||
|
.filter { (key, _) -> key.isAssignableFrom(cl) }
|
||||||
|
.map { (_, value) -> value as StaticUserData<T> }
|
||||||
|
.firstOrNull()
|
||||||
|
?: throw RuntimeException("Class $cl not registered as a user class! Use registerUserClass()")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the player class for the given folder name.
|
* Returns the player class for the given folder name.
|
||||||
*
|
*
|
||||||
* @param foldername The folder to get the class from (like "minecraft")
|
* @param foldername The folder to get the class from (like "minecraft")
|
||||||
* @return The type for the given folder name or null if not found
|
* @return The type for the given folder name or null if not found
|
||||||
*/
|
*/
|
||||||
fun getTypeForFolder(foldername: String?): Class<out ChromaGamerBase> {
|
fun getTypeForFolder(foldername: String?): Class<out ChromaGamerBase>? {
|
||||||
synchronized(staticDataMap) {
|
synchronized(staticDataMap) {
|
||||||
return staticDataMap.entries.stream()
|
return staticDataMap.filter { (_, value) -> value.folder.equals(foldername, ignoreCase = true) }
|
||||||
.filter { (_, value): Map.Entry<Class<out ChromaGamerBase>, StaticUserData<*>> ->
|
.map { (key, _) -> key }.singleOrNull()
|
||||||
value.folder.equals(
|
|
||||||
foldername,
|
|
||||||
ignoreCase = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.map { (key, value) -> java.util.Map.Entry.key }.findAny().orElse(null)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,36 +246,32 @@ abstract class ChromaGamerBase {
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun <T : ChromaGamerBase> getUser(fname: String, cl: Class<T>): T {
|
fun <T : S, S : ChromaGamerBase> getUser(fname: String, cl: Class<T>): T {
|
||||||
val staticUserData: StaticUserData<*> = staticDataMap.entries
|
@Suppress("UNCHECKED_CAST")
|
||||||
.filter { (key, _) -> key.isAssignableFrom(cl) }
|
val staticUserData: StaticUserData<S> = getStaticData(cl)
|
||||||
.map { (_, value) -> value }
|
|
||||||
.firstOrNull()
|
|
||||||
?: throw RuntimeException("User class not registered! Use @UserClass or @AbstractUserClass")
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val commonUserData: CommonUserData<T> = (staticUserData.userDataMap[fname]
|
val commonUserData: CommonUserData<S> = staticUserData.userDataMap[fname]
|
||||||
?: run {
|
?: run {
|
||||||
val folder = staticUserData.folder
|
val folder = staticUserData.folder
|
||||||
val file = File(TBMC_PLAYERS_DIR + folder, "$fname.yml")
|
val file = File(TBMC_PLAYERS_DIR + folder, "$fname.yml")
|
||||||
file.parentFile.mkdirs()
|
file.parentFile.mkdirs()
|
||||||
val playerData = YamlConfiguration.loadConfiguration(file)
|
val playerData = YamlConfiguration.loadConfiguration(file)
|
||||||
playerData[staticUserData.folder + "_id"] = fname
|
playerData[staticUserData.folder + "_id"] = fname
|
||||||
CommonUserData<T>(playerData)
|
CommonUserData<S>(playerData)
|
||||||
}.also { staticUserData.userDataMap[fname] = it }) as CommonUserData<T>
|
}.also { staticUserData.userDataMap[fname] = it }
|
||||||
|
|
||||||
return if (commonUserData.userCache.containsKey(cl)) commonUserData.userCache[cl] as T
|
return commonUserData.userCache[cl] ?: run {
|
||||||
else {
|
|
||||||
val obj = createNewUser(cl, staticUserData, commonUserData)
|
val obj = createNewUser(cl, staticUserData, commonUserData)
|
||||||
commonUserData.userCache[cl] = obj
|
commonUserData.userCache.put(obj)
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : ChromaGamerBase> createNewUser(
|
private fun <T : S, S : ChromaGamerBase> createNewUser(
|
||||||
cl: Class<T>,
|
cl: Class<T>,
|
||||||
staticUserData: StaticUserData<*>,
|
staticUserData: StaticUserData<S>,
|
||||||
commonUserData: CommonUserData<*>
|
commonUserData: CommonUserData<S>
|
||||||
): T {
|
): T {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val obj = staticUserData.constructors[cl]?.get() as T? ?: run {
|
val obj = staticUserData.constructors[cl]?.get() as T? ?: run {
|
||||||
|
@ -290,6 +282,7 @@ abstract class ChromaGamerBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
obj.commonUserData = commonUserData
|
obj.commonUserData = commonUserData
|
||||||
|
obj.config = IHaveConfig({ obj.save() }, commonUserData.playerData)
|
||||||
obj.init()
|
obj.init()
|
||||||
obj.scheduleUncache()
|
obj.scheduleUncache()
|
||||||
return obj
|
return obj
|
||||||
|
@ -300,7 +293,7 @@ abstract class ChromaGamerBase {
|
||||||
*
|
*
|
||||||
* @param converter The converter that returns an object corresponding to the sender or null, if it's not the right type.
|
* @param converter The converter that returns an object corresponding to the sender or null, if it's not the right type.
|
||||||
*/
|
*/
|
||||||
fun <T : ChromaGamerBase?> addConverter(converter: Function<CommandSender, Optional<T>>) {
|
fun addConverter(converter: Function<CommandSender, Optional<out ChromaGamerBase>>) {
|
||||||
senderConverters.add(0, converter)
|
senderConverters.add(0, converter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
package buttondevteam.lib.player
|
package buttondevteam.lib.player
|
||||||
|
|
||||||
|
import buttondevteam.lib.utils.TypedMap
|
||||||
import org.bukkit.configuration.file.YamlConfiguration
|
import org.bukkit.configuration.file.YamlConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per user, regardless of actual type
|
* Per user, regardless of actual type
|
||||||
*
|
*
|
||||||
* @param <T> The user class, may be abstract
|
* @param T The user class, may be abstract
|
||||||
</T> */
|
</T> */
|
||||||
class CommonUserData<T : ChromaGamerBase>(@JvmField val playerData: YamlConfiguration) {
|
class CommonUserData<T : ChromaGamerBase>(@JvmField val playerData: YamlConfiguration) {
|
||||||
|
/**
|
||||||
|
* Caches users of the given user type. This is used to avoid loading the same user multiple times.
|
||||||
|
* Also contains instances of subclasses of the given user type.
|
||||||
|
*/
|
||||||
@JvmField
|
@JvmField
|
||||||
val userCache: HashMap<Class<out T>, out T> = HashMap()
|
val userCache: TypedMap<T> = TypedMap()
|
||||||
}
|
}
|
|
@ -1,23 +1,21 @@
|
||||||
package buttondevteam.lib.player;
|
package buttondevteam.lib.player
|
||||||
|
|
||||||
import lombok.Getter;
|
import java.util.function.Supplier
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per user class
|
* Per user class
|
||||||
*
|
*
|
||||||
* @param <T> The user class type, may be abstract
|
* @param T The user class type, may be abstract
|
||||||
*/
|
*/
|
||||||
@Getter
|
class StaticUserData<T : ChromaGamerBase>(val folder: String) {
|
||||||
@RequiredArgsConstructor
|
/**
|
||||||
public class StaticUserData<T extends ChromaGamerBase> {
|
* Constructors for the subclasses of the given user type.
|
||||||
private final HashMap<Class<? extends T>, Supplier<T>> constructors = new HashMap<>();
|
*/
|
||||||
/**
|
val constructors = HashMap<Class<out T>, Supplier<T>>()
|
||||||
* Key: User ID
|
|
||||||
*/
|
/**
|
||||||
private final HashMap<String, CommonUserData<?>> userDataMap = new HashMap<>();
|
* Key: User ID
|
||||||
private final String folder;
|
*/
|
||||||
}
|
val userDataMap = HashMap<String, CommonUserData<T>>()
|
||||||
|
|
||||||
|
}
|
|
@ -1,83 +1,65 @@
|
||||||
package buttondevteam.lib.player;
|
package buttondevteam.lib.player
|
||||||
|
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import org.bukkit.Bukkit
|
||||||
import buttondevteam.lib.architecture.IHaveConfig;
|
import java.util.*
|
||||||
import lombok.Getter;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.OfflinePlayer;
|
|
||||||
|
|
||||||
import java.util.Set;
|
@AbstractUserClass(foldername = "minecraft", prototype = TBMCPlayer::class)
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@AbstractUserClass(foldername = "minecraft", prototype = TBMCPlayer.class)
|
|
||||||
@TBMCPlayerEnforcer
|
@TBMCPlayerEnforcer
|
||||||
public abstract class TBMCPlayerBase extends ChromaGamerBase {
|
abstract class TBMCPlayerBase : ChromaGamerBase() {
|
||||||
protected UUID uuid;
|
val uniqueId: UUID get() = UUID.fromString(fileName)
|
||||||
|
|
||||||
@Getter
|
@JvmField
|
||||||
private final IHaveConfig config = new IHaveConfig(this::save);
|
val playerName = super.config.getData("PlayerName", "")
|
||||||
|
public override fun init() {
|
||||||
|
super.init()
|
||||||
|
val pluginName = if (javaClass.isAnnotationPresent(PlayerClass::class.java))
|
||||||
|
javaClass.getAnnotation(PlayerClass::class.java).pluginname
|
||||||
|
else
|
||||||
|
throw RuntimeException("Class not defined as player class! Use @PlayerClass")
|
||||||
|
val playerData = commonUserData.playerData
|
||||||
|
var section = playerData.getConfigurationSection(pluginName)
|
||||||
|
if (section == null) section = playerData.createSection(pluginName)
|
||||||
|
config.reset(section)
|
||||||
|
}
|
||||||
|
|
||||||
public UUID getUUID() {
|
/**
|
||||||
if (uuid == null)
|
* If the player is online, we don't uncache, it will be uncached when they log out
|
||||||
uuid = UUID.fromString(getFileName());
|
*/
|
||||||
return uuid;
|
override fun scheduleUncache() {
|
||||||
}
|
val p = Bukkit.getPlayer(uniqueId)
|
||||||
|
if (p == null || !p.isOnline) super.scheduleUncache()
|
||||||
|
}
|
||||||
|
|
||||||
public final ConfigData<String> PlayerName = super.config.getData("PlayerName", "");
|
override fun save() {
|
||||||
|
val keys = commonUserData.playerData.getKeys(false)
|
||||||
|
if (keys.size > 1) // PlayerName is always saved, but we don't need a file for just that
|
||||||
|
super.save()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
companion object {
|
||||||
* Get player as a plugin player.
|
/**
|
||||||
*
|
* Get player as a plugin player.
|
||||||
* @param uuid The UUID of the player to get
|
*
|
||||||
* @param cl The type of the player
|
* @param uuid The UUID of the player to get
|
||||||
* @return The requested player object
|
* @param cl The type of the player
|
||||||
*/
|
* @return The requested player object
|
||||||
public static <T extends TBMCPlayerBase> T getPlayer(UUID uuid, Class<T> cl) {
|
*/
|
||||||
var player = ChromaGamerBase.getUser(uuid.toString(), cl);
|
fun <T : TBMCPlayerBase> getPlayer(uuid: UUID, cl: Class<T>): T {
|
||||||
if (!player.getUUID().equals(uuid)) //It will be set from the filename because we check it for scheduling the uncache.
|
val player = getUser(uuid.toString(), cl)
|
||||||
throw new IllegalStateException("Player UUID differs after converting from and to string...");
|
check(player.uniqueId == uuid) { "Player UUID differs after converting from and to string..." }
|
||||||
return player;
|
return player
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void init() {
|
* This method returns a TBMC player from their name. See [Bukkit.getOfflinePlayer].
|
||||||
super.init();
|
*
|
||||||
|
* @param name The player's name
|
||||||
String pluginname;
|
* @return The [TBMCPlayer] object for the player
|
||||||
if (getClass().isAnnotationPresent(PlayerClass.class))
|
*/
|
||||||
pluginname = getClass().getAnnotation(PlayerClass.class).pluginname();
|
@Suppress("deprecation")
|
||||||
else
|
fun <T : TBMCPlayerBase> getFromName(name: String, cl: Class<T>): T {
|
||||||
throw new RuntimeException("Class not defined as player class! Use @PlayerClass");
|
val p = Bukkit.getOfflinePlayer(name)
|
||||||
|
return getPlayer(p.uniqueId, cl)
|
||||||
var playerData = commonUserData.playerData;
|
}
|
||||||
var section = playerData.getConfigurationSection(pluginname);
|
}
|
||||||
if (section == null) section = playerData.createSection(pluginname);
|
}
|
||||||
config.reset(section);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void scheduleUncache() { //Don't schedule it, it will happen on quit - if the player is online
|
|
||||||
var p = Bukkit.getPlayer(getUUID());
|
|
||||||
if (p == null || !p.isOnline())
|
|
||||||
super.scheduleUncache();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method returns a TBMC player from their name. See {@link Bukkit#getOfflinePlayer(String)}.
|
|
||||||
*
|
|
||||||
* @param name The player's name
|
|
||||||
* @return The {@link TBMCPlayer} object for the player
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public static <T extends TBMCPlayerBase> T getFromName(String name, Class<T> cl) {
|
|
||||||
OfflinePlayer p = Bukkit.getOfflinePlayer(name);
|
|
||||||
return getPlayer(p.getUniqueId(), cl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void save() {
|
|
||||||
Set<String> keys = commonUserData.playerData.getKeys(false);
|
|
||||||
if (keys.size() > 1) // PlayerName is always saved, but we don't need a file for just that
|
|
||||||
super.save();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package buttondevteam.lib.utils
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map that can be used to store data of different types. The data is stored in a map, but the type is checked when getting
|
||||||
|
* the data.
|
||||||
|
*
|
||||||
|
* @param T The upper bound of the types that can be stored in this map
|
||||||
|
*/
|
||||||
|
class TypedMap<T : Any> {
|
||||||
|
private val map = HashMap<KClass<*>, T>()
|
||||||
|
fun <S : T> put(value: S) {
|
||||||
|
map[value::class] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun <S : T> get(type: Class<S>): S? {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return map[type.kotlin] as S?
|
||||||
|
}
|
||||||
|
|
||||||
|
fun containsKey(type: Class<in T>): Boolean {
|
||||||
|
return map.containsKey(type.kotlin)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(type: Class<in T>): T? {
|
||||||
|
return map.remove(type.kotlin)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue