add linking chat accounts to uuid

implement global timeouts for all commands
improved permission requests
This commit is contained in:
nikky 2018-07-08 02:02:38 +02:00
parent 254c990d76
commit 0057b3037b
34 changed files with 1012 additions and 275 deletions

View File

@ -1,9 +1,11 @@
package matterlink
import matterlink.command.CommandMatterlink
import com.mojang.authlib.GameProfile
import matterlink.command.MatterLinkCommand
import matterlink.command.MatterLinkCommandSender
import matterlink.config.BaseConfig
import matterlink.config.cfg
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.util.text.TextComponentString
import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.fml.common.FMLCommonHandler
@ -14,6 +16,7 @@ import net.minecraftforge.fml.common.event.FMLServerStartingEvent
import net.minecraftforge.fml.common.event.FMLServerStoppingEvent
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
import java.util.*
lateinit var logger: Logger
@ -47,7 +50,7 @@ object MatterLink : IMatterLink() {
@Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) {
logger.debug("Registering server commands")
event.registerServerCommand(CommandMatterlink())
event.registerServerCommand(MatterLinkCommand())
start()
}
@ -61,10 +64,55 @@ object MatterLink : IMatterLink() {
FMLCommonHandler.instance().minecraftServerInstance.playerList.sendChatMsg(TextComponentString(msg))
}
override fun wrappedSendToPlayer(user: String, msg: String) {
val profile = profileByName(user) ?: profileByUUID(user) ?: run {
error("cannot find player by name or uuid $user")
return
}
val player = playerByProfile(profile) ?: run {
error("${profile.name} is not online")
return
}
player.sendMessage(TextComponentString(msg))
}
override fun isOnline(username: String) = FMLCommonHandler.instance().minecraftServerInstance.onlinePlayerNames.contains(username)
private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? = FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id)
private fun profileByUUID(uuid: String): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getProfileByUUID(UUID.fromString(uuid))
} catch (e: IllegalArgumentException) {
warn("cannot find profile by uuid $uuid")
null
}
private fun profileByName(username: String): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getGameProfileForUsername(username)
} catch (e: IllegalArgumentException) {
warn("cannot find profile by username $username")
null
}
override fun nameToUUID(username: String) = profileByName(username)?.id?.toString()
override fun uuidToName(uuid: String?): String? {
return uuid?.let { profileByUUID(it)?.name }
}
override fun log(level: String, formatString: String, vararg data: Any) =
logger.log(Level.toLevel(level, Level.INFO), formatString, *data)
override fun commandSenderFor(user: String, userId: String, server: String, op: Boolean) = MatterLinkCommandSender(user, userId, server, op)
override fun commandSenderFor(
user: String,
userId: String,
server: String,
uuid: String?,
username: String?,
op: Boolean
) = MatterLinkCommandSender(user, userId, server, uuid, username, op)
override val mcVersion: String = MCVERSION
override val modVersion: String = MODVERSION

View File

@ -0,0 +1,42 @@
package matterlink.command
import matterlink.logger
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString
class AuthCommand : CommandBase() {
override fun getName(): String {
return CommandCoreAuth.name
}
override fun getUsage(sender: ICommandSender): String {
return CommandCoreAuth.usage
}
override fun getAliases(): List<String> {
return CommandCoreAuth.aliases
}
override fun getRequiredPermissionLevel(): Int {
return 0
}
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) {
if (args.isEmpty()) {
throw WrongUsageException("Invalid command! Valid uses: ${this.getUsage(sender)}")
}
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()
val reply = CommandCoreAuth.execute(args, sender.name, uuid)
if (reply.isNotEmpty() && sender.sendCommandFeedback()) {
sender.sendMessage(TextComponentString(reply))
}
}
}

View File

@ -3,21 +3,22 @@ package matterlink.command
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString
class CommandMatterlink : CommandBase() {
class MatterLinkCommand : CommandBase() {
override fun getName(): String {
return CommandCore.getName()
return CommandCoreML.name
}
override fun getUsage(sender: ICommandSender): String {
return CommandCore.getUsage()
return CommandCoreML.usage
}
override fun getAliases(): List<String> {
return CommandCore.getAliases()
return CommandCoreML.aliases
}
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) {
@ -25,7 +26,8 @@ class CommandMatterlink : CommandBase() {
throw WrongUsageException("Invalid command! Valid uses: ${this.getUsage(sender)}")
}
val reply = CommandCore.execute(args, sender.name)
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()
val reply = CommandCoreML.execute(args, sender.name, uuid)
if (reply.isNotEmpty() && sender.sendCommandFeedback()) {
sender.sendMessage(TextComponentString(reply))

View File

@ -13,8 +13,13 @@ import net.minecraft.world.World
import net.minecraftforge.fml.common.FMLCommonHandler
import javax.annotation.Nonnull
class MatterLinkCommandSender(user: String, userId: String, server: String, op: Boolean) : IMinecraftCommandSender(user, userId, server, op), ICommandSender {
private var level: Int = 0
class MatterLinkCommandSender(
user: String,
userId: String,
server: String,
uuid: String?,
username: String?,
op: Boolean) : IMinecraftCommandSender(user, userId, server, uuid, username, op), ICommandSender {
override fun execute(cmdString: String): Boolean {
return 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand(
@ -24,7 +29,7 @@ class MatterLinkCommandSender(user: String, userId: String, server: String, op:
}
override fun getDisplayName(): ITextComponent {
return TextComponentString(user)
return TextComponentString(displayName)
}
override fun getName() = accountName

View File

@ -1,9 +1,11 @@
package matterlink
import matterlink.command.CommandMatterlink
import com.mojang.authlib.GameProfile
import matterlink.command.MatterLinkCommand
import matterlink.command.MatterLinkCommandSender
import matterlink.config.BaseConfig
import matterlink.config.cfg
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.util.text.TextComponentString
import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.fml.common.FMLCommonHandler
@ -14,6 +16,7 @@ import net.minecraftforge.fml.common.event.FMLServerStartingEvent
import net.minecraftforge.fml.common.event.FMLServerStoppingEvent
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
import java.util.*
lateinit var logger: Logger
@ -47,7 +50,7 @@ object MatterLink : IMatterLink() {
@Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) {
logger.debug("Registering server commands")
event.registerServerCommand(CommandMatterlink())
event.registerServerCommand(MatterLinkCommand())
start()
}
@ -61,10 +64,54 @@ object MatterLink : IMatterLink() {
FMLCommonHandler.instance().minecraftServerInstance.playerList.sendMessage(TextComponentString(msg))
}
override fun wrappedSendToPlayer(user: String, msg: String) {
val profile = profileByName(user) ?: profileByUUID(user) ?: run {
error("cannot find player by name or uuid $user")
return
}
val player = playerByProfile(profile) ?: run {
error("${profile.name} is not online")
return
}
player.sendMessage(TextComponentString(msg))
}
override fun isOnline(username: String) = FMLCommonHandler.instance().minecraftServerInstance.onlinePlayerNames.contains(username)
private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? = FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id)
private fun profileByUUID(uuid: String): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getProfileByUUID(UUID.fromString(uuid))
} catch (e: IllegalArgumentException) {
warn("cannot find profile by uuid $uuid")
null
}
private fun profileByName(username: String): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getGameProfileForUsername(username)
} catch (e: IllegalArgumentException) {
warn("cannot find profile by username $username")
null
}
override fun nameToUUID(username: String) = profileByName(username)?.id?.toString()
override fun uuidToName(uuid: String?): String? {
return uuid?.let { profileByUUID(it)?.name }
}
override fun log(level: String, formatString: String, vararg data: Any) =
logger.log(Level.toLevel(level, Level.INFO), formatString, *data)
override fun commandSenderFor(user: String, userId: String, server: String, op: Boolean) = MatterLinkCommandSender(user, userId, server, op)
override fun commandSenderFor(
user: String,
userId: String,
server: String,
uuid: String?,
username: String?,
op: Boolean
) = MatterLinkCommandSender(user, userId, server, uuid, username, op)
override val mcVersion: String = MCVERSION
override val modVersion: String = MODVERSION

View File

@ -0,0 +1,42 @@
package matterlink.command
import matterlink.logger
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString
class AuthCommand : CommandBase() {
override fun getName(): String {
return CommandCoreAuth.name
}
override fun getUsage(sender: ICommandSender): String {
return CommandCoreAuth.usage
}
override fun getAliases(): List<String> {
return CommandCoreAuth.aliases
}
override fun getRequiredPermissionLevel(): Int {
return 0
}
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) {
if (args.isEmpty()) {
throw WrongUsageException("Invalid command! Valid uses: ${this.getUsage(sender)}")
}
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()
val reply = CommandCoreAuth.execute(args, sender.name, uuid)
if (reply.isNotEmpty() && sender.sendCommandFeedback()) {
sender.sendMessage(TextComponentString(reply))
}
}
}

View File

@ -3,21 +3,22 @@ package matterlink.command
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString
class CommandMatterlink : CommandBase() {
class MatterLinkCommand : CommandBase() {
override fun getName(): String {
return CommandCore.getName()
return CommandCoreML.name
}
override fun getUsage(sender: ICommandSender): String {
return CommandCore.getUsage()
return CommandCoreML.usage
}
override fun getAliases(): List<String> {
return CommandCore.getAliases()
return CommandCoreML.aliases
}
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) {
@ -25,11 +26,11 @@ class CommandMatterlink : CommandBase() {
throw WrongUsageException("Invalid command! Valid uses: ${this.getUsage(sender)}")
}
val reply = CommandCore.execute(args, sender.name)
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()
val reply = CommandCoreML.execute(args, sender.name, uuid)
if (reply.isNotEmpty() && sender.sendCommandFeedback()) {
sender.sendMessage(TextComponentString(reply))
}
}
}

View File

@ -13,7 +13,13 @@ import net.minecraft.world.World
import net.minecraftforge.fml.common.FMLCommonHandler
import javax.annotation.Nonnull
class MatterLinkCommandSender(user: String, userId: String, server: String, op: Boolean) : IMinecraftCommandSender(user, userId, server, op), ICommandSender {
class MatterLinkCommandSender(
user: String,
userId: String,
server: String,
uuid: String?,
username: String?,
op: Boolean) : IMinecraftCommandSender(user, userId, server, uuid, username, op), ICommandSender {
override fun execute(cmdString: String): Boolean {
return 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand(
@ -23,7 +29,7 @@ class MatterLinkCommandSender(user: String, userId: String, server: String, op:
}
override fun getDisplayName(): ITextComponent {
return TextComponentString(user)
return TextComponentString(displayName)
}
override fun getName() = accountName

View File

@ -1,9 +1,13 @@
package matterlink
import matterlink.command.CommandMatterlink
import com.mojang.authlib.GameProfile
import jline.internal.Log.warn
import matterlink.command.AuthCommand
import matterlink.command.MatterLinkCommand
import matterlink.command.MatterLinkCommandSender
import matterlink.config.BaseConfig
import matterlink.config.cfg
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.util.text.TextComponentString
import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.fml.common.FMLCommonHandler
@ -14,6 +18,7 @@ import net.minecraftforge.fml.common.event.FMLServerStartingEvent
import net.minecraftforge.fml.common.event.FMLServerStoppingEvent
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
import java.util.*
lateinit var logger: Logger
@ -27,6 +32,7 @@ lateinit var logger: Logger
dependencies = DEPENDENCIES
)
object MatterLink : IMatterLink() {
init {
instance = this
}
@ -47,7 +53,8 @@ object MatterLink : IMatterLink() {
@Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) {
log("DEBUG", "Registering server commands")
event.registerServerCommand(CommandMatterlink())
event.registerServerCommand(MatterLinkCommand())
event.registerServerCommand(AuthCommand())
start()
}
@ -61,10 +68,55 @@ object MatterLink : IMatterLink() {
FMLCommonHandler.instance().minecraftServerInstance.playerList.sendMessage(TextComponentString(msg))
}
override fun wrappedSendToPlayer(user: String, msg: String) {
val profile = profileByName(user) ?: profileByUUID(user) ?: run {
error("cannot find player by name or uuid $user")
return
}
val player = playerByProfile(profile) ?: run {
error("${profile.name} is not online")
return
}
player.sendMessage(TextComponentString(msg))
}
override fun isOnline(username: String) = FMLCommonHandler.instance().minecraftServerInstance.onlinePlayerNames.contains(username)
private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP?
= FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id)
private fun profileByUUID(uuid: String): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getProfileByUUID(UUID.fromString(uuid))
} catch (e: IllegalArgumentException) {
warn("cannot find profile by uuid $uuid")
null
}
private fun profileByName(username: String): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getGameProfileForUsername(username)
} catch (e: IllegalArgumentException) {
warn("cannot find profile by username $username")
null
}
override fun nameToUUID(username: String) = profileByName(username)?.id?.toString()
override fun uuidToName(uuid: String?): String? {
return uuid?.let { profileByUUID(it)?.name }
}
override fun log(level: String, formatString: String, vararg data: Any) =
logger.log(Level.toLevel(level, Level.INFO), formatString, *data)
override fun commandSenderFor(user: String, userId: String, server: String, op: Boolean) = MatterLinkCommandSender(user, userId, server, op)
override fun commandSenderFor(
user: String,
userId: String,
server: String,
uuid: String?,
username: String?,
op: Boolean
) = MatterLinkCommandSender(user, userId, server, uuid, username, op)
override val mcVersion: String = MCVERSION
override val modVersion: String = MODVERSION

View File

@ -0,0 +1,42 @@
package matterlink.command
import matterlink.logger
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString
class AuthCommand : CommandBase() {
override fun getName(): String {
return CommandCoreAuth.name
}
override fun getUsage(sender: ICommandSender): String {
return CommandCoreAuth.usage
}
override fun getAliases(): List<String> {
return CommandCoreAuth.aliases
}
override fun getRequiredPermissionLevel(): Int {
return 0
}
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) {
if (args.isEmpty()) {
throw WrongUsageException("Invalid command! Valid uses: ${this.getUsage(sender)}")
}
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()
val reply = CommandCoreAuth.execute(args, sender.name, uuid)
if (reply.isNotEmpty() && sender.sendCommandFeedback()) {
sender.sendMessage(TextComponentString(reply))
}
}
}

View File

@ -3,21 +3,22 @@ package matterlink.command
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString
class CommandMatterlink : CommandBase() {
class MatterLinkCommand : CommandBase() {
override fun getName(): String {
return CommandCore.getName()
return CommandCoreML.name
}
override fun getUsage(sender: ICommandSender): String {
return CommandCore.getUsage()
return CommandCoreML.usage
}
override fun getAliases(): List<String> {
return CommandCore.getAliases()
return CommandCoreML.aliases
}
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) {
@ -25,7 +26,8 @@ class CommandMatterlink : CommandBase() {
throw WrongUsageException("Invalid command! Valid uses: ${this.getUsage(sender)}")
}
val reply = CommandCore.execute(args, sender.name)
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()
val reply = CommandCoreML.execute(args, sender.name, uuid)
if (reply.isNotEmpty() && sender.sendCommandFeedback()) {
sender.sendMessage(TextComponentString(reply))

View File

@ -9,7 +9,14 @@ import net.minecraft.world.World
import net.minecraftforge.fml.common.FMLCommonHandler
import javax.annotation.Nonnull
class MatterLinkCommandSender(user: String, userId: String, server: String, op: Boolean) : IMinecraftCommandSender(user, userId, server, op), ICommandSender {
class MatterLinkCommandSender(
user: String,
userId: String,
server: String,
uuid: String?,
username: String?,
op: Boolean) : IMinecraftCommandSender(user, userId, server, uuid, username, op), ICommandSender {
override fun execute(cmdString: String): Boolean {
return 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand(
this,
@ -18,7 +25,7 @@ class MatterLinkCommandSender(user: String, userId: String, server: String, op:
}
override fun getDisplayName(): ITextComponent {
return TextComponentString(user)
return TextComponentString(displayName)
}
override fun getName() = accountName

View File

@ -1,21 +1,24 @@
package matterlink
import com.mojang.authlib.GameProfile
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.common.Mod
import cpw.mods.fml.common.event.FMLInitializationEvent
import cpw.mods.fml.common.event.FMLPreInitializationEvent
import cpw.mods.fml.common.event.FMLServerStartingEvent
import cpw.mods.fml.common.event.FMLServerStoppingEvent
import matterlink.command.CommandMatterlink
import matterlink.command.MatterLinkCommand
import matterlink.command.MatterLinkCommandSender
import matterlink.config.BaseConfig
import matterlink.config.cfg
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.server.MinecraftServer
import net.minecraft.util.ChatComponentText
import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.common.MinecraftForge
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
import java.util.*
lateinit var logger: Logger
@ -49,7 +52,7 @@ class MatterLink : IMatterLink() {
@Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) {
logger.debug("Registering server commands")
event.registerServerCommand(CommandMatterlink())
event.registerServerCommand(MatterLinkCommand())
start()
}
@ -63,10 +66,56 @@ class MatterLink : IMatterLink() {
MinecraftServer.getServer().configurationManager.sendChatMsg(ChatComponentText(msg))
}
override fun wrappedSendToPlayer(user: String, msg: String) {
val profile = profileByName(user) ?: profileByUUID(user) ?: run {
error("cannot find player by name or uuid $user")
return
}
val player = playerByProfile(profile) ?: run {
error("${profile.name} is not online")
return
}
player.addChatMessage(ChatComponentText(msg))
}
override fun isOnline(username: String) = (FMLCommonHandler.instance()
.minecraftServerInstance.configurationManager.getPlayerByUsername(username) ?: null) != null
private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? {
return FMLCommonHandler.instance().minecraftServerInstance.configurationManager.createPlayerForUser(gameProfile)
}
private fun profileByUUID(uuid: String): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.func_152652_a(UUID.fromString(uuid))
} catch (e: IllegalArgumentException) {
warn("cannot find profile by uuid $uuid")
null
}
private fun profileByName(username: String): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getGameProfileForUsername(username)
} catch (e: IllegalArgumentException) {
warn("cannot find profile by username $username")
null
}
override fun nameToUUID(username: String) = profileByName(username)?.id?.toString()
override fun uuidToName(uuid: String?): String? {
return uuid?.let { profileByUUID(it)?.name }
}
override fun log(level: String, formatString: String, vararg data: Any) =
logger.log(Level.toLevel(level, Level.INFO), formatString, *data)
override fun commandSenderFor(user: String, userId: String, server: String, op: Boolean) = MatterLinkCommandSender(user, userId, server, op)
override fun commandSenderFor(
user: String,
userId: String,
server: String,
uuid: String?,
username: String?,
op: Boolean
) = MatterLinkCommandSender(user, userId, server, uuid, username, op)
override val mcVersion: String = MCVERSION
override val modVersion: String = MODVERSION

View File

@ -0,0 +1,35 @@
package matterlink.command
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.util.ChatComponentText
class AuthCommand : CommandBase() {
override fun getCommandName(): String {
return CommandCoreAuth.name
}
override fun getCommandUsage(sender: ICommandSender): String {
return CommandCoreAuth.usage
}
override fun getCommandAliases(): List<String> {
return CommandCoreAuth.aliases
}
override fun processCommand(sender: ICommandSender, args: Array<String>) {
if (args.isEmpty()) {
throw WrongUsageException("Invalid command! Valid uses: ${this.getCommandUsage(sender)}")
}
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()
val reply = CommandCoreAuth.execute(args, sender.commandSenderName, uuid)
if (reply.isNotEmpty()) {
sender.addChatMessage(ChatComponentText(reply))
}
}
}

View File

@ -3,31 +3,33 @@ package matterlink.command
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.util.ChatComponentText
class CommandMatterlink : CommandBase() {
class MatterLinkCommand : CommandBase() {
override fun getCommandName(): String {
return CommandCoreML.name
}
override fun getCommandUsage(sender: ICommandSender): String {
return CommandCoreML.usage
}
override fun getCommandAliases(): List<String> {
return CommandCoreML.aliases
}
override fun processCommand(sender: ICommandSender, args: Array<String>) {
if (args.isEmpty()) {
throw WrongUsageException("Invalid command! Valid uses: ${this.getCommandUsage(sender)}")
}
val reply = CommandCore.execute(args, sender.commandSenderName)
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()
val reply = CommandCoreML.execute(args, sender.commandSenderName, uuid)
if (reply.isNotEmpty()) {
sender.addChatMessage(ChatComponentText(reply))
}
}
override fun getCommandName(): String {
return CommandCore.getName()
}
override fun getCommandUsage(sender: ICommandSender): String {
return CommandCore.getUsage()
}
override fun getCommandAliases(): List<String> {
return CommandCore.getAliases()
}
}

View File

@ -8,9 +8,13 @@ import net.minecraft.util.ChunkCoordinates
import net.minecraft.util.IChatComponent
import net.minecraft.world.World
class MatterLinkCommandSender(user: String, userId: String, server: String, op: Boolean) : IMinecraftCommandSender(user, userId, server, op), ICommandSender {
private var level: Int = 0
class MatterLinkCommandSender(
user: String,
userId: String,
server: String,
uuid: String?,
username: String?,
op: Boolean) : IMinecraftCommandSender(user, userId, server, uuid, username, op), ICommandSender {
override fun execute(cmdString: String): Boolean {
return 0 < MinecraftServer.getServer().commandManager.executeCommand(
@ -20,7 +24,7 @@ class MatterLinkCommandSender(user: String, userId: String, server: String, op:
}
override fun getFormattedCommandSenderName(): IChatComponent {
return ChatComponentText(user)
return ChatComponentText(displayName)
}
override fun getCommandSenderName() = accountName

View File

@ -28,6 +28,7 @@ dependencies {
compile group: 'commons-logging', name: 'commons-logging', version: '1.1.3'
compile group: 'com.google.code.gson', name: 'gson', version: '+'
compile group: 'com.google.guava', name: 'guava', version: '+'
compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: project.kotlin_version
}

View File

@ -13,10 +13,15 @@ abstract class IMatterLink {
abstract val modVersion: String
abstract val forgeVersion: String
abstract fun commandSenderFor(user: String, userId: String, server: String, op: Boolean): IMinecraftCommandSender
abstract fun commandSenderFor(user: String, userId: String, server: String, uuid: String?, username: String?, op: Boolean): IMinecraftCommandSender
abstract fun wrappedSendToPlayers(msg: String)
abstract fun wrappedSendToPlayer(user: String, msg: String)
abstract fun isOnline(username: String): Boolean
abstract fun nameToUUID(username: String): String?
abstract fun uuidToName(uuid: String?): String?
fun start() {
MessageHandlerInst.logger = { level, msg ->
when (level) {
@ -85,4 +90,5 @@ abstract class IMatterLink {
fun registerBridgeCommands() {
BridgeCommandRegistry.reloadCommands()
}
}

View File

@ -1,10 +1,15 @@
package matterlink
import blue.endless.jankson.Jankson
import blue.endless.jankson.JsonArray
import blue.endless.jankson.JsonElement
import blue.endless.jankson.JsonObject
import blue.endless.jankson.impl.Marshaller
import matterlink.config.cfg
import java.io.PrintWriter
import java.io.StringWriter
import java.util.*
import kotlin.streams.asSequence
private const val ZWSP: Char = '\u200b'
@ -57,11 +62,39 @@ val Exception.stackTraceString: String
return sw.toString()
}
fun randomString(length: Long = 6, source: String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ") = Random().ints(length, 0, source.length)
.asSequence()
.map(source::get)
.joinToString("")
fun <T : Any> JsonObject.getOrDefault(key: String, default: T, comment: String? = null): T {
// instance.info("type: ${default.javaClass.name} key: $key json: >>>${this.getObject(key)?.toJson()}<<< default: $default")
return putDefault(key, default, comment)!!
}
inline fun <reified T : Any> Jankson.fromJson(obj: JsonObject): T = this.fromJson(obj, T::class.java)
inline fun <reified T : Any> Jankson.Builder.registerTypeAdapter(noinline adapter: (JsonObject) -> T) = this.registerTypeAdapter(T::class.java, adapter)
inline fun <reified T : Any> Jankson.Builder.registerPrimitiveTypeAdapter(noinline adapter: (Any) -> T) = this.registerPrimitiveTypeAdapter(T::class.java, adapter)
inline fun <reified T : Any> Jankson.Builder.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T) -> JsonElement) = this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> JsonObject.getReified(key: String): T? = this.get(T::class.java, key)
inline fun <reified T : Any> JsonObject.getList(key: String): List<T>? {
return this[key]?.let { array ->
when (array) {
is JsonArray -> {
array.indices.map { i ->
array.get(T::class.java, i) ?: throw NullPointerException("cannot parse ${array.get(i)}")
}
}
else -> null
}
}
}

View File

@ -0,0 +1,68 @@
package matterlink.bridge.command
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.AuthRequest
import matterlink.config.IdentitiesConfig
import matterlink.config.cfg
import matterlink.instance
import matterlink.randomString
object AuthBridgeCommand : IBridgeCommand() {
override val help: String = "Requests authentication on the bridge. Syntax: auth [username]"
override val permLevel: Double
get() = cfg.command.defaultPermUnauthenticated
override fun execute(alias: String, user: String, userId: String, platform: String, uuid: String?, args: String): Boolean {
if (uuid != null) {
val name = instance.uuidToName(uuid)
respond("you are already authenticated as name: $name uuid: $uuid")
return true
}
val argList = args.split(' ', limit = 2)
val target = argList.getOrNull(0) ?: run {
respond("no username/uuid provided")
return true
}
var targetUserName = target
val targetUUid: String = instance.nameToUUID(target) ?: run {
targetUserName = instance.uuidToName(target) ?: run {
respond("cannot find player by username/uuid $target")
return true
}
target
}
val online = instance.isOnline(targetUserName)
if (!online) {
respond("$targetUserName is not online, please log in and try again to send instructions")
return true
}
val nonce = randomString(length = 3).toUpperCase()
val requestId = user.toLowerCase()
instance.wrappedSendToPlayer(targetUserName, "have you requested authentication with the MatterLink system?")
instance.wrappedSendToPlayer(targetUserName, "if yes please execute /auth accept $user $nonce")
instance.wrappedSendToPlayer(targetUserName, "otherwise you may ignore this message")
IdentitiesConfig.authRequests.put(requestId, AuthRequest(username = targetUserName, uuid = targetUUid, nonce = nonce, platform = platform, userid = userId))
respond("please accept the authentication request ingame, do not share the code")
return true
}
private fun respond(text: String) {
MessageHandlerInst.transmit(
ApiMessage(
text = text
)
)
}
}

View File

@ -1,16 +1,23 @@
package matterlink.bridge.command
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.CommandConfig
import matterlink.config.IdentitiesConfig
import matterlink.config.PermissionConfig
import matterlink.config.cfg
import matterlink.instance
import matterlink.stripColorOut
import java.util.*
object BridgeCommandRegistry {
private val commandMap: HashMap<String, IBridgeCommand> = hashMapOf()
/**
*
* @return consume message flag
*/
fun handleCommand(input: ApiMessage): Boolean {
if (!cfg.command.enable || input.text.isBlank()) return false
@ -19,7 +26,24 @@ object BridgeCommandRegistry {
val cmd = input.text.substring(1).split(' ', ignoreCase = false, limit = 2)
val args = if (cmd.size == 2) cmd[1] else ""
return commandMap[cmd[0]]?.execute(cmd[0], input.username, input.userid, input.account, args) ?: false
val uuid = IdentitiesConfig.getUUID(input.account, input.userid)
return commandMap[cmd[0]]?.let {
if (!it.reachedTimeout()) {
instance.debug("dropped command ${it.alias}")
return false
}
it.preExecute() // resets the tickCounter
if (!it.canExecute(uuid)) {
MessageHandlerInst.transmit(
ApiMessage(
text = "${input.username} is not permitted to perform command: ${cmd[0]}".stripColorOut
)
)
return false
}
it.execute(cmd[0], input.username, input.userid, input.account, uuid, args)
} ?: false
}
fun register(alias: String, cmd: IBridgeCommand): Boolean {
@ -56,11 +80,15 @@ object BridgeCommandRegistry {
fun reloadCommands() {
commandMap.clear()
val permStatus = PermissionConfig.loadPermFile()
register("help", HelpCommand)
if(cfg.command.permissionRequests)
register("req", PermCommand)
val cmdStatus = CommandConfig.readConfig()
if (cfg.command.authRequests)
register("auth", AuthBridgeCommand)
if (cfg.command.permisionRequests)
register("request", RequestPermissionsCommand)
PermissionConfig.loadFile()
CommandConfig.loadFile()
IdentitiesConfig.loadFile()
CommandConfig.commands.forEach { (alias, command) ->
register(alias, command)
}

View File

@ -2,7 +2,6 @@ package matterlink.bridge.command
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.handlers.TickHandler
import matterlink.instance
import matterlink.lazyFormat
import matterlink.stripColorIn
@ -14,18 +13,13 @@ data class CustomCommand(
val response: String? = null,
override val permLevel: Double = 0.0,
override val help: String = "",
val timeout: Int = 20,
override val timeout: Int = 20,
val defaultCommand: Boolean? = null,
val execOp: Boolean? = null,
val argumentsRegex: Regex? = null
) : IBridgeCommand {
val alias: String
get() = BridgeCommandRegistry.getName(this)!!
) : IBridgeCommand() {
@Transient
private var lastUsed: Int = 0
override fun execute(alias: String, user: String, userId: String, server: String, args: String): Boolean {
override fun execute(alias: String, user: String, userId: String, platform: String, uuid: String?, args: String): Boolean {
if (argumentsRegex != null) {
instance.debug("testing '$args' against '${argumentsRegex.pattern}'")
if (!argumentsRegex.matches(args)) {
@ -38,34 +32,20 @@ data class CustomCommand(
}
}
if (TickHandler.tickCounter - lastUsed < timeout) {
instance.debug("dropped command $alias")
return true //eat command silently
}
if (!canExecute(userId, server)) {
MessageHandlerInst.transmit(
ApiMessage(
text = "$user is not permitted to perform command: $alias".stripColorOut
)
)
return false
}
lastUsed = TickHandler.tickCounter
val username = instance.uuidToName(uuid)
return when (type) {
CommandType.EXECUTE -> {
// uses a new commandsender for each use
val commandSender = instance.commandSenderFor(user, userId, server, execOp ?: false)
val cmd = execute?.lazyFormat(getReplacements(user, userId, server, args))?.stripColorIn ?: return false
val commandSender = instance.commandSenderFor(user, userId, platform, uuid, username, execOp ?: false)
val cmd = execute?.lazyFormat(getReplacements(user, userId, platform, uuid, args))?.stripColorIn
?: return false
commandSender.execute(cmd) || commandSender.reply.isNotBlank()
}
CommandType.RESPONSE -> {
MessageHandlerInst.transmit(
ApiMessage(
text = (response?.lazyFormat(getReplacements(user, userId, server, args))
text = (response?.lazyFormat(getReplacements(user, userId, platform, uuid, args))
?: "")
)
)
@ -90,11 +70,13 @@ data class CustomCommand(
companion object {
val DEFAULT = CustomCommand()
fun getReplacements(user: String, userId: String, server: String, args: String): Map<String, () -> String> = mapOf(
fun getReplacements(user: String, userId: String, platform: String, uuid: String?, args: String): Map<String, () -> String> = mapOf(
"{uptime}" to instance::getUptimeAsString,
"{user}" to { user },
"{userid}" to { userId },
"{server}" to { server },
"{uuid}" to { uuid.toString() },
"{username}" to { uuid?.let { instance.uuidToName(it) }.toString() },
"{platform}" to { platform },
"{args}" to { args }
)
}

View File

@ -2,15 +2,18 @@ package matterlink.bridge.command
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg
import matterlink.stripColorOut
object HelpCommand : IBridgeCommand {
object HelpCommand : IBridgeCommand() {
override val help: String = "Returns the help string for the given command. Syntax: help <command>"
override val permLevel = 0.0
override fun execute(alias: String, user: String, userId: String, server: String, args: String): Boolean {
override val permLevel: Double
get() = cfg.command.defaultPermUnauthenticated
override fun execute(alias: String, user: String, userId: String, platform: String, uuid: String?, args: String): Boolean {
val msg: String = when {
args.isEmpty() ->
"Available commands: ${BridgeCommandRegistry.getCommandList(IBridgeCommand.getPermLevel(userId, server))}"
"Available commands: ${BridgeCommandRegistry.getCommandList(IBridgeCommand.getPermLevel(uuid))}"
else -> args.split(" ", ignoreCase = false)
.joinToString(separator = "\n") {
"$it: ${BridgeCommandRegistry.getHelpString(it)}"

View File

@ -1,27 +1,50 @@
package matterlink.bridge.command
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.PermissionConfig
import matterlink.config.cfg
import matterlink.handlers.TickHandler
import matterlink.instance
interface IBridgeCommand {
val help: String
val permLevel: Double
abstract class IBridgeCommand {
abstract val help: String
abstract val permLevel: Double
open val timeout: Int = 20
fun execute(alias: String, user: String, userId: String, server: String, args: String): Boolean
protected var lastUsed: Int = 0
fun canExecute(userId: String, server: String): Boolean {
instance.trace("canExecute this: $this canExecute: $userId server: $server permLevel: $permLevel")
val canExec = getPermLevel(userId, server) >= permLevel
val alias: String
get() = BridgeCommandRegistry.getName(this)!!
fun reachedTimeout(): Boolean {
return (TickHandler.tickCounter - lastUsed > timeout)
}
fun preExecute() {
lastUsed = TickHandler.tickCounter
}
/**
*
* @return consume message flag
*/
abstract fun execute(alias: String, user: String, userId: String, platform: String, uuid: String?, args: String): Boolean
fun canExecute(uuid: String?): Boolean {
instance.trace("canExecute this: $this uuid: $uuid permLevel: $permLevel")
val canExec = getPermLevel(uuid) >= permLevel
instance.trace("canExecute return $canExec")
return canExec
}
fun validate() = true
open fun validate() = true
companion object {
fun getPermLevel(userId: String, server: String): Double {
val serverMap = PermissionConfig.perms[server] ?: return 0.0
return serverMap[userId] ?: 0.0
fun getPermLevel(uuid: String?): Double {
if(uuid == null) return cfg.command.defaultPermUnauthenticated
return PermissionConfig.perms[uuid] ?: cfg.command.defaultPermAuthenticated
}
}
}

View File

@ -4,7 +4,7 @@ import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.stripColorOut
abstract class IMinecraftCommandSender(val user: String, val userId: String, val server: String, val op: Boolean) {
abstract class IMinecraftCommandSender(val user: String, val userId: String, val server: String, val uuid: String?, val username: String?, val op: Boolean) {
/**
* @param cmdString The command to execute with its arguments
*
@ -12,12 +12,13 @@ abstract class IMinecraftCommandSender(val user: String, val userId: String, val
*/
abstract fun execute(cmdString: String): Boolean
val accountName = "$user (id=$userId server=$server)"
val displayName = username ?: user
val accountName = "$user (id=$userId server=$server uuid=$uuid)"
fun canExecute(commandName: String): Boolean {
if (op) return true
val command = BridgeCommandRegistry[commandName] ?: return false
return command.canExecute(userId, server)
return command.canExecute(uuid)
}
var reply: String = ""

View File

@ -1,57 +0,0 @@
package matterlink.bridge.command
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.PermissionConfig
import matterlink.config.PermissionRequest
object PermCommand : IBridgeCommand {
override val help: String = "Requests permissions on the bridge. Syntax: req [powerlevel] [key]"
override val permLevel = 0.0
override fun execute(alias: String, user: String, userId: String, server: String, args: String): Boolean {
val argList = args.split(' ', limit = 2)
val requestedLevel = args.toDoubleOrNull() ?: 20.0
var unfilteredKey = user
var key = unfilteredKey.replace("[^A-Za-z0-9 ]".toRegex(), "").toLowerCase()
if (argList.size > 1) {
unfilteredKey = argList[1]
key = unfilteredKey.replace("[^A-Za-z0-9 ]".toRegex(), "").toLowerCase()
}
if (key.isBlank()) {
MessageHandlerInst.transmit(
ApiMessage(
text = "$unfilteredKey is made up of invalid characters.. please specifiy a key for tracking this request"
)
)
return true
} else if (PermissionConfig.permissionRequests.containsKey(key)) {
MessageHandlerInst.transmit(
ApiMessage(
text = "there is already a permission request for $key"
)
)
return true
}
val currentPowerlevel = IBridgeCommand.getPermLevel(userId, server)
if(currentPowerlevel < 0.0) {
MessageHandlerInst.transmit(
ApiMessage(
text = "Your level is $currentPowerlevel seems like someone banned you from making any more requests"
)
)
return true
}
MessageHandlerInst.transmit(
ApiMessage(
text = "requet for powerlevel $requestedLevel from user $user userID: $userId server: $server\n" +
"accept this by executing `bridge acceptPerm $key <level>`\n" +
"setting a negative level will prevent people from sending any more requests"
)
)
PermissionConfig.permissionRequests[key.toLowerCase()] = PermissionRequest(user, server, userId, requestedLevel)
// PermissionConfig.add(server, userId, requestedlevel, "authorized by $user")
return true
}
}

View File

@ -0,0 +1,50 @@
package matterlink.bridge.command
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.PermissionConfig
import matterlink.config.PermissionRequest
import matterlink.config.cfg
import matterlink.randomString
object RequestPermissionsCommand : IBridgeCommand() {
override val help: String = "Requests permissions on the bridge. Syntax: request [permissionLevel]"
override val permLevel: Double
get() = cfg.command.defaultPermAuthenticated
override fun execute(alias: String, user: String, userId: String, platform: String, uuid: String?, args: String): Boolean {
if (uuid == null) {
respond("$user is not authenticated (userid: $userId platform: $platform)")
return true
}
val argList = args.split(' ', limit = 2)
val requestedLevelArg = argList.getOrNull(0)
val requestedlevel = requestedLevelArg?.let {
it.toDoubleOrNull() ?: run {
respond("cannot parse permlevel")
return true
}
}
val nonce = randomString(length = 3).toUpperCase()
val requestId = user.toLowerCase()
PermissionConfig.permissionRequests.put(requestId, PermissionRequest(uuid = uuid, user = user, nonce = nonce, powerlevel = requestedlevel))
respond("please ask a op to accept your permission elevation with `/ml permAccept $requestId $nonce [powerlevel]`")
return true
}
private fun respond(text: String) {
MessageHandlerInst.transmit(
ApiMessage(
text = text
)
)
}
}

View File

@ -1,52 +0,0 @@
package matterlink.command
import matterlink.bridge.MessageHandlerInst
import matterlink.bridge.command.BridgeCommandRegistry
import matterlink.config.PermissionConfig
import matterlink.config.baseCfg
import matterlink.config.cfg
object CommandCore {
fun getName() = "bridge"
fun getAliases() = listOf("BRIDGE", "bridge")
fun getUsage() = "bridge <connect|disconnect|reload|acceptPerm>"
fun execute(args: Array<String>, user: String): String {
val cmd = args[0].toLowerCase()
return when (cmd) {
"connect" -> {
MessageHandlerInst.start( "Bridge connected by console", true)
"Attempting bridge connection!"
}
"disconnect" -> {
MessageHandlerInst.stop("Bridge disconnected by console")
"Bridge disconnected!"
}
"reload" -> {
// if (MessageHandlerInst.connected)
MessageHandlerInst.stop("Bridge restarting (reload command issued by console)")
cfg = baseCfg.load()
BridgeCommandRegistry.reloadCommands()
// if (!MessageHandlerInst.connected)
MessageHandlerInst.start("Bridge reconnected", false)
"Bridge config reloaded!"
}
"acceptperm" -> {
val key = args.getOrNull(1)?.toLowerCase() ?: return "No argument providing they request key"
val request = PermissionConfig.permissionRequests[key] ?: return "No request found for key $key"
val powerLevelArg = args.getOrNull(2)?.toDoubleOrNull()
val powerLevel = powerLevelArg ?: request.powerlevel ?: return "no powerLevel provided or it cannot be parsed"
PermissionConfig.add(request.platform, request.userId, powerLevel, "${request.user} Authorized by $user")
PermissionConfig.permissionRequests.remove(key)
"added ${request.user} (platform: ${request.platform} userId: ${request.userId}) with power level: $powerLevel"
}
else -> {
"Invalid arguments for command!"
}
}
}
}

View File

@ -0,0 +1,77 @@
package matterlink.command
import matterlink.bridge.MessageHandlerInst
import matterlink.bridge.command.BridgeCommandRegistry
import matterlink.config.IdentitiesConfig
import matterlink.config.PermissionConfig
import matterlink.config.baseCfg
import matterlink.config.cfg
object CommandCoreAuth {
val name = "auth"
val aliases = listOf("authenticate")
val usage = "auth <accept|reject> <id> <code>"
fun execute(args: Array<String>, user: String, uuid: String?): String {
val cmd = args[0].toLowerCase()
return when (cmd) {
"accept" -> {
val requestId = args.getOrNull(1)?.toLowerCase() ?: run {
return "no requestId passed"
}
val request = IdentitiesConfig.authRequests.getIfPresent(requestId.toLowerCase()) ?: run {
return "No request available"
}
val nonce = args.getOrNull(2)?.toUpperCase() ?: run {
return "no code passed"
}
if(request.nonce != nonce) {
return "nonce in request does not match"
}
if(request.username != user) {
return "username in request does not match ${request.username} != $user"
}
if(request.uuid != uuid) {
return "uuid in request does not match ${request.uuid} != $uuid"
}
IdentitiesConfig.add(request.uuid, request.username, request.platform, request.userid, "Accepted by $user")
IdentitiesConfig.authRequests.invalidate(requestId)
"${request.userid} on ${request.platform} is now identified as $user"
}
"reject" -> {
val requestId = args.getOrNull(1)?.toLowerCase() ?: run {
return "no requestId passed"
}
val request = IdentitiesConfig.authRequests.getIfPresent(requestId.toLowerCase()) ?: run {
return "No request available"
}
val nonce = args.getOrNull(2)?.toUpperCase() ?: run {
return "no code passed"
}
if(request.nonce != nonce) {
return "nonce in request does not match"
}
if(request.username != user) {
return "username in request does not match ${request.username} != $user"
}
if(request.uuid != uuid) {
return "uuid in request does not match ${request.uuid} != $uuid"
}
IdentitiesConfig.authRequests.invalidate(requestId)
"request $nonce for ${request.userid} on ${request.platform} was invalidated"
}
else -> {
"Invalid arguments for command! \n" +
"usage: $usage"
}
}
}
}

View File

@ -0,0 +1,65 @@
package matterlink.command
import matterlink.bridge.MessageHandlerInst
import matterlink.bridge.command.BridgeCommandRegistry
import matterlink.config.PermissionConfig
import matterlink.config.baseCfg
import matterlink.config.cfg
object CommandCoreML {
val name = "ml"
val aliases = listOf("matterlink")
val usage = "ml <connect|disconnect|reload|auth>"
fun execute(args: Array<String>, user: String, uuid: String?): String {
val cmd = args[0].toLowerCase()
return when (cmd) {
"connect" -> {
MessageHandlerInst.start("Bridge connected by console", true)
"Attempting bridge connection!"
}
"disconnect" -> {
MessageHandlerInst.stop("Bridge disconnected by console")
"Bridge disconnected!"
}
"reload" -> {
// if (MessageHandlerInst.connected)
MessageHandlerInst.stop("Bridge restarting (reload command issued by console)")
cfg = baseCfg.load()
BridgeCommandRegistry.reloadCommands()
// if (!MessageHandlerInst.connected)
MessageHandlerInst.start("Bridge reconnected", false)
"Bridge config reloaded!"
}
"permaccept" -> {
val requestId = args.getOrNull(1)?.toLowerCase() ?: run {
return "no requestId passed"
}
val request = PermissionConfig.permissionRequests.getIfPresent(requestId.toLowerCase()) ?: run {
return "No request available"
}
val nonce = args.getOrNull(2)?.toUpperCase() ?: run {
return "no code passed"
}
if (request.nonce != nonce) {
return "nonce in request does not match"
}
val powerLevelArg = args.getOrNull(2)?.toDoubleOrNull()
val powerLevel = powerLevelArg ?: run { return "permLevel cannot be parsed" }
?: request.powerlevel
?: return "no permLevel provided"
PermissionConfig.add(request.uuid, powerLevel, "${request.user} Authorized by $user")
PermissionConfig.permissionRequests.invalidate(requestId)
"added ${request.user} (uuid: ${request.uuid}) with power level: $powerLevel"
}
else -> {
"Invalid arguments for command! \n" +
"usage: ${CommandCoreAuth.usage}"
}
}
}
}

View File

@ -37,7 +37,10 @@ data class BaseConfig(val rootDir: File) {
data class CommandOptions(
val prefix: Char = '!',
val enable: Boolean = true,
val permissionRequests: Boolean = true
val authRequests: Boolean = true,
val permisionRequests: Boolean = true,
val defaultPermUnauthenticated: Double = 0.0,
val defaultPermAuthenticated: Double = 1.0
)
data class ConnectOptions(
@ -175,10 +178,25 @@ data class BaseConfig(val rootDir: File) {
prefix,
"Prefix for MC bridge commands. Accepts a single character (not alphanumeric or /)"
),
permissionRequests = it.getOrDefault(
"permissionRequests",
permissionRequests,
"Enable the 'req' command for requestion permissions from chat"
authRequests = it.getOrDefault(
"authRequests",
authRequests,
"Enable the 'auth' command for linking chat accounts to uuid / ingame account"
),
permisionRequests = it.getOrDefault(
"permisionRequests",
authRequests,
"Enable the 'request' command for requestion permissions from chat"
),
defaultPermUnauthenticated = it.getOrDefault(
"defaultPermUnauthenticated",
defaultPermUnauthenticated,
"default permission level for unauthenticated players"
),
defaultPermAuthenticated = it.getOrDefault(
"defaultPermAuthenticated",
defaultPermAuthenticated,
"default permission level for players that hve linked their accounts"
)
)
}

View File

@ -54,7 +54,7 @@ object CommandConfig {
"whoami" to ("this shows you some of the other response macros"
to CustomCommand(
type = CommandType.RESPONSE,
response = "server: `{server}` userid: `{userid}` user: `{user}`",
response = "name: `{user}` userid: `{userid}` platform: `{platform}` username: `{username}` uuid: `{uuid}`",
help = "Print debug user data",
timeout = 200,
defaultCommand = true
@ -73,7 +73,7 @@ object CommandConfig {
val commands: CommandMap = hashMapOf()
fun readConfig(): Boolean {
fun loadFile() {
val jankson = Jankson
.builder()
.registerTypeAdapter { jsonObj ->
@ -96,7 +96,7 @@ object CommandConfig {
}
.build()
jankson.marshaller.registerSerializer(Regex::class.java) { regex, marshaller ->
jankson.marshaller.registerSerializer(Regex::class.java) { regex, _ ->
JsonPrimitive(regex.pattern)
}
@ -142,7 +142,5 @@ object CommandConfig {
}
}
configFile.writeText(nonDefaultJsonObj.toJson(true, true))
return true
}
}

View File

@ -0,0 +1,126 @@
package matterlink.config
import blue.endless.jankson.Jankson
import blue.endless.jankson.JsonObject
import blue.endless.jankson.impl.Marshaller
import blue.endless.jankson.impl.SyntaxError
import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import matterlink.getList
import matterlink.getOrDefault
import matterlink.instance
import java.io.File
import java.io.FileNotFoundException
import java.util.concurrent.TimeUnit
typealias IdentMap = Map<String, Map<String, List<String>>>
data class AuthRequest(
val username: String,
val uuid: String,
val nonce: String,
val platform: String,
val userid: String
)
object IdentitiesConfig {
val authRequests: Cache<String, AuthRequest> = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build()
private val jankson = Jankson
.builder()
.build()
private val configFile: File = baseCfg.cfgDirectory.resolve("identities.hjson")
private val default = mapOf(
("edd31c45-b095-49c5-a9f5-59cec4cfed8c" to mapOf(
"discord.game" to (listOf("112228624366575616") to "discord id")
) to "username: NikkyAi")
)
var idents: IdentMap = mapOf()
private set
private var jsonObject: JsonObject = JsonObject()
fun loadFile() {
val defaultJsonObject = JsonObject().apply {
default.forEach { (uuid, userMap), uuidComment ->
val jsonUserMap = this.putDefault(uuid, JsonObject(), uuidComment)
if (jsonUserMap is JsonObject) {
userMap.forEach { platform, (user, comment) ->
instance.trace("loading uuid: $uuid platform: $platform user: $user")
val element = Marshaller.getFallback().serialize(user)
jsonUserMap.putDefault(platform, element, comment.takeUnless { it.isBlank() })
}
this[uuid] = jsonUserMap
} else {
instance.error("cannot parse uuid: $uuid , value: '$jsonUserMap' as Map, skipping")
}
}
}
var save = true
jsonObject = try {
jankson.load(configFile)
} catch (e: SyntaxError) {
instance.error("error parsing config: ${e.completeMessage}")
save = false
defaultJsonObject
} catch (e: FileNotFoundException) {
instance.error("cannot find config: $configFile .. creating sample permissions mapping")
configFile.createNewFile()
defaultJsonObject
}
load(save)
}
private fun load(save: Boolean = true) {
val tmpIdents: MutableMap<String, Map<String, List<String>>> = mutableMapOf()
jsonObject.forEach { uuid, jsonIdentifier ->
val identMap: MutableMap<String, List<String>> = tmpIdents[uuid]?.toMutableMap() ?: mutableMapOf()
if (jsonIdentifier is JsonObject) {
jsonIdentifier.forEach { platform, user ->
instance.info("$uuid $platform $user")
identMap[platform] = jsonIdentifier.getList(platform) ?: emptyList()
}
}
tmpIdents[uuid] = identMap.toMap()
}
idents = tmpIdents.toMap()
instance.info("Identities loaded")
if (save)
configFile.writeText(jsonObject.toJson(true, true))
}
fun add(uuid: String, username: String, platform: String, userid: String, comment: String? = null) {
val platformObject = jsonObject.getObject(uuid) ?: JsonObject()
platformObject.putDefault(platform, userid, comment)
val userIdList = platformObject.getList<String>(platform) ?: emptyList()
platformObject[platform] = platformObject.marshaller.serialize(userIdList + userid)
jsonObject[uuid] = platformObject
if (jsonObject.getComment(uuid) == null) {
jsonObject.setComment(uuid, "Username: $username")
}
load()
}
//TODO: rewrite, store ident map differently in memory
fun getUUID(platform: String, userid: String): String? {
return idents.entries.firstOrNull { (uuid, usermap) ->
usermap.entries.any { (_platform, userids) ->
if (platform.equals(_platform, true))
instance.info("platform: $_platform userids: $userids")
platform.equals(_platform, true) && userids.contains(userid)
}
}?.key
}
}

View File

@ -2,23 +2,28 @@ package matterlink.config
import blue.endless.jankson.Jankson
import blue.endless.jankson.JsonObject
import blue.endless.jankson.impl.Marshaller
import blue.endless.jankson.impl.SyntaxError
import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import matterlink.getReified
import matterlink.instance
import java.io.File
import java.io.FileNotFoundException
import java.util.concurrent.TimeUnit
typealias PermissionMap = MutableMap<String, MutableMap<String, Double>>
typealias PermissionMap = Map<String, Double>
data class PermissionRequest(
val uuid: String,
val user: String,
val platform: String,
val userId: String,
val nonce: String,
val powerlevel: Double? = null
)
object PermissionConfig {
val permissionRequests = mutableMapOf<String, PermissionRequest>()
val permissionRequests: Cache<String, PermissionRequest> = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build()
private val jankson = Jankson
.builder()
.build()
@ -26,34 +31,16 @@ object PermissionConfig {
private val configFile: File = baseCfg.cfgDirectory.resolve("permissions.hjson")
private val default = mapOf(
"irc.esper" to mapOf(
"~nikky@nikky.moe" to (0.0 to "IRC users are identified by their username and hostmask"),
"user@example.com" to (0.0 to "")
),
"discord.game" to mapOf(
"112228624366575616" to (0.0 to "thats a discord user id")
)
"edd31c45-b095-49c5-a9f5-59cec4cfed8c" to 9000.0 to "Superuser"
)
val perms: PermissionMap = mutableMapOf()
private var jsonObject: JsonObject = JsonObject()
fun loadPermFile(): Boolean {
permissionRequests.clear()
fun loadFile() {
val defaultJsonObject = JsonObject().apply {
default.forEach { platform, userMap ->
val jsonUserMap = this.getOrDefault(platform, JsonObject())
if (jsonUserMap is JsonObject) {
userMap.forEach { user, (powerlevel, comment) ->
instance.trace("loading platform: $platform user: $user powerlevel: $powerlevel")
val element = Marshaller.getFallback().serialize(powerlevel)
jsonUserMap.putDefault(user, element, comment.takeUnless { it.isBlank() })
}
this[platform] = jsonUserMap
} else {
instance.error("cannot parse platform: $platform , value: '$jsonUserMap' as Map, skipping")
}
default.forEach { (uuid, level), comment ->
jsonObject.putDefault(uuid, level, comment)
}
}
@ -71,33 +58,27 @@ object PermissionConfig {
}
load(save)
return true
}
private fun load(save: Boolean = true) {
perms.clear()
jsonObject.forEach { platform, jsonUserMap ->
val userMap = perms[platform] ?: mutableMapOf()
if (jsonUserMap is JsonObject) {
jsonUserMap.forEach { user, powerlevel ->
instance.info("$platform $user $powerlevel")
userMap[user] = jsonUserMap.get(Double::class.java, user) ?: 0.0
val tmpPerms = mutableMapOf<String, Double>()
for ((uuid, powerlevel) in jsonObject) {
val tmpLevel = jsonObject.getReified<Double>(uuid)
if (tmpLevel == null) {
instance.warn("cannot parse permission uuid: $uuid level: $powerlevel")
continue
}
tmpPerms[uuid] = tmpLevel
}
perms[platform] = userMap
}
instance.info("Permissions reloaded")
if (save)
configFile.writeText(jsonObject.toJson(true, true))
}
fun add(platform: String, userid: String, powerlevel: Double, comment: String? = null) {
val platformObject = jsonObject.getObject(platform) ?: JsonObject()
platformObject.putDefault(userid, powerlevel, comment)
jsonObject[platform] = platformObject
fun add(uuid: String, powerlevel: Double, comment: String? = null) {
jsonObject.putDefault(uuid, powerlevel, comment)
load()
}
}