Implement channel handling for Chroma users
- This allows properly implementing Minecraft chat for Discord users instead of creating fake CommandSenders - Channel access needs to be implemented on all platforms - this could allow automatically managing who has access to a Discord channel for example - In the future custom commands could be supported without having to connect accounts - Also any information can be retrieved about the user when they chat, not just their name
This commit is contained in:
parent
291fc068fd
commit
cd108dc787
9 changed files with 80 additions and 52 deletions
|
@ -57,6 +57,11 @@ class MainPlugin : ButtonPlugin() {
|
||||||
*/
|
*/
|
||||||
val prioritizeCustomCommands = iConfig.getData("prioritizeCustomCommands", false)
|
val prioritizeCustomCommands = iConfig.getData("prioritizeCustomCommands", false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The permission group to use for players who are not in the server.
|
||||||
|
*/ // TODO: Combine the channel access test with command permissions (with a generic implementation)
|
||||||
|
val externalPlayerPermissionGroup get() = iConfig.getData("externalPlayerPermissionGroup", "default")
|
||||||
|
|
||||||
public override fun pluginEnable() {
|
public override fun pluginEnable() {
|
||||||
instance = this
|
instance = this
|
||||||
val pdf = description
|
val pdf = description
|
||||||
|
|
|
@ -67,7 +67,7 @@ class PlayerListener(val plugin: MainPlugin) : Listener {
|
||||||
private fun handlePreprocess(sender: CommandSender, message: String, event: Cancellable) {
|
private fun handlePreprocess(sender: CommandSender, message: String, event: Cancellable) {
|
||||||
if (event.isCancelled) return
|
if (event.isCancelled) return
|
||||||
val cg = ChromaGamerBase.getFromSender(sender)
|
val cg = ChromaGamerBase.getFromSender(sender)
|
||||||
val ev = TBMCCommandPreprocessEvent(cg, cg.channel.get(), message, sender)
|
val ev = TBMCCommandPreprocessEvent(cg, cg.channel.get(), message, cg)
|
||||||
Bukkit.getPluginManager().callEvent(ev)
|
Bukkit.getPluginManager().callEvent(ev)
|
||||||
if (ev.isCancelled) event.isCancelled = true //Cancel the original event
|
if (ev.isCancelled) event.isCancelled = true //Cancel the original event
|
||||||
}
|
}
|
||||||
|
@ -76,13 +76,7 @@ class PlayerListener(val plugin: MainPlugin) : Listener {
|
||||||
fun onTBMCPreprocess(event: TBMCCommandPreprocessEvent) {
|
fun onTBMCPreprocess(event: TBMCCommandPreprocessEvent) {
|
||||||
if (event.isCancelled) return
|
if (event.isCancelled) return
|
||||||
try {
|
try {
|
||||||
val mcuser = event.sender.getAs(TBMCPlayerBase::class.java)
|
val sender = Command2MCSender(event.sender, event.channel, event.permCheck)
|
||||||
if (mcuser == null) { // TODO: The chat should continue to support unconnected accounts.
|
|
||||||
event.sender.sendMessage("You need to have your Minecraft account connected to send commands.")
|
|
||||||
event.isCancelled = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val sender = Command2MCSender(mcuser, event.channel, event.permCheck)
|
|
||||||
event.isCancelled = ButtonPlugin.command2MC.handleCommand(sender, event.message)
|
event.isCancelled = ButtonPlugin.command2MC.handleCommand(sender, event.message)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
TBMCCoreAPI.SendException(
|
TBMCCoreAPI.SendException(
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
package buttondevteam.core.component.channel
|
package buttondevteam.core.component.channel
|
||||||
|
|
||||||
import buttondevteam.core.ComponentManager.get
|
import buttondevteam.core.ComponentManager.get
|
||||||
import buttondevteam.core.MainPlugin
|
|
||||||
import buttondevteam.lib.architecture.ConfigData
|
import buttondevteam.lib.architecture.ConfigData
|
||||||
import buttondevteam.lib.architecture.IHaveConfig
|
import buttondevteam.lib.architecture.IHaveConfig
|
||||||
import buttondevteam.lib.architecture.ListConfigData
|
import buttondevteam.lib.architecture.ListConfigData
|
||||||
import buttondevteam.lib.chat.Color
|
import buttondevteam.lib.chat.Color
|
||||||
import buttondevteam.lib.player.ChromaGamerBase
|
import buttondevteam.lib.player.ChromaGamerBase
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.command.CommandSender
|
|
||||||
import org.bukkit.entity.Player
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
|
@ -45,7 +42,7 @@ open class Channel
|
||||||
* Only those with access can see the messages.
|
* Only those with access can see the messages.
|
||||||
* If null, everyone has access.
|
* If null, everyone has access.
|
||||||
*/
|
*/
|
||||||
private val filterAndErrorMSG: Function<CommandSender, RecipientTestResult>?
|
private val filterAndErrorMSG: Function<ChromaGamerBase, RecipientTestResult>?
|
||||||
) {
|
) {
|
||||||
private val config: IHaveConfig? = null // TODO: Use this
|
private val config: IHaveConfig? = null // TODO: Use this
|
||||||
|
|
||||||
|
@ -188,28 +185,27 @@ open class Channel
|
||||||
* @param permgroup The group that can access the channel or **null** to only allow OPs.
|
* @param permgroup The group that can access the channel or **null** to only allow OPs.
|
||||||
* @return If has access
|
* @return If has access
|
||||||
*/
|
*/
|
||||||
fun inGroupFilter(permgroup: String?): Function<CommandSender, RecipientTestResult> {
|
@JvmStatic
|
||||||
// TODO: This is Minecraft specific. Change all of this so it supports other ways of checking permissions.
|
fun inGroupFilter(permgroup: String?): Function<ChromaGamerBase, RecipientTestResult> {
|
||||||
// TODO: The commands have to be Minecraft specific, but the channels should be generic.
|
return Function { it.checkChannelInGroup(permgroup) }
|
||||||
// TODO: Implement a way to check permissions for other platforms. Maybe specific strings, like "admin" or "mod"?
|
|
||||||
return noScoreResult(
|
|
||||||
{ s ->
|
|
||||||
s.isOp || s is Player && permgroup?.let { pg ->
|
|
||||||
MainPlugin.permission.playerInGroup(s, pg)
|
|
||||||
} ?: false
|
|
||||||
},
|
|
||||||
"You need to be a(n) " + (permgroup ?: "OP") + " to use this channel."
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
fun noScoreResult(
|
fun noScoreResult(
|
||||||
filter: Predicate<CommandSender>,
|
filter: Predicate<ChromaGamerBase>,
|
||||||
errormsg: String?
|
errormsg: String?
|
||||||
): Function<CommandSender, RecipientTestResult> {
|
): Function<ChromaGamerBase, RecipientTestResult> {
|
||||||
return Function { s ->
|
return Function { noScoreResult(filter, errormsg, it) }
|
||||||
if (filter.test(s)) RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE)
|
|
||||||
else RecipientTestResult(errormsg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun noScoreResult(
|
||||||
|
filter: Predicate<ChromaGamerBase>,
|
||||||
|
errormsg: String?,
|
||||||
|
user: ChromaGamerBase
|
||||||
|
): RecipientTestResult {
|
||||||
|
return if (filter.test(user)) RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE)
|
||||||
|
else RecipientTestResult(errormsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var globalChat: Channel
|
lateinit var globalChat: Channel
|
||||||
|
|
|
@ -2,7 +2,6 @@ package buttondevteam.lib
|
||||||
|
|
||||||
import buttondevteam.core.component.channel.Channel
|
import buttondevteam.core.component.channel.Channel
|
||||||
import buttondevteam.lib.player.ChromaGamerBase
|
import buttondevteam.lib.player.ChromaGamerBase
|
||||||
import org.bukkit.command.CommandSender
|
|
||||||
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
|
||||||
|
@ -17,7 +16,7 @@ class TBMCCommandPreprocessEvent(
|
||||||
val sender: ChromaGamerBase,
|
val sender: ChromaGamerBase,
|
||||||
val channel: Channel,
|
val channel: Channel,
|
||||||
val message: String,
|
val message: String,
|
||||||
val permCheck: CommandSender
|
val permCheck: ChromaGamerBase
|
||||||
) : Event(), Cancellable {
|
) : Event(), Cancellable {
|
||||||
private var cancelled = false
|
private var cancelled = false
|
||||||
override fun getHandlers(): HandlerList {
|
override fun getHandlers(): HandlerList {
|
||||||
|
|
|
@ -59,7 +59,22 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasPermission(sender: Command2MCSender, data: SubcommandData<ICommand2MC, Command2MCSender>): Boolean {
|
override fun hasPermission(sender: Command2MCSender, data: SubcommandData<ICommand2MC, Command2MCSender>): Boolean {
|
||||||
if (sender.sender.isConsole) return true //Always allow the console
|
val defWorld = Bukkit.getWorlds().first().name
|
||||||
|
val check = if (sender.permCheck !is TBMCPlayerBase) ({
|
||||||
|
MainPlugin.permission.groupHas(
|
||||||
|
defWorld,
|
||||||
|
MainPlugin.instance.externalPlayerPermissionGroup.get(),
|
||||||
|
it
|
||||||
|
)
|
||||||
|
})
|
||||||
|
else if (sender.permCheck.isConsole) ({ true }) //Always allow the console
|
||||||
|
else ({ perm: String ->
|
||||||
|
MainPlugin.permission.playerHas(
|
||||||
|
sender.permCheck.player?.location?.world?.name ?: defWorld,
|
||||||
|
sender.permCheck.offlinePlayer,
|
||||||
|
perm
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
var p = true
|
var p = true
|
||||||
val cmdperm = "chroma.command.${data.fullPath.replace(' ', '.')}"
|
val cmdperm = "chroma.command.${data.fullPath.replace(' ', '.')}"
|
||||||
|
@ -71,11 +86,7 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
for (perm in perms) {
|
for (perm in perms) {
|
||||||
if (perm != null) {
|
if (perm != null) {
|
||||||
if (p) { //Use OfflinePlayer to avoid fetching player data
|
if (p) { //Use OfflinePlayer to avoid fetching player data
|
||||||
p = MainPlugin.permission.playerHas(
|
p = check(perm)
|
||||||
sender.sender.player?.location?.world?.name,
|
|
||||||
sender.sender.offlinePlayer,
|
|
||||||
perm
|
|
||||||
)
|
|
||||||
} else break //If any of the permissions aren't granted then don't allow
|
} else break //If any of the permissions aren't granted then don't allow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,14 +124,18 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
if (original != null) {
|
if (original != null) {
|
||||||
return original
|
return original
|
||||||
}
|
}
|
||||||
// Check Bukkit sender type - TODO: This is no longer the Bukkit sender type
|
val cg = sender.sender
|
||||||
if (senderType.isAssignableFrom(sender.sender.javaClass))
|
if (senderType.isAssignableFrom(cg.javaClass))
|
||||||
return sender.sender
|
return cg
|
||||||
|
// Check Bukkit sender type
|
||||||
|
if (cg is TBMCPlayerBase) {
|
||||||
|
if (senderType.isAssignableFrom(cg.offlinePlayer.javaClass)) return cg.offlinePlayer
|
||||||
|
if (cg.player?.javaClass?.let { senderType.isAssignableFrom(it) } == true) return cg.player
|
||||||
|
}
|
||||||
//The command expects a user of our system
|
//The command expects a user of our system
|
||||||
if (ChromaGamerBase::class.java.isAssignableFrom(senderType)) {
|
if (ChromaGamerBase::class.java.isAssignableFrom(senderType)) {
|
||||||
val cg = sender.sender
|
@Suppress("UNCHECKED_CAST")
|
||||||
if (cg.javaClass == senderType)
|
return sender.sender.getAs(senderType as Class<out ChromaGamerBase>)
|
||||||
return cg
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -187,7 +202,7 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
val user = ChromaGamerBase.getFromSender(sender) // TODO: Senders should only be used for TBMCPlayerBase classes.
|
val user = ChromaGamerBase.getFromSender(sender) // TODO: Senders should only be used for TBMCPlayerBase classes.
|
||||||
///trim(): remove space if there are no args
|
///trim(): remove space if there are no args
|
||||||
handleCommand(
|
handleCommand(
|
||||||
Command2MCSender(user as TBMCPlayerBase, user.channel.get(), sender),
|
Command2MCSender(user as TBMCPlayerBase, user.channel.get(), user),
|
||||||
("/${command.name} ${args.joinToString(" ")}").trim { it <= ' ' }, false
|
("/${command.name} ${args.joinToString(" ")}").trim { it <= ' ' }, false
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package buttondevteam.lib.chat
|
package buttondevteam.lib.chat
|
||||||
|
|
||||||
import buttondevteam.core.component.channel.Channel
|
import buttondevteam.core.component.channel.Channel
|
||||||
import buttondevteam.lib.player.TBMCPlayerBase
|
import buttondevteam.lib.player.ChromaGamerBase
|
||||||
import org.bukkit.command.CommandSender
|
|
||||||
|
|
||||||
class Command2MCSender(val sender: TBMCPlayerBase, val channel: Channel, val permCheck: CommandSender) : Command2Sender {
|
class Command2MCSender(val sender: ChromaGamerBase, val channel: Channel, val permCheck: ChromaGamerBase) : Command2Sender {
|
||||||
// TODO: Remove this class and only use the user classes.
|
// TODO: Remove this class and only use the user classes.
|
||||||
// TODO: The command context should be stored separately.
|
// TODO: The command context should be stored separately.
|
||||||
override fun sendMessage(message: String) {
|
override fun sendMessage(message: String) {
|
||||||
|
|
|
@ -10,9 +10,9 @@ import buttondevteam.lib.TBMCChatEvent
|
||||||
import buttondevteam.lib.TBMCChatPreprocessEvent
|
import buttondevteam.lib.TBMCChatPreprocessEvent
|
||||||
import buttondevteam.lib.TBMCSystemChatEvent
|
import buttondevteam.lib.TBMCSystemChatEvent
|
||||||
import buttondevteam.lib.TBMCSystemChatEvent.BroadcastTarget
|
import buttondevteam.lib.TBMCSystemChatEvent.BroadcastTarget
|
||||||
|
import buttondevteam.lib.player.ChromaGamerBase
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.ChatColor
|
import org.bukkit.ChatColor
|
||||||
import org.bukkit.command.CommandSender
|
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
object TBMCChatAPI {
|
object TBMCChatAPI {
|
||||||
|
@ -38,7 +38,7 @@ object TBMCChatAPI {
|
||||||
val rtr = getScoreOrSendError(channel, cm.permCheck)
|
val rtr = getScoreOrSendError(channel, cm.permCheck)
|
||||||
val score = rtr.score
|
val score = rtr.score
|
||||||
if (score == Channel.SCORE_SEND_NOPE || rtr.groupID == null) return@Supplier true
|
if (score == Channel.SCORE_SEND_NOPE || rtr.groupID == null) return@Supplier true
|
||||||
val eventPre = TBMCChatPreprocessEvent(cm.sender, channel, cm.message)
|
val eventPre = TBMCChatPreprocessEvent(cm.user, channel, cm.message)
|
||||||
Bukkit.getPluginManager().callEvent(eventPre)
|
Bukkit.getPluginManager().callEvent(eventPre)
|
||||||
if (eventPre.isCancelled) return@Supplier true
|
if (eventPre.isCancelled) return@Supplier true
|
||||||
cm.message = eventPre.message
|
cm.message = eventPre.message
|
||||||
|
@ -74,7 +74,7 @@ object TBMCChatAPI {
|
||||||
return callEventAsync(event)
|
return callEventAsync(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getScoreOrSendError(channel: Channel, sender: CommandSender): RecipientTestResult {
|
private fun getScoreOrSendError(channel: Channel, sender: ChromaGamerBase): RecipientTestResult {
|
||||||
val result = channel.getRTR(sender)
|
val result = channel.getRTR(sender)
|
||||||
if (result.errormessage != null) sender.sendMessage("${ChatColor.RED}" + result.errormessage)
|
if (result.errormessage != null) sender.sendMessage("${ChatColor.RED}" + result.errormessage)
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -167,6 +167,13 @@ abstract class ChromaGamerBase : Command2Sender {
|
||||||
.findAny().orElseThrow { RuntimeException("Channel $id not found!") }
|
.findAny().orElseThrow { RuntimeException("Channel $id not found!") }
|
||||||
}, { ch -> ch.identifier })
|
}, { ch -> ch.identifier })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check channel access by checking if the user is in the given group. If the group is null, check if the user is OP.
|
||||||
|
*
|
||||||
|
* Note that these groups originally come from Minecraft.
|
||||||
|
*/ // TODO: Allow if a connected account has access
|
||||||
|
abstract fun checkChannelInGroup(group: String?): Channel.RecipientTestResult
|
||||||
|
|
||||||
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<CommandSender, out Optional<out ChromaGamerBase>>>()
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package buttondevteam.lib.player
|
package buttondevteam.lib.player
|
||||||
|
|
||||||
|
import buttondevteam.core.MainPlugin
|
||||||
|
import buttondevteam.core.component.channel.Channel
|
||||||
import buttondevteam.lib.architecture.IHaveConfig
|
import buttondevteam.lib.architecture.IHaveConfig
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
|
@ -45,7 +47,6 @@ abstract class TBMCPlayerBase : ChromaGamerBase() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendMessage(message: String) {
|
override fun sendMessage(message: String) {
|
||||||
// TODO: Random senders (Discord) won't receive messages. Including when trying to chat.
|
|
||||||
player?.sendMessage(message)
|
player?.sendMessage(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +58,18 @@ abstract class TBMCPlayerBase : ChromaGamerBase() {
|
||||||
return playerName.get()
|
return playerName.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun checkChannelInGroup(group: String?): Channel.RecipientTestResult {
|
||||||
|
return Channel.noScoreResult(
|
||||||
|
{ s ->
|
||||||
|
s is TBMCPlayerBase && (s.offlinePlayer.isOp || s.player != null && group?.let { pg ->
|
||||||
|
MainPlugin.permission.playerInGroup(s.player, pg)
|
||||||
|
} ?: false)
|
||||||
|
},
|
||||||
|
"You need to be a(n) ${group ?: "OP"} to use this channel.",
|
||||||
|
this
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Get player as a plugin player.
|
* Get player as a plugin player.
|
||||||
|
|
Loading…
Reference in a new issue