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
\.flooignore
bugreport/

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package matterlink.command
import kotlinx.coroutines.runBlocking
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
@ -21,9 +22,9 @@ object MatterLinkCommand : CommandBase() {
return CommandCoreML.aliases
}
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) {
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) = runBlocking {
if (args.isEmpty()) {
throw WrongUsageException("Invalid command! Valid uses: ${this.getUsage(sender)}")
throw WrongUsageException("Invalid command! Valid uses: ${getUsage(sender)}")
}
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()

View File

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

View File

@ -11,17 +11,12 @@ buildscript {
}
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '1.2-SNAPSHOT'
classpath group: 'com.github.jengelman.gradle.plugins', name: 'shadow', version: shadowVersion
classpath group: 'gradle.plugin.com.matthewprenger', name: 'CurseGradle', version: cursegradleVersion
classpath group: 'com.vanniktech', name: 'gradle-dependency-graph-generator-plugin', version: '0.5.0'
}
}
apply plugin: 'forge'
//apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.matthewprenger.cursegradle'
apply plugin: "com.vanniktech.dependency.graph.generator"
version = project.mc_version + '-' + project.modVersion
@ -39,6 +34,12 @@ dependencies {
shade (project(':Jankson')) { transitive = false }
shade group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion
shade(group: "com.github.NikkyAi.Fuel", name: "fuel", version: "feature~chunked-SNAPSHOT")
shade(group: "com.github.NikkyAi.Fuel", name: "fuel-coroutines", version: "feature~chunked-SNAPSHOT")
shade(group: 'com.github.kittinunf.result', name: 'result', version: resultVersion)
shade(group: "org.jetbrains.kotlinx", name: "kotlinx-coroutines-core", version: "1.0.0")
shade(group: "org.jetbrains.kotlinx", name: "kotlinx-serialization-runtime", version: "0.9.0")
shade group: 'com.github.kittinunf.fuel', name: 'fuel', version: fuelVersion
shade group: 'com.github.kittinunf.result', name: 'result', version: resultVersion

View File

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

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

View File

@ -1,5 +1,6 @@
package matterlink.command
import kotlinx.coroutines.runBlocking
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
@ -20,9 +21,9 @@ object MatterLinkCommand : CommandBase() {
return CommandCoreML.aliases
}
override fun processCommand(sender: ICommandSender, args: Array<String>) {
override fun processCommand(sender: ICommandSender, args: Array<String>) = runBlocking {
if (args.isEmpty()) {
throw WrongUsageException("Invalid command! Valid uses: ${this.getCommandUsage(sender)}")
throw WrongUsageException("Invalid command! Valid uses: ${getCommandUsage(sender)}")
}
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package matterlink.command
import kotlinx.coroutines.runBlocking
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
@ -21,9 +22,9 @@ object MatterLinkCommand : CommandBase() {
return CommandCoreML.aliases
}
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) {
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) = runBlocking {
if (args.isEmpty()) {
throw WrongUsageException("Invalid command! Valid uses: ${this.getCommandUsage(sender)}")
throw WrongUsageException("Invalid command! Valid uses: ${getCommandUsage(sender)}")
}
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()

View File

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

@ -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 {
classpath group: "org.jetbrains.kotlin", name: "kotlin-gradle-plugin", version: kotlinVersion
classpath group: "org.jetbrains.kotlin", name: "kotlin-serialization", version: kotlinVersion
}
}
@ -14,6 +15,7 @@ plugins {
subprojects {
apply plugin: "kotlin"
apply plugin: "kotlinx-serialization"
apply plugin: "idea"
if (System.env.BUILD_NUMBER) {
@ -35,24 +37,28 @@ subprojects {
}
repositories {
jcenter()
mavenCentral()
maven {
name = "bintray"
url = 'http://jcenter.bintray.com'
url = "http://jcenter.bintray.com"
}
maven {
name = "elytradev"
url = 'http://unascribed.com/maven/releases'
url = 'https://repo.elytradev.com'
name = "jitpack"
url = "https://jitpack.io"
}
maven {
name = "kotlinx"
url = "https://kotlin.bintray.com/kotlinx/"
}
// maven {
// name = "elytradev"
// // url = 'http://unascribed.com/maven/releases'
// url = "https://repo.elytradev.com"
// }
maven {
name = "shadowfacts"
url = "http://maven.shadowfacts.net/"
}
ivy {
//Resolves baubles and jankson
name = "endless.blue dependency mirror"
artifactPattern "https://endless.blue/files/ivy/[module]-[revision].[ext]"
}
}
compileKotlin {
kotlinOptions {

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 {
compile project(":Jankson")
shadow (project(':Jankson')) { transitive = false }
shadow(project(':Jankson')) { transitive = false }
compile group: 'com.google.code.gson', name: 'gson', version: '+'
compile group: 'com.google.guava', name: 'guava', version: '+'
compile group: 'com.github.kittinunf.fuel', name: 'fuel', version: fuelVersion
shadow (group: 'com.github.kittinunf.fuel', name: 'fuel', version: fuelVersion) { transitive = false }
compile group: 'com.github.kittinunf.result', name: 'result', version: resultVersion
shadow (group: 'com.github.kittinunf.result', name: 'result', version: resultVersion) { transitive = false }
compile(group: "com.github.NikkyAi.Fuel", name: "fuel", version: "feature~chunked-SNAPSHOT")
shadow(group: "com.github.NikkyAi.Fuel", name: "fuel", version: "feature~chunked-SNAPSHOT") { transitive = false }
compile(group: "com.github.NikkyAi.Fuel", name: "fuel-coroutines", version: "feature~chunked-SNAPSHOT")
shadow(group: "com.github.NikkyAi.Fuel", name: "fuel-coroutines", version: "feature~chunked-SNAPSHOT") { transitive = false }
compile(group: 'com.github.kittinunf.result', name: 'result', version: resultVersion)
shadow(group: 'com.github.kittinunf.result', name: 'result', version: resultVersion) { transitive = false }
compile(group: "org.jetbrains.kotlinx", name: "kotlinx-coroutines-core", version: "1.0.0")
compile(group: "org.jetbrains.kotlinx", name: "kotlinx-serialization-runtime", version: "0.9.0")
compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion
}

View File

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

View File

@ -4,10 +4,9 @@ import matterlink.bridge.MessageHandlerInst
import matterlink.bridge.command.BridgeCommandRegistry
import matterlink.bridge.command.IBridgeCommand
import matterlink.bridge.command.IMinecraftCommandSender
import matterlink.config.BaseConfig
import matterlink.config.cfg
import matterlink.update.UpdateChecker
import java.util.*
import java.util.UUID
lateinit var logger: Logger
@ -19,7 +18,11 @@ abstract class IMatterLink {
abstract val buildNumber: Int
abstract val forgeVersion: String
abstract fun commandSenderFor(user: String, env: IBridgeCommand.CommandEnvironment, op: Boolean): IMinecraftCommandSender
abstract fun commandSenderFor(
user: String,
env: IBridgeCommand.CommandEnvironment,
op: Boolean
): IMinecraftCommandSender
abstract fun wrappedSendToPlayers(msg: String)
@ -29,50 +32,19 @@ abstract class IMatterLink {
abstract fun nameToUUID(username: String): UUID?
abstract fun uuidToName(uuid: UUID): String?
init {
}
fun start() {
// MessageHandlerInst.logger = { level, msg ->
// when (level) {
// "FATAL" -> logger.fatal(msg)
// "ERROR" -> logger.error(msg)
// "WARN" -> logger.warn(msg)
// "INFO" -> logger.info(msg)
// "DEBUG" -> logger.debug(msg)
// "TRACE" -> logger.trace(msg)
// }
// }
suspend fun start() {
MessageHandlerInst.logger = logger
serverStartTime = System.currentTimeMillis()
if (cfg.connect.autoConnect)
MessageHandlerInst.start("Server started, connecting to matterbridge API", true)
UpdateChecker.run()
UpdateChecker.check()
}
fun stop() {
suspend fun stop() {
MessageHandlerInst.stop("Server shutting down, disconnecting from matterbridge API")
}
// abstract fun log(level: String, formatString: String, vararg data: Any)
// fun fatal(formatString: String, vararg data: Any) = log("FATAL", formatString, *data)
// fun error(formatString: String, vararg data: Any) = log("ERROR", formatString, *data)
// fun warn(formatString: String, vararg data: Any) = log("WARN", formatString, *data)
// fun info(formatString: String, vararg data: Any) = log("INFO", formatString, *data)
//
// fun debug(formatString: String, vararg data: Any) {
// if (cfg.debug.logLevel == "DEBUG" || cfg.debug.logLevel == "TRACE")
// log("INFO", "DEBUG: " + formatString.replace("\n", "\nDEBUG: "), *data)
// }
//
// fun trace(formatString: String, vararg data: Any) {
// if (cfg.debug.logLevel == "TRACE")
// log("INFO", "TRACE: " + formatString.replace("\n", "\nTRACE: "), *data)
// }
/**
* in milliseconds
*/

View File

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

View File

@ -8,9 +8,7 @@ import blue.endless.jankson.impl.Marshaller
import matterlink.config.cfg
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.Thread.yield
import java.util.*
import kotlin.streams.asSequence
private const val ZWSP: Char = '\u200b'
@ -64,7 +62,7 @@ val Exception.stackTraceString: String
}
fun randomString(length: Int = 6): String =
java.util.UUID.randomUUID().toString().replace("-", "").take(length)
java.util.UUID.randomUUID().toString().replace("-", "").take(length)
fun <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")
@ -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(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) }
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) }
?: run {
this.remove(key)
@ -124,7 +129,11 @@ inline fun <reified T : Any> JsonObject.getOrPutList(key: String, default: List<
} ?: 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 ->
when (map) {
is JsonObject -> {

View File

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

View File

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

View File

@ -1,15 +1,31 @@
package matterlink.api
import awaitStringResponse
import com.github.kittinunf.fuel.core.FuelManager
import com.github.kittinunf.fuel.core.Method
import com.github.kittinunf.fuel.core.ResponseDeserializable
import com.github.kittinunf.fuel.httpGet
import com.github.kittinunf.fuel.httpPost
import com.github.kittinunf.result.Result
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.channels.broadcast
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.JSON
import kotlinx.serialization.list
import matterlink.Logger
import java.io.BufferedReader
import java.io.DataOutputStream
import java.io.IOException
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.MalformedURLException
import java.net.ProtocolException
import java.net.URL
import java.util.concurrent.ConcurrentLinkedQueue
import java.io.Reader
import kotlin.coroutines.CoroutineContext
/**
* Created by nikky on 07/05/18.
@ -17,66 +33,60 @@ import java.util.concurrent.ConcurrentLinkedQueue
* @author Nikky
* @version 1.0
*/
open class MessageHandler {
open class MessageHandler : CoroutineScope {
override val coroutineContext: CoroutineContext = Job()
private var enabled = false
private var connectErrors = 0
private var reconnectCooldown = 0
private var reconnectCooldown = 0L
private var sendErrors = 0
var config: Config = Config()
set(value) {
field = value.apply {
sync(streamConnection)
}
private var sendChannel: SendChannel<ApiMessage> = senderActor()
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 var streamConnection: StreamConnection = StreamConnection(queue)
var logger: Logger
get() = streamConnection.logger
set(l) {
streamConnection.logger = l
private val keepOpenManager = FuelManager().apply {
timeoutInMillisecond = 0
timeoutReadInMillisecond = 0
}
var config: Config = Config()
private var nextCheck: Long = 0
init {
streamConnection.addOnSuccess { success ->
if (success) {
logger.info("connected successfully")
connectErrors = 0
reconnectCooldown = 0
} else {
reconnectCooldown = connectErrors
connectErrors++
logger.error(String.format("connectErrors: %d", connectErrors))
}
}
var logger = object : Logger {
override fun info(message: String) = println("INFO: $message")
override fun debug(message: String) = println("DEBUG: $message")
override fun error(message: String) = println("ERROR: $message")
override fun warn(message: String) = println("WARN: $message")
override fun trace(message: String) = println("TRACE: $message")
}
fun stop(message: String? = null) {
suspend fun stop(message: String? = null) {
if (message != null && config.announceDisconnect) {
sendStatusUpdate(message)
}
enabled = false
streamConnection.close()
rcvJob?.cancel()
rcvJob = null
}
private var rcvJob: Job? = null
fun start(message: String?, clear: Boolean) {
config.sync(streamConnection)
suspend fun start(message: String?, clear: Boolean) {
logger.debug("starting connection")
if (clear) {
clear()
}
enabled = true
streamConnection.open()
rcvJob = messageBroadcast()
if (message != null && config.announceConnect) {
sendStatusUpdate(message)
@ -84,118 +94,138 @@ open class MessageHandler {
}
private fun clear() {
try {
val url = URL(config.url + "/api/messages")
val conn = url.openConnection() as HttpURLConnection
if (!config.token.isEmpty()) {
val bearerAuth = "Bearer " + config.token
conn.setRequestProperty("Authorization", bearerAuth)
private suspend fun clear() {
val url = "${config.url}/api/messages"
val (request, response, result) = url.httpGet()
.apply {
if (config.token.isNotEmpty()) {
headers["Authorization"] = "Bearer ${config.token}"
}
}
.awaitStringResponse()
conn.requestMethod = "GET"
BufferedReader(InputStreamReader(conn.inputStream)).forEachLine { line ->
logger.trace("skipping $line")
when (result) {
is Result.Success -> {
val messages: List<ApiMessage> = JSON.parse(ApiMessage.list, result.value)
messages.forEach { msg ->
logger.trace("skipping $msg")
}
logger.debug("skipped ${messages.count()} messages")
}
is Result.Failure -> {
logger.error("failed to clear messages")
logger.error("url: $url")
logger.error("cUrl: ${request.cUrlString()}")
logger.error("response: $response")
logger.error(result.error.exception.localizedMessage)
result.error.exception.printStackTrace()
}
} catch (e: MalformedURLException) {
e.printStackTrace()
} catch (e: ProtocolException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}
open fun sendStatusUpdate(message: String) {
open suspend fun sendStatusUpdate(message: String) {
transmit(ApiMessage(text = message))
}
open fun transmit(msg: ApiMessage) {
if (streamConnection.isConnected || streamConnection.isConnecting) {
if (msg.username.isEmpty())
msg.username = config.systemUser
if (msg.gateway.isEmpty()) {
logger.error("missing gateway on message: $msg")
return
}
logger.debug("Transmitting: $msg")
transmitMessage(msg)
open suspend fun transmit(msg: ApiMessage) {
// if (streamConnection.isConnected || streamConnection.isConnecting) {
if (msg.username.isEmpty())
msg.username = config.systemUser
if (msg.gateway.isEmpty()) {
logger.error("missing gateway on message: $msg")
return
}
logger.debug("Transmitting: $msg")
sendChannel.send(msg)
// }
}
private fun transmitMessage(message: ApiMessage) {
try {
val url = URL(config.url + "/api/message")
val conn = url.openConnection() as HttpURLConnection
if (!config.token.isEmpty()) {
val bearerAuth = "Bearer " + config.token
conn.setRequestProperty("Authorization", bearerAuth)
}
val postData = message.encode()
logger.trace(postData)
conn.requestMethod = "POST"
conn.setRequestProperty("Content-Type", "application/json")
conn.setRequestProperty("charset", "utf-8")
conn.setRequestProperty("Content-Length", "" + postData.toByteArray().size)
conn.doOutput = true
conn.doInput = true
DataOutputStream(conn.outputStream).use { wr -> wr.write(postData.toByteArray()) }
// conn.getInputStream().close();
conn.connect()
val code = conn.responseCode
if (code != 200) {
logger.error("Server returned $code")
sendErrors++
if (sendErrors > 5) {
logger.error("Interrupting Connection to matterbridge API due to status code $code")
stop()
}
} else {
sendErrors = 0
}
} catch (e: IOException) {
e.printStackTrace()
logger.error("sending message caused $e")
sendErrors++
if (sendErrors > 5) {
logger.error("Caught too many errors, closing bridge")
stop()
}
}
}
/**
* clll this method every tick / cycle to make sure it is reconnecting
*/
@Deprecated("use coroutine api", level = DeprecationLevel.ERROR)
fun checkConnection() {
if (enabled && !streamConnection.isConnected && !streamConnection.isConnecting) {
logger.trace("check connection")
logger.trace("next: $nextCheck")
logger.trace("now: " + System.currentTimeMillis())
if (nextCheck > System.currentTimeMillis()) return
nextCheck = System.currentTimeMillis() + config.reconnectWait
}
if (connectErrors >= 10) {
logger.error("Caught too many errors, closing bridge")
stop("Interrupting connection to matterbridge API due to accumulated connection errors")
return
private fun CoroutineScope.senderActor() = actor<ApiMessage>(context = Dispatchers.IO) {
consumeEach {
logger.debug("sending $it")
val url = "${config.url}/api/message"
val (request, response, result) = url.httpPost()
.apply {
if (config.token.isNotEmpty()) {
headers["Authorization"] = "Bearer ${config.token}"
}
}
.jsonBody(it.encode())
.responseString()
when (result) {
is Result.Success -> {
logger.info("sent $it")
sendErrors = 0
}
is Result.Failure -> {
sendErrors++
logger.error("failed to deliver: $it")
logger.error("url: $url")
logger.error("cUrl: ${request.cUrlString()}")
logger.error("response: $response")
logger.error(result.error.exception.localizedMessage)
result.error.exception.printStackTrace()
// close()
throw result.error.exception
}
}
}
}
if (reconnectCooldown <= 0) {
logger.info("Trying to reconnect")
start("Reconnecting to matterbridge API after connection error", false)
} else {
reconnectCooldown--
private fun CoroutineScope.messageBroadcast() = launch(context = Dispatchers.IO + CoroutineName("msgBroadcaster")) {
loop@ while (isActive) {
logger.info("opening connection")
val url = "${config.url}/api/stream"
val (request, response, result) = keepOpenManager.request(Method.GET, url)
.apply {
if (config.token.isNotEmpty()) {
headers["Authorization"] = "Bearer ${config.token}"
}
}
.responseObject(object : ResponseDeserializable<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 java.io.IOException
import java.io.InputStream
import java.net.ConnectException
import java.net.HttpURLConnection
import java.net.MalformedURLException
@ -22,7 +21,7 @@ class StreamConnection(private val rcvQueue: ConcurrentLinkedQueue<ApiMessage>)
private var urlConnection: HttpURLConnection? = null
private val onSuccessCallbacks = LinkedList<(Boolean) -> Unit>()
var logger = object : Logger {
var logger = object : Logger {
override fun info(message: String) = println("INFO: $message")
override fun debug(message: String) = println("DEBUG: $message")
override fun error(message: String) = println("ERROR: $message")
@ -88,7 +87,7 @@ class StreamConnection(private val rcvQueue: ConcurrentLinkedQueue<ApiMessage>)
}
val chars = input.read(buf)
logger.trace( String.format("read %d chars", chars))
logger.trace(String.format("read %d chars", chars))
if (chars > 0) {
val added = String(Arrays.copyOfRange(buf, 0, chars))
logger.debug("json: $added")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@ object CommandCoreML {
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()
return when (cmd) {
@ -49,8 +49,8 @@ object CommandCoreML {
}
val powerLevelArg = args.getOrNull(2)?.toDoubleOrNull()
val powerLevel = powerLevelArg ?: run { return "permLevel cannot be parsed" }
?: request.powerlevel
?: return "no permLevel provided"
?: request.powerlevel
?: return "no permLevel provided"
PermissionConfig.add(request.uuid, powerLevel, "${request.user} Authorized by $user")
PermissionConfig.permissionRequests.invalidate(requestId)
"added ${request.user} (uuid: ${request.uuid}) with power level: $powerLevel"

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,12 +1,12 @@
package matterlink.jenkins
import java.util.*
import java.util.Date
//@JsonIgnoreProperties(ignoreUnknown = true)
data class BuildWithDetails(
val number: Int,
val url: String,
val artifacts: List<Artifact>,
val number: Int,
val url: String,
val artifacts: List<Artifact>,
// @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? {
val requestURL = getUrl(job) + "/api/json"
val (_, _, result) = requestURL
.httpGet()
.header("User-Agent" to userAgent)
.responseString()
.httpGet()
.header("User-Agent" to userAgent)
.responseString()
return when (result) {
is Result.Success -> {
gson.fromJson(result.value, Job::class.java)

View File

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

View File

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

View File

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

View File

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

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