From b57420c72a8403dc9f24b457baf6d485d0dbf857 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 14 May 2023 03:03:51 +0200 Subject: [PATCH] Command sender and config improvements - Use Command2Sender for getting Chroma users - Throw exception if a proper converter doesn't exist - Make ConfigData non-nullable altogether, but allow for nullable type + documentation - Converter stuff is older, config stuff is from earlier today - Command2 stuff isn't really worked out yet --- .../lib/TBMCChatPreprocessEvent.kt | 4 +- .../lib/architecture/ConfigData.kt | 65 ++++++++++--------- .../lib/architecture/IHaveConfig.kt | 26 ++++---- .../lib/architecture/ListConfigData.kt | 3 +- .../lib/architecture/config/IConfigData.kt | 14 +++- .../buttondevteam/lib/chat/ChatMessage.kt | 2 +- .../lib/player/ChromaGamerBase.kt | 33 ++++++++-- 7 files changed, 92 insertions(+), 55 deletions(-) diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/TBMCChatPreprocessEvent.kt b/Chroma-Core/src/main/java/buttondevteam/lib/TBMCChatPreprocessEvent.kt index 8367910..fcff4d6 100755 --- a/Chroma-Core/src/main/java/buttondevteam/lib/TBMCChatPreprocessEvent.kt +++ b/Chroma-Core/src/main/java/buttondevteam/lib/TBMCChatPreprocessEvent.kt @@ -1,7 +1,7 @@ package buttondevteam.lib import buttondevteam.core.component.channel.Channel -import org.bukkit.command.CommandSender +import buttondevteam.lib.chat.Command2Sender import org.bukkit.event.Cancellable import org.bukkit.event.Event import org.bukkit.event.HandlerList @@ -12,7 +12,7 @@ import org.bukkit.event.HandlerList * * @author NorbiPeti */ -class TBMCChatPreprocessEvent(val sender: CommandSender, val channel: Channel, var message: String) : Event(true), +class TBMCChatPreprocessEvent(val sender: Command2Sender, val channel: Channel, var message: String) : Event(true), Cancellable { private var cancelled = false override fun getHandlers(): HandlerList { diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ConfigData.kt b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ConfigData.kt index 264edde..a9149d9 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ConfigData.kt +++ b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ConfigData.kt @@ -15,18 +15,21 @@ import java.util.function.Function * * **Note:** The instance can become outdated if the config is reloaded. * @param config May be null for testing - * @param getter The parameter is of a primitive type as returned by [Configuration.get] - * @param setter The result should be a primitive type or string that can be retrieved correctly later + * @param path The path to the config value + * @param primitiveDef The default value, as stored in the config. Non-nullable as it needs to be saved to the config + * @param getter Function to convert primtive types to [T]. The parameter is of a primitive type as returned by [Configuration.get] + * @param setter Function to convert [T] to a primitive type. The result should be a primitive type or string that can be retrieved correctly later + * @param readOnly If true, changing the value will have no effect + * @param T The type of the config value. May be nullable if the getter cannot always return a value */ -class ConfigData internal constructor( +class ConfigData internal constructor( val config: IHaveConfig?, override val path: String, - primitiveDef: Any?, - private val getter: Function, - private val setter: Function, + private val primitiveDef: Any, + private val getter: Function, + private val setter: Function, private val readOnly: Boolean ) : IConfigData { - private val pdef: Any? /** * The config value should not change outside this instance @@ -34,8 +37,6 @@ class ConfigData internal constructor( private var value: T? = null init { - this.pdef = primitiveDef - ?: throw IllegalArgumentException("Either def or primitiveDef must be set. A getter and setter must be present when using primitiveDef.") get() //Generate config automatically } @@ -47,28 +48,25 @@ class ConfigData internal constructor( val cachedValue = value if (cachedValue != null) return cachedValue //Speed things up val config = config?.config - var `val`: Any? - if (config == null || !config.isSet(path)) { - `val` = pdef - setInternal(pdef) // Save default value even if read-only - } else `val` = config.get(path) //config==null: testing - if (`val` == null) //If it's set to null explicitly - `val` = pdef - fun convert(cval: Any?, cpdef: Any?): Any? { - return if (cpdef is Number) //If we expect a number - if (cval is Number) - ChromaUtils.convertNumber(cval, cpdef.javaClass) - else cpdef //If we didn't get a number, return default (which is a number) - else if (cval is List<*> && cpdef != null && cpdef.javaClass.isArray) - cval.toTypedArray() - else cval - } - return getter.apply(convert(`val`, pdef)).also { value = it } + val freshValue = config?.get(path) ?: primitiveDef.also { setInternal(it) } + return getter.apply(convertPrimitiveType(freshValue)).also { value = it } } - override fun set(value: T?) { // TODO: Have a separate method for removing the value from the config and make this non-nullable + /** + * Converts a value to [T] from the representation returned by [Configuration.get]. + */ + private fun convertPrimitiveType(value: Any): Any { + return if (primitiveDef is Number) //If we expect a number + if (value is Number) ChromaUtils.convertNumber(value, primitiveDef.javaClass) + else primitiveDef //If we didn't get a number, return default (which is a number) + else if (value is List<*> && primitiveDef.javaClass.isArray) // If we got a list and we expected an array + value.toTypedArray() + else value + } + + override fun set(value: T) { if (readOnly) return //Safety for Discord channel/role data - val `val` = value?.let { setter.apply(it) } + val `val` = setter.apply(value) setInternal(`val`) this.value = value } @@ -86,6 +84,10 @@ class ConfigData internal constructor( companion object { private val saveTasks = HashMap() + + /** + * Signals that the config has changed and should be saved + */ fun signalChange(config: IHaveConfig) { val cc = config.config val sa = config.saveAction @@ -99,7 +101,7 @@ class ConfigData internal constructor( return } if (!MainPlugin.isInitialized) { - // If the plugin isn't initilized, we can't schedule a task - do it when the plugin is enabled + // If the plugin isn't initialized, we can't schedule a task - do it when the plugin is enabled synchronized(saveTasks) { saveTasks.put(root, SaveTask(null, sa)) } @@ -118,6 +120,11 @@ class ConfigData internal constructor( } } + /** + * Saves the config immediately, if it's scheduled to be saved. Used to save configs when the plugin is disabled or reloaded. + * + * Also performs cleanup of the save task, so it must be called when the ConfigData is invalidated (which is the above two cases). + */ @JvmStatic fun saveNow(config: Configuration): Boolean { synchronized(saveTasks) { diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/IHaveConfig.kt b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/IHaveConfig.kt index 1b16560..75ca3f8 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/IHaveConfig.kt +++ b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/IHaveConfig.kt @@ -2,7 +2,6 @@ package buttondevteam.lib.architecture import buttondevteam.lib.architecture.config.IConfigData import org.bukkit.configuration.ConfigurationSection -import java.util.* import java.util.function.Function /** @@ -26,12 +25,12 @@ class IHaveConfig( * if you're not using primitive types directly. * These primitive types are strings, numbers, characters, booleans and lists of these things. * - * @param path The path in config to use - * @param def The value to use by default - * @param getter A function that converts a primitive representation to the correct value - * @param setter A function that converts a value to a primitive representation - * @param primDef Whether the default value is a primitive value that needs to be converted to the correct type using the getter - * @param The type of this variable (can be any class) + * @param path The path in config to use + * @param def The value to use by default + * @param getter A function that converts a primitive representation to the correct value + * @param setter A function that converts a value to a primitive representation + * @param readOnly If true, changing the value will have no effect + * @param The type of this variable (can be any class) * @return The data object that can be used to get or set the value */ @Suppress("UNCHECKED_CAST") @@ -39,11 +38,12 @@ class IHaveConfig( fun getData( path: String, def: T, - getter: Function? = null, - setter: Function? = null, + getter: Function? = null, + setter: Function? = null, readOnly: Boolean = false ): ConfigData { - return getData(path, getter ?: Function { it as T }, setter ?: Function { it }, def, readOnly) + val safeSetter = setter ?: Function { it ?: throw RuntimeException("No setter specified for nullable config data $path!") } + return getData(path, getter ?: Function { it as T }, safeSetter, safeSetter.apply(def), readOnly) } /** @@ -61,9 +61,9 @@ class IHaveConfig( @JvmOverloads fun getData( path: String, - getter: Function, - setter: Function, - primitiveDef: Any?, + getter: Function, + setter: Function, + primitiveDef: Any, readOnly: Boolean = false ): ConfigData { val data = diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ListConfigData.kt b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ListConfigData.kt index 15970a3..d34b45f 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ListConfigData.kt +++ b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ListConfigData.kt @@ -1,7 +1,6 @@ package buttondevteam.lib.architecture import buttondevteam.lib.architecture.config.IConfigData -import java.util.ArrayList import java.util.function.Function import java.util.function.Predicate import java.util.function.UnaryOperator @@ -23,7 +22,7 @@ class ListConfigData internal constructor( return listConfig.get() } - override fun set(value: List?) { + override fun set(value: List) { listConfig.set(value) } diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/config/IConfigData.kt b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/config/IConfigData.kt index 9b2ba6b..8b606f1 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/config/IConfigData.kt +++ b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/config/IConfigData.kt @@ -1,8 +1,18 @@ package buttondevteam.lib.architecture.config interface IConfigData { - fun get(): T? - fun set(value: T?) + /** + * Gets the value from the config using the getter specified for the config. If the config is not set, the default value is returned. + */ + fun get(): T + /** + * Sets the value in the config using the setter specified for the config. If the config is read-only, this does nothing. + */ + fun set(value: T) + + /** + * The path to the config value. + */ val path: String } \ No newline at end of file diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/chat/ChatMessage.kt b/Chroma-Core/src/main/java/buttondevteam/lib/chat/ChatMessage.kt index 2ca32c8..552fbef 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/chat/ChatMessage.kt +++ b/Chroma-Core/src/main/java/buttondevteam/lib/chat/ChatMessage.kt @@ -8,7 +8,7 @@ class ChatMessage internal constructor( /** * The sender which sends the message. */ - val sender: CommandSender, + val sender: Command2Sender, /** * The Chroma user which sends the message. */ diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/player/ChromaGamerBase.kt b/Chroma-Core/src/main/java/buttondevteam/lib/player/ChromaGamerBase.kt index 1e794e0..3288571 100755 --- a/Chroma-Core/src/main/java/buttondevteam/lib/player/ChromaGamerBase.kt +++ b/Chroma-Core/src/main/java/buttondevteam/lib/player/ChromaGamerBase.kt @@ -7,6 +7,8 @@ import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.architecture.ConfigData import buttondevteam.lib.architecture.ConfigData.Companion.saveNow import buttondevteam.lib.architecture.IHaveConfig +import buttondevteam.lib.chat.Command2MCSender +import buttondevteam.lib.chat.Command2Sender import org.bukkit.Bukkit import org.bukkit.command.CommandSender import org.bukkit.configuration.file.YamlConfiguration @@ -168,7 +170,7 @@ abstract class ChromaGamerBase { companion object { private const val TBMC_PLAYERS_DIR = "TBMC/players/" - private val senderConverters = ArrayList>>() + private val senderConverters = ArrayList>>() /** * Holds data per user class @@ -303,22 +305,41 @@ abstract class ChromaGamerBase { */ @JvmStatic fun addConverter(converter: Function>) { - senderConverters.add(0, converter) + senderConverters.add(0) { sender -> + when (sender) { + is Command2MCSender -> converter.apply(sender.sender) + else -> Optional.empty() + } + } } /** - * Get from the given sender. the object's type will depend on the sender's type. May be null, but shouldn't be. + * Get from the given sender. the object's type will depend on the sender's type. + * Throws an exception if the sender type is not supported. * * @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 */ @JvmStatic - fun getFromSender(sender: CommandSender): ChromaGamerBase? { // TODO: Use Command2Sender + @Deprecated("Use Command2Sender instead", ReplaceWith("getFromSender(Command2MCSender(sender, Channel.globalChat, sender))", "buttondevteam.lib.player.ChromaGamerBase.Companion.getFromSender", "buttondevteam.lib.chat.Command2MCSender", "buttondevteam.core.component.channel.Channel")) + fun getFromSender(sender: CommandSender): ChromaGamerBase { + return getFromSender(Command2MCSender(sender, Channel.globalChat, sender)) + } + + /** + * Get from the given sender. the object's type will depend on the sender's type. + * Throws an exception if the sender type is not supported. + * + * @param sender The sender to use + * @return A user as returned by a converter + */ + @JvmStatic + fun getFromSender(sender: Command2Sender): ChromaGamerBase { for (converter in senderConverters) { val ocg = converter.apply(sender) if (ocg.isPresent) return ocg.get() } - return null + throw RuntimeException("No converter found for sender type ${sender::class.java.simpleName}") } fun saveUsers() {