Compare commits

..

No commits in common. "master" and "1.6.3" have entirely different histories.

85 changed files with 1826 additions and 2651 deletions

6
.gitignore vendored
View File

@ -87,6 +87,10 @@ gradle-app.setting
# Cache of project # Cache of project
.gradletasknamecache .gradletasknamecache
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
# End of https://www.gitignore.io/api/gradle,kotlin,intellij+iml # End of https://www.gitignore.io/api/gradle,kotlin,intellij+iml
### MatterLink ### ### MatterLink ###
@ -98,5 +102,3 @@ run/
\.floo \.floo
\.flooignore \.flooignore
bugreport/

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "api"]
path = api
url = https://github.com/NikkyAI/MatterLinkApi.git
[submodule "Jankson"] [submodule "Jankson"]
path = Jankson path = Jankson
url = https://github.com/falkreon/Jankson.git url = https://github.com/falkreon/Jankson.git

View File

@ -1,4 +0,0 @@
{
"java.configuration.updateBuildConfiguration": "automatic",
"java.home": "/usr/lib/jvm/java-8-openjdk/"
}

View File

@ -11,8 +11,8 @@ buildscript {
} }
dependencies { dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '2.2-SNAPSHOT' classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '2.2-SNAPSHOT'
classpath group: 'com.github.jengelman.gradle.plugins', name: 'shadow', version: shadowVersion classpath group: 'com.github.jengelman.gradle.plugins', name: 'shadow', version: shadow_version
classpath group: 'gradle.plugin.com.matthewprenger', name: 'CurseGradle', version: cursegradleVersion classpath group: 'gradle.plugin.com.matthewprenger', name: 'CurseGradle', version: cursegradle_version
} }
} }
@ -20,26 +20,32 @@ apply plugin: 'net.minecraftforge.gradle.forge'
apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.matthewprenger.cursegradle' apply plugin: 'com.matthewprenger.cursegradle'
version = project.mc_version + '-' + project.modVersion version = project.mc_version + '-' + project.mod_version
archivesBaseName = project.modName archivesBaseName = project.mod_name
sourceCompatibility = targetCompatibility = '1.8' sourceCompatibility = targetCompatibility = '1.8'
dependencies { dependencies {
compile project(':core') compile project(':core')
shadow (project(path: ':core', configuration: 'shadow')) { transitive = false } compile group: 'net.shadowfacts', name: 'Forgelin', version: project.forgelin_version
compile group: 'net.shadowfacts', name: 'Forgelin', version: project.forgelinVersion
} }
shadowJar { shadowJar {
classifier = '' classifier ''
relocate 'blue.endless', 'matterlink.repack.blue.endless'
dependencies {
include project(':core')
include project(':api')
include project(':Jankson')
}
exclude 'dummyThing' exclude 'dummyThing'
configurations = [project.configurations.shadow]
} }
import net.minecraftforge.gradle.user.TaskSourceCopy import net.minecraftforge.gradle.user.TaskSourceCopy
// Mad hacks to make source replacements work for Kotlin // Mad hacks to make source replacements work for Kotlin
@ -68,16 +74,16 @@ minecraft {
mappings = project.mcp_mappings mappings = project.mcp_mappings
replaceIn 'Constants.kt' replaceIn 'Constants.kt'
replace '@MODVERSION@', project.modVersion replace '@MODVERSION@', project.mod_version
replace '@MCVERSION@', project.mc_version replace '@MCVERSION@', project.mc_version
replace '@FORGELIN-VERSION@', project.forgelinVersion replace '@FORGELIN-VERSION@', project.forgelin_version
replace '@FORGE-VERSION@', project.forge_version replace '@FORGE-VERSION@', project.forge_version
replace '-1//@BUILD_NUMBER@', System.env.BUILD_NUMBER ?: -1 replace '-1//@BUILD_NUMBER@', System.env.BUILD_NUMBER ?: -1
} }
processResources { processResources {
// this will ensure that this task is redone when the versions change. // this will ensure that this task is redone when the versions change.
inputs.property 'version', project.modVersion inputs.property 'version', project.mod_version
inputs.property 'mcversion', project.minecraft.version inputs.property 'mcversion', project.minecraft.version
// replace stuff in mcmod.info, nothing else // replace stuff in mcmod.info, nothing else
@ -85,7 +91,7 @@ processResources {
include 'mcmod.info' include 'mcmod.info'
// replace version and mcversion // replace version and mcversion
expand 'version': project.modVersion, 'mcversion': project.minecraft.version expand 'version': project.mod_version, 'mcversion': project.minecraft.version
} }
// copy everything else except the mcmod.info // copy everything else except the mcmod.info
@ -110,6 +116,7 @@ sourceJar {
reobf { reobf {
shadowJar { mappingType = 'SEARGE' } shadowJar { mappingType = 'SEARGE' }
} }
tasks.shadowJar.finalizedBy reobfShadowJar tasks.shadowJar.finalizedBy reobfShadowJar
curseforge { curseforge {
@ -117,9 +124,8 @@ curseforge {
apiKey = CURSEFORGE_API_TOKEN apiKey = CURSEFORGE_API_TOKEN
} }
project { project {
id = project.curseId id = project.curse_id
releaseType = project.curseReleaseType releaseType = project.curse_release_type
addGameVersion '1.10'
if (project.hasProperty('changelog_file')) { if (project.hasProperty('changelog_file')) {
println("changelog = $changelog_file") println("changelog = $changelog_file")
changelogType = 'markdown' changelogType = 'markdown'

3
1.10.2/gradle.properties Normal file
View File

@ -0,0 +1,3 @@
mc_version = 1.10.2
mcp_mappings = stable_29
forge_version = 12.18.3.2185

View File

@ -0,0 +1,108 @@
package matterlink
import matterlink.api.ApiMessage.Companion.USER_ACTION
import matterlink.config.cfg
import matterlink.handlers.*
import net.minecraft.command.server.CommandBroadcast
import net.minecraft.command.server.CommandEmote
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.server.dedicated.DedicatedServer
import net.minecraftforge.event.CommandEvent
import net.minecraftforge.event.ServerChatEvent
import net.minecraftforge.event.entity.living.LivingDeathEvent
import net.minecraftforge.event.entity.player.AchievementEvent
import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.PlayerEvent
import net.minecraftforge.fml.common.gameevent.TickEvent
//FORGE-DEPENDENT
@Mod.EventBusSubscriber
object EventHandler {
//MC-VERSION & FORGE DEPENDENT
@SubscribeEvent
@JvmStatic
fun progressEvent(e: AchievementEvent) {
val achievement = e.achievement
val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return
val statFile = entityPlayer.statFile
if (!statFile.canUnlockAchievement(achievement) || statFile.hasAchievementUnlocked(achievement)) {
return
}
ProgressHandler.handleProgress(
name = e.entityPlayer.displayName.unformattedText,
message = "has earned the achievement",
display = e.achievement.statName.unformattedText
)
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun chatEvent(e: ServerChatEvent) {
if(e.isCanceled) return
e.isCanceled = ChatProcessor.sendToBridge(
user = e.player.displayName.unformattedText,
msg = e.message,
event = "",
uuid = e.player.gameProfile.id
)
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun commandEvent(e: CommandEvent) {
val sender = when {
e.sender is DedicatedServer -> cfg.outgoing.systemUser
else -> e.sender.displayName.unformattedText
}
val args = e.parameters.joinToString(" ")
val type = when {
e.command is CommandEmote -> USER_ACTION
e.command.name == "me" -> USER_ACTION
e.command is CommandBroadcast -> ""
else -> return
}
ChatProcessor.sendToBridge(user = sender, msg = args, event = type)
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun deathEvent(e: LivingDeathEvent) {
if (e.entityLiving is EntityPlayer) {
DeathHandler.handleDeath(
player = e.entityLiving.displayName.unformattedText,
deathMessage = e.entityLiving.combatTracker.deathMessage.unformattedText,
damageType = e.source.damageType
)
}
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) {
JoinLeaveHandler.handleJoin(e.player.displayName.unformattedText)
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) {
JoinLeaveHandler.handleLeave(e.player.displayName.unformattedText)
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun serverTickEvent(e: TickEvent.ServerTickEvent) {
if (e.phase == TickEvent.Phase.END)
TickHandler.handleTick()
}
}

View File

@ -1,9 +1,7 @@
package matterlink package matterlink
import com.mojang.authlib.GameProfile import com.mojang.authlib.GameProfile
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IBridgeCommand
import matterlink.command.AuthCommand
import matterlink.command.MatterLinkCommand import matterlink.command.MatterLinkCommand
import matterlink.command.MatterLinkCommandSender import matterlink.command.MatterLinkCommandSender
import matterlink.config.BaseConfig import matterlink.config.BaseConfig
@ -11,14 +9,15 @@ import matterlink.config.cfg
import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.util.text.TextComponentString import net.minecraft.util.text.TextComponentString
import net.minecraftforge.common.ForgeVersion import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.fml.common.FMLCommonHandler import net.minecraftforge.fml.common.FMLCommonHandler
import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.common.event.FMLInitializationEvent import net.minecraftforge.fml.common.event.FMLInitializationEvent
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent import net.minecraftforge.fml.common.event.FMLPreInitializationEvent
import net.minecraftforge.fml.common.event.FMLServerStartingEvent import net.minecraftforge.fml.common.event.FMLServerStartingEvent
import net.minecraftforge.fml.common.event.FMLServerStoppingEvent import net.minecraftforge.fml.common.event.FMLServerStoppingEvent
import java.util.UUID import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
import java.util.*
@Mod( @Mod(
modid = MODID, modid = MODID,
@ -36,17 +35,7 @@ object MatterLink : IMatterLink() {
@Mod.EventHandler @Mod.EventHandler
fun preInit(event: FMLPreInitializationEvent) { fun preInit(event: FMLPreInitializationEvent) {
MinecraftForge.EVENT_BUS.register(EventHandler) logger = event.modLog as org.apache.logging.log4j.core.Logger
logger = with(event.modLog) {
object : Logger {
override fun info(message: String) = this@with.info(message)
override fun debug(message: String) = this@with.debug(message)
override fun error(message: String) = this@with.error(message)
override fun warn(message: String) = this@with.warn(message)
override fun trace(message: String) = this@with.trace(message)
}
}
logger.info("Building bridge!") logger.info("Building bridge!")
cfg = BaseConfig(event.modConfigurationDirectory).load() cfg = BaseConfig(event.modConfigurationDirectory).load()
@ -58,15 +47,14 @@ object MatterLink : IMatterLink() {
} }
@Mod.EventHandler @Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) = runBlocking { fun serverStarting(event: FMLServerStartingEvent) {
logger.debug("Registering server commands") logger.debug("Registering server commands")
event.registerServerCommand(MatterLinkCommand) event.registerServerCommand(MatterLinkCommand())
event.registerServerCommand(AuthCommand)
start() start()
} }
@Mod.EventHandler @Mod.EventHandler
fun serverStopping(event: FMLServerStoppingEvent) = runBlocking { fun serverStopping(event: FMLServerStoppingEvent) {
stop() stop()
} }
@ -84,7 +72,7 @@ object MatterLink : IMatterLink() {
error("${profile.name} is not online") error("${profile.name} is not online")
return return
} }
player.addChatMessage(TextComponentString(msg)) player.sendMessage(TextComponentString(msg))
} }
override fun wrappedSendToPlayer(uuid: UUID, msg: String) { override fun wrappedSendToPlayer(uuid: UUID, msg: String) {
@ -96,14 +84,12 @@ object MatterLink : IMatterLink() {
logger.error("${profile.name} is not online") logger.error("${profile.name} is not online")
return return
} }
player.addChatMessage(TextComponentString(msg)) player.sendMessage(TextComponentString(msg))
} }
override fun isOnline(username: String) = override fun isOnline(username: String) = FMLCommonHandler.instance().minecraftServerInstance.onlinePlayerNames.contains(username)
FMLCommonHandler.instance().minecraftServerInstance.playerList.allUsernames.contains(username)
private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? = private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? = FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id)
FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id)
private fun profileByUUID(uuid: UUID): GameProfile? = try { private fun profileByUUID(uuid: UUID): GameProfile? = try {
@ -120,17 +106,6 @@ object MatterLink : IMatterLink() {
null null
} }
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())
}
return players.map { it.uniqueID }.toSet()
}
override fun nameToUUID(username: String): UUID? = profileByName(username)?.id override fun nameToUUID(username: String): UUID? = profileByName(username)?.id
override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name

View File

@ -1,5 +1,6 @@
package matterlink.command package matterlink.command
import matterlink.logger
import net.minecraft.command.CommandBase import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException import net.minecraft.command.WrongUsageException
@ -8,16 +9,16 @@ import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString import net.minecraft.util.text.TextComponentString
object AuthCommand : CommandBase() { class AuthCommand : CommandBase() {
override fun getCommandName(): String { override fun getName(): String {
return CommandCoreAuth.name return CommandCoreAuth.name
} }
override fun getCommandUsage(sender: ICommandSender): String { override fun getUsage(sender: ICommandSender): String {
return CommandCoreAuth.usage return CommandCoreAuth.usage
} }
override fun getCommandAliases(): List<String> { override fun getAliases(): List<String> {
return CommandCoreAuth.aliases return CommandCoreAuth.aliases
} }
@ -27,14 +28,14 @@ object AuthCommand : CommandBase() {
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) { override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) {
if (args.isEmpty()) { if (args.isEmpty()) {
throw WrongUsageException("Invalid command! Valid uses: ${this.getCommandUsage(sender)}") throw WrongUsageException("Invalid command! Valid uses: ${this.getUsage(sender)}")
} }
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString() val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()
val reply = CommandCoreAuth.execute(args, sender.name, uuid) val reply = CommandCoreAuth.execute(args, sender.name, uuid)
if (reply.isNotEmpty() && sender.sendCommandFeedback()) { if (reply.isNotEmpty() && sender.sendCommandFeedback()) {
sender.addChatMessage(TextComponentString(reply)) sender.sendMessage(TextComponentString(reply))
} }
} }

View File

@ -1,6 +1,5 @@
package matterlink.command package matterlink.command
import kotlinx.coroutines.runBlocking
import net.minecraft.command.CommandBase import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException import net.minecraft.command.WrongUsageException
@ -9,29 +8,29 @@ import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString import net.minecraft.util.text.TextComponentString
object MatterLinkCommand : CommandBase() { class MatterLinkCommand : CommandBase() {
override fun getCommandName(): String { override fun getName(): String {
return CommandCoreML.name return CommandCoreML.name
} }
override fun getCommandUsage(sender: ICommandSender): String { override fun getUsage(sender: ICommandSender): String {
return CommandCoreML.usage return CommandCoreML.usage
} }
override fun getCommandAliases(): List<String> { override fun getAliases(): List<String> {
return CommandCoreML.aliases return CommandCoreML.aliases
} }
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) = runBlocking { override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) {
if (args.isEmpty()) { if (args.isEmpty()) {
throw WrongUsageException("Invalid command! Valid uses: ${getCommandUsage(sender)}") throw WrongUsageException("Invalid command! Valid uses: ${this.getUsage(sender)}")
} }
val uuid = (sender as? EntityPlayer)?.uniqueID?.toString() val uuid = (sender as? EntityPlayer)?.uniqueID?.toString()
val reply = CommandCoreML.execute(args, sender.name, uuid) val reply = CommandCoreML.execute(args, sender.name, uuid)
if (reply.isNotEmpty() && sender.sendCommandFeedback()) { if (reply.isNotEmpty() && sender.sendCommandFeedback()) {
sender.addChatMessage(TextComponentString(reply)) sender.sendMessage(TextComponentString(reply))
} }
} }
} }

View File

@ -1,6 +1,5 @@
package matterlink.command package matterlink.command
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IBridgeCommand
import matterlink.bridge.command.IMinecraftCommandSender import matterlink.bridge.command.IMinecraftCommandSender
import net.minecraft.command.CommandResultStats import net.minecraft.command.CommandResultStats
@ -18,11 +17,11 @@ import javax.annotation.Nonnull
class MatterLinkCommandSender( class MatterLinkCommandSender(
user: String, user: String,
env: IBridgeCommand.CommandEnvironment, env: IBridgeCommand.CommandEnvironment,
op: Boolean op: Boolean) : IMinecraftCommandSender(user, env, op), ICommandSender {
) : IMinecraftCommandSender(user, env, op), ICommandSender {
override fun execute(cmdString: String): Boolean = runBlocking { override fun execute(cmdString: String): Boolean {
return@runBlocking 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand( return 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand(
this@MatterLinkCommandSender, this,
cmdString cmdString
).apply { ).apply {
sendReply(cmdString) sendReply(cmdString)
@ -39,7 +38,7 @@ class MatterLinkCommandSender(
return FMLCommonHandler.instance().minecraftServerInstance.worldServerForDimension(0) return FMLCommonHandler.instance().minecraftServerInstance.worldServerForDimension(0)
} }
override fun canCommandSenderUseCommand(permLevel: Int, commandName: String): Boolean { override fun canUseCommand(permLevel: Int, commandName: String): Boolean {
//we check user on our end //we check user on our end
return canExecute(commandName) return canExecute(commandName)
} }
@ -48,7 +47,7 @@ class MatterLinkCommandSender(
return FMLCommonHandler.instance().minecraftServerInstance return FMLCommonHandler.instance().minecraftServerInstance
} }
override fun addChatMessage(@Nonnull component: ITextComponent?) { override fun sendMessage(@Nonnull component: ITextComponent?) {
appendReply(component!!.unformattedComponentText) appendReply(component!!.unformattedComponentText)
} }

143
1.11.2/build.gradle Normal file
View File

@ -0,0 +1,143 @@
import net.minecraftforge.gradle.user.TaskSourceCopy
buildscript {
repositories {
jcenter()
maven {
url = 'http://files.minecraftforge.net/maven'
}
mavenCentral()
maven {
url = 'https://oss.sonatype.org/content/groups/public'
}
maven {
url = 'https://plugins.gradle.org/m2/'
}
}
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '2.2-SNAPSHOT'
classpath group: 'com.github.jengelman.gradle.plugins', name: 'shadow', version: shadow_version
classpath group: 'gradle.plugin.com.matthewprenger', name: 'CurseGradle', version: cursegradle_version
}
}
apply plugin: 'net.minecraftforge.gradle.forge'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.matthewprenger.cursegradle'
version = project.mc_version + '-' + project.mod_version
archivesBaseName = project.mod_name
sourceCompatibility = targetCompatibility = '1.8'
dependencies {
compile project(':core')
compile group: 'net.shadowfacts', name: 'Forgelin', version: project.forgelin_version
}
shadowJar {
classifier = ''
relocate 'blue.endless', 'matterlink.repack.blue.endless'
dependencies {
include project(':core')
include project(':api')
include project(':Jankson')
}
exclude 'dummyThing'
}
// Mad hacks to make source replacements work for Kotlin
// source: https://github.com/PaleoCrafter/VanillaImmersion/blob/ee82ecafb76659cf7d7822a722c8f63f43f41d01/build.gradle#L119
for (set in sourceSets) {
def taskName = "source${set.name.capitalize()}Kotlin"
def dir = new File(project.getBuildDir(), "sources/${set.name}/kotlin")
task(taskName, type: TaskSourceCopy) {
source = set.getKotlin()
output = dir
}
def compileTask = tasks[set.getCompileTaskName('kotlin')]
compileTask.source = dir
compileTask.dependsOn taskName
def dirPath = dir.toPath()
compileKotlin.include {
return it.file.toPath().startsWith(dirPath)
}
}
sourceJar.from sourceSets.main.kotlin
minecraft {
version = project.mc_version + '-' + project.forge_version
runDir = 'run'
mappings = project.mcp_mappings
replaceIn 'Constants.kt'
replace '@MODVERSION@', project.mod_version
replace '@MCVERSION@', project.mc_version
replace '@FORGELIN-VERSION@', project.forgelin_version
replace '@FORGE-VERSION@', project.forge_version
replace '-1//@BUILD_NUMBER@', System.env.BUILD_NUMBER ?: -1
}
processResources {
// this will ensure that this task is redone when the versions change.
inputs.property 'version', project.mod_version
inputs.property 'mcversion', project.minecraft.version
// replace stuff in mcmod.info, nothing else
from(project(':core').sourceSets.main.resources.srcDirs) {
include 'mcmod.info'
// replace version and mcversion
expand 'version': project.mod_version, 'mcversion': project.minecraft.version
}
// copy everything else except the mcmod.info
from(project(':core').sourceSets.main.resources.srcDirs) {
exclude 'mcmod.info'
}
}
sourceJar {
classifier = 'sources'
// copy all the minecraftforge specific classes
from sourceSets.main.allSource
// copy everything else except the mcmod.info
from(project(':core').sourceSets.main.allSource) {
exclude 'mcmod.info'
}
}
reobf {
shadowJar { mappingType = 'SEARGE' }
}
tasks.shadowJar.finalizedBy reobfShadowJar
curseforge {
if (project.hasProperty('CURSEFORGE_API_TOKEN') && project.hasProperty('release')) {
apiKey = CURSEFORGE_API_TOKEN
}
project {
id = project.curse_id
releaseType = project.curse_release_type
if (project.hasProperty('changelog_file')) {
println("changelog = $changelog_file")
changelogType = 'markdown'
changelog = file(changelog_file)
}
relations {
requiredLibrary 'shadowfacts-forgelin'
}
mainArtifact(shadowJar) {
displayName = "MatterLink $version"
}
}
}

3
1.11.2/gradle.properties Normal file
View File

@ -0,0 +1,3 @@
mc_version = 1.11.2
mcp_mappings = stable_32
forge_version = 13.20.1.2386

View File

@ -0,0 +1,8 @@
package matterlink
const val MODID = "matterlink"
const val NAME = "MatterLink"
const val MODVERSION = "@MODVERSION@"
const val MCVERSION = "@MCVERSION@"
const val DEPENDENCIES = "required-after:forgelin@[@FORGELIN-VERSION@,);required-after:forge@[@FORGE-VERSION@,);"
const val BUILD_NUMBER = -1//@BUILD_NUMBER@

View File

@ -0,0 +1,109 @@
package matterlink
import matterlink.api.ApiMessage.Companion.USER_ACTION
import matterlink.config.cfg
import matterlink.handlers.*
import net.minecraft.command.server.CommandBroadcast
import net.minecraft.command.server.CommandEmote
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.server.dedicated.DedicatedServer
import net.minecraftforge.event.CommandEvent
import net.minecraftforge.event.ServerChatEvent
import net.minecraftforge.event.entity.living.LivingDeathEvent
import net.minecraftforge.event.entity.player.AchievementEvent
import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.PlayerEvent
import net.minecraftforge.fml.common.gameevent.TickEvent
//FORGE-DEPENDENT
@Mod.EventBusSubscriber
object EventHandler {
//MC-VERSION & FORGE DEPENDENT
@SubscribeEvent
@JvmStatic
fun progressEvent(e: AchievementEvent) {
val achievement = e.achievement
val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return
val statFile = entityPlayer.statFile
if (!statFile.canUnlockAchievement(achievement) || statFile.hasAchievementUnlocked(achievement)) {
return
}
ProgressHandler.handleProgress(
name = e.entityPlayer.displayName.unformattedText,
message = "has earned the achievement",
display = e.achievement.statName.unformattedText
)
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun chatEvent(e: ServerChatEvent) {
if(e.isCanceled) return
e.isCanceled = ChatProcessor.sendToBridge(
user = e.player.displayName.unformattedText,
msg = e.message,
event = "",
uuid = e.player.gameProfile.id
)
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun commandEvent(e: CommandEvent) {
val sender = when {
e.sender is DedicatedServer -> cfg.outgoing.systemUser
else -> e.sender.displayName.unformattedText
}
val args = e.parameters.joinToString(" ")
val type = when {
e.command is CommandEmote -> USER_ACTION
e.command.name == "me" -> USER_ACTION
e.command is CommandBroadcast -> ""
else -> return
}
ChatProcessor.sendToBridge(user = sender, msg = args, event = type)
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun deathEvent(e: LivingDeathEvent) {
if (e.entityLiving is EntityPlayer) {
DeathHandler.handleDeath(
player = e.entityLiving.displayName.unformattedText,
deathMessage = e.entityLiving.combatTracker.deathMessage.unformattedText,
damageType = e.source.damageType
)
}
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) {
JoinLeaveHandler.handleJoin(e.player.displayName.unformattedText)
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) {
JoinLeaveHandler.handleLeave(e.player.displayName.unformattedText)
}
//FORGE-DEPENDENT
@SubscribeEvent
@JvmStatic
fun serverTickEvent(e: TickEvent.ServerTickEvent) {
if (e.phase == TickEvent.Phase.END)
TickHandler.handleTick()
}
}

View File

@ -0,0 +1,125 @@
package matterlink
import com.mojang.authlib.GameProfile
import jline.internal.Log.warn
import matterlink.bridge.command.IBridgeCommand
import matterlink.command.MatterLinkCommand
import matterlink.command.MatterLinkCommandSender
import matterlink.config.BaseConfig
import matterlink.config.cfg
import net.minecraft.entity.ai.EntityMoveHelper
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.util.text.TextComponentString
import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.fml.common.FMLCommonHandler
import net.minecraftforge.fml.common.Mod
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 org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
import java.util.*
@Mod(
modid = MODID,
name = NAME, version = MODVERSION,
serverSideOnly = true,
useMetadata = true,
acceptableRemoteVersions = "*",
modLanguageAdapter = "net.shadowfacts.forgelin.KotlinAdapter",
dependencies = DEPENDENCIES
)
object MatterLink : IMatterLink() {
init {
instance = this
}
@Mod.EventHandler
fun preInit(event: FMLPreInitializationEvent) {
logger = event.modLog as org.apache.logging.log4j.core.Logger
logger.info("Building bridge!")
cfg = BaseConfig(event.modConfigurationDirectory).load()
}
@Mod.EventHandler
fun init(event: FMLInitializationEvent) {
this.registerBridgeCommands()
}
@Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) {
logger.debug("Registering server commands")
event.registerServerCommand(MatterLinkCommand())
start()
}
@Mod.EventHandler
fun serverStopping(event: FMLServerStoppingEvent) {
stop()
}
//FORGE-DEPENDENT
override fun wrappedSendToPlayers(msg: String) {
FMLCommonHandler.instance().minecraftServerInstance.playerList.sendMessage(TextComponentString(msg))
}
override fun wrappedSendToPlayer(username: String, msg: String) {
val profile = profileByName(username) ?: run {
logger.error("cannot find player by name $username")
return
}
val player = playerByProfile(profile) ?: run {
logger.error("${profile.name} is not online")
return
}
player.sendMessage(TextComponentString(msg))
}
override fun wrappedSendToPlayer(uuid: UUID, msg: String) {
val profile = profileByUUID(uuid) ?: run {
logger.error("cannot find player by uuid $uuid")
return
}
val player = playerByProfile(profile) ?: run {
logger.error("${profile.name} is not online")
return
}
player.sendMessage(TextComponentString(msg))
}
override fun isOnline(username: String) = FMLCommonHandler.instance().minecraftServerInstance.onlinePlayerNames.contains(username)
private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? = FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id)
private fun profileByUUID(uuid: UUID): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getProfileByUUID(uuid)
} catch (e: IllegalArgumentException) {
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) {
logger.warn("cannot find profile by username $username")
null
}
override fun nameToUUID(username: String): UUID? = profileByName(username)?.id
override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name
override fun commandSenderFor(
user: String,
env: IBridgeCommand.CommandEnvironment,
op: Boolean
) = MatterLinkCommandSender(user, env, op)
override val mcVersion: String = MCVERSION
override val modVersion: String = MODVERSION
override val buildNumber = BUILD_NUMBER
override val forgeVersion = ForgeVersion.getVersion()
}

View File

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

View File

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

View File

@ -0,0 +1,66 @@
package matterlink.command
import matterlink.bridge.command.IBridgeCommand
import matterlink.bridge.command.IMinecraftCommandSender
import net.minecraft.command.CommandResultStats
import net.minecraft.command.ICommandSender
import net.minecraft.entity.Entity
import net.minecraft.server.MinecraftServer
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d
import net.minecraft.util.text.ITextComponent
import net.minecraft.util.text.TextComponentString
import net.minecraft.world.World
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
).apply {
sendReply(cmdString)
}
}
override fun getDisplayName(): ITextComponent {
return TextComponentString(displayName)
}
override fun getName() = accountName
override fun getEntityWorld(): World {
return FMLCommonHandler.instance().minecraftServerInstance.getWorld(0)
}
override fun canUseCommand(permLevel: Int, commandName: String): Boolean {
//we now do permissions checking on our end
return canExecute(commandName)
}
override fun getServer(): MinecraftServer? {
return FMLCommonHandler.instance().minecraftServerInstance
}
override fun sendMessage(@Nonnull component: ITextComponent?) {
appendReply(component!!.unformattedComponentText)
}
override fun sendCommandFeedback(): Boolean {
return true
}
//WtfMojangWhy
override fun getPosition(): BlockPos = BlockPos.ORIGIN
override fun setCommandStat(type: CommandResultStats.Type?, amount: Int) {}
override fun getPositionVector(): Vec3d = Vec3d.ZERO
override fun getCommandSenderEntity(): Entity? = null
}

View File

@ -14,35 +14,38 @@ buildscript {
} }
dependencies { dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '2.3-SNAPSHOT' classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '2.3-SNAPSHOT'
classpath group: 'com.github.jengelman.gradle.plugins', name: 'shadow', version: shadowVersion classpath group: 'com.github.jengelman.gradle.plugins', name: 'shadow', version: shadow_version
classpath group: 'gradle.plugin.com.matthewprenger', name: 'CurseGradle', version: cursegradleVersion classpath group: 'gradle.plugin.com.matthewprenger', name: 'CurseGradle', version: cursegradle_version
classpath group: 'com.vanniktech', name: 'gradle-dependency-graph-generator-plugin', version: '0.5.0'
} }
} }
apply plugin: 'net.minecraftforge.gradle.forge' apply plugin: 'net.minecraftforge.gradle.forge'
apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.matthewprenger.cursegradle' apply plugin: 'com.matthewprenger.cursegradle'
apply plugin: "com.vanniktech.dependency.graph.generator"
version = project.mc_version + '-' + project.modVersion version = project.mc_version + '-' + project.mod_version
archivesBaseName = project.modName archivesBaseName = project.mod_name
sourceCompatibility = targetCompatibility = '1.8' sourceCompatibility = targetCompatibility = '1.8'
dependencies { dependencies {
compile project(':core') compile project(':core')
shadow (project(path: ':core', configuration: 'shadow')) { transitive = false } compile group: 'net.shadowfacts', name: 'Forgelin', version: project.forgelin_version
compile group: 'net.shadowfacts', name: 'Forgelin', version: project.forgelinVersion
} }
shadowJar { shadowJar {
classifier = '' classifier = ''
relocate 'blue.endless', 'matterlink.repack.blue.endless'
dependencies {
include project(':core')
include project(':api')
include project(':Jankson')
}
exclude 'dummyThing' exclude 'dummyThing'
configurations = [project.configurations.shadow]
} }
minecraft { minecraft {
@ -52,16 +55,16 @@ minecraft {
mappings = project.mcp_mappings mappings = project.mcp_mappings
replaceIn 'Constants.kt' replaceIn 'Constants.kt'
replace '@MODVERSION@', project.modVersion replace '@MODVERSION@', project.mod_version
replace '@MCVERSION@', project.mc_version replace '@MCVERSION@', project.mc_version
replace '@FORGELIN-VERSION@', project.forgelinVersion replace '@FORGELIN-VERSION@', project.forgelin_version
replace '@FORGE-VERSION@', project.forge_version replace '@FORGE-VERSION@', project.forge_version
replace '-1//@BUILD_NUMBER@', System.env.BUILD_NUMBER ?: -1 replace '-1//@BUILD_NUMBER@', System.env.BUILD_NUMBER ?: -1
} }
processResources { processResources {
// this will ensure that this task is redone when the versions change. // this will ensure that this task is redone when the versions change.
inputs.property "version", project.modVersion inputs.property "version", project.mod_version
inputs.property "mcversion", project.minecraft.version inputs.property "mcversion", project.minecraft.version
// replace stuff in mcmod.info, nothing else // replace stuff in mcmod.info, nothing else
@ -69,7 +72,7 @@ processResources {
include 'mcmod.info' include 'mcmod.info'
// replace version and mcversion // replace version and mcversion
expand 'version': project.modVersion, 'mcversion': project.minecraft.version expand 'version': project.mod_version, 'mcversion': project.minecraft.version
} }
// copy everything else except the mcmod.info // copy everything else except the mcmod.info
@ -93,6 +96,7 @@ sourceJar {
reobf { reobf {
shadowJar { mappingType = 'SEARGE' } shadowJar { mappingType = 'SEARGE' }
} }
tasks.shadowJar.finalizedBy reobfShadowJar tasks.shadowJar.finalizedBy reobfShadowJar
curseforge { curseforge {
@ -100,8 +104,8 @@ curseforge {
apiKey = CURSEFORGE_API_TOKEN apiKey = CURSEFORGE_API_TOKEN
} }
project { project {
id = project.curseId id = project.curse_id
releaseType = project.curseReleaseType releaseType = project.curse_release_type
if (project.hasProperty('changelog_file')) { if (project.hasProperty('changelog_file')) {
println("changelog = $changelog_file") println("changelog = $changelog_file")
changelogType = 'markdown' changelogType = 'markdown'
@ -115,7 +119,3 @@ curseforge {
} }
} }
} }
runServer {
outputs.upToDateWhen { false }
}

View File

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

View File

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

View File

@ -1,7 +1,7 @@
package matterlink package matterlink
import com.mojang.authlib.GameProfile import com.mojang.authlib.GameProfile
import kotlinx.coroutines.runBlocking import jline.internal.Log.warn
import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IBridgeCommand
import matterlink.command.AuthCommand import matterlink.command.AuthCommand
import matterlink.command.MatterLinkCommand import matterlink.command.MatterLinkCommand
@ -19,7 +19,7 @@ import net.minecraftforge.fml.common.event.FMLServerStartingEvent
import net.minecraftforge.fml.common.event.FMLServerStoppingEvent import net.minecraftforge.fml.common.event.FMLServerStoppingEvent
import org.apache.logging.log4j.Level import org.apache.logging.log4j.Level
import org.apache.logging.log4j.core.config.Configurator import org.apache.logging.log4j.core.config.Configurator
import java.util.UUID import java.util.*
@Mod( @Mod(
@ -40,16 +40,7 @@ object MatterLink : IMatterLink() {
@Mod.EventHandler @Mod.EventHandler
fun preInit(event: FMLPreInitializationEvent) { fun preInit(event: FMLPreInitializationEvent) {
logger = with(event.modLog) { logger = event.modLog as org.apache.logging.log4j.core.Logger
object : Logger {
override fun info(message: String) = this@with.info(message)
override fun debug(message: String) = this@with.debug(message)
override fun error(message: String) = this@with.error(message)
override fun warn(message: String) = this@with.warn(message)
override fun trace(message: String) = this@with.trace(message)
}
}
logger.info("Building bridge!") logger.info("Building bridge!")
cfg = BaseConfig(event.modConfigurationDirectory).load() cfg = BaseConfig(event.modConfigurationDirectory).load()
@ -57,22 +48,19 @@ object MatterLink : IMatterLink() {
@Mod.EventHandler @Mod.EventHandler
fun init(event: FMLInitializationEvent) { fun init(event: FMLInitializationEvent) {
logger.debug("Registering bridge commands")
this.registerBridgeCommands() this.registerBridgeCommands()
} }
@Mod.EventHandler @Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) { fun serverStarting(event: FMLServerStartingEvent) {
logger.debug("Registering server commands") logger.debug("Registering server commands")
event.registerServerCommand(MatterLinkCommand) event.registerServerCommand(MatterLinkCommand())
event.registerServerCommand(AuthCommand) event.registerServerCommand(AuthCommand())
runBlocking {
start() start()
} }
}
@Mod.EventHandler @Mod.EventHandler
fun serverStopping(event: FMLServerStoppingEvent) = runBlocking { fun serverStopping(event: FMLServerStoppingEvent) {
stop() stop()
} }
@ -105,34 +93,24 @@ object MatterLink : IMatterLink() {
player.sendMessage(TextComponentString(msg)) player.sendMessage(TextComponentString(msg))
} }
override fun isOnline(username: String) = override fun isOnline(username: String) = FMLCommonHandler.instance().minecraftServerInstance.onlinePlayerNames.contains(username)
FMLCommonHandler.instance().minecraftServerInstance.onlinePlayerNames.contains(username)
private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? = private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? = FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id)
FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id)
private fun profileByUUID(uuid: UUID): GameProfile? = try { private fun profileByUUID(uuid: UUID): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getProfileByUUID(uuid) FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getProfileByUUID(uuid)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
logger.warn("cannot find profile by uuid $uuid") warn("cannot find profile by uuid $uuid")
null null
} }
private fun profileByName(username: String): GameProfile? = try { private fun profileByName(username: String): GameProfile? = try {
FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getGameProfileForUsername(username) FMLCommonHandler.instance().minecraftServerInstance.playerProfileCache.getGameProfileForUsername(username)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
logger.warn("cannot find profile by username $username") warn("cannot find profile by username $username")
null null
} }
override fun collectPlayers(area: Area): Set<UUID> {
val players = FMLCommonHandler.instance().minecraftServerInstance.playerList.players.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()
}
override fun nameToUUID(username: String): UUID? = profileByName(username)?.id override fun nameToUUID(username: String): UUID? = profileByName(username)?.id
override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name

View File

@ -1,5 +1,6 @@
package matterlink.command package matterlink.command
import matterlink.logger
import net.minecraft.command.CommandBase import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException import net.minecraft.command.WrongUsageException
@ -8,7 +9,7 @@ import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString import net.minecraft.util.text.TextComponentString
object AuthCommand : CommandBase() { class AuthCommand : CommandBase() {
override fun getName(): String { override fun getName(): String {
return CommandCoreAuth.name return CommandCoreAuth.name
} }

View File

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

View File

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

View File

@ -11,16 +11,16 @@ buildscript {
} }
dependencies { dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '1.2-SNAPSHOT' classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '1.2-SNAPSHOT'
classpath group: 'gradle.plugin.com.matthewprenger', name: 'CurseGradle', version: cursegradleVersion classpath group: 'gradle.plugin.com.matthewprenger', name: 'CurseGradle', version: cursegradle_version
} }
} }
apply plugin: 'forge' apply plugin: 'forge'
apply plugin: 'com.matthewprenger.cursegradle' apply plugin: 'com.matthewprenger.cursegradle'
version = project.mc_version + '-' + project.modVersion version = project.mc_version + '-' + project.mod_version
archivesBaseName = project.modName archivesBaseName = project.mod_name
sourceCompatibility = targetCompatibility = '1.8' sourceCompatibility = targetCompatibility = '1.8'
@ -30,35 +30,21 @@ configurations {
} }
dependencies { dependencies {
shade (project(':core')) { transitive = true } shade project(':core')
shade (project(':Jankson')) { transitive = false } shade project(':api')
shade project(':Jankson')
shade group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion
shade(group: "org.jetbrains.kotlinx", name: "kotlinx-coroutines-core", version: coroutinesVersion)
shade(group: "org.jetbrains.kotlinx", name: "kotlinx-serialization-runtime", version: serializationVersion)
shade(group: "com.github.kittinunf.Fuel", name: "fuel", version: fuelVersion)
shade(group: "com.github.kittinunf.Fuel", name: "fuel-coroutines", version: fuelVersion)
shade(group: "com.github.kittinunf.Fuel", name: "fuel-kotlinx-serialization", version: fuelVersion)
shade(group: 'com.github.kittinunf.result', name: 'result', version: resultVersion)
// shade group: 'com.github.kittinunf.fuel', name: 'fuel', version: fuelVersion
// shade group: 'com.github.kittinunf.result', name: 'result', version: resultVersion
} }
minecraft { minecraft {
version = project.mc_version + '-' + project.forge_version + '-' + project.mc_version version = project.mc_version + '-' + project.forge_version + '-' + project.mc_version
runDir = 'run' runDir = 'run'
mappings = project.mcp_mappings mappings = project.mcp_mappings
// srgExtra 'PK: kotlin matterlink/repack/kotlin' srgExtra 'PK: kotlin matterlink/repack/kotlin'
// srgExtra 'PK: org/jetbrains/annotations matterlink/repack/org/jetbrains/annotations' srgExtra 'PK: org/jetbrains/annotations matterlink/repack/org/jetbrains/annotations'
// srgExtra 'PK: org/intellij matterlink/repack/org/intellij' srgExtra 'PK: org/intellij matterlink/repack/org/intellij'
// srgExtra 'PK: blue/endless/ matterlink/repack/blue/endless/' srgExtra 'PK: blue/endless matterlink/repack/blue/endless'
} }
compileKotlin.doFirst { compileKotlin.doFirst {
@ -67,13 +53,13 @@ compileKotlin.doFirst {
from('src/templates/kotlin/matterlink/Constants.kt') from('src/templates/kotlin/matterlink/Constants.kt')
into(target) into(target)
} }
ant.replaceregexp(match: '@MODVERSION@', replace: project.modVersion, flags: 'g', byline: true) { ant.replaceregexp(match: '@MODVERSION@', replace: project.mod_version, flags: 'g', byline: true) {
fileset(dir: target, includes: 'Constants.kt') fileset(dir: target, includes: 'Constants.kt')
} }
ant.replaceregexp(match: '@MCVERSION@', replace: project.mc_version, flags: 'g', byline: true) { ant.replaceregexp(match: '@MCVERSION@', replace: project.mc_version, flags: 'g', byline: true) {
fileset(dir: target, includes: 'Constants.kt') fileset(dir: target, includes: 'Constants.kt')
} }
ant.replaceregexp(match: '@FORGELIN-VERSION@', replace: project.forgelinVersion, flags: 'g', byline: true) { ant.replaceregexp(match: '@FORGELIN-VERSION@', replace: project.forgelin_version, flags: 'g', byline: true) {
fileset(dir: target, includes: 'Constants.kt') fileset(dir: target, includes: 'Constants.kt')
} }
ant.replaceregexp(match: '@FORGE-VERSION@', replace: project.forge_version, flags: 'g', byline: true) { ant.replaceregexp(match: '@FORGE-VERSION@', replace: project.forge_version, flags: 'g', byline: true) {
@ -86,7 +72,7 @@ compileKotlin.doFirst {
processResources { processResources {
// this will ensure that this task is redone when the versions change. // this will ensure that this task is redone when the versions change.
inputs.property 'version', project.modVersion inputs.property 'version', project.mod_version
inputs.property 'mcversion', project.minecraft.version inputs.property 'mcversion', project.minecraft.version
// replace stuff in mcmod.info, nothing else // replace stuff in mcmod.info, nothing else
@ -94,7 +80,7 @@ processResources {
include 'mcmod.info' include 'mcmod.info'
// replace version and mcversion // replace version and mcversion
expand 'version': project.modVersion, 'mcversion': project.minecraft.version expand 'version': project.mod_version, 'mcversion': project.minecraft.version
} }
// copy everything else except the mcmod.info // copy everything else except the mcmod.info
@ -107,6 +93,7 @@ jar {
configurations.shade.each { dep -> configurations.shade.each { dep ->
from(project.zipTree(dep)) { from(project.zipTree(dep)) {
exclude 'META-INF', 'META-INF/**' exclude 'META-INF', 'META-INF/**'
exclude 'com/google/gson', 'com/google/gson/**'
} }
} }
} }
@ -116,8 +103,8 @@ curseforge {
apiKey = CURSEFORGE_API_TOKEN apiKey = CURSEFORGE_API_TOKEN
} }
project { project {
id = project.curseId id = project.curse_id
releaseType = project.curseReleaseType releaseType = project.curse_release_type
if (project.hasProperty('changelog_file')) { if (project.hasProperty('changelog_file')) {
println("changelog = $changelog_file") println("changelog = $changelog_file")
changelogType = 'markdown' changelogType = 'markdown'

View File

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

View File

@ -7,7 +7,6 @@ import cpw.mods.fml.common.event.FMLInitializationEvent
import cpw.mods.fml.common.event.FMLPreInitializationEvent import cpw.mods.fml.common.event.FMLPreInitializationEvent
import cpw.mods.fml.common.event.FMLServerStartingEvent import cpw.mods.fml.common.event.FMLServerStartingEvent
import cpw.mods.fml.common.event.FMLServerStoppingEvent import cpw.mods.fml.common.event.FMLServerStoppingEvent
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IBridgeCommand
import matterlink.command.AuthCommand import matterlink.command.AuthCommand
import matterlink.command.MatterLinkCommand import matterlink.command.MatterLinkCommand
@ -19,7 +18,7 @@ import net.minecraft.server.MinecraftServer
import net.minecraft.util.ChatComponentText import net.minecraft.util.ChatComponentText
import net.minecraftforge.common.ForgeVersion import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.MinecraftForge
import java.util.UUID import java.util.*
@Mod( @Mod(
modid = MODID, modid = MODID,
@ -37,16 +36,7 @@ class MatterLink : IMatterLink() {
MinecraftForge.EVENT_BUS.register(EventHandler) MinecraftForge.EVENT_BUS.register(EventHandler)
FMLCommonHandler.instance().bus().register(EventHandler) FMLCommonHandler.instance().bus().register(EventHandler)
logger = with(event.modLog) { logger = event.modLog as org.apache.logging.log4j.core.Logger
object : Logger {
override fun info(message: String) = this@with.info(message)
override fun debug(message: String) = this@with.debug(message)
override fun error(message: String) = this@with.error(message)
override fun warn(message: String) = this@with.warn(message)
override fun trace(message: String) = this@with.trace(message)
}
}
logger.info("Building bridge!") logger.info("Building bridge!")
cfg = BaseConfig(event.modConfigurationDirectory).load() cfg = BaseConfig(event.modConfigurationDirectory).load()
@ -58,15 +48,15 @@ class MatterLink : IMatterLink() {
} }
@Mod.EventHandler @Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) = runBlocking { fun serverStarting(event: FMLServerStartingEvent) {
logger.debug("Registering server commands") logger.debug("Registering server commands")
event.registerServerCommand(MatterLinkCommand) event.registerServerCommand(MatterLinkCommand())
event.registerServerCommand(AuthCommand) event.registerServerCommand(AuthCommand())
start() start()
} }
@Mod.EventHandler @Mod.EventHandler
fun serverStopping(event: FMLServerStoppingEvent) = runBlocking { fun serverStopping(event: FMLServerStoppingEvent) {
stop() stop()
} }
@ -103,8 +93,7 @@ class MatterLink : IMatterLink() {
.minecraftServerInstance.configurationManager.getPlayerByUsername(username) ?: null) != null .minecraftServerInstance.configurationManager.getPlayerByUsername(username) ?: null) != null
private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? { private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? {
return FMLCommonHandler.instance() return FMLCommonHandler.instance().minecraftServerInstance.configurationManager.createPlayerForUser(gameProfile)
.minecraftServerInstance.configurationManager.getPlayerByUsername(gameProfile.name)
} }
private fun profileByUUID(uuid: UUID): GameProfile? = try { private fun profileByUUID(uuid: UUID): GameProfile? = try {
@ -121,16 +110,6 @@ class MatterLink : IMatterLink() {
null null
} }
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())
}
return players.map { it.uniqueID }.toSet()
}
override fun nameToUUID(username: String): UUID? = profileByName(username)?.id override fun nameToUUID(username: String): UUID? = profileByName(username)?.id
override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name override fun uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name

View File

@ -7,7 +7,7 @@ import net.minecraft.entity.player.EntityPlayer
import net.minecraft.util.ChatComponentText import net.minecraft.util.ChatComponentText
object AuthCommand : CommandBase() { class AuthCommand : CommandBase() {
override fun getCommandName(): String { override fun getCommandName(): String {
return CommandCoreAuth.name return CommandCoreAuth.name
} }

View File

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

View File

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

View File

@ -1,3 +0,0 @@
mc_version = 1.9.4
mcp_mappings = stable_26
forge_version = 12.17.0.2051

View File

@ -1,139 +0,0 @@
package matterlink
import kotlinx.coroutines.runBlocking
import matterlink.config.cfg
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
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.server.dedicated.DedicatedServer
import net.minecraftforge.event.CommandEvent
import net.minecraftforge.event.ServerChatEvent
import net.minecraftforge.event.entity.living.LivingDeathEvent
import net.minecraftforge.event.entity.player.AchievementEvent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.PlayerEvent
import net.minecraftforge.fml.common.gameevent.TickEvent
//FORGE-DEPENDENT
object EventHandler {
//MC-VERSION & FORGE DEPENDENT
@SubscribeEvent
fun progressEvent(e: AchievementEvent) = runBlocking {
val achievement = e.achievement
val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return@runBlocking
val statFile = entityPlayer.statFile
if (!statFile.canUnlockAchievement(achievement) || statFile.hasAchievementUnlocked(achievement)) {
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
)
}
//FORGE-DEPENDENT
@SubscribeEvent
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
)
}
//FORGE-DEPENDENT
@SubscribeEvent
fun commandEvent(e: CommandEvent) = runBlocking {
val sender = when {
e.sender is DedicatedServer -> cfg.outgoing.systemUser
else -> e.sender.displayName.unformattedText
}
val args = e.parameters.joinToString(" ")
val type = with(e.command) {
when {
this is CommandEmote || commandName.equals("me", true) -> ChatEvent.ACTION
this is CommandBroadcast || commandName.equals("say", true) -> ChatEvent.BROADCAST
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
}
)
}
//FORGE-DEPENDENT
@SubscribeEvent
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
)
}
}
//FORGE-DEPENDENT
@SubscribeEvent
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
)
}
//FORGE-DEPENDENT
@SubscribeEvent
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
)
}
//FORGE-DEPENDENT
@SubscribeEvent
fun serverTickEvent(e: TickEvent.ServerTickEvent) = runBlocking {
if (e.phase == TickEvent.Phase.END)
TickHandler.handleTick()
}
}

@ -1 +1 @@
Subproject commit 7d27c28784bacba17450faa9e723ca6b6eb39602 Subproject commit 93ab86ee821380584c22ac60d77737388976e531

18
Jenkinsfile vendored
View File

@ -14,12 +14,20 @@ pipeline {
archiveArtifacts artifacts: '1.7.10/build/libs/*jar' archiveArtifacts artifacts: '1.7.10/build/libs/*jar'
} }
} }
stage("1.9.4") { stage("1.10.2") {
steps { steps {
sh './gradlew :1.9.4:setupCiWorkspace' sh './gradlew :1.10.2:setupCiWorkspace'
sh './gradlew :1.9.4:clean' sh './gradlew :1.10.2:clean'
sh './gradlew :1.9.4:build' sh './gradlew :1.10.2:build'
archiveArtifacts artifacts: '1.9.4/build/libs/*jar' archiveArtifacts artifacts: '1.10.2/build/libs/*jar'
}
}
stage("1.11.2") {
steps {
sh './gradlew :1.11.2:setupCiWorkspace'
sh './gradlew :1.11.2:clean'
sh './gradlew :1.11.2:build'
archiveArtifacts artifacts: '1.11.2/build/libs/*jar'
} }
} }
stage("1.12.2") { stage("1.12.2") {

View File

@ -28,7 +28,11 @@ Chat with us on IRC: [#matterlink @ irc.esper.net](irc://irc.esper.net/matterlin
[![Download 1.12.2](https://curse.nikky.moe/api/img/287323?logo&style=for-the-badge&version=1.12.2)](https://curse.nikky.moe/api/url/287323?version=1.12.2) [![Download 1.12.2](https://curse.nikky.moe/api/img/287323?logo&style=for-the-badge&version=1.12.2)](https://curse.nikky.moe/api/url/287323?version=1.12.2)
[![Download 1.9.4](https://curse.nikky.moe/api/img/287323?logo&style=for-the-badge&version=1.9.4)](https://curse.nikky.moe/api/url/287323?version=1.9.4) [![Download 1.11.2](https://curse.nikky.moe/api/img/287323?logo&style=for-the-badge&version=1.11.2)](https://curse.nikky.moe/api/url/287323?version=1.11.2)
[![Download 1.10.2](https://curse.nikky.moe/api/img/287323?logo&style=for-the-badge&version=1.10.2)](https://curse.nikky.moe/api/url/287323?version=1.10.2)
[![Download 1.7.10](https://curse.nikky.moe/api/img/287323?logo&style=for-the-badge&version=1.7.10)](https://curse.nikky.moe/api/url/287323?version=1.7.10)
## Dependencies ## Dependencies
@ -163,13 +167,6 @@ matterbridge
now start the server with matterlink (and forgelin) in the mods folder now start the server with matterlink (and forgelin) in the mods folder
and then [configure](https://github.com/42wim/matterbridge#configuration) all your needed gateways, endpoints etc and then [RTFM!!!](https://github.com/42wim/matterbridge#configuration) and configure all your needed gateways, endpoints etc
### Building powered by wishful thinking
```bash
git clone --recursive https://git.lain.faith/sorceress/MatterLink.git
cd MatterLink/
./gradlew setupDecompWorkspace
./gradlew build
```

14
TODO.MD
View File

@ -1,14 +0,0 @@
# 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`

1
api Submodule

@ -0,0 +1 @@
Subproject commit 75138cec00c66d479709f4b44d78ca3005993474

View File

@ -3,8 +3,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath group: "org.jetbrains.kotlin", name: "kotlin-gradle-plugin", version: kotlinVersion classpath group: "org.jetbrains.kotlin", name: "kotlin-gradle-plugin", version: kotlin_version
classpath group: "org.jetbrains.kotlin", name: "kotlin-serialization", version: kotlinVersion
} }
} }
@ -15,14 +14,13 @@ plugins {
subprojects { subprojects {
apply plugin: "kotlin" apply plugin: "kotlin"
apply plugin: "kotlinx-serialization"
apply plugin: "idea" apply plugin: "idea"
if (System.env.BUILD_NUMBER) { if (System.env.BUILD_NUMBER) {
modVersion += "-${System.env.BUILD_NUMBER}" mod_version += "-${System.env.BUILD_NUMBER}"
} else if (!project.hasProperty('release')) { } else if (!project.hasProperty('release')) {
// modVersion += "-dev" // mod_version += "-dev"
modVersion += "-dev" mod_version += "-dev"
} }
idea { idea {
@ -37,28 +35,19 @@ subprojects {
} }
repositories { repositories {
jcenter() jcenter()
mavenCentral()
maven { maven {
name = "bintray" name = "unascribed"
url = "http://jcenter.bintray.com" url = 'http://unascribed.com/maven/releases'
} }
maven {
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 { maven {
name = "shadowfacts" name = "shadowfacts"
url = "http://maven.shadowfacts.net/" url = "http://maven.shadowfacts.net/"
} }
ivy {
//Resolves baubles and jankson
name = "endless.blue dependency mirror"
artifactPattern "https://endless.blue/files/ivy/[module]-[revision].[ext]"
}
} }
compileKotlin { compileKotlin {
kotlinOptions { kotlinOptions {

View File

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

@ -4,40 +4,30 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath group: "org.jetbrains.kotlin", name: "kotlin-gradle-plugin", version: kotlinVersion // classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath group: "com.github.jengelman.gradle.plugins", name: "shadow", version: shadowVersion classpath group: "com.github.jengelman.gradle.plugins", name: "shadow", version: shadow_version
classpath group: 'com.vanniktech', name: 'gradle-dependency-graph-generator-plugin', version: '0.5.0'
} }
} }
apply plugin: 'kotlin' apply plugin: 'kotlin'
apply plugin: "com.github.johnrengelman.shadow" apply plugin: "com.github.johnrengelman.shadow"
apply plugin: "com.vanniktech.dependency.graph.generator"
sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
dependencies { dependencies {
compile project(":Jankson") compile project(':api')
shadow(project(':Jankson')) { transitive = false }
compile project(":Jankson")
// compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '+'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '+'
// compile group: 'commons-logging', name: 'commons-logging', version: '1.1.3'
compile group: 'com.google.code.gson', name: 'gson', version: '+'
compile group: 'com.google.guava', name: 'guava', version: '+' compile group: 'com.google.guava', name: 'guava', version: '+'
compile(group: "com.github.kittinunf.Fuel", name: "fuel", version: fuelVersion) compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '+'
shadow(group: "com.github.kittinunf.Fuel", name: "fuel", version: fuelVersion) { transitive = true }
compile(group: "com.github.kittinunf.Fuel", name: "fuel-coroutines", version: fuelVersion)
shadow(group: "com.github.kittinunf.Fuel", name: "fuel-coroutines", version: fuelVersion) { transitive = true }
compile(group: "com.github.kittinunf.Fuel", name: "fuel-kotlinx-serialization", version: fuelVersion)
shadow(group: "com.github.kittinunf.Fuel", name: "fuel-kotlinx-serialization", version: fuelVersion) { transitive = true }
// 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: coroutinesVersion) compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: project.kotlin_version
// shadow(group: "org.jetbrains.kotlinx", name: "kotlinx-coroutines-core", version: coroutinesVersion) { transitive = false }
compile(group: "org.jetbrains.kotlinx", name: "kotlinx-serialization-runtime", version: serializationVersion)
shadow(group: "org.jetbrains.kotlinx", name: "kotlinx-serialization-runtime", version: serializationVersion) { transitive = false }
compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion
} }
compileKotlin { compileKotlin {
@ -47,12 +37,10 @@ compileKotlin {
} }
shadowJar { shadowJar {
classifier = '' classifier ''
relocate 'blue.endless', 'matterlink.repack.blue.endless' dependencies {
relocate 'com.github', 'matterlink.repack.com.github' include project(":api")
relocate 'kotlinx', 'matterlink.repack.kotlinx' include project(":Jankson")
configurations = [project.configurations.shadow] }
} }
//tasks.build.dependsOn shadowJar

View File

@ -1,185 +0,0 @@
package matterlink
import blue.endless.jankson.JsonObject
import blue.endless.jankson.impl.SyntaxError
import kotlin.math.sqrt
fun JsonObject.parseDimensions(): List<Int> = getOrPutList("dimensions", emptyList(), "list of dimension ids")
fun JsonObject.parseAllDimensions(): Boolean = getOrDefault("allDimensions", false, "ignores dimension list")
sealed class Area {
abstract val type: String
abstract val dimensions: List<Int>
abstract val allDimensions: Boolean
abstract fun testInBounds(x: Int, y: Int, z: Int): Boolean
fun testForDim(dimension: Int?): Boolean {
if (allDimensions) return true
if (dimension == null) return false
return dimensions.contains(dimension)
}
companion object {
fun parse(jsonObj: JsonObject): Area {
val type: String = jsonObj.getOrDefault("type", "INFINITE", "Area type identifier")
return when (type.toUpperCase()) {
"INFINITE" -> Infinite.parse(jsonObj)
"RADIUS" -> Radius.parse(jsonObj)
"SPHERE" -> Sphere.parse(jsonObj)
"BOX" -> Box.parse(jsonObj)
"SQUARE" -> Square.parse(jsonObj)
else -> throw SyntaxError("no Area type '$type' found")
}
}
}
data class Infinite(
override val dimensions: List<Int> = listOf(),
override val allDimensions: Boolean = false
) : Area() {
override val type = "INFINITE"
override fun testInBounds(x: Int, y: Int, z: Int): Boolean {
return true
}
companion object {
fun parse(jsonObj: JsonObject): Area {
return Infinite(
dimensions = jsonObj.parseDimensions(),
allDimensions = jsonObj.parseAllDimensions()
)
}
}
}
data class Radius(
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"
override fun testInBounds(x: Int, y: Int, z: Int): Boolean {
if (radius == null) return true
return sqrt(((this.x - x) * (this.x - x)) + ((this.z - z) * (this.z - z)).toFloat()) < this.radius
}
companion object {
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")
)
}
}
}
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
}
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")
)
}
}
}
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 {
return x in x1..x2 && y in y1..y2 && z in z1..z2
}
companion object {
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)
)
}
}
}
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)
)
}
}
}
//
//
// class FakePlayer (
// val x: Int,
// val y: Int,
// val z: Int,
// val name: String
// ): Area()
}

View File

@ -0,0 +1,42 @@
package matterlink
import matterlink.HttpClientUtil.client
import org.apache.http.HttpResponse
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpRequestBase
import org.apache.http.impl.client.HttpClientBuilder
/**
* Created by nikky on 15/07/18.
* @author Nikky
*/
object HttpClientUtil {
val client = HttpClientBuilder.create().build()
}
fun String.httpGet(): HttpGet =
HttpGet(this)
fun HttpGet.header(pair: Pair<String, String>): HttpGet = this.apply {
addHeader(pair.first, pair.second)
}
fun HttpGet.responseString(): Triple<HttpRequestBase, HttpResponse, Result> {
val response = client.execute(this)
val result = response.entity.content.bufferedReader().use { it.readText() }
return Triple(this, response, Result.Success(result))
}
sealed class Result {
class Success(
val value: String
) : Result()
class Failure(
val error: Throwable
) : Result()
}

View File

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

View File

@ -1,9 +0,0 @@
package matterlink
interface Logger {
fun info(message: String)
fun debug(message: String)
fun error(message: String)
fun warn(message: String)
fun trace(message: String)
}

View File

@ -1,6 +1,8 @@
package matterlink package matterlink
import blue.endless.jankson.Jankson import blue.endless.jankson.Jankson
import blue.endless.jankson.JsonObject
import blue.endless.jankson.impl.Marshaller
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL

View File

@ -8,7 +8,9 @@ import blue.endless.jankson.impl.Marshaller
import matterlink.config.cfg import matterlink.config.cfg
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.lang.Thread.yield
import java.util.* import java.util.*
import kotlin.streams.asSequence
private const val ZWSP: Char = '\u200b' private const val ZWSP: Char = '\u200b'
@ -65,41 +67,24 @@ fun randomString(length: Int = 6): String =
java.util.UUID.randomUUID().toString().replace("-", "").take(length) java.util.UUID.randomUUID().toString().replace("-", "").take(length)
fun <T : Any> JsonObject.getOrDefault(key: String, default: T, comment: String? = null): T { fun <T : Any> JsonObject.getOrDefault(key: String, default: T, comment: String? = null): T {
logger.trace("type: ${default.javaClass.name} key: $key json: >>>${this.getObject(key)?.toJson()}<<< default: $default") // logger.info("type: ${default.javaClass.name} key: $key json: >>>${this.getObject(key)?.toJson()}<<< default: $default")
return putDefault(key, default, comment)!!.also { return putDefault(key, default, comment)!!
setComment(key, comment)
}
} }
inline fun <reified T : Any> Jankson.fromJson(obj: JsonObject): T = this.fromJson(obj, T::class.java) inline fun <reified T : Any> Jankson.fromJson(obj: JsonObject): T = this.fromJson(obj, T::class.java)
inline fun <reified T : Any> Jankson.fromJson(json: String): T = this.fromJson(json, T::class.java) inline fun <reified T : Any> Jankson.fromJson(json: String): T = this.fromJson(json, T::class.java)
inline fun <reified T : Any> Jankson.Builder.registerTypeAdapter(noinline adapter: (JsonObject) -> T) = inline fun <reified T : Any> Jankson.Builder.registerTypeAdapter(noinline adapter: (JsonObject) -> T) = this.registerTypeAdapter(T::class.java, adapter)
this.registerTypeAdapter(T::class.java, adapter)
inline fun <reified T : Any> Jankson.Builder.registerPrimitiveTypeAdapter(noinline adapter: (Any) -> T) = inline fun <reified T : Any> Jankson.Builder.registerPrimitiveTypeAdapter(noinline adapter: (Any) -> T) = this.registerPrimitiveTypeAdapter(T::class.java, adapter)
this.registerPrimitiveTypeAdapter(T::class.java, adapter)
inline fun <reified T : Any> Jankson.Builder.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = inline fun <reified T : Any> Jankson.Builder.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = this.registerSerializer(T::class.java, serializer)
this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T) -> JsonElement) = inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T) -> JsonElement) = this.registerSerializer(T::class.java, serializer)
this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = this.registerSerializer(T::class.java, serializer)
this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> JsonObject.getReified(key: String, comment: String? = null): T? = inline fun <reified T : Any> JsonObject.getReified(key: String): T? = this.get(T::class.java, key)
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)
?.also { setComment(key, comment) }
?: run {
this.remove(key)
null
}
inline fun <reified T : Any> JsonObject.getList(key: String): List<T>? { inline fun <reified T : Any> JsonObject.getList(key: String): List<T>? {
return this[key]?.let { array -> return this[key]?.let { array ->
@ -113,37 +98,3 @@ inline fun <reified T : Any> JsonObject.getList(key: String): List<T>? {
} }
} }
} }
inline fun <reified T : Any> JsonObject.getOrPutList(key: String, default: List<T>, comment: String?): List<T> {
return this[key]?.let { array ->
when (array) {
is JsonArray -> {
array.indices.map { i ->
array.get(T::class.java, i) ?: throw NullPointerException("cannot parse ${array.get(i)}")
}
}
else -> null
}
}.also {
setComment(key, comment)
} ?: this.putDefault(key, default, comment) ?: default
}
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 -> {
map.mapValues { (key, element) ->
map.get(T::class.java, key) ?: throw NullPointerException("cannot parse $element")
}
}
else -> null
}
}.also {
setComment(key, comment)
} ?: this.putDefault(key, default, comment) ?: default
}

View File

@ -1,88 +0,0 @@
package matterlink.api
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.
*
* @author Nikky
* @version 1.0
*/
@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
) {
fun encode(): String {
return JSON.nonstrict.stringify(ApiMessage.serializer(), this)
}
override fun toString(): String = encode()
@Serializer(forClass = ApiMessage::class)
companion object {
val USER_ACTION = "user_action"
val JOIN_LEAVE = "join_leave"
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)
}
fun decode(input: String): ApiMessage {
return JSON.nonstrict.parse(ApiMessage.serializer(), input)
}
}
}

View File

@ -1,10 +0,0 @@
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"
)

View File

@ -1,240 +0,0 @@
package matterlink.api
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.core.extensions.cUrlString
import com.github.kittinunf.fuel.core.extensions.jsonBody
import com.github.kittinunf.fuel.coroutines.awaitStringResponseResult
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.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.ObsoleteCoroutinesApi
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.Reader
import java.net.ConnectException
import kotlin.coroutines.CoroutineContext
/**
* Created by nikky on 07/05/18.
*
* @author Nikky
* @version 1.0
*/
open class MessageHandler : CoroutineScope {
override val coroutineContext: CoroutineContext = Job()
private var enabled = false
private var connectErrors = 0
private var reconnectCooldown = 0L
private var sendErrors = 0
private var sendChannel: SendChannel<ApiMessage> = senderActor()
private val messageStream = Channel<ApiMessage>(Channel.UNLIMITED)
@UseExperimental(ExperimentalCoroutinesApi::class)
var broadcast: BroadcastChannel<ApiMessage> = broadcast {
while (true) {
val msg = messageStream.receive()
send(msg)
}
}
private set
private val keepOpenManager = FuelManager().apply {
timeoutInMillisecond = 0
timeoutReadInMillisecond = 0
}
var config: Config = Config()
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")
}
suspend fun stop(message: String? = null) {
if (message != null && config.announceDisconnect) {
sendStatusUpdate(message)
}
enabled = false
rcvJob?.cancel()
rcvJob = null
}
private var rcvJob: Job? = null
suspend fun start(message: String?, clear: Boolean) {
logger.debug("starting connection")
if (clear) {
clear()
}
enabled = true
rcvJob = messageBroadcast()
if (message != null && config.announceConnect) {
sendStatusUpdate(message)
}
}
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}"
}
}
.awaitStringResponseResult()
when (result) {
is Result.Success -> {
val messages: List<ApiMessage> = JSON.nonstrict.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 suspend fun sendStatusUpdate(message: String) {
transmit(ApiMessage(text = message))
}
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)
// }
}
@Deprecated("use coroutine api", level = DeprecationLevel.ERROR)
fun checkConnection() {
}
@UseExperimental(ObsoleteCoroutinesApi::class)
private fun CoroutineScope.senderActor() = actor<ApiMessage>(context = Dispatchers.IO) {
consumeEach {
try {
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.debug("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
}
}
} catch (connectError: ConnectException) {
connectError.printStackTrace()
sendErrors++
}
}
}
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.debug("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

@ -1,44 +1,25 @@
package matterlink.bridge package matterlink.bridge
import matterlink.Paste import matterlink.*
import matterlink.PasteSection
import matterlink.PasteUtil
import matterlink.antiping
import matterlink.api.ApiMessage import matterlink.api.ApiMessage
import matterlink.api.MessageHandler import matterlink.api.MessageHandler
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.handlers.ChatEvent
import matterlink.handlers.LocationHandler
import matterlink.mapFormat
import matterlink.stackTraceString
object MessageHandlerInst : MessageHandler() { object MessageHandlerInst : MessageHandler() {
override suspend fun transmit(msg: ApiMessage) { override fun transmit(msg: ApiMessage) {
transmit(msg, cause = "") transmit(msg, cause = "")
} }
override suspend fun sendStatusUpdate(message: String) { fun transmit(msg: ApiMessage, cause: String, maxLines: Int = cfg.outgoing.inlineLimit) {
LocationHandler.sendToLocations(
msg = message,
x = 0, y = 0, z = 0, dimension = null,
systemuser = true,
event = ChatEvent.STATUS,
cause = "status update message"
)
}
suspend fun transmit(msg: ApiMessage, cause: String, maxLines: Int = cfg.outgoing.inlineLimit) {
if (msg.username.isEmpty()) { if (msg.username.isEmpty()) {
msg.username = cfg.outgoing.systemUser msg.username = cfg.outgoing.systemUser
if (msg.avatar.isEmpty() && cfg.outgoing.avatar.enable) { if(msg.avatar.isEmpty() && cfg.outgoing.avatar.enable) {
msg.avatar = cfg.outgoing.avatar.systemUserAvatar msg.avatar = cfg.outgoing.avatar.systemUserAvatar
} }
} }
if (msg.gateway.isEmpty()) { if (msg.gateway.isEmpty())
logger.error("dropped message '$msg' due to missing gateway") msg.gateway = cfg.connect.gateway
return
}
if (msg.text.lines().count() >= maxLines) { if (msg.text.lines().count() >= maxLines) {
try { try {
@ -56,8 +37,7 @@ object MessageHandlerInst : MessageHandler() {
) )
msg.text = msg.text.substringBefore('\n') msg.text = msg.text.substringBefore('\n')
.take(25) + "... " + response.link .take(25) + "... " + response.link
} catch (e: Exception) { } catch(e: Exception) {
logger.error(cause)
logger.error(e.stackTraceString) logger.error(e.stackTraceString)
} }
} }

View File

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

View File

@ -1,13 +1,15 @@
package matterlink.bridge.command package matterlink.bridge.command
import matterlink.api.ApiMessage import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.CommandConfig import matterlink.config.CommandConfig
import matterlink.config.IdentitiesConfig import matterlink.config.IdentitiesConfig
import matterlink.config.PermissionConfig import matterlink.config.PermissionConfig
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.instance
import matterlink.logger import matterlink.logger
import java.util.HashMap import matterlink.stripColorOut
import java.util.UUID import java.util.*
object BridgeCommandRegistry { object BridgeCommandRegistry {
@ -17,7 +19,7 @@ object BridgeCommandRegistry {
* *
* @return consume message flag * @return consume message flag
*/ */
suspend fun handleCommand(input: ApiMessage): Boolean { fun handleCommand(input: ApiMessage): Boolean {
if (!cfg.command.enable || input.text.isBlank()) return false if (!cfg.command.enable || input.text.isBlank()) return false
if (input.text[0] != cfg.command.prefix || input.text.length < 2) return false if (input.text[0] != cfg.command.prefix || input.text.length < 2) return false
@ -27,13 +29,7 @@ object BridgeCommandRegistry {
val uuid = IdentitiesConfig.getUUID(input.account, input.userid) val uuid = IdentitiesConfig.getUUID(input.account, input.userid)
val env = IBridgeCommand.CommandEnvironment.BridgeEnv( val env = IBridgeCommand.CommandEnvironment.BridgeEnv(input.username, input.userid, input.account, uuid)
input.username,
input.userid,
input.account,
input.gateway,
uuid
)
return commandMap[cmd[0]]?.let { return commandMap[cmd[0]]?.let {
if (!it.reachedTimeout()) { if (!it.reachedTimeout()) {
logger.debug("dropped command ${it.alias}") logger.debug("dropped command ${it.alias}")
@ -50,7 +46,7 @@ object BridgeCommandRegistry {
} ?: false } ?: false
} }
suspend fun handleCommand(text: String, username: String, uuid: UUID): Boolean { fun handleCommand(text: String, username: String, uuid: UUID): Boolean {
if (!cfg.command.enable || text.isBlank()) return false if (!cfg.command.enable || text.isBlank()) return false
if (text[0] != cfg.command.prefix || text.length < 2) return false if (text[0] != cfg.command.prefix || text.length < 2) return false

View File

@ -17,7 +17,7 @@ data class CustomCommand(
val argumentsRegex: Regex? = null val argumentsRegex: Regex? = null
) : IBridgeCommand() { ) : IBridgeCommand() {
override suspend fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean { override fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean {
if (argumentsRegex != null) { if (argumentsRegex != null) {
logger.debug("testing '$args' against '${argumentsRegex.pattern}'") logger.debug("testing '$args' against '${argumentsRegex.pattern}'")
if (!argumentsRegex.matches(args)) { if (!argumentsRegex.matches(args)) {
@ -35,10 +35,8 @@ data class CustomCommand(
commandSender.execute(cmd) || commandSender.reply.isNotEmpty() commandSender.execute(cmd) || commandSender.reply.isNotEmpty()
} }
CommandType.RESPONSE -> { CommandType.RESPONSE -> {
env.respond( env.respond(response?.lazyFormat(getReplacements(user, env, args))
response?.lazyFormat(getReplacements(user, env, args)) ?: "", cause = "response to command: $alias")
?: "", cause = "response to command: $alias"
)
true true
} }

View File

@ -1,13 +1,16 @@
package matterlink.bridge.command package matterlink.bridge.command
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.stripColorOut
object HelpCommand : IBridgeCommand() { object HelpCommand : IBridgeCommand() {
override val help: String = "Returns the help string for the given command. Syntax: help <command>" override val help: String = "Returns the help string for the given command. Syntax: help <command>"
override val permLevel: Double override val permLevel: Double
get() = cfg.command.defaultPermUnauthenticated get() = cfg.command.defaultPermUnauthenticated
override suspend fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean { override fun execute(alias: String, user: String, env: CommandEnvironment, args: String): Boolean {
val msg: String = when { val msg: String = when {
args.isEmpty() -> args.isEmpty() ->
"Available commands: ${BridgeCommandRegistry.getCommandList(IBridgeCommand.getPermLevel(env.uuid))}" "Available commands: ${BridgeCommandRegistry.getCommandList(IBridgeCommand.getPermLevel(env.uuid))}"

View File

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

View File

@ -1,5 +1,7 @@
package matterlink.bridge.command package matterlink.bridge.command
import matterlink.stripColorOut
abstract class IMinecraftCommandSender(val user: String, val env: IBridgeCommand.CommandEnvironment, val op: Boolean) { abstract class IMinecraftCommandSender(val user: String, val env: IBridgeCommand.CommandEnvironment, val op: Boolean) {
/** /**
* @param cmdString The command to execute with its arguments * @param cmdString The command to execute with its arguments
@ -35,7 +37,7 @@ abstract class IMinecraftCommandSender(val user: String, val env: IBridgeCommand
reply += text reply += text
} }
suspend fun sendReply(cmdString: String) { fun sendReply(cmdString: String) {
env.respond( env.respond(
text = reply.joinToString("\n"), text = reply.joinToString("\n"),
cause = "executed command: $cmdString" cause = "executed command: $cmdString"

View File

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

View File

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

View File

@ -13,7 +13,7 @@ object CommandCoreML {
val usage = "ml <connect|disconnect|reload|permAccept>" val usage = "ml <connect|disconnect|reload|permAccept>"
suspend fun execute(args: Array<String>, user: String, uuid: String?): String { fun execute(args: Array<String>, user: String, uuid: String?): String {
val cmd = args[0].toLowerCase() val cmd = args[0].toLowerCase()
return when (cmd) { return when (cmd) {
@ -45,10 +45,10 @@ object CommandCoreML {
return "no code passed" return "no code passed"
} }
if (request.nonce != nonce) { if (request.nonce != nonce) {
return "nonce in request does not match with $nonce" return "nonce in request does not match"
} }
val powerLevelArg = args.getOrNull(3)?.toDoubleOrNull() val powerLevelArg = args.getOrNull(2)?.toDoubleOrNull()
val powerLevel = powerLevelArg ?: run { return "permLevel cannot be parsed: ${args.getOrNull(3)}" } val powerLevel = powerLevelArg ?: run { return "permLevel cannot be parsed" }
?: request.powerlevel ?: request.powerlevel
?: return "no permLevel provided" ?: return "no permLevel provided"
PermissionConfig.add(request.uuid, powerLevel, "${request.user} Authorized by $user") PermissionConfig.add(request.uuid, powerLevel, "${request.user} Authorized by $user")

View File

@ -4,13 +4,9 @@ import blue.endless.jankson.Jankson
import blue.endless.jankson.JsonObject import blue.endless.jankson.JsonObject
import blue.endless.jankson.impl.Marshaller import blue.endless.jankson.impl.Marshaller
import blue.endless.jankson.impl.SyntaxError import blue.endless.jankson.impl.SyntaxError
import matterlink.Area
import matterlink.bridge.MessageHandlerInst import matterlink.bridge.MessageHandlerInst
import matterlink.getOrDefault import matterlink.getOrDefault
import matterlink.getOrPutList
import matterlink.getReifiedOrDelete
import matterlink.logger import matterlink.logger
import matterlink.registerSerializer
import matterlink.registerTypeAdapter import matterlink.registerTypeAdapter
import matterlink.stackTraceString import matterlink.stackTraceString
import java.io.File import java.io.File
@ -24,88 +20,18 @@ data class BaseConfig(val rootDir: File) {
val configFile: File = cfgDirectory.resolve("matterlink.hjson") val configFile: File = cfgDirectory.resolve("matterlink.hjson")
init { init {
logger.info("Reading bridge blueprints... from $rootDir") logger.info("Reading bridge blueprints... from {}", rootDir)
baseCfg = this baseCfg = this
} }
data class MatterLinkConfig( data class MatterLinkConfig(
val connect: ConnectOptions = ConnectOptions(), val connect: ConnectOptions = ConnectOptions(),
val outgoingDefaults: DefaultSettingsOutgoing = DefaultSettingsOutgoing(),
val incomingDefaults: DefaultSettingsIncoming = DefaultSettingsIncoming(),
val locations: List<Location> = listOf(
Location(
label = "default",
gateway = "minecraft",
area = Area.Infinite(dimensions = listOf(-1, 0, 1), allDimensions = true),
outgoing = SettingsOutgoing(
plain = true,
action = true,
join = true,
leave = true,
advancement = true,
death = true,
broadcast = true,
status = true
),
incoming = SettingsIncoming(
plain = true,
action = true,
join_leave = true,
commands = true
)
)
),
val incoming: IncomingOptions = IncomingOptions(), val incoming: IncomingOptions = IncomingOptions(),
val outgoing: OutgoingOptions = OutgoingOptions(), val outgoing: OutgoingOptions = OutgoingOptions(),
val command: CommandOptions = CommandOptions(), val command: CommandOptions = CommandOptions(),
val update: UpdateOptions = UpdateOptions() val update: UpdateOptions = UpdateOptions()
) )
data class DefaultSettingsOutgoing(
val plain: Boolean = true,
val action: Boolean = true,
val join: Boolean = false,
val leave: Boolean = false,
val advancement: Boolean = false,
val death: Boolean = false,
val broadcast: Boolean = false,
val status: Boolean = false
)
data class SettingsOutgoing(
val plain: Boolean? = null,
val action: Boolean? = null,
val join: Boolean? = null,
val leave: Boolean? = null,
val advancement: Boolean? = null,
val death: Boolean? = null,
val broadcast: Boolean? = null,
val status: Boolean? = null,
val skip: List<String> = listOf()
)
data class DefaultSettingsIncoming(
val plain: Boolean = true,
val action: Boolean = true,
val join_leave: Boolean = true,
val commands: Boolean = true
)
data class SettingsIncoming(
val plain: Boolean? = null,
val action: Boolean? = null,
val join_leave: Boolean? = null,
val commands: Boolean? = null,
val skip: List<String> = listOf()
)
data class Location(
val label: String = "unlabeled",
val gateway: String = "",
val area: Area = Area.Infinite(),
val outgoing: SettingsOutgoing = SettingsOutgoing(),
val incoming: SettingsIncoming = SettingsIncoming()
)
data class CommandOptions( data class CommandOptions(
val prefix: Char = '!', val prefix: Char = '!',
@ -119,6 +45,7 @@ data class BaseConfig(val rootDir: File) {
data class ConnectOptions( data class ConnectOptions(
val url: String = "http://localhost:4242", val url: String = "http://localhost:4242",
val authToken: String = "", val authToken: String = "",
val gateway: String = "minecraft",
val autoConnect: Boolean = true, val autoConnect: Boolean = true,
val reconnectWait: Long = 500 val reconnectWait: Long = 500
) )
@ -177,30 +104,21 @@ data class BaseConfig(val rootDir: File) {
"thrown" to arrayOf("彡°"), "thrown" to arrayOf("彡°"),
"thorns" to arrayOf("\uD83C\uDF39"), //🌹 "thorns" to arrayOf("\uD83C\uDF39"), //🌹
"explosion" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5"), //💣 💥 "explosion" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5"), //💣 💥
"explosion.player" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5"), //💣 💥 "explosion.player" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5") //💣 💥
"ieWireShock" to arrayOf("\uD83D\uDD0C", "\u26A1"), //🔌 ⚡
"immersiverailroading:hitByTrain" to arrayOf(
"\uD83D\uDE82",
"\uD83D\uDE83",
"\uD83D\uDE84",
"\uD83D\uDE85",
"\uD83D\uDE87",
"\uD83D\uDE88",
"\uD83D\uDE8A"
) //🚂 🚃 🚄 🚅 🚇 🚈 🚊
) )
) )
data class AvatarOptions( data class AvatarOptions(
val enable: Boolean = true, val enable: Boolean = true,
val urlTemplate: String = "https://mc-heads.net/head/{uuid}", val urlTemplate: String = "https://visage.surgeplay.com/head/512/{uuid}",
val systemUserAvatar: String = "" // https://www.freepik.com/free-icon/right-arrow-angle-and-horizontal-down-line-code-signs_732795.htm
val systemUserAvatar: String = "https://image.freepik.com/free-icon/right-arrow-angle-and-horizontal-down-line-code-signs_318-53994.jpg"
) )
data class JoinPartOptions( data class JoinPartOptions(
val enable: Boolean = true, val enable: Boolean = true,
val joinServer: String = "{username:antiping} has connected to the server", val joinServer: String = "{username:antiping} has connected to the platform",
val partServer: String = "{username:antiping} has disconnected from the server" val partServer: String = "{username:antiping} has disconnected from the platform"
) )
data class UpdateOptions( data class UpdateOptions(
@ -211,36 +129,20 @@ data class BaseConfig(val rootDir: File) {
val jankson = Jankson val jankson = Jankson
.builder() .builder()
.registerTypeAdapter { .registerTypeAdapter {
with(MatterLinkConfig()) {
MatterLinkConfig( MatterLinkConfig(
command = it.getOrDefault( command = it.getOrDefault(
"command", "command",
command, CommandOptions(),
"User commands" "User commands"
), ),
outgoingDefaults = it.getOrDefault(
"outgoingDefaults",
outgoingDefaults,
"default settings for outgoing"
),
incomingDefaults = it.getOrDefault(
"incomingDefaults",
incomingDefaults,
"default settings for incoming"
),
locations = it.getOrPutList(
"locations",
locations,
"list of fixed chat locations"
),
connect = it.getOrDefault( connect = it.getOrDefault(
"connect", "connect",
connect, ConnectOptions(),
"Connection Settings" "Connection Settings"
), ),
incoming = it.getOrDefault( incoming = it.getOrDefault(
"incoming", "incoming",
incoming, IncomingOptions(),
""" """
Gateway -> Server Gateway -> Server
Options all about receiving messages from the API Options all about receiving messages from the API
@ -250,7 +152,7 @@ data class BaseConfig(val rootDir: File) {
), ),
outgoing = it.getOrDefault( outgoing = it.getOrDefault(
"outgoing", "outgoing",
outgoing, OutgoingOptions(),
""" """
Server -> Gateway Server -> Gateway
Options all about sending messages to the API Options all about sending messages to the API
@ -258,146 +160,11 @@ data class BaseConfig(val rootDir: File) {
), ),
update = it.getOrDefault( update = it.getOrDefault(
"update", "update",
update, UpdateOptions(),
"Update Settings" "Update Settings"
) )
) )
} }
}
.registerTypeAdapter {
with(DefaultSettingsOutgoing()) {
DefaultSettingsOutgoing(
plain = it.getOrDefault(
"plain",
plain,
"plain text messages"
),
action = it.getOrDefault(
"action",
action,
"action messages"
),
join = it.getOrDefault(
"join",
join,
"handle join event"
),
leave = it.getOrDefault(
"leave",
leave,
"handle leave events"
),
advancement = it.getOrDefault(
"advancement",
advancement,
"handle advancement events"
),
death = it.getOrDefault(
"death",
death,
"handle death events"
),
broadcast = it.getOrDefault(
"broadcast",
broadcast,
"handle broadcast command"
),
status = it.getOrDefault(
"status",
status,
"handles tatus updates"
)
)
}
}
.registerTypeAdapter {
with(SettingsOutgoing()) {
SettingsOutgoing(
plain = it.getReifiedOrDelete("plain", "transmit join events"),
action = it.getReifiedOrDelete("action", "transmit join events"),
join = it.getReifiedOrDelete("join", "transmit join events"),
leave = it.getReifiedOrDelete("leave", "transmit leave events"),
advancement = it.getReifiedOrDelete("advancement", "transmit advancements"),
death = it.getReifiedOrDelete("death", "transmit death messages"),
broadcast = it.getReifiedOrDelete("say", "transmit broadcasts"),
status = it.getReifiedOrDelete("status", "transmit status updates"),
skip = it.getOrPutList(
"skip",
skip,
"list of other locations to ignore after handling this"
)
)
}
}
.registerTypeAdapter {
with(DefaultSettingsIncoming()) {
DefaultSettingsIncoming(
plain = it.getOrDefault(
"plain",
plain,
"plain text messages"
),
action = it.getOrDefault(
"action",
action,
"action messages"
),
join_leave = it.getOrDefault(
"join_leave",
join_leave,
"handle join/leave event"
),
commands = it.getOrDefault(
"commands",
join_leave,
"receive commands"
)
)
}
}
.registerTypeAdapter {
with(SettingsIncoming()) {
SettingsIncoming(
plain = it.getReifiedOrDelete("plain", "transmit join events"),
action = it.getReifiedOrDelete("action", "transmit join events"),
join_leave = it.getReifiedOrDelete("join_leave", "transmit join_leave events"),
commands = it.getReifiedOrDelete("commands", "receive commands"),
skip = it.getOrPutList(
"skip",
skip,
"list of other locations to ignore after handling this"
)
)
}
}
.registerTypeAdapter {
with(Location()) {
Location(
label = it.getOrDefault(
"label",
label,
"location label for identification"
),
gateway = it.getOrDefault(
"gateway",
gateway,
"matterbridge gateway identifier"
),
area = Area.parse(it.getObject("area") ?: JsonObject()),
outgoing = it.getOrDefault(
"outgoing",
outgoing,
"Location outgoing settings"
),
incoming = it.getOrDefault(
"incoming",
incoming,
"incoming settings"
)
)
}
}
.registerTypeAdapter { .registerTypeAdapter {
with(CommandOptions()) { with(CommandOptions()) {
CommandOptions( CommandOptions(
@ -447,15 +214,15 @@ data class BaseConfig(val rootDir: File) {
authToken, authToken,
"Auth token used to connect to the bridge platform" "Auth token used to connect to the bridge platform"
), ),
gateway = it.getOrDefault(
"gateway",
gateway,
"MatterBridge gateway"
),
autoConnect = it.getOrDefault( autoConnect = it.getOrDefault(
"autoConnect", "autoConnect",
autoConnect, autoConnect,
"Connect the relay on startup" "Connect the relay on startup"
),
reconnectWait = it.getOrDefault(
"reconnectWait",
reconnectWait,
"base delay in milliseconds between attempting reconnects"
) )
) )
} }
@ -562,7 +329,7 @@ data class BaseConfig(val rootDir: File) {
"damageTypMapping", "damageTypMapping",
"Damage type mapping for death cause" "Damage type mapping for death cause"
) )
it.mapValues { (key, _) -> it.mapValues { (key, element) ->
it.getOrDefault(key, damageTypeMapping[key] ?: emptyArray(), key) it.getOrDefault(key, damageTypeMapping[key] ?: emptyArray(), key)
.apply { it[key] }.apply { .apply { it[key] }.apply {
jsonObj["damageTypeMapping"] = it jsonObj["damageTypeMapping"] = it
@ -584,11 +351,6 @@ data class BaseConfig(val rootDir: File) {
"urlTemplate", "urlTemplate",
urlTemplate, urlTemplate,
"template for constructing the user avatar url using the uuid" "template for constructing the user avatar url using the uuid"
),
systemUserAvatar = it.getOrDefault(
"systemUserAvatar",
systemUserAvatar,
"avatar url for the system user"
) )
) )
} }
@ -627,38 +389,7 @@ data class BaseConfig(val rootDir: File) {
) )
} }
} }
.registerSerializer { locationSettings: SettingsOutgoing, marshaller: Marshaller -> .build()
val jsonObject = JsonObject()
locationSettings.plain?.let {
jsonObject["plain"] = marshaller.serialize(it)
}
locationSettings.action?.let {
jsonObject["action"] = marshaller.serialize(it)
}
locationSettings.join?.let {
jsonObject["join"] = marshaller.serialize(it)
}
locationSettings.leave?.let {
jsonObject["leave"] = marshaller.serialize(it)
}
locationSettings.advancement?.let {
jsonObject["advancement"] = marshaller.serialize(it)
}
locationSettings.death?.let {
jsonObject["death"] = marshaller.serialize(it)
}
locationSettings.broadcast?.let {
jsonObject["broadcast"] = marshaller.serialize(it)
}
locationSettings.status?.let {
jsonObject["status"] = marshaller.serialize(it)
}
locationSettings.skip.let {
jsonObject["skip"] = marshaller.serialize(it)
}
jsonObject
}
.build()!!
} }
fun load(): MatterLinkConfig { fun load(): MatterLinkConfig {
@ -686,15 +417,15 @@ data class BaseConfig(val rootDir: File) {
logger.error("error parsing config: ${e.completeMessage} ") logger.error("error parsing config: ${e.completeMessage} ")
logger.error(e.stackTraceString) logger.error(e.stackTraceString)
cfgDirectory.resolve("error.matterlink.hjson").writeText(jsonObject.toJson(false, true)) cfgDirectory.resolve("error.matterlink.hjson").writeText(jsonObject.toJson(false, true))
if (::cfg.isInitialized) cfg else MatterLinkConfig() MatterLinkConfig()
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
logger.error(e.stackTraceString) logger.error(e.stackTraceString)
cfgDirectory.resolve("error.matterlink.hjson").writeText(jsonObject.toJson(false, true)) cfgDirectory.resolve("error.matterlink.hjson").writeText(jsonObject.toJson(false, true))
if (::cfg.isInitialized) cfg else MatterLinkConfig() MatterLinkConfig()
} catch (e: NullPointerException) { } catch (e: NullPointerException) {
logger.error("error loading config: ${e.stackTraceString}") logger.error("error loading config: ${e.stackTraceString}")
cfgDirectory.resolve("error.matterlink.hjson").writeText(jsonObject.toJson(false, true)) cfgDirectory.resolve("error.matterlink.hjson").writeText(jsonObject.toJson(false, true))
if (::cfg.isInitialized) cfg else MatterLinkConfig() MatterLinkConfig()
} }
// val defaultJsonObject = jankson.load("{}") // val defaultJsonObject = jankson.load("{}")
@ -703,6 +434,7 @@ data class BaseConfig(val rootDir: File) {
MessageHandlerInst.config.url = tmpCfg.connect.url MessageHandlerInst.config.url = tmpCfg.connect.url
MessageHandlerInst.config.token = tmpCfg.connect.authToken MessageHandlerInst.config.token = tmpCfg.connect.authToken
MessageHandlerInst.config.gateway = tmpCfg.connect.gateway
MessageHandlerInst.config.reconnectWait = tmpCfg.connect.reconnectWait MessageHandlerInst.config.reconnectWait = tmpCfg.connect.reconnectWait
MessageHandlerInst.config.systemUser = tmpCfg.outgoing.systemUser MessageHandlerInst.config.systemUser = tmpCfg.outgoing.systemUser

View File

@ -4,12 +4,9 @@ import blue.endless.jankson.Jankson
import blue.endless.jankson.JsonObject import blue.endless.jankson.JsonObject
import blue.endless.jankson.JsonPrimitive import blue.endless.jankson.JsonPrimitive
import blue.endless.jankson.impl.SyntaxError import blue.endless.jankson.impl.SyntaxError
import matterlink.*
import matterlink.bridge.command.CommandType import matterlink.bridge.command.CommandType
import matterlink.bridge.command.CustomCommand import matterlink.bridge.command.CustomCommand
import matterlink.getOrDefault
import matterlink.logger
import matterlink.registerPrimitiveTypeAdapter
import matterlink.registerTypeAdapter
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException

View File

@ -11,7 +11,7 @@ import matterlink.logger
import matterlink.stackTraceString import matterlink.stackTraceString
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.util.UUID import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
typealias IdentMap = Map<String, Map<String, List<String>>> typealias IdentMap = Map<String, Map<String, List<String>>>

View File

@ -6,10 +6,11 @@ import blue.endless.jankson.impl.SyntaxError
import com.google.common.cache.Cache import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder import com.google.common.cache.CacheBuilder
import matterlink.getReified import matterlink.getReified
import matterlink.instance
import matterlink.logger import matterlink.logger
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.util.UUID import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
typealias PermissionMap = Map<String, Double> typealias PermissionMap = Map<String, Double>

View File

@ -1,38 +1,35 @@
package matterlink.handlers package matterlink.handlers
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.bridge.command.BridgeCommandRegistry import matterlink.bridge.command.BridgeCommandRegistry
import matterlink.config.cfg
import matterlink.instance
import matterlink.logger import matterlink.logger
import java.util.UUID import matterlink.stripColorOut
import java.util.*
object ChatProcessor { object ChatProcessor {
/** /**
* @return cancel message flag * @return cancel message flag
*/ */
suspend fun sendToBridge( fun sendToBridge(user: String, msg: String, event: String, uuid: UUID? = null): Boolean {
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() val message = msg.trim()
if (uuid != null && BridgeCommandRegistry.handleCommand(message, user, uuid)) return true if(uuid != null && BridgeCommandRegistry.handleCommand(message, user, uuid)) return true
when { when {
message.isNotBlank() -> LocationHandler.sendToLocations( message.isNotBlank() -> MessageHandlerInst.transmit(
user = user, ApiMessage(
msg = message, username = user.stripColorOut,
x = x, y = y, z = z, dimension = dimension, text = message.stripColorOut,
event = event, event = event
cause = "Message from $user", ).apply {
uuid = uuid if(cfg.outgoing.avatar.enable) {
if(uuid != null)
avatar = cfg.outgoing.avatar.urlTemplate.replace("{uuid}", uuid.toString())
}
},
cause = "Message from $user"
) )
else -> logger.warn("WARN: dropped blank message by '$user'") else -> logger.warn("WARN: dropped blank message by '$user'")
} }
return false return false

View File

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

View File

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

View File

@ -1,110 +0,0 @@
package matterlink.handlers
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg
import matterlink.logger
import matterlink.stripColorOut
import java.util.UUID
enum class ChatEvent {
PLAIN, ACTION, DEATH, JOIN, LEAVE, ADVANCEMENT, BROADCAST, STATUS
}
object LocationHandler {
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.debug("locations: ${cfg.locations.map { it.label }}")
for (location in cfg.locations) {
val label = location.label
if (skips.contains(label)) {
logger.debug("skipping $label (contained in in $skips)")
continue
}
if (!location.area.testForDim(dimension)) {
logger.debug("location: $label dropped message '$msg' from $user due to mismatched dimension")
continue
}
if (!location.area.testInBounds(x, y, z)) {
logger.debug("location: $label dropped message '$msg' from $user out of coordinate bounds")
continue
}
val matchesEvent = when (event) {
ChatEvent.PLAIN -> location.outgoing.plain ?: defaults.plain
ChatEvent.ACTION -> location.outgoing.action ?: defaults.action
ChatEvent.DEATH -> location.outgoing.death ?: defaults.death
ChatEvent.JOIN -> location.outgoing.join ?: defaults.join
ChatEvent.LEAVE -> location.outgoing.leave ?: defaults.leave
ChatEvent.ADVANCEMENT -> location.outgoing.advancement ?: defaults.advancement
ChatEvent.BROADCAST -> location.outgoing.broadcast ?: defaults.broadcast
ChatEvent.STATUS -> location.outgoing.status ?: defaults.status
}
if (!matchesEvent) {
logger.debug("location: $label dropped message '$msg' from user: '$user', event not enabled")
logger.debug("event: $event")
logger.debug("location.outgoing: ${location.outgoing}")
logger.debug("defaults: $defaults")
continue
}
skips.addAll(location.outgoing.skip)
val eventStr = when (event) {
ChatEvent.PLAIN -> ""
ChatEvent.ACTION -> ApiMessage.USER_ACTION
ChatEvent.DEATH -> ""
ChatEvent.JOIN -> ApiMessage.JOIN_LEAVE
ChatEvent.LEAVE -> ApiMessage.JOIN_LEAVE
ChatEvent.ADVANCEMENT -> ""
ChatEvent.BROADCAST -> ""
ChatEvent.STATUS -> ""
}
val username = when {
systemuser -> cfg.outgoing.systemUser
else -> user
}
val avatar = when {
systemuser ->
cfg.outgoing.avatar.systemUserAvatar
cfg.outgoing.avatar.enable && uuid != null ->
cfg.outgoing.avatar.urlTemplate.replace("{uuid}", uuid.toString())
else ->
null
}
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
)
else -> logger.warn("WARN: dropped blank message by '$user'")
}
logger.debug("sent message through location: $label, cause: $cause")
handled = true
}
return handled
}
}

View File

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

View File

@ -1,80 +1,26 @@
package matterlink.handlers package matterlink.handlers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst import matterlink.bridge.MessageHandlerInst
import matterlink.bridge.command.BridgeCommandRegistry import matterlink.bridge.command.BridgeCommandRegistry
import matterlink.bridge.format import matterlink.bridge.format
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.instance import matterlink.instance
import matterlink.logger import matterlink.logger
import java.util.UUID import matterlink.stripColorIn
object ServerChatHandler { object ServerChatHandler {
@UseExperimental(ExperimentalCoroutinesApi::class)
val rcvChannel = MessageHandlerInst.broadcast.openSubscription()
/** /**
* This method must be called every server tick with no arguments. * This method must be called every server tick with no arguments.
*/ */
suspend fun writeIncomingToChat() { fun writeIncomingToChat() {
val nextMessage = rcvChannel.poll() ?: return if (MessageHandlerInst.queue.isNotEmpty())
logger.debug("incoming: " + MessageHandlerInst.queue.toString())
val defaults = cfg.incomingDefaults val nextMessage = MessageHandlerInst.queue.poll() ?: null
val sourceGateway = nextMessage.gateway
// find all areas to send to
val targetUUIDs = mutableSetOf<UUID>()
val skips = mutableSetOf<String>()
val locations = cfg.locations.filter {
it.gateway == sourceGateway
}
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) {
if (BridgeCommandRegistry.handleCommand(nextMessage)) return
}
}
for (location in locations) {
val label = location.label
if (skips.contains(label)) {
logger.debug("skipping $label")
continue
}
val matchesEvent = when (nextMessage.event) {
"" -> {
// if (location.incoming.commands ?: defaults.commands
// && BridgeCommandRegistry.handleCommand(nextMessage)) return
location.incoming.plain ?: defaults.plain
}
ApiMessage.JOIN_LEAVE -> location.incoming.join_leave ?: defaults.join_leave
ApiMessage.USER_ACTION -> location.incoming.action ?: defaults.action
else -> {
logger.error("unknown event type '${nextMessage.event}' on incoming message")
return
}
}
if (!matchesEvent) {
logger.info("location: $label dropped message '$nextMessage' event not enabled")
continue
}
targetUUIDs.addAll(instance.collectPlayers(location.area))
}
if (nextMessage?.gateway == cfg.connect.gateway) {
if (!nextMessage.text.isBlank()) {
nextMessage.text = nextMessage.text.stripColorIn
val message = when (nextMessage.event) { val message = when (nextMessage.event) {
"user_action" -> nextMessage.format(cfg.incoming.action) "user_action" -> nextMessage.format(cfg.incoming.action)
"" -> { "" -> {
@ -95,38 +41,8 @@ object ServerChatHandler {
return return
} }
} }
instance.wrappedSendToPlayers(message)
targetUUIDs.forEach { }
//TODO: optimize send to all at once
instance.wrappedSendToPlayer(it, message)
} }
// if (nextMessage?.gateway == cfg.connect.gateway) {
// if (!nextMessage.text.isBlank()) {
// nextMessage.text = nextMessage.text.stripColorIn
// val message = when (nextMessage.event) {
// "user_action" -> nextMessage.format(cfg.incoming.action)
// "" -> {
// // try to handle command and do not handle as a chat message
// if (BridgeCommandRegistry.handleCommand(nextMessage)) return
// nextMessage.format(cfg.incoming.chat)
// }
// "join_leave" -> nextMessage.format(cfg.incoming.joinPart)
// else -> {
// val user = nextMessage.username
// val text = nextMessage.text
// val json = nextMessage.encode()
// logger.debug("Threw out message with unhandled event: ${nextMessage.event}")
// logger.debug(" Message contents:")
// logger.debug(" User: $user")
// logger.debug(" Text: $text")
// logger.debug(" JSON: $json")
// return
// }
// }
// instance.wrappedSendToPlayers(message)
// }
// }
} }
} }

View File

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

View File

@ -1,13 +1,13 @@
package matterlink.jenkins package matterlink.jenkins
import kotlinx.serialization.Serializable import com.google.gson.annotations.SerializedName
/** /**
* Created by nikky on 03/02/18. * Created by nikky on 03/02/18.
* @author Nikky * @author Nikky
*/ */
@Serializable //@JsonIgnoreProperties(ignoreUnknown = true)
data class Artifact( data class Artifact(
val displayPath: String, val displayPath: String,
val fileName: String, val fileName: String,

View File

@ -1,11 +1,11 @@
package matterlink.jenkins package matterlink.jenkins
import com.github.kittinunf.fuel.httpGet import com.google.gson.Gson
import com.github.kittinunf.fuel.serialization.kotlinxDeserializerOf import matterlink.*
import com.github.kittinunf.result.Result import matterlink.Result
import kotlinx.serialization.json.JSON import matterlink.header
import matterlink.logger import matterlink.httpGet
import matterlink.responseString
/** /**
* Created by nikky on 03/02/18. * Created by nikky on 03/02/18.
@ -21,10 +21,10 @@ data class Build(
val (request, response, result) = "$url/api/json" val (request, response, result) = "$url/api/json"
.httpGet() .httpGet()
.header("User-Agent" to userAgent) .header("User-Agent" to userAgent)
.responseObject(kotlinxDeserializerOf(loader = BuildWithDetails.serializer(), json = JSON.nonstrict)) .responseString()
return when (result) { return when(result) {
is Result.Success -> { is Result.Success -> {
result.value gson.fromJson(result.value, BuildWithDetails::class.java)
} }
is Result.Failure -> { is Result.Failure -> {
logger.error(result.error.toString()) logger.error(result.error.toString())
@ -32,5 +32,9 @@ data class Build(
} }
} }
} }
companion object {
val gson = Gson()
}
} }

View File

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

View File

@ -1,10 +1,12 @@
package matterlink.jenkins package voodoo.util.jenkins
import com.github.kittinunf.fuel.httpGet import com.google.gson.Gson
import com.github.kittinunf.fuel.serialization.kotlinxDeserializerOf import matterlink.Result
import com.github.kittinunf.result.Result import matterlink.header
import kotlinx.serialization.json.JSON import matterlink.httpGet
import matterlink.responseString
import matterlink.jenkins.Job
import matterlink.logger import matterlink.logger
/** /**
@ -22,10 +24,10 @@ class JenkinsServer(val url: String) {
val (_, _, result) = requestURL val (_, _, result) = requestURL
.httpGet() .httpGet()
.header("User-Agent" to userAgent) .header("User-Agent" to userAgent)
.responseObject(kotlinxDeserializerOf(loader = Job.serializer(), json = JSON.nonstrict)) .responseString()
return when (result) { return when (result) {
is Result.Success -> { is Result.Success -> {
result.value gson.fromJson(result.value, Job::class.java)
} }
is Result.Failure -> { is Result.Failure -> {
logger.error(result.error.toString()) logger.error(result.error.toString())
@ -34,4 +36,8 @@ class JenkinsServer(val url: String) {
} }
} }
companion object {
val gson = Gson()
}
} }

View File

@ -1,13 +1,11 @@
package matterlink.jenkins package matterlink.jenkins;
import kotlinx.serialization.Serializable
/** /**
* Created by nikky on 03/02/18. * Created by nikky on 03/02/18.
* @author Nikky * @author Nikky
*/ */
@Serializable //@JsonIgnoreProperties(ignoreUnknown = true)
data class Job( data class Job(
val url: String, val url: String,
val name: String, val name: String,

View File

@ -1,12 +1,8 @@
package matterlink.update package matterlink.update
import kotlinx.serialization.Serializable
@Serializable
data class CurseFile( data class CurseFile(
val downloadUrl: String, val downloadURL: String,
val fileName: String, val fileName: String,
val fileNameOnDisk: String,
val gameVersion: List<String>, val gameVersion: List<String>,
val releaseType: String, val releaseType: String,
val fileStatus: String val fileStatus: String

View File

@ -1,33 +1,31 @@
package matterlink.update package matterlink.update
import com.github.kittinunf.fuel.core.extensions.cUrlString import com.google.gson.FieldNamingPolicy
import com.github.kittinunf.fuel.httpGet import com.google.gson.GsonBuilder
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.serialization.list
import matterlink.api.ApiMessage import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg import matterlink.config.cfg
import matterlink.handlers.ChatEvent
import matterlink.handlers.LocationHandler
import matterlink.instance import matterlink.instance
import matterlink.jenkins.JenkinsServer
import matterlink.logger import matterlink.logger
import com.github.kittinunf.fuel.serialization.kotlinxDeserializerOf import voodoo.util.jenkins.JenkinsServer
import com.github.kittinunf.result.Result import java.io.BufferedReader
import kotlinx.serialization.json.JSON import java.net.HttpURLConnection
import java.net.URL
object UpdateChecker : CoroutineScope { class UpdateChecker : Thread() {
override val coroutineContext = Job() + CoroutineName("UpdateChacker") companion object {
fun run() {
suspend fun check() {
if (cfg.update.enable) { if (cfg.update.enable) {
run() UpdateChecker().start()
}
} }
} }
private suspend fun run() { init {
name = "UpdateCheckerThread"
}
override fun run() {
if (instance.buildNumber > 0) { if (instance.buildNumber > 0) {
val server = JenkinsServer("https://ci.elytradev.com") val server = JenkinsServer("https://ci.elytradev.com")
val job = server.getJob("elytra/MatterLink/master", "MatterLink/${instance.modVersion}") val job = server.getJob("elytra/MatterLink/master", "MatterLink/${instance.modVersion}")
@ -44,12 +42,11 @@ object UpdateChecker : CoroutineScope {
when { when {
number > instance.buildNumber -> { number > instance.buildNumber -> {
logger.warn("Mod out of date! New build $number available at $url") logger.warn("Mod out of date! New build $number available at $url")
val difference = number - instance.buildNumber val difference = number - build.number
LocationHandler.sendToLocations( MessageHandlerInst.transmit(
msg = "MatterLink out of date! You are $difference builds behind! Please download new version from $url", ApiMessage(
x = 0, y = 0, z = 0, dimension = null, text = "MatterLink out of date! You are $difference builds behind! Please download new version from $url"
event = ChatEvent.STATUS, )
cause = "MatterLink update notice"
) )
} }
number < instance.buildNumber -> logger.error("lastSuccessfulBuild: $number is older than installed build: ${instance.buildNumber}") number < instance.buildNumber -> logger.error("lastSuccessfulBuild: $number is older than installed build: ${instance.buildNumber}")
@ -63,30 +60,40 @@ object UpdateChecker : CoroutineScope {
return return
} }
val gson = GsonBuilder()
// .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.create()
logger.info("Checking for new versions...") logger.info("Checking for new versions...")
val (request, response, result) = with(instance) {
val useragent = val url = URL("https://staging_cursemeta.dries007.net/api/v3/direct/addon/287323/files")
"MatterLink/$modVersion MinecraftForge/$mcVersion-$forgeVersion (https://github.com/elytra/MatterLink)" val con = url.openConnection() as HttpURLConnection
with(instance) {
val useragent = "MatterLink/$modVersion MinecraftForge/$mcVersion-$forgeVersion (https://github.com/elytra/MatterLink)"
logger.debug("setting User-Agent: '$useragent'") logger.debug("setting User-Agent: '$useragent'")
con.setRequestProperty("User-Agent", useragent)
"https://curse.nikky.moe/api/addon/287323/files".httpGet()
.header("User-Agent" to useragent)
.responseObject(kotlinxDeserializerOf(loader = CurseFile.serializer().list, json = JSON.nonstrict))
} }
val apiUpdateList = when(result) { con.connect()
is Result.Success -> {
result.value val apiUpdateList = if (200 == con.responseCode) { //HTTP 200 OK
val buffer: BufferedReader = con.inputStream.bufferedReader()
//put all of the buffer content onto the string
val content = buffer.readText()
logger.trace("updateData: $content")
gson.fromJson(content, Array<CurseFile>::class.java)
.filter {
it.fileStatus == "SemiNormal" && it.gameVersion.contains(instance.mcVersion)
} }
is Result.Failure -> { .sortedByDescending { it.fileName.substringAfterLast(" ") }
} else {
logger.error("Could not check for updates!") logger.error("Could not check for updates!")
logger.error("cUrl: ${request.cUrlString()}")
logger.error("request: $request")
logger.error("response: $response")
return return
} }
}
.filter { it.fileStatus == "SemiNormal" && it.gameVersion.contains(instance.mcVersion) }
val modVersionChunks = instance.modVersion val modVersionChunks = instance.modVersion
.substringBefore("-dev") .substringBefore("-dev")
@ -124,13 +131,13 @@ object UpdateChecker : CoroutineScope {
logger.info("Matterlink out of date! You are $count $version behind") logger.info("Matterlink out of date! You are $count $version behind")
possibleUpdates.forEach { possibleUpdates.forEach {
logger.info("version: ${it.fileName} download: ${it.downloadUrl}") logger.info("version: {} download: {}", it.fileName, it.downloadURL)
} }
logger.warn("Mod out of date! New $version available at ${latest.downloadUrl}") logger.warn("Mod out of date! New $version available at ${latest.downloadURL}")
MessageHandlerInst.transmit( MessageHandlerInst.transmit(
ApiMessage( ApiMessage(
text = "MatterLink out of date! You are $count $version behind! Please download new version from ${latest.downloadUrl}" text = "MatterLink out of date! You are $count $version behind! Please download new version from ${latest.downloadURL}"
) )
) )
} }

View File

@ -1,12 +1,8 @@
modName = MatterLink mod_name = MatterLink
modVersion = 1.6.4 mod_version = 1.6.3
forgelinVersion = 1.8.2 forgelin_version = 1.6.0
kotlinVersion = 1.3.10 kotlin_version = 1.2.41
coroutinesVersion = 1.0.1 shadow_version = 2.0.2
serializationVersion = 0.9.1 cursegradle_version = 1.0.10
shadowVersion = 2.0.2 curse_id = 287323
fuelVersion = 8690665998 curse_release_type = beta
resultVersion = 2.0.0
cursegradleVersion = 1.1.2
curseId = 287323
curseReleaseType = beta

View File

@ -1,23 +0,0 @@
#!/usr/bin/env bash
java -jar forge-installer.jar --installServer
FORGE_FILE=`grep "The server installed successfully, you should now be able to run the file " forge-installer.jar.log | tail -1`
FORGE_FILE=${FORGE_FILE#"The server installed successfully, you should now be able to run the file "}
echo $FORGE_FILE
cp -f "$FORGE_FILE" forge.jar
if [ ! $? -eq 0 ]; then
echo "Error installing forge"
exit 1
fi
echo "installed forge"
cp ../eula.txt .
mkdir -p config
rm -rf config/matterlink
cp -r ../matterlink_config config/matterlink
java -jar forge.jar

View File

@ -1,22 +0,0 @@
#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )"
RUN="$PWD/run/1.12.2"
rm -rf "$RUN/mods"
mkdir -p "$RUN/mods"
"$PWD/gradlew" :1.12.2:clean :1.12.2:build && cp -f 1.12.2/build/libs/MatterLink-1.12.2-*-dev.jar "$RUN/mods"
if [ ! $? -eq 0 ]; then
echo "Error compiling matterlink"
exit 1
fi
cd "$RUN"
curl -o forge-installer.jar "https://files.minecraftforge.net/maven/net/minecraftforge/forge/1.12.2-14.23.5.2768/forge-1.12.2-14.23.5.2768-installer.jar"
curl -L -o "$RUN/mods/Forgelin.jar" "https://minecraft.curseforge.com/projects/shadowfacts-forgelin/files/2640952/download"
$DIR/scripts/start.sh

View File

@ -1,19 +0,0 @@
#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )"
RUN="$PWD/run/1.7.10"
rm -rf "$RUN/mods"
mkdir -p "$RUN/mods"
"$PWD/gradlew" :1.7.10:clean :1.7.10:build && cp -f $DIR/1.7.10/build/libs/MatterLink-1.7.10-*-dev.jar "$RUN/mods"
if [ ! $? -eq 0 ]; then
echo "Error compiling matterlink"
exit 1
fi
cd "$RUN"
curl -o forge-installer.jar "https://files.minecraftforge.net/maven/net/minecraftforge/forge/1.7.10-10.13.4.1614-1.7.10/forge-1.7.10-10.13.4.1614-1.7.10-installer.jar"
$DIR/scripts/start.sh

View File

@ -1,22 +0,0 @@
#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )"
RUN="$PWD/run/1.9.4"
rm -rf "$RUN/mods"
mkdir -p "$RUN/mods"
"$PWD/gradlew" :1.9.4:clean :1.9.4:build && cp -f 1.9.4/build/libs/MatterLink-1.9.4-*-dev.jar "$RUN/mods"
if [ ! $? -eq 0 ]; then
echo "Error compiling matterlink"
exit 1
fi
cd "$RUN"
curl -o forge-installer.jar "https://files.minecraftforge.net/maven/net/minecraftforge/forge/1.9.4-12.17.0.2051/forge-1.9.4-12.17.0.2051-installer.jar"
curl -L -o "$RUN/mods/Forgelin.jar" "https://minecraft.curseforge.com/projects/shadowfacts-forgelin/files/2640952/download" # "https://minecraft.curseforge.com/projects/shadowfacts-forgelin/files/2573311/download"
$DIR/scripts/start.sh

View File

@ -1,4 +1,4 @@
rootProject.name = 'MatterLink' rootProject.name = 'MatterLink'
include 'core' include 'core', 'api'
include 'Jankson' include 'Jankson'
include '1.12.2', '1.9.4', '1.7.10' include '1.12.2', '1.11.2', '1.10.2', '1.7.10'