Fix test issues and command issues

- Fixed test config loading (it loaded it twice, probably would've on a regular server too)
- Fixed missing permission plugin when testing
- Fixed command node casting errors
- Fixed event handler lists not being JVM static
This commit is contained in:
Norbi Peti 2023-06-27 01:07:22 +02:00
parent 529fef1c5f
commit 4f0e05892a
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
17 changed files with 105 additions and 55 deletions

View file

@ -8,6 +8,7 @@ import buttondevteam.core.component.randomtp.RandomTPComponent
import buttondevteam.core.component.restart.RestartComponent import buttondevteam.core.component.restart.RestartComponent
import buttondevteam.core.component.spawn.SpawnComponent import buttondevteam.core.component.spawn.SpawnComponent
import buttondevteam.core.component.towny.TownyComponent import buttondevteam.core.component.towny.TownyComponent
import buttondevteam.lib.ChromaUtils
import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.ButtonPlugin import buttondevteam.lib.architecture.ButtonPlugin
import buttondevteam.lib.architecture.Component.Companion.registerComponent import buttondevteam.lib.architecture.Component.Companion.registerComponent
@ -17,6 +18,7 @@ import buttondevteam.lib.chat.TBMCChatAPI
import buttondevteam.lib.player.ChromaGamerBase import buttondevteam.lib.player.ChromaGamerBase
import buttondevteam.lib.player.TBMCPlayer import buttondevteam.lib.player.TBMCPlayer
import buttondevteam.lib.player.TBMCPlayerBase import buttondevteam.lib.player.TBMCPlayerBase
import buttondevteam.lib.test.TestPermissions
import com.earth2me.essentials.Essentials import com.earth2me.essentials.Essentials
import net.milkbowl.vault.economy.Economy import net.milkbowl.vault.economy.Economy
import net.milkbowl.vault.permission.Permission import net.milkbowl.vault.permission.Permission
@ -66,14 +68,16 @@ class MainPlugin : ButtonPlugin {
val externalPlayerPermissionGroup get() = iConfig.getData("externalPlayerPermissionGroup", "default") val externalPlayerPermissionGroup get() = iConfig.getData("externalPlayerPermissionGroup", "default")
constructor() : super() constructor() : super()
constructor(loader: JavaPluginLoader, description: PluginDescriptionFile, dataFolder: File, file: File) : super(loader, description, dataFolder, file) constructor(loader: JavaPluginLoader, description: PluginDescriptionFile, dataFolder: File, file: File, @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") test: java.lang.Boolean) : super(loader, description, dataFolder, file) {
ChromaUtils.isTest = test.booleanValue()
}
public override fun pluginEnable() { public override fun pluginEnable() {
instance = this instance = this
val pdf = description val pdf = description
setupPermissions() setupPermissions(ChromaUtils.isTest)
if (!setupEconomy()) //Though Essentials always provides economy, but we don't require Essentials if (!setupEconomy() && !ChromaUtils.isTest) //Though Essentials always provides economy, but we don't require Essentials
logger.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.")
ConfigData.saveNow(config) // Run pending save tasks ConfigData.saveNow(config) // Run pending save tasks
registerComponent(this, RestartComponent()) registerComponent(this, RestartComponent())
@ -159,8 +163,10 @@ class MainPlugin : ButtonPlugin {
logger.info("Player data saved.") logger.info("Player data saved.")
} }
private fun setupPermissions() { private fun setupPermissions(test: Boolean) {
permission = setupProvider(Permission::class.java) ?: throw NullPointerException("No permission plugin found!") permission = setupProvider(Permission::class.java)
?: if (test) TestPermissions()
else throw NullPointerException("No permission plugin found!")
} }
private fun setupEconomy(): Boolean { private fun setupEconomy(): Boolean {
@ -182,6 +188,7 @@ class MainPlugin : ButtonPlugin {
companion object { companion object {
lateinit var instance: MainPlugin lateinit var instance: MainPlugin
private set
lateinit var permission: Permission lateinit var permission: Permission

View file

@ -1,21 +0,0 @@
package buttondevteam.core
import be.seeseemelk.mockbukkit.MockBukkit
import buttondevteam.core.component.channel.Channel
import buttondevteam.core.component.channel.ChannelComponent
import buttondevteam.lib.ChromaUtils.isTest
import buttondevteam.lib.architecture.Component.Companion.registerComponent
import buttondevteam.lib.chat.Color
import buttondevteam.lib.chat.TBMCChatAPI.registerChatChannel
import org.bukkit.ChatColor
@Deprecated("Use MockBukkit")
object TestPrepare {
@JvmStatic
fun prepareServer() {
isTest = true //Needs to be in a separate class because of the potential lack of Mockito
MockBukkit.mock()
registerComponent(MockBukkit.load(MainPlugin::class.java), ChannelComponent())
registerChatChannel(Channel("${ChatColor.WHITE}g${ChatColor.WHITE}", Color.White, "g", null).also { Channel.globalChat = it })
}
}

View file

@ -10,6 +10,7 @@ class ScheduledServerRestartEvent(val restartTicks: Int, val command: ScheduledR
} }
companion object { companion object {
@JvmStatic
val handlerList = HandlerList() val handlerList = HandlerList()
} }
} }

View file

@ -54,6 +54,7 @@ class TBMCChatEvent(
val isFromCommand get() = chatMessage.isFromCommand val isFromCommand get() = chatMessage.isFromCommand
companion object { companion object {
@JvmStatic
val handlerList = HandlerList() val handlerList = HandlerList()
} }
} }

View file

@ -24,6 +24,7 @@ class TBMCChatPreprocessEvent(val sender: Command2Sender, val channel: Channel,
override fun setCancelled(cancelled: Boolean) = run { this.cancelled = cancelled } override fun setCancelled(cancelled: Boolean) = run { this.cancelled = cancelled }
companion object { companion object {
@JvmStatic
val handlerList = HandlerList() val handlerList = HandlerList()
} }
} }

View file

@ -28,6 +28,7 @@ class TBMCCommandPreprocessEvent(
override fun setCancelled(cancelled: Boolean) = run { this.cancelled = cancelled } override fun setCancelled(cancelled: Boolean) = run { this.cancelled = cancelled }
companion object { companion object {
@JvmStatic
val handlerList = HandlerList() val handlerList = HandlerList()
} }
} }

View file

@ -18,6 +18,7 @@ class TBMCExceptionEvent(val sourceMessage: String, val exception: Throwable) :
} }
companion object { companion object {
@JvmStatic
val handlerList = HandlerList() val handlerList = HandlerList()
} }
} }

View file

@ -56,6 +56,7 @@ class TBMCSystemChatEvent(
} }
companion object { companion object {
@JvmStatic
val handlerList = HandlerList() val handlerList = HandlerList()
} }
} }

View file

@ -5,6 +5,7 @@ import buttondevteam.core.ComponentManager
import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.chat.Command2MC import buttondevteam.lib.chat.Command2MC
import buttondevteam.lib.chat.ICommand2MC import buttondevteam.lib.chat.ICommand2MC
import org.bukkit.Bukkit
import org.bukkit.configuration.InvalidConfigurationException import org.bukkit.configuration.InvalidConfigurationException
import org.bukkit.configuration.file.FileConfiguration import org.bukkit.configuration.file.FileConfiguration
import org.bukkit.configuration.file.YamlConfiguration import org.bukkit.configuration.file.YamlConfiguration
@ -50,8 +51,9 @@ abstract class ButtonPlugin : JavaPlugin {
*/ */
protected open fun pluginPreDisable() {} protected open fun pluginPreDisable() {}
override fun onEnable() { override fun onEnable() {
if (!tryReloadConfig()) { if (!isConfigLoaded) {
logger.warning("Please fix the issues and restart the server to load the plugin.") logger.warning("Please fix the issues and restart the server to load the plugin.")
Bukkit.getPluginManager().disablePlugin(this)
return return
} }
try { try {
@ -65,6 +67,7 @@ abstract class ButtonPlugin : JavaPlugin {
override fun onDisable() { override fun onDisable() {
try { try {
if (!isConfigLoaded) return
pluginPreDisable() pluginPreDisable()
ComponentManager.unregComponents(this) ComponentManager.unregComponents(this)
pluginDisable() pluginDisable()

View file

@ -80,7 +80,7 @@ class ConfigData<T : Any?> internal constructor(
if (config != null) { if (config != null) {
config.set(path, `val`) config.set(path, `val`)
signalChange(this.config) signalChange(this.config)
} else { } else if (!ChromaUtils.isTest) {
ChromaUtils.logWarn("Attempted to get/set config value with no config! Path: $path, value: $`val`") ChromaUtils.logWarn("Attempted to get/set config value with no config! Path: $path, value: $`val`")
} }
} }

View file

@ -3,7 +3,7 @@ package buttondevteam.lib.chat
import buttondevteam.core.MainPlugin import buttondevteam.core.MainPlugin
import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.chat.commands.* import buttondevteam.lib.chat.commands.*
import buttondevteam.lib.chat.commands.CommandUtils.core import buttondevteam.lib.chat.commands.CommandUtils.coreCommand
import buttondevteam.lib.chat.commands.CommandUtils.coreExecutable import buttondevteam.lib.chat.commands.CommandUtils.coreExecutable
import com.mojang.brigadier.CommandDispatcher import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.* import com.mojang.brigadier.arguments.*
@ -164,14 +164,14 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
*/ */
private fun getExecutableNode(method: Method, command: TC, ann: Subcommand, remainingPath: String, private fun getExecutableNode(method: Method, command: TC, ann: Subcommand, remainingPath: String,
argHelpManager: CommandArgumentHelpManager<TC, TP>, fullPath: String): LiteralCommandNode<TP> { argHelpManager: CommandArgumentHelpManager<TC, TP>, fullPath: String): LiteralCommandNode<TP> {
val (params, _) = getCommandParametersAndSender(method, argHelpManager) // Param order is important val (params, senderType) = getCommandParametersAndSender(method, argHelpManager) // Param order is important
val paramMap = HashMap<String, CommandArgument>() val paramMap = HashMap<String, CommandArgument>()
for (param in params) { for (param in params) {
paramMap[param.name] = param paramMap[param.name] = param
} }
val helpText = command.getHelpText(method, ann) val helpText = command.getHelpText(method, ann)
val node = CoreCommandBuilder.literal( val node = CoreCommandBuilder.literal(
remainingPath, params[0].type, paramMap, params, command, remainingPath, senderType, paramMap, params, command,
{ helpText }, // TODO: Help text getter support { helpText }, // TODO: Help text getter support
{ sender: TP, data: SubcommandData<TC, TP> -> hasPermission(sender, data) }, { sender: TP, data: SubcommandData<TC, TP> -> hasPermission(sender, data) },
method.annotations.filterNot { it is Subcommand }.toTypedArray(), method.annotations.filterNot { it is Subcommand }.toTypedArray(),
@ -364,9 +364,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
* @return A set of command node objects containing the commands * @return A set of command node objects containing the commands
*/ */
val commandNodes: Set<CoreCommandNode<TP, NoOpSubcommandData>> val commandNodes: Set<CoreCommandNode<TP, NoOpSubcommandData>>
get() = dispatcher.root.children.stream() get() = dispatcher.root.children.mapNotNull { it.coreCommand<TP, NoOpSubcommandData>() }.toSet()
.map { node: CommandNode<TP> -> node.core<TP, NoOpSubcommandData>() }
.collect(Collectors.toUnmodifiableSet())
/** /**
* Get a node that belongs to the given command. * Get a node that belongs to the given command.
@ -375,7 +373,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
* @return A command node * @return A command node
*/ */
fun getCommandNode(command: String): CoreCommandNode<TP, NoOpSubcommandData>? { // TODO: What should this return? No-op? Executable? What's the use case? fun getCommandNode(command: String): CoreCommandNode<TP, NoOpSubcommandData>? { // TODO: What should this return? No-op? Executable? What's the use case?
return dispatcher.root.getChild(command)?.core() return dispatcher.root.getChild(command)?.coreCommand()
} }
/** /**
@ -394,7 +392,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
*/ */
fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, SubcommandData<TC, TP>>>, nested: Boolean) { fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, SubcommandData<TC, TP>>>, nested: Boolean) {
dispatcher.root.children.removeIf { node -> node.coreExecutable<TP, TC>()?.let { condition.test(it) } ?: false } dispatcher.root.children.removeIf { node -> node.coreExecutable<TP, TC>()?.let { condition.test(it) } ?: false }
if (nested) for (child in dispatcher.root.children) unregisterCommandIf(condition, child.core()) if (nested) for (child in dispatcher.root.children) child.coreCommand<_, NoOpSubcommandData>()?.let { unregisterCommandIf(condition, it) }
} }
private fun unregisterCommandIf( private fun unregisterCommandIf(
@ -404,7 +402,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
// TODO: Remvoe no-op nodes without children // TODO: Remvoe no-op nodes without children
// Can't use getCoreChildren() here because the collection needs to be modifiable // Can't use getCoreChildren() here because the collection needs to be modifiable
root.children.removeIf { node -> node.coreExecutable<TP, TC>()?.let { condition.test(it) } ?: false } root.children.removeIf { node -> node.coreExecutable<TP, TC>()?.let { condition.test(it) } ?: false }
for (child in root.children) unregisterCommandIf(condition, child.core()) for (child in root.children) child.coreCommand<_, NoOpSubcommandData>()?.let { unregisterCommandIf(condition, it) }
} }
/** /**
@ -417,7 +415,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
mainCommand: LiteralCommandNode<TP>, mainCommand: LiteralCommandNode<TP>,
deep: Boolean = true deep: Boolean = true
): List<CoreExecutableNode<TP, TC>> { ): List<CoreExecutableNode<TP, TC>> {
return getSubcommands(deep, mainCommand.core()) return mainCommand.coreCommand<_, NoOpSubcommandData>()?.let { getSubcommands(deep, it) } ?: emptyList()
} }
private fun getSubcommands( private fun getSubcommands(
@ -425,6 +423,6 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
root: CoreNoOpNode<TP> root: CoreNoOpNode<TP>
): List<CoreExecutableNode<TP, TC>> { ): List<CoreExecutableNode<TP, TC>> {
return root.children.mapNotNull { it.coreExecutable<TP, TC>() } + return root.children.mapNotNull { it.coreExecutable<TP, TC>() } +
if (deep) root.children.flatMap { getSubcommands(deep, it.core()) } else emptyList() if (deep) root.children.flatMap { child -> child.coreCommand<_, NoOpSubcommandData>()?.let { getSubcommands(deep, it) } ?: emptyList() } else emptyList()
} }
} }

View file

@ -1,6 +1,7 @@
package buttondevteam.lib.chat package buttondevteam.lib.chat
import buttondevteam.core.MainPlugin import buttondevteam.core.MainPlugin
import buttondevteam.lib.ChromaUtils
import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.ButtonPlugin import buttondevteam.lib.architecture.ButtonPlugin
import buttondevteam.lib.architecture.Component import buttondevteam.lib.architecture.Component
@ -238,8 +239,12 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
fun registerTabcomplete(command2MC: ICommand2MC, commandNode: CoreCommandNode<Command2MCSender, *>, bukkitCommand: Command) { fun registerTabcomplete(command2MC: ICommand2MC, commandNode: CoreCommandNode<Command2MCSender, *>, bukkitCommand: Command) {
if (!CommodoreProvider.isSupported()) { if (!CommodoreProvider.isSupported()) {
if (ChromaUtils.isTest) {
return
} else {
throw UnsupportedOperationException("Commodore is not supported! Please use 1.14 or higher. Server version: ${Bukkit.getVersion()}") throw UnsupportedOperationException("Commodore is not supported! Please use 1.14 or higher. Server version: ${Bukkit.getVersion()}")
} }
}
// TODO: Allow extending annotation processing for methods and parameters // TODO: Allow extending annotation processing for methods and parameters
val customTabCompleteMethods = command2MC.javaClass.declaredMethods val customTabCompleteMethods = command2MC.javaClass.declaredMethods
.flatMap { method -> .flatMap { method ->
@ -316,5 +321,3 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
} }
} }
} }
private typealias CNode = CommandNode<Command2MCSender>

View file

@ -36,6 +36,13 @@ class CoreArgumentBuilder<S : Command2Sender, T>(
) )
} }
override fun then(argument: ArgumentBuilder<S, *>?): CoreArgumentBuilder<S, T> {
if (argument is CoreArgumentBuilder<*, *>) {
(argument as CoreArgumentBuilder<S, *>).data = data
}
return super.then(argument)
}
companion object { companion object {
fun <S : Command2Sender, T> argument(name: String, type: ArgumentType<T>, optional: Boolean): CoreArgumentBuilder<S, T> { fun <S : Command2Sender, T> argument(name: String, type: ArgumentType<T>, optional: Boolean): CoreArgumentBuilder<S, T> {
return CoreArgumentBuilder(name, type, optional) return CoreArgumentBuilder(name, type, optional)

View file

@ -12,8 +12,8 @@ class CoreCommandNode<T : Command2Sender, TSD : NoOpSubcommandData>(
literal: String, literal: String,
command: Command<T>, command: Command<T>,
requirement: Predicate<T>, requirement: Predicate<T>,
redirect: CommandNode<T>, redirect: CommandNode<T>?,
modifier: RedirectModifier<T>, modifier: RedirectModifier<T>?,
forks: Boolean, forks: Boolean,
val data: TSD val data: TSD
) : LiteralCommandNode<T>(literal, command, requirement, redirect, modifier, forks) ) : LiteralCommandNode<T>(literal, command, requirement, redirect, modifier, forks)

View file

@ -1,6 +1,7 @@
package buttondevteam.lib.chat.commands package buttondevteam.lib.chat.commands
import buttondevteam.core.MainPlugin import buttondevteam.core.MainPlugin
import buttondevteam.lib.ChromaUtils
import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.chat.Command2Sender import buttondevteam.lib.chat.Command2Sender
import buttondevteam.lib.chat.ICommand2 import buttondevteam.lib.chat.ICommand2
@ -29,11 +30,9 @@ class CommandArgumentHelpManager<TC : ICommand2<TP>, TP : Command2Sender>(comman
try { try {
commandClass.getResourceAsStream("/commands.yml").use { str -> commandClass.getResourceAsStream("/commands.yml").use { str ->
if (str == null) { if (str == null) {
TBMCCoreAPI.SendException( if (!ChromaUtils.isTest) {
"Error while getting command data!", ChromaUtils.logWarn("Failed to get command data for $commandClass! No commands.yml file found.")
Exception("Resource not found!"), }
MainPlugin.instance
)
return@use return@use
} }
val config = YamlConfiguration.loadConfiguration(InputStreamReader(str)) val config = YamlConfiguration.loadConfiguration(InputStreamReader(str))
@ -56,7 +55,9 @@ 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) {
if (!ChromaUtils.isTest) {
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
} }

View file

@ -28,19 +28,20 @@ object CommandUtils {
} }
/** /**
* Casts the node to whatever you say. Use responsibly. * Casts the node to whatever you say if it's a command node. Use responsibly. Returns null if an argument node.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <TP : Command2Sender, TSD : NoOpSubcommandData> CommandNode<TP>.core(): CoreCommandNode<TP, TSD> { fun <TP : Command2Sender, TSD : NoOpSubcommandData> CommandNode<TP>.coreCommand(): CoreCommandNode<TP, TSD>? {
return this as CoreCommandNode<TP, TSD> return if (this is CoreCommandNode<*, *>) this as CoreCommandNode<TP, TSD>
else null
} }
/** /**
* Returns the node as an executable core command node or returns null if it's a no-op node. * Returns the node as an executable core command node or returns null if it's a no-op node.
*/ */
fun <TP : Command2Sender, TC : ICommand2<*>> CommandNode<TP>.coreExecutable(): CoreExecutableNode<TP, TC>? { fun <TP : Command2Sender, TC : ICommand2<*>> CommandNode<TP>.coreExecutable(): CoreExecutableNode<TP, TC>? {
val ret = core<TP, NoOpSubcommandData>() val ret = this.coreCommand<TP, NoOpSubcommandData>()
return if (ret.data is SubcommandData<*, *>) ret.core() else null return if (ret?.data is SubcommandData<*, *>) ret.coreCommand() else null
} }
fun <TP : Command2Sender> CommandNode<TP>.coreArgument(): CoreArgumentCommandNode<TP, *>? { fun <TP : Command2Sender> CommandNode<TP>.coreArgument(): CoreArgumentCommandNode<TP, *>? {

View file

@ -0,0 +1,45 @@
package buttondevteam.lib.test
import net.milkbowl.vault.permission.Permission
class TestPermissions : Permission() {
override fun getName(): String = "TestPermissions"
override fun isEnabled(): Boolean = true
override fun hasSuperPermsCompat(): Boolean = false
@Deprecated("Deprecated in Java")
override fun playerHas(world: String?, player: String?, permission: String?): Boolean = true
@Deprecated("Deprecated in Java")
override fun playerAdd(world: String?, player: String?, permission: String?): Boolean = true
@Deprecated("Deprecated in Java")
override fun playerRemove(world: String?, player: String?, permission: String?): Boolean = true
override fun groupHas(world: String?, group: String?, permission: String?): Boolean = true
override fun groupAdd(world: String?, group: String?, permission: String?): Boolean = true
override fun groupRemove(world: String?, group: String?, permission: String?): Boolean = true
@Deprecated("Deprecated in Java")
override fun playerInGroup(world: String?, player: String?, group: String?): Boolean = true
@Deprecated("Deprecated in Java")
override fun playerAddGroup(world: String?, player: String?, group: String?): Boolean = true
@Deprecated("Deprecated in Java")
override fun playerRemoveGroup(world: String?, player: String?, group: String?): Boolean = true
@Deprecated("Deprecated in Java")
override fun getPlayerGroups(world: String?, player: String?): Array<String> = arrayOf()
@Deprecated("Deprecated in Java")
override fun getPrimaryGroup(world: String?, player: String?): String = "default"
override fun getGroups(): Array<String> = arrayOf()
override fun hasGroupSupport(): Boolean = true
}