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

@ -115,3 +115,7 @@ curseforge {
}
}
}
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,8 +28,8 @@ 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",
@ -38,8 +44,8 @@ object EventHandler {
//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,
@ -55,7 +61,7 @@ object EventHandler {
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun commandEvent(e: CommandEvent) {
fun commandEvent(e: CommandEvent) = runBlocking {
val sender = when {
e.sender is DedicatedServer -> cfg.outgoing.systemUser
@ -66,7 +72,7 @@ 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(
@ -86,7 +92,7 @@ object EventHandler {
//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,
@ -103,7 +109,7 @@ 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(),
@ -116,7 +122,7 @@ object EventHandler {
//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(),
@ -129,8 +135,9 @@ object EventHandler {
//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,7 +19,7 @@ import net.minecraftforge.fml.common.event.FMLServerStartingEvent
import net.minecraftforge.fml.common.event.FMLServerStoppingEvent
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.core.config.Configurator
import java.util.*
import java.util.UUID
@Mod(
@ -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)
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
}

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
@ -13,11 +14,12 @@ import javax.annotation.Nonnull
class MatterLinkCommandSender(
user: String,
env: IBridgeCommand.CommandEnvironment,
op: Boolean) : IMinecraftCommandSender(user, env, op), ICommandSender {
op: Boolean
) : IMinecraftCommandSender(user, env, op), ICommandSender {
override fun execute(cmdString: String): Boolean {
return 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand(
this,
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,13 +27,13 @@ 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(
@ -44,8 +49,8 @@ object EventHandler {
//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,
@ -60,7 +65,7 @@ object EventHandler {
//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,7 +75,7 @@ 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
@ -95,7 +100,7 @@ object EventHandler {
//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(
@ -112,18 +117,19 @@ object EventHandler {
//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)
dimension = e.player.dimension
)
}
//FORGE-DEPENDENT
@SubscribeEvent
fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) {
fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) = runBlocking {
JoinLeaveHandler.handleLeave(
player = e.player.displayName,
x = e.player.posX.toInt(),
@ -135,7 +141,7 @@ object EventHandler {
//FORGE-DEPENDENT
@SubscribeEvent
fun serverTickEvent(e: TickEvent.ServerTickEvent) {
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,7 +19,7 @@ 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,
@ -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()
}
@ -102,7 +103,8 @@ class MatterLink : IMatterLink() {
.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 {

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
@ -12,11 +13,12 @@ import net.minecraft.world.World
class MatterLinkCommandSender(
user: String,
env: IBridgeCommand.CommandEnvironment,
op: Boolean) : IMinecraftCommandSender(user, env, op), ICommandSender {
op: Boolean
) : IMinecraftCommandSender(user, env, op), ICommandSender {
override fun execute(cmdString: String): Boolean {
return 0 < MinecraftServer.getServer().commandManager.executeCommand(
this,
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,13 +26,13 @@ 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,
@ -41,8 +47,8 @@ object EventHandler {
//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,
@ -57,7 +63,7 @@ object EventHandler {
//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,7 +73,7 @@ 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(
@ -86,7 +92,7 @@ object EventHandler {
//FORGE-DEPENDENT
@SubscribeEvent
fun deathEvent(e: LivingDeathEvent) {
fun deathEvent(e: LivingDeathEvent) = runBlocking {
if (e.entityLiving is EntityPlayer) {
DeathHandler.handleDeath(
player = e.entityLiving.displayName.unformattedText,
@ -102,7 +108,7 @@ object EventHandler {
//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(),
@ -114,7 +120,7 @@ object EventHandler {
//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(),
@ -126,7 +132,7 @@ object EventHandler {
//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,7 +18,7 @@ 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,
@ -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 {

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
@ -17,10 +18,11 @@ 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,
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

@ -23,11 +23,16 @@ dependencies {
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
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

@ -159,6 +159,7 @@ sealed class Area {
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 {

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

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

@ -7,8 +7,7 @@ data class Config (
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,37 +94,42 @@ 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}"
}
conn.requestMethod = "GET"
BufferedReader(InputStreamReader(conn.inputStream)).forEachLine { line ->
logger.trace("skipping $line")
}
} catch (e: MalformedURLException) {
e.printStackTrace()
} catch (e: ProtocolException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
.awaitStringResponse()
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()
}
}
}
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) {
open suspend fun transmit(msg: ApiMessage) {
// if (streamConnection.isConnected || streamConnection.isConnecting) {
if (msg.username.isEmpty())
msg.username = config.systemUser
if (msg.gateway.isEmpty()) {
@ -122,80 +137,95 @@ open class MessageHandler {
return
}
logger.debug("Transmitting: $msg")
transmitMessage(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)
@Deprecated("use coroutine api", level = DeprecationLevel.ERROR)
fun checkConnection() {
}
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()
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}"
}
} else {
}
.jsonBody(it.encode())
.responseString()
when (result) {
is Result.Success -> {
logger.info("sent $it")
sendErrors = 0
}
} catch (e: IOException) {
e.printStackTrace()
logger.error("sending message caused $e")
is Result.Failure -> {
sendErrors++
if (sendErrors > 5) {
logger.error("Caught too many errors, closing bridge")
stop()
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
}
}
}
}
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
/**
* clll this method every tick / cycle to make sure it is reconnecting
*/
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
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")
return
}
if (reconnectCooldown <= 0) {
logger.info("Trying to reconnect")
start("Reconnecting to matterbridge API after connection error", false)
} else {
reconnectCooldown--
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

View File

@ -1,18 +1,23 @@
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,
@ -22,7 +27,7 @@ object MessageHandlerInst : MessageHandler() {
)
}
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

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

View File

@ -17,7 +17,7 @@ data class CustomCommand(
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)) {
@ -35,8 +35,10 @@ data class CustomCommand(
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
}

View File

@ -1,16 +1,13 @@
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))}"

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
@ -35,7 +35,7 @@ abstract class IBridgeCommand {
override val uuid: UUID
) : CommandEnvironment()
fun respond(text: String, cause: String = "") {
suspend fun respond(text: String, cause: String = "") {
when (this) {
is BridgeEnv -> {
MessageHandlerInst.transmit(
@ -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
@ -37,7 +35,7 @@ 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"

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"
@ -38,7 +33,13 @@ object CommandCoreAuth {
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"

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) {

View File

@ -4,12 +4,17 @@ import blue.endless.jankson.Jankson
import blue.endless.jankson.JsonObject
import blue.endless.jankson.impl.Marshaller
import blue.endless.jankson.impl.SyntaxError
import matterlink.*
import matterlink.Area
import matterlink.bridge.MessageHandlerInst
import matterlink.getOrDefault
import matterlink.getOrPutList
import matterlink.getReifiedOrDelete
import matterlink.logger
import matterlink.registerSerializer
import matterlink.registerTypeAdapter
import matterlink.stackTraceString
import java.io.File
import java.io.FileNotFoundException
import java.util.*
import kotlin.collections.LinkedHashMap
lateinit var cfg: BaseConfig.MatterLinkConfig
lateinit var baseCfg: BaseConfig
@ -174,7 +179,15 @@ data class BaseConfig(val rootDir: File) {
"explosion" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5"), //💣 💥
"explosion.player" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5"), //💣 💥
"ieWireShock" to arrayOf("\uD83D\uDD0C", "\u26A1"), //🔌 ⚡
"immersiverailroading:hitByTrain" to arrayOf("\uD83D\uDE82", "\uD83D\uDE83", "\uD83D\uDE84", "\uD83D\uDE85", "\uD83D\uDE87", "\uD83D\uDE88", "\uD83D\uDE8A") //🚂 🚃 🚄 🚅 🚇 🚈 🚊
"immersiverailroading:hitByTrain" to arrayOf(
"\uD83D\uDE82",
"\uD83D\uDE83",
"\uD83D\uDE84",
"\uD83D\uDE85",
"\uD83D\uDE87",
"\uD83D\uDE88",
"\uD83D\uDE8A"
) //🚂 🚃 🚄 🚅 🚇 🚈 🚊
)
)
@ -362,7 +375,8 @@ data class BaseConfig(val rootDir: File) {
.registerTypeAdapter {
with(Location()) {
Location(
label = it.getOrDefault("label",
label = it.getOrDefault(
"label",
label,
"location label for identification"
),
@ -549,7 +563,7 @@ data class BaseConfig(val rootDir: File) {
"damageTypMapping",
"Damage type mapping for death cause"
)
it.mapValues { (key, element) ->
it.mapValues { (key, _) ->
it.getOrDefault(key, damageTypeMapping[key] ?: emptyArray(), key)
.apply { it[key] }.apply {
jsonObj["damageTypeMapping"] = it
@ -628,7 +642,7 @@ data class BaseConfig(val rootDir: File) {
}
jsonObject
}
.build()
.build()!!
}
fun load(): MatterLinkConfig {

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

View File

@ -11,7 +11,7 @@ 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>>>

View File

@ -6,11 +6,10 @@ 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>

View File

@ -1,18 +1,23 @@
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()

View File

@ -1,16 +1,14 @@
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(
suspend fun handleDeath(
player: String,
deathMessage: String,
damageType: String,

View File

@ -1,17 +1,16 @@
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,
suspend fun handleJoin(
player: String,
x: Int, y: Int, z: Int,
dimension: Int) {
dimension: Int
) {
if (cfg.outgoing.joinPart.enable) {
val msg = cfg.outgoing.joinPart.joinServer.mapFormat(
mapOf(
@ -29,9 +28,11 @@ object JoinLeaveHandler {
}
}
fun handleLeave(player: String,
suspend fun handleLeave(
player: String,
x: Int, y: Int, z: Int,
dimension: Int) {
dimension: Int
) {
if (cfg.outgoing.joinPart.enable) {
val msg = cfg.outgoing.joinPart.partServer.mapFormat(
mapOf(

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,7 +14,7 @@ enum class ChatEvent {
object LocationHandler {
fun sendToLocations(
suspend fun sendToLocations(
user: String = cfg.outgoing.systemUser,
msg: String,
x: Int, y: Int, z: Int,

View File

@ -1,16 +1,16 @@
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,
suspend fun handleProgress(
name: String, message: String, display: String,
x: Int, y: Int, z: Int,
dimension: Int) {
dimension: Int
) {
if (!cfg.outgoing.advancements) return
val usr = name.stripColorOut.antiping
LocationHandler.sendToLocations(

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

@ -1,6 +1,6 @@
package matterlink.jenkins
import java.util.*
import java.util.Date
//@JsonIgnoreProperties(ignoreUnknown = true)
data class BuildWithDetails(

View File

@ -1,32 +1,31 @@
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() {
object UpdateChecker : CoroutineScope {
override val coroutineContext = Job() + CoroutineName("UpdateChacker")
suspend fun check() {
if (cfg.update.enable) {
UpdateChecker().start()
}
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}")
@ -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)
}

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