update to kotlin 1.3

[1.7.10] fixed send to player NPE
This commit is contained in:
NikkyAI 2018-11-02 04:13:29 +01:00
parent 3082d3e592
commit 34887a833c
62 changed files with 1682 additions and 1560 deletions

2
.gitignore vendored
View File

@ -102,3 +102,5 @@ run/
\.floo \.floo
\.flooignore \.flooignore
bugreport/

View File

@ -114,4 +114,8 @@ curseforge {
displayName = "MatterLink $version" displayName = "MatterLink $version"
} }
} }
}
runServer {
outputs.upToDateWhen { false }
} }

View File

@ -1,3 +1,3 @@
mc_version = 1.12.2 mc_version = 1.12.2
mcp_mappings = snapshot_20171003 mcp_mappings = stable_39
forge_version = 14.23.1.2599 forge_version = 14.23.5.2768

View File

@ -1,7 +1,13 @@
package matterlink package matterlink
import kotlinx.coroutines.runBlocking
import matterlink.config.cfg 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.CommandBroadcast
import net.minecraft.command.server.CommandEmote import net.minecraft.command.server.CommandEmote
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
@ -22,40 +28,40 @@ object EventHandler {
//MC-VERSION & FORGE DEPENDENT //MC-VERSION & FORGE DEPENDENT
@SubscribeEvent @SubscribeEvent
@JvmStatic @JvmStatic
fun progressEvent(e: AdvancementEvent) { fun progressEvent(e: AdvancementEvent) = runBlocking {
if (e.advancement.display == null) return if (e.advancement.display == null) return@runBlocking
ProgressHandler.handleProgress( ProgressHandler.handleProgress(
name = e.entityPlayer.displayName.unformattedText, name = e.entityPlayer.displayName.unformattedText,
message = "has made the advancement", message = "has made the advancement",
display = e.advancement.displayText.unformattedText, display = e.advancement.displayText.unformattedText,
x = e.entityPlayer.posX.toInt(), x = e.entityPlayer.posX.toInt(),
y = e.entityPlayer.posY.toInt(), y = e.entityPlayer.posY.toInt(),
z = e.entityPlayer.posZ.toInt(), z = e.entityPlayer.posZ.toInt(),
dimension = e.entityPlayer.dimension dimension = e.entityPlayer.dimension
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
@JvmStatic @JvmStatic
fun chatEvent(e: ServerChatEvent) { fun chatEvent(e: ServerChatEvent) = runBlocking {
if (e.isCanceled) return if (e.isCanceled) return@runBlocking
e.isCanceled = ChatProcessor.sendToBridge( e.isCanceled = ChatProcessor.sendToBridge(
user = e.player.displayName.unformattedText, user = e.player.displayName.unformattedText,
msg = e.message, msg = e.message,
x = e.player.posX.toInt(), x = e.player.posX.toInt(),
y = e.player.posY.toInt(), y = e.player.posY.toInt(),
z = e.player.posZ.toInt(), z = e.player.posZ.toInt(),
dimension = e.player.dimension, dimension = e.player.dimension,
event = ChatEvent.PLAIN, event = ChatEvent.PLAIN,
uuid = e.player.gameProfile.id uuid = e.player.gameProfile.id
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
@JvmStatic @JvmStatic
fun commandEvent(e: CommandEvent) { fun commandEvent(e: CommandEvent) = runBlocking {
val sender = when { val sender = when {
e.sender is DedicatedServer -> cfg.outgoing.systemUser e.sender is DedicatedServer -> cfg.outgoing.systemUser
@ -66,36 +72,36 @@ object EventHandler {
when { when {
this is CommandEmote || name.equals("me", true) -> ChatEvent.ACTION this is CommandEmote || name.equals("me", true) -> ChatEvent.ACTION
this is CommandBroadcast || name.equals("say", true) -> ChatEvent.BROADCAST this is CommandBroadcast || name.equals("say", true) -> ChatEvent.BROADCAST
else -> return else -> return@runBlocking
} }
} }
ChatProcessor.sendToBridge( ChatProcessor.sendToBridge(
user = sender, user = sender,
msg = args, msg = args,
event = type, event = type,
x = e.sender.position.x, x = e.sender.position.x,
y = e.sender.position.y, y = e.sender.position.y,
z = e.sender.position.z, z = e.sender.position.z,
dimension = when { dimension = when {
e.sender is DedicatedServer -> null e.sender is DedicatedServer -> null
else -> e.sender.commandSenderEntity?.dimension ?: e.sender.entityWorld.provider.dimension else -> e.sender.commandSenderEntity?.dimension ?: e.sender.entityWorld.provider.dimension
} }
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
@JvmStatic @JvmStatic
fun deathEvent(e: LivingDeathEvent) { fun deathEvent(e: LivingDeathEvent) = runBlocking {
if (e.entityLiving is EntityPlayer) { if (e.entityLiving is EntityPlayer) {
DeathHandler.handleDeath( DeathHandler.handleDeath(
player = e.entityLiving.displayName.unformattedText, player = e.entityLiving.displayName.unformattedText,
deathMessage = e.entityLiving.combatTracker.deathMessage.unformattedText, deathMessage = e.entityLiving.combatTracker.deathMessage.unformattedText,
damageType = e.source.damageType, damageType = e.source.damageType,
x = e.entityLiving.posX.toInt(), x = e.entityLiving.posX.toInt(),
y = e.entityLiving.posY.toInt(), y = e.entityLiving.posY.toInt(),
z = e.entityLiving.posZ.toInt(), z = e.entityLiving.posZ.toInt(),
dimension = e.entityLiving.dimension dimension = e.entityLiving.dimension
) )
} }
} }
@ -103,34 +109,35 @@ object EventHandler {
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
@JvmStatic @JvmStatic
fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) { fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) = runBlocking {
JoinLeaveHandler.handleJoin( JoinLeaveHandler.handleJoin(
player = e.player.displayName.unformattedText, player = e.player.displayName.unformattedText,
x = e.player.posX.toInt(), x = e.player.posX.toInt(),
y = e.player.posY.toInt(), y = e.player.posY.toInt(),
z = e.player.posZ.toInt(), z = e.player.posZ.toInt(),
dimension = e.player.dimension dimension = e.player.dimension
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
@JvmStatic @JvmStatic
fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) { fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) = runBlocking {
JoinLeaveHandler.handleLeave( JoinLeaveHandler.handleLeave(
player = e.player.displayName.unformattedText, player = e.player.displayName.unformattedText,
x = e.player.posX.toInt(), x = e.player.posX.toInt(),
y = e.player.posY.toInt(), y = e.player.posY.toInt(),
z = e.player.posZ.toInt(), z = e.player.posZ.toInt(),
dimension = e.player.dimension dimension = e.player.dimension
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
@JvmStatic @JvmStatic
fun serverTickEvent(e: TickEvent.ServerTickEvent) { fun serverTickEvent(e: TickEvent.ServerTickEvent) = runBlocking {
if (e.phase == TickEvent.Phase.END) if (e.phase == TickEvent.Phase.END)
TickHandler.handleTick() TickHandler.handleTick()
} }
} }

View File

@ -1,7 +1,7 @@
package matterlink package matterlink
import com.mojang.authlib.GameProfile import com.mojang.authlib.GameProfile
import jline.internal.Log.warn import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IBridgeCommand
import matterlink.command.AuthCommand import matterlink.command.AuthCommand
import matterlink.command.MatterLinkCommand import matterlink.command.MatterLinkCommand
@ -9,9 +9,7 @@ import matterlink.command.MatterLinkCommandSender
import matterlink.config.BaseConfig import matterlink.config.BaseConfig
import matterlink.config.cfg import matterlink.config.cfg
import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString import net.minecraft.util.text.TextComponentString
import net.minecraftforge.common.DimensionManager
import net.minecraftforge.common.ForgeVersion import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.fml.common.FMLCommonHandler import net.minecraftforge.fml.common.FMLCommonHandler
import net.minecraftforge.fml.common.Mod 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 net.minecraftforge.fml.common.event.FMLServerStoppingEvent
import org.apache.logging.log4j.Level import org.apache.logging.log4j.Level
import org.apache.logging.log4j.core.config.Configurator import org.apache.logging.log4j.core.config.Configurator
import java.util.* import java.util.UUID
@Mod( @Mod(
modid = MODID, modid = MODID,
name = NAME, version = MODVERSION, name = NAME, version = MODVERSION,
serverSideOnly = true, serverSideOnly = true,
useMetadata = true, useMetadata = true,
acceptableRemoteVersions = "*", acceptableRemoteVersions = "*",
modLanguageAdapter = "net.shadowfacts.forgelin.KotlinAdapter", modLanguageAdapter = "net.shadowfacts.forgelin.KotlinAdapter",
dependencies = DEPENDENCIES dependencies = DEPENDENCIES
) )
object MatterLink : IMatterLink() { object MatterLink : IMatterLink() {
@ -59,6 +57,7 @@ object MatterLink : IMatterLink() {
@Mod.EventHandler @Mod.EventHandler
fun init(event: FMLInitializationEvent) { fun init(event: FMLInitializationEvent) {
logger.debug("Registering bridge commands")
this.registerBridgeCommands() this.registerBridgeCommands()
} }
@ -67,11 +66,13 @@ object MatterLink : IMatterLink() {
logger.debug("Registering server commands") logger.debug("Registering server commands")
event.registerServerCommand(MatterLinkCommand) event.registerServerCommand(MatterLinkCommand)
event.registerServerCommand(AuthCommand) event.registerServerCommand(AuthCommand)
start() runBlocking {
start()
}
} }
@Mod.EventHandler @Mod.EventHandler
fun serverStopping(event: FMLServerStoppingEvent) { fun serverStopping(event: FMLServerStoppingEvent) = runBlocking {
stop() stop()
} }
@ -104,21 +105,23 @@ object MatterLink : IMatterLink() {
player.sendMessage(TextComponentString(msg)) 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 { private fun profileByUUID(uuid: UUID): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getProfileByUUID(uuid) FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getProfileByUUID(uuid)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
warn("cannot find profile by uuid $uuid") logger.warn("cannot find profile by uuid $uuid")
null null
} }
private fun profileByName(username: String): GameProfile? = try { private fun profileByName(username: String): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getGameProfileForUsername(username) FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getGameProfileForUsername(username)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
warn("cannot find profile by username $username") logger.warn("cannot find profile by username $username")
null null
} }
@ -135,9 +138,9 @@ object MatterLink : IMatterLink() {
override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name
override fun commandSenderFor( override fun commandSenderFor(
user: String, user: String,
env: IBridgeCommand.CommandEnvironment, env: IBridgeCommand.CommandEnvironment,
op: Boolean op: Boolean
) = MatterLinkCommandSender(user, env, op) ) = MatterLinkCommandSender(user, env, op)
override val mcVersion: String = MCVERSION override val mcVersion: String = MCVERSION

View File

@ -1,5 +1,6 @@
package matterlink.command package matterlink.command
import kotlinx.coroutines.runBlocking
import net.minecraft.command.CommandBase import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException import net.minecraft.command.WrongUsageException
@ -21,9 +22,9 @@ object MatterLinkCommand : CommandBase() {
return CommandCoreML.aliases return CommandCoreML.aliases
} }
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) { override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) = runBlocking {
if (args.isEmpty()) { 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() val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()

View File

@ -1,5 +1,6 @@
package matterlink.command package matterlink.command
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IBridgeCommand
import matterlink.bridge.command.IMinecraftCommandSender import matterlink.bridge.command.IMinecraftCommandSender
import net.minecraft.command.ICommandSender import net.minecraft.command.ICommandSender
@ -11,14 +12,15 @@ import net.minecraftforge.fml.common.FMLCommonHandler
import javax.annotation.Nonnull import javax.annotation.Nonnull
class MatterLinkCommandSender( class MatterLinkCommandSender(
user: String, user: String,
env: IBridgeCommand.CommandEnvironment, env: IBridgeCommand.CommandEnvironment,
op: Boolean) : IMinecraftCommandSender(user, env, op), ICommandSender { op: Boolean
) : IMinecraftCommandSender(user, env, op), ICommandSender {
override fun execute(cmdString: String): Boolean { override fun execute(cmdString: String): Boolean = runBlocking {
return 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand( 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand(
this, this@MatterLinkCommandSender,
cmdString cmdString
).apply { ).apply {
sendReply(cmdString) sendReply(cmdString)
} }

View File

@ -11,17 +11,12 @@ buildscript {
} }
dependencies { dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '1.2-SNAPSHOT' 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: '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: 'forge'
//apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.matthewprenger.cursegradle' apply plugin: 'com.matthewprenger.cursegradle'
apply plugin: "com.vanniktech.dependency.graph.generator"
version = project.mc_version + '-' + project.modVersion version = project.mc_version + '-' + project.modVersion
@ -39,6 +34,12 @@ dependencies {
shade (project(':Jankson')) { transitive = false } shade (project(':Jankson')) { transitive = false }
shade group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion 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.fuel', name: 'fuel', version: fuelVersion
shade group: 'com.github.kittinunf.result', name: 'result', version: resultVersion shade group: 'com.github.kittinunf.result', name: 'result', version: resultVersion

View File

@ -3,9 +3,14 @@ package matterlink
import cpw.mods.fml.common.eventhandler.SubscribeEvent import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.common.gameevent.PlayerEvent import cpw.mods.fml.common.gameevent.PlayerEvent
import cpw.mods.fml.common.gameevent.TickEvent import cpw.mods.fml.common.gameevent.TickEvent
import matterlink.api.ApiMessage.Companion.USER_ACTION import kotlinx.coroutines.runBlocking
import matterlink.config.cfg 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.CommandBroadcast
import net.minecraft.command.server.CommandEmote import net.minecraft.command.server.CommandEmote
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
@ -22,45 +27,45 @@ object EventHandler {
//MC-VERSION & FORGE DEPENDENT //MC-VERSION & FORGE DEPENDENT
@SubscribeEvent @SubscribeEvent
fun progressEvent(e: AchievementEvent) { fun progressEvent(e: AchievementEvent) = runBlocking {
val achievement = e.achievement val achievement = e.achievement
val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return@runBlocking
val statFile = entityPlayer.statFile val statFile = entityPlayer.statFile
if (!statFile.canUnlockAchievement(achievement) || statFile.hasAchievementUnlocked(achievement)) { if (!statFile.canUnlockAchievement(achievement) || statFile.hasAchievementUnlocked(achievement)) {
return return@runBlocking
} }
ProgressHandler.handleProgress( ProgressHandler.handleProgress(
name = e.entityPlayer.displayName, name = e.entityPlayer.displayName,
message = "has earned the achievement", message = "has earned the achievement",
display = e.achievement.statName.unformattedText, display = e.achievement.statName.unformattedText,
x = e.entityPlayer.posX.toInt(), x = e.entityPlayer.posX.toInt(),
y = e.entityPlayer.posY.toInt(), y = e.entityPlayer.posY.toInt(),
z = e.entityPlayer.posZ.toInt(), z = e.entityPlayer.posZ.toInt(),
dimension = e.entityPlayer.dimension dimension = e.entityPlayer.dimension
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
fun chatEvent(e: ServerChatEvent) { fun chatEvent(e: ServerChatEvent) = runBlocking {
if(e.isCanceled) return if (e.isCanceled) return@runBlocking
e.isCanceled = ChatProcessor.sendToBridge( e.isCanceled = ChatProcessor.sendToBridge(
user = e.player.displayName, user = e.player.displayName,
msg = e.message, msg = e.message,
x = e.player.posX.toInt(), x = e.player.posX.toInt(),
y = e.player.posY.toInt(), y = e.player.posY.toInt(),
z = e.player.posZ.toInt(), z = e.player.posZ.toInt(),
dimension = e.player.dimension, dimension = e.player.dimension,
event = ChatEvent.PLAIN, event = ChatEvent.PLAIN,
uuid = e.player.gameProfile.id uuid = e.player.gameProfile.id
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
fun commandEvent(e: CommandEvent) { fun commandEvent(e: CommandEvent) = runBlocking {
val sender = when { val sender = when {
e.sender is DedicatedServer -> cfg.outgoing.systemUser e.sender is DedicatedServer -> cfg.outgoing.systemUser
else -> e.sender.commandSenderName else -> e.sender.commandSenderName
@ -70,72 +75,73 @@ object EventHandler {
when { when {
this is CommandEmote || commandName.equals("me", true) -> ChatEvent.ACTION this is CommandEmote || commandName.equals("me", true) -> ChatEvent.ACTION
this is CommandBroadcast || commandName.equals("say", true) -> ChatEvent.BROADCAST this is CommandBroadcast || commandName.equals("say", true) -> ChatEvent.BROADCAST
else -> return else -> return@runBlocking
} }
} }
val s = e.sender 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()) is Entity -> Triple(s.posX.toInt(), s.posY.toInt(), s.posZ.toInt())
else -> with(s.commandSenderPosition) { Triple(posX, posY, posZ) } else -> with(s.commandSenderPosition) { Triple(posX, posY, posZ) }
} }
ChatProcessor.sendToBridge( ChatProcessor.sendToBridge(
user = sender, user = sender,
msg = args, msg = args,
event = type, event = type,
x = x, x = x,
y = y, y = y,
z = z, z = z,
dimension = when { dimension = when {
e.sender is DedicatedServer -> null e.sender is DedicatedServer -> null
else -> e.sender.entityWorld.provider.dimensionId else -> e.sender.entityWorld.provider.dimensionId
} }
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
fun deathEvent(e: LivingDeathEvent) { fun deathEvent(e: LivingDeathEvent) = runBlocking {
if (e.entityLiving is EntityPlayer) { if (e.entityLiving is EntityPlayer) {
val player = e.entityLiving as EntityPlayer val player = e.entityLiving as EntityPlayer
DeathHandler.handleDeath( DeathHandler.handleDeath(
player = player.displayName, player = player.displayName,
deathMessage = e.entityLiving.combatTracker.func_151521_b().unformattedText, deathMessage = e.entityLiving.combatTracker.func_151521_b().unformattedText,
damageType = e.source.damageType, damageType = e.source.damageType,
x = e.entityLiving.posX.toInt(), x = e.entityLiving.posX.toInt(),
y = e.entityLiving.posY.toInt(), y = e.entityLiving.posY.toInt(),
z = e.entityLiving.posZ.toInt(), z = e.entityLiving.posZ.toInt(),
dimension = e.entityLiving.dimension dimension = e.entityLiving.dimension
) )
} }
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) { fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) = runBlocking {
JoinLeaveHandler.handleJoin( JoinLeaveHandler.handleJoin(
player = e.player.displayName, player = e.player.displayName,
x = e.player.posX.toInt(), x = e.player.posX.toInt(),
y = e.player.posY.toInt(), y = e.player.posY.toInt(),
z = e.player.posZ.toInt(), z = e.player.posZ.toInt(),
dimension = e.player.dimension) 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
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @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) if (e.phase == TickEvent.Phase.END)
TickHandler.handleTick() TickHandler.handleTick()
} }

View File

@ -7,6 +7,7 @@ import cpw.mods.fml.common.event.FMLInitializationEvent
import cpw.mods.fml.common.event.FMLPreInitializationEvent import cpw.mods.fml.common.event.FMLPreInitializationEvent
import cpw.mods.fml.common.event.FMLServerStartingEvent import cpw.mods.fml.common.event.FMLServerStartingEvent
import cpw.mods.fml.common.event.FMLServerStoppingEvent import cpw.mods.fml.common.event.FMLServerStoppingEvent
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IBridgeCommand
import matterlink.command.AuthCommand import matterlink.command.AuthCommand
import matterlink.command.MatterLinkCommand import matterlink.command.MatterLinkCommand
@ -18,13 +19,13 @@ import net.minecraft.server.MinecraftServer
import net.minecraft.util.ChatComponentText import net.minecraft.util.ChatComponentText
import net.minecraftforge.common.ForgeVersion import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.MinecraftForge
import java.util.* import java.util.UUID
@Mod( @Mod(
modid = MODID, modid = MODID,
name = NAME, version = MODVERSION, name = NAME, version = MODVERSION,
useMetadata = true, useMetadata = true,
acceptableRemoteVersions = "*" acceptableRemoteVersions = "*"
) )
class MatterLink : IMatterLink() { class MatterLink : IMatterLink() {
init { init {
@ -57,7 +58,7 @@ class MatterLink : IMatterLink() {
} }
@Mod.EventHandler @Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) { fun serverStarting(event: FMLServerStartingEvent) = runBlocking {
logger.debug("Registering server commands") logger.debug("Registering server commands")
event.registerServerCommand(MatterLinkCommand) event.registerServerCommand(MatterLinkCommand)
event.registerServerCommand(AuthCommand) event.registerServerCommand(AuthCommand)
@ -65,7 +66,7 @@ class MatterLink : IMatterLink() {
} }
@Mod.EventHandler @Mod.EventHandler
fun serverStopping(event: FMLServerStoppingEvent) { fun serverStopping(event: FMLServerStoppingEvent) = runBlocking {
stop() stop()
} }
@ -99,10 +100,11 @@ class MatterLink : IMatterLink() {
} }
override fun isOnline(username: String) = (FMLCommonHandler.instance() 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? { 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 { private fun profileByUUID(uuid: UUID): GameProfile? = try {
@ -121,11 +123,11 @@ class MatterLink : IMatterLink() {
override fun collectPlayers(area: Area): Set<UUID> { override fun collectPlayers(area: Area): Set<UUID> {
val players = MinecraftServer.getServer().configurationManager.playerEntityList val players = MinecraftServer.getServer().configurationManager.playerEntityList
.map { it as EntityPlayerMP } .map { it as EntityPlayerMP }
.filter { .filter {
(area.allDimensions || area.dimensions.contains(it.dimension)) (area.allDimensions || area.dimensions.contains(it.dimension))
&& area.testInBounds(it.posX.toInt(), it.posY.toInt(), it.posZ.toInt()) && area.testInBounds(it.posX.toInt(), it.posY.toInt(), it.posZ.toInt())
} }
return players.map { it.uniqueID }.toSet() return players.map { it.uniqueID }.toSet()
} }
@ -134,9 +136,9 @@ class MatterLink : IMatterLink() {
override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name
override fun commandSenderFor( override fun commandSenderFor(
user: String, user: String,
env: IBridgeCommand.CommandEnvironment, env: IBridgeCommand.CommandEnvironment,
op: Boolean op: Boolean
) = MatterLinkCommandSender(user, env, op) ) = MatterLinkCommandSender(user, env, op)
override val mcVersion: String = MCVERSION override val mcVersion: String = MCVERSION

View File

@ -1,5 +1,6 @@
package matterlink.command package matterlink.command
import kotlinx.coroutines.runBlocking
import net.minecraft.command.CommandBase import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException import net.minecraft.command.WrongUsageException
@ -20,9 +21,9 @@ object MatterLinkCommand : CommandBase() {
return CommandCoreML.aliases return CommandCoreML.aliases
} }
override fun processCommand(sender: ICommandSender, args: Array<String>) { override fun processCommand(sender: ICommandSender, args: Array<String>) = runBlocking {
if (args.isEmpty()) { 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() val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()

View File

@ -1,5 +1,6 @@
package matterlink.command package matterlink.command
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IBridgeCommand
import matterlink.bridge.command.IMinecraftCommandSender import matterlink.bridge.command.IMinecraftCommandSender
import net.minecraft.command.ICommandSender import net.minecraft.command.ICommandSender
@ -10,14 +11,15 @@ import net.minecraft.util.IChatComponent
import net.minecraft.world.World import net.minecraft.world.World
class MatterLinkCommandSender( class MatterLinkCommandSender(
user: String, user: String,
env: IBridgeCommand.CommandEnvironment, env: IBridgeCommand.CommandEnvironment,
op: Boolean) : IMinecraftCommandSender(user, env, op), ICommandSender { op: Boolean
) : IMinecraftCommandSender(user, env, op), ICommandSender {
override fun execute(cmdString: String): Boolean { override fun execute(cmdString: String): Boolean = runBlocking {
return 0 < MinecraftServer.getServer().commandManager.executeCommand( return@runBlocking 0 < MinecraftServer.getServer().commandManager.executeCommand(
this, this@MatterLinkCommandSender,
cmdString cmdString
).apply { ).apply {
sendReply(cmdString) sendReply(cmdString)
} }

View File

@ -1,7 +1,13 @@
package matterlink package matterlink
import kotlinx.coroutines.runBlocking
import matterlink.config.cfg 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.CommandBroadcast
import net.minecraft.command.server.CommandEmote import net.minecraft.command.server.CommandEmote
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
@ -20,44 +26,44 @@ object EventHandler {
//MC-VERSION & FORGE DEPENDENT //MC-VERSION & FORGE DEPENDENT
@SubscribeEvent @SubscribeEvent
fun progressEvent(e: AchievementEvent) { fun progressEvent(e: AchievementEvent) = runBlocking {
val achievement = e.achievement val achievement = e.achievement
val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return@runBlocking
val statFile = entityPlayer.statFile val statFile = entityPlayer.statFile
if (!statFile.canUnlockAchievement(achievement) || statFile.hasAchievementUnlocked(achievement)) { if (!statFile.canUnlockAchievement(achievement) || statFile.hasAchievementUnlocked(achievement)) {
return return@runBlocking
} }
ProgressHandler.handleProgress( ProgressHandler.handleProgress(
name = e.entityPlayer.displayName.unformattedText, name = e.entityPlayer.displayName.unformattedText,
message = "has earned the achievement", message = "has earned the achievement",
display = e.achievement.statName.unformattedText, display = e.achievement.statName.unformattedText,
x = e.entityPlayer.posX.toInt(), x = e.entityPlayer.posX.toInt(),
y = e.entityPlayer.posY.toInt(), y = e.entityPlayer.posY.toInt(),
z = e.entityPlayer.posZ.toInt(), z = e.entityPlayer.posZ.toInt(),
dimension = e.entityPlayer.dimension dimension = e.entityPlayer.dimension
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
fun chatEvent(e: ServerChatEvent) { fun chatEvent(e: ServerChatEvent) = runBlocking {
if(e.isCanceled) return if (e.isCanceled) return@runBlocking
e.isCanceled = ChatProcessor.sendToBridge( e.isCanceled = ChatProcessor.sendToBridge(
user = e.player.displayName.unformattedText, user = e.player.displayName.unformattedText,
msg = e.message, msg = e.message,
x = e.player.posX.toInt(), x = e.player.posX.toInt(),
y = e.player.posY.toInt(), y = e.player.posY.toInt(),
z = e.player.posZ.toInt(), z = e.player.posZ.toInt(),
dimension = e.player.dimension, dimension = e.player.dimension,
event = ChatEvent.PLAIN, event = ChatEvent.PLAIN,
uuid = e.player.gameProfile.id uuid = e.player.gameProfile.id
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
fun commandEvent(e: CommandEvent) { fun commandEvent(e: CommandEvent) = runBlocking {
val sender = when { val sender = when {
e.sender is DedicatedServer -> cfg.outgoing.systemUser e.sender is DedicatedServer -> cfg.outgoing.systemUser
else -> e.sender.displayName.unformattedText else -> e.sender.displayName.unformattedText
@ -67,66 +73,66 @@ object EventHandler {
when { when {
this is CommandEmote || commandName.equals("me", true) -> ChatEvent.ACTION this is CommandEmote || commandName.equals("me", true) -> ChatEvent.ACTION
this is CommandBroadcast || commandName.equals("say", true) -> ChatEvent.BROADCAST this is CommandBroadcast || commandName.equals("say", true) -> ChatEvent.BROADCAST
else -> return else -> return@runBlocking
} }
} }
ChatProcessor.sendToBridge( ChatProcessor.sendToBridge(
user = sender, user = sender,
msg = args, msg = args,
event = type, event = type,
x = e.sender.position.x, x = e.sender.position.x,
y = e.sender.position.y, y = e.sender.position.y,
z = e.sender.position.z, z = e.sender.position.z,
dimension = when { dimension = when {
e.sender is DedicatedServer -> null e.sender is DedicatedServer -> null
else -> e.sender.commandSenderEntity?.dimension ?: e.sender.entityWorld.provider.dimension else -> e.sender.commandSenderEntity?.dimension ?: e.sender.entityWorld.provider.dimension
} }
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
fun deathEvent(e: LivingDeathEvent) { fun deathEvent(e: LivingDeathEvent) = runBlocking {
if (e.entityLiving is EntityPlayer) { if (e.entityLiving is EntityPlayer) {
DeathHandler.handleDeath( DeathHandler.handleDeath(
player = e.entityLiving.displayName.unformattedText, player = e.entityLiving.displayName.unformattedText,
deathMessage = e.entityLiving.combatTracker.deathMessage.unformattedText, deathMessage = e.entityLiving.combatTracker.deathMessage.unformattedText,
damageType = e.source.damageType, damageType = e.source.damageType,
x = e.entityLiving.posX.toInt(), x = e.entityLiving.posX.toInt(),
y = e.entityLiving.posY.toInt(), y = e.entityLiving.posY.toInt(),
z = e.entityLiving.posZ.toInt(), z = e.entityLiving.posZ.toInt(),
dimension = e.entityLiving.dimension dimension = e.entityLiving.dimension
) )
} }
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) { fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) = runBlocking {
JoinLeaveHandler.handleJoin( JoinLeaveHandler.handleJoin(
player = e.player.displayName.unformattedText, player = e.player.displayName.unformattedText,
x = e.player.posX.toInt(), x = e.player.posX.toInt(),
y = e.player.posY.toInt(), y = e.player.posY.toInt(),
z = e.player.posZ.toInt(), z = e.player.posZ.toInt(),
dimension = e.player.dimension dimension = e.player.dimension
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) { fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) = runBlocking {
JoinLeaveHandler.handleLeave( JoinLeaveHandler.handleLeave(
player = e.player.displayName.unformattedText, player = e.player.displayName.unformattedText,
x = e.player.posX.toInt(), x = e.player.posX.toInt(),
y = e.player.posY.toInt(), y = e.player.posY.toInt(),
z = e.player.posZ.toInt(), z = e.player.posZ.toInt(),
dimension = e.player.dimension dimension = e.player.dimension
) )
} }
//FORGE-DEPENDENT //FORGE-DEPENDENT
@SubscribeEvent @SubscribeEvent
fun serverTickEvent(e: TickEvent.ServerTickEvent) { fun serverTickEvent(e: TickEvent.ServerTickEvent) = runBlocking {
if (e.phase == TickEvent.Phase.END) if (e.phase == TickEvent.Phase.END)
TickHandler.handleTick() TickHandler.handleTick()
} }

View File

@ -1,6 +1,7 @@
package matterlink package matterlink
import com.mojang.authlib.GameProfile import com.mojang.authlib.GameProfile
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IBridgeCommand
import matterlink.command.AuthCommand import matterlink.command.AuthCommand
import matterlink.command.MatterLinkCommand import matterlink.command.MatterLinkCommand
@ -8,7 +9,6 @@ import matterlink.command.MatterLinkCommandSender
import matterlink.config.BaseConfig import matterlink.config.BaseConfig
import matterlink.config.cfg import matterlink.config.cfg
import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString import net.minecraft.util.text.TextComponentString
import net.minecraftforge.common.ForgeVersion import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.common.MinecraftForge 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.FMLPreInitializationEvent
import net.minecraftforge.fml.common.event.FMLServerStartingEvent import net.minecraftforge.fml.common.event.FMLServerStartingEvent
import net.minecraftforge.fml.common.event.FMLServerStoppingEvent import net.minecraftforge.fml.common.event.FMLServerStoppingEvent
import java.util.* import java.util.UUID
@Mod( @Mod(
modid = MODID, modid = MODID,
name = NAME, version = MODVERSION, name = NAME, version = MODVERSION,
serverSideOnly = true, serverSideOnly = true,
useMetadata = true, useMetadata = true,
acceptableRemoteVersions = "*", acceptableRemoteVersions = "*",
modLanguageAdapter = "net.shadowfacts.forgelin.KotlinAdapter", modLanguageAdapter = "net.shadowfacts.forgelin.KotlinAdapter",
dependencies = DEPENDENCIES dependencies = DEPENDENCIES
) )
object MatterLink : IMatterLink() { object MatterLink : IMatterLink() {
init { init {
@ -58,7 +58,7 @@ object MatterLink : IMatterLink() {
} }
@Mod.EventHandler @Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) { fun serverStarting(event: FMLServerStartingEvent) = runBlocking {
logger.debug("Registering server commands") logger.debug("Registering server commands")
event.registerServerCommand(MatterLinkCommand) event.registerServerCommand(MatterLinkCommand)
event.registerServerCommand(AuthCommand) event.registerServerCommand(AuthCommand)
@ -66,7 +66,7 @@ object MatterLink : IMatterLink() {
} }
@Mod.EventHandler @Mod.EventHandler
fun serverStopping(event: FMLServerStoppingEvent) { fun serverStopping(event: FMLServerStoppingEvent) = runBlocking {
stop() stop()
} }
@ -99,9 +99,11 @@ object MatterLink : IMatterLink() {
player.addChatMessage(TextComponentString(msg)) 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 { private fun profileByUUID(uuid: UUID): GameProfile? = try {
@ -121,11 +123,11 @@ object MatterLink : IMatterLink() {
override fun collectPlayers(area: Area): Set<UUID> { override fun collectPlayers(area: Area): Set<UUID> {
val playerList = FMLCommonHandler.instance().minecraftServerInstance.playerList val playerList = FMLCommonHandler.instance().minecraftServerInstance.playerList
val players = playerList.allProfiles val players = playerList.allProfiles
.map { playerList.getPlayerByUUID(it.id) } .map { playerList.getPlayerByUUID(it.id) }
.filter { .filter {
(area.allDimensions || area.dimensions.contains(it.dimension)) (area.allDimensions || area.dimensions.contains(it.dimension))
&& area.testInBounds(it.posX.toInt(), it.posY.toInt(), it.posZ.toInt()) && area.testInBounds(it.posX.toInt(), it.posY.toInt(), it.posZ.toInt())
} }
return players.map { it.uniqueID }.toSet() return players.map { it.uniqueID }.toSet()
} }
@ -134,9 +136,9 @@ object MatterLink : IMatterLink() {
override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name
override fun commandSenderFor( override fun commandSenderFor(
user: String, user: String,
env: IBridgeCommand.CommandEnvironment, env: IBridgeCommand.CommandEnvironment,
op: Boolean op: Boolean
) = MatterLinkCommandSender(user, env, op) ) = MatterLinkCommandSender(user, env, op)
override val mcVersion: String = MCVERSION override val mcVersion: String = MCVERSION

View File

@ -1,5 +1,6 @@
package matterlink.command package matterlink.command
import kotlinx.coroutines.runBlocking
import net.minecraft.command.CommandBase import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException import net.minecraft.command.WrongUsageException
@ -21,9 +22,9 @@ object MatterLinkCommand : CommandBase() {
return CommandCoreML.aliases return CommandCoreML.aliases
} }
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) { override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) = runBlocking {
if (args.isEmpty()) { 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() val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()

View File

@ -1,5 +1,6 @@
package matterlink.command package matterlink.command
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IBridgeCommand
import matterlink.bridge.command.IMinecraftCommandSender import matterlink.bridge.command.IMinecraftCommandSender
import net.minecraft.command.CommandResultStats import net.minecraft.command.CommandResultStats
@ -15,13 +16,14 @@ import net.minecraftforge.fml.common.FMLCommonHandler
import javax.annotation.Nonnull import javax.annotation.Nonnull
class MatterLinkCommandSender( class MatterLinkCommandSender(
user: String, user: String,
env: IBridgeCommand.CommandEnvironment, env: IBridgeCommand.CommandEnvironment,
op: Boolean) : IMinecraftCommandSender(user, env, op), ICommandSender { op: Boolean
override fun execute(cmdString: String): Boolean { ) : IMinecraftCommandSender(user, env, op), ICommandSender {
return 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand( override fun execute(cmdString: String): Boolean = runBlocking {
this, return@runBlocking 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand(
cmdString this@MatterLinkCommandSender,
cmdString
).apply { ).apply {
sendReply(cmdString) sendReply(cmdString)
} }

@ -1 +1 @@
Subproject commit 091281a436b6b3807484aec2236641716d1a94ed Subproject commit 7d27c28784bacba17450faa9e723ca6b6eb39602

14
TODO.MD Normal file
View File

@ -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`

View File

@ -4,6 +4,7 @@ buildscript {
} }
dependencies { dependencies {
classpath group: "org.jetbrains.kotlin", name: "kotlin-gradle-plugin", version: kotlinVersion 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 { subprojects {
apply plugin: "kotlin" apply plugin: "kotlin"
apply plugin: "kotlinx-serialization"
apply plugin: "idea" apply plugin: "idea"
if (System.env.BUILD_NUMBER) { if (System.env.BUILD_NUMBER) {
@ -35,24 +37,28 @@ subprojects {
} }
repositories { repositories {
jcenter() jcenter()
mavenCentral()
maven { maven {
name = "bintray" name = "bintray"
url = 'http://jcenter.bintray.com' url = "http://jcenter.bintray.com"
} }
maven { maven {
name = "elytradev" name = "jitpack"
url = 'http://unascribed.com/maven/releases' url = "https://jitpack.io"
url = 'https://repo.elytradev.com'
} }
maven {
name = "kotlinx"
url = "https://kotlin.bintray.com/kotlinx/"
}
// maven {
// name = "elytradev"
// // url = 'http://unascribed.com/maven/releases'
// url = "https://repo.elytradev.com"
// }
maven { maven {
name = "shadowfacts" name = "shadowfacts"
url = "http://maven.shadowfacts.net/" 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 { compileKotlin {
kotlinOptions { kotlinOptions {

View File

@ -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"
}

View File

@ -18,15 +18,20 @@ sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse t
dependencies { dependencies {
compile project(":Jankson") 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.code.gson', name: 'gson', version: '+'
compile group: 'com.google.guava', name: 'guava', version: '+' compile group: 'com.google.guava', name: 'guava', version: '+'
compile group: 'com.github.kittinunf.fuel', name: 'fuel', version: fuelVersion compile(group: "com.github.NikkyAi.Fuel", name: "fuel", version: "feature~chunked-SNAPSHOT")
shadow (group: 'com.github.kittinunf.fuel', name: 'fuel', version: fuelVersion) { transitive = false } shadow(group: "com.github.NikkyAi.Fuel", name: "fuel", version: "feature~chunked-SNAPSHOT") { transitive = false }
compile group: 'com.github.kittinunf.result', name: 'result', version: resultVersion compile(group: "com.github.NikkyAi.Fuel", name: "fuel-coroutines", version: "feature~chunked-SNAPSHOT")
shadow (group: 'com.github.kittinunf.result', name: 'result', version: resultVersion) { transitive = false } 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 compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion
} }

View File

@ -16,7 +16,7 @@ sealed class Area {
fun testForDim(dimension: Int?): Boolean { fun testForDim(dimension: Int?): Boolean {
if (allDimensions) return true if (allDimensions) return true
if(dimension == null) return false if (dimension == null) return false
return dimensions.contains(dimension) return dimensions.contains(dimension)
} }
@ -35,8 +35,8 @@ sealed class Area {
} }
data class Infinite( data class Infinite(
override val dimensions: List<Int> = listOf(), override val dimensions: List<Int> = listOf(),
override val allDimensions: Boolean = false override val allDimensions: Boolean = false
) : Area() { ) : Area() {
override val type = "INFINITE" override val type = "INFINITE"
@ -47,8 +47,8 @@ sealed class Area {
companion object { companion object {
fun parse(jsonObj: JsonObject): Area { fun parse(jsonObj: JsonObject): Area {
return Infinite( return Infinite(
dimensions = jsonObj.parseDimensions(), dimensions = jsonObj.parseDimensions(),
allDimensions = jsonObj.parseAllDimensions() allDimensions = jsonObj.parseAllDimensions()
) )
} }
} }
@ -56,11 +56,11 @@ sealed class Area {
} }
data class Radius( data class Radius(
override val dimensions: List<Int> = listOf(), override val dimensions: List<Int> = listOf(),
override val allDimensions: Boolean = false, override val allDimensions: Boolean = false,
val x: Int, val x: Int,
val z: Int, val z: Int,
val radius: Int? val radius: Int?
) : Area() { ) : Area() {
override val type = "RADIUS" override val type = "RADIUS"
@ -73,56 +73,56 @@ sealed class Area {
fun parse(jsonObj: JsonObject): Area { fun parse(jsonObj: JsonObject): Area {
return Radius( return Radius(
dimensions = jsonObj.parseDimensions(), dimensions = jsonObj.parseDimensions(),
allDimensions = jsonObj.parseAllDimensions(), allDimensions = jsonObj.parseAllDimensions(),
x = jsonObj.getOrDefault("x", 0), x = jsonObj.getOrDefault("x", 0),
z = jsonObj.getOrDefault("z", 0), z = jsonObj.getOrDefault("z", 0),
radius = jsonObj.getReified("radius") radius = jsonObj.getReified("radius")
) )
} }
} }
} }
class Sphere ( class Sphere(
override val dimensions: List<Int> = listOf(), override val dimensions: List<Int> = listOf(),
override val allDimensions: Boolean = false, override val allDimensions: Boolean = false,
val x: Int, val x: Int,
val y: Int, val y: Int,
val z: Int, val z: Int,
val radius: Int? = null val radius: Int? = null
): Area() { ) : Area() {
override val type = "SPHERE" override val type = "SPHERE"
override fun testInBounds(x: Int, y: Int, z: Int): Boolean { override fun testInBounds(x: Int, y: Int, z: Int): Boolean {
if (radius == null) return true 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 { companion object {
fun parse(jsonObj: JsonObject): Area { fun parse(jsonObj: JsonObject): Area {
return Sphere( return Sphere(
dimensions = jsonObj.parseDimensions(), dimensions = jsonObj.parseDimensions(),
allDimensions = jsonObj.parseAllDimensions(), allDimensions = jsonObj.parseAllDimensions(),
x = jsonObj.getOrDefault("x", 0), x = jsonObj.getOrDefault("x", 0),
y = jsonObj.getOrDefault("y", 0), y = jsonObj.getOrDefault("y", 0),
z = jsonObj.getOrDefault("z", 0), z = jsonObj.getOrDefault("z", 0),
radius = jsonObj.getReified("radius") radius = jsonObj.getReified("radius")
) )
} }
} }
} }
class Box ( class Box(
override val dimensions: List<Int> = listOf(), override val dimensions: List<Int> = listOf(),
override val allDimensions: Boolean = false, override val allDimensions: Boolean = false,
val x1: Int, val x1: Int,
val x2: Int, val x2: Int,
val y1: Int, val y1: Int,
val y2: Int, val y2: Int,
val z1: Int, val z1: Int,
val z2: Int val z2: Int
): Area() { ) : Area() {
override val type = "BOX" override val type = "BOX"
override fun testInBounds(x: Int, y: Int, z: Int): Boolean { override fun testInBounds(x: Int, y: Int, z: Int): Boolean {
@ -133,42 +133,43 @@ sealed class Area {
fun parse(jsonObj: JsonObject): Area { fun parse(jsonObj: JsonObject): Area {
return Box( return Box(
dimensions = jsonObj.parseDimensions(), dimensions = jsonObj.parseDimensions(),
allDimensions = jsonObj.parseAllDimensions(), allDimensions = jsonObj.parseAllDimensions(),
x1 = jsonObj.getOrDefault("x1", 0), x1 = jsonObj.getOrDefault("x1", 0),
x2 = jsonObj.getOrDefault("x2", 0), x2 = jsonObj.getOrDefault("x2", 0),
y1 = jsonObj.getOrDefault("y1", 0), y1 = jsonObj.getOrDefault("y1", 0),
y2 = jsonObj.getOrDefault("y2", 0), y2 = jsonObj.getOrDefault("y2", 0),
z1 = jsonObj.getOrDefault("z1", 0), z1 = jsonObj.getOrDefault("z1", 0),
z2 = jsonObj.getOrDefault("z2", 0) z2 = jsonObj.getOrDefault("z2", 0)
) )
} }
} }
} }
class Square ( class Square(
override val dimensions: List<Int> = listOf(), override val dimensions: List<Int> = listOf(),
override val allDimensions: Boolean = false, override val allDimensions: Boolean = false,
val x1: Int, val x1: Int,
val x2: Int, val x2: Int,
val z1: Int, val z1: Int,
val z2: Int val z2: Int
): Area() { ) : Area() {
override val type = "SQUARE" override val type = "SQUARE"
override fun testInBounds(x: Int, y: Int, z: Int): Boolean { override fun testInBounds(x: Int, y: Int, z: Int): Boolean {
return x in x1..x2 && z in z1..z2 return x in x1..x2 && z in z1..z2
} }
companion object { companion object {
fun parse(jsonObj: JsonObject): Area { fun parse(jsonObj: JsonObject): Area {
return Square( return Square(
dimensions = jsonObj.parseDimensions(), dimensions = jsonObj.parseDimensions(),
allDimensions = jsonObj.parseAllDimensions(), allDimensions = jsonObj.parseAllDimensions(),
x1 = jsonObj.getOrDefault("x1", 0), x1 = jsonObj.getOrDefault("x1", 0),
x2 = jsonObj.getOrDefault("x2", 0), x2 = jsonObj.getOrDefault("x2", 0),
z1 = jsonObj.getOrDefault("z1", 0), z1 = jsonObj.getOrDefault("z1", 0),
z2 = jsonObj.getOrDefault("z2", 0) z2 = jsonObj.getOrDefault("z2", 0)
) )
} }
} }

View File

@ -4,10 +4,9 @@ import matterlink.bridge.MessageHandlerInst
import matterlink.bridge.command.BridgeCommandRegistry import matterlink.bridge.command.BridgeCommandRegistry
import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IBridgeCommand
import matterlink.bridge.command.IMinecraftCommandSender import matterlink.bridge.command.IMinecraftCommandSender
import matterlink.config.BaseConfig
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.update.UpdateChecker import matterlink.update.UpdateChecker
import java.util.* import java.util.UUID
lateinit var logger: Logger lateinit var logger: Logger
@ -19,7 +18,11 @@ abstract class IMatterLink {
abstract val buildNumber: Int abstract val buildNumber: Int
abstract val forgeVersion: String 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) abstract fun wrappedSendToPlayers(msg: String)
@ -29,50 +32,19 @@ abstract class IMatterLink {
abstract fun nameToUUID(username: String): UUID? abstract fun nameToUUID(username: String): UUID?
abstract fun uuidToName(uuid: UUID): String? abstract fun uuidToName(uuid: UUID): String?
init { suspend fun start() {
}
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)
// }
// }
MessageHandlerInst.logger = logger MessageHandlerInst.logger = logger
serverStartTime = System.currentTimeMillis() serverStartTime = System.currentTimeMillis()
if (cfg.connect.autoConnect) if (cfg.connect.autoConnect)
MessageHandlerInst.start("Server started, connecting to matterbridge API", true) 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") 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 * in milliseconds
*/ */

View File

@ -1,8 +1,6 @@
package matterlink package matterlink
import blue.endless.jankson.Jankson import blue.endless.jankson.Jankson
import blue.endless.jankson.JsonObject
import blue.endless.jankson.impl.Marshaller
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
@ -13,34 +11,34 @@ import java.net.URL
*/ */
data class Paste( data class Paste(
val encrypted: Boolean = false, val encrypted: Boolean = false,
val description: String, val description: String,
val sections: List<PasteSection> val sections: List<PasteSection>
) )
data class PasteSection( data class PasteSection(
val name: String, val name: String,
val syntax: String = "text", val syntax: String = "text",
val contents: String val contents: String
) )
data class PasteResponse( data class PasteResponse(
val id: String, val id: String,
val link: String val link: String
) )
object PasteUtil { object PasteUtil {
private const val DEFAULT_KEY = "uKJoyicVJFnmpnrIZMklOURWxrCKXYaiBWOzPmvon" private const val DEFAULT_KEY = "uKJoyicVJFnmpnrIZMklOURWxrCKXYaiBWOzPmvon"
private val jankson = Jankson.builder() private val jankson = Jankson.builder()
.registerTypeAdapter { .registerTypeAdapter {
PasteResponse( PasteResponse(
id = it.getReified("id") ?: "", id = it.getReified("id") ?: "",
link = it.getReified<String>("link") link = it.getReified<String>("link")
?.replace("\\/", "/") ?.replace("\\/", "/")
?: "invalid" ?: "invalid"
) )
} }
// .registerSerializer { paste: Paste, marshaller: Marshaller -> // .registerSerializer { paste: Paste, marshaller: Marshaller ->
// JsonObject().apply { // JsonObject().apply {
// with(paste) { // with(paste) {
@ -62,7 +60,7 @@ object PasteUtil {
// } // }
// } // }
// } // }
.build() .build()
fun paste(paste: Paste, key: String = ""): PasteResponse { fun paste(paste: Paste, key: String = ""): PasteResponse {
val apiKey = key.takeIf { it.isNotBlank() } ?: DEFAULT_KEY val apiKey = key.takeIf { it.isNotBlank() } ?: DEFAULT_KEY
@ -73,8 +71,8 @@ object PasteUtil {
http.doOutput = true http.doOutput = true
val out = jankson.toJson(paste) val out = jankson.toJson(paste)
.toJson(false, false) .toJson(false, false)
.toByteArray() .toByteArray()
http.setFixedLengthStreamingMode(out.size) http.setFixedLengthStreamingMode(out.size)
http.setRequestProperty("Content-Type", "application/json; charset=UTF-8") http.setRequestProperty("Content-Type", "application/json; charset=UTF-8")

View File

@ -8,9 +8,7 @@ import blue.endless.jankson.impl.Marshaller
import matterlink.config.cfg import matterlink.config.cfg
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.lang.Thread.yield
import java.util.* import java.util.*
import kotlin.streams.asSequence
private const val ZWSP: Char = '\u200b' private const val ZWSP: Char = '\u200b'
@ -64,7 +62,7 @@ val Exception.stackTraceString: String
} }
fun randomString(length: Int = 6): String = fun randomString(length: Int = 6): String =
java.util.UUID.randomUUID().toString().replace("-", "").take(length) java.util.UUID.randomUUID().toString().replace("-", "").take(length)
fun <T : Any> JsonObject.getOrDefault(key: String, default: T, comment: String? = null): T { fun <T : Any> 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") logger.trace("type: ${default.javaClass.name} key: $key json: >>>${this.getObject(key)?.toJson()}<<< default: $default")
@ -76,20 +74,27 @@ fun <T : Any> JsonObject.getOrDefault(key: String, default: T, comment: String?
inline fun <reified T : Any> Jankson.fromJson(obj: JsonObject): T = this.fromJson(obj, T::class.java) inline fun <reified T : Any> Jankson.fromJson(obj: JsonObject): T = this.fromJson(obj, T::class.java)
inline fun <reified T : Any> Jankson.fromJson(json: String): T = this.fromJson(json, T::class.java) inline fun <reified T : Any> Jankson.fromJson(json: String): T = this.fromJson(json, T::class.java)
inline fun <reified T : Any> Jankson.Builder.registerTypeAdapter(noinline adapter: (JsonObject) -> T) = this.registerTypeAdapter(T::class.java, adapter) inline fun <reified T : Any> Jankson.Builder.registerTypeAdapter(noinline adapter: (JsonObject) -> T) =
this.registerTypeAdapter(T::class.java, adapter)
inline fun <reified T : Any> Jankson.Builder.registerPrimitiveTypeAdapter(noinline adapter: (Any) -> T) = this.registerPrimitiveTypeAdapter(T::class.java, adapter) inline fun <reified T : Any> Jankson.Builder.registerPrimitiveTypeAdapter(noinline adapter: (Any) -> T) =
this.registerPrimitiveTypeAdapter(T::class.java, adapter)
inline fun <reified T : Any> Jankson.Builder.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = this.registerSerializer(T::class.java, serializer) inline fun <reified T : Any> Jankson.Builder.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) =
this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T) -> JsonElement) = this.registerSerializer(T::class.java, serializer) inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T) -> JsonElement) =
this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = this.registerSerializer(T::class.java, serializer) inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) =
this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> JsonObject.getReified(key: String, comment: String? = null): T? = this.get(T::class.java, key) inline fun <reified T : Any> JsonObject.getReified(key: String, comment: String? = null): T? =
this.get(T::class.java, key)
?.also { setComment(key, comment) } ?.also { setComment(key, comment) }
inline fun <reified T : Any> JsonObject.getReifiedOrDelete(key: String, comment: String? = null): T? = this.get(T::class.java, key) inline fun <reified T : Any> JsonObject.getReifiedOrDelete(key: String, comment: String? = null): T? =
this.get(T::class.java, key)
?.also { setComment(key, comment) } ?.also { setComment(key, comment) }
?: run { ?: run {
this.remove(key) this.remove(key)
@ -124,7 +129,11 @@ inline fun <reified T : Any> JsonObject.getOrPutList(key: String, default: List<
} ?: this.putDefault(key, default, comment) ?: default } ?: this.putDefault(key, default, comment) ?: default
} }
inline fun <reified T : Any> JsonObject.getOrPutMap(key: String, default: Map<String, T>, comment: String?): Map<String, T> { inline fun <reified T : Any> JsonObject.getOrPutMap(
key: String,
default: Map<String, T>,
comment: String?
): Map<String, T> {
return this[key]?.let { map -> return this[key]?.let { map ->
when (map) { when (map) {
is JsonObject -> { is JsonObject -> {

View File

@ -1,7 +1,10 @@
package matterlink.api package matterlink.api
import com.google.gson.GsonBuilder import kotlinx.serialization.Encoder
import com.google.gson.annotations.SerializedName import kotlinx.serialization.Optional
import kotlinx.serialization.Serializable
import kotlinx.serialization.Serializer
import kotlinx.serialization.json.JSON
/** /**
* Created by nikky on 07/05/18. * Created by nikky on 07/05/18.
@ -9,84 +12,77 @@ import com.google.gson.annotations.SerializedName
* @author Nikky * @author Nikky
* @version 1.0 * @version 1.0
*/ */
class ApiMessage ( @Serializable
username: String? = null, data class ApiMessage(
text: String? = null, @Optional var username: String = "",
gateway: String? = null, @Optional var text: String = "",
channel: String? = null, @Optional var gateway: String = "",
userid: String? = null, @Optional var timestamp: String = "",
avatar: String? = null, @Optional var channel: String = "",
account: String? = null, @Optional var userid: String = "",
protocol: String? = null, @Optional var avatar: String = "",
event: String? = null, @Optional var account: String = "",
id: String? = null @Optional var protocol: String = "",
@Optional var event: String = "",
@Optional var id: String = "",
@Optional var Extra: Map<String, String>? = 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 { fun encode(): String {
return gson.toJson(this) return JSON.stringify(Companion, this)
} }
override fun toString(): String = encode() override fun toString(): String = encode()
@Serializer(forClass = ApiMessage::class)
companion object { 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 USER_ACTION = "user_action"
val JOIN_LEAVE = "join_leave" val JOIN_LEAVE = "join_leave"
private val gson = GsonBuilder()
.create()
fun decode(json: String): ApiMessage { fun decode(json: String): ApiMessage {
return gson.fromJson(json, ApiMessage::class.java) return JSON.parse(Companion, json)
} }
} }
} }

View File

@ -1,14 +1,13 @@
package matterlink.api package matterlink.api
data class Config ( data class Config(
var url: String = "", var url: String = "",
var token: String = "", var token: String = "",
var announceConnect: Boolean = true, var announceConnect: Boolean = true,
var announceDisconnect: Boolean = true, var announceDisconnect: Boolean = true,
var reconnectWait: Long = 500, var reconnectWait: Long = 500,
var systemUser: String = "Server" var systemUser: String = "Server"
) ) {
{
fun sync(connection: StreamConnection) { fun sync(connection: StreamConnection) {
connection.token = token connection.token = token
connection.host = url connection.host = url

View File

@ -1,15 +1,31 @@
package matterlink.api 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 matterlink.Logger
import java.io.BufferedReader import java.io.Reader
import java.io.DataOutputStream import kotlin.coroutines.CoroutineContext
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
/** /**
* Created by nikky on 07/05/18. * Created by nikky on 07/05/18.
@ -17,66 +33,60 @@ import java.util.concurrent.ConcurrentLinkedQueue
* @author Nikky * @author Nikky
* @version 1.0 * @version 1.0
*/ */
open class MessageHandler { open class MessageHandler : CoroutineScope {
override val coroutineContext: CoroutineContext = Job()
private var enabled = false private var enabled = false
private var connectErrors = 0 private var connectErrors = 0
private var reconnectCooldown = 0 private var reconnectCooldown = 0L
private var sendErrors = 0 private var sendErrors = 0
var config: Config = Config()
set(value) { private var sendChannel: SendChannel<ApiMessage> = senderActor()
field = value.apply {
sync(streamConnection) private val messageStream = Channel<ApiMessage>(Channel.UNLIMITED)
} var broadcast: BroadcastChannel<ApiMessage> = broadcast {
while (true) {
val msg = messageStream.receive()
send(msg)
} }
}
//TODO: make callbacks: onConnect onDisconnect onError etc
var queue: ConcurrentLinkedQueue<ApiMessage> = ConcurrentLinkedQueue()
private set private set
private var streamConnection: StreamConnection = StreamConnection(queue) private val keepOpenManager = FuelManager().apply {
timeoutInMillisecond = 0
var logger: Logger timeoutReadInMillisecond = 0
get() = streamConnection.logger
set(l) {
streamConnection.logger = l
} }
var config: Config = Config()
private var nextCheck: Long = 0 var logger = object : Logger {
override fun info(message: String) = println("INFO: $message")
init { override fun debug(message: String) = println("DEBUG: $message")
streamConnection.addOnSuccess { success -> override fun error(message: String) = println("ERROR: $message")
if (success) { override fun warn(message: String) = println("WARN: $message")
logger.info("connected successfully") override fun trace(message: String) = println("TRACE: $message")
connectErrors = 0
reconnectCooldown = 0
} else {
reconnectCooldown = connectErrors
connectErrors++
logger.error(String.format("connectErrors: %d", connectErrors))
}
}
} }
fun stop(message: String? = null) { suspend fun stop(message: String? = null) {
if (message != null && config.announceDisconnect) { if (message != null && config.announceDisconnect) {
sendStatusUpdate(message) sendStatusUpdate(message)
} }
enabled = false enabled = false
streamConnection.close() rcvJob?.cancel()
rcvJob = null
} }
private var rcvJob: Job? = null
fun start(message: String?, clear: Boolean) { suspend fun start(message: String?, clear: Boolean) {
config.sync(streamConnection) logger.debug("starting connection")
if (clear) { if (clear) {
clear() clear()
} }
enabled = true enabled = true
streamConnection.open()
rcvJob = messageBroadcast()
if (message != null && config.announceConnect) { if (message != null && config.announceConnect) {
sendStatusUpdate(message) sendStatusUpdate(message)
@ -84,118 +94,138 @@ open class MessageHandler {
} }
private fun clear() { private suspend fun clear() {
try { val url = "${config.url}/api/messages"
val url = URL(config.url + "/api/messages") val (request, response, result) = url.httpGet()
val conn = url.openConnection() as HttpURLConnection .apply {
if (config.token.isNotEmpty()) {
if (!config.token.isEmpty()) { headers["Authorization"] = "Bearer ${config.token}"
val bearerAuth = "Bearer " + config.token }
conn.setRequestProperty("Authorization", bearerAuth)
} }
.awaitStringResponse()
conn.requestMethod = "GET" when (result) {
is Result.Success -> {
BufferedReader(InputStreamReader(conn.inputStream)).forEachLine { line -> val messages: List<ApiMessage> = JSON.parse(ApiMessage.list, result.value)
logger.trace("skipping $line") 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)) transmit(ApiMessage(text = message))
} }
open fun transmit(msg: ApiMessage) { open suspend fun transmit(msg: ApiMessage) {
if (streamConnection.isConnected || streamConnection.isConnecting) { // if (streamConnection.isConnected || streamConnection.isConnecting) {
if (msg.username.isEmpty()) if (msg.username.isEmpty())
msg.username = config.systemUser msg.username = config.systemUser
if (msg.gateway.isEmpty()) { if (msg.gateway.isEmpty()) {
logger.error("missing gateway on message: $msg") logger.error("missing gateway on message: $msg")
return return
}
logger.debug("Transmitting: $msg")
transmitMessage(msg)
} }
logger.debug("Transmitting: $msg")
sendChannel.send(msg)
// }
} }
private fun transmitMessage(message: ApiMessage) { @Deprecated("use coroutine api", level = DeprecationLevel.ERROR)
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
*/
fun checkConnection() { 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) { private fun CoroutineScope.senderActor() = actor<ApiMessage>(context = Dispatchers.IO) {
logger.error("Caught too many errors, closing bridge") consumeEach {
stop("Interrupting connection to matterbridge API due to accumulated connection errors") logger.debug("sending $it")
return 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) { private fun CoroutineScope.messageBroadcast() = launch(context = Dispatchers.IO + CoroutineName("msgBroadcaster")) {
logger.info("Trying to reconnect") loop@ while (isActive) {
start("Reconnecting to matterbridge API after connection error", false) logger.info("opening connection")
} else { val url = "${config.url}/api/stream"
reconnectCooldown-- val (request, response, result) = keepOpenManager.request(Method.GET, url)
.apply {
if (config.token.isNotEmpty()) {
headers["Authorization"] = "Bearer ${config.token}"
}
}
.responseObject(object : ResponseDeserializable<Unit> {
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
} }
} }
} }

View File

@ -2,7 +2,6 @@ package matterlink.api
import matterlink.Logger import matterlink.Logger
import java.io.IOException import java.io.IOException
import java.io.InputStream
import java.net.ConnectException import java.net.ConnectException
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.MalformedURLException import java.net.MalformedURLException
@ -22,7 +21,7 @@ class StreamConnection(private val rcvQueue: ConcurrentLinkedQueue<ApiMessage>)
private var urlConnection: HttpURLConnection? = null private var urlConnection: HttpURLConnection? = null
private val onSuccessCallbacks = LinkedList<(Boolean) -> Unit>() private val onSuccessCallbacks = LinkedList<(Boolean) -> Unit>()
var logger = object : Logger { var logger = object : Logger {
override fun info(message: String) = println("INFO: $message") override fun info(message: String) = println("INFO: $message")
override fun debug(message: String) = println("DEBUG: $message") override fun debug(message: String) = println("DEBUG: $message")
override fun error(message: String) = println("ERROR: $message") override fun error(message: String) = println("ERROR: $message")
@ -88,7 +87,7 @@ class StreamConnection(private val rcvQueue: ConcurrentLinkedQueue<ApiMessage>)
} }
val chars = input.read(buf) val chars = input.read(buf)
logger.trace( String.format("read %d chars", chars)) logger.trace(String.format("read %d chars", chars))
if (chars > 0) { if (chars > 0) {
val added = String(Arrays.copyOfRange(buf, 0, chars)) val added = String(Arrays.copyOfRange(buf, 0, chars))
logger.debug("json: $added") logger.debug("json: $added")

View File

@ -1,32 +1,37 @@
package matterlink.bridge package matterlink.bridge
import matterlink.* import matterlink.Paste
import matterlink.PasteSection
import matterlink.PasteUtil
import matterlink.antiping
import matterlink.api.ApiMessage import matterlink.api.ApiMessage
import matterlink.api.MessageHandler import matterlink.api.MessageHandler
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.handlers.ChatEvent import matterlink.handlers.ChatEvent
import matterlink.handlers.LocationHandler import matterlink.handlers.LocationHandler
import matterlink.mapFormat
import matterlink.stackTraceString
object MessageHandlerInst : MessageHandler() { object MessageHandlerInst : MessageHandler() {
override fun transmit(msg: ApiMessage) { override suspend fun transmit(msg: ApiMessage) {
transmit(msg, cause = "") transmit(msg, cause = "")
} }
override fun sendStatusUpdate(message: String) { override suspend fun sendStatusUpdate(message: String) {
LocationHandler.sendToLocations( LocationHandler.sendToLocations(
msg = message, msg = message,
x = 0, y = 0, z = 0, dimension = null, x = 0, y = 0, z = 0, dimension = null,
systemuser = true, systemuser = true,
event = ChatEvent.STATUS, event = ChatEvent.STATUS,
cause = "status update message" 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()) { if (msg.username.isEmpty()) {
msg.username = cfg.outgoing.systemUser 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 msg.avatar = cfg.outgoing.avatar.systemUserAvatar
} }
} }
@ -38,20 +43,20 @@ object MessageHandlerInst : MessageHandler() {
if (msg.text.lines().count() >= maxLines) { if (msg.text.lines().count() >= maxLines) {
try { try {
val response = PasteUtil.paste( val response = PasteUtil.paste(
Paste( Paste(
description = cause, description = cause,
sections = listOf( sections = listOf(
PasteSection( PasteSection(
name = "log.txt", name = "log.txt",
syntax = "text", syntax = "text",
contents = msg.text contents = msg.text
) )
)
) )
)
) )
msg.text = msg.text.substringBefore('\n') msg.text = msg.text.substringBefore('\n')
.take(25) + "... " + response.link .take(25) + "... " + response.link
} catch(e: Exception) { } catch (e: Exception) {
logger.error(cause) logger.error(cause)
logger.error(e.stackTraceString) logger.error(e.stackTraceString)
} }
@ -62,13 +67,13 @@ object MessageHandlerInst : MessageHandler() {
fun ApiMessage.format(fmt: String): String { fun ApiMessage.format(fmt: String): String {
return fmt.mapFormat( return fmt.mapFormat(
mapOf( mapOf(
"{username}" to username, "{username}" to username,
"{text}" to text, "{text}" to text,
"{gateway}" to gateway, "{gateway}" to gateway,
"{channel}" to channel, "{channel}" to channel,
"{protocol}" to protocol, "{protocol}" to protocol,
"{username:antiping}" to username.antiping "{username:antiping}" to username.antiping
) )
) )
} }

View File

@ -5,7 +5,7 @@ import matterlink.config.IdentitiesConfig
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.instance import matterlink.instance
import matterlink.randomString import matterlink.randomString
import java.util.* import java.util.UUID
object AuthBridgeCommand : IBridgeCommand() { object AuthBridgeCommand : IBridgeCommand() {
val syntax = "Syntax: auth [username]" val syntax = "Syntax: auth [username]"
@ -13,7 +13,7 @@ object AuthBridgeCommand : IBridgeCommand() {
override val permLevel: Double override val permLevel: Double
get() = cfg.command.defaultPermUnauthenticated 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) { if (env !is CommandEnvironment.BridgeEnv) {
env.respond("please initiate authentication from linked external chat") env.respond("please initiate authentication from linked external chat")
return true return true
@ -28,8 +28,10 @@ object AuthBridgeCommand : IBridgeCommand() {
val argList = args.split(' ', limit = 2) val argList = args.split(' ', limit = 2)
val target = argList.getOrNull(0) ?: run { val target = argList.getOrNull(0) ?: run {
env.respond("no username/uuid provided\n" + env.respond(
syntax) "no username/uuid provided\n" +
syntax
)
return true return true
} }
@ -62,7 +64,16 @@ object AuthBridgeCommand : IBridgeCommand() {
instance.wrappedSendToPlayer(targetUserName, "otherwise you may ignore this message") 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") env.respond("please accept the authentication request ingame, do not share the code")
return true return true

View File

@ -1,15 +1,13 @@
package matterlink.bridge.command package matterlink.bridge.command
import matterlink.api.ApiMessage import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.CommandConfig import matterlink.config.CommandConfig
import matterlink.config.IdentitiesConfig import matterlink.config.IdentitiesConfig
import matterlink.config.PermissionConfig import matterlink.config.PermissionConfig
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.instance
import matterlink.logger import matterlink.logger
import matterlink.stripColorOut import java.util.HashMap
import java.util.* import java.util.UUID
object BridgeCommandRegistry { object BridgeCommandRegistry {
@ -19,7 +17,7 @@ object BridgeCommandRegistry {
* *
* @return consume message flag * @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 (!cfg.command.enable || input.text.isBlank()) return false
if (input.text[0] != cfg.command.prefix || input.text.length < 2) 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 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 { return commandMap[cmd[0]]?.let {
if (!it.reachedTimeout()) { if (!it.reachedTimeout()) {
logger.debug("dropped command ${it.alias}") logger.debug("dropped command ${it.alias}")
@ -38,7 +42,7 @@ object BridgeCommandRegistry {
it.preExecute() // resets the tickCounter it.preExecute() // resets the tickCounter
if (!it.canExecute(uuid)) { if (!it.canExecute(uuid)) {
env.respond( 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 return false
} }
@ -46,7 +50,7 @@ object BridgeCommandRegistry {
} ?: false } ?: 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 (!cfg.command.enable || text.isBlank()) return false
if (text[0] != cfg.command.prefix || text.length < 2) return false if (text[0] != cfg.command.prefix || text.length < 2) return false
@ -64,7 +68,7 @@ object BridgeCommandRegistry {
it.preExecute() // resets the tickCounter it.preExecute() // resets the tickCounter
if (!it.canExecute(uuid)) { if (!it.canExecute(uuid)) {
env.respond( env.respond(
text = "$username is not permitted to perform command: ${cmd[0]}" text = "$username is not permitted to perform command: ${cmd[0]}"
) )
return false return false
} }
@ -98,11 +102,11 @@ object BridgeCommandRegistry {
fun getCommandList(permLvl: Double): String { fun getCommandList(permLvl: Double): String {
return commandMap return commandMap
.filterValues { .filterValues {
it.permLevel <= permLvl it.permLevel <= permLvl
} }
.keys .keys
.joinToString(" ") .joinToString(" ")
} }
fun reloadCommands() { fun reloadCommands() {

View File

@ -6,18 +6,18 @@ import matterlink.logger
import matterlink.stripColorIn import matterlink.stripColorIn
data class CustomCommand( data class CustomCommand(
val type: CommandType = CommandType.RESPONSE, val type: CommandType = CommandType.RESPONSE,
val execute: String? = null, val execute: String? = null,
val response: String? = null, val response: String? = null,
override val permLevel: Double = 0.0, override val permLevel: Double = 0.0,
override val help: String = "", override val help: String = "",
override val timeout: Int = 20, override val timeout: Int = 20,
val defaultCommand: Boolean? = null, val defaultCommand: Boolean? = null,
val execOp: Boolean? = null, val execOp: Boolean? = null,
val argumentsRegex: Regex? = null val argumentsRegex: Regex? = null
) : IBridgeCommand() { ) : 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) { if (argumentsRegex != null) {
logger.debug("testing '$args' against '${argumentsRegex.pattern}'") logger.debug("testing '$args' against '${argumentsRegex.pattern}'")
if (!argumentsRegex.matches(args)) { if (!argumentsRegex.matches(args)) {
@ -31,12 +31,14 @@ data class CustomCommand(
// uses a new commandsender for each use // uses a new commandsender for each use
val commandSender = instance.commandSenderFor(user, env, execOp ?: false) val commandSender = instance.commandSenderFor(user, env, execOp ?: false)
val cmd = execute?.lazyFormat(getReplacements(user, env, args))?.stripColorIn val cmd = execute?.lazyFormat(getReplacements(user, env, args))?.stripColorIn
?: return false ?: return false
commandSender.execute(cmd) || commandSender.reply.isNotEmpty() commandSender.execute(cmd) || commandSender.reply.isNotEmpty()
} }
CommandType.RESPONSE -> { CommandType.RESPONSE -> {
env.respond(response?.lazyFormat(getReplacements(user, env, args)) env.respond(
?: "", cause = "response to command: $alias") response?.lazyFormat(getReplacements(user, env, args))
?: "", cause = "response to command: $alias"
)
true true
} }
@ -60,34 +62,34 @@ data class CustomCommand(
val DEFAULT = CustomCommand() val DEFAULT = CustomCommand()
fun getReplacements(user: String, env: CommandEnvironment, args: String): Map<String, () -> String?> = mapOf( fun getReplacements(user: String, env: CommandEnvironment, args: String): Map<String, () -> String?> = mapOf(
"{uptime}" to instance::getUptimeAsString, "{uptime}" to instance::getUptimeAsString,
"{user}" to { user }, "{user}" to { user },
"{userid}" to { "{userid}" to {
when (env) { when (env) {
is CommandEnvironment.BridgeEnv -> env.userId is CommandEnvironment.BridgeEnv -> env.userId
else -> null else -> null
} }
}, },
"{uuid}" to { "{uuid}" to {
when (env) { when (env) {
is CommandEnvironment.BridgeEnv -> env.uuid.toString() is CommandEnvironment.BridgeEnv -> env.uuid.toString()
is CommandEnvironment.GameEnv -> env.uuid.toString() is CommandEnvironment.GameEnv -> env.uuid.toString()
} }
}, },
"{username}" to { "{username}" to {
when (env) { when (env) {
is CommandEnvironment.BridgeEnv -> env.uuid is CommandEnvironment.BridgeEnv -> env.uuid
is CommandEnvironment.GameEnv -> env.uuid is CommandEnvironment.GameEnv -> env.uuid
}?.let { instance.uuidToName(it) } }?.let { instance.uuidToName(it) }
}, },
"{platform}" to { "{platform}" to {
when (env) { when (env) {
is CommandEnvironment.BridgeEnv -> env.platform is CommandEnvironment.BridgeEnv -> env.platform
else -> null else -> null
} }
}, },
"{args}" to { args }, "{args}" to { args },
"{version}" to { instance.modVersion } "{version}" to { instance.modVersion }
) )
} }
} }

View File

@ -1,27 +1,24 @@
package matterlink.bridge.command package matterlink.bridge.command
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.stripColorOut
object HelpCommand : IBridgeCommand() { object HelpCommand : IBridgeCommand() {
override val help: String = "Returns the help string for the given command. Syntax: help <command>" override val help: String = "Returns the help string for the given command. Syntax: help <command>"
override val permLevel: Double override val permLevel: Double
get() = cfg.command.defaultPermUnauthenticated 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 { val msg: String = when {
args.isEmpty() -> args.isEmpty() ->
"Available commands: ${BridgeCommandRegistry.getCommandList(IBridgeCommand.getPermLevel(env.uuid))}" "Available commands: ${BridgeCommandRegistry.getCommandList(IBridgeCommand.getPermLevel(env.uuid))}"
else -> args.split(" ", ignoreCase = false) else -> args.split(" ", ignoreCase = false)
.joinToString(separator = "\n") { .joinToString(separator = "\n") {
"$it: ${BridgeCommandRegistry.getHelpString(it)}" "$it: ${BridgeCommandRegistry.getHelpString(it)}"
} }
} }
env.respond( env.respond(
text = msg, text = msg,
cause = "Help Requested $args" cause = "Help Requested $args"
) )
return true return true
} }

View File

@ -8,7 +8,7 @@ import matterlink.handlers.TickHandler
import matterlink.instance import matterlink.instance
import matterlink.logger import matterlink.logger
import matterlink.stripColorOut import matterlink.stripColorOut
import java.util.* import java.util.UUID
abstract class IBridgeCommand { abstract class IBridgeCommand {
abstract val help: String abstract val help: String
@ -20,30 +20,30 @@ abstract class IBridgeCommand {
abstract val username: String? abstract val username: String?
data class BridgeEnv( data class BridgeEnv(
val name: String, val name: String,
val userId: String, val userId: String,
val platform: String, val platform: String,
val gateway: String, val gateway: String,
override val uuid: UUID? override val uuid: UUID?
) : CommandEnvironment() { ) : CommandEnvironment() {
override val username: String? override val username: String?
get() = uuid?.let { instance.uuidToName(uuid) } get() = uuid?.let { instance.uuidToName(uuid) }
} }
data class GameEnv( data class GameEnv(
override val username: String, override val username: String,
override val uuid: UUID override val uuid: UUID
) : CommandEnvironment() ) : CommandEnvironment()
fun respond(text: String, cause: String = "") { suspend fun respond(text: String, cause: String = "") {
when (this) { when (this) {
is BridgeEnv -> { is BridgeEnv -> {
MessageHandlerInst.transmit( MessageHandlerInst.transmit(
ApiMessage( ApiMessage(
gateway = this.gateway, gateway = this.gateway,
text = text.stripColorOut text = text.stripColorOut
), ),
cause = cause cause = cause
) )
} }
is GameEnv -> { is GameEnv -> {
@ -72,7 +72,7 @@ abstract class IBridgeCommand {
* *
* @return consume message flag * @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 { fun canExecute(uuid: UUID?): Boolean {
logger.trace("canExecute this: $this uuid: $uuid permLevel: $permLevel") logger.trace("canExecute this: $this uuid: $uuid permLevel: $permLevel")

View File

@ -1,7 +1,5 @@
package matterlink.bridge.command package matterlink.bridge.command
import matterlink.stripColorOut
abstract class IMinecraftCommandSender(val user: String, val env: IBridgeCommand.CommandEnvironment, val op: Boolean) { abstract class IMinecraftCommandSender(val user: String, val env: IBridgeCommand.CommandEnvironment, val op: Boolean) {
/** /**
* @param cmdString The command to execute with its arguments * @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 displayName = env.username ?: user
val accountName = when (env) { val accountName = when (env) {
is IBridgeCommand.CommandEnvironment.BridgeEnv -> "$user (id=${env.userId} platform=${env.platform}${env.uuid?.let { " uuid=$it" } 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})" 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 reply += text
} }
fun sendReply(cmdString: String) { suspend fun sendReply(cmdString: String) {
env.respond( env.respond(
text = reply.joinToString("\n"), text = reply.joinToString("\n"),
cause = "executed command: $cmdString" cause = "executed command: $cmdString"
) )
finished = true finished = true
} }

View File

@ -11,7 +11,7 @@ object RequestPermissionsCommand : IBridgeCommand() {
override val permLevel: Double override val permLevel: Double
get() = cfg.command.defaultPermAuthenticated 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 val uuid = env.uuid
if (uuid == null) { if (uuid == null) {
@ -23,8 +23,10 @@ object RequestPermissionsCommand : IBridgeCommand() {
val requestedLevelArg = argList.getOrNull(0) val requestedLevelArg = argList.getOrNull(0)
val requestedLevel = requestedLevelArg?.takeIf { it.isNotEmpty() }?.let { val requestedLevel = requestedLevelArg?.takeIf { it.isNotEmpty() }?.let {
it.toDoubleOrNull() ?: run { it.toDoubleOrNull() ?: run {
env.respond("cannot parse permlevel '$requestedLevelArg'\n" + env.respond(
syntax) "cannot parse permlevel '$requestedLevelArg'\n" +
syntax
)
return true return true
} }
} }
@ -33,7 +35,10 @@ object RequestPermissionsCommand : IBridgeCommand() {
val requestId = user.toLowerCase() 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]`") env.respond("please ask a op to accept your permission elevation with `/ml permAccept $requestId $nonce [permLevel]`")
return true return true

View File

@ -1,11 +1,6 @@
package matterlink.command package matterlink.command
import matterlink.bridge.MessageHandlerInst
import matterlink.bridge.command.BridgeCommandRegistry
import matterlink.config.IdentitiesConfig import matterlink.config.IdentitiesConfig
import matterlink.config.PermissionConfig
import matterlink.config.baseCfg
import matterlink.config.cfg
object CommandCoreAuth { object CommandCoreAuth {
val name = "auth" val name = "auth"
@ -28,17 +23,23 @@ object CommandCoreAuth {
val nonce = args.getOrNull(2)?.toUpperCase() ?: run { val nonce = args.getOrNull(2)?.toUpperCase() ?: run {
return "no code passed" return "no code passed"
} }
if(request.nonce != nonce) { if (request.nonce != nonce) {
return "nonce in request does not match" 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" 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" 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) IdentitiesConfig.authRequests.invalidate(requestId)
"${request.userid} on ${request.platform} is now identified as $user" "${request.userid} on ${request.platform} is now identified as $user"
@ -54,13 +55,13 @@ object CommandCoreAuth {
val nonce = args.getOrNull(2)?.toUpperCase() ?: run { val nonce = args.getOrNull(2)?.toUpperCase() ?: run {
return "no code passed" return "no code passed"
} }
if(request.nonce != nonce) { if (request.nonce != nonce) {
return "nonce in request does not match" 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" 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" return "uuid in request does not match ${request.uuid} != $uuid"
} }

View File

@ -13,7 +13,7 @@ object CommandCoreML {
val usage = "ml <connect|disconnect|reload|permAccept>" val usage = "ml <connect|disconnect|reload|permAccept>"
fun execute(args: Array<String>, user: String, uuid: String?): String { suspend fun execute(args: Array<String>, user: String, uuid: String?): String {
val cmd = args[0].toLowerCase() val cmd = args[0].toLowerCase()
return when (cmd) { return when (cmd) {
@ -49,8 +49,8 @@ object CommandCoreML {
} }
val powerLevelArg = args.getOrNull(2)?.toDoubleOrNull() val powerLevelArg = args.getOrNull(2)?.toDoubleOrNull()
val powerLevel = powerLevelArg ?: run { return "permLevel cannot be parsed" } val powerLevel = powerLevelArg ?: run { return "permLevel cannot be parsed" }
?: request.powerlevel ?: request.powerlevel
?: return "no permLevel provided" ?: return "no permLevel provided"
PermissionConfig.add(request.uuid, powerLevel, "${request.user} Authorized by $user") PermissionConfig.add(request.uuid, powerLevel, "${request.user} Authorized by $user")
PermissionConfig.permissionRequests.invalidate(requestId) PermissionConfig.permissionRequests.invalidate(requestId)
"added ${request.user} (uuid: ${request.uuid}) with power level: $powerLevel" "added ${request.user} (uuid: ${request.uuid}) with power level: $powerLevel"

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,12 @@ import blue.endless.jankson.Jankson
import blue.endless.jankson.JsonObject import blue.endless.jankson.JsonObject
import blue.endless.jankson.JsonPrimitive import blue.endless.jankson.JsonPrimitive
import blue.endless.jankson.impl.SyntaxError import blue.endless.jankson.impl.SyntaxError
import matterlink.*
import matterlink.bridge.command.CommandType import matterlink.bridge.command.CommandType
import matterlink.bridge.command.CustomCommand import matterlink.bridge.command.CustomCommand
import matterlink.getOrDefault
import matterlink.logger
import matterlink.registerPrimitiveTypeAdapter
import matterlink.registerTypeAdapter
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
@ -17,89 +20,89 @@ object CommandConfig {
private val configFile: File = baseCfg.cfgDirectory.resolve("commands.hjson") private val configFile: File = baseCfg.cfgDirectory.resolve("commands.hjson")
private val default: DefaultCommands = mapOf( 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 |make sure to disable defaultCommand if you want your edits to have any effect
""".trimMargin() """.trimMargin()
to CustomCommand( to CustomCommand(
type = CommandType.EXECUTE, type = CommandType.EXECUTE,
execute = "forge tps", execute = "forge tps",
help = "Print platform tps", help = "Print platform tps",
timeout = 200, timeout = 200,
defaultCommand = true defaultCommand = true
)), )),
"list" to ("lists all the players, this is just a straight pass-through" "list" to ("lists all the players, this is just a straight pass-through"
to CustomCommand( to CustomCommand(
type = CommandType.EXECUTE, type = CommandType.EXECUTE,
execute = "list", execute = "list",
help = "List online players", help = "List online players",
defaultCommand = true defaultCommand = true
)), )),
"seed" to ("another straight pass-through" "seed" to ("another straight pass-through"
to CustomCommand( to CustomCommand(
type = CommandType.EXECUTE, type = CommandType.EXECUTE,
execute = "seed", execute = "seed",
help = "Print platform world seed", help = "Print platform world seed",
defaultCommand = true defaultCommand = true
)), )),
"uptime" to ("this is a reponse command, it uses the uptime function, time since the mod was first loaded" "uptime" to ("this is a reponse command, it uses the uptime function, time since the mod was first loaded"
to CustomCommand( to CustomCommand(
type = CommandType.RESPONSE, type = CommandType.RESPONSE,
response = "{uptime}", response = "{uptime}",
help = "Print platform uptime", help = "Print platform uptime",
defaultCommand = true defaultCommand = true
)), )),
"whoami" to ("this shows you some of the other response macros" "whoami" to ("this shows you some of the other response macros"
to CustomCommand( to CustomCommand(
type = CommandType.RESPONSE, type = CommandType.RESPONSE,
response = "name: `{user}` userid: `{userid}` platform: `{platform}` username: `{username}` uuid: `{uuid}`", response = "name: `{user}` userid: `{userid}` platform: `{platform}` username: `{username}` uuid: `{uuid}`",
help = "Print debug user data", help = "Print debug user data",
timeout = 200, timeout = 200,
defaultCommand = true defaultCommand = true
)), )),
"version" to ("are you out of date huh ?" "version" to ("are you out of date huh ?"
to CustomCommand( to CustomCommand(
type = CommandType.RESPONSE, type = CommandType.RESPONSE,
response = "{version}", response = "{version}",
help = "are you out of date huh ?", help = "are you out of date huh ?",
timeout = 200, timeout = 200,
defaultCommand = true defaultCommand = true
)), )),
"exec" to ("this uses arguments in a passed-through command, you could restrict the arguments with a regex" "exec" to ("this uses arguments in a passed-through command, you could restrict the arguments with a regex"
to CustomCommand( to CustomCommand(
type = CommandType.EXECUTE, type = CommandType.EXECUTE,
execute = "{args}", execute = "{args}",
argumentsRegex = ".*".toRegex(), argumentsRegex = ".*".toRegex(),
permLevel = 50.0, permLevel = 50.0,
help = "Execute any command as OP, be careful with this one", help = "Execute any command as OP, be careful with this one",
execOp = true, execOp = true,
defaultCommand = true defaultCommand = true
)) ))
) )
val commands: CommandMap = hashMapOf() val commands: CommandMap = hashMapOf()
fun loadFile() { fun loadFile() {
val jankson = Jankson val jankson = Jankson
.builder() .builder()
.registerTypeAdapter { jsonObj -> .registerTypeAdapter { jsonObj ->
with(CustomCommand.DEFAULT) { with(CustomCommand.DEFAULT) {
CustomCommand( CustomCommand(
type = jsonObj.get(CommandType::class.java, "type") ?: type, type = jsonObj.get(CommandType::class.java, "type") ?: type,
execute = jsonObj.get(String::class.java, "execute") ?: execute, execute = jsonObj.get(String::class.java, "execute") ?: execute,
response = jsonObj.get(String::class.java, "response") ?: response, response = jsonObj.get(String::class.java, "response") ?: response,
permLevel = jsonObj.get(Double::class.java, "permLevel") ?: permLevel, permLevel = jsonObj.get(Double::class.java, "permLevel") ?: permLevel,
help = jsonObj.get(String::class.java, "help") ?: help, help = jsonObj.get(String::class.java, "help") ?: help,
timeout = jsonObj.get(Int::class.java, "timeout") ?: timeout, timeout = jsonObj.get(Int::class.java, "timeout") ?: timeout,
defaultCommand = jsonObj.get(Boolean::class.java, "defaultCommand") ?: defaultCommand, defaultCommand = jsonObj.get(Boolean::class.java, "defaultCommand") ?: defaultCommand,
execOp = jsonObj.get(Boolean::class.java, "execOp") ?: execOp, execOp = jsonObj.get(Boolean::class.java, "execOp") ?: execOp,
argumentsRegex = jsonObj.get(Regex::class.java, "argumentsRegex") ?: argumentsRegex argumentsRegex = jsonObj.get(Regex::class.java, "argumentsRegex") ?: argumentsRegex
) )
}
} }
.registerPrimitiveTypeAdapter { }
it.toString().toRegex() .registerPrimitiveTypeAdapter {
} it.toString().toRegex()
.build() }
.build()
jankson.marshaller.registerSerializer(Regex::class.java) { regex, _ -> jankson.marshaller.registerSerializer(Regex::class.java) { regex, _ ->
JsonPrimitive(regex.pattern) JsonPrimitive(regex.pattern)

View File

@ -11,34 +11,34 @@ import matterlink.logger
import matterlink.stackTraceString import matterlink.stackTraceString
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.util.* import java.util.UUID
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
typealias IdentMap = Map<String, Map<String, List<String>>> typealias IdentMap = Map<String, Map<String, List<String>>>
data class AuthRequest( data class AuthRequest(
val username: String, val username: String,
val uuid: String, val uuid: String,
val nonce: String, val nonce: String,
val platform: String, val platform: String,
val userid: String val userid: String
) )
object IdentitiesConfig { object IdentitiesConfig {
val authRequests: Cache<String, AuthRequest> = CacheBuilder.newBuilder() val authRequests: Cache<String, AuthRequest> = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES) .expireAfterWrite(10, TimeUnit.MINUTES)
.build() .build()
private val jankson = Jankson private val jankson = Jankson
.builder() .builder()
.build() .build()
private val configFile: File = baseCfg.cfgDirectory.resolve("identities.hjson") private val configFile: File = baseCfg.cfgDirectory.resolve("identities.hjson")
private val default = mapOf( private val default = mapOf(
("edd31c45-b095-49c5-a9f5-59cec4cfed8c" to mapOf( ("edd31c45-b095-49c5-a9f5-59cec4cfed8c" to mapOf(
"discord.game" to (listOf("112228624366575616") to "discord id") "discord.game" to (listOf("112228624366575616") to "discord id")
) to "username: NikkyAi") ) to "username: NikkyAi")
) )
var idents: IdentMap = mapOf() var idents: IdentMap = mapOf()

View File

@ -6,34 +6,33 @@ import blue.endless.jankson.impl.SyntaxError
import com.google.common.cache.Cache import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder import com.google.common.cache.CacheBuilder
import matterlink.getReified import matterlink.getReified
import matterlink.instance
import matterlink.logger import matterlink.logger
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.util.* import java.util.UUID
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
typealias PermissionMap = Map<String, Double> typealias PermissionMap = Map<String, Double>
data class PermissionRequest( data class PermissionRequest(
val uuid: UUID, val uuid: UUID,
val user: String, val user: String,
val nonce: String, val nonce: String,
val powerlevel: Double? = null val powerlevel: Double? = null
) )
object PermissionConfig { object PermissionConfig {
val permissionRequests: Cache<String, PermissionRequest> = CacheBuilder.newBuilder() val permissionRequests: Cache<String, PermissionRequest> = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES) .expireAfterWrite(10, TimeUnit.MINUTES)
.build() .build()
private val jankson = Jankson private val jankson = Jankson
.builder() .builder()
.build() .build()
private val configFile: File = baseCfg.cfgDirectory.resolve("permissions.hjson") private val configFile: File = baseCfg.cfgDirectory.resolve("permissions.hjson")
private val default = mapOf( 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() val perms: PermissionMap = mutableMapOf()

View File

@ -1,29 +1,34 @@
package matterlink.handlers package matterlink.handlers
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.bridge.command.BridgeCommandRegistry import matterlink.bridge.command.BridgeCommandRegistry
import matterlink.config.cfg
import matterlink.logger import matterlink.logger
import matterlink.stripColorOut import java.util.UUID
import java.util.*
object ChatProcessor { object ChatProcessor {
/** /**
* @return cancel message flag * @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 //TODO: pass message to Locations
logger.info("position: $x $y $z dimension: $dimension") logger.info("position: $x $y $z dimension: $dimension")
val message = msg.trim() val message = msg.trim()
if (uuid != null && BridgeCommandRegistry.handleCommand(message, user, uuid)) return true if (uuid != null && BridgeCommandRegistry.handleCommand(message, user, uuid)) return true
when { when {
message.isNotBlank() -> LocationHandler.sendToLocations( message.isNotBlank() -> LocationHandler.sendToLocations(
user = user, user = user,
msg = message, msg = message,
x = x, y = y, z = z, dimension = dimension, x = x, y = y, z = z, dimension = dimension,
event = event, event = event,
cause = "Message from $user" cause = "Message from $user"
) )

View File

@ -1,36 +1,34 @@
package matterlink.handlers package matterlink.handlers
import matterlink.antiping import matterlink.antiping
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.stripColorOut import matterlink.stripColorOut
import java.util.* import java.util.Random
object DeathHandler { object DeathHandler {
private val random = Random() private val random = Random()
fun handleDeath( suspend fun handleDeath(
player: String, player: String,
deathMessage: String, deathMessage: String,
damageType: String, damageType: String,
x: Int, y: Int, z: Int, x: Int, y: Int, z: Int,
dimension: Int dimension: Int
) { ) {
if (cfg.outgoing.death.enable) { if (cfg.outgoing.death.enable) {
var msg = deathMessage.stripColorOut.replace(player, player.stripColorOut.antiping) var msg = deathMessage.stripColorOut.replace(player, player.stripColorOut.antiping)
if (cfg.outgoing.death.damageType) { if (cfg.outgoing.death.damageType) {
val emojis = cfg.outgoing.death.damageTypeMapping[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)] val damageEmoji = emojis[random.nextInt(emojis.size)]
msg += " $damageEmoji" msg += " $damageEmoji"
} }
LocationHandler.sendToLocations( LocationHandler.sendToLocations(
msg = msg, msg = msg,
x = x, y = y, z = z, dimension = dimension, x = x, y = y, z = z, dimension = dimension,
event = ChatEvent.DEATH, event = ChatEvent.DEATH,
cause = "Death Event of $player", cause = "Death Event of $player",
systemuser = true systemuser = true
) )
} }
} }

View File

@ -1,50 +1,51 @@
package matterlink.handlers package matterlink.handlers
import matterlink.antiping import matterlink.antiping
import matterlink.api.ApiMessage
import matterlink.api.ApiMessage.Companion.JOIN_LEAVE
import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.mapFormat import matterlink.mapFormat
import matterlink.stripColorOut import matterlink.stripColorOut
object JoinLeaveHandler { object JoinLeaveHandler {
fun handleJoin(player: String, suspend fun handleJoin(
x: Int, y: Int, z: Int, player: String,
dimension: Int) { x: Int, y: Int, z: Int,
dimension: Int
) {
if (cfg.outgoing.joinPart.enable) { if (cfg.outgoing.joinPart.enable) {
val msg = cfg.outgoing.joinPart.joinServer.mapFormat( val msg = cfg.outgoing.joinPart.joinServer.mapFormat(
mapOf( mapOf(
"{username}" to player.stripColorOut, "{username}" to player.stripColorOut,
"{username:antiping}" to player.stripColorOut.antiping "{username:antiping}" to player.stripColorOut.antiping
) )
) )
LocationHandler.sendToLocations( LocationHandler.sendToLocations(
msg = msg, msg = msg,
x = x, y = y, z = z, dimension = dimension, x = x, y = y, z = z, dimension = dimension,
event = ChatEvent.JOIN, event = ChatEvent.JOIN,
systemuser = true, systemuser = true,
cause = "$player joined" cause = "$player joined"
) )
} }
} }
fun handleLeave(player: String, suspend fun handleLeave(
x: Int, y: Int, z: Int, player: String,
dimension: Int) { x: Int, y: Int, z: Int,
dimension: Int
) {
if (cfg.outgoing.joinPart.enable) { if (cfg.outgoing.joinPart.enable) {
val msg = cfg.outgoing.joinPart.partServer.mapFormat( val msg = cfg.outgoing.joinPart.partServer.mapFormat(
mapOf( mapOf(
"{username}" to player.stripColorOut, "{username}" to player.stripColorOut,
"{username:antiping}" to player.stripColorOut.antiping "{username:antiping}" to player.stripColorOut.antiping
) )
) )
LocationHandler.sendToLocations( LocationHandler.sendToLocations(
msg = msg, msg = msg,
x = x, y = y, z = z, dimension = dimension, x = x, y = y, z = z, dimension = dimension,
event = ChatEvent.JOIN, event = ChatEvent.JOIN,
systemuser = true, systemuser = true,
cause = "$player left" cause = "$player left"
) )
} }
} }

View File

@ -5,7 +5,7 @@ import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.logger import matterlink.logger
import matterlink.stripColorOut import matterlink.stripColorOut
import java.util.* import java.util.UUID
enum class ChatEvent { enum class ChatEvent {
@ -14,31 +14,31 @@ enum class ChatEvent {
object LocationHandler { object LocationHandler {
fun sendToLocations( suspend fun sendToLocations(
user: String = cfg.outgoing.systemUser, user: String = cfg.outgoing.systemUser,
msg: String, msg: String,
x: Int, y: Int, z: Int, x: Int, y: Int, z: Int,
dimension: Int?, dimension: Int?,
event: ChatEvent, event: ChatEvent,
systemuser: Boolean = false, systemuser: Boolean = false,
uuid: UUID? = null, uuid: UUID? = null,
cause: String cause: String
): Boolean { ): Boolean {
val defaults = cfg.outgoingDefaults val defaults = cfg.outgoingDefaults
var handled = false var handled = false
val skips = mutableSetOf<String>() val skips = mutableSetOf<String>()
logger.info("locations: ${cfg.locations.map { it.label }}") logger.info("locations: ${cfg.locations.map { it.label }}")
for (location in cfg.locations) { for (location in cfg.locations) {
val label = location.label val label = location.label
if(skips.contains(label)) { if (skips.contains(label)) {
logger.info("skipping $label (contained in in $skips)") logger.info("skipping $label (contained in in $skips)")
continue continue
} }
if(!location.area.testForDim(dimension)) { if (!location.area.testForDim(dimension)) {
logger.info("location: $label dropped message '$msg' from $user due to mismatched dimension") logger.info("location: $label dropped message '$msg' from $user due to mismatched dimension")
continue 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") logger.info("location: $label dropped message '$msg' from $user out of coordinate bounds")
continue continue
} }
@ -85,17 +85,17 @@ object LocationHandler {
} }
when { when {
msg.isNotBlank() -> MessageHandlerInst.transmit( msg.isNotBlank() -> MessageHandlerInst.transmit(
ApiMessage( ApiMessage(
username = username.stripColorOut, username = username.stripColorOut,
text = msg.stripColorOut, text = msg.stripColorOut,
event = eventStr, event = eventStr,
gateway = location.gateway gateway = location.gateway
).apply { ).apply {
avatar?.let { avatar?.let {
this.avatar = it this.avatar = it
} }
}, },
cause = cause cause = cause
) )
else -> logger.warn("WARN: dropped blank message by '$user'") else -> logger.warn("WARN: dropped blank message by '$user'")
} }

View File

@ -1,24 +1,24 @@
package matterlink.handlers package matterlink.handlers
import matterlink.antiping import matterlink.antiping
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.stripColorOut import matterlink.stripColorOut
object ProgressHandler { object ProgressHandler {
fun handleProgress(name: String, message: String, display: String, suspend fun handleProgress(
x: Int, y: Int, z: Int, name: String, message: String, display: String,
dimension: Int) { x: Int, y: Int, z: Int,
dimension: Int
) {
if (!cfg.outgoing.advancements) return if (!cfg.outgoing.advancements) return
val usr = name.stripColorOut.antiping val usr = name.stripColorOut.antiping
LocationHandler.sendToLocations( LocationHandler.sendToLocations(
msg = "$usr $message $display".stripColorOut, msg = "$usr $message $display".stripColorOut,
x = x, y = y, z = z, dimension = dimension, x = x, y = y, z = z, dimension = dimension,
event = ChatEvent.ADVANCEMENT, event = ChatEvent.ADVANCEMENT,
cause = "Progress Event by $usr", cause = "Progress Event by $usr",
systemuser = true systemuser = true
) )
} }
} }

View File

@ -7,17 +7,16 @@ import matterlink.bridge.format
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.instance import matterlink.instance
import matterlink.logger import matterlink.logger
import java.util.* import java.util.UUID
object ServerChatHandler { object ServerChatHandler {
val rcvChannel = MessageHandlerInst.broadcast.openSubscription()
/** /**
* This method must be called every server tick with no arguments. * This method must be called every server tick with no arguments.
*/ */
fun writeIncomingToChat() { suspend fun writeIncomingToChat() {
if (MessageHandlerInst.queue.isNotEmpty()) val nextMessage = rcvChannel.poll() ?: return
logger.debug("incoming: " + MessageHandlerInst.queue.toString())
val nextMessage = MessageHandlerInst.queue.poll() ?: return
val defaults = cfg.incomingDefaults val defaults = cfg.incomingDefaults
@ -32,14 +31,14 @@ object ServerChatHandler {
it.gateway == sourceGateway it.gateway == sourceGateway
} }
if(nextMessage.event.isEmpty()) { if (nextMessage.event.isEmpty()) {
// filter command handlers // filter command handlers
val commandLocations = locations.filter { val commandLocations = locations.filter {
it.incoming.commands ?: cfg.incomingDefaults.commands it.incoming.commands ?: cfg.incomingDefaults.commands
} }
// process potential command // process potential command
for (( label, location) in commandLocations) { for ((label, location) in commandLocations) {
if (BridgeCommandRegistry.handleCommand(nextMessage)) return if (BridgeCommandRegistry.handleCommand(nextMessage)) return
} }
} }
@ -48,7 +47,7 @@ object ServerChatHandler {
for (location in locations) { for (location in locations) {
val label = location.label val label = location.label
if(skips.contains(label)) { if (skips.contains(label)) {
logger.debug("skipping $label") logger.debug("skipping $label")
continue continue
} }
@ -101,7 +100,6 @@ object ServerChatHandler {
} }
// if (nextMessage?.gateway == cfg.connect.gateway) { // if (nextMessage?.gateway == cfg.connect.gateway) {
// if (!nextMessage.text.isBlank()) { // if (!nextMessage.text.isBlank()) {
// nextMessage.text = nextMessage.text.stripColorIn // nextMessage.text = nextMessage.text.stripColorIn

View File

@ -1,6 +1,5 @@
package matterlink.handlers package matterlink.handlers
import matterlink.bridge.MessageHandlerInst
import matterlink.update.UpdateChecker import matterlink.update.UpdateChecker
/** /**
@ -13,17 +12,17 @@ object TickHandler {
private set private set
private var accumulator = 0 private var accumulator = 0
private const val updateInterval = 12 * 60 * 60 * 20 private const val updateInterval = 12 * 60 * 60 * 20
fun handleTick() { suspend fun handleTick() {
tickCounter++ tickCounter++
if (tickCounter % 100 == 0) { // if (tickCounter % 100 == 0) {
MessageHandlerInst.checkConnection() // MessageHandlerInst.checkConnection()
} // }
ServerChatHandler.writeIncomingToChat() ServerChatHandler.writeIncomingToChat()
if (accumulator++ > updateInterval) { if (accumulator++ > updateInterval) {
accumulator -= updateInterval accumulator -= updateInterval
UpdateChecker.run() UpdateChecker.check()
} }
} }
} }

View File

@ -7,7 +7,7 @@ package matterlink.jenkins
//@JsonIgnoreProperties(ignoreUnknown = true) //@JsonIgnoreProperties(ignoreUnknown = true)
data class Artifact( data class Artifact(
val displayPath: String, val displayPath: String,
val fileName: String, val fileName: String,
val relativePath: String val relativePath: String
) )

View File

@ -13,15 +13,15 @@ import matterlink.logger
//@JsonIgnoreProperties(ignoreUnknown = true) //@JsonIgnoreProperties(ignoreUnknown = true)
data class Build( data class Build(
val number: Int, val number: Int,
val url: String val url: String
) { ) {
fun details(userAgent: String): BuildWithDetails? { fun details(userAgent: String): BuildWithDetails? {
val (request, response, result) = "$url/api/json" val (request, response, result) = "$url/api/json"
.httpGet() .httpGet()
.header("User-Agent" to userAgent) .header("User-Agent" to userAgent)
.responseString() .responseString()
return when(result) { return when (result) {
is Result.Success -> { is Result.Success -> {
gson.fromJson(result.value, BuildWithDetails::class.java) gson.fromJson(result.value, BuildWithDetails::class.java)
} }

View File

@ -1,12 +1,12 @@
package matterlink.jenkins package matterlink.jenkins
import java.util.* import java.util.Date
//@JsonIgnoreProperties(ignoreUnknown = true) //@JsonIgnoreProperties(ignoreUnknown = true)
data class BuildWithDetails( data class BuildWithDetails(
val number: Int, val number: Int,
val url: String, val url: String,
val artifacts: List<Artifact>, val artifacts: List<Artifact>,
// @JsonFormat(shape=JsonFormat.Shape.NUMBER, pattern="s") // @JsonFormat(shape=JsonFormat.Shape.NUMBER, pattern="s")
val timestamp: Date val timestamp: Date
) )

View File

@ -19,9 +19,9 @@ class JenkinsServer(val url: String) {
fun getJob(job: String, userAgent: String): Job? { fun getJob(job: String, userAgent: String): Job? {
val requestURL = getUrl(job) + "/api/json" val requestURL = getUrl(job) + "/api/json"
val (_, _, result) = requestURL val (_, _, result) = requestURL
.httpGet() .httpGet()
.header("User-Agent" to userAgent) .header("User-Agent" to userAgent)
.responseString() .responseString()
return when (result) { return when (result) {
is Result.Success -> { is Result.Success -> {
gson.fromJson(result.value, Job::class.java) gson.fromJson(result.value, Job::class.java)

View File

@ -7,14 +7,14 @@ package matterlink.jenkins
//@JsonIgnoreProperties(ignoreUnknown = true) //@JsonIgnoreProperties(ignoreUnknown = true)
data class Job( data class Job(
val url: String, val url: String,
val name: String, val name: String,
val fullName: String, val fullName: String,
val displayName: String, val displayName: String,
val fullDisplayName: String, val fullDisplayName: String,
val builds: List<Build>?, val builds: List<Build>?,
val lastSuccessfulBuild: Build?, val lastSuccessfulBuild: Build?,
val lastStableBuild: Build? val lastStableBuild: Build?
) { ) {
fun getBuildByNumber(build: Int, userAgent: String): BuildWithDetails? { fun getBuildByNumber(build: Int, userAgent: String): BuildWithDetails? {
return builds?.find { it.number == build }?.details(userAgent) return builds?.find { it.number == build }?.details(userAgent)

View File

@ -1,9 +1,9 @@
package matterlink.update package matterlink.update
data class CurseFile( data class CurseFile(
val downloadURL: String, val downloadURL: String,
val fileName: String, val fileName: String,
val gameVersion: List<String>, val gameVersion: List<String>,
val releaseType: String, val releaseType: String,
val fileStatus: String val fileStatus: String
) )

View File

@ -1,39 +1,38 @@
package matterlink.update package matterlink.update
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import matterlink.api.ApiMessage import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.handlers.ChatEvent import matterlink.handlers.ChatEvent
import matterlink.handlers.LocationHandler import matterlink.handlers.LocationHandler
import matterlink.instance import matterlink.instance
import matterlink.logger
import matterlink.jenkins.JenkinsServer import matterlink.jenkins.JenkinsServer
import matterlink.logger
import java.io.BufferedReader import java.io.BufferedReader
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
class UpdateChecker : Thread() { object UpdateChecker : CoroutineScope {
companion object { override val coroutineContext = Job() + CoroutineName("UpdateChacker")
fun run() {
if (cfg.update.enable) { suspend fun check() {
UpdateChecker().start() if (cfg.update.enable) {
} run()
} }
} }
init { private suspend fun run() {
name = "UpdateCheckerThread"
}
override fun run() {
if (instance.buildNumber > 0) { if (instance.buildNumber > 0) {
val server = JenkinsServer("https://ci.elytradev.com") val server = JenkinsServer("https://ci.elytradev.com")
val job = server.getJob("elytra/MatterLink/master", "MatterLink/${instance.modVersion}") val job = server.getJob("elytra/MatterLink/master", "MatterLink/${instance.modVersion}")
?: run { ?: run {
logger.error("failed obtaining job: elytra/MatterLink/master") logger.error("failed obtaining job: elytra/MatterLink/master")
return return
} }
//TODO: add job name to constants at build time //TODO: add job name to constants at build time
val build = job.lastSuccessfulBuild ?: run { val build = job.lastSuccessfulBuild ?: run {
logger.error("no successful build found") 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") logger.warn("Mod out of date! New build $number available at $url")
val difference = number - build.number val difference = number - build.number
LocationHandler.sendToLocations( LocationHandler.sendToLocations(
msg = "MatterLink out of date! You are $difference builds behind! Please download new version from $url", msg = "MatterLink out of date! You are $difference builds behind! Please download new version from $url",
x = 0, y =0, z = 0, dimension = null, x = 0, y = 0, z = 0, dimension = null,
event = ChatEvent.STATUS, event = ChatEvent.STATUS,
cause = "MatterLink update notice" cause = "MatterLink update notice"
) )
} }
number < instance.buildNumber -> logger.error("lastSuccessfulBuild: $number is older than installed build: ${instance.buildNumber}") number < instance.buildNumber -> logger.error("lastSuccessfulBuild: $number is older than installed build: ${instance.buildNumber}")
@ -64,7 +63,7 @@ class UpdateChecker : Thread() {
val gson = GsonBuilder() val gson = GsonBuilder()
// .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) // .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.create() .create()
logger.info("Checking for new versions...") logger.info("Checking for new versions...")
@ -72,7 +71,8 @@ class UpdateChecker : Thread() {
val con = url.openConnection() as HttpURLConnection val con = url.openConnection() as HttpURLConnection
with(instance) { 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'") logger.debug("setting User-Agent: '$useragent'")
con.setRequestProperty("User-Agent", useragent) con.setRequestProperty("User-Agent", useragent)
} }
@ -87,10 +87,10 @@ class UpdateChecker : Thread() {
logger.trace("updateData: $content") logger.trace("updateData: $content")
gson.fromJson(content, Array<CurseFile>::class.java) gson.fromJson(content, Array<CurseFile>::class.java)
.filter { .filter {
it.fileStatus == "SemiNormal" && it.gameVersion.contains(instance.mcVersion) it.fileStatus == "SemiNormal" && it.gameVersion.contains(instance.mcVersion)
} }
.sortedByDescending { it.fileName.substringAfterLast(" ") } .sortedByDescending { it.fileName.substringAfterLast(" ") }
} else { } else {
logger.error("Could not check for updates!") logger.error("Could not check for updates!")
@ -98,12 +98,12 @@ class UpdateChecker : Thread() {
} }
val modVersionChunks = instance.modVersion val modVersionChunks = instance.modVersion
.substringBefore("-dev") .substringBefore("-dev")
.substringBefore("-build") .substringBefore("-build")
.split('.') .split('.')
.map { .map {
it.toInt() it.toInt()
} }
val possibleUpdates = mutableListOf<CurseFile>() val possibleUpdates = mutableListOf<CurseFile>()
apiUpdateList.forEach { apiUpdateList.forEach {
@ -138,9 +138,9 @@ class UpdateChecker : Thread() {
logger.warn("Mod out of date! New $version available at ${latest.downloadURL}") logger.warn("Mod out of date! New $version available at ${latest.downloadURL}")
MessageHandlerInst.transmit( MessageHandlerInst.transmit(
ApiMessage( ApiMessage(
text = "MatterLink out of date! You are $count $version behind! Please download new version from ${latest.downloadURL}" text = "MatterLink out of date! You are $count $version behind! Please download new version from ${latest.downloadURL}"
) )
) )
} }
} }

View File

@ -1,10 +1,10 @@
modName = MatterLink modName = MatterLink
modVersion = 1.6.4 modVersion = 1.6.4
forgelinVersion = 1.6.0 forgelinVersion = 1.8.0
kotlinVersion = 1.2.41 kotlinVersion = 1.3.0
shadowVersion = 2.0.2 shadowVersion = 2.0.2
fuelVersion = 1.13.0 fuelVersion = 1.16.0
resultVersion = 1.4.0 resultVersion = 1.6.0
cursegradleVersion = 1.0.10 cursegradleVersion = 1.1.0
curseId = 287323 curseId = 287323
curseReleaseType = beta curseReleaseType = beta

0
scripts/start.sh Normal file → Executable file
View File

0
scripts/test12.sh Normal file → Executable file
View File

0
scripts/test7.sh Normal file → Executable file
View File

0
scripts/test9.sh Normal file → Executable file
View File