diff --git a/.gitignore b/.gitignore index c91c99e..34dd0c0 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,5 @@ run/ \.floo \.flooignore + +bugreport/ diff --git a/1.12.2/build.gradle b/1.12.2/build.gradle index c14d64b..5db1438 100644 --- a/1.12.2/build.gradle +++ b/1.12.2/build.gradle @@ -114,4 +114,8 @@ curseforge { displayName = "MatterLink $version" } } +} + +runServer { + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/1.12.2/gradle.properties b/1.12.2/gradle.properties index 485de95..2643ba4 100644 --- a/1.12.2/gradle.properties +++ b/1.12.2/gradle.properties @@ -1,3 +1,3 @@ mc_version = 1.12.2 -mcp_mappings = snapshot_20171003 -forge_version = 14.23.1.2599 \ No newline at end of file +mcp_mappings = stable_39 +forge_version = 14.23.5.2768 \ No newline at end of file diff --git a/1.12.2/src/main/kotlin/matterlink/EventHandler.kt b/1.12.2/src/main/kotlin/matterlink/EventHandler.kt index 791b1e1..e7778b1 100644 --- a/1.12.2/src/main/kotlin/matterlink/EventHandler.kt +++ b/1.12.2/src/main/kotlin/matterlink/EventHandler.kt @@ -1,7 +1,13 @@ package matterlink +import kotlinx.coroutines.runBlocking import matterlink.config.cfg -import matterlink.handlers.* +import matterlink.handlers.ChatEvent +import matterlink.handlers.ChatProcessor +import matterlink.handlers.DeathHandler +import matterlink.handlers.JoinLeaveHandler +import matterlink.handlers.ProgressHandler +import matterlink.handlers.TickHandler import net.minecraft.command.server.CommandBroadcast import net.minecraft.command.server.CommandEmote import net.minecraft.entity.player.EntityPlayer @@ -22,40 +28,40 @@ object EventHandler { //MC-VERSION & FORGE DEPENDENT @SubscribeEvent @JvmStatic - fun progressEvent(e: AdvancementEvent) { - if (e.advancement.display == null) return + fun progressEvent(e: AdvancementEvent) = runBlocking { + if (e.advancement.display == null) return@runBlocking ProgressHandler.handleProgress( - name = e.entityPlayer.displayName.unformattedText, - message = "has made the advancement", - display = e.advancement.displayText.unformattedText, - x = e.entityPlayer.posX.toInt(), - y = e.entityPlayer.posY.toInt(), - z = e.entityPlayer.posZ.toInt(), - dimension = e.entityPlayer.dimension + name = e.entityPlayer.displayName.unformattedText, + message = "has made the advancement", + display = e.advancement.displayText.unformattedText, + x = e.entityPlayer.posX.toInt(), + y = e.entityPlayer.posY.toInt(), + z = e.entityPlayer.posZ.toInt(), + dimension = e.entityPlayer.dimension ) } //FORGE-DEPENDENT @SubscribeEvent @JvmStatic - fun chatEvent(e: ServerChatEvent) { - if (e.isCanceled) return + fun chatEvent(e: ServerChatEvent) = runBlocking { + if (e.isCanceled) return@runBlocking e.isCanceled = ChatProcessor.sendToBridge( - user = e.player.displayName.unformattedText, - msg = e.message, - x = e.player.posX.toInt(), - y = e.player.posY.toInt(), - z = e.player.posZ.toInt(), - dimension = e.player.dimension, - event = ChatEvent.PLAIN, - uuid = e.player.gameProfile.id + user = e.player.displayName.unformattedText, + msg = e.message, + x = e.player.posX.toInt(), + y = e.player.posY.toInt(), + z = e.player.posZ.toInt(), + dimension = e.player.dimension, + event = ChatEvent.PLAIN, + uuid = e.player.gameProfile.id ) } //FORGE-DEPENDENT @SubscribeEvent @JvmStatic - fun commandEvent(e: CommandEvent) { + fun commandEvent(e: CommandEvent) = runBlocking { val sender = when { e.sender is DedicatedServer -> cfg.outgoing.systemUser @@ -66,36 +72,36 @@ object EventHandler { when { this is CommandEmote || name.equals("me", true) -> ChatEvent.ACTION this is CommandBroadcast || name.equals("say", true) -> ChatEvent.BROADCAST - else -> return + else -> return@runBlocking } } ChatProcessor.sendToBridge( - user = sender, - msg = args, - event = type, - x = e.sender.position.x, - y = e.sender.position.y, - z = e.sender.position.z, - dimension = when { - e.sender is DedicatedServer -> null - else -> e.sender.commandSenderEntity?.dimension ?: e.sender.entityWorld.provider.dimension - } + user = sender, + msg = args, + event = type, + x = e.sender.position.x, + y = e.sender.position.y, + z = e.sender.position.z, + dimension = when { + e.sender is DedicatedServer -> null + else -> e.sender.commandSenderEntity?.dimension ?: e.sender.entityWorld.provider.dimension + } ) } //FORGE-DEPENDENT @SubscribeEvent @JvmStatic - fun deathEvent(e: LivingDeathEvent) { + fun deathEvent(e: LivingDeathEvent) = runBlocking { if (e.entityLiving is EntityPlayer) { DeathHandler.handleDeath( - player = e.entityLiving.displayName.unformattedText, - deathMessage = e.entityLiving.combatTracker.deathMessage.unformattedText, - damageType = e.source.damageType, - x = e.entityLiving.posX.toInt(), - y = e.entityLiving.posY.toInt(), - z = e.entityLiving.posZ.toInt(), - dimension = e.entityLiving.dimension + player = e.entityLiving.displayName.unformattedText, + deathMessage = e.entityLiving.combatTracker.deathMessage.unformattedText, + damageType = e.source.damageType, + x = e.entityLiving.posX.toInt(), + y = e.entityLiving.posY.toInt(), + z = e.entityLiving.posZ.toInt(), + dimension = e.entityLiving.dimension ) } } @@ -103,34 +109,35 @@ object EventHandler { //FORGE-DEPENDENT @SubscribeEvent @JvmStatic - fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) { + fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) = runBlocking { JoinLeaveHandler.handleJoin( - player = e.player.displayName.unformattedText, - x = e.player.posX.toInt(), - y = e.player.posY.toInt(), - z = e.player.posZ.toInt(), - dimension = e.player.dimension + player = e.player.displayName.unformattedText, + x = e.player.posX.toInt(), + y = e.player.posY.toInt(), + z = e.player.posZ.toInt(), + dimension = e.player.dimension ) } //FORGE-DEPENDENT @SubscribeEvent @JvmStatic - fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) { + fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) = runBlocking { JoinLeaveHandler.handleLeave( - player = e.player.displayName.unformattedText, - x = e.player.posX.toInt(), - y = e.player.posY.toInt(), - z = e.player.posZ.toInt(), - dimension = e.player.dimension + player = e.player.displayName.unformattedText, + x = e.player.posX.toInt(), + y = e.player.posY.toInt(), + z = e.player.posZ.toInt(), + dimension = e.player.dimension ) } //FORGE-DEPENDENT @SubscribeEvent @JvmStatic - fun serverTickEvent(e: TickEvent.ServerTickEvent) { + fun serverTickEvent(e: TickEvent.ServerTickEvent) = runBlocking { if (e.phase == TickEvent.Phase.END) TickHandler.handleTick() + } } \ No newline at end of file diff --git a/1.12.2/src/main/kotlin/matterlink/MatterLink.kt b/1.12.2/src/main/kotlin/matterlink/MatterLink.kt index f61e026..d73a426 100644 --- a/1.12.2/src/main/kotlin/matterlink/MatterLink.kt +++ b/1.12.2/src/main/kotlin/matterlink/MatterLink.kt @@ -1,7 +1,7 @@ package matterlink import com.mojang.authlib.GameProfile -import jline.internal.Log.warn +import kotlinx.coroutines.runBlocking import matterlink.bridge.command.IBridgeCommand import matterlink.command.AuthCommand import matterlink.command.MatterLinkCommand @@ -9,9 +9,7 @@ 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.text.TextComponentString -import net.minecraftforge.common.DimensionManager import net.minecraftforge.common.ForgeVersion import net.minecraftforge.fml.common.FMLCommonHandler import net.minecraftforge.fml.common.Mod @@ -21,17 +19,17 @@ 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.core.config.Configurator -import java.util.* +import java.util.UUID @Mod( - modid = MODID, - name = NAME, version = MODVERSION, - serverSideOnly = true, - useMetadata = true, - acceptableRemoteVersions = "*", - modLanguageAdapter = "net.shadowfacts.forgelin.KotlinAdapter", - dependencies = DEPENDENCIES + modid = MODID, + name = NAME, version = MODVERSION, + serverSideOnly = true, + useMetadata = true, + acceptableRemoteVersions = "*", + modLanguageAdapter = "net.shadowfacts.forgelin.KotlinAdapter", + dependencies = DEPENDENCIES ) object MatterLink : IMatterLink() { @@ -59,6 +57,7 @@ object MatterLink : IMatterLink() { @Mod.EventHandler fun init(event: FMLInitializationEvent) { + logger.debug("Registering bridge commands") this.registerBridgeCommands() } @@ -67,11 +66,13 @@ object MatterLink : IMatterLink() { logger.debug("Registering server commands") event.registerServerCommand(MatterLinkCommand) event.registerServerCommand(AuthCommand) - start() + runBlocking { + start() + } } @Mod.EventHandler - fun serverStopping(event: FMLServerStoppingEvent) { + fun serverStopping(event: FMLServerStoppingEvent) = runBlocking { stop() } @@ -104,21 +105,23 @@ object MatterLink : IMatterLink() { player.sendMessage(TextComponentString(msg)) } - override fun isOnline(username: String) = FMLCommonHandler.instance().minecraftServerInstance.onlinePlayerNames.contains(username) + 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 playerByProfile(gameProfile: GameProfile): EntityPlayerMP? = + FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id) private fun profileByUUID(uuid: UUID): GameProfile? = try { FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getProfileByUUID(uuid) } catch (e: IllegalArgumentException) { - warn("cannot find profile by uuid $uuid") + logger.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") + logger.warn("cannot find profile by username $username") null } @@ -135,9 +138,9 @@ object MatterLink : IMatterLink() { override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name override fun commandSenderFor( - user: String, - env: IBridgeCommand.CommandEnvironment, - op: Boolean + user: String, + env: IBridgeCommand.CommandEnvironment, + op: Boolean ) = MatterLinkCommandSender(user, env, op) override val mcVersion: String = MCVERSION diff --git a/1.12.2/src/main/kotlin/matterlink/command/MatterLinkCommand.kt b/1.12.2/src/main/kotlin/matterlink/command/MatterLinkCommand.kt index e6049e1..a893d84 100644 --- a/1.12.2/src/main/kotlin/matterlink/command/MatterLinkCommand.kt +++ b/1.12.2/src/main/kotlin/matterlink/command/MatterLinkCommand.kt @@ -1,5 +1,6 @@ package matterlink.command +import kotlinx.coroutines.runBlocking import net.minecraft.command.CommandBase import net.minecraft.command.ICommandSender import net.minecraft.command.WrongUsageException @@ -21,9 +22,9 @@ object MatterLinkCommand : CommandBase() { return CommandCoreML.aliases } - override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array) { + override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array) = runBlocking { if (args.isEmpty()) { - throw WrongUsageException("Invalid command! Valid uses: ${this.getUsage(sender)}") + throw WrongUsageException("Invalid command! Valid uses: ${getUsage(sender)}") } val uuid = (sender as? EntityPlayer)?.uniqueID?.toString() diff --git a/1.12.2/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt b/1.12.2/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt index 7a54141..dc64799 100644 --- a/1.12.2/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt +++ b/1.12.2/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt @@ -1,5 +1,6 @@ package matterlink.command +import kotlinx.coroutines.runBlocking import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IMinecraftCommandSender import net.minecraft.command.ICommandSender @@ -11,14 +12,15 @@ import net.minecraftforge.fml.common.FMLCommonHandler import javax.annotation.Nonnull class MatterLinkCommandSender( - user: String, - env: IBridgeCommand.CommandEnvironment, - op: Boolean) : IMinecraftCommandSender(user, env, op), ICommandSender { + user: String, + env: IBridgeCommand.CommandEnvironment, + op: Boolean +) : IMinecraftCommandSender(user, env, op), ICommandSender { - override fun execute(cmdString: String): Boolean { - return 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand( - this, - cmdString + override fun execute(cmdString: String): Boolean = runBlocking { + 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand( + this@MatterLinkCommandSender, + cmdString ).apply { sendReply(cmdString) } diff --git a/1.7.10/build.gradle b/1.7.10/build.gradle index 5e53a0d..0758491 100644 --- a/1.7.10/build.gradle +++ b/1.7.10/build.gradle @@ -11,17 +11,12 @@ buildscript { } dependencies { classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '1.2-SNAPSHOT' - classpath group: 'com.github.jengelman.gradle.plugins', name: 'shadow', version: shadowVersion classpath group: 'gradle.plugin.com.matthewprenger', name: 'CurseGradle', version: cursegradleVersion - classpath group: 'com.vanniktech', name: 'gradle-dependency-graph-generator-plugin', version: '0.5.0' } } apply plugin: 'forge' -//apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'com.matthewprenger.cursegradle' -apply plugin: "com.vanniktech.dependency.graph.generator" - version = project.mc_version + '-' + project.modVersion @@ -39,6 +34,12 @@ dependencies { shade (project(':Jankson')) { transitive = false } shade group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion + shade(group: "com.github.NikkyAi.Fuel", name: "fuel", version: "feature~chunked-SNAPSHOT") + shade(group: "com.github.NikkyAi.Fuel", name: "fuel-coroutines", version: "feature~chunked-SNAPSHOT") + shade(group: 'com.github.kittinunf.result', name: 'result', version: resultVersion) + + shade(group: "org.jetbrains.kotlinx", name: "kotlinx-coroutines-core", version: "1.0.0") + shade(group: "org.jetbrains.kotlinx", name: "kotlinx-serialization-runtime", version: "0.9.0") shade group: 'com.github.kittinunf.fuel', name: 'fuel', version: fuelVersion shade group: 'com.github.kittinunf.result', name: 'result', version: resultVersion diff --git a/1.7.10/src/main/kotlin/matterlink/EventHandler.kt b/1.7.10/src/main/kotlin/matterlink/EventHandler.kt index a08c46b..afd58d0 100644 --- a/1.7.10/src/main/kotlin/matterlink/EventHandler.kt +++ b/1.7.10/src/main/kotlin/matterlink/EventHandler.kt @@ -3,9 +3,14 @@ package matterlink import cpw.mods.fml.common.eventhandler.SubscribeEvent import cpw.mods.fml.common.gameevent.PlayerEvent import cpw.mods.fml.common.gameevent.TickEvent -import matterlink.api.ApiMessage.Companion.USER_ACTION +import kotlinx.coroutines.runBlocking import matterlink.config.cfg -import matterlink.handlers.* +import matterlink.handlers.ChatEvent +import matterlink.handlers.ChatProcessor +import matterlink.handlers.DeathHandler +import matterlink.handlers.JoinLeaveHandler +import matterlink.handlers.ProgressHandler +import matterlink.handlers.TickHandler import net.minecraft.command.server.CommandBroadcast import net.minecraft.command.server.CommandEmote import net.minecraft.entity.Entity @@ -22,45 +27,45 @@ object EventHandler { //MC-VERSION & FORGE DEPENDENT @SubscribeEvent - fun progressEvent(e: AchievementEvent) { + fun progressEvent(e: AchievementEvent) = runBlocking { val achievement = e.achievement - val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return + val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return@runBlocking val statFile = entityPlayer.statFile if (!statFile.canUnlockAchievement(achievement) || statFile.hasAchievementUnlocked(achievement)) { - return + return@runBlocking } ProgressHandler.handleProgress( - name = e.entityPlayer.displayName, - message = "has earned the achievement", - display = e.achievement.statName.unformattedText, - x = e.entityPlayer.posX.toInt(), - y = e.entityPlayer.posY.toInt(), - z = e.entityPlayer.posZ.toInt(), - dimension = e.entityPlayer.dimension + name = e.entityPlayer.displayName, + message = "has earned the achievement", + display = e.achievement.statName.unformattedText, + x = e.entityPlayer.posX.toInt(), + y = e.entityPlayer.posY.toInt(), + z = e.entityPlayer.posZ.toInt(), + dimension = e.entityPlayer.dimension ) } //FORGE-DEPENDENT @SubscribeEvent - fun chatEvent(e: ServerChatEvent) { - if(e.isCanceled) return + fun chatEvent(e: ServerChatEvent) = runBlocking { + if (e.isCanceled) return@runBlocking e.isCanceled = ChatProcessor.sendToBridge( - user = e.player.displayName, - msg = e.message, - x = e.player.posX.toInt(), - y = e.player.posY.toInt(), - z = e.player.posZ.toInt(), - dimension = e.player.dimension, - event = ChatEvent.PLAIN, - uuid = e.player.gameProfile.id + user = e.player.displayName, + msg = e.message, + x = e.player.posX.toInt(), + y = e.player.posY.toInt(), + z = e.player.posZ.toInt(), + dimension = e.player.dimension, + event = ChatEvent.PLAIN, + uuid = e.player.gameProfile.id ) } //FORGE-DEPENDENT @SubscribeEvent - fun commandEvent(e: CommandEvent) { + fun commandEvent(e: CommandEvent) = runBlocking { val sender = when { e.sender is DedicatedServer -> cfg.outgoing.systemUser else -> e.sender.commandSenderName @@ -70,72 +75,73 @@ object EventHandler { when { this is CommandEmote || commandName.equals("me", true) -> ChatEvent.ACTION this is CommandBroadcast || commandName.equals("say", true) -> ChatEvent.BROADCAST - else -> return + else -> return@runBlocking } } val s = e.sender - val (x, y, z) = when(s) { + val (x, y, z) = when (s) { is Entity -> Triple(s.posX.toInt(), s.posY.toInt(), s.posZ.toInt()) else -> with(s.commandSenderPosition) { Triple(posX, posY, posZ) } } ChatProcessor.sendToBridge( - user = sender, - msg = args, - event = type, - x = x, - y = y, - z = z, - dimension = when { - e.sender is DedicatedServer -> null - else -> e.sender.entityWorld.provider.dimensionId - } + user = sender, + msg = args, + event = type, + x = x, + y = y, + z = z, + dimension = when { + e.sender is DedicatedServer -> null + else -> e.sender.entityWorld.provider.dimensionId + } ) } //FORGE-DEPENDENT @SubscribeEvent - fun deathEvent(e: LivingDeathEvent) { + fun deathEvent(e: LivingDeathEvent) = runBlocking { if (e.entityLiving is EntityPlayer) { val player = e.entityLiving as EntityPlayer DeathHandler.handleDeath( - player = player.displayName, - deathMessage = e.entityLiving.combatTracker.func_151521_b().unformattedText, - damageType = e.source.damageType, - x = e.entityLiving.posX.toInt(), - y = e.entityLiving.posY.toInt(), - z = e.entityLiving.posZ.toInt(), - dimension = e.entityLiving.dimension + player = player.displayName, + deathMessage = e.entityLiving.combatTracker.func_151521_b().unformattedText, + damageType = e.source.damageType, + x = e.entityLiving.posX.toInt(), + y = e.entityLiving.posY.toInt(), + z = e.entityLiving.posZ.toInt(), + dimension = e.entityLiving.dimension ) } } //FORGE-DEPENDENT @SubscribeEvent - fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) { + fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) = runBlocking { JoinLeaveHandler.handleJoin( - player = e.player.displayName, - x = e.player.posX.toInt(), - y = e.player.posY.toInt(), - z = e.player.posZ.toInt(), - dimension = e.player.dimension) - } - - //FORGE-DEPENDENT - @SubscribeEvent - fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) { - JoinLeaveHandler.handleLeave( - player = e.player.displayName, - x = e.player.posX.toInt(), - y = e.player.posY.toInt(), - z = e.player.posZ.toInt(), - dimension = e.player.dimension + player = e.player.displayName, + x = e.player.posX.toInt(), + y = e.player.posY.toInt(), + z = e.player.posZ.toInt(), + dimension = e.player.dimension ) } //FORGE-DEPENDENT @SubscribeEvent - fun serverTickEvent(e: TickEvent.ServerTickEvent) { + fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) = runBlocking { + JoinLeaveHandler.handleLeave( + player = e.player.displayName, + x = e.player.posX.toInt(), + y = e.player.posY.toInt(), + z = e.player.posZ.toInt(), + dimension = e.player.dimension + ) + } + + //FORGE-DEPENDENT + @SubscribeEvent + fun serverTickEvent(e: TickEvent.ServerTickEvent) = runBlocking { if (e.phase == TickEvent.Phase.END) TickHandler.handleTick() } diff --git a/1.7.10/src/main/kotlin/matterlink/MatterLink.kt b/1.7.10/src/main/kotlin/matterlink/MatterLink.kt index e971342..358e02b 100644 --- a/1.7.10/src/main/kotlin/matterlink/MatterLink.kt +++ b/1.7.10/src/main/kotlin/matterlink/MatterLink.kt @@ -7,6 +7,7 @@ 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 kotlinx.coroutines.runBlocking import matterlink.bridge.command.IBridgeCommand import matterlink.command.AuthCommand import matterlink.command.MatterLinkCommand @@ -18,13 +19,13 @@ import net.minecraft.server.MinecraftServer import net.minecraft.util.ChatComponentText import net.minecraftforge.common.ForgeVersion import net.minecraftforge.common.MinecraftForge -import java.util.* +import java.util.UUID @Mod( - modid = MODID, - name = NAME, version = MODVERSION, - useMetadata = true, - acceptableRemoteVersions = "*" + modid = MODID, + name = NAME, version = MODVERSION, + useMetadata = true, + acceptableRemoteVersions = "*" ) class MatterLink : IMatterLink() { init { @@ -57,7 +58,7 @@ class MatterLink : IMatterLink() { } @Mod.EventHandler - fun serverStarting(event: FMLServerStartingEvent) { + fun serverStarting(event: FMLServerStartingEvent) = runBlocking { logger.debug("Registering server commands") event.registerServerCommand(MatterLinkCommand) event.registerServerCommand(AuthCommand) @@ -65,7 +66,7 @@ class MatterLink : IMatterLink() { } @Mod.EventHandler - fun serverStopping(event: FMLServerStoppingEvent) { + fun serverStopping(event: FMLServerStoppingEvent) = runBlocking { stop() } @@ -99,10 +100,11 @@ class MatterLink : IMatterLink() { } override fun isOnline(username: String) = (FMLCommonHandler.instance() - .minecraftServerInstance.configurationManager.getPlayerByUsername(username) ?: null) != null + .minecraftServerInstance.configurationManager.getPlayerByUsername(username) ?: null) != null private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? { - return FMLCommonHandler.instance().minecraftServerInstance.configurationManager.createPlayerForUser(gameProfile) + return FMLCommonHandler.instance() + .minecraftServerInstance.configurationManager.getPlayerByUsername(gameProfile.name) } private fun profileByUUID(uuid: UUID): GameProfile? = try { @@ -121,11 +123,11 @@ class MatterLink : IMatterLink() { override fun collectPlayers(area: Area): Set { val players = MinecraftServer.getServer().configurationManager.playerEntityList - .map { it as EntityPlayerMP } - .filter { - (area.allDimensions || area.dimensions.contains(it.dimension)) - && area.testInBounds(it.posX.toInt(), it.posY.toInt(), it.posZ.toInt()) - } + .map { it as EntityPlayerMP } + .filter { + (area.allDimensions || area.dimensions.contains(it.dimension)) + && area.testInBounds(it.posX.toInt(), it.posY.toInt(), it.posZ.toInt()) + } return players.map { it.uniqueID }.toSet() } @@ -134,9 +136,9 @@ class MatterLink : IMatterLink() { override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name override fun commandSenderFor( - user: String, - env: IBridgeCommand.CommandEnvironment, - op: Boolean + user: String, + env: IBridgeCommand.CommandEnvironment, + op: Boolean ) = MatterLinkCommandSender(user, env, op) override val mcVersion: String = MCVERSION diff --git a/1.7.10/src/main/kotlin/matterlink/command/MatterLinkCommand.kt b/1.7.10/src/main/kotlin/matterlink/command/MatterLinkCommand.kt index d6fd6a6..77a5775 100644 --- a/1.7.10/src/main/kotlin/matterlink/command/MatterLinkCommand.kt +++ b/1.7.10/src/main/kotlin/matterlink/command/MatterLinkCommand.kt @@ -1,5 +1,6 @@ package matterlink.command +import kotlinx.coroutines.runBlocking import net.minecraft.command.CommandBase import net.minecraft.command.ICommandSender import net.minecraft.command.WrongUsageException @@ -20,9 +21,9 @@ object MatterLinkCommand : CommandBase() { return CommandCoreML.aliases } - override fun processCommand(sender: ICommandSender, args: Array) { + override fun processCommand(sender: ICommandSender, args: Array) = runBlocking { if (args.isEmpty()) { - throw WrongUsageException("Invalid command! Valid uses: ${this.getCommandUsage(sender)}") + throw WrongUsageException("Invalid command! Valid uses: ${getCommandUsage(sender)}") } val uuid = (sender as? EntityPlayer)?.uniqueID?.toString() diff --git a/1.7.10/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt b/1.7.10/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt index 564e697..fa95507 100644 --- a/1.7.10/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt +++ b/1.7.10/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt @@ -1,5 +1,6 @@ package matterlink.command +import kotlinx.coroutines.runBlocking import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IMinecraftCommandSender import net.minecraft.command.ICommandSender @@ -10,14 +11,15 @@ import net.minecraft.util.IChatComponent import net.minecraft.world.World class MatterLinkCommandSender( - user: String, - env: IBridgeCommand.CommandEnvironment, - op: Boolean) : IMinecraftCommandSender(user, env, op), ICommandSender { + user: String, + env: IBridgeCommand.CommandEnvironment, + op: Boolean +) : IMinecraftCommandSender(user, env, op), ICommandSender { - override fun execute(cmdString: String): Boolean { - return 0 < MinecraftServer.getServer().commandManager.executeCommand( - this, - cmdString + override fun execute(cmdString: String): Boolean = runBlocking { + return@runBlocking 0 < MinecraftServer.getServer().commandManager.executeCommand( + this@MatterLinkCommandSender, + cmdString ).apply { sendReply(cmdString) } diff --git a/1.9.4/src/main/kotlin/matterlink/EventHandler.kt b/1.9.4/src/main/kotlin/matterlink/EventHandler.kt index dd012ab..0bb6a79 100644 --- a/1.9.4/src/main/kotlin/matterlink/EventHandler.kt +++ b/1.9.4/src/main/kotlin/matterlink/EventHandler.kt @@ -1,7 +1,13 @@ package matterlink +import kotlinx.coroutines.runBlocking import matterlink.config.cfg -import matterlink.handlers.* +import matterlink.handlers.ChatEvent +import matterlink.handlers.ChatProcessor +import matterlink.handlers.DeathHandler +import matterlink.handlers.JoinLeaveHandler +import matterlink.handlers.ProgressHandler +import matterlink.handlers.TickHandler import net.minecraft.command.server.CommandBroadcast import net.minecraft.command.server.CommandEmote import net.minecraft.entity.player.EntityPlayer @@ -20,44 +26,44 @@ object EventHandler { //MC-VERSION & FORGE DEPENDENT @SubscribeEvent - fun progressEvent(e: AchievementEvent) { + fun progressEvent(e: AchievementEvent) = runBlocking { val achievement = e.achievement - val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return + val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return@runBlocking val statFile = entityPlayer.statFile if (!statFile.canUnlockAchievement(achievement) || statFile.hasAchievementUnlocked(achievement)) { - return + return@runBlocking } ProgressHandler.handleProgress( - name = e.entityPlayer.displayName.unformattedText, - message = "has earned the achievement", - display = e.achievement.statName.unformattedText, - x = e.entityPlayer.posX.toInt(), - y = e.entityPlayer.posY.toInt(), - z = e.entityPlayer.posZ.toInt(), - dimension = e.entityPlayer.dimension + name = e.entityPlayer.displayName.unformattedText, + message = "has earned the achievement", + display = e.achievement.statName.unformattedText, + x = e.entityPlayer.posX.toInt(), + y = e.entityPlayer.posY.toInt(), + z = e.entityPlayer.posZ.toInt(), + dimension = e.entityPlayer.dimension ) } //FORGE-DEPENDENT @SubscribeEvent - fun chatEvent(e: ServerChatEvent) { - if(e.isCanceled) return + fun chatEvent(e: ServerChatEvent) = runBlocking { + if (e.isCanceled) return@runBlocking e.isCanceled = ChatProcessor.sendToBridge( - user = e.player.displayName.unformattedText, - msg = e.message, - x = e.player.posX.toInt(), - y = e.player.posY.toInt(), - z = e.player.posZ.toInt(), - dimension = e.player.dimension, - event = ChatEvent.PLAIN, - uuid = e.player.gameProfile.id + user = e.player.displayName.unformattedText, + msg = e.message, + x = e.player.posX.toInt(), + y = e.player.posY.toInt(), + z = e.player.posZ.toInt(), + dimension = e.player.dimension, + event = ChatEvent.PLAIN, + uuid = e.player.gameProfile.id ) } //FORGE-DEPENDENT @SubscribeEvent - fun commandEvent(e: CommandEvent) { + fun commandEvent(e: CommandEvent) = runBlocking { val sender = when { e.sender is DedicatedServer -> cfg.outgoing.systemUser else -> e.sender.displayName.unformattedText @@ -67,66 +73,66 @@ object EventHandler { when { this is CommandEmote || commandName.equals("me", true) -> ChatEvent.ACTION this is CommandBroadcast || commandName.equals("say", true) -> ChatEvent.BROADCAST - else -> return + else -> return@runBlocking } } ChatProcessor.sendToBridge( - user = sender, - msg = args, - event = type, - x = e.sender.position.x, - y = e.sender.position.y, - z = e.sender.position.z, - dimension = when { - e.sender is DedicatedServer -> null - else -> e.sender.commandSenderEntity?.dimension ?: e.sender.entityWorld.provider.dimension - } + user = sender, + msg = args, + event = type, + x = e.sender.position.x, + y = e.sender.position.y, + z = e.sender.position.z, + dimension = when { + e.sender is DedicatedServer -> null + else -> e.sender.commandSenderEntity?.dimension ?: e.sender.entityWorld.provider.dimension + } ) } //FORGE-DEPENDENT @SubscribeEvent - fun deathEvent(e: LivingDeathEvent) { + fun deathEvent(e: LivingDeathEvent) = runBlocking { if (e.entityLiving is EntityPlayer) { DeathHandler.handleDeath( - player = e.entityLiving.displayName.unformattedText, - deathMessage = e.entityLiving.combatTracker.deathMessage.unformattedText, - damageType = e.source.damageType, - x = e.entityLiving.posX.toInt(), - y = e.entityLiving.posY.toInt(), - z = e.entityLiving.posZ.toInt(), - dimension = e.entityLiving.dimension + player = e.entityLiving.displayName.unformattedText, + deathMessage = e.entityLiving.combatTracker.deathMessage.unformattedText, + damageType = e.source.damageType, + x = e.entityLiving.posX.toInt(), + y = e.entityLiving.posY.toInt(), + z = e.entityLiving.posZ.toInt(), + dimension = e.entityLiving.dimension ) } } //FORGE-DEPENDENT @SubscribeEvent - fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) { + fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) = runBlocking { JoinLeaveHandler.handleJoin( - player = e.player.displayName.unformattedText, - x = e.player.posX.toInt(), - y = e.player.posY.toInt(), - z = e.player.posZ.toInt(), - dimension = e.player.dimension + player = e.player.displayName.unformattedText, + x = e.player.posX.toInt(), + y = e.player.posY.toInt(), + z = e.player.posZ.toInt(), + dimension = e.player.dimension ) } //FORGE-DEPENDENT @SubscribeEvent - fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) { + fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) = runBlocking { JoinLeaveHandler.handleLeave( - player = e.player.displayName.unformattedText, - x = e.player.posX.toInt(), - y = e.player.posY.toInt(), - z = e.player.posZ.toInt(), - dimension = e.player.dimension + player = e.player.displayName.unformattedText, + x = e.player.posX.toInt(), + y = e.player.posY.toInt(), + z = e.player.posZ.toInt(), + dimension = e.player.dimension ) } //FORGE-DEPENDENT @SubscribeEvent - fun serverTickEvent(e: TickEvent.ServerTickEvent) { + fun serverTickEvent(e: TickEvent.ServerTickEvent) = runBlocking { if (e.phase == TickEvent.Phase.END) TickHandler.handleTick() } diff --git a/1.9.4/src/main/kotlin/matterlink/MatterLink.kt b/1.9.4/src/main/kotlin/matterlink/MatterLink.kt index b6a8d24..61273a2 100644 --- a/1.9.4/src/main/kotlin/matterlink/MatterLink.kt +++ b/1.9.4/src/main/kotlin/matterlink/MatterLink.kt @@ -1,6 +1,7 @@ package matterlink import com.mojang.authlib.GameProfile +import kotlinx.coroutines.runBlocking import matterlink.bridge.command.IBridgeCommand import matterlink.command.AuthCommand import matterlink.command.MatterLinkCommand @@ -8,7 +9,6 @@ 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.text.TextComponentString import net.minecraftforge.common.ForgeVersion import net.minecraftforge.common.MinecraftForge @@ -18,16 +18,16 @@ import net.minecraftforge.fml.common.event.FMLInitializationEvent import net.minecraftforge.fml.common.event.FMLPreInitializationEvent import net.minecraftforge.fml.common.event.FMLServerStartingEvent import net.minecraftforge.fml.common.event.FMLServerStoppingEvent -import java.util.* +import java.util.UUID @Mod( - modid = MODID, - name = NAME, version = MODVERSION, - serverSideOnly = true, - useMetadata = true, - acceptableRemoteVersions = "*", - modLanguageAdapter = "net.shadowfacts.forgelin.KotlinAdapter", - dependencies = DEPENDENCIES + modid = MODID, + name = NAME, version = MODVERSION, + serverSideOnly = true, + useMetadata = true, + acceptableRemoteVersions = "*", + modLanguageAdapter = "net.shadowfacts.forgelin.KotlinAdapter", + dependencies = DEPENDENCIES ) object MatterLink : IMatterLink() { init { @@ -58,7 +58,7 @@ object MatterLink : IMatterLink() { } @Mod.EventHandler - fun serverStarting(event: FMLServerStartingEvent) { + fun serverStarting(event: FMLServerStartingEvent) = runBlocking { logger.debug("Registering server commands") event.registerServerCommand(MatterLinkCommand) event.registerServerCommand(AuthCommand) @@ -66,7 +66,7 @@ object MatterLink : IMatterLink() { } @Mod.EventHandler - fun serverStopping(event: FMLServerStoppingEvent) { + fun serverStopping(event: FMLServerStoppingEvent) = runBlocking { stop() } @@ -99,9 +99,11 @@ object MatterLink : IMatterLink() { player.addChatMessage(TextComponentString(msg)) } - override fun isOnline(username: String) = FMLCommonHandler.instance().minecraftServerInstance.playerList.allUsernames.contains(username) + override fun isOnline(username: String) = + FMLCommonHandler.instance().minecraftServerInstance.playerList.allUsernames.contains(username) - private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? = FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id) + private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? = + FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id) private fun profileByUUID(uuid: UUID): GameProfile? = try { @@ -121,11 +123,11 @@ object MatterLink : IMatterLink() { override fun collectPlayers(area: Area): Set { val playerList = FMLCommonHandler.instance().minecraftServerInstance.playerList val players = playerList.allProfiles - .map { playerList.getPlayerByUUID(it.id) } - .filter { - (area.allDimensions || area.dimensions.contains(it.dimension)) - && area.testInBounds(it.posX.toInt(), it.posY.toInt(), it.posZ.toInt()) - } + .map { playerList.getPlayerByUUID(it.id) } + .filter { + (area.allDimensions || area.dimensions.contains(it.dimension)) + && area.testInBounds(it.posX.toInt(), it.posY.toInt(), it.posZ.toInt()) + } return players.map { it.uniqueID }.toSet() } @@ -134,9 +136,9 @@ object MatterLink : IMatterLink() { override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name override fun commandSenderFor( - user: String, - env: IBridgeCommand.CommandEnvironment, - op: Boolean + user: String, + env: IBridgeCommand.CommandEnvironment, + op: Boolean ) = MatterLinkCommandSender(user, env, op) override val mcVersion: String = MCVERSION diff --git a/1.9.4/src/main/kotlin/matterlink/command/MatterLinkCommand.kt b/1.9.4/src/main/kotlin/matterlink/command/MatterLinkCommand.kt index 95de65b..8edf3a8 100644 --- a/1.9.4/src/main/kotlin/matterlink/command/MatterLinkCommand.kt +++ b/1.9.4/src/main/kotlin/matterlink/command/MatterLinkCommand.kt @@ -1,5 +1,6 @@ package matterlink.command +import kotlinx.coroutines.runBlocking import net.minecraft.command.CommandBase import net.minecraft.command.ICommandSender import net.minecraft.command.WrongUsageException @@ -21,9 +22,9 @@ object MatterLinkCommand : CommandBase() { return CommandCoreML.aliases } - override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array) { + override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array) = runBlocking { if (args.isEmpty()) { - throw WrongUsageException("Invalid command! Valid uses: ${this.getCommandUsage(sender)}") + throw WrongUsageException("Invalid command! Valid uses: ${getCommandUsage(sender)}") } val uuid = (sender as? EntityPlayer)?.uniqueID?.toString() diff --git a/1.9.4/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt b/1.9.4/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt index 90e6692..d8660ed 100644 --- a/1.9.4/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt +++ b/1.9.4/src/main/kotlin/matterlink/command/MatterLinkCommandSender.kt @@ -1,5 +1,6 @@ package matterlink.command +import kotlinx.coroutines.runBlocking import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IMinecraftCommandSender import net.minecraft.command.CommandResultStats @@ -15,13 +16,14 @@ import net.minecraftforge.fml.common.FMLCommonHandler import javax.annotation.Nonnull class MatterLinkCommandSender( - user: String, - env: IBridgeCommand.CommandEnvironment, - op: Boolean) : IMinecraftCommandSender(user, env, op), ICommandSender { - override fun execute(cmdString: String): Boolean { - return 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand( - this, - cmdString + user: String, + env: IBridgeCommand.CommandEnvironment, + op: Boolean +) : IMinecraftCommandSender(user, env, op), ICommandSender { + override fun execute(cmdString: String): Boolean = runBlocking { + return@runBlocking 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand( + this@MatterLinkCommandSender, + cmdString ).apply { sendReply(cmdString) } diff --git a/Jankson b/Jankson index 091281a..7d27c28 160000 --- a/Jankson +++ b/Jankson @@ -1 +1 @@ -Subproject commit 091281a436b6b3807484aec2236641716d1a94ed +Subproject commit 7d27c28784bacba17450faa9e723ca6b6eb39602 diff --git a/TODO.MD b/TODO.MD new file mode 100644 index 0000000..a377c2c --- /dev/null +++ b/TODO.MD @@ -0,0 +1,14 @@ +# Replacement for guava + +# using Fuel everywhere to simplify code + + +# Adittional player values + + +add optional feature: +GET json dictionary of player values like TEAM, NICK, TOWN +from url based on uuid / playername + +config input: `http://rest.wurmcraft.com/user/find/{UUID}` +url: `http://rest.wurmcraft.com/user/find/148cf139-dd14-4bf4-97a2-08305dfef0a9` \ No newline at end of file diff --git a/build.gradle b/build.gradle index 246a232..116b6f4 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,7 @@ buildscript { } dependencies { classpath group: "org.jetbrains.kotlin", name: "kotlin-gradle-plugin", version: kotlinVersion + classpath group: "org.jetbrains.kotlin", name: "kotlin-serialization", version: kotlinVersion } } @@ -14,6 +15,7 @@ plugins { subprojects { apply plugin: "kotlin" + apply plugin: "kotlinx-serialization" apply plugin: "idea" if (System.env.BUILD_NUMBER) { @@ -35,24 +37,28 @@ subprojects { } repositories { jcenter() + mavenCentral() maven { name = "bintray" - url = 'http://jcenter.bintray.com' + url = "http://jcenter.bintray.com" } maven { - name = "elytradev" - url = 'http://unascribed.com/maven/releases' - url = 'https://repo.elytradev.com' + name = "jitpack" + url = "https://jitpack.io" } + maven { + name = "kotlinx" + url = "https://kotlin.bintray.com/kotlinx/" + } +// maven { +// name = "elytradev" +// // url = 'http://unascribed.com/maven/releases' +// url = "https://repo.elytradev.com" +// } maven { name = "shadowfacts" url = "http://maven.shadowfacts.net/" } - ivy { - //Resolves baubles and jankson - name = "endless.blue dependency mirror" - artifactPattern "https://endless.blue/files/ivy/[module]-[revision].[ext]" - } } compileKotlin { kotlinOptions { diff --git a/buildSrc/src/main/kotlin/Constants.kt b/buildSrc/src/main/kotlin/Constants.kt new file mode 100644 index 0000000..1f04d90 --- /dev/null +++ b/buildSrc/src/main/kotlin/Constants.kt @@ -0,0 +1,11 @@ +object MatterLink { + const val version = "1.6.5" +} + +object Kotlin { + const val version = "1.3.0" +} + +object Forgelin { + const val version = "1.8.0" +} \ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle index 1cb1c9d..66bb3e8 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -18,15 +18,20 @@ sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse t dependencies { compile project(":Jankson") - shadow (project(':Jankson')) { transitive = false } + shadow(project(':Jankson')) { transitive = false } compile group: 'com.google.code.gson', name: 'gson', version: '+' compile group: 'com.google.guava', name: 'guava', version: '+' - compile group: 'com.github.kittinunf.fuel', name: 'fuel', version: fuelVersion - shadow (group: 'com.github.kittinunf.fuel', name: 'fuel', version: fuelVersion) { transitive = false } - compile group: 'com.github.kittinunf.result', name: 'result', version: resultVersion - shadow (group: 'com.github.kittinunf.result', name: 'result', version: resultVersion) { transitive = false } + compile(group: "com.github.NikkyAi.Fuel", name: "fuel", version: "feature~chunked-SNAPSHOT") + shadow(group: "com.github.NikkyAi.Fuel", name: "fuel", version: "feature~chunked-SNAPSHOT") { transitive = false } + compile(group: "com.github.NikkyAi.Fuel", name: "fuel-coroutines", version: "feature~chunked-SNAPSHOT") + shadow(group: "com.github.NikkyAi.Fuel", name: "fuel-coroutines", version: "feature~chunked-SNAPSHOT") { transitive = false } + compile(group: 'com.github.kittinunf.result', name: 'result', version: resultVersion) + shadow(group: 'com.github.kittinunf.result', name: 'result', version: resultVersion) { transitive = false } + + compile(group: "org.jetbrains.kotlinx", name: "kotlinx-coroutines-core", version: "1.0.0") + compile(group: "org.jetbrains.kotlinx", name: "kotlinx-serialization-runtime", version: "0.9.0") compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion } diff --git a/core/src/main/kotlin/matterlink/Area.kt b/core/src/main/kotlin/matterlink/Area.kt index d24acca..c4bb213 100644 --- a/core/src/main/kotlin/matterlink/Area.kt +++ b/core/src/main/kotlin/matterlink/Area.kt @@ -16,7 +16,7 @@ sealed class Area { fun testForDim(dimension: Int?): Boolean { if (allDimensions) return true - if(dimension == null) return false + if (dimension == null) return false return dimensions.contains(dimension) } @@ -35,8 +35,8 @@ sealed class Area { } data class Infinite( - override val dimensions: List = listOf(), - override val allDimensions: Boolean = false + override val dimensions: List = listOf(), + override val allDimensions: Boolean = false ) : Area() { override val type = "INFINITE" @@ -47,8 +47,8 @@ sealed class Area { companion object { fun parse(jsonObj: JsonObject): Area { return Infinite( - dimensions = jsonObj.parseDimensions(), - allDimensions = jsonObj.parseAllDimensions() + dimensions = jsonObj.parseDimensions(), + allDimensions = jsonObj.parseAllDimensions() ) } } @@ -56,11 +56,11 @@ sealed class Area { } data class Radius( - override val dimensions: List = listOf(), - override val allDimensions: Boolean = false, - val x: Int, - val z: Int, - val radius: Int? + override val dimensions: List = listOf(), + override val allDimensions: Boolean = false, + val x: Int, + val z: Int, + val radius: Int? ) : Area() { override val type = "RADIUS" @@ -73,56 +73,56 @@ sealed class Area { fun parse(jsonObj: JsonObject): Area { return Radius( - dimensions = jsonObj.parseDimensions(), - allDimensions = jsonObj.parseAllDimensions(), - x = jsonObj.getOrDefault("x", 0), - z = jsonObj.getOrDefault("z", 0), - radius = jsonObj.getReified("radius") + dimensions = jsonObj.parseDimensions(), + allDimensions = jsonObj.parseAllDimensions(), + x = jsonObj.getOrDefault("x", 0), + z = jsonObj.getOrDefault("z", 0), + radius = jsonObj.getReified("radius") ) } } } - class Sphere ( - override val dimensions: List = listOf(), - override val allDimensions: Boolean = false, - val x: Int, - val y: Int, - val z: Int, - val radius: Int? = null - ): Area() { + class Sphere( + override val dimensions: List = listOf(), + override val allDimensions: Boolean = false, + val x: Int, + val y: Int, + val z: Int, + val radius: Int? = null + ) : Area() { override val type = "SPHERE" override fun testInBounds(x: Int, y: Int, z: Int): Boolean { if (radius == null) return true - return sqrt(((this.x - x) * (this.x - x)) +((this.y - y) * (this.y - y)) + ((this.z - z) * (this.z - z)).toFloat()) < this.radius + return sqrt(((this.x - x) * (this.x - x)) + ((this.y - y) * (this.y - y)) + ((this.z - z) * (this.z - z)).toFloat()) < this.radius } companion object { fun parse(jsonObj: JsonObject): Area { return Sphere( - dimensions = jsonObj.parseDimensions(), - allDimensions = jsonObj.parseAllDimensions(), - x = jsonObj.getOrDefault("x", 0), - y = jsonObj.getOrDefault("y", 0), - z = jsonObj.getOrDefault("z", 0), - radius = jsonObj.getReified("radius") + dimensions = jsonObj.parseDimensions(), + allDimensions = jsonObj.parseAllDimensions(), + x = jsonObj.getOrDefault("x", 0), + y = jsonObj.getOrDefault("y", 0), + z = jsonObj.getOrDefault("z", 0), + radius = jsonObj.getReified("radius") ) } } } - class Box ( - override val dimensions: List = listOf(), - override val allDimensions: Boolean = false, - val x1: Int, - val x2: Int, - val y1: Int, - val y2: Int, - val z1: Int, - val z2: Int - ): Area() { + class Box( + override val dimensions: List = listOf(), + override val allDimensions: Boolean = false, + val x1: Int, + val x2: Int, + val y1: Int, + val y2: Int, + val z1: Int, + val z2: Int + ) : Area() { override val type = "BOX" override fun testInBounds(x: Int, y: Int, z: Int): Boolean { @@ -133,42 +133,43 @@ sealed class Area { fun parse(jsonObj: JsonObject): Area { return Box( - dimensions = jsonObj.parseDimensions(), - allDimensions = jsonObj.parseAllDimensions(), - x1 = jsonObj.getOrDefault("x1", 0), - x2 = jsonObj.getOrDefault("x2", 0), - y1 = jsonObj.getOrDefault("y1", 0), - y2 = jsonObj.getOrDefault("y2", 0), - z1 = jsonObj.getOrDefault("z1", 0), - z2 = jsonObj.getOrDefault("z2", 0) + dimensions = jsonObj.parseDimensions(), + allDimensions = jsonObj.parseAllDimensions(), + x1 = jsonObj.getOrDefault("x1", 0), + x2 = jsonObj.getOrDefault("x2", 0), + y1 = jsonObj.getOrDefault("y1", 0), + y2 = jsonObj.getOrDefault("y2", 0), + z1 = jsonObj.getOrDefault("z1", 0), + z2 = jsonObj.getOrDefault("z2", 0) ) } } } - class Square ( - override val dimensions: List = listOf(), - override val allDimensions: Boolean = false, - val x1: Int, - val x2: Int, - val z1: Int, - val z2: Int - ): Area() { + class Square( + override val dimensions: List = listOf(), + override val allDimensions: Boolean = false, + val x1: Int, + val x2: Int, + val z1: Int, + val z2: Int + ) : Area() { override val type = "SQUARE" override fun testInBounds(x: Int, y: Int, z: Int): Boolean { return x in x1..x2 && z in z1..z2 } + companion object { fun parse(jsonObj: JsonObject): Area { return Square( - dimensions = jsonObj.parseDimensions(), - allDimensions = jsonObj.parseAllDimensions(), - x1 = jsonObj.getOrDefault("x1", 0), - x2 = jsonObj.getOrDefault("x2", 0), - z1 = jsonObj.getOrDefault("z1", 0), - z2 = jsonObj.getOrDefault("z2", 0) + dimensions = jsonObj.parseDimensions(), + allDimensions = jsonObj.parseAllDimensions(), + x1 = jsonObj.getOrDefault("x1", 0), + x2 = jsonObj.getOrDefault("x2", 0), + z1 = jsonObj.getOrDefault("z1", 0), + z2 = jsonObj.getOrDefault("z2", 0) ) } } diff --git a/core/src/main/kotlin/matterlink/IMatterLink.kt b/core/src/main/kotlin/matterlink/IMatterLink.kt index f93bd7b..a4f1fba 100644 --- a/core/src/main/kotlin/matterlink/IMatterLink.kt +++ b/core/src/main/kotlin/matterlink/IMatterLink.kt @@ -4,10 +4,9 @@ import matterlink.bridge.MessageHandlerInst import matterlink.bridge.command.BridgeCommandRegistry import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IMinecraftCommandSender -import matterlink.config.BaseConfig import matterlink.config.cfg import matterlink.update.UpdateChecker -import java.util.* +import java.util.UUID lateinit var logger: Logger @@ -19,7 +18,11 @@ abstract class IMatterLink { abstract val buildNumber: Int abstract val forgeVersion: String - abstract fun commandSenderFor(user: String, env: IBridgeCommand.CommandEnvironment, op: Boolean): IMinecraftCommandSender + abstract fun commandSenderFor( + user: String, + env: IBridgeCommand.CommandEnvironment, + op: Boolean + ): IMinecraftCommandSender abstract fun wrappedSendToPlayers(msg: String) @@ -29,50 +32,19 @@ abstract class IMatterLink { abstract fun nameToUUID(username: String): UUID? abstract fun uuidToName(uuid: UUID): String? - init { - - } - - fun start() { -// MessageHandlerInst.logger = { level, msg -> -// when (level) { -// "FATAL" -> logger.fatal(msg) -// "ERROR" -> logger.error(msg) -// "WARN" -> logger.warn(msg) -// "INFO" -> logger.info(msg) -// "DEBUG" -> logger.debug(msg) -// "TRACE" -> logger.trace(msg) -// } -// } + suspend fun start() { MessageHandlerInst.logger = logger serverStartTime = System.currentTimeMillis() if (cfg.connect.autoConnect) MessageHandlerInst.start("Server started, connecting to matterbridge API", true) - UpdateChecker.run() + UpdateChecker.check() } - fun stop() { + suspend fun stop() { MessageHandlerInst.stop("Server shutting down, disconnecting from matterbridge API") } -// abstract fun log(level: String, formatString: String, vararg data: Any) - -// fun fatal(formatString: String, vararg data: Any) = log("FATAL", formatString, *data) -// fun error(formatString: String, vararg data: Any) = log("ERROR", formatString, *data) -// fun warn(formatString: String, vararg data: Any) = log("WARN", formatString, *data) -// fun info(formatString: String, vararg data: Any) = log("INFO", formatString, *data) -// -// fun debug(formatString: String, vararg data: Any) { -// if (cfg.debug.logLevel == "DEBUG" || cfg.debug.logLevel == "TRACE") -// log("INFO", "DEBUG: " + formatString.replace("\n", "\nDEBUG: "), *data) -// } -// -// fun trace(formatString: String, vararg data: Any) { -// if (cfg.debug.logLevel == "TRACE") -// log("INFO", "TRACE: " + formatString.replace("\n", "\nTRACE: "), *data) -// } - /** * in milliseconds */ diff --git a/core/src/main/kotlin/matterlink/PasteUtil.kt b/core/src/main/kotlin/matterlink/PasteUtil.kt index 852f75d..cad1243 100644 --- a/core/src/main/kotlin/matterlink/PasteUtil.kt +++ b/core/src/main/kotlin/matterlink/PasteUtil.kt @@ -1,8 +1,6 @@ package matterlink import blue.endless.jankson.Jankson -import blue.endless.jankson.JsonObject -import blue.endless.jankson.impl.Marshaller import java.net.HttpURLConnection import java.net.URL @@ -13,34 +11,34 @@ import java.net.URL */ data class Paste( - val encrypted: Boolean = false, - val description: String, - val sections: List + val encrypted: Boolean = false, + val description: String, + val sections: List ) data class PasteSection( - val name: String, - val syntax: String = "text", - val contents: String + val name: String, + val syntax: String = "text", + val contents: String ) data class PasteResponse( - val id: String, - val link: String + val id: String, + val link: String ) object PasteUtil { private const val DEFAULT_KEY = "uKJoyicVJFnmpnrIZMklOURWxrCKXYaiBWOzPmvon" private val jankson = Jankson.builder() - .registerTypeAdapter { - PasteResponse( - id = it.getReified("id") ?: "", - link = it.getReified("link") - ?.replace("\\/", "/") - ?: "invalid" - ) - } + .registerTypeAdapter { + PasteResponse( + id = it.getReified("id") ?: "", + link = it.getReified("link") + ?.replace("\\/", "/") + ?: "invalid" + ) + } // .registerSerializer { paste: Paste, marshaller: Marshaller -> // JsonObject().apply { // with(paste) { @@ -62,7 +60,7 @@ object PasteUtil { // } // } // } - .build() + .build() fun paste(paste: Paste, key: String = ""): PasteResponse { val apiKey = key.takeIf { it.isNotBlank() } ?: DEFAULT_KEY @@ -73,8 +71,8 @@ object PasteUtil { http.doOutput = true val out = jankson.toJson(paste) - .toJson(false, false) - .toByteArray() + .toJson(false, false) + .toByteArray() http.setFixedLengthStreamingMode(out.size) http.setRequestProperty("Content-Type", "application/json; charset=UTF-8") diff --git a/core/src/main/kotlin/matterlink/Util.kt b/core/src/main/kotlin/matterlink/Util.kt index 6d4b65f..5396957 100644 --- a/core/src/main/kotlin/matterlink/Util.kt +++ b/core/src/main/kotlin/matterlink/Util.kt @@ -8,9 +8,7 @@ import blue.endless.jankson.impl.Marshaller import matterlink.config.cfg import java.io.PrintWriter import java.io.StringWriter -import java.lang.Thread.yield import java.util.* -import kotlin.streams.asSequence private const val ZWSP: Char = '\u200b' @@ -64,7 +62,7 @@ val Exception.stackTraceString: String } fun randomString(length: Int = 6): String = - java.util.UUID.randomUUID().toString().replace("-", "").take(length) + java.util.UUID.randomUUID().toString().replace("-", "").take(length) fun JsonObject.getOrDefault(key: String, default: T, comment: String? = null): T { logger.trace("type: ${default.javaClass.name} key: $key json: >>>${this.getObject(key)?.toJson()}<<< default: $default") @@ -76,20 +74,27 @@ fun JsonObject.getOrDefault(key: String, default: T, comment: String? inline fun Jankson.fromJson(obj: JsonObject): T = this.fromJson(obj, T::class.java) inline fun Jankson.fromJson(json: String): T = this.fromJson(json, T::class.java) -inline fun Jankson.Builder.registerTypeAdapter(noinline adapter: (JsonObject) -> T) = this.registerTypeAdapter(T::class.java, adapter) +inline fun Jankson.Builder.registerTypeAdapter(noinline adapter: (JsonObject) -> T) = + this.registerTypeAdapter(T::class.java, adapter) -inline fun Jankson.Builder.registerPrimitiveTypeAdapter(noinline adapter: (Any) -> T) = this.registerPrimitiveTypeAdapter(T::class.java, adapter) +inline fun Jankson.Builder.registerPrimitiveTypeAdapter(noinline adapter: (Any) -> T) = + this.registerPrimitiveTypeAdapter(T::class.java, adapter) -inline fun Jankson.Builder.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = this.registerSerializer(T::class.java, serializer) +inline fun Jankson.Builder.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = + this.registerSerializer(T::class.java, serializer) -inline fun Marshaller.registerSerializer(noinline serializer: (T) -> JsonElement) = this.registerSerializer(T::class.java, serializer) +inline fun Marshaller.registerSerializer(noinline serializer: (T) -> JsonElement) = + this.registerSerializer(T::class.java, serializer) -inline fun Marshaller.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = this.registerSerializer(T::class.java, serializer) +inline fun Marshaller.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = + this.registerSerializer(T::class.java, serializer) -inline fun JsonObject.getReified(key: String, comment: String? = null): T? = this.get(T::class.java, key) +inline fun JsonObject.getReified(key: String, comment: String? = null): T? = + this.get(T::class.java, key) ?.also { setComment(key, comment) } -inline fun JsonObject.getReifiedOrDelete(key: String, comment: String? = null): T? = this.get(T::class.java, key) +inline fun JsonObject.getReifiedOrDelete(key: String, comment: String? = null): T? = + this.get(T::class.java, key) ?.also { setComment(key, comment) } ?: run { this.remove(key) @@ -124,7 +129,11 @@ inline fun JsonObject.getOrPutList(key: String, default: List< } ?: this.putDefault(key, default, comment) ?: default } -inline fun JsonObject.getOrPutMap(key: String, default: Map, comment: String?): Map { +inline fun JsonObject.getOrPutMap( + key: String, + default: Map, + comment: String? +): Map { return this[key]?.let { map -> when (map) { is JsonObject -> { diff --git a/core/src/main/kotlin/matterlink/api/ApiMessage.kt b/core/src/main/kotlin/matterlink/api/ApiMessage.kt index 53ea19b..814713d 100644 --- a/core/src/main/kotlin/matterlink/api/ApiMessage.kt +++ b/core/src/main/kotlin/matterlink/api/ApiMessage.kt @@ -1,7 +1,10 @@ package matterlink.api -import com.google.gson.GsonBuilder -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Encoder +import kotlinx.serialization.Optional +import kotlinx.serialization.Serializable +import kotlinx.serialization.Serializer +import kotlinx.serialization.json.JSON /** * Created by nikky on 07/05/18. @@ -9,84 +12,77 @@ import com.google.gson.annotations.SerializedName * @author Nikky * @version 1.0 */ -class ApiMessage ( - username: String? = null, - text: String? = null, - gateway: String? = null, - channel: String? = null, - userid: String? = null, - avatar: String? = null, - account: String? = null, - protocol: String? = null, - event: String? = null, - id: String? = null +@Serializable +data class ApiMessage( + @Optional var username: String = "", + @Optional var text: String = "", + @Optional var gateway: String = "", + @Optional var timestamp: String = "", + @Optional var channel: String = "", + @Optional var userid: String = "", + @Optional var avatar: String = "", + @Optional var account: String = "", + @Optional var protocol: String = "", + @Optional var event: String = "", + @Optional var id: String = "", + @Optional var Extra: Map? = null ) { - @SerializedName("username") private var _username: String? = username - @SerializedName("text") private var _text: String? = text - @SerializedName("gateway") private var _gateway: String? = gateway - @SerializedName("channel") private var _channel: String? = channel - @SerializedName("userid") private var _userid: String? = userid - @SerializedName("avatar") private var _avatar: String? = avatar - @SerializedName("account") private var _account: String? = account - @SerializedName("protocol") private var _protocol: String? = protocol - @SerializedName("event") private var _event: String? = event - @SerializedName("id") private var _id: String? = id - var username: String - get() = _username ?: "" - set(username) { this._username = username } - - var text: String - get() = _text ?: "" - set(text) { this._text = text } - - var gateway: String - get() = _gateway ?: "" - set(gateway) { this._gateway = gateway } - - var channel: String - get() = _channel ?: "" - set(channel) { this._channel = channel } - - var userid: String - get() = _userid ?: "" - set(userid) { this._userid = userid } - - var avatar: String - get() = _avatar ?: "" - set(avatar) { this._avatar = avatar } - - var account: String - get() = _account ?: "" - set(account) { this._account = account } - - var protocol: String - get() = _protocol ?: "" - set(protocol) { this._protocol = protocol } - - var event: String - get() = _event ?: "" - set(event) { this._event = event } - - var id: String - get() = _id ?: "" - set(id) { this._id = id } - fun encode(): String { - return gson.toJson(this) + return JSON.stringify(Companion, this) } override fun toString(): String = encode() + @Serializer(forClass = ApiMessage::class) companion object { + override fun serialize(output: Encoder, obj: ApiMessage) { + val elemOutput = output.beginStructure(descriptor) + obj.username.takeIf { it.isNotEmpty() }?.let { + elemOutput.encodeStringElement(descriptor, 0, it) + } + obj.text.takeIf { it.isNotEmpty() }?.let { + elemOutput.encodeStringElement(descriptor, 1, it) + } + obj.gateway.takeIf { it.isNotEmpty() }?.let { + elemOutput.encodeStringElement(descriptor, 2, it) + } + obj.timestamp.takeIf { it.isNotEmpty() }?.let { + elemOutput.encodeStringElement(descriptor, 3, it) + } + obj.channel.takeIf { it.isNotEmpty() }?.let { + elemOutput.encodeStringElement(descriptor, 4, it) + } + obj.userid.takeIf { it.isNotEmpty() }?.let { + elemOutput.encodeStringElement(descriptor, 5, it) + } + obj.avatar.takeIf { it.isNotEmpty() }?.let { + elemOutput.encodeStringElement(descriptor, 6, it) + } + obj.account.takeIf { it.isNotEmpty() }?.let { + elemOutput.encodeStringElement(descriptor, 7, it) + } + obj.protocol.takeIf { it.isNotEmpty() }?.let { + elemOutput.encodeStringElement(descriptor, 8, it) + } + obj.event.takeIf { it.isNotEmpty() }?.let { + elemOutput.encodeStringElement(descriptor, 9, it) + } + obj.id.takeIf { it.isNotEmpty() }?.let { + elemOutput.encodeStringElement(descriptor, 10, it) + } +// obj.Extra.takeIf { ! it.isNullOrEmpty() }?.let { +// elemOutput.encodeStringElement(descriptor, 11, it) +// } + elemOutput.endStructure(descriptor) + } + val USER_ACTION = "user_action" val JOIN_LEAVE = "join_leave" - private val gson = GsonBuilder() - .create() fun decode(json: String): ApiMessage { - return gson.fromJson(json, ApiMessage::class.java) + return JSON.parse(Companion, json) } } } diff --git a/core/src/main/kotlin/matterlink/api/Config.kt b/core/src/main/kotlin/matterlink/api/Config.kt index cc9d7fa..0226746 100644 --- a/core/src/main/kotlin/matterlink/api/Config.kt +++ b/core/src/main/kotlin/matterlink/api/Config.kt @@ -1,14 +1,13 @@ package matterlink.api -data class Config ( - var url: String = "", - var token: String = "", - var announceConnect: Boolean = true, - var announceDisconnect: Boolean = true, - var reconnectWait: Long = 500, - var systemUser: String = "Server" -) -{ +data class Config( + var url: String = "", + var token: String = "", + var announceConnect: Boolean = true, + var announceDisconnect: Boolean = true, + var reconnectWait: Long = 500, + var systemUser: String = "Server" +) { fun sync(connection: StreamConnection) { connection.token = token connection.host = url diff --git a/core/src/main/kotlin/matterlink/api/MessageHandler.kt b/core/src/main/kotlin/matterlink/api/MessageHandler.kt index 5f4baf9..1102325 100644 --- a/core/src/main/kotlin/matterlink/api/MessageHandler.kt +++ b/core/src/main/kotlin/matterlink/api/MessageHandler.kt @@ -1,15 +1,31 @@ package matterlink.api +import awaitStringResponse +import com.github.kittinunf.fuel.core.FuelManager +import com.github.kittinunf.fuel.core.Method +import com.github.kittinunf.fuel.core.ResponseDeserializable +import com.github.kittinunf.fuel.httpGet +import com.github.kittinunf.fuel.httpPost +import com.github.kittinunf.result.Result +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.BroadcastChannel +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.channels.actor +import kotlinx.coroutines.channels.broadcast +import kotlinx.coroutines.channels.consumeEach +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.serialization.json.JSON +import kotlinx.serialization.list import matterlink.Logger -import java.io.BufferedReader -import java.io.DataOutputStream -import java.io.IOException -import java.io.InputStreamReader -import java.net.HttpURLConnection -import java.net.MalformedURLException -import java.net.ProtocolException -import java.net.URL -import java.util.concurrent.ConcurrentLinkedQueue +import java.io.Reader +import kotlin.coroutines.CoroutineContext /** * Created by nikky on 07/05/18. @@ -17,66 +33,60 @@ import java.util.concurrent.ConcurrentLinkedQueue * @author Nikky * @version 1.0 */ -open class MessageHandler { +open class MessageHandler : CoroutineScope { + override val coroutineContext: CoroutineContext = Job() private var enabled = false private var connectErrors = 0 - private var reconnectCooldown = 0 + private var reconnectCooldown = 0L private var sendErrors = 0 - var config: Config = Config() - set(value) { - field = value.apply { - sync(streamConnection) - } + + private var sendChannel: SendChannel = senderActor() + + private val messageStream = Channel(Channel.UNLIMITED) + var broadcast: BroadcastChannel = broadcast { + while (true) { + val msg = messageStream.receive() + send(msg) } - - //TODO: make callbacks: onConnect onDisconnect onError etc - - var queue: ConcurrentLinkedQueue = ConcurrentLinkedQueue() + } private set - private var streamConnection: StreamConnection = StreamConnection(queue) - - var logger: Logger - get() = streamConnection.logger - set(l) { - streamConnection.logger = l + private val keepOpenManager = FuelManager().apply { + timeoutInMillisecond = 0 + timeoutReadInMillisecond = 0 } + var config: Config = Config() - private var nextCheck: Long = 0 - - init { - streamConnection.addOnSuccess { success -> - if (success) { - logger.info("connected successfully") - connectErrors = 0 - reconnectCooldown = 0 - } else { - reconnectCooldown = connectErrors - connectErrors++ - logger.error(String.format("connectErrors: %d", connectErrors)) - } - } + var logger = object : Logger { + override fun info(message: String) = println("INFO: $message") + override fun debug(message: String) = println("DEBUG: $message") + override fun error(message: String) = println("ERROR: $message") + override fun warn(message: String) = println("WARN: $message") + override fun trace(message: String) = println("TRACE: $message") } - fun stop(message: String? = null) { + suspend fun stop(message: String? = null) { if (message != null && config.announceDisconnect) { sendStatusUpdate(message) } enabled = false - streamConnection.close() + rcvJob?.cancel() + rcvJob = null } + private var rcvJob: Job? = null - fun start(message: String?, clear: Boolean) { - config.sync(streamConnection) + suspend fun start(message: String?, clear: Boolean) { + logger.debug("starting connection") if (clear) { clear() } enabled = true - streamConnection.open() + + rcvJob = messageBroadcast() if (message != null && config.announceConnect) { sendStatusUpdate(message) @@ -84,118 +94,138 @@ open class MessageHandler { } - private fun clear() { - try { - val url = URL(config.url + "/api/messages") - val conn = url.openConnection() as HttpURLConnection - - if (!config.token.isEmpty()) { - val bearerAuth = "Bearer " + config.token - conn.setRequestProperty("Authorization", bearerAuth) + private suspend fun clear() { + val url = "${config.url}/api/messages" + val (request, response, result) = url.httpGet() + .apply { + if (config.token.isNotEmpty()) { + headers["Authorization"] = "Bearer ${config.token}" + } } + .awaitStringResponse() - conn.requestMethod = "GET" - - BufferedReader(InputStreamReader(conn.inputStream)).forEachLine { line -> - logger.trace("skipping $line") + when (result) { + is Result.Success -> { + val messages: List = JSON.parse(ApiMessage.list, result.value) + messages.forEach { msg -> + logger.trace("skipping $msg") + } + logger.debug("skipped ${messages.count()} messages") + } + is Result.Failure -> { + logger.error("failed to clear messages") + logger.error("url: $url") + logger.error("cUrl: ${request.cUrlString()}") + logger.error("response: $response") + logger.error(result.error.exception.localizedMessage) + result.error.exception.printStackTrace() } - } catch (e: MalformedURLException) { - e.printStackTrace() - } catch (e: ProtocolException) { - e.printStackTrace() - } catch (e: IOException) { - e.printStackTrace() } } - open fun sendStatusUpdate(message: String) { + open suspend fun sendStatusUpdate(message: String) { transmit(ApiMessage(text = message)) } - open fun transmit(msg: ApiMessage) { - if (streamConnection.isConnected || streamConnection.isConnecting) { - if (msg.username.isEmpty()) - msg.username = config.systemUser - if (msg.gateway.isEmpty()) { - logger.error("missing gateway on message: $msg") - return - } - logger.debug("Transmitting: $msg") - transmitMessage(msg) + open suspend fun transmit(msg: ApiMessage) { +// if (streamConnection.isConnected || streamConnection.isConnecting) { + if (msg.username.isEmpty()) + msg.username = config.systemUser + if (msg.gateway.isEmpty()) { + logger.error("missing gateway on message: $msg") + return } + logger.debug("Transmitting: $msg") + sendChannel.send(msg) +// } } - private fun transmitMessage(message: ApiMessage) { - try { - val url = URL(config.url + "/api/message") - val conn = url.openConnection() as HttpURLConnection - - if (!config.token.isEmpty()) { - val bearerAuth = "Bearer " + config.token - conn.setRequestProperty("Authorization", bearerAuth) - } - - val postData = message.encode() - logger.trace(postData) - - conn.requestMethod = "POST" - conn.setRequestProperty("Content-Type", "application/json") - conn.setRequestProperty("charset", "utf-8") - conn.setRequestProperty("Content-Length", "" + postData.toByteArray().size) - conn.doOutput = true - conn.doInput = true - - DataOutputStream(conn.outputStream).use { wr -> wr.write(postData.toByteArray()) } - - // conn.getInputStream().close(); - conn.connect() - val code = conn.responseCode - if (code != 200) { - logger.error("Server returned $code") - sendErrors++ - if (sendErrors > 5) { - logger.error("Interrupting Connection to matterbridge API due to status code $code") - stop() - } - } else { - sendErrors = 0 - } - } catch (e: IOException) { - e.printStackTrace() - logger.error("sending message caused $e") - sendErrors++ - if (sendErrors > 5) { - logger.error("Caught too many errors, closing bridge") - stop() - } - } - - } - - /** - * clll this method every tick / cycle to make sure it is reconnecting - */ + @Deprecated("use coroutine api", level = DeprecationLevel.ERROR) fun checkConnection() { - if (enabled && !streamConnection.isConnected && !streamConnection.isConnecting) { - logger.trace("check connection") - logger.trace("next: $nextCheck") - logger.trace("now: " + System.currentTimeMillis()) - if (nextCheck > System.currentTimeMillis()) return - nextCheck = System.currentTimeMillis() + config.reconnectWait + } - if (connectErrors >= 10) { - logger.error("Caught too many errors, closing bridge") - stop("Interrupting connection to matterbridge API due to accumulated connection errors") - return + private fun CoroutineScope.senderActor() = actor(context = Dispatchers.IO) { + consumeEach { + logger.debug("sending $it") + val url = "${config.url}/api/message" + val (request, response, result) = url.httpPost() + .apply { + if (config.token.isNotEmpty()) { + headers["Authorization"] = "Bearer ${config.token}" + } + } + .jsonBody(it.encode()) + .responseString() + when (result) { + is Result.Success -> { + logger.info("sent $it") + sendErrors = 0 + } + is Result.Failure -> { + sendErrors++ + logger.error("failed to deliver: $it") + logger.error("url: $url") + logger.error("cUrl: ${request.cUrlString()}") + logger.error("response: $response") + logger.error(result.error.exception.localizedMessage) + result.error.exception.printStackTrace() +// close() + throw result.error.exception + } } + } + } - if (reconnectCooldown <= 0) { - logger.info("Trying to reconnect") - start("Reconnecting to matterbridge API after connection error", false) - } else { - reconnectCooldown-- + private fun CoroutineScope.messageBroadcast() = launch(context = Dispatchers.IO + CoroutineName("msgBroadcaster")) { + loop@ while (isActive) { + logger.info("opening connection") + val url = "${config.url}/api/stream" + val (request, response, result) = keepOpenManager.request(Method.GET, url) + .apply { + if (config.token.isNotEmpty()) { + headers["Authorization"] = "Bearer ${config.token}" + } + } + .responseObject(object : ResponseDeserializable { + override fun deserialize(reader: Reader) = + runBlocking(Dispatchers.IO + CoroutineName("msgReceiver")) { + logger.info("connected successfully") + connectErrors = 0 + reconnectCooldown = 0 + + reader.useLines { lines -> + lines.forEach { line -> + val msg = ApiMessage.decode(line) + logger.info("received: $msg") + if (msg.event != "api_connect") { + messageStream.send(msg) + } + } + } + } + }) + + when (result) { + is Result.Success -> { + logger.info("connection closed") + } + is Result.Failure -> { + connectErrors++ + reconnectCooldown = connectErrors * 1000L + logger.error("connectErrors: $connectErrors") + logger.error("connection error") + logger.error("curl: ${request.cUrlString()}") + logger.error(result.error.localizedMessage) + result.error.exception.printStackTrace() + if (connectErrors >= 10) { + logger.error("Caught too many errors, closing bridge") + stop("Interrupting connection to matterbridge API due to accumulated connection errors") + break@loop + } + } } + delay(reconnectCooldown) // reconnect delay in ms } } } diff --git a/core/src/main/kotlin/matterlink/api/StreamConnection.kt b/core/src/main/kotlin/matterlink/api/StreamConnection.kt index 040ec83..9eb08de 100644 --- a/core/src/main/kotlin/matterlink/api/StreamConnection.kt +++ b/core/src/main/kotlin/matterlink/api/StreamConnection.kt @@ -2,7 +2,6 @@ package matterlink.api import matterlink.Logger import java.io.IOException -import java.io.InputStream import java.net.ConnectException import java.net.HttpURLConnection import java.net.MalformedURLException @@ -22,7 +21,7 @@ class StreamConnection(private val rcvQueue: ConcurrentLinkedQueue) private var urlConnection: HttpURLConnection? = null private val onSuccessCallbacks = LinkedList<(Boolean) -> Unit>() - var logger = object : Logger { + var logger = object : Logger { override fun info(message: String) = println("INFO: $message") override fun debug(message: String) = println("DEBUG: $message") override fun error(message: String) = println("ERROR: $message") @@ -88,7 +87,7 @@ class StreamConnection(private val rcvQueue: ConcurrentLinkedQueue) } val chars = input.read(buf) - logger.trace( String.format("read %d chars", chars)) + logger.trace(String.format("read %d chars", chars)) if (chars > 0) { val added = String(Arrays.copyOfRange(buf, 0, chars)) logger.debug("json: $added") diff --git a/core/src/main/kotlin/matterlink/bridge/MessageHandlerInst.kt b/core/src/main/kotlin/matterlink/bridge/MessageHandlerInst.kt index 02ad2dd..ca3d8d2 100644 --- a/core/src/main/kotlin/matterlink/bridge/MessageHandlerInst.kt +++ b/core/src/main/kotlin/matterlink/bridge/MessageHandlerInst.kt @@ -1,32 +1,37 @@ package matterlink.bridge -import matterlink.* +import matterlink.Paste +import matterlink.PasteSection +import matterlink.PasteUtil +import matterlink.antiping import matterlink.api.ApiMessage import matterlink.api.MessageHandler import matterlink.config.cfg import matterlink.handlers.ChatEvent import matterlink.handlers.LocationHandler +import matterlink.mapFormat +import matterlink.stackTraceString object MessageHandlerInst : MessageHandler() { - override fun transmit(msg: ApiMessage) { + override suspend fun transmit(msg: ApiMessage) { transmit(msg, cause = "") } - override fun sendStatusUpdate(message: String) { + override suspend fun sendStatusUpdate(message: String) { LocationHandler.sendToLocations( - msg = message, - x = 0, y = 0, z = 0, dimension = null, - systemuser = true, - event = ChatEvent.STATUS, - cause = "status update message" + msg = message, + x = 0, y = 0, z = 0, dimension = null, + systemuser = true, + event = ChatEvent.STATUS, + cause = "status update message" ) } - fun transmit(msg: ApiMessage, cause: String, maxLines: Int = cfg.outgoing.inlineLimit) { + suspend fun transmit(msg: ApiMessage, cause: String, maxLines: Int = cfg.outgoing.inlineLimit) { if (msg.username.isEmpty()) { msg.username = cfg.outgoing.systemUser - if(msg.avatar.isEmpty() && cfg.outgoing.avatar.enable) { + if (msg.avatar.isEmpty() && cfg.outgoing.avatar.enable) { msg.avatar = cfg.outgoing.avatar.systemUserAvatar } } @@ -38,20 +43,20 @@ object MessageHandlerInst : MessageHandler() { if (msg.text.lines().count() >= maxLines) { try { val response = PasteUtil.paste( - Paste( - description = cause, - sections = listOf( - PasteSection( - name = "log.txt", - syntax = "text", - contents = msg.text - ) - ) + Paste( + description = cause, + sections = listOf( + PasteSection( + name = "log.txt", + syntax = "text", + contents = msg.text + ) ) + ) ) msg.text = msg.text.substringBefore('\n') - .take(25) + "... " + response.link - } catch(e: Exception) { + .take(25) + "... " + response.link + } catch (e: Exception) { logger.error(cause) logger.error(e.stackTraceString) } @@ -62,13 +67,13 @@ object MessageHandlerInst : MessageHandler() { fun ApiMessage.format(fmt: String): String { return fmt.mapFormat( - mapOf( - "{username}" to username, - "{text}" to text, - "{gateway}" to gateway, - "{channel}" to channel, - "{protocol}" to protocol, - "{username:antiping}" to username.antiping - ) + mapOf( + "{username}" to username, + "{text}" to text, + "{gateway}" to gateway, + "{channel}" to channel, + "{protocol}" to protocol, + "{username:antiping}" to username.antiping + ) ) } \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/bridge/command/AuthBridgeCommand.kt b/core/src/main/kotlin/matterlink/bridge/command/AuthBridgeCommand.kt index 0c3d41b..f53c543 100644 --- a/core/src/main/kotlin/matterlink/bridge/command/AuthBridgeCommand.kt +++ b/core/src/main/kotlin/matterlink/bridge/command/AuthBridgeCommand.kt @@ -5,7 +5,7 @@ import matterlink.config.IdentitiesConfig import matterlink.config.cfg import matterlink.instance import matterlink.randomString -import java.util.* +import java.util.UUID object AuthBridgeCommand : IBridgeCommand() { val syntax = "Syntax: auth [username]" @@ -13,7 +13,7 @@ object AuthBridgeCommand : IBridgeCommand() { override val permLevel: Double get() = cfg.command.defaultPermUnauthenticated - override fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean { + override suspend fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean { if (env !is CommandEnvironment.BridgeEnv) { env.respond("please initiate authentication from linked external chat") return true @@ -28,8 +28,10 @@ object AuthBridgeCommand : IBridgeCommand() { val argList = args.split(' ', limit = 2) val target = argList.getOrNull(0) ?: run { - env.respond("no username/uuid provided\n" + - syntax) + env.respond( + "no username/uuid provided\n" + + syntax + ) return true } @@ -62,7 +64,16 @@ object AuthBridgeCommand : IBridgeCommand() { instance.wrappedSendToPlayer(targetUserName, "otherwise you may ignore this message") - IdentitiesConfig.authRequests.put(requestId, AuthRequest(username = targetUserName, uuid = targetUUid, nonce = nonce, platform = env.platform, userid = env.userId)) + IdentitiesConfig.authRequests.put( + requestId, + AuthRequest( + username = targetUserName, + uuid = targetUUid, + nonce = nonce, + platform = env.platform, + userid = env.userId + ) + ) env.respond("please accept the authentication request ingame, do not share the code") return true diff --git a/core/src/main/kotlin/matterlink/bridge/command/BridgeCommandRegistry.kt b/core/src/main/kotlin/matterlink/bridge/command/BridgeCommandRegistry.kt index ed19dc5..6541e5f 100644 --- a/core/src/main/kotlin/matterlink/bridge/command/BridgeCommandRegistry.kt +++ b/core/src/main/kotlin/matterlink/bridge/command/BridgeCommandRegistry.kt @@ -1,15 +1,13 @@ 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.logger -import matterlink.stripColorOut -import java.util.* +import java.util.HashMap +import java.util.UUID object BridgeCommandRegistry { @@ -19,7 +17,7 @@ object BridgeCommandRegistry { * * @return consume message flag */ - fun handleCommand(input: ApiMessage): Boolean { + suspend fun handleCommand(input: ApiMessage): Boolean { if (!cfg.command.enable || input.text.isBlank()) return false if (input.text[0] != cfg.command.prefix || input.text.length < 2) return false @@ -29,7 +27,13 @@ object BridgeCommandRegistry { val uuid = IdentitiesConfig.getUUID(input.account, input.userid) - val env = IBridgeCommand.CommandEnvironment.BridgeEnv(input.username, input.userid, input.account, input.gateway, uuid) + val env = IBridgeCommand.CommandEnvironment.BridgeEnv( + input.username, + input.userid, + input.account, + input.gateway, + uuid + ) return commandMap[cmd[0]]?.let { if (!it.reachedTimeout()) { logger.debug("dropped command ${it.alias}") @@ -38,7 +42,7 @@ object BridgeCommandRegistry { it.preExecute() // resets the tickCounter if (!it.canExecute(uuid)) { env.respond( - text = "${input.username} is not permitted to perform command: ${cmd[0]}" + text = "${input.username} is not permitted to perform command: ${cmd[0]}" ) return false } @@ -46,7 +50,7 @@ object BridgeCommandRegistry { } ?: false } - fun handleCommand(text: String, username: String, uuid: UUID): Boolean { + suspend fun handleCommand(text: String, username: String, uuid: UUID): Boolean { if (!cfg.command.enable || text.isBlank()) return false if (text[0] != cfg.command.prefix || text.length < 2) return false @@ -64,7 +68,7 @@ object BridgeCommandRegistry { it.preExecute() // resets the tickCounter if (!it.canExecute(uuid)) { env.respond( - text = "$username is not permitted to perform command: ${cmd[0]}" + text = "$username is not permitted to perform command: ${cmd[0]}" ) return false } @@ -98,11 +102,11 @@ object BridgeCommandRegistry { fun getCommandList(permLvl: Double): String { return commandMap - .filterValues { - it.permLevel <= permLvl - } - .keys - .joinToString(" ") + .filterValues { + it.permLevel <= permLvl + } + .keys + .joinToString(" ") } fun reloadCommands() { diff --git a/core/src/main/kotlin/matterlink/bridge/command/CustomCommand.kt b/core/src/main/kotlin/matterlink/bridge/command/CustomCommand.kt index 764e077..a7008a2 100644 --- a/core/src/main/kotlin/matterlink/bridge/command/CustomCommand.kt +++ b/core/src/main/kotlin/matterlink/bridge/command/CustomCommand.kt @@ -6,18 +6,18 @@ import matterlink.logger import matterlink.stripColorIn data class CustomCommand( - val type: CommandType = CommandType.RESPONSE, - val execute: String? = null, - val response: String? = null, - override val permLevel: Double = 0.0, - override val help: String = "", - override val timeout: Int = 20, - val defaultCommand: Boolean? = null, - val execOp: Boolean? = null, - val argumentsRegex: Regex? = null + val type: CommandType = CommandType.RESPONSE, + val execute: String? = null, + val response: String? = null, + override val permLevel: Double = 0.0, + override val help: String = "", + override val timeout: Int = 20, + val defaultCommand: Boolean? = null, + val execOp: Boolean? = null, + val argumentsRegex: Regex? = null ) : IBridgeCommand() { - override fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean { + override suspend fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean { if (argumentsRegex != null) { logger.debug("testing '$args' against '${argumentsRegex.pattern}'") if (!argumentsRegex.matches(args)) { @@ -31,12 +31,14 @@ data class CustomCommand( // uses a new commandsender for each use val commandSender = instance.commandSenderFor(user, env, execOp ?: false) val cmd = execute?.lazyFormat(getReplacements(user, env, args))?.stripColorIn - ?: return false + ?: return false commandSender.execute(cmd) || commandSender.reply.isNotEmpty() } CommandType.RESPONSE -> { - env.respond(response?.lazyFormat(getReplacements(user, env, args)) - ?: "", cause = "response to command: $alias") + env.respond( + response?.lazyFormat(getReplacements(user, env, args)) + ?: "", cause = "response to command: $alias" + ) true } @@ -60,34 +62,34 @@ data class CustomCommand( val DEFAULT = CustomCommand() fun getReplacements(user: String, env: CommandEnvironment, args: String): Map String?> = mapOf( - "{uptime}" to instance::getUptimeAsString, - "{user}" to { user }, - "{userid}" to { - when (env) { - is CommandEnvironment.BridgeEnv -> env.userId - else -> null - } - }, - "{uuid}" to { - when (env) { - is CommandEnvironment.BridgeEnv -> env.uuid.toString() - is CommandEnvironment.GameEnv -> env.uuid.toString() - } - }, - "{username}" to { - when (env) { - is CommandEnvironment.BridgeEnv -> env.uuid - is CommandEnvironment.GameEnv -> env.uuid - }?.let { instance.uuidToName(it) } - }, - "{platform}" to { - when (env) { - is CommandEnvironment.BridgeEnv -> env.platform - else -> null - } - }, - "{args}" to { args }, - "{version}" to { instance.modVersion } + "{uptime}" to instance::getUptimeAsString, + "{user}" to { user }, + "{userid}" to { + when (env) { + is CommandEnvironment.BridgeEnv -> env.userId + else -> null + } + }, + "{uuid}" to { + when (env) { + is CommandEnvironment.BridgeEnv -> env.uuid.toString() + is CommandEnvironment.GameEnv -> env.uuid.toString() + } + }, + "{username}" to { + when (env) { + is CommandEnvironment.BridgeEnv -> env.uuid + is CommandEnvironment.GameEnv -> env.uuid + }?.let { instance.uuidToName(it) } + }, + "{platform}" to { + when (env) { + is CommandEnvironment.BridgeEnv -> env.platform + else -> null + } + }, + "{args}" to { args }, + "{version}" to { instance.modVersion } ) } } diff --git a/core/src/main/kotlin/matterlink/bridge/command/HelpCommand.kt b/core/src/main/kotlin/matterlink/bridge/command/HelpCommand.kt index 29d415f..ca2570e 100644 --- a/core/src/main/kotlin/matterlink/bridge/command/HelpCommand.kt +++ b/core/src/main/kotlin/matterlink/bridge/command/HelpCommand.kt @@ -1,27 +1,24 @@ package matterlink.bridge.command -import matterlink.api.ApiMessage -import matterlink.bridge.MessageHandlerInst import matterlink.config.cfg -import matterlink.stripColorOut object HelpCommand : IBridgeCommand() { override val help: String = "Returns the help string for the given command. Syntax: help " override val permLevel: Double get() = cfg.command.defaultPermUnauthenticated - override fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean { + override suspend fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean { val msg: String = when { args.isEmpty() -> "Available commands: ${BridgeCommandRegistry.getCommandList(IBridgeCommand.getPermLevel(env.uuid))}" else -> args.split(" ", ignoreCase = false) - .joinToString(separator = "\n") { - "$it: ${BridgeCommandRegistry.getHelpString(it)}" - } + .joinToString(separator = "\n") { + "$it: ${BridgeCommandRegistry.getHelpString(it)}" + } } env.respond( - text = msg, - cause = "Help Requested $args" + text = msg, + cause = "Help Requested $args" ) return true } diff --git a/core/src/main/kotlin/matterlink/bridge/command/IBridgeCommand.kt b/core/src/main/kotlin/matterlink/bridge/command/IBridgeCommand.kt index b536e92..7c4356d 100644 --- a/core/src/main/kotlin/matterlink/bridge/command/IBridgeCommand.kt +++ b/core/src/main/kotlin/matterlink/bridge/command/IBridgeCommand.kt @@ -8,7 +8,7 @@ import matterlink.handlers.TickHandler import matterlink.instance import matterlink.logger import matterlink.stripColorOut -import java.util.* +import java.util.UUID abstract class IBridgeCommand { abstract val help: String @@ -20,30 +20,30 @@ abstract class IBridgeCommand { abstract val username: String? data class BridgeEnv( - val name: String, - val userId: String, - val platform: String, - val gateway: String, - override val uuid: UUID? + val name: String, + val userId: String, + val platform: String, + val gateway: String, + override val uuid: UUID? ) : CommandEnvironment() { override val username: String? get() = uuid?.let { instance.uuidToName(uuid) } } data class GameEnv( - override val username: String, - override val uuid: UUID + override val username: String, + override val uuid: UUID ) : CommandEnvironment() - fun respond(text: String, cause: String = "") { + suspend fun respond(text: String, cause: String = "") { when (this) { is BridgeEnv -> { MessageHandlerInst.transmit( - ApiMessage( - gateway = this.gateway, - text = text.stripColorOut - ), - cause = cause + ApiMessage( + gateway = this.gateway, + text = text.stripColorOut + ), + cause = cause ) } is GameEnv -> { @@ -72,7 +72,7 @@ abstract class IBridgeCommand { * * @return consume message flag */ - abstract fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean + abstract suspend fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean fun canExecute(uuid: UUID?): Boolean { logger.trace("canExecute this: $this uuid: $uuid permLevel: $permLevel") diff --git a/core/src/main/kotlin/matterlink/bridge/command/IMinecraftCommandSender.kt b/core/src/main/kotlin/matterlink/bridge/command/IMinecraftCommandSender.kt index 838caff..a129a3e 100644 --- a/core/src/main/kotlin/matterlink/bridge/command/IMinecraftCommandSender.kt +++ b/core/src/main/kotlin/matterlink/bridge/command/IMinecraftCommandSender.kt @@ -1,7 +1,5 @@ package matterlink.bridge.command -import matterlink.stripColorOut - abstract class IMinecraftCommandSender(val user: String, val env: IBridgeCommand.CommandEnvironment, val op: Boolean) { /** * @param cmdString The command to execute with its arguments @@ -13,7 +11,7 @@ abstract class IMinecraftCommandSender(val user: String, val env: IBridgeCommand val displayName = env.username ?: user val accountName = when (env) { is IBridgeCommand.CommandEnvironment.BridgeEnv -> "$user (id=${env.userId} platform=${env.platform}${env.uuid?.let { " uuid=$it" } - ?: ""}${env.username?.let { " username=$it" } ?: ""})" + ?: ""}${env.username?.let { " username=$it" } ?: ""})" is IBridgeCommand.CommandEnvironment.GameEnv -> "$user (username=${env.username} uuid=${env.uuid})" } @@ -37,10 +35,10 @@ abstract class IMinecraftCommandSender(val user: String, val env: IBridgeCommand reply += text } - fun sendReply(cmdString: String) { + suspend fun sendReply(cmdString: String) { env.respond( - text = reply.joinToString("\n"), - cause = "executed command: $cmdString" + text = reply.joinToString("\n"), + cause = "executed command: $cmdString" ) finished = true } diff --git a/core/src/main/kotlin/matterlink/bridge/command/RequestPermissionsCommand.kt b/core/src/main/kotlin/matterlink/bridge/command/RequestPermissionsCommand.kt index 339e4ed..4329466 100644 --- a/core/src/main/kotlin/matterlink/bridge/command/RequestPermissionsCommand.kt +++ b/core/src/main/kotlin/matterlink/bridge/command/RequestPermissionsCommand.kt @@ -11,7 +11,7 @@ object RequestPermissionsCommand : IBridgeCommand() { override val permLevel: Double get() = cfg.command.defaultPermAuthenticated - override fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean { + override suspend fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean { val uuid = env.uuid if (uuid == null) { @@ -23,8 +23,10 @@ object RequestPermissionsCommand : IBridgeCommand() { val requestedLevelArg = argList.getOrNull(0) val requestedLevel = requestedLevelArg?.takeIf { it.isNotEmpty() }?.let { it.toDoubleOrNull() ?: run { - env.respond("cannot parse permlevel '$requestedLevelArg'\n" + - syntax) + env.respond( + "cannot parse permlevel '$requestedLevelArg'\n" + + syntax + ) return true } } @@ -33,7 +35,10 @@ object RequestPermissionsCommand : IBridgeCommand() { val requestId = user.toLowerCase() - PermissionConfig.permissionRequests.put(requestId, PermissionRequest(uuid = uuid, user = user, nonce = nonce, powerlevel = requestedLevel)) + PermissionConfig.permissionRequests.put( + requestId, + PermissionRequest(uuid = uuid, user = user, nonce = nonce, powerlevel = requestedLevel) + ) env.respond("please ask a op to accept your permission elevation with `/ml permAccept $requestId $nonce [permLevel]`") return true diff --git a/core/src/main/kotlin/matterlink/command/CommandCoreAuth.kt b/core/src/main/kotlin/matterlink/command/CommandCoreAuth.kt index 0718b05..29add1e 100644 --- a/core/src/main/kotlin/matterlink/command/CommandCoreAuth.kt +++ b/core/src/main/kotlin/matterlink/command/CommandCoreAuth.kt @@ -1,11 +1,6 @@ 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" @@ -28,17 +23,23 @@ object CommandCoreAuth { val nonce = args.getOrNull(2)?.toUpperCase() ?: run { return "no code passed" } - if(request.nonce != nonce) { + if (request.nonce != nonce) { return "nonce in request does not match" } - if(request.username != user) { + if (request.username != user) { return "username in request does not match ${request.username} != $user" } - if(request.uuid != uuid) { + 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.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" @@ -54,13 +55,13 @@ object CommandCoreAuth { val nonce = args.getOrNull(2)?.toUpperCase() ?: run { return "no code passed" } - if(request.nonce != nonce) { + if (request.nonce != nonce) { return "nonce in request does not match" } - if(request.username != user) { + if (request.username != user) { return "username in request does not match ${request.username} != $user" } - if(request.uuid != uuid) { + if (request.uuid != uuid) { return "uuid in request does not match ${request.uuid} != $uuid" } diff --git a/core/src/main/kotlin/matterlink/command/CommandCoreML.kt b/core/src/main/kotlin/matterlink/command/CommandCoreML.kt index 796550a..3c9c2bc 100644 --- a/core/src/main/kotlin/matterlink/command/CommandCoreML.kt +++ b/core/src/main/kotlin/matterlink/command/CommandCoreML.kt @@ -13,7 +13,7 @@ object CommandCoreML { val usage = "ml " - fun execute(args: Array, user: String, uuid: String?): String { + suspend fun execute(args: Array, user: String, uuid: String?): String { val cmd = args[0].toLowerCase() return when (cmd) { @@ -49,8 +49,8 @@ object CommandCoreML { } val powerLevelArg = args.getOrNull(2)?.toDoubleOrNull() val powerLevel = powerLevelArg ?: run { return "permLevel cannot be parsed" } - ?: request.powerlevel - ?: return "no permLevel provided" + ?: 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" diff --git a/core/src/main/kotlin/matterlink/config/BaseConfig.kt b/core/src/main/kotlin/matterlink/config/BaseConfig.kt index f33484a..19d4877 100644 --- a/core/src/main/kotlin/matterlink/config/BaseConfig.kt +++ b/core/src/main/kotlin/matterlink/config/BaseConfig.kt @@ -4,12 +4,17 @@ import blue.endless.jankson.Jankson import blue.endless.jankson.JsonObject import blue.endless.jankson.impl.Marshaller import blue.endless.jankson.impl.SyntaxError -import matterlink.* +import matterlink.Area import matterlink.bridge.MessageHandlerInst +import matterlink.getOrDefault +import matterlink.getOrPutList +import matterlink.getReifiedOrDelete +import matterlink.logger +import matterlink.registerSerializer +import matterlink.registerTypeAdapter +import matterlink.stackTraceString import java.io.File import java.io.FileNotFoundException -import java.util.* -import kotlin.collections.LinkedHashMap lateinit var cfg: BaseConfig.MatterLinkConfig lateinit var baseCfg: BaseConfig @@ -24,611 +29,620 @@ data class BaseConfig(val rootDir: File) { } data class MatterLinkConfig( - val connect: ConnectOptions = ConnectOptions(), - val outgoingDefaults: DefaultSettingsOutgoing = DefaultSettingsOutgoing(), - val incomingDefaults: DefaultSettingsIncoming = DefaultSettingsIncoming(), - val locations: List = listOf( - Location( - label = "default", - gateway = "minecraft", - area = Area.Infinite(dimensions = listOf(-1, 0, 1), allDimensions = true), - outgoing = SettingsOutgoing( - plain = true, - action = true, - join = true, - leave = true, - advancement = true, - death = true, - broadcast = true, - status = true - ), - incoming = SettingsIncoming( - plain = true, - action = true, - join_leave = true, - commands = true - ) - ) - ), - val incoming: IncomingOptions = IncomingOptions(), - val outgoing: OutgoingOptions = OutgoingOptions(), - val command: CommandOptions = CommandOptions(), - val update: UpdateOptions = UpdateOptions() + val connect: ConnectOptions = ConnectOptions(), + val outgoingDefaults: DefaultSettingsOutgoing = DefaultSettingsOutgoing(), + val incomingDefaults: DefaultSettingsIncoming = DefaultSettingsIncoming(), + val locations: List = listOf( + Location( + label = "default", + gateway = "minecraft", + area = Area.Infinite(dimensions = listOf(-1, 0, 1), allDimensions = true), + outgoing = SettingsOutgoing( + plain = true, + action = true, + join = true, + leave = true, + advancement = true, + death = true, + broadcast = true, + status = true + ), + incoming = SettingsIncoming( + plain = true, + action = true, + join_leave = true, + commands = true + ) + ) + ), + val incoming: IncomingOptions = IncomingOptions(), + val outgoing: OutgoingOptions = OutgoingOptions(), + val command: CommandOptions = CommandOptions(), + val update: UpdateOptions = UpdateOptions() ) data class DefaultSettingsOutgoing( - val plain: Boolean = true, - val action: Boolean = true, - val join: Boolean = false, - val leave: Boolean = false, - val advancement: Boolean = false, - val death: Boolean = false, - val broadcast: Boolean = false, - val status: Boolean = false + val plain: Boolean = true, + val action: Boolean = true, + val join: Boolean = false, + val leave: Boolean = false, + val advancement: Boolean = false, + val death: Boolean = false, + val broadcast: Boolean = false, + val status: Boolean = false ) data class SettingsOutgoing( - val plain: Boolean? = null, - val action: Boolean? = null, - val join: Boolean? = null, - val leave: Boolean? = null, - val advancement: Boolean? = null, - val death: Boolean? = null, - val broadcast: Boolean? = null, - val status: Boolean? = null, - val skip: List = listOf() + val plain: Boolean? = null, + val action: Boolean? = null, + val join: Boolean? = null, + val leave: Boolean? = null, + val advancement: Boolean? = null, + val death: Boolean? = null, + val broadcast: Boolean? = null, + val status: Boolean? = null, + val skip: List = listOf() ) data class DefaultSettingsIncoming( - val plain: Boolean = true, - val action: Boolean = true, - val join_leave: Boolean = true, - val commands: Boolean = true + val plain: Boolean = true, + val action: Boolean = true, + val join_leave: Boolean = true, + val commands: Boolean = true ) data class SettingsIncoming( - val plain: Boolean? = null, - val action: Boolean? = null, - val join_leave: Boolean? = null, - val commands: Boolean? = null, - val skip: List = listOf() + val plain: Boolean? = null, + val action: Boolean? = null, + val join_leave: Boolean? = null, + val commands: Boolean? = null, + val skip: List = listOf() ) data class Location( - val label: String = "unlabeled", - val gateway: String = "", - val area: Area = Area.Infinite(), - val outgoing: SettingsOutgoing = SettingsOutgoing(), - val incoming: SettingsIncoming = SettingsIncoming() + val label: String = "unlabeled", + val gateway: String = "", + val area: Area = Area.Infinite(), + val outgoing: SettingsOutgoing = SettingsOutgoing(), + val incoming: SettingsIncoming = SettingsIncoming() ) data class CommandOptions( - val prefix: Char = '!', - val enable: Boolean = true, - val authRequests: Boolean = true, - val permisionRequests: Boolean = true, - val defaultPermUnauthenticated: Double = 0.0, - val defaultPermAuthenticated: Double = 1.0 + val prefix: Char = '!', + val enable: Boolean = true, + val authRequests: Boolean = true, + val permisionRequests: Boolean = true, + val defaultPermUnauthenticated: Double = 0.0, + val defaultPermAuthenticated: Double = 1.0 ) data class ConnectOptions( - val url: String = "http://localhost:4242", - val authToken: String = "", - val autoConnect: Boolean = true, - val reconnectWait: Long = 500 + val url: String = "http://localhost:4242", + val authToken: String = "", + val autoConnect: Boolean = true, + val reconnectWait: Long = 500 ) data class IncomingOptions( - val chat: String = "<{username}> {text}", - val joinPart: String = "§6-- {username} {text}", - val action: String = "§5* {username} {text}", - val stripColors: Boolean = true + val chat: String = "<{username}> {text}", + val joinPart: String = "§6-- {username} {text}", + val action: String = "§5* {username} {text}", + val stripColors: Boolean = true ) data class OutgoingOptions( - val systemUser: String = "Server", - //outgoing toggles - val announceConnect: Boolean = true, - val announceDisconnect: Boolean = true, - val advancements: Boolean = true, - val stripColors: Boolean = true, - val pasteEEKey: String = "", - val inlineLimit: Int = 5, + val systemUser: String = "Server", + //outgoing toggles + val announceConnect: Boolean = true, + val announceDisconnect: Boolean = true, + val advancements: Boolean = true, + val stripColors: Boolean = true, + val pasteEEKey: String = "", + val inlineLimit: Int = 5, - val joinPart: JoinPartOptions = JoinPartOptions(), - var avatar: AvatarOptions = AvatarOptions(), - val death: DeathOptions = DeathOptions() + val joinPart: JoinPartOptions = JoinPartOptions(), + var avatar: AvatarOptions = AvatarOptions(), + val death: DeathOptions = DeathOptions() ) data class DeathOptions( - val enable: Boolean = true, - val damageType: Boolean = true, - val damageTypeMapping: Map> = mapOf( - "inFire" to arrayOf("\uD83D\uDD25"), //🔥 - "lightningBolt" to arrayOf("\uD83C\uDF29"), //🌩 - "onFire" to arrayOf("\uD83D\uDD25"), //🔥 - "lava" to arrayOf("\uD83D\uDD25"), //🔥 - "hotFloor" to arrayOf("♨️"), - "inWall" to arrayOf(), - "cramming" to arrayOf(), - "drown" to arrayOf("\uD83C\uDF0A"), //🌊 - "starve" to arrayOf("\uD83D\uDC80"), //💀 - "cactus" to arrayOf("\uD83C\uDF35"), //🌵 - "fall" to arrayOf("\u2BEF️"), //⯯️ - "flyIntoWall" to arrayOf("\uD83D\uDCA8"), //💨 - "outOfWorld" to arrayOf("\u2734"), //✴ - "generic" to arrayOf("\uD83D\uDC7B"), //👻 - "magic" to arrayOf("✨", "⚚"), - "indirectMagic" to arrayOf("✨", "⚚"), - "wither" to arrayOf("\uD83D\uDD71"), //🕱 - "anvil" to arrayOf(), - "fallingBlock" to arrayOf(), - "dragonBreath" to arrayOf("\uD83D\uDC32"), //🐲 - "fireworks" to arrayOf("\uD83C\uDF86"), //🎆 + val enable: Boolean = true, + val damageType: Boolean = true, + val damageTypeMapping: Map> = mapOf( + "inFire" to arrayOf("\uD83D\uDD25"), //🔥 + "lightningBolt" to arrayOf("\uD83C\uDF29"), //🌩 + "onFire" to arrayOf("\uD83D\uDD25"), //🔥 + "lava" to arrayOf("\uD83D\uDD25"), //🔥 + "hotFloor" to arrayOf("♨️"), + "inWall" to arrayOf(), + "cramming" to arrayOf(), + "drown" to arrayOf("\uD83C\uDF0A"), //🌊 + "starve" to arrayOf("\uD83D\uDC80"), //💀 + "cactus" to arrayOf("\uD83C\uDF35"), //🌵 + "fall" to arrayOf("\u2BEF️"), //⯯️ + "flyIntoWall" to arrayOf("\uD83D\uDCA8"), //💨 + "outOfWorld" to arrayOf("\u2734"), //✴ + "generic" to arrayOf("\uD83D\uDC7B"), //👻 + "magic" to arrayOf("✨", "⚚"), + "indirectMagic" to arrayOf("✨", "⚚"), + "wither" to arrayOf("\uD83D\uDD71"), //🕱 + "anvil" to arrayOf(), + "fallingBlock" to arrayOf(), + "dragonBreath" to arrayOf("\uD83D\uDC32"), //🐲 + "fireworks" to arrayOf("\uD83C\uDF86"), //🎆 - "mob" to arrayOf("\uD83D\uDC80"), //💀 - "player" to arrayOf("\uD83D\uDDE1"), //🗡 - "arrow" to arrayOf("\uD83C\uDFF9"), //🏹 - "thrown" to arrayOf("彡°"), - "thorns" to arrayOf("\uD83C\uDF39"), //🌹 - "explosion" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5"), //💣 💥 - "explosion.player" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5"), //💣 💥 - "ieWireShock" to arrayOf("\uD83D\uDD0C", "\u26A1"), //🔌 ⚡ - "immersiverailroading:hitByTrain" to arrayOf("\uD83D\uDE82", "\uD83D\uDE83", "\uD83D\uDE84", "\uD83D\uDE85", "\uD83D\uDE87", "\uD83D\uDE88", "\uD83D\uDE8A") //🚂 🚃 🚄 🚅 🚇 🚈 🚊 - ) + "mob" to arrayOf("\uD83D\uDC80"), //💀 + "player" to arrayOf("\uD83D\uDDE1"), //🗡 + "arrow" to arrayOf("\uD83C\uDFF9"), //🏹 + "thrown" to arrayOf("彡°"), + "thorns" to arrayOf("\uD83C\uDF39"), //🌹 + "explosion" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5"), //💣 💥 + "explosion.player" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5"), //💣 💥 + "ieWireShock" to arrayOf("\uD83D\uDD0C", "\u26A1"), //🔌 ⚡ + "immersiverailroading:hitByTrain" to arrayOf( + "\uD83D\uDE82", + "\uD83D\uDE83", + "\uD83D\uDE84", + "\uD83D\uDE85", + "\uD83D\uDE87", + "\uD83D\uDE88", + "\uD83D\uDE8A" + ) //🚂 🚃 🚄 🚅 🚇 🚈 🚊 + ) ) data class AvatarOptions( - val enable: Boolean = true, - val urlTemplate: String = "https://visage.surgeplay.com/head/512/{uuid}", - // https://www.freepik.com/free-icon/right-arrow-angle-and-horizontal-down-line-code-signs_732795.htm - val systemUserAvatar: String = "https://image.freepik.com/free-icon/right-arrow-angle-and-horizontal-down-line-code-signs_318-53994.jpg" + val enable: Boolean = true, + val urlTemplate: String = "https://visage.surgeplay.com/head/512/{uuid}", + // https://www.freepik.com/free-icon/right-arrow-angle-and-horizontal-down-line-code-signs_732795.htm + val systemUserAvatar: String = "https://image.freepik.com/free-icon/right-arrow-angle-and-horizontal-down-line-code-signs_318-53994.jpg" ) data class JoinPartOptions( - val enable: Boolean = true, - val joinServer: String = "{username:antiping} has connected to the server", - val partServer: String = "{username:antiping} has disconnected from the server" + val enable: Boolean = true, + val joinServer: String = "{username:antiping} has connected to the server", + val partServer: String = "{username:antiping} has disconnected from the server" ) data class UpdateOptions( - val enable: Boolean = true + val enable: Boolean = true ) companion object { val jankson = Jankson - .builder() - .registerTypeAdapter { - with(MatterLinkConfig()) { - MatterLinkConfig( - command = it.getOrDefault( - "command", - command, - "User commands" - ), - outgoingDefaults = it.getOrDefault( - "outgoingDefaults", - outgoingDefaults, - "default settings for outgoing" - ), - incomingDefaults = it.getOrDefault( - "incomingDefaults", - incomingDefaults, - "default settings for incoming" - ), - locations = it.getOrPutList( - "locations", - locations, - "list of fixed chat locations" - ), - connect = it.getOrDefault( - "connect", - connect, - "Connection Settings" - ), - incoming = it.getOrDefault( - "incoming", - incoming, - """ + .builder() + .registerTypeAdapter { + with(MatterLinkConfig()) { + MatterLinkConfig( + command = it.getOrDefault( + "command", + command, + "User commands" + ), + outgoingDefaults = it.getOrDefault( + "outgoingDefaults", + outgoingDefaults, + "default settings for outgoing" + ), + incomingDefaults = it.getOrDefault( + "incomingDefaults", + incomingDefaults, + "default settings for incoming" + ), + locations = it.getOrPutList( + "locations", + locations, + "list of fixed chat locations" + ), + connect = it.getOrDefault( + "connect", + connect, + "Connection Settings" + ), + incoming = it.getOrDefault( + "incoming", + incoming, + """ Gateway -> Server Options all about receiving messages from the API Formatting options: Available variables: {username}, {text}, {gateway}, {channel}, {protocol}, {username:antiping} """.trimIndent() - ), - outgoing = it.getOrDefault( - "outgoing", - outgoing, - """ + ), + outgoing = it.getOrDefault( + "outgoing", + outgoing, + """ Server -> Gateway Options all about sending messages to the API """.trimIndent() - ), - update = it.getOrDefault( - "update", - update, - "Update Settings" - ) + ), + update = it.getOrDefault( + "update", + update, + "Update Settings" ) - } + ) } - .registerTypeAdapter { - with(DefaultSettingsOutgoing()) { - DefaultSettingsOutgoing( - plain = it.getOrDefault( - "plain", - plain, - "plain text messages" - ), - action = it.getOrDefault( - "action", - action, - "action messages" - ), - join = it.getOrDefault( - "join", - join, - "handle join event" - ), - leave = it.getOrDefault( - "leave", - leave, - "handle leave events" - ), - advancement = it.getOrDefault( - "advancement", - advancement, - "handle advancement events" - ), - death = it.getOrDefault( - "death", - death, - "handle death events" - ), - broadcast = it.getOrDefault( - "broadcast", - broadcast, - "handle broadcast command" - ), - status = it.getOrDefault( - "status", - status, - "handles tatus updates" - ) + } + .registerTypeAdapter { + with(DefaultSettingsOutgoing()) { + DefaultSettingsOutgoing( + plain = it.getOrDefault( + "plain", + plain, + "plain text messages" + ), + action = it.getOrDefault( + "action", + action, + "action messages" + ), + join = it.getOrDefault( + "join", + join, + "handle join event" + ), + leave = it.getOrDefault( + "leave", + leave, + "handle leave events" + ), + advancement = it.getOrDefault( + "advancement", + advancement, + "handle advancement events" + ), + death = it.getOrDefault( + "death", + death, + "handle death events" + ), + broadcast = it.getOrDefault( + "broadcast", + broadcast, + "handle broadcast command" + ), + status = it.getOrDefault( + "status", + status, + "handles tatus updates" ) - } + ) } - .registerTypeAdapter { - with(SettingsOutgoing()) { - SettingsOutgoing( - plain = it.getReifiedOrDelete("plain", "transmit join events"), - action = it.getReifiedOrDelete("action", "transmit join events"), - join = it.getReifiedOrDelete("join", "transmit join events"), - leave = it.getReifiedOrDelete("leave", "transmit leave events"), - advancement = it.getReifiedOrDelete("advancement", "transmit advancements"), - death = it.getReifiedOrDelete("death", "transmit death messages"), - broadcast = it.getReifiedOrDelete("say", "transmit broadcasts"), - status = it.getReifiedOrDelete("status", "transmit status updates"), - skip = it.getOrPutList( - "skip", - skip, - "list of other locations to ignore after handling this" - ) + } + .registerTypeAdapter { + with(SettingsOutgoing()) { + SettingsOutgoing( + plain = it.getReifiedOrDelete("plain", "transmit join events"), + action = it.getReifiedOrDelete("action", "transmit join events"), + join = it.getReifiedOrDelete("join", "transmit join events"), + leave = it.getReifiedOrDelete("leave", "transmit leave events"), + advancement = it.getReifiedOrDelete("advancement", "transmit advancements"), + death = it.getReifiedOrDelete("death", "transmit death messages"), + broadcast = it.getReifiedOrDelete("say", "transmit broadcasts"), + status = it.getReifiedOrDelete("status", "transmit status updates"), + skip = it.getOrPutList( + "skip", + skip, + "list of other locations to ignore after handling this" ) - } + ) } + } - .registerTypeAdapter { - with(DefaultSettingsIncoming()) { - DefaultSettingsIncoming( - plain = it.getOrDefault( - "plain", - plain, - "plain text messages" - ), - action = it.getOrDefault( - "action", - action, - "action messages" - ), - join_leave = it.getOrDefault( - "join_leave", - join_leave, - "handle join/leave event" - ), - commands = it.getOrDefault( - "commands", - join_leave, - "receive commands" - ) + .registerTypeAdapter { + with(DefaultSettingsIncoming()) { + DefaultSettingsIncoming( + plain = it.getOrDefault( + "plain", + plain, + "plain text messages" + ), + action = it.getOrDefault( + "action", + action, + "action messages" + ), + join_leave = it.getOrDefault( + "join_leave", + join_leave, + "handle join/leave event" + ), + commands = it.getOrDefault( + "commands", + join_leave, + "receive commands" ) - } + ) } - .registerTypeAdapter { - with(SettingsIncoming()) { - SettingsIncoming( - plain = it.getReifiedOrDelete("plain", "transmit join events"), - action = it.getReifiedOrDelete("action", "transmit join events"), - join_leave = it.getReifiedOrDelete("join_leave", "transmit join_leave events"), - commands = it.getReifiedOrDelete("commands", "receive commands"), - skip = it.getOrPutList( - "skip", - skip, - "list of other locations to ignore after handling this" - ) + } + .registerTypeAdapter { + with(SettingsIncoming()) { + SettingsIncoming( + plain = it.getReifiedOrDelete("plain", "transmit join events"), + action = it.getReifiedOrDelete("action", "transmit join events"), + join_leave = it.getReifiedOrDelete("join_leave", "transmit join_leave events"), + commands = it.getReifiedOrDelete("commands", "receive commands"), + skip = it.getOrPutList( + "skip", + skip, + "list of other locations to ignore after handling this" ) - } + ) } - .registerTypeAdapter { - with(Location()) { - Location( - label = it.getOrDefault("label", - label, - "location label for identification" - ), - gateway = it.getOrDefault( - "gateway", - gateway, - "matterbridge gateway identifier" - ), - area = Area.parse(it.getObject("area") ?: JsonObject()), - outgoing = it.getOrDefault( - "outgoing", - outgoing, - "Location outgoing settings" - ), - incoming = it.getOrDefault( - "incoming", - incoming, - "incoming settings" - ) + } + .registerTypeAdapter { + with(Location()) { + Location( + label = it.getOrDefault( + "label", + label, + "location label for identification" + ), + gateway = it.getOrDefault( + "gateway", + gateway, + "matterbridge gateway identifier" + ), + area = Area.parse(it.getObject("area") ?: JsonObject()), + outgoing = it.getOrDefault( + "outgoing", + outgoing, + "Location outgoing settings" + ), + incoming = it.getOrDefault( + "incoming", + incoming, + "incoming settings" ) - } + ) } - .registerTypeAdapter { - with(CommandOptions()) { - CommandOptions( - enable = it.getOrDefault( - "enable", - enable, - "Enable MC bridge commands" - ), - prefix = it.getOrDefault( - "prefix", - prefix, - "Prefix for MC bridge commands. Accepts a single character (not alphanumeric or /)" - ), - 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" - ) + } + .registerTypeAdapter { + with(CommandOptions()) { + CommandOptions( + enable = it.getOrDefault( + "enable", + enable, + "Enable MC bridge commands" + ), + prefix = it.getOrDefault( + "prefix", + prefix, + "Prefix for MC bridge commands. Accepts a single character (not alphanumeric or /)" + ), + 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" ) - } + ) } - .registerTypeAdapter { - with(ConnectOptions()) { - ConnectOptions( - url = it.getOrDefault( - "url", - url, - "The URL or IP address of the bridge platform" - ), - authToken = it.getOrDefault( - "authToken", - authToken, - "Auth token used to connect to the bridge platform" - ), - autoConnect = it.getOrDefault( - "autoConnect", - autoConnect, - "Connect the relay on startup" - ), - reconnectWait = it.getOrDefault( - "reconnectWait", - reconnectWait, - "base delay in milliseconds between attempting reconnects" - ) + } + .registerTypeAdapter { + with(ConnectOptions()) { + ConnectOptions( + url = it.getOrDefault( + "url", + url, + "The URL or IP address of the bridge platform" + ), + authToken = it.getOrDefault( + "authToken", + authToken, + "Auth token used to connect to the bridge platform" + ), + autoConnect = it.getOrDefault( + "autoConnect", + autoConnect, + "Connect the relay on startup" + ), + reconnectWait = it.getOrDefault( + "reconnectWait", + reconnectWait, + "base delay in milliseconds between attempting reconnects" ) - } + ) } - .registerTypeAdapter { - with(IncomingOptions()) { - IncomingOptions( - chat = it.getOrDefault( - "chat", - chat, - "Generic chat event, just talking" - ), - joinPart = it.getOrDefault( - "joinPart", - joinPart, - "Join and part events from other gateways" - ), - action = it.getOrDefault( - "action", - action, - "User actions (/me) sent by users from other gateways" - ), - stripColors = it.getOrDefault( - "stripColors", - stripColors, - "strip colors from incoming text" - ) + } + .registerTypeAdapter { + with(IncomingOptions()) { + IncomingOptions( + chat = it.getOrDefault( + "chat", + chat, + "Generic chat event, just talking" + ), + joinPart = it.getOrDefault( + "joinPart", + joinPart, + "Join and part events from other gateways" + ), + action = it.getOrDefault( + "action", + action, + "User actions (/me) sent by users from other gateways" + ), + stripColors = it.getOrDefault( + "stripColors", + stripColors, + "strip colors from incoming text" ) - } + ) } - .registerTypeAdapter { - with(OutgoingOptions()) { - OutgoingOptions( - systemUser = it.getOrDefault( - "systemUser", - systemUser, - "Name of the platform user (used by death and advancement messages and the /say command)" - ), - advancements = it.getOrDefault( - "advancements", - advancements, - "Relay player achievements / advancements" - ), - announceConnect = it.getOrDefault( - "announceConnect", - announceConnect, - "announce successful connection to the gateway" - ), - announceDisconnect = it.getOrDefault( - "announceDisconnect", - announceConnect, - "announce intention to disconnect / reconnect" - ), - stripColors = it.getOrDefault( - "stripColors", - stripColors, - "strip colors from nicknames and messages" - ), - pasteEEKey = it.getOrDefault( - "pasteEEKey", - pasteEEKey, - "paste.ee api key, leave empty to use application default" - ), - inlineLimit = it.getOrDefault( - "inlineLimit", - inlineLimit, - "messages with more lines than this will get shortened via paste.ee" - ), - death = it.getOrDefault( - "death", - DeathOptions(), - "Death messages settings" - ), - avatar = it.getOrDefault( - "avatar", - AvatarOptions(), - "Avatar options" - ), - joinPart = it.getOrDefault( - "joinPart", - JoinPartOptions(), - "relay join and part messages to the gatway" - ) + } + .registerTypeAdapter { + with(OutgoingOptions()) { + OutgoingOptions( + systemUser = it.getOrDefault( + "systemUser", + systemUser, + "Name of the platform user (used by death and advancement messages and the /say command)" + ), + advancements = it.getOrDefault( + "advancements", + advancements, + "Relay player achievements / advancements" + ), + announceConnect = it.getOrDefault( + "announceConnect", + announceConnect, + "announce successful connection to the gateway" + ), + announceDisconnect = it.getOrDefault( + "announceDisconnect", + announceConnect, + "announce intention to disconnect / reconnect" + ), + stripColors = it.getOrDefault( + "stripColors", + stripColors, + "strip colors from nicknames and messages" + ), + pasteEEKey = it.getOrDefault( + "pasteEEKey", + pasteEEKey, + "paste.ee api key, leave empty to use application default" + ), + inlineLimit = it.getOrDefault( + "inlineLimit", + inlineLimit, + "messages with more lines than this will get shortened via paste.ee" + ), + death = it.getOrDefault( + "death", + DeathOptions(), + "Death messages settings" + ), + avatar = it.getOrDefault( + "avatar", + AvatarOptions(), + "Avatar options" + ), + joinPart = it.getOrDefault( + "joinPart", + JoinPartOptions(), + "relay join and part messages to the gatway" ) - } + ) } - .registerTypeAdapter { jsonObj -> - with(DeathOptions()) { - DeathOptions( - enable = jsonObj.getOrDefault( - "enable", - enable, - "Relay player death messages" - ), - damageType = jsonObj.getOrDefault( - "damageType", - damageType, - "Enable Damage type symbols on death messages" - ), - damageTypeMapping = (jsonObj.getObject("damageTypeMapping") - ?: Marshaller.getFallback().serialize(damageTypeMapping) as JsonObject) - .let { - jsonObj.setComment( - "damageTypMapping", - "Damage type mapping for death cause" - ) - it.mapValues { (key, element) -> - it.getOrDefault(key, damageTypeMapping[key] ?: emptyArray(), key) - .apply { it[key] }.apply { - jsonObj["damageTypeMapping"] = it - } - } + } + .registerTypeAdapter { jsonObj -> + with(DeathOptions()) { + DeathOptions( + enable = jsonObj.getOrDefault( + "enable", + enable, + "Relay player death messages" + ), + damageType = jsonObj.getOrDefault( + "damageType", + damageType, + "Enable Damage type symbols on death messages" + ), + damageTypeMapping = (jsonObj.getObject("damageTypeMapping") + ?: Marshaller.getFallback().serialize(damageTypeMapping) as JsonObject) + .let { + jsonObj.setComment( + "damageTypMapping", + "Damage type mapping for death cause" + ) + it.mapValues { (key, _) -> + it.getOrDefault(key, damageTypeMapping[key] ?: emptyArray(), key) + .apply { it[key] }.apply { + jsonObj["damageTypeMapping"] = it } + } + } + ) + } + } + .registerTypeAdapter { + with(AvatarOptions()) { + AvatarOptions( + enable = it.getOrDefault( + "enable", + enable, + "enable ingame avatar" + ), + urlTemplate = it.getOrDefault( + "urlTemplate", + urlTemplate, + "template for constructing the user avatar url using the uuid" ) - } + ) } - .registerTypeAdapter { - with(AvatarOptions()) { - AvatarOptions( - enable = it.getOrDefault( - "enable", - enable, - "enable ingame avatar" - ), - urlTemplate = it.getOrDefault( - "urlTemplate", - urlTemplate, - "template for constructing the user avatar url using the uuid" - ) + } + .registerTypeAdapter { + with(JoinPartOptions()) { + JoinPartOptions( + enable = it.getOrDefault( + "enable", + enable, + "Relay when a player joins / parts the game" + + "\nany receiving end still needs to be configured with showJoinPart = true" + + "\nto display the messages" + ), + joinServer = it.getOrDefault( + "joinServer", + joinServer, + "user join message sent to other gateways, available variables: {username}, {username:antiping}" + ), + partServer = it.getOrDefault( + "partServer", + partServer, + "user part message sent to other gateways, available variables: {username}, {username:antiping}" ) - } + ) } - .registerTypeAdapter { - with(JoinPartOptions()) { - JoinPartOptions( - enable = it.getOrDefault( - "enable", - enable, - "Relay when a player joins / parts the game" + - "\nany receiving end still needs to be configured with showJoinPart = true" + - "\nto display the messages" - ), - joinServer = it.getOrDefault( - "joinServer", - joinServer, - "user join message sent to other gateways, available variables: {username}, {username:antiping}" - ), - partServer = it.getOrDefault( - "partServer", - partServer, - "user part message sent to other gateways, available variables: {username}, {username:antiping}" - ) + } + .registerTypeAdapter { + with(UpdateOptions()) { + UpdateOptions( + enable = it.getOrDefault( + "enable", + enable, + "Enable Update checking" ) - } + ) } - .registerTypeAdapter { - with(UpdateOptions()) { - UpdateOptions( - enable = it.getOrDefault( - "enable", - enable, - "Enable Update checking" - ) - ) - } + } + .registerSerializer { locationSettings: SettingsOutgoing, marshaller: Marshaller -> + val jsonObject = JsonObject() + locationSettings.advancement?.let { + jsonObject["advancements"] = marshaller.serialize(it) } - .registerSerializer { locationSettings: SettingsOutgoing, marshaller: Marshaller -> - val jsonObject = JsonObject() - locationSettings.advancement?.let { - jsonObject["advancements"] = marshaller.serialize(it) - } - locationSettings.death?.let { - jsonObject["death"] = marshaller.serialize(it) - } - locationSettings.join?.let { - jsonObject["joins"] = marshaller.serialize(it) - } - locationSettings.leave?.let { - jsonObject["leaves"] = marshaller.serialize(it) - } - locationSettings.broadcast?.let { - jsonObject["say"] = marshaller.serialize(it) - } - jsonObject + locationSettings.death?.let { + jsonObject["death"] = marshaller.serialize(it) } - .build() + locationSettings.join?.let { + jsonObject["joins"] = marshaller.serialize(it) + } + locationSettings.leave?.let { + jsonObject["leaves"] = marshaller.serialize(it) + } + locationSettings.broadcast?.let { + jsonObject["say"] = marshaller.serialize(it) + } + jsonObject + } + .build()!! } fun load(): MatterLinkConfig { diff --git a/core/src/main/kotlin/matterlink/config/CommandConfig.kt b/core/src/main/kotlin/matterlink/config/CommandConfig.kt index a932367..70f72ac 100644 --- a/core/src/main/kotlin/matterlink/config/CommandConfig.kt +++ b/core/src/main/kotlin/matterlink/config/CommandConfig.kt @@ -4,9 +4,12 @@ import blue.endless.jankson.Jankson import blue.endless.jankson.JsonObject import blue.endless.jankson.JsonPrimitive import blue.endless.jankson.impl.SyntaxError -import matterlink.* import matterlink.bridge.command.CommandType import matterlink.bridge.command.CustomCommand +import matterlink.getOrDefault +import matterlink.logger +import matterlink.registerPrimitiveTypeAdapter +import matterlink.registerTypeAdapter import java.io.File import java.io.FileNotFoundException @@ -17,89 +20,89 @@ object CommandConfig { private val configFile: File = baseCfg.cfgDirectory.resolve("commands.hjson") private val default: DefaultCommands = mapOf( - "tps" to ("""Your run off the mill tps commands, change it to /sampler tps or /cofh tps if you like + "tps" to ("""Your run off the mill tps commands, change it to /sampler tps or /cofh tps if you like |make sure to disable defaultCommand if you want your edits to have any effect """.trimMargin() - to CustomCommand( - type = CommandType.EXECUTE, - execute = "forge tps", - help = "Print platform tps", - timeout = 200, - defaultCommand = true - )), - "list" to ("lists all the players, this is just a straight pass-through" - to CustomCommand( - type = CommandType.EXECUTE, - execute = "list", - help = "List online players", - defaultCommand = true - )), - "seed" to ("another straight pass-through" - to CustomCommand( - type = CommandType.EXECUTE, - execute = "seed", - help = "Print platform world seed", - defaultCommand = true - )), - "uptime" to ("this is a reponse command, it uses the uptime function, time since the mod was first loaded" - to CustomCommand( - type = CommandType.RESPONSE, - response = "{uptime}", - help = "Print platform uptime", - defaultCommand = true - )), - "whoami" to ("this shows you some of the other response macros" - to CustomCommand( - type = CommandType.RESPONSE, - response = "name: `{user}` userid: `{userid}` platform: `{platform}` username: `{username}` uuid: `{uuid}`", - help = "Print debug user data", - timeout = 200, - defaultCommand = true - )), - "version" to ("are you out of date huh ?" - to CustomCommand( - type = CommandType.RESPONSE, - response = "{version}", - help = "are you out of date huh ?", - timeout = 200, - defaultCommand = true - )), - "exec" to ("this uses arguments in a passed-through command, you could restrict the arguments with a regex" - to CustomCommand( - type = CommandType.EXECUTE, - execute = "{args}", - argumentsRegex = ".*".toRegex(), - permLevel = 50.0, - help = "Execute any command as OP, be careful with this one", - execOp = true, - defaultCommand = true - )) + to CustomCommand( + type = CommandType.EXECUTE, + execute = "forge tps", + help = "Print platform tps", + timeout = 200, + defaultCommand = true + )), + "list" to ("lists all the players, this is just a straight pass-through" + to CustomCommand( + type = CommandType.EXECUTE, + execute = "list", + help = "List online players", + defaultCommand = true + )), + "seed" to ("another straight pass-through" + to CustomCommand( + type = CommandType.EXECUTE, + execute = "seed", + help = "Print platform world seed", + defaultCommand = true + )), + "uptime" to ("this is a reponse command, it uses the uptime function, time since the mod was first loaded" + to CustomCommand( + type = CommandType.RESPONSE, + response = "{uptime}", + help = "Print platform uptime", + defaultCommand = true + )), + "whoami" to ("this shows you some of the other response macros" + to CustomCommand( + type = CommandType.RESPONSE, + response = "name: `{user}` userid: `{userid}` platform: `{platform}` username: `{username}` uuid: `{uuid}`", + help = "Print debug user data", + timeout = 200, + defaultCommand = true + )), + "version" to ("are you out of date huh ?" + to CustomCommand( + type = CommandType.RESPONSE, + response = "{version}", + help = "are you out of date huh ?", + timeout = 200, + defaultCommand = true + )), + "exec" to ("this uses arguments in a passed-through command, you could restrict the arguments with a regex" + to CustomCommand( + type = CommandType.EXECUTE, + execute = "{args}", + argumentsRegex = ".*".toRegex(), + permLevel = 50.0, + help = "Execute any command as OP, be careful with this one", + execOp = true, + defaultCommand = true + )) ) val commands: CommandMap = hashMapOf() fun loadFile() { val jankson = Jankson - .builder() - .registerTypeAdapter { jsonObj -> - with(CustomCommand.DEFAULT) { - CustomCommand( - type = jsonObj.get(CommandType::class.java, "type") ?: type, - execute = jsonObj.get(String::class.java, "execute") ?: execute, - response = jsonObj.get(String::class.java, "response") ?: response, - permLevel = jsonObj.get(Double::class.java, "permLevel") ?: permLevel, - help = jsonObj.get(String::class.java, "help") ?: help, - timeout = jsonObj.get(Int::class.java, "timeout") ?: timeout, - defaultCommand = jsonObj.get(Boolean::class.java, "defaultCommand") ?: defaultCommand, - execOp = jsonObj.get(Boolean::class.java, "execOp") ?: execOp, - argumentsRegex = jsonObj.get(Regex::class.java, "argumentsRegex") ?: argumentsRegex - ) - } + .builder() + .registerTypeAdapter { jsonObj -> + with(CustomCommand.DEFAULT) { + CustomCommand( + type = jsonObj.get(CommandType::class.java, "type") ?: type, + execute = jsonObj.get(String::class.java, "execute") ?: execute, + response = jsonObj.get(String::class.java, "response") ?: response, + permLevel = jsonObj.get(Double::class.java, "permLevel") ?: permLevel, + help = jsonObj.get(String::class.java, "help") ?: help, + timeout = jsonObj.get(Int::class.java, "timeout") ?: timeout, + defaultCommand = jsonObj.get(Boolean::class.java, "defaultCommand") ?: defaultCommand, + execOp = jsonObj.get(Boolean::class.java, "execOp") ?: execOp, + argumentsRegex = jsonObj.get(Regex::class.java, "argumentsRegex") ?: argumentsRegex + ) } - .registerPrimitiveTypeAdapter { - it.toString().toRegex() - } - .build() + } + .registerPrimitiveTypeAdapter { + it.toString().toRegex() + } + .build() jankson.marshaller.registerSerializer(Regex::class.java) { regex, _ -> JsonPrimitive(regex.pattern) diff --git a/core/src/main/kotlin/matterlink/config/IdentitiesConfig.kt b/core/src/main/kotlin/matterlink/config/IdentitiesConfig.kt index 60c126f..c440210 100644 --- a/core/src/main/kotlin/matterlink/config/IdentitiesConfig.kt +++ b/core/src/main/kotlin/matterlink/config/IdentitiesConfig.kt @@ -11,34 +11,34 @@ import matterlink.logger import matterlink.stackTraceString import java.io.File import java.io.FileNotFoundException -import java.util.* +import java.util.UUID import java.util.concurrent.TimeUnit typealias IdentMap = Map>> data class AuthRequest( - val username: String, - val uuid: String, - val nonce: String, - val platform: String, - val userid: String + val username: String, + val uuid: String, + val nonce: String, + val platform: String, + val userid: String ) object IdentitiesConfig { val authRequests: Cache = CacheBuilder.newBuilder() - .expireAfterWrite(10, TimeUnit.MINUTES) - .build() + .expireAfterWrite(10, TimeUnit.MINUTES) + .build() private val jankson = Jankson - .builder() - .build() + .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") + ("edd31c45-b095-49c5-a9f5-59cec4cfed8c" to mapOf( + "discord.game" to (listOf("112228624366575616") to "discord id") + ) to "username: NikkyAi") ) var idents: IdentMap = mapOf() diff --git a/core/src/main/kotlin/matterlink/config/PermissionConfig.kt b/core/src/main/kotlin/matterlink/config/PermissionConfig.kt index a84704f..5b35fd8 100644 --- a/core/src/main/kotlin/matterlink/config/PermissionConfig.kt +++ b/core/src/main/kotlin/matterlink/config/PermissionConfig.kt @@ -6,34 +6,33 @@ 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 matterlink.logger import java.io.File import java.io.FileNotFoundException -import java.util.* +import java.util.UUID import java.util.concurrent.TimeUnit typealias PermissionMap = Map data class PermissionRequest( - val uuid: UUID, - val user: String, - val nonce: String, - val powerlevel: Double? = null + val uuid: UUID, + val user: String, + val nonce: String, + val powerlevel: Double? = null ) object PermissionConfig { val permissionRequests: Cache = CacheBuilder.newBuilder() - .expireAfterWrite(10, TimeUnit.MINUTES) - .build() + .expireAfterWrite(10, TimeUnit.MINUTES) + .build() private val jankson = Jankson - .builder() - .build() + .builder() + .build() private val configFile: File = baseCfg.cfgDirectory.resolve("permissions.hjson") private val default = mapOf( - "edd31c45-b095-49c5-a9f5-59cec4cfed8c" to 9000.0 to "Superuser" + "edd31c45-b095-49c5-a9f5-59cec4cfed8c" to 9000.0 to "Superuser" ) val perms: PermissionMap = mutableMapOf() diff --git a/core/src/main/kotlin/matterlink/handlers/ChatProcessor.kt b/core/src/main/kotlin/matterlink/handlers/ChatProcessor.kt index 415da99..afaab53 100644 --- a/core/src/main/kotlin/matterlink/handlers/ChatProcessor.kt +++ b/core/src/main/kotlin/matterlink/handlers/ChatProcessor.kt @@ -1,29 +1,34 @@ package matterlink.handlers -import matterlink.api.ApiMessage -import matterlink.bridge.MessageHandlerInst import matterlink.bridge.command.BridgeCommandRegistry -import matterlink.config.cfg import matterlink.logger -import matterlink.stripColorOut -import java.util.* +import java.util.UUID object ChatProcessor { /** * @return cancel message flag */ - fun sendToBridge(user: String, msg: String, x: Int, y: Int, z: Int, dimension: Int?, event: ChatEvent, uuid: UUID? = null): Boolean { + suspend fun sendToBridge( + user: String, + msg: String, + x: Int, + y: Int, + z: Int, + dimension: Int?, + event: ChatEvent, + uuid: UUID? = null + ): Boolean { //TODO: pass message to Locations logger.info("position: $x $y $z dimension: $dimension") val message = msg.trim() if (uuid != null && BridgeCommandRegistry.handleCommand(message, user, uuid)) return true when { message.isNotBlank() -> LocationHandler.sendToLocations( - user = user, - msg = message, - x = x, y = y, z = z, dimension = dimension, - event = event, - cause = "Message from $user" + user = user, + msg = message, + x = x, y = y, z = z, dimension = dimension, + event = event, + cause = "Message from $user" ) diff --git a/core/src/main/kotlin/matterlink/handlers/DeathHandler.kt b/core/src/main/kotlin/matterlink/handlers/DeathHandler.kt index a084c8e..a71d762 100644 --- a/core/src/main/kotlin/matterlink/handlers/DeathHandler.kt +++ b/core/src/main/kotlin/matterlink/handlers/DeathHandler.kt @@ -1,36 +1,34 @@ package matterlink.handlers import matterlink.antiping -import matterlink.api.ApiMessage -import matterlink.bridge.MessageHandlerInst import matterlink.config.cfg import matterlink.stripColorOut -import java.util.* +import java.util.Random object DeathHandler { private val random = Random() - fun handleDeath( - player: String, - deathMessage: String, - damageType: String, - x: Int, y: Int, z: Int, - dimension: Int + suspend fun handleDeath( + player: String, + deathMessage: String, + damageType: String, + x: Int, y: Int, z: Int, + dimension: Int ) { if (cfg.outgoing.death.enable) { var msg = deathMessage.stripColorOut.replace(player, player.stripColorOut.antiping) if (cfg.outgoing.death.damageType) { val emojis = cfg.outgoing.death.damageTypeMapping[damageType] - ?: arrayOf("\uD83D\uDC7B unknown type '$damageType'") + ?: arrayOf("\uD83D\uDC7B unknown type '$damageType'") val damageEmoji = emojis[random.nextInt(emojis.size)] msg += " $damageEmoji" } LocationHandler.sendToLocations( - msg = msg, - x = x, y = y, z = z, dimension = dimension, - event = ChatEvent.DEATH, - cause = "Death Event of $player", - systemuser = true + msg = msg, + x = x, y = y, z = z, dimension = dimension, + event = ChatEvent.DEATH, + cause = "Death Event of $player", + systemuser = true ) } } diff --git a/core/src/main/kotlin/matterlink/handlers/JoinLeaveHandler.kt b/core/src/main/kotlin/matterlink/handlers/JoinLeaveHandler.kt index 2b639b6..23f8ffc 100644 --- a/core/src/main/kotlin/matterlink/handlers/JoinLeaveHandler.kt +++ b/core/src/main/kotlin/matterlink/handlers/JoinLeaveHandler.kt @@ -1,50 +1,51 @@ package matterlink.handlers import matterlink.antiping -import matterlink.api.ApiMessage -import matterlink.api.ApiMessage.Companion.JOIN_LEAVE -import matterlink.bridge.MessageHandlerInst import matterlink.config.cfg import matterlink.mapFormat import matterlink.stripColorOut object JoinLeaveHandler { - fun handleJoin(player: String, - x: Int, y: Int, z: Int, - dimension: Int) { + suspend fun handleJoin( + player: String, + x: Int, y: Int, z: Int, + dimension: Int + ) { if (cfg.outgoing.joinPart.enable) { val msg = cfg.outgoing.joinPart.joinServer.mapFormat( - mapOf( - "{username}" to player.stripColorOut, - "{username:antiping}" to player.stripColorOut.antiping - ) + mapOf( + "{username}" to player.stripColorOut, + "{username:antiping}" to player.stripColorOut.antiping + ) ) LocationHandler.sendToLocations( - msg = msg, - x = x, y = y, z = z, dimension = dimension, - event = ChatEvent.JOIN, - systemuser = true, - cause = "$player joined" + msg = msg, + x = x, y = y, z = z, dimension = dimension, + event = ChatEvent.JOIN, + systemuser = true, + cause = "$player joined" ) } } - fun handleLeave(player: String, - x: Int, y: Int, z: Int, - dimension: Int) { + suspend fun handleLeave( + player: String, + x: Int, y: Int, z: Int, + dimension: Int + ) { if (cfg.outgoing.joinPart.enable) { val msg = cfg.outgoing.joinPart.partServer.mapFormat( - mapOf( - "{username}" to player.stripColorOut, - "{username:antiping}" to player.stripColorOut.antiping - ) + mapOf( + "{username}" to player.stripColorOut, + "{username:antiping}" to player.stripColorOut.antiping + ) ) LocationHandler.sendToLocations( - msg = msg, - x = x, y = y, z = z, dimension = dimension, - event = ChatEvent.JOIN, - systemuser = true, - cause = "$player left" + msg = msg, + x = x, y = y, z = z, dimension = dimension, + event = ChatEvent.JOIN, + systemuser = true, + cause = "$player left" ) } } diff --git a/core/src/main/kotlin/matterlink/handlers/LocationHandler.kt b/core/src/main/kotlin/matterlink/handlers/LocationHandler.kt index 9433bde..9359a0b 100644 --- a/core/src/main/kotlin/matterlink/handlers/LocationHandler.kt +++ b/core/src/main/kotlin/matterlink/handlers/LocationHandler.kt @@ -5,7 +5,7 @@ import matterlink.bridge.MessageHandlerInst import matterlink.config.cfg import matterlink.logger import matterlink.stripColorOut -import java.util.* +import java.util.UUID enum class ChatEvent { @@ -14,31 +14,31 @@ enum class ChatEvent { object LocationHandler { - fun sendToLocations( - user: String = cfg.outgoing.systemUser, - msg: String, - x: Int, y: Int, z: Int, - dimension: Int?, - event: ChatEvent, - systemuser: Boolean = false, - uuid: UUID? = null, - cause: String + suspend fun sendToLocations( + user: String = cfg.outgoing.systemUser, + msg: String, + x: Int, y: Int, z: Int, + dimension: Int?, + event: ChatEvent, + systemuser: Boolean = false, + uuid: UUID? = null, + cause: String ): Boolean { val defaults = cfg.outgoingDefaults var handled = false val skips = mutableSetOf() - logger.info("locations: ${cfg.locations.map { it.label }}") + logger.info("locations: ${cfg.locations.map { it.label }}") for (location in cfg.locations) { val label = location.label - if(skips.contains(label)) { + if (skips.contains(label)) { logger.info("skipping $label (contained in in $skips)") continue } - if(!location.area.testForDim(dimension)) { + if (!location.area.testForDim(dimension)) { logger.info("location: $label dropped message '$msg' from $user due to mismatched dimension") continue - } - if(!location.area.testInBounds(x, y, z)) { + } + if (!location.area.testInBounds(x, y, z)) { logger.info("location: $label dropped message '$msg' from $user out of coordinate bounds") continue } @@ -85,17 +85,17 @@ object LocationHandler { } when { msg.isNotBlank() -> MessageHandlerInst.transmit( - ApiMessage( - username = username.stripColorOut, - text = msg.stripColorOut, - event = eventStr, - gateway = location.gateway - ).apply { - avatar?.let { - this.avatar = it - } - }, - cause = cause + ApiMessage( + username = username.stripColorOut, + text = msg.stripColorOut, + event = eventStr, + gateway = location.gateway + ).apply { + avatar?.let { + this.avatar = it + } + }, + cause = cause ) else -> logger.warn("WARN: dropped blank message by '$user'") } diff --git a/core/src/main/kotlin/matterlink/handlers/ProgressHandler.kt b/core/src/main/kotlin/matterlink/handlers/ProgressHandler.kt index 9e74533..4058a3c 100644 --- a/core/src/main/kotlin/matterlink/handlers/ProgressHandler.kt +++ b/core/src/main/kotlin/matterlink/handlers/ProgressHandler.kt @@ -1,24 +1,24 @@ package matterlink.handlers import matterlink.antiping -import matterlink.api.ApiMessage -import matterlink.bridge.MessageHandlerInst import matterlink.config.cfg import matterlink.stripColorOut object ProgressHandler { - fun handleProgress(name: String, message: String, display: String, - x: Int, y: Int, z: Int, - dimension: Int) { + suspend fun handleProgress( + name: String, message: String, display: String, + x: Int, y: Int, z: Int, + dimension: Int + ) { if (!cfg.outgoing.advancements) return val usr = name.stripColorOut.antiping LocationHandler.sendToLocations( - msg = "$usr $message $display".stripColorOut, - x = x, y = y, z = z, dimension = dimension, - event = ChatEvent.ADVANCEMENT, - cause = "Progress Event by $usr", - systemuser = true + msg = "$usr $message $display".stripColorOut, + x = x, y = y, z = z, dimension = dimension, + event = ChatEvent.ADVANCEMENT, + cause = "Progress Event by $usr", + systemuser = true ) } } \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/handlers/ServerChatHandler.kt b/core/src/main/kotlin/matterlink/handlers/ServerChatHandler.kt index 6ae9a06..5862a9f 100644 --- a/core/src/main/kotlin/matterlink/handlers/ServerChatHandler.kt +++ b/core/src/main/kotlin/matterlink/handlers/ServerChatHandler.kt @@ -7,17 +7,16 @@ import matterlink.bridge.format import matterlink.config.cfg import matterlink.instance import matterlink.logger -import java.util.* +import java.util.UUID object ServerChatHandler { + val rcvChannel = MessageHandlerInst.broadcast.openSubscription() /** * This method must be called every server tick with no arguments. */ - fun writeIncomingToChat() { - if (MessageHandlerInst.queue.isNotEmpty()) - logger.debug("incoming: " + MessageHandlerInst.queue.toString()) - val nextMessage = MessageHandlerInst.queue.poll() ?: return + suspend fun writeIncomingToChat() { + val nextMessage = rcvChannel.poll() ?: return val defaults = cfg.incomingDefaults @@ -32,14 +31,14 @@ object ServerChatHandler { it.gateway == sourceGateway } - if(nextMessage.event.isEmpty()) { + if (nextMessage.event.isEmpty()) { // filter command handlers val commandLocations = locations.filter { it.incoming.commands ?: cfg.incomingDefaults.commands } // process potential command - for (( label, location) in commandLocations) { + for ((label, location) in commandLocations) { if (BridgeCommandRegistry.handleCommand(nextMessage)) return } } @@ -48,7 +47,7 @@ object ServerChatHandler { for (location in locations) { val label = location.label - if(skips.contains(label)) { + if (skips.contains(label)) { logger.debug("skipping $label") continue } @@ -101,7 +100,6 @@ object ServerChatHandler { } - // if (nextMessage?.gateway == cfg.connect.gateway) { // if (!nextMessage.text.isBlank()) { // nextMessage.text = nextMessage.text.stripColorIn diff --git a/core/src/main/kotlin/matterlink/handlers/TickHandler.kt b/core/src/main/kotlin/matterlink/handlers/TickHandler.kt index fd6f434..468ee03 100644 --- a/core/src/main/kotlin/matterlink/handlers/TickHandler.kt +++ b/core/src/main/kotlin/matterlink/handlers/TickHandler.kt @@ -1,6 +1,5 @@ package matterlink.handlers -import matterlink.bridge.MessageHandlerInst import matterlink.update.UpdateChecker /** @@ -13,17 +12,17 @@ object TickHandler { private set private var accumulator = 0 private const val updateInterval = 12 * 60 * 60 * 20 - fun handleTick() { + suspend fun handleTick() { tickCounter++ - if (tickCounter % 100 == 0) { - MessageHandlerInst.checkConnection() - } +// if (tickCounter % 100 == 0) { +// MessageHandlerInst.checkConnection() +// } ServerChatHandler.writeIncomingToChat() if (accumulator++ > updateInterval) { accumulator -= updateInterval - UpdateChecker.run() + UpdateChecker.check() } } } \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/jenkins/Artifact.kt b/core/src/main/kotlin/matterlink/jenkins/Artifact.kt index ea8226e..7341ac3 100644 --- a/core/src/main/kotlin/matterlink/jenkins/Artifact.kt +++ b/core/src/main/kotlin/matterlink/jenkins/Artifact.kt @@ -7,7 +7,7 @@ package matterlink.jenkins //@JsonIgnoreProperties(ignoreUnknown = true) data class Artifact( - val displayPath: String, - val fileName: String, - val relativePath: String + val displayPath: String, + val fileName: String, + val relativePath: String ) \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/jenkins/Build.kt b/core/src/main/kotlin/matterlink/jenkins/Build.kt index c67b72e..c3cc095 100644 --- a/core/src/main/kotlin/matterlink/jenkins/Build.kt +++ b/core/src/main/kotlin/matterlink/jenkins/Build.kt @@ -13,15 +13,15 @@ import matterlink.logger //@JsonIgnoreProperties(ignoreUnknown = true) data class Build( - val number: Int, - val url: String + val number: Int, + val url: String ) { fun details(userAgent: String): BuildWithDetails? { val (request, response, result) = "$url/api/json" - .httpGet() - .header("User-Agent" to userAgent) - .responseString() - return when(result) { + .httpGet() + .header("User-Agent" to userAgent) + .responseString() + return when (result) { is Result.Success -> { gson.fromJson(result.value, BuildWithDetails::class.java) } diff --git a/core/src/main/kotlin/matterlink/jenkins/BuildWithDetails.kt b/core/src/main/kotlin/matterlink/jenkins/BuildWithDetails.kt index 69f5d11..f308412 100644 --- a/core/src/main/kotlin/matterlink/jenkins/BuildWithDetails.kt +++ b/core/src/main/kotlin/matterlink/jenkins/BuildWithDetails.kt @@ -1,12 +1,12 @@ package matterlink.jenkins -import java.util.* +import java.util.Date //@JsonIgnoreProperties(ignoreUnknown = true) data class BuildWithDetails( - val number: Int, - val url: String, - val artifacts: List, + val number: Int, + val url: String, + val artifacts: List, // @JsonFormat(shape=JsonFormat.Shape.NUMBER, pattern="s") - val timestamp: Date + val timestamp: Date ) \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/jenkins/JenkinsServer.kt b/core/src/main/kotlin/matterlink/jenkins/JenkinsServer.kt index bd3a7ec..8da7a8c 100644 --- a/core/src/main/kotlin/matterlink/jenkins/JenkinsServer.kt +++ b/core/src/main/kotlin/matterlink/jenkins/JenkinsServer.kt @@ -19,9 +19,9 @@ class JenkinsServer(val url: String) { fun getJob(job: String, userAgent: String): Job? { val requestURL = getUrl(job) + "/api/json" val (_, _, result) = requestURL - .httpGet() - .header("User-Agent" to userAgent) - .responseString() + .httpGet() + .header("User-Agent" to userAgent) + .responseString() return when (result) { is Result.Success -> { gson.fromJson(result.value, Job::class.java) diff --git a/core/src/main/kotlin/matterlink/jenkins/Job.kt b/core/src/main/kotlin/matterlink/jenkins/Job.kt index 29178af..fba7d3e 100644 --- a/core/src/main/kotlin/matterlink/jenkins/Job.kt +++ b/core/src/main/kotlin/matterlink/jenkins/Job.kt @@ -7,14 +7,14 @@ package matterlink.jenkins //@JsonIgnoreProperties(ignoreUnknown = true) data class Job( - val url: String, - val name: String, - val fullName: String, - val displayName: String, - val fullDisplayName: String, - val builds: List?, - val lastSuccessfulBuild: Build?, - val lastStableBuild: Build? + val url: String, + val name: String, + val fullName: String, + val displayName: String, + val fullDisplayName: String, + val builds: List?, + val lastSuccessfulBuild: Build?, + val lastStableBuild: Build? ) { fun getBuildByNumber(build: Int, userAgent: String): BuildWithDetails? { return builds?.find { it.number == build }?.details(userAgent) diff --git a/core/src/main/kotlin/matterlink/update/CurseFile.kt b/core/src/main/kotlin/matterlink/update/CurseFile.kt index 6187589..15d9d64 100644 --- a/core/src/main/kotlin/matterlink/update/CurseFile.kt +++ b/core/src/main/kotlin/matterlink/update/CurseFile.kt @@ -1,9 +1,9 @@ package matterlink.update data class CurseFile( - val downloadURL: String, - val fileName: String, - val gameVersion: List, - val releaseType: String, - val fileStatus: String + val downloadURL: String, + val fileName: String, + val gameVersion: List, + val releaseType: String, + val fileStatus: String ) \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/update/UpdateChecker.kt b/core/src/main/kotlin/matterlink/update/UpdateChecker.kt index 19e8159..9c3c8b5 100644 --- a/core/src/main/kotlin/matterlink/update/UpdateChecker.kt +++ b/core/src/main/kotlin/matterlink/update/UpdateChecker.kt @@ -1,39 +1,38 @@ package matterlink.update import com.google.gson.GsonBuilder +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import matterlink.api.ApiMessage import matterlink.bridge.MessageHandlerInst import matterlink.config.cfg import matterlink.handlers.ChatEvent import matterlink.handlers.LocationHandler import matterlink.instance -import matterlink.logger import matterlink.jenkins.JenkinsServer +import matterlink.logger import java.io.BufferedReader import java.net.HttpURLConnection import java.net.URL -class UpdateChecker : Thread() { - companion object { - fun run() { - if (cfg.update.enable) { - UpdateChecker().start() - } +object UpdateChecker : CoroutineScope { + override val coroutineContext = Job() + CoroutineName("UpdateChacker") + + suspend fun check() { + if (cfg.update.enable) { + run() } } - init { - name = "UpdateCheckerThread" - } - - override fun run() { + private suspend fun run() { if (instance.buildNumber > 0) { val server = JenkinsServer("https://ci.elytradev.com") val job = server.getJob("elytra/MatterLink/master", "MatterLink/${instance.modVersion}") - ?: run { - logger.error("failed obtaining job: elytra/MatterLink/master") - return - } + ?: run { + logger.error("failed obtaining job: elytra/MatterLink/master") + return + } //TODO: add job name to constants at build time val build = job.lastSuccessfulBuild ?: run { logger.error("no successful build found") @@ -45,10 +44,10 @@ class UpdateChecker : Thread() { logger.warn("Mod out of date! New build $number available at $url") val difference = number - build.number LocationHandler.sendToLocations( - msg = "MatterLink out of date! You are $difference builds behind! Please download new version from $url", - x = 0, y =0, z = 0, dimension = null, - event = ChatEvent.STATUS, - cause = "MatterLink update notice" + msg = "MatterLink out of date! You are $difference builds behind! Please download new version from $url", + x = 0, y = 0, z = 0, dimension = null, + event = ChatEvent.STATUS, + cause = "MatterLink update notice" ) } number < instance.buildNumber -> logger.error("lastSuccessfulBuild: $number is older than installed build: ${instance.buildNumber}") @@ -64,7 +63,7 @@ class UpdateChecker : Thread() { val gson = GsonBuilder() // .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) - .create() + .create() logger.info("Checking for new versions...") @@ -72,7 +71,8 @@ class UpdateChecker : Thread() { val con = url.openConnection() as HttpURLConnection with(instance) { - val useragent = "MatterLink/$modVersion MinecraftForge/$mcVersion-$forgeVersion (https://github.com/elytra/MatterLink)" + val useragent = + "MatterLink/$modVersion MinecraftForge/$mcVersion-$forgeVersion (https://github.com/elytra/MatterLink)" logger.debug("setting User-Agent: '$useragent'") con.setRequestProperty("User-Agent", useragent) } @@ -87,10 +87,10 @@ class UpdateChecker : Thread() { logger.trace("updateData: $content") gson.fromJson(content, Array::class.java) - .filter { - it.fileStatus == "SemiNormal" && it.gameVersion.contains(instance.mcVersion) - } - .sortedByDescending { it.fileName.substringAfterLast(" ") } + .filter { + it.fileStatus == "SemiNormal" && it.gameVersion.contains(instance.mcVersion) + } + .sortedByDescending { it.fileName.substringAfterLast(" ") } } else { logger.error("Could not check for updates!") @@ -98,12 +98,12 @@ class UpdateChecker : Thread() { } val modVersionChunks = instance.modVersion - .substringBefore("-dev") - .substringBefore("-build") - .split('.') - .map { - it.toInt() - } + .substringBefore("-dev") + .substringBefore("-build") + .split('.') + .map { + it.toInt() + } val possibleUpdates = mutableListOf() apiUpdateList.forEach { @@ -138,9 +138,9 @@ class UpdateChecker : Thread() { logger.warn("Mod out of date! New $version available at ${latest.downloadURL}") MessageHandlerInst.transmit( - ApiMessage( - text = "MatterLink out of date! You are $count $version behind! Please download new version from ${latest.downloadURL}" - ) + ApiMessage( + text = "MatterLink out of date! You are $count $version behind! Please download new version from ${latest.downloadURL}" + ) ) } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index dcebd1d..9c0e860 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,10 +1,10 @@ modName = MatterLink modVersion = 1.6.4 -forgelinVersion = 1.6.0 -kotlinVersion = 1.2.41 +forgelinVersion = 1.8.0 +kotlinVersion = 1.3.0 shadowVersion = 2.0.2 -fuelVersion = 1.13.0 -resultVersion = 1.4.0 -cursegradleVersion = 1.0.10 +fuelVersion = 1.16.0 +resultVersion = 1.6.0 +cursegradleVersion = 1.1.0 curseId = 287323 curseReleaseType = beta \ No newline at end of file diff --git a/scripts/start.sh b/scripts/start.sh old mode 100644 new mode 100755 diff --git a/scripts/test12.sh b/scripts/test12.sh old mode 100644 new mode 100755 diff --git a/scripts/test7.sh b/scripts/test7.sh old mode 100644 new mode 100755 diff --git a/scripts/test9.sh b/scripts/test9.sh old mode 100644 new mode 100755