More conversions, user classes, some configs
Removed writePluginList config option
This commit is contained in:
parent
85efc873d6
commit
4ed001cb54
16 changed files with 423 additions and 447 deletions
|
@ -248,7 +248,7 @@
|
||||||
<github.global.server>github</github.global.server>
|
<github.global.server>github</github.global.server>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<noprefix.version>1.0.1</noprefix.version>
|
<noprefix.version>1.0.1</noprefix.version>
|
||||||
<kotlin.version>1.8.10</kotlin.version>
|
<kotlin.version>1.8.20</kotlin.version>
|
||||||
</properties>
|
</properties>
|
||||||
<scm>
|
<scm>
|
||||||
<url>https://github.com/TBMCPlugins/mvn-repo</url>
|
<url>https://github.com/TBMCPlugins/mvn-repo</url>
|
||||||
|
|
|
@ -26,17 +26,10 @@ import org.bukkit.command.Command
|
||||||
import org.bukkit.command.CommandSender
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.command.ConsoleCommandSender
|
import org.bukkit.command.ConsoleCommandSender
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
import org.bukkit.plugin.Plugin
|
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Function
|
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
import java.util.logging.Logger
|
|
||||||
|
|
||||||
class MainPlugin : ButtonPlugin() {
|
class MainPlugin : ButtonPlugin() {
|
||||||
private var logger: Logger? = null
|
|
||||||
private var economy: Economy? = null
|
private var economy: Economy? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,12 +38,6 @@ class MainPlugin : ButtonPlugin() {
|
||||||
*/
|
*/
|
||||||
var isChatHandlerEnabled = true
|
var isChatHandlerEnabled = true
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether the plugin should write a list of installed plugins in a txt file.
|
|
||||||
* It can be useful if some other software needs to know the plugins.
|
|
||||||
*/
|
|
||||||
private val writePluginList = iConfig.getData("writePluginList", false)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The chat format to use for messages from other platforms if Chroma-Chat is not installed.
|
* The chat format to use for messages from other platforms if Chroma-Chat is not installed.
|
||||||
*/
|
*/
|
||||||
|
@ -68,12 +55,11 @@ class MainPlugin : ButtonPlugin() {
|
||||||
*/
|
*/
|
||||||
val prioritizeCustomCommands = iConfig.getData("prioritizeCustomCommands", false)
|
val prioritizeCustomCommands = iConfig.getData("prioritizeCustomCommands", false)
|
||||||
public override fun pluginEnable() {
|
public override fun pluginEnable() {
|
||||||
Instance = this
|
instance = this
|
||||||
val pdf = description
|
val pdf = description
|
||||||
logger = getLogger()
|
|
||||||
if (!setupPermissions()) throw NullPointerException("No permission plugin found!")
|
if (!setupPermissions()) throw NullPointerException("No permission plugin found!")
|
||||||
if (!setupEconomy()) //Though Essentials always provides economy, but we don't require Essentials
|
if (!setupEconomy()) //Though Essentials always provides economy, but we don't require Essentials
|
||||||
getLogger().warning("No economy plugin found! Components using economy will not be registered.")
|
logger.warning("No economy plugin found! Components using economy will not be registered.")
|
||||||
saveConfig()
|
saveConfig()
|
||||||
registerComponent(this, RestartComponent())
|
registerComponent(this, RestartComponent())
|
||||||
registerComponent(this, ChannelComponent())
|
registerComponent(this, ChannelComponent())
|
||||||
|
@ -125,31 +111,20 @@ class MainPlugin : ButtonPlugin() {
|
||||||
TBMCChatAPI.RegisterChatChannel(ChatRoom("§aGREEN§f", Color.Green, "green"))
|
TBMCChatAPI.RegisterChatChannel(ChatRoom("§aGREEN§f", Color.Green, "green"))
|
||||||
TBMCChatAPI.RegisterChatChannel(ChatRoom("§bBLUE§f", Color.Blue, "blue"))
|
TBMCChatAPI.RegisterChatChannel(ChatRoom("§bBLUE§f", Color.Blue, "blue"))
|
||||||
TBMCChatAPI.RegisterChatChannel(ChatRoom("§5PURPLE§f", Color.DarkPurple, "purple"))
|
TBMCChatAPI.RegisterChatChannel(ChatRoom("§5PURPLE§f", Color.DarkPurple, "purple"))
|
||||||
val playerSupplier = Supplier { Bukkit.getOnlinePlayers().map { obj: Player -> obj.name }.asIterable() }
|
val playerSupplier = Supplier { Bukkit.getOnlinePlayers().map { obj -> obj.name }.asIterable() }
|
||||||
command2MC.addParamConverter(OfflinePlayer::class.java, { name: String? ->
|
command2MC.addParamConverter(
|
||||||
Bukkit.getOfflinePlayer(
|
OfflinePlayer::class.java,
|
||||||
name!!
|
{ name -> Bukkit.getOfflinePlayer(name) },
|
||||||
|
"Player not found!",
|
||||||
|
playerSupplier
|
||||||
)
|
)
|
||||||
}, "Player not found!", playerSupplier)
|
command2MC.addParamConverter(
|
||||||
command2MC.addParamConverter<Player>(
|
Player::class.java,
|
||||||
Player::class.java, Function { name: String ->
|
{ name -> Bukkit.getPlayer(name) },
|
||||||
Bukkit.getPlayer(name)
|
"Online player not found!",
|
||||||
}, "Online player not found!", playerSupplier
|
playerSupplier
|
||||||
)
|
|
||||||
if (writePluginList.get()) {
|
|
||||||
try {
|
|
||||||
Files.write(File("plugins", "plugins.txt").toPath(), Iterable {
|
|
||||||
Arrays.stream(Bukkit.getPluginManager().plugins)
|
|
||||||
.map { p: Plugin -> p.dataFolder.name as CharSequence }
|
|
||||||
.iterator()
|
|
||||||
})
|
|
||||||
} catch (e: IOException) {
|
|
||||||
TBMCCoreAPI.SendException("Failed to write plugin list!", e, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (server.pluginManager.isPluginEnabled("Essentials")) ess = getPlugin(
|
|
||||||
Essentials::class.java
|
|
||||||
)
|
)
|
||||||
|
if (server.pluginManager.isPluginEnabled("Essentials")) ess = getPlugin(Essentials::class.java)
|
||||||
logger!!.info(pdf.name + " has been Enabled (V." + pdf.version + ") Test: " + test.get() + ".")
|
logger!!.info(pdf.name + " has been Enabled (V." + pdf.version + ") Test: " + test.get() + ".")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,8 +157,7 @@ class MainPlugin : ButtonPlugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmField
|
lateinit var instance: MainPlugin
|
||||||
var Instance: MainPlugin = null
|
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var permission: Permission? = null
|
var permission: Permission? = null
|
||||||
|
|
|
@ -1,83 +1,69 @@
|
||||||
package buttondevteam.core.component.channel;
|
package buttondevteam.core.component.channel
|
||||||
|
|
||||||
import buttondevteam.lib.ChromaUtils;
|
import buttondevteam.core.MainPlugin
|
||||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
import buttondevteam.lib.ChromaUtils
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.TBMCSystemChatEvent.BroadcastTarget
|
||||||
import buttondevteam.lib.chat.*;
|
import buttondevteam.lib.architecture.Component
|
||||||
import buttondevteam.lib.player.ChromaGamerBase;
|
import buttondevteam.lib.chat.*
|
||||||
import lombok.RequiredArgsConstructor;
|
import buttondevteam.lib.chat.Command2.*
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import buttondevteam.lib.player.ChromaGamerBase
|
||||||
|
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.
|
||||||
*/
|
*/
|
||||||
public class ChannelComponent extends Component<JavaPlugin> {
|
class ChannelComponent : Component<MainPlugin>() {
|
||||||
static TBMCSystemChatEvent.BroadcastTarget roomJoinLeave;
|
override fun register(plugin: JavaPlugin) {
|
||||||
|
super.register(plugin)
|
||||||
@Override
|
roomJoinLeave = BroadcastTarget.add("roomJoinLeave") //Even if it's disabled, global channels continue to work
|
||||||
protected void register(JavaPlugin plugin) {
|
|
||||||
super.register(plugin);
|
|
||||||
roomJoinLeave = TBMCSystemChatEvent.BroadcastTarget.add("roomJoinLeave"); //Even if it's disabled, global channels continue to work
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun unregister(plugin: JavaPlugin) {
|
||||||
protected void unregister(JavaPlugin plugin) {
|
super.unregister(plugin)
|
||||||
super.unregister(plugin);
|
BroadcastTarget.remove(roomJoinLeave)
|
||||||
TBMCSystemChatEvent.BroadcastTarget.remove(roomJoinLeave);
|
roomJoinLeave = null
|
||||||
roomJoinLeave = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun enable() {}
|
||||||
protected void enable() {
|
override fun disable() {}
|
||||||
}
|
fun registerChannelCommand(channel: Channel) {
|
||||||
|
if (!ChromaUtils.isTest()) registerCommand(ChannelCommand(channel))
|
||||||
@Override
|
|
||||||
protected void disable() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void registerChannelCommand(Channel channel) {
|
|
||||||
if (!ChromaUtils.isTest())
|
|
||||||
registerCommand(new ChannelCommand(channel));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@CommandClass
|
@CommandClass
|
||||||
@RequiredArgsConstructor
|
private class ChannelCommand(private val channel: Channel) : ICommand2MC() {
|
||||||
private static class ChannelCommand extends ICommand2MC {
|
override fun getCommandPath(): String {
|
||||||
private final Channel channel;
|
return channel.identifier
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCommandPath() {
|
|
||||||
return channel.identifier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun getCommandPaths(): Array<String> {
|
||||||
public String[] getCommandPaths() {
|
return channel.extraIdentifiers.get().toTypedArray()
|
||||||
return channel.extraIdentifiers.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command2.Subcommand
|
@Subcommand
|
||||||
public void def(Command2MCSender senderMC, @Command2.OptionalArg @Command2.TextArg String message) {
|
fun def(senderMC: Command2MCSender, @OptionalArg @TextArg message: String?) {
|
||||||
var sender = senderMC.getSender();
|
val sender = senderMC.sender
|
||||||
var user = ChromaGamerBase.getFromSender(sender);
|
val user = ChromaGamerBase.getFromSender(sender)
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
sender.sendMessage("§cYou can't use channels from this platform.");
|
sender.sendMessage("§cYou can't use channels from this platform.")
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
Channel oldch = user.channel.get();
|
val oldch = user.channel.get()
|
||||||
if (oldch instanceof ChatRoom)
|
if (oldch is ChatRoom) oldch.leaveRoom(sender)
|
||||||
((ChatRoom) oldch).leaveRoom(sender);
|
if (oldch == channel) user.channel.set(Channel.GlobalChat) else {
|
||||||
if (oldch.equals(channel))
|
user.channel.set(channel)
|
||||||
user.channel.set(Channel.GlobalChat);
|
if (channel is ChatRoom) channel.joinRoom(sender)
|
||||||
else {
|
|
||||||
user.channel.set(channel);
|
|
||||||
if (channel instanceof ChatRoom)
|
|
||||||
((ChatRoom) channel).joinRoom(sender);
|
|
||||||
}
|
}
|
||||||
sender.sendMessage("§6You are now talking in: §b" + user.channel.get().displayName.get());
|
sender.sendMessage("§6You are now talking in: §b" + user.channel.get().displayName.get())
|
||||||
} else
|
} else TBMCChatAPI.SendChatMessage(
|
||||||
TBMCChatAPI.SendChatMessage(ChatMessage.builder(sender, user, message).fromCommand(true)
|
ChatMessage.builder(sender, user, message).fromCommand(true)
|
||||||
.permCheck(senderMC.getPermCheck()).build(), channel);
|
.permCheck(senderMC.permCheck).build(), channel
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var roomJoinLeave: BroadcastTarget? = null
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -33,7 +33,7 @@ public class MemberCommand extends ICommand2MC {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean addRemove(CommandSender sender, OfflinePlayer op, boolean add) {
|
public boolean addRemove(CommandSender sender, OfflinePlayer op, boolean add) {
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, () -> {
|
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.instance, () -> {
|
||||||
if (!op.hasPlayedBefore()) {
|
if (!op.hasPlayedBefore()) {
|
||||||
sender.sendMessage("§cCannot find player or haven't played before.");
|
sender.sendMessage("§cCannot find player or haven't played before.");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class ScheduledRestartCommand extends ICommand2MC {
|
||||||
sender.sendMessage("Scheduled restart in " + seconds);
|
sender.sendMessage("Scheduled restart in " + seconds);
|
||||||
ScheduledServerRestartEvent e = new ScheduledServerRestartEvent(restarttime, this);
|
ScheduledServerRestartEvent e = new ScheduledServerRestartEvent(restarttime, this);
|
||||||
Bukkit.getPluginManager().callEvent(e);
|
Bukkit.getPluginManager().callEvent(e);
|
||||||
restarttask = Bukkit.getScheduler().runTaskTimer(MainPlugin.Instance, () -> {
|
restarttask = Bukkit.getScheduler().runTaskTimer(MainPlugin.instance, () -> {
|
||||||
if (restartCounter < 0) {
|
if (restartCounter < 0) {
|
||||||
restarttask.cancel();
|
restarttask.cancel();
|
||||||
restartbar.getPlayers().forEach(p -> restartbar.removePlayer(p));
|
restartbar.getPlayers().forEach(p -> restartbar.removePlayer(p));
|
||||||
|
|
|
@ -81,7 +81,7 @@ public final class ChromaUtils {
|
||||||
*/
|
*/
|
||||||
public static <T> T doItAsync(Supplier<T> what, T def) {
|
public static <T> T doItAsync(Supplier<T> what, T def) {
|
||||||
if (Bukkit.isPrimaryThread())
|
if (Bukkit.isPrimaryThread())
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, what::get);
|
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.instance, what::get);
|
||||||
else
|
else
|
||||||
return what.get();
|
return what.get();
|
||||||
return def;
|
return def;
|
||||||
|
|
|
@ -170,7 +170,7 @@ public class TBMCCoreAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean IsTestServer() {
|
public static boolean IsTestServer() {
|
||||||
if (MainPlugin.Instance == null) return true;
|
if (MainPlugin.instance == null) return true;
|
||||||
return MainPlugin.Instance.test.get();
|
return MainPlugin.instance.test.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -67,9 +67,9 @@ class ConfigData<T> internal constructor(
|
||||||
return getter.apply(convert(`val`, pdef)).also { value = it }
|
return getter.apply(convert(`val`, pdef)).also { value = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun set(value: T?) {
|
override fun set(value: T?) { // TODO: Have a separate method for removing the value from the config and make this non-nullable
|
||||||
if (readOnly) return //Safety for Discord channel/role data
|
if (readOnly) return //Safety for Discord channel/role data
|
||||||
val `val` = value?.let { setter.apply(value) }
|
val `val` = value?.let { setter.apply(it) }
|
||||||
setInternal(`val`)
|
setInternal(`val`)
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
@ -89,14 +89,14 @@ class ConfigData<T> internal constructor(
|
||||||
val sa = config.saveAction
|
val sa = config.saveAction
|
||||||
val root = cc.root
|
val root = cc.root
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
MainPlugin.Instance.logger.warning("Attempted to save config with no root! Name: ${config.config.name}")
|
MainPlugin.instance.logger.warning("Attempted to save config with no root! Name: ${config.config.name}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!saveTasks.containsKey(cc.root)) {
|
if (!saveTasks.containsKey(cc.root)) {
|
||||||
synchronized(saveTasks) {
|
synchronized(saveTasks) {
|
||||||
saveTasks.put(
|
saveTasks.put(
|
||||||
root,
|
root,
|
||||||
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, {
|
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.instance, {
|
||||||
synchronized(saveTasks) {
|
synchronized(saveTasks) {
|
||||||
saveTasks.remove(root)
|
saveTasks.remove(root)
|
||||||
sa.run()
|
sa.run()
|
||||||
|
|
|
@ -179,7 +179,7 @@ class IHaveConfig(
|
||||||
.filter(Predicate<ConfigData<Any?>> { obj: ConfigData<Any?>? -> Objects.nonNull(obj) })
|
.filter(Predicate<ConfigData<Any?>> { obj: ConfigData<Any?>? -> Objects.nonNull(obj) })
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList())
|
||||||
} else {
|
} else {
|
||||||
if (TBMCCoreAPI.IsTestServer()) MainPlugin.Instance.logger.warning(
|
if (TBMCCoreAPI.IsTestServer()) MainPlugin.instance.logger.warning(
|
||||||
"Method " + mName + " returns a config but its parameters are unknown: " + Arrays.toString(
|
"Method " + mName + " returns a config but its parameters are unknown: " + Arrays.toString(
|
||||||
m.parameterTypes
|
m.parameterTypes
|
||||||
)
|
)
|
||||||
|
@ -187,7 +187,7 @@ class IHaveConfig(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for (c in configList) {
|
for (c in configList) {
|
||||||
if (c.path.length == 0) c.setPath(mName) else if (c.path != mName) MainPlugin.Instance.logger.warning(
|
if (c.path.length == 0) c.setPath(mName) else if (c.path != mName) MainPlugin.instance.logger.warning(
|
||||||
"Config name does not match: " + c.path + " instead of " + mName
|
"Config name does not match: " + c.path + " instead of " + mName
|
||||||
)
|
)
|
||||||
c.get() //Saves the default value if needed - also checks validity
|
c.get() //Saves the default value if needed - also checks validity
|
||||||
|
|
|
@ -23,7 +23,7 @@ class ListConfigData<T> internal constructor(
|
||||||
listConfig.reset()
|
listConfig.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(): List? {
|
override fun get(): List {
|
||||||
return listConfig.get()
|
return listConfig.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
||||||
return false // Unknown command
|
return false // Unknown command
|
||||||
}
|
}
|
||||||
//Needed because permission checking may load the (perhaps offline) sender's file which is disallowed on the main thread
|
//Needed because permission checking may load the (perhaps offline) sender's file which is disallowed on the main thread
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance) { _ ->
|
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.instance) { _ ->
|
||||||
try {
|
try {
|
||||||
dispatcher.execute(results)
|
dispatcher.execute(results)
|
||||||
} catch (e: CommandSyntaxException) {
|
} catch (e: CommandSyntaxException) {
|
||||||
|
@ -103,7 +103,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
||||||
TBMCCoreAPI.SendException(
|
TBMCCoreAPI.SendException(
|
||||||
"Command execution failed for sender " + sender.name + "(" + sender.javaClass.canonicalName + ") and message " + commandline,
|
"Command execution failed for sender " + sender.name + "(" + sender.javaClass.canonicalName + ") and message " + commandline,
|
||||||
e,
|
e,
|
||||||
MainPlugin.Instance
|
MainPlugin.instance
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
||||||
lastNode.addChild(getExecutableNode(meth, command, ann, remainingPath, CommandArgumentHelpManager(command), fullPath))
|
lastNode.addChild(getExecutableNode(meth, command, ann, remainingPath, CommandArgumentHelpManager(command), fullPath))
|
||||||
if (mainCommandNode == null) mainCommandNode = mainNode
|
if (mainCommandNode == null) mainCommandNode = mainNode
|
||||||
else if (mainNode!!.name != mainCommandNode.name) {
|
else if (mainNode!!.name != mainCommandNode.name) {
|
||||||
MainPlugin.Instance.logger.warning("Multiple commands are defined in the same class! This is not supported. Class: " + command.javaClass.simpleName)
|
MainPlugin.instance.logger.warning("Multiple commands are defined in the same class! This is not supported. Class: " + command.javaClass.simpleName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mainCommandNode == null) {
|
if (mainCommandNode == null) {
|
||||||
|
|
|
@ -170,8 +170,9 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
val i = commandline.indexOf(' ')
|
val i = commandline.indexOf(' ')
|
||||||
val mainpath = commandline.substring(1, if (i == -1) commandline.length else i) //Without the slash
|
val mainpath = commandline.substring(1, if (i == -1) commandline.length else i) //Without the slash
|
||||||
//Our commands aren't PluginCommands, unless it's specified in the plugin.yml
|
//Our commands aren't PluginCommands, unless it's specified in the plugin.yml
|
||||||
return if ((!checkPlugin || (MainPlugin.Instance.prioritizeCustomCommands.get() == true))
|
return if ((!checkPlugin || (MainPlugin.instance.prioritizeCustomCommands.get() == true))
|
||||||
|| Bukkit.getPluginCommand(mainpath)?.let { it.plugin is ButtonPlugin } != false)
|
|| Bukkit.getPluginCommand(mainpath)?.let { it.plugin is ButtonPlugin } != false
|
||||||
|
)
|
||||||
super.handleCommand(sender, commandline) else false
|
super.handleCommand(sender, commandline) else false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +208,11 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
private fun executeCommand(sender: CommandSender, command: Command, label: String, args: Array<String>): Boolean {
|
private fun executeCommand(sender: CommandSender, command: Command, label: String, args: Array<String>): Boolean {
|
||||||
val user = ChromaGamerBase.getFromSender(sender)
|
val user = ChromaGamerBase.getFromSender(sender)
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
TBMCCoreAPI.SendException("Failed to run Bukkit command for user!", Throwable("No Chroma user found"), MainPlugin.Instance)
|
TBMCCoreAPI.SendException(
|
||||||
|
"Failed to run Bukkit command for user!",
|
||||||
|
Throwable("No Chroma user found"),
|
||||||
|
MainPlugin.instance
|
||||||
|
)
|
||||||
sender.sendMessage("§cAn internal error occurred.")
|
sender.sendMessage("§cAn internal error occurred.")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -250,9 +255,16 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
|
|
||||||
private fun registerTabcomplete(command2MC: ICommand2MC, commandNode: LiteralCommandNode<Command2MCSender>, bukkitCommand: Command) {
|
private fun registerTabcomplete(command2MC: ICommand2MC, commandNode: LiteralCommandNode<Command2MCSender>, bukkitCommand: Command) {
|
||||||
if (commodore == null) {
|
if (commodore == null) {
|
||||||
commodore = CommodoreProvider.getCommodore(MainPlugin.Instance) //Register all to the Core, it's easier
|
commodore = CommodoreProvider.getCommodore(MainPlugin.instance) //Register all to the Core, it's easier
|
||||||
commodore.register(LiteralArgumentBuilder.literal<Any?>("un").redirect(RequiredArgumentBuilder.argument<Any?, String>("unsomething",
|
commodore.register(LiteralArgumentBuilder.literal<Any?>("un")
|
||||||
StringArgumentType.word()).suggests { context: CommandContext<Any?>?, builder: SuggestionsBuilder -> builder.suggest("untest").buildFuture() }.build()))
|
.redirect(RequiredArgumentBuilder.argument<Any?, String>(
|
||||||
|
"unsomething",
|
||||||
|
StringArgumentType.word()
|
||||||
|
).suggests { context: CommandContext<Any?>?, builder: SuggestionsBuilder ->
|
||||||
|
builder.suggest("untest").buildFuture()
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
commodore!!.dispatcher.root.getChild(commandNode.name) // TODO: Probably unnecessary
|
commodore!!.dispatcher.root.getChild(commandNode.name) // TODO: Probably unnecessary
|
||||||
val customTCmethods = Arrays.stream(command2MC.javaClass.declaredMethods) //val doesn't recognize the type arguments
|
val customTCmethods = Arrays.stream(command2MC.javaClass.declaredMethods) //val doesn't recognize the type arguments
|
||||||
|
|
|
@ -32,18 +32,18 @@ class CommandArgumentHelpManager<TC : ICommand2<TP>, TP : Command2Sender>(comman
|
||||||
TBMCCoreAPI.SendException(
|
TBMCCoreAPI.SendException(
|
||||||
"Error while getting command data!",
|
"Error while getting command data!",
|
||||||
Exception("Resource not found!"),
|
Exception("Resource not found!"),
|
||||||
MainPlugin.Instance
|
MainPlugin.instance
|
||||||
)
|
)
|
||||||
return@use
|
return@use
|
||||||
}
|
}
|
||||||
val config = YamlConfiguration.loadConfiguration(InputStreamReader(str))
|
val config = YamlConfiguration.loadConfiguration(InputStreamReader(str))
|
||||||
commandConfig = config.getConfigurationSection(commandClass.canonicalName.replace('$', '.'))
|
commandConfig = config.getConfigurationSection(commandClass.canonicalName.replace('$', '.'))
|
||||||
if (commandConfig == null) {
|
if (commandConfig == null) {
|
||||||
MainPlugin.Instance.logger.warning("Failed to get command data for $commandClass! Make sure to use 'clean install' when building the project.")
|
MainPlugin.instance.logger.warning("Failed to get command data for $commandClass! Make sure to use 'clean install' when building the project.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
TBMCCoreAPI.SendException("Error while getting command data!", e, MainPlugin.Instance)
|
TBMCCoreAPI.SendException("Error while getting command data!", e, MainPlugin.instance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ class CommandArgumentHelpManager<TC : ICommand2<TP>, TP : Command2Sender>(comman
|
||||||
fun getParameterHelpForMethod(method: Method): String? {
|
fun getParameterHelpForMethod(method: Method): String? {
|
||||||
val cs = commandConfig?.getConfigurationSection(method.name)
|
val cs = commandConfig?.getConfigurationSection(method.name)
|
||||||
if (cs == null) {
|
if (cs == null) {
|
||||||
MainPlugin.Instance.logger.warning("Failed to get command data for $method! Make sure to use 'clean install' when building the project.")
|
MainPlugin.instance.logger.warning("Failed to get command data for $method! Make sure to use 'clean install' when building the project.")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
val mname = cs.getString("method")
|
val mname = cs.getString("method")
|
||||||
|
@ -68,7 +68,7 @@ class CommandArgumentHelpManager<TC : ICommand2<TP>, TP : Command2Sender>(comman
|
||||||
} else TBMCCoreAPI.SendException(
|
} else TBMCCoreAPI.SendException(
|
||||||
"Error while getting command data for $method!",
|
"Error while getting command data for $method!",
|
||||||
Exception("Method '$method' != $mname or params is $params"),
|
Exception("Method '$method' != $mname or params is $params"),
|
||||||
MainPlugin.Instance
|
MainPlugin.instance
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,82 +1,225 @@
|
||||||
package buttondevteam.lib.player;
|
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.core.component.channel.Channel.Companion.getChannels
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import buttondevteam.lib.TBMCCoreAPI
|
||||||
import buttondevteam.lib.architecture.IHaveConfig;
|
import buttondevteam.lib.architecture.ConfigData
|
||||||
import lombok.Getter;
|
import buttondevteam.lib.architecture.ConfigData.Companion.saveNow
|
||||||
import lombok.val;
|
import buttondevteam.lib.architecture.IHaveConfig
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration
|
||||||
|
import java.io.File
|
||||||
import javax.annotation.Nullable;
|
import java.util.*
|
||||||
import java.io.File;
|
import java.util.function.Consumer
|
||||||
import java.util.ArrayList;
|
import java.util.function.Function
|
||||||
import java.util.HashMap;
|
import java.util.function.Supplier
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
@ChromaGamerEnforcer
|
@ChromaGamerEnforcer
|
||||||
public abstract class ChromaGamerBase {
|
abstract class ChromaGamerBase {
|
||||||
private static final String TBMC_PLAYERS_DIR = "TBMC/players/";
|
lateinit var config: IHaveConfig
|
||||||
private static final ArrayList<Function<CommandSender, ? extends Optional<? extends ChromaGamerBase>>> senderConverters = new ArrayList<>();
|
|
||||||
|
@JvmField
|
||||||
|
protected var commonUserData: CommonUserData<*>? = null
|
||||||
|
protected open fun init() {
|
||||||
|
config.reset(commonUserData!!.playerData)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun updateUserConfig() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the player. It'll handle all exceptions that may happen. Called automatically.
|
||||||
|
*/
|
||||||
|
protected open fun save() {
|
||||||
|
try {
|
||||||
|
if (commonUserData!!.playerData.getKeys(false).size > 0) commonUserData!!.playerData.save(
|
||||||
|
File(
|
||||||
|
TBMC_PLAYERS_DIR + folder, fileName + ".yml"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
TBMCCoreAPI.SendException(
|
||||||
|
"Error while saving player to " + folder + "/" + fileName + ".yml!",
|
||||||
|
e,
|
||||||
|
MainPlugin.instance
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the user from the cache. This will be called automatically after some time by default.
|
||||||
|
*/
|
||||||
|
fun uncache() {
|
||||||
|
val userCache: HashMap<Class<out Any?>, out ChromaGamerBase> = commonUserData!!.userCache
|
||||||
|
synchronized(userCache) { if (userCache.containsKey(javaClass)) check(userCache.remove(javaClass) === this) { "A different player instance was cached!" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun scheduleUncache() {
|
||||||
|
Bukkit.getScheduler().runTaskLaterAsynchronously(
|
||||||
|
MainPlugin.instance,
|
||||||
|
Runnable { uncache() },
|
||||||
|
(2 * 60 * 60 * 20).toLong()
|
||||||
|
) //2 hours
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect two accounts. Do not use for connecting two Minecraft accounts or similar. Also make sure you have the "id" tag set.
|
||||||
|
*
|
||||||
|
* @param user The account to connect with
|
||||||
|
*/
|
||||||
|
fun <T : ChromaGamerBase?> connectWith(user: T) {
|
||||||
|
// Set the ID, go through all linked files and connect them as well
|
||||||
|
val ownFolder = folder
|
||||||
|
val userFolder = user!!.folder
|
||||||
|
if (ownFolder.equals(
|
||||||
|
userFolder,
|
||||||
|
ignoreCase = true
|
||||||
|
)
|
||||||
|
) throw RuntimeException("Do not connect two accounts of the same type! Type: $ownFolder")
|
||||||
|
val ownData = commonUserData!!.playerData
|
||||||
|
val userData = user.commonUserData!!.playerData
|
||||||
|
userData[ownFolder + "_id"] = ownData.getString(ownFolder + "_id")
|
||||||
|
ownData[userFolder + "_id"] = userData.getString(userFolder + "_id")
|
||||||
|
config.signalChange()
|
||||||
|
user.config.signalChange()
|
||||||
|
val sync = Consumer { sourcedata: YamlConfiguration ->
|
||||||
|
val sourcefolder = if (sourcedata === ownData) ownFolder else userFolder
|
||||||
|
val id = sourcedata.getString(sourcefolder + "_id")!!
|
||||||
|
for ((key, value) in staticDataMap) { // Set our ID in all files we can find, both from our connections and the new ones
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cg.config.signalChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sync.accept(ownData)
|
||||||
|
sync.accept(userData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ID for the T typed player object connected with this one or null if no connection found.
|
||||||
|
*
|
||||||
|
* @param cl The player class to get the ID from
|
||||||
|
* @return The ID or null if not found
|
||||||
|
*/
|
||||||
|
fun <T : ChromaGamerBase?> getConnectedID(cl: Class<T>): String {
|
||||||
|
return commonUserData!!.playerData.getString(getFolderForType(cl) + "_id")!!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a player instance of the given type that represents the same player. This will return a new instance unless the player is cached.<br></br>
|
||||||
|
* If the class is a subclass of the current class then the same ID is used, otherwise, a connected ID is used, if found.
|
||||||
|
*
|
||||||
|
* @param cl The target player class
|
||||||
|
* @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? {
|
||||||
|
if (cl.simpleName == javaClass.simpleName) return this as T
|
||||||
|
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
|
||||||
|
return getUser(fileName, cl)
|
||||||
|
val playerData = commonUserData!!.playerData
|
||||||
|
return if (!playerData.contains(newfolder + "_id")) null else getUser(
|
||||||
|
playerData.getString(newfolder + "_id")!!,
|
||||||
|
cl
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val fileName: String
|
||||||
|
/**
|
||||||
|
* 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**
|
||||||
|
*/
|
||||||
|
get() = commonUserData!!.playerData.getString(folder + "_id")!!
|
||||||
|
val folder: String
|
||||||
|
/**
|
||||||
|
* This method returns the folder that this player data is stored in. For example: "minecraft".
|
||||||
|
*/
|
||||||
|
get() = getFolderForType(javaClass)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get player information. This method calls the [TBMCPlayerGetInfoEvent] to get all the player information across the TBMC plugins.
|
||||||
|
*
|
||||||
|
* @param target The [InfoTarget] to return the info for.
|
||||||
|
* @return The player information.
|
||||||
|
*/
|
||||||
|
fun getInfo(target: InfoTarget?): String {
|
||||||
|
val event = TBMCPlayerGetInfoEvent(this, target)
|
||||||
|
Bukkit.getServer().pluginManager.callEvent(event)
|
||||||
|
return event.result
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class InfoTarget {
|
||||||
|
MCHover, MCCommand, Discord
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------
|
||||||
|
@JvmField
|
||||||
|
val channel: ConfigData<Channel> = config.getData("channel", Channel.GlobalChat,
|
||||||
|
{ id ->
|
||||||
|
getChannels().filter { ch: Channel -> ch.identifier.equals(id as String, ignoreCase = true) }
|
||||||
|
.findAny().orElse(null)
|
||||||
|
}) { ch -> ch.ID }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TBMC_PLAYERS_DIR = "TBMC/players/"
|
||||||
|
private val senderConverters = ArrayList<Function<CommandSender, out Optional<out ChromaGamerBase>>>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds data per user class
|
* Holds data per user class
|
||||||
*/
|
*/
|
||||||
private static final HashMap<Class<? extends ChromaGamerBase>, StaticUserData<?>> staticDataMap = new HashMap<>();
|
private val staticDataMap = HashMap<Class<out ChromaGamerBase>, StaticUserData<*>>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use {@link #getConfig()} where possible; the 'id' must be always set
|
* Used for connecting with every type of user ([.connectWith]) and to init the configs.
|
||||||
*/
|
|
||||||
//protected YamlConfiguration plugindata;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
protected final IHaveConfig config = new IHaveConfig(this::save);
|
|
||||||
protected CommonUserData<?> commonUserData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for connecting with every type of user ({@link #connectWith(ChromaGamerBase)}) 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.
|
||||||
*/
|
*/
|
||||||
public static <T extends ChromaGamerBase> void RegisterPluginUserClass(Class<T> userclass, Supplier<T> constructor) {
|
@JvmStatic
|
||||||
Class<? extends T> cl;
|
fun <T : ChromaGamerBase?> RegisterPluginUserClass(userclass: Class<T>, constructor: Supplier<T>?) {
|
||||||
String folderName;
|
val cl: Class<out T>
|
||||||
if (userclass.isAnnotationPresent(UserClass.class)) {
|
val folderName: String
|
||||||
cl = userclass;
|
if (userclass.isAnnotationPresent(UserClass::class.java)) {
|
||||||
folderName = userclass.getAnnotation(UserClass.class).foldername();
|
cl = userclass
|
||||||
} else if (userclass.isAnnotationPresent(AbstractUserClass.class)) {
|
folderName = userclass.getAnnotation(UserClass::class.java).foldername
|
||||||
var ucl = userclass.getAnnotation(AbstractUserClass.class).prototype();
|
} else if (userclass.isAnnotationPresent(AbstractUserClass::class.java)) {
|
||||||
if (!userclass.isAssignableFrom(ucl))
|
val ucl: Class<out ChromaGamerBase> = userclass.getAnnotation(
|
||||||
throw new RuntimeException("The prototype class (" + ucl.getSimpleName() + ") must be a subclass of the userclass parameter (" + userclass.getSimpleName() + ")!");
|
AbstractUserClass::class.java
|
||||||
//noinspection unchecked
|
).prototype
|
||||||
cl = (Class<? extends T>) ucl;
|
if (!userclass.isAssignableFrom(ucl)) throw RuntimeException("The prototype class (" + ucl.simpleName + ") must be a subclass of the userclass parameter (" + userclass.simpleName + ")!")
|
||||||
folderName = userclass.getAnnotation(AbstractUserClass.class).foldername();
|
cl = ucl as Class<out T>
|
||||||
} else // <-- Really important
|
folderName = userclass.getAnnotation(AbstractUserClass::class.java).foldername
|
||||||
throw new 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")
|
||||||
var sud = new StaticUserData<T>(folderName);
|
val sud = StaticUserData<T>(folderName)
|
||||||
sud.getConstructors().put(cl, constructor);
|
sud.constructors[cl] = constructor
|
||||||
sud.getConstructors().put(userclass, constructor); // Alawys register abstract and prototype class (TBMCPlayerBase and TBMCPlayer)
|
sud.constructors[userclass] =
|
||||||
staticDataMap.put(userclass, sud);
|
constructor // Alawys register abstract and prototype class (TBMCPlayerBase and TBMCPlayer)
|
||||||
|
staticDataMap[userclass] = sud
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the folder name for the given player class.
|
* Returns the folder name for the given player class.
|
||||||
*
|
*
|
||||||
* @param cl The class to get the folder from (like {@link TBMCPlayerBase} or one of it's subclasses)
|
* @param cl The class to get the folder from (like [TBMCPlayerBase] or one of it's subclasses)
|
||||||
* @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 {@link UserClass} annotation.
|
* @throws RuntimeException If the class doesn't have the [UserClass] annotation.
|
||||||
*/
|
*/
|
||||||
public static <T extends ChromaGamerBase> String getFolderForType(Class<T> cl) {
|
fun <T : ChromaGamerBase?> getFolderForType(cl: Class<T>): String {
|
||||||
if (cl.isAnnotationPresent(UserClass.class))
|
if (cl.isAnnotationPresent(UserClass::class.java)) return cl.getAnnotation(UserClass::class.java).foldername else if (cl.isAnnotationPresent(
|
||||||
return cl.getAnnotation(UserClass.class).foldername();
|
AbstractUserClass::class.java
|
||||||
else if (cl.isAnnotationPresent(AbstractUserClass.class))
|
)
|
||||||
return cl.getAnnotation(AbstractUserClass.class).foldername();
|
) return cl.getAnnotation(AbstractUserClass::class.java).foldername
|
||||||
throw new 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,10 +228,16 @@ public abstract class ChromaGamerBase {
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
public static Class<? extends ChromaGamerBase> getTypeForFolder(String foldername) {
|
fun getTypeForFolder(foldername: String?): Class<out ChromaGamerBase> {
|
||||||
synchronized (staticDataMap) {
|
synchronized(staticDataMap) {
|
||||||
return staticDataMap.entrySet().stream().filter(e -> e.getValue().getFolder().equalsIgnoreCase(foldername))
|
return staticDataMap.entries.stream()
|
||||||
.map(Map.Entry::getKey).findAny().orElse(null);
|
.filter { (_, value): Map.Entry<Class<out ChromaGamerBase>, StaticUserData<*>> ->
|
||||||
|
value.folder.equals(
|
||||||
|
foldername,
|
||||||
|
ignoreCase = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.map { (key, value) -> java.util.Map.Entry.key }.findAny().orElse(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,43 +248,51 @@ public abstract class ChromaGamerBase {
|
||||||
* @param cl User class
|
* @param cl User class
|
||||||
* @return The user object
|
* @return The user object
|
||||||
*/
|
*/
|
||||||
public static synchronized <T extends ChromaGamerBase> T getUser(String fname, Class<T> cl) {
|
@JvmStatic
|
||||||
StaticUserData<?> staticUserData = null;
|
@Synchronized
|
||||||
for (var sud : staticDataMap.entrySet()) {
|
fun <T : ChromaGamerBase> getUser(fname: String, cl: Class<T>): T {
|
||||||
if (sud.getKey().isAssignableFrom(cl)) {
|
val staticUserData: StaticUserData<*> = staticDataMap.entries
|
||||||
staticUserData = sud.getValue();
|
.filter { (key, _) -> key.isAssignableFrom(cl) }
|
||||||
break;
|
.map { (_, value) -> value }
|
||||||
}
|
.firstOrNull()
|
||||||
}
|
?: throw RuntimeException("User class not registered! Use @UserClass or @AbstractUserClass")
|
||||||
if (staticUserData == null)
|
|
||||||
throw new RuntimeException("User class not registered! Use @UserClass or @AbstractUserClass");
|
@Suppress("UNCHECKED_CAST")
|
||||||
var commonUserData = staticUserData.getUserDataMap().get(fname);
|
val commonUserData: CommonUserData<T> = (staticUserData.userDataMap[fname]
|
||||||
if (commonUserData == null) {
|
?: run {
|
||||||
final String folder = staticUserData.getFolder();
|
val folder = staticUserData.folder
|
||||||
final File file = new File(TBMC_PLAYERS_DIR + folder, fname + ".yml");
|
val file = File(TBMC_PLAYERS_DIR + folder, "$fname.yml")
|
||||||
file.getParentFile().mkdirs();
|
file.parentFile.mkdirs()
|
||||||
var playerData = YamlConfiguration.loadConfiguration(file);
|
val playerData = YamlConfiguration.loadConfiguration(file)
|
||||||
commonUserData = new CommonUserData<>(playerData);
|
playerData[staticUserData.folder + "_id"] = fname
|
||||||
playerData.set(staticUserData.getFolder() + "_id", fname);
|
CommonUserData<T>(playerData)
|
||||||
staticUserData.getUserDataMap().put(fname, commonUserData);
|
}.also { staticUserData.userDataMap[fname] = it }) as CommonUserData<T>
|
||||||
}
|
|
||||||
if (commonUserData.getUserCache().containsKey(cl))
|
return if (commonUserData.userCache.containsKey(cl)) commonUserData.userCache[cl] as T
|
||||||
return (T) commonUserData.getUserCache().get(cl);
|
|
||||||
T obj;
|
|
||||||
if (staticUserData.getConstructors().containsKey(cl))
|
|
||||||
//noinspection unchecked
|
|
||||||
obj = (T) staticUserData.getConstructors().get(cl).get();
|
|
||||||
else {
|
else {
|
||||||
|
val obj = createNewUser(cl, staticUserData, commonUserData)
|
||||||
|
commonUserData.userCache[cl] = obj
|
||||||
|
obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T : ChromaGamerBase> createNewUser(
|
||||||
|
cl: Class<T>,
|
||||||
|
staticUserData: StaticUserData<*>,
|
||||||
|
commonUserData: CommonUserData<*>
|
||||||
|
): T {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val obj = staticUserData.constructors[cl]?.get() as T? ?: run {
|
||||||
try {
|
try {
|
||||||
obj = cl.getConstructor().newInstance();
|
cl.getConstructor().newInstance()
|
||||||
} catch (Exception e) {
|
} catch (e: Exception) {
|
||||||
throw new RuntimeException("Failed to create new instance of user of type " + cl.getSimpleName() + "!", e);
|
throw RuntimeException("Failed to create new instance of user of type ${cl.simpleName}!", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
obj.commonUserData = commonUserData;
|
obj.commonUserData = commonUserData
|
||||||
obj.init();
|
obj.init()
|
||||||
obj.scheduleUncache();
|
obj.scheduleUncache()
|
||||||
return obj;
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,8 +300,8 @@ public 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.
|
||||||
*/
|
*/
|
||||||
public static <T extends ChromaGamerBase> void addConverter(Function<CommandSender, Optional<T>> converter) {
|
fun <T : ChromaGamerBase?> addConverter(converter: Function<CommandSender, Optional<T>>) {
|
||||||
senderConverters.add(0, converter);
|
senderConverters.add(0, converter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -153,165 +310,18 @@ public abstract class ChromaGamerBase {
|
||||||
* @param sender The sender to use
|
* @param sender The sender to use
|
||||||
* @return A user as returned by a converter or null if none can supply it
|
* @return A user as returned by a converter or null if none can supply it
|
||||||
*/
|
*/
|
||||||
public static ChromaGamerBase getFromSender(CommandSender sender) { // TODO: Use Command2Sender
|
fun getFromSender(sender: CommandSender): ChromaGamerBase? { // TODO: Use Command2Sender
|
||||||
for (val converter : senderConverters) {
|
for (converter in senderConverters) {
|
||||||
val ocg = converter.apply(sender);
|
val ocg = converter.apply(sender)
|
||||||
if (ocg.isPresent())
|
if (ocg.isPresent) return ocg.get()
|
||||||
return ocg.get();
|
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveUsers() {
|
fun saveUsers() {
|
||||||
synchronized (staticDataMap) {
|
synchronized(staticDataMap) {
|
||||||
for (var sud : staticDataMap.values())
|
for (sud in staticDataMap.values) for (cud in sud.userDataMap.values) saveNow(cud.playerData) //Calls save()
|
||||||
for (var cud : sud.getUserDataMap().values())
|
|
||||||
ConfigData.saveNow(cud.getPlayerData()); //Calls save()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void init() {
|
|
||||||
config.reset(commonUserData.getPlayerData());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the player. It'll handle all exceptions that may happen. Called automatically.
|
|
||||||
*/
|
|
||||||
protected void save() {
|
|
||||||
try {
|
|
||||||
if (commonUserData.getPlayerData().getKeys(false).size() > 0)
|
|
||||||
commonUserData.getPlayerData().save(new File(TBMC_PLAYERS_DIR + getFolder(), getFileName() + ".yml"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
TBMCCoreAPI.SendException("Error while saving player to " + getFolder() + "/" + getFileName() + ".yml!", e, MainPlugin.Instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the user from the cache. This will be called automatically after some time by default.
|
|
||||||
*/
|
|
||||||
public void uncache() {
|
|
||||||
final var userCache = commonUserData.getUserCache();
|
|
||||||
//noinspection SynchronizationOnLocalVariableOrMethodParameter
|
|
||||||
synchronized (userCache) {
|
|
||||||
if (userCache.containsKey(getClass()))
|
|
||||||
if (userCache.remove(getClass()) != this)
|
|
||||||
throw new IllegalStateException("A different player instance was cached!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void scheduleUncache() {
|
|
||||||
Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, this::uncache, 2 * 60 * 60 * 20); //2 hours
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect two accounts. Do not use for connecting two Minecraft accounts or similar. Also make sure you have the "id" tag set.
|
|
||||||
*
|
|
||||||
* @param user The account to connect with
|
|
||||||
*/
|
|
||||||
public final <T extends ChromaGamerBase> void connectWith(T user) {
|
|
||||||
// Set the ID, go through all linked files and connect them as well
|
|
||||||
final String ownFolder = getFolder();
|
|
||||||
final String userFolder = user.getFolder();
|
|
||||||
if (ownFolder.equalsIgnoreCase(userFolder))
|
|
||||||
throw new RuntimeException("Do not connect two accounts of the same type! Type: " + ownFolder);
|
|
||||||
var ownData = commonUserData.getPlayerData();
|
|
||||||
var userData = user.commonUserData.getPlayerData();
|
|
||||||
userData.set(ownFolder + "_id", ownData.getString(ownFolder + "_id"));
|
|
||||||
ownData.set(userFolder + "_id", userData.getString(userFolder + "_id"));
|
|
||||||
config.signalChange();
|
|
||||||
user.config.signalChange();
|
|
||||||
Consumer<YamlConfiguration> sync = sourcedata -> {
|
|
||||||
final String sourcefolder = sourcedata == ownData ? ownFolder : userFolder;
|
|
||||||
final String id = sourcedata.getString(sourcefolder + "_id");
|
|
||||||
for (val entry : staticDataMap.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;
|
|
||||||
var entryFolder = entry.getValue().getFolder();
|
|
||||||
final String otherid = sourcedata.getString(entryFolder + "_id");
|
|
||||||
if (otherid == null)
|
|
||||||
continue;
|
|
||||||
ChromaGamerBase cg = getUser(otherid, entry.getKey());
|
|
||||||
var cgData = cg.commonUserData.getPlayerData();
|
|
||||||
cgData.set(sourcefolder + "_id", id); // Set new IDs
|
|
||||||
for (val item : staticDataMap.entrySet()) {
|
|
||||||
var itemFolder = item.getValue().getFolder();
|
|
||||||
if (sourcedata.contains(itemFolder + "_id")) {
|
|
||||||
cgData.set(itemFolder + "_id", sourcedata.getString(itemFolder + "_id")); // Set all existing IDs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cg.config.signalChange();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
sync.accept(ownData);
|
|
||||||
sync.accept(userData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the ID for the T typed player object connected with this one or null if no connection found.
|
|
||||||
*
|
|
||||||
* @param cl The player class to get the ID from
|
|
||||||
* @return The ID or null if not found
|
|
||||||
*/
|
|
||||||
public final <T extends ChromaGamerBase> String getConnectedID(Class<T> cl) {
|
|
||||||
return commonUserData.getPlayerData().getString(getFolderForType(cl) + "_id");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a player instance of the given type that represents the same player. This will return a new instance unless the player is cached.<br>
|
|
||||||
* If the class is a subclass of the current class then the same ID is used, otherwise, a connected ID is used, if found.
|
|
||||||
*
|
|
||||||
* @param cl The target player class
|
|
||||||
* @return The player as a {@link T} object or null if the user doesn't have an account there
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Nullable
|
|
||||||
public final <T extends ChromaGamerBase> T getAs(Class<T> cl) {
|
|
||||||
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!");
|
|
||||||
if (newfolder.equals(getFolder())) // If in the same folder, the same filename is used
|
|
||||||
return getUser(getFileName(), cl);
|
|
||||||
var playerData = commonUserData.getPlayerData();
|
|
||||||
if (!playerData.contains(newfolder + "_id"))
|
|
||||||
return null;
|
|
||||||
return getUser(playerData.getString(newfolder + "_id"), cl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method returns the filename for this player data. For example, for Minecraft-related data, MC UUIDs, for Discord data, Discord IDs, etc.<br>
|
|
||||||
* <b>Does not include .yml</b>
|
|
||||||
*/
|
|
||||||
public final String getFileName() {
|
|
||||||
return commonUserData.getPlayerData().getString(getFolder() + "_id");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method returns the folder that this player data is stored in. For example: "minecraft".
|
|
||||||
*/
|
|
||||||
public final String getFolder() {
|
|
||||||
return getFolderForType(getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
* @return The player information.
|
|
||||||
*/
|
|
||||||
public final String getInfo(InfoTarget target) {
|
|
||||||
TBMCPlayerGetInfoEvent event = new TBMCPlayerGetInfoEvent(this, target);
|
|
||||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
|
||||||
return event.getResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum InfoTarget {
|
|
||||||
MCHover, MCCommand, Discord
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------
|
|
||||||
|
|
||||||
public final ConfigData<Channel> channel = config.getData("channel", Channel.GlobalChat,
|
|
||||||
id -> Channel.getChannels().filter(ch -> ch.identifier.equalsIgnoreCase((String) id)).findAny().orElse(null), ch -> ch.ID);
|
|
||||||
}
|
}
|
|
@ -1,19 +1,13 @@
|
||||||
package buttondevteam.lib.player;
|
package buttondevteam.lib.player
|
||||||
|
|
||||||
import lombok.Getter;
|
import org.bukkit.configuration.file.YamlConfiguration
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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> */
|
||||||
@Getter
|
class CommonUserData<T : ChromaGamerBase>(@JvmField val playerData: YamlConfiguration) {
|
||||||
@RequiredArgsConstructor
|
@JvmField
|
||||||
public class CommonUserData<T extends ChromaGamerBase> {
|
val userCache: HashMap<Class<out T>, out T> = HashMap()
|
||||||
private final HashMap<Class<? extends T>, ? extends T> userCache = new HashMap<>();
|
|
||||||
private final YamlConfiguration playerData;
|
|
||||||
}
|
}
|
|
@ -49,7 +49,7 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase {
|
||||||
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 playerData = commonUserData.getPlayerData();
|
var playerData = commonUserData.playerData;
|
||||||
var section = playerData.getConfigurationSection(pluginname);
|
var section = playerData.getConfigurationSection(pluginname);
|
||||||
if (section == null) section = playerData.createSection(pluginname);
|
if (section == null) section = playerData.createSection(pluginname);
|
||||||
config.reset(section);
|
config.reset(section);
|
||||||
|
@ -76,7 +76,7 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void save() {
|
protected void save() {
|
||||||
Set<String> keys = commonUserData.getPlayerData().getKeys(false);
|
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
|
if (keys.size() > 1) // PlayerName is always saved, but we don't need a file for just that
|
||||||
super.save();
|
super.save();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue