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
|
package buttondevteam.lib
|
||||||
|
|
||||||
import buttondevteam.core.component.channel.Channel
|
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.Cancellable
|
||||||
import org.bukkit.event.Event
|
import org.bukkit.event.Event
|
||||||
import org.bukkit.event.HandlerList
|
import org.bukkit.event.HandlerList
|
||||||
|
@ -12,7 +12,7 @@ import org.bukkit.event.HandlerList
|
||||||
*
|
*
|
||||||
* @author NorbiPeti
|
* @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 {
|
Cancellable {
|
||||||
private var cancelled = false
|
private var cancelled = false
|
||||||
override fun getHandlers(): HandlerList {
|
override fun getHandlers(): HandlerList {
|
||||||
|
|
|
@ -15,18 +15,21 @@ import java.util.function.Function
|
||||||
*
|
*
|
||||||
* **Note:** The instance can become outdated if the config is reloaded.
|
* **Note:** The instance can become outdated if the config is reloaded.
|
||||||
* @param config May be null for testing
|
* @param config May be null for testing
|
||||||
* @param getter The parameter is of a primitive type as returned by [Configuration.get]
|
* @param path The path to the config value
|
||||||
* @param setter The result should be a primitive type or string that can be retrieved correctly later
|
* @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?,
|
val config: IHaveConfig?,
|
||||||
override val path: String,
|
override val path: String,
|
||||||
primitiveDef: Any?,
|
private val primitiveDef: Any,
|
||||||
private val getter: Function<Any?, T>,
|
private val getter: Function<Any, T>,
|
||||||
private val setter: Function<T, Any?>,
|
private val setter: Function<T, Any>,
|
||||||
private val readOnly: Boolean
|
private val readOnly: Boolean
|
||||||
) : IConfigData<T> {
|
) : IConfigData<T> {
|
||||||
private val pdef: Any?
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The config value should not change outside this instance
|
* The config value should not change outside this instance
|
||||||
|
@ -34,8 +37,6 @@ class ConfigData<T> internal constructor(
|
||||||
private var value: T? = null
|
private var value: T? = null
|
||||||
|
|
||||||
init {
|
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
|
get() //Generate config automatically
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,28 +48,25 @@ class ConfigData<T> internal constructor(
|
||||||
val cachedValue = value
|
val cachedValue = value
|
||||||
if (cachedValue != null) return cachedValue //Speed things up
|
if (cachedValue != null) return cachedValue //Speed things up
|
||||||
val config = config?.config
|
val config = config?.config
|
||||||
var `val`: Any?
|
val freshValue = config?.get(path) ?: primitiveDef.also { setInternal(it) }
|
||||||
if (config == null || !config.isSet(path)) {
|
return getter.apply(convertPrimitiveType(freshValue)).also { value = it }
|
||||||
`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 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
if (readOnly) return //Safety for Discord channel/role data
|
||||||
val `val` = value?.let { setter.apply(it) }
|
val `val` = setter.apply(value)
|
||||||
setInternal(`val`)
|
setInternal(`val`)
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
@ -86,6 +84,10 @@ class ConfigData<T> internal constructor(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val saveTasks = HashMap<Configuration, SaveTask>()
|
private val saveTasks = HashMap<Configuration, SaveTask>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals that the config has changed and should be saved
|
||||||
|
*/
|
||||||
fun signalChange(config: IHaveConfig) {
|
fun signalChange(config: IHaveConfig) {
|
||||||
val cc = config.config
|
val cc = config.config
|
||||||
val sa = config.saveAction
|
val sa = config.saveAction
|
||||||
|
@ -99,7 +101,7 @@ class ConfigData<T> internal constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!MainPlugin.isInitialized) {
|
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) {
|
synchronized(saveTasks) {
|
||||||
saveTasks.put(root, SaveTask(null, sa))
|
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
|
@JvmStatic
|
||||||
fun saveNow(config: Configuration): Boolean {
|
fun saveNow(config: Configuration): Boolean {
|
||||||
synchronized(saveTasks) {
|
synchronized(saveTasks) {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package buttondevteam.lib.architecture
|
||||||
|
|
||||||
import buttondevteam.lib.architecture.config.IConfigData
|
import buttondevteam.lib.architecture.config.IConfigData
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
import org.bukkit.configuration.ConfigurationSection
|
||||||
import java.util.*
|
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,7 +29,7 @@ class IHaveConfig(
|
||||||
* @param def The value to use by default
|
* @param def The value to use by default
|
||||||
* @param getter A function that converts a primitive representation to the correct value
|
* @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 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 readOnly If true, changing the value will have no effect
|
||||||
* @param <T> The type of this variable (can be any class)
|
* @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
|
* @return The data object that can be used to get or set the value
|
||||||
</T> */
|
</T> */
|
||||||
|
@ -39,11 +38,12 @@ class IHaveConfig(
|
||||||
fun <T> getData(
|
fun <T> getData(
|
||||||
path: String,
|
path: String,
|
||||||
def: T,
|
def: T,
|
||||||
getter: Function<Any?, T>? = null,
|
getter: Function<Any, T>? = null,
|
||||||
setter: Function<T, Any?>? = null,
|
setter: Function<T, Any>? = null,
|
||||||
readOnly: Boolean = false
|
readOnly: Boolean = false
|
||||||
): ConfigData<T> {
|
): 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
|
@JvmOverloads
|
||||||
fun <T> getData(
|
fun <T> getData(
|
||||||
path: String,
|
path: String,
|
||||||
getter: Function<Any?, T>,
|
getter: Function<Any, T>,
|
||||||
setter: Function<T, Any?>,
|
setter: Function<T, Any>,
|
||||||
primitiveDef: Any?,
|
primitiveDef: Any,
|
||||||
readOnly: Boolean = false
|
readOnly: Boolean = false
|
||||||
): ConfigData<T> {
|
): ConfigData<T> {
|
||||||
val data =
|
val data =
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package buttondevteam.lib.architecture
|
package buttondevteam.lib.architecture
|
||||||
|
|
||||||
import buttondevteam.lib.architecture.config.IConfigData
|
import buttondevteam.lib.architecture.config.IConfigData
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
import java.util.function.UnaryOperator
|
import java.util.function.UnaryOperator
|
||||||
|
@ -23,7 +22,7 @@ class ListConfigData<T> internal constructor(
|
||||||
return listConfig.get()
|
return listConfig.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun set(value: List?) {
|
override fun set(value: List) {
|
||||||
listConfig.set(value)
|
listConfig.set(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
package buttondevteam.lib.architecture.config
|
package buttondevteam.lib.architecture.config
|
||||||
|
|
||||||
interface IConfigData<T> {
|
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
|
val path: String
|
||||||
}
|
}
|
|
@ -8,7 +8,7 @@ class ChatMessage internal constructor(
|
||||||
/**
|
/**
|
||||||
* The sender which sends the message.
|
* The sender which sends the message.
|
||||||
*/
|
*/
|
||||||
val sender: CommandSender,
|
val sender: Command2Sender,
|
||||||
/**
|
/**
|
||||||
* The Chroma user which sends the message.
|
* 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
|
||||||
import buttondevteam.lib.architecture.ConfigData.Companion.saveNow
|
import buttondevteam.lib.architecture.ConfigData.Companion.saveNow
|
||||||
import buttondevteam.lib.architecture.IHaveConfig
|
import buttondevteam.lib.architecture.IHaveConfig
|
||||||
|
import buttondevteam.lib.chat.Command2MCSender
|
||||||
|
import buttondevteam.lib.chat.Command2Sender
|
||||||
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
|
||||||
|
@ -168,7 +170,7 @@ abstract class ChromaGamerBase {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TBMC_PLAYERS_DIR = "TBMC/players/"
|
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
|
* Holds data per user class
|
||||||
|
@ -303,22 +305,41 @@ abstract class ChromaGamerBase {
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun addConverter(converter: Function<CommandSender, Optional<out ChromaGamerBase>>) {
|
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
|
* @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
|
@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) {
|
for (converter in senderConverters) {
|
||||||
val ocg = converter.apply(sender)
|
val ocg = converter.apply(sender)
|
||||||
if (ocg.isPresent) return ocg.get()
|
if (ocg.isPresent) return ocg.get()
|
||||||
}
|
}
|
||||||
return null
|
throw RuntimeException("No converter found for sender type ${sender::class.java.simpleName}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveUsers() {
|
fun saveUsers() {
|
||||||
|
|
Loading…
Reference in a new issue