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
This commit is contained in:
parent
aabc2cd48c
commit
b57420c72a
7 changed files with 92 additions and 55 deletions
|
@ -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 {
|
||||
|
|
|
@ -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<T> internal constructor(
|
||||
class ConfigData<T : Any?> internal constructor(
|
||||
val config: IHaveConfig?,
|
||||
override val path: String,
|
||||
primitiveDef: Any?,
|
||||
private val getter: Function<Any?, T>,
|
||||
private val setter: Function<T, Any?>,
|
||||
private val primitiveDef: Any,
|
||||
private val getter: Function<Any, T>,
|
||||
private val setter: Function<T, Any>,
|
||||
private val readOnly: Boolean
|
||||
) : IConfigData<T> {
|
||||
private val pdef: Any?
|
||||
|
||||
/**
|
||||
* The config value should not change outside this instance
|
||||
|
@ -34,8 +37,6 @@ class ConfigData<T> 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<T> 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<Any?>()
|
||||
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<T> internal constructor(
|
|||
|
||||
companion object {
|
||||
private val saveTasks = HashMap<Configuration, SaveTask>()
|
||||
|
||||
/**
|
||||
* 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<T> 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<T> 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) {
|
||||
|
|
|
@ -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 <T> 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 <T> The type of this variable (can be any class)
|
||||
* @return The data object that can be used to get or set the value
|
||||
</T> */
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
@ -39,11 +38,12 @@ class IHaveConfig(
|
|||
fun <T> getData(
|
||||
path: String,
|
||||
def: T,
|
||||
getter: Function<Any?, T>? = null,
|
||||
setter: Function<T, Any?>? = null,
|
||||
getter: Function<Any, T>? = null,
|
||||
setter: Function<T, Any>? = null,
|
||||
readOnly: Boolean = false
|
||||
): ConfigData<T> {
|
||||
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 <T> getData(
|
||||
path: String,
|
||||
getter: Function<Any?, T>,
|
||||
setter: Function<T, Any?>,
|
||||
primitiveDef: Any?,
|
||||
getter: Function<Any, T>,
|
||||
setter: Function<T, Any>,
|
||||
primitiveDef: Any,
|
||||
readOnly: Boolean = false
|
||||
): ConfigData<T> {
|
||||
val data =
|
||||
|
|
|
@ -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<T> internal constructor(
|
|||
return listConfig.get()
|
||||
}
|
||||
|
||||
override fun set(value: List?) {
|
||||
override fun set(value: List) {
|
||||
listConfig.set(value)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
package buttondevteam.lib.architecture.config
|
||||
|
||||
interface IConfigData<T> {
|
||||
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
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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<Function<CommandSender, out Optional<out ChromaGamerBase>>>()
|
||||
private val senderConverters = ArrayList<Function<Command2Sender, out Optional<out ChromaGamerBase>>>()
|
||||
|
||||
/**
|
||||
* Holds data per user class
|
||||
|
@ -303,22 +305,41 @@ abstract class ChromaGamerBase {
|
|||
*/
|
||||
@JvmStatic
|
||||
fun addConverter(converter: Function<CommandSender, Optional<out ChromaGamerBase>>) {
|
||||
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() {
|
||||
|
|
Loading…
Reference in a new issue