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
.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
### MatterLink ###
@ -98,5 +102,3 @@ run/
\.floo
\.flooignore
bugreport/

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "api"]
path = api
url = https://github.com/NikkyAI/MatterLinkApi.git
[submodule "Jankson"]
path = Jankson
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 {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '2.2-SNAPSHOT'
classpath group: 'com.github.jengelman.gradle.plugins', name: 'shadow', version: shadowVersion
classpath group: 'gradle.plugin.com.matthewprenger', name: 'CurseGradle', version: cursegradleVersion
classpath group: 'com.github.jengelman.gradle.plugins', name: 'shadow', version: shadow_version
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.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'
dependencies {
compile project(':core')
shadow (project(path: ':core', configuration: 'shadow')) { transitive = false }
compile group: 'net.shadowfacts', name: 'Forgelin', version: project.forgelinVersion
compile group: 'net.shadowfacts', name: 'Forgelin', version: project.forgelin_version
}
shadowJar {
classifier = ''
classifier ''
relocate 'blue.endless', 'matterlink.repack.blue.endless'
dependencies {
include project(':core')
include project(':api')
include project(':Jankson')
}
exclude 'dummyThing'
configurations = [project.configurations.shadow]
}
import net.minecraftforge.gradle.user.TaskSourceCopy
// Mad hacks to make source replacements work for Kotlin
@ -68,16 +74,16 @@ minecraft {
mappings = project.mcp_mappings
replaceIn 'Constants.kt'
replace '@MODVERSION@', project.modVersion
replace '@MODVERSION@', project.mod_version
replace '@MCVERSION@', project.mc_version
replace '@FORGELIN-VERSION@', project.forgelinVersion
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.modVersion
inputs.property 'version', project.mod_version
inputs.property 'mcversion', project.minecraft.version
// replace stuff in mcmod.info, nothing else
@ -85,7 +91,7 @@ processResources {
include 'mcmod.info'
// 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
@ -110,6 +116,7 @@ sourceJar {
reobf {
shadowJar { mappingType = 'SEARGE' }
}
tasks.shadowJar.finalizedBy reobfShadowJar
curseforge {
@ -117,9 +124,8 @@ curseforge {
apiKey = CURSEFORGE_API_TOKEN
}
project {
id = project.curseId
releaseType = project.curseReleaseType
addGameVersion '1.10'
id = project.curse_id
releaseType = project.curse_release_type
if (project.hasProperty('changelog_file')) {
println("changelog = $changelog_file")
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
import com.mojang.authlib.GameProfile
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand
import matterlink.command.AuthCommand
import matterlink.command.MatterLinkCommand
import matterlink.command.MatterLinkCommandSender
import matterlink.config.BaseConfig
@ -11,14 +9,15 @@ import matterlink.config.cfg
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.util.text.TextComponentString
import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.common.MinecraftForge
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 java.util.UUID
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
import java.util.*
@Mod(
modid = MODID,
@ -36,17 +35,7 @@ object MatterLink : IMatterLink() {
@Mod.EventHandler
fun preInit(event: FMLPreInitializationEvent) {
MinecraftForge.EVENT_BUS.register(EventHandler)
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 = event.modLog as org.apache.logging.log4j.core.Logger
logger.info("Building bridge!")
cfg = BaseConfig(event.modConfigurationDirectory).load()
@ -58,15 +47,14 @@ object MatterLink : IMatterLink() {
}
@Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) = runBlocking {
fun serverStarting(event: FMLServerStartingEvent) {
logger.debug("Registering server commands")
event.registerServerCommand(MatterLinkCommand)
event.registerServerCommand(AuthCommand)
event.registerServerCommand(MatterLinkCommand())
start()
}
@Mod.EventHandler
fun serverStopping(event: FMLServerStoppingEvent) = runBlocking {
fun serverStopping(event: FMLServerStoppingEvent) {
stop()
}
@ -84,7 +72,7 @@ object MatterLink : IMatterLink() {
error("${profile.name} is not online")
return
}
player.addChatMessage(TextComponentString(msg))
player.sendMessage(TextComponentString(msg))
}
override fun wrappedSendToPlayer(uuid: UUID, msg: String) {
@ -96,14 +84,12 @@ object MatterLink : IMatterLink() {
logger.error("${profile.name} is not online")
return
}
player.addChatMessage(TextComponentString(msg))
player.sendMessage(TextComponentString(msg))
}
override fun isOnline(username: String) =
FMLCommonHandler.instance().minecraftServerInstance.playerList.allUsernames.contains(username)
override fun isOnline(username: String) = FMLCommonHandler.instance().minecraftServerInstance.onlinePlayerNames.contains(username)
private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? =
FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id)
private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? = FMLCommonHandler.instance().minecraftServerInstance.playerList.getPlayerByUUID(gameProfile.id)
private fun profileByUUID(uuid: UUID): GameProfile? = try {
@ -120,17 +106,6 @@ object MatterLink : IMatterLink() {
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 uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name

View File

@ -1,5 +1,6 @@
package matterlink.command
import matterlink.logger
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
@ -8,16 +9,16 @@ import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString
object AuthCommand : CommandBase() {
override fun getCommandName(): String {
class AuthCommand : CommandBase() {
override fun getName(): String {
return CommandCoreAuth.name
}
override fun getCommandUsage(sender: ICommandSender): String {
override fun getUsage(sender: ICommandSender): String {
return CommandCoreAuth.usage
}
override fun getCommandAliases(): List<String> {
override fun getAliases(): List<String> {
return CommandCoreAuth.aliases
}
@ -27,14 +28,14 @@ object AuthCommand : CommandBase() {
override fun execute(server: MinecraftServer, sender: ICommandSender, args: Array<String>) {
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 reply = CommandCoreAuth.execute(args, sender.name, uuid)
if (reply.isNotEmpty() && sender.sendCommandFeedback()) {
sender.addChatMessage(TextComponentString(reply))
sender.sendMessage(TextComponentString(reply))
}
}

View File

@ -1,6 +1,5 @@
package matterlink.command
import kotlinx.coroutines.runBlocking
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
@ -9,29 +8,29 @@ import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString
object MatterLinkCommand : CommandBase() {
override fun getCommandName(): String {
class MatterLinkCommand : CommandBase() {
override fun getName(): String {
return CommandCoreML.name
}
override fun getCommandUsage(sender: ICommandSender): String {
override fun getUsage(sender: ICommandSender): String {
return CommandCoreML.usage
}
override fun getCommandAliases(): List<String> {
override fun getAliases(): List<String> {
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()) {
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 reply = CommandCoreML.execute(args, sender.name, uuid)
if (reply.isNotEmpty() && sender.sendCommandFeedback()) {
sender.addChatMessage(TextComponentString(reply))
sender.sendMessage(TextComponentString(reply))
}
}
}

View File

@ -1,6 +1,5 @@
package matterlink.command
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand
import matterlink.bridge.command.IMinecraftCommandSender
import net.minecraft.command.CommandResultStats
@ -18,11 +17,11 @@ import javax.annotation.Nonnull
class MatterLinkCommandSender(
user: String,
env: IBridgeCommand.CommandEnvironment,
op: Boolean
) : IMinecraftCommandSender(user, env, op), ICommandSender {
override fun execute(cmdString: String): Boolean = runBlocking {
return@runBlocking 0 < FMLCommonHandler.instance().minecraftServerInstance.commandManager.executeCommand(
this@MatterLinkCommandSender,
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)
@ -39,7 +38,7 @@ class MatterLinkCommandSender(
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
return canExecute(commandName)
}
@ -48,7 +47,7 @@ class MatterLinkCommandSender(
return FMLCommonHandler.instance().minecraftServerInstance
}
override fun addChatMessage(@Nonnull component: ITextComponent?) {
override fun sendMessage(@Nonnull component: ITextComponent?) {
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 {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '2.3-SNAPSHOT'
classpath group: 'com.github.jengelman.gradle.plugins', name: 'shadow', version: shadowVersion
classpath group: 'gradle.plugin.com.matthewprenger', name: 'CurseGradle', version: cursegradleVersion
classpath group: 'com.vanniktech', name: 'gradle-dependency-graph-generator-plugin', version: '0.5.0'
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'
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'
dependencies {
compile project(':core')
shadow (project(path: ':core', configuration: 'shadow')) { transitive = false }
compile group: 'net.shadowfacts', name: 'Forgelin', version: project.forgelinVersion
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'
configurations = [project.configurations.shadow]
}
minecraft {
@ -52,16 +55,16 @@ minecraft {
mappings = project.mcp_mappings
replaceIn 'Constants.kt'
replace '@MODVERSION@', project.modVersion
replace '@MODVERSION@', project.mod_version
replace '@MCVERSION@', project.mc_version
replace '@FORGELIN-VERSION@', project.forgelinVersion
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.modVersion
inputs.property "version", project.mod_version
inputs.property "mcversion", project.minecraft.version
// replace stuff in mcmod.info, nothing else
@ -69,7 +72,7 @@ processResources {
include 'mcmod.info'
// 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
@ -93,6 +96,7 @@ sourceJar {
reobf {
shadowJar { mappingType = 'SEARGE' }
}
tasks.shadowJar.finalizedBy reobfShadowJar
curseforge {
@ -100,8 +104,8 @@ curseforge {
apiKey = CURSEFORGE_API_TOKEN
}
project {
id = project.curseId
releaseType = project.curseReleaseType
id = project.curse_id
releaseType = project.curse_release_type
if (project.hasProperty('changelog_file')) {
println("changelog = $changelog_file")
changelogType = 'markdown'
@ -115,7 +119,3 @@ curseforge {
}
}
}
runServer {
outputs.upToDateWhen { false }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,16 +11,16 @@ buildscript {
}
dependencies {
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: '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'
@ -30,35 +30,21 @@ configurations {
}
dependencies {
shade (project(':core')) { transitive = true }
shade (project(':Jankson')) { transitive = false }
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
shade project(':core')
shade project(':api')
shade project(':Jankson')
}
minecraft {
version = project.mc_version + '-' + project.forge_version + '-' + project.mc_version
runDir = 'run'
mappings = project.mcp_mappings
// srgExtra 'PK: kotlin matterlink/repack/kotlin'
// srgExtra 'PK: org/jetbrains/annotations matterlink/repack/org/jetbrains/annotations'
// srgExtra 'PK: org/intellij matterlink/repack/org/intellij'
// srgExtra 'PK: blue/endless/ matterlink/repack/blue/endless/'
srgExtra 'PK: kotlin matterlink/repack/kotlin'
srgExtra 'PK: org/jetbrains/annotations matterlink/repack/org/jetbrains/annotations'
srgExtra 'PK: org/intellij matterlink/repack/org/intellij'
srgExtra 'PK: blue/endless matterlink/repack/blue/endless'
}
compileKotlin.doFirst {
@ -67,13 +53,13 @@ compileKotlin.doFirst {
from('src/templates/kotlin/matterlink/Constants.kt')
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')
}
ant.replaceregexp(match: '@MCVERSION@', replace: project.mc_version, flags: 'g', byline: true) {
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')
}
ant.replaceregexp(match: '@FORGE-VERSION@', replace: project.forge_version, flags: 'g', byline: true) {
@ -86,7 +72,7 @@ compileKotlin.doFirst {
processResources {
// 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
// replace stuff in mcmod.info, nothing else
@ -94,7 +80,7 @@ processResources {
include 'mcmod.info'
// 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
@ -107,6 +93,7 @@ jar {
configurations.shade.each { dep ->
from(project.zipTree(dep)) {
exclude 'META-INF', 'META-INF/**'
exclude 'com/google/gson', 'com/google/gson/**'
}
}
}
@ -116,8 +103,8 @@ curseforge {
apiKey = CURSEFORGE_API_TOKEN
}
project {
id = project.curseId
releaseType = project.curseReleaseType
id = project.curse_id
releaseType = project.curse_release_type
if (project.hasProperty('changelog_file')) {
println("changelog = $changelog_file")
changelogType = 'markdown'

View File

@ -3,17 +3,11 @@ package matterlink
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.common.gameevent.PlayerEvent
import cpw.mods.fml.common.gameevent.TickEvent
import kotlinx.coroutines.runBlocking
import matterlink.api.ApiMessage.Companion.USER_ACTION
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 matterlink.handlers.*
import net.minecraft.command.server.CommandBroadcast
import net.minecraft.command.server.CommandEmote
import net.minecraft.entity.Entity
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.server.dedicated.DedicatedServer
@ -27,121 +21,80 @@ object EventHandler {
//MC-VERSION & FORGE DEPENDENT
@SubscribeEvent
fun progressEvent(e: AchievementEvent) = runBlocking {
fun progressEvent(e: AchievementEvent) {
val achievement = e.achievement
val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return@runBlocking
val entityPlayer = e.entityPlayer as? EntityPlayerMP ?: return
val statFile = entityPlayer.statFile
if (!statFile.canUnlockAchievement(achievement) || statFile.hasAchievementUnlocked(achievement)) {
return@runBlocking
return
}
ProgressHandler.handleProgress(
name = e.entityPlayer.displayName,
message = "has earned the achievement",
display = e.achievement.statName.unformattedText,
x = e.entityPlayer.posX.toInt(),
y = e.entityPlayer.posY.toInt(),
z = e.entityPlayer.posZ.toInt(),
dimension = e.entityPlayer.dimension
display = e.achievement.statName.unformattedText
)
}
//FORGE-DEPENDENT
@SubscribeEvent
fun chatEvent(e: ServerChatEvent) = runBlocking {
if (e.isCanceled) return@runBlocking
fun chatEvent(e: ServerChatEvent) {
if(e.isCanceled) return
e.isCanceled = ChatProcessor.sendToBridge(
user = e.player.displayName,
msg = e.message,
x = e.player.posX.toInt(),
y = e.player.posY.toInt(),
z = e.player.posZ.toInt(),
dimension = e.player.dimension,
event = ChatEvent.PLAIN,
event = "",
uuid = e.player.gameProfile.id
)
}
//FORGE-DEPENDENT
@SubscribeEvent
fun commandEvent(e: CommandEvent) = runBlocking {
fun commandEvent(e: CommandEvent) {
val sender = when {
e.sender is DedicatedServer -> cfg.outgoing.systemUser
else -> e.sender.commandSenderName
}
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
val type = when {
e.command is CommandEmote -> USER_ACTION
e.command.commandName == "me" -> USER_ACTION
e.command is CommandBroadcast -> ""
else -> return
}
}
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
}
)
ChatProcessor.sendToBridge(user = sender, msg = args, event = type)
}
//FORGE-DEPENDENT
@SubscribeEvent
fun deathEvent(e: LivingDeathEvent) = runBlocking {
fun deathEvent(e: LivingDeathEvent) {
if (e.entityLiving is EntityPlayer) {
val player = e.entityLiving as EntityPlayer
DeathHandler.handleDeath(
player = player.displayName,
deathMessage = e.entityLiving.combatTracker.func_151521_b().unformattedText,
damageType = e.source.damageType,
x = e.entityLiving.posX.toInt(),
y = e.entityLiving.posY.toInt(),
z = e.entityLiving.posZ.toInt(),
dimension = e.entityLiving.dimension
damageType = e.source.damageType
)
}
}
//FORGE-DEPENDENT
@SubscribeEvent
fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) = runBlocking {
JoinLeaveHandler.handleJoin(
player = e.player.displayName,
x = e.player.posX.toInt(),
y = e.player.posY.toInt(),
z = e.player.posZ.toInt(),
dimension = e.player.dimension
)
fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) {
JoinLeaveHandler.handleJoin(e.player.displayName)
}
//FORGE-DEPENDENT
@SubscribeEvent
fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) = runBlocking {
JoinLeaveHandler.handleLeave(
player = e.player.displayName,
x = e.player.posX.toInt(),
y = e.player.posY.toInt(),
z = e.player.posZ.toInt(),
dimension = e.player.dimension
)
fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) {
JoinLeaveHandler.handleLeave(e.player.displayName)
}
//FORGE-DEPENDENT
@SubscribeEvent
fun serverTickEvent(e: TickEvent.ServerTickEvent) = runBlocking {
fun serverTickEvent(e: TickEvent.ServerTickEvent) {
if (e.phase == TickEvent.Phase.END)
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.FMLServerStartingEvent
import cpw.mods.fml.common.event.FMLServerStoppingEvent
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand
import matterlink.command.AuthCommand
import matterlink.command.MatterLinkCommand
@ -19,7 +18,7 @@ import net.minecraft.server.MinecraftServer
import net.minecraft.util.ChatComponentText
import net.minecraftforge.common.ForgeVersion
import net.minecraftforge.common.MinecraftForge
import java.util.UUID
import java.util.*
@Mod(
modid = MODID,
@ -37,16 +36,7 @@ class MatterLink : IMatterLink() {
MinecraftForge.EVENT_BUS.register(EventHandler)
FMLCommonHandler.instance().bus().register(EventHandler)
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 = event.modLog as org.apache.logging.log4j.core.Logger
logger.info("Building bridge!")
cfg = BaseConfig(event.modConfigurationDirectory).load()
@ -58,15 +48,15 @@ class MatterLink : IMatterLink() {
}
@Mod.EventHandler
fun serverStarting(event: FMLServerStartingEvent) = runBlocking {
fun serverStarting(event: FMLServerStartingEvent) {
logger.debug("Registering server commands")
event.registerServerCommand(MatterLinkCommand)
event.registerServerCommand(AuthCommand)
event.registerServerCommand(MatterLinkCommand())
event.registerServerCommand(AuthCommand())
start()
}
@Mod.EventHandler
fun serverStopping(event: FMLServerStoppingEvent) = runBlocking {
fun serverStopping(event: FMLServerStoppingEvent) {
stop()
}
@ -103,8 +93,7 @@ class MatterLink : IMatterLink() {
.minecraftServerInstance.configurationManager.getPlayerByUsername(username) ?: null) != null
private fun playerByProfile(gameProfile: GameProfile): EntityPlayerMP? {
return FMLCommonHandler.instance()
.minecraftServerInstance.configurationManager.getPlayerByUsername(gameProfile.name)
return FMLCommonHandler.instance().minecraftServerInstance.configurationManager.createPlayerForUser(gameProfile)
}
private fun profileByUUID(uuid: UUID): GameProfile? = try {
@ -121,16 +110,6 @@ class MatterLink : IMatterLink() {
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 uuidToName(uuid: UUID): String? = profileByUUID(uuid)?.name

View File

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

View File

@ -1,6 +1,5 @@
package matterlink.command
import kotlinx.coroutines.runBlocking
import net.minecraft.command.CommandBase
import net.minecraft.command.ICommandSender
import net.minecraft.command.WrongUsageException
@ -8,7 +7,7 @@ import net.minecraft.entity.player.EntityPlayer
import net.minecraft.util.ChatComponentText
object MatterLinkCommand : CommandBase() {
class MatterLinkCommand : CommandBase() {
override fun getCommandName(): String {
return CommandCoreML.name
}
@ -21,9 +20,9 @@ object MatterLinkCommand : CommandBase() {
return CommandCoreML.aliases
}
override fun processCommand(sender: ICommandSender, args: Array<String>) = runBlocking {
override fun processCommand(sender: ICommandSender, args: Array<String>) {
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()

View File

@ -1,6 +1,5 @@
package matterlink.command
import kotlinx.coroutines.runBlocking
import matterlink.bridge.command.IBridgeCommand
import matterlink.bridge.command.IMinecraftCommandSender
import net.minecraft.command.ICommandSender
@ -13,12 +12,11 @@ import net.minecraft.world.World
class MatterLinkCommandSender(
user: String,
env: IBridgeCommand.CommandEnvironment,
op: Boolean
) : IMinecraftCommandSender(user, env, op), ICommandSender {
op: Boolean) : IMinecraftCommandSender(user, env, op), ICommandSender {
override fun execute(cmdString: String): Boolean = runBlocking {
return@runBlocking 0 < MinecraftServer.getServer().commandManager.executeCommand(
this@MatterLinkCommandSender,
override fun execute(cmdString: String): Boolean {
return 0 < MinecraftServer.getServer().commandManager.executeCommand(
this,
cmdString
).apply {
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'
}
}
stage("1.9.4") {
stage("1.10.2") {
steps {
sh './gradlew :1.9.4:setupCiWorkspace'
sh './gradlew :1.9.4:clean'
sh './gradlew :1.9.4:build'
archiveArtifacts artifacts: '1.9.4/build/libs/*jar'
sh './gradlew :1.10.2:setupCiWorkspace'
sh './gradlew :1.10.2:clean'
sh './gradlew :1.10.2:build'
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") {

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.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
@ -163,13 +167,6 @@ matterbridge
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
```bash
git clone --recursive https://git.lain.faith/sorceress/MatterLink.git
cd MatterLink/
./gradlew setupDecompWorkspace
./gradlew build
```
powered by wishful thinking

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

View File

@ -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()
}
dependencies {
classpath group: "org.jetbrains.kotlin", name: "kotlin-gradle-plugin", version: kotlinVersion
classpath group: "com.github.jengelman.gradle.plugins", name: "shadow", version: shadowVersion
classpath group: 'com.vanniktech', name: 'gradle-dependency-graph-generator-plugin', version: '0.5.0'
// classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath group: "com.github.jengelman.gradle.plugins", name: "shadow", version: shadow_version
}
}
apply plugin: 'kotlin'
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.
dependencies {
compile project(":Jankson")
shadow(project(':Jankson')) { transitive = false }
compile project(':api')
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.github.kittinunf.Fuel", name: "fuel", version: fuelVersion)
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.apache.httpcomponents', name: 'httpclient', version: '+'
// compile(group: "org.jetbrains.kotlinx", name: "kotlinx-coroutines-core", version: coroutinesVersion)
// 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
compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: project.kotlin_version
}
compileKotlin {
@ -47,12 +37,10 @@ compileKotlin {
}
shadowJar {
classifier = ''
classifier ''
relocate 'blue.endless', 'matterlink.repack.blue.endless'
relocate 'com.github', 'matterlink.repack.com.github'
relocate 'kotlinx', 'matterlink.repack.kotlinx'
configurations = [project.configurations.shadow]
dependencies {
include project(":api")
include project(":Jankson")
}
}
//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.config.cfg
import matterlink.update.UpdateChecker
import java.util.UUID
lateinit var logger: Logger
import org.apache.logging.log4j.Logger
import java.util.*
lateinit var instance: IMatterLink
lateinit var logger: Logger
abstract class IMatterLink {
abstract val mcVersion: String
@ -18,11 +18,7 @@ abstract class IMatterLink {
abstract val buildNumber: Int
abstract val forgeVersion: String
abstract fun commandSenderFor(
user: String,
env: IBridgeCommand.CommandEnvironment,
op: Boolean
): IMinecraftCommandSender
abstract fun commandSenderFor(user: String, env: IBridgeCommand.CommandEnvironment, op: Boolean): IMinecraftCommandSender
abstract fun wrappedSendToPlayers(msg: String)
@ -32,19 +28,46 @@ abstract class IMatterLink {
abstract fun nameToUUID(username: String): UUID?
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
serverStartTime = System.currentTimeMillis()
if (cfg.connect.autoConnect)
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")
}
// 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
*/
@ -75,6 +98,4 @@ abstract class IMatterLink {
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
import blue.endless.jankson.Jankson
import blue.endless.jankson.JsonObject
import blue.endless.jankson.impl.Marshaller
import java.net.HttpURLConnection
import java.net.URL

View File

@ -8,7 +8,9 @@ import blue.endless.jankson.impl.Marshaller
import matterlink.config.cfg
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.Thread.yield
import java.util.*
import kotlin.streams.asSequence
private const val ZWSP: Char = '\u200b'
@ -65,41 +67,24 @@ fun randomString(length: Int = 6): String =
java.util.UUID.randomUUID().toString().replace("-", "").take(length)
fun <T : Any> JsonObject.getOrDefault(key: String, default: T, comment: String? = null): T {
logger.trace("type: ${default.javaClass.name} key: $key json: >>>${this.getObject(key)?.toJson()}<<< default: $default")
return putDefault(key, default, comment)!!.also {
setComment(key, comment)
}
// logger.info("type: ${default.javaClass.name} key: $key json: >>>${this.getObject(key)?.toJson()}<<< default: $default")
return putDefault(key, default, comment)!!
}
inline fun <reified T : Any> Jankson.fromJson(obj: JsonObject): T = this.fromJson(obj, T::class.java)
inline fun <reified T : Any> Jankson.fromJson(json: String): T = this.fromJson(json, T::class.java)
inline fun <reified T : Any> Jankson.Builder.registerTypeAdapter(noinline adapter: (JsonObject) -> T) =
this.registerTypeAdapter(T::class.java, adapter)
inline fun <reified T : Any> Jankson.Builder.registerTypeAdapter(noinline adapter: (JsonObject) -> T) = this.registerTypeAdapter(T::class.java, adapter)
inline fun <reified T : Any> Jankson.Builder.registerPrimitiveTypeAdapter(noinline adapter: (Any) -> T) =
this.registerPrimitiveTypeAdapter(T::class.java, adapter)
inline fun <reified T : Any> Jankson.Builder.registerPrimitiveTypeAdapter(noinline adapter: (Any) -> T) = this.registerPrimitiveTypeAdapter(T::class.java, adapter)
inline fun <reified T : Any> Jankson.Builder.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) =
this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> Jankson.Builder.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T) -> JsonElement) =
this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T) -> JsonElement) = this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) =
this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> Marshaller.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = this.registerSerializer(T::class.java, serializer)
inline fun <reified T : Any> JsonObject.getReified(key: String, comment: String? = null): T? =
this.get(T::class.java, key)
?.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.getReified(key: String): T? = this.get(T::class.java, key)
inline fun <reified T : Any> JsonObject.getList(key: String): List<T>? {
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
import matterlink.Paste
import matterlink.PasteSection
import matterlink.PasteUtil
import matterlink.antiping
import matterlink.*
import matterlink.api.ApiMessage
import matterlink.api.MessageHandler
import matterlink.config.cfg
import matterlink.handlers.ChatEvent
import matterlink.handlers.LocationHandler
import matterlink.mapFormat
import matterlink.stackTraceString
object MessageHandlerInst : MessageHandler() {
override suspend fun transmit(msg: ApiMessage) {
override fun transmit(msg: ApiMessage) {
transmit(msg, cause = "")
}
override suspend fun sendStatusUpdate(message: String) {
LocationHandler.sendToLocations(
msg = message,
x = 0, y = 0, z = 0, dimension = null,
systemuser = true,
event = ChatEvent.STATUS,
cause = "status update message"
)
}
suspend fun transmit(msg: ApiMessage, cause: String, maxLines: Int = cfg.outgoing.inlineLimit) {
fun transmit(msg: ApiMessage, cause: String, maxLines: Int = cfg.outgoing.inlineLimit) {
if (msg.username.isEmpty()) {
msg.username = cfg.outgoing.systemUser
if (msg.avatar.isEmpty() && cfg.outgoing.avatar.enable) {
if(msg.avatar.isEmpty() && cfg.outgoing.avatar.enable) {
msg.avatar = cfg.outgoing.avatar.systemUserAvatar
}
}
if (msg.gateway.isEmpty()) {
logger.error("dropped message '$msg' due to missing gateway")
return
}
if (msg.gateway.isEmpty())
msg.gateway = cfg.connect.gateway
if (msg.text.lines().count() >= maxLines) {
try {
@ -56,8 +37,7 @@ object MessageHandlerInst : MessageHandler() {
)
msg.text = msg.text.substringBefore('\n')
.take(25) + "... " + response.link
} catch (e: Exception) {
logger.error(cause)
} catch(e: Exception) {
logger.error(e.stackTraceString)
}
}

View File

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

View File

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

View File

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

View File

@ -1,13 +1,16 @@
package matterlink.bridge.command
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg
import matterlink.stripColorOut
object HelpCommand : IBridgeCommand() {
override val help: String = "Returns the help string for the given command. Syntax: help <command>"
override val permLevel: Double
get() = cfg.command.defaultPermUnauthenticated
override 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 {
args.isEmpty() ->
"Available commands: ${BridgeCommandRegistry.getCommandList(IBridgeCommand.getPermLevel(env.uuid))}"

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@ object CommandCoreML {
val usage = "ml <connect|disconnect|reload|permAccept>"
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()
return when (cmd) {
@ -45,10 +45,10 @@ object CommandCoreML {
return "no code passed"
}
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 powerLevel = powerLevelArg ?: run { return "permLevel cannot be parsed: ${args.getOrNull(3)}" }
val powerLevelArg = args.getOrNull(2)?.toDoubleOrNull()
val powerLevel = powerLevelArg ?: run { return "permLevel cannot be parsed" }
?: request.powerlevel
?: return "no permLevel provided"
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.impl.Marshaller
import blue.endless.jankson.impl.SyntaxError
import matterlink.Area
import matterlink.bridge.MessageHandlerInst
import matterlink.getOrDefault
import matterlink.getOrPutList
import matterlink.getReifiedOrDelete
import matterlink.logger
import matterlink.registerSerializer
import matterlink.registerTypeAdapter
import matterlink.stackTraceString
import java.io.File
@ -24,88 +20,18 @@ data class BaseConfig(val rootDir: File) {
val configFile: File = cfgDirectory.resolve("matterlink.hjson")
init {
logger.info("Reading bridge blueprints... from $rootDir")
logger.info("Reading bridge blueprints... from {}", rootDir)
baseCfg = this
}
data class MatterLinkConfig(
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 outgoing: OutgoingOptions = OutgoingOptions(),
val command: CommandOptions = CommandOptions(),
val update: UpdateOptions = UpdateOptions()
)
data class DefaultSettingsOutgoing(
val plain: Boolean = true,
val action: Boolean = true,
val join: Boolean = false,
val leave: Boolean = false,
val advancement: Boolean = false,
val death: Boolean = false,
val broadcast: Boolean = false,
val status: Boolean = false
)
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(
val prefix: Char = '!',
@ -119,6 +45,7 @@ data class BaseConfig(val rootDir: File) {
data class ConnectOptions(
val url: String = "http://localhost:4242",
val authToken: String = "",
val gateway: String = "minecraft",
val autoConnect: Boolean = true,
val reconnectWait: Long = 500
)
@ -177,30 +104,21 @@ data class BaseConfig(val rootDir: File) {
"thrown" to arrayOf("彡°"),
"thorns" to arrayOf("\uD83C\uDF39"), //🌹
"explosion" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5"), //💣 💥
"explosion.player" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5"), //💣 💥
"ieWireShock" to arrayOf("\uD83D\uDD0C", "\u26A1"), //🔌 ⚡
"immersiverailroading:hitByTrain" to arrayOf(
"\uD83D\uDE82",
"\uD83D\uDE83",
"\uD83D\uDE84",
"\uD83D\uDE85",
"\uD83D\uDE87",
"\uD83D\uDE88",
"\uD83D\uDE8A"
) //🚂 🚃 🚄 🚅 🚇 🚈 🚊
"explosion.player" to arrayOf("\uD83D\uDCA3", "\uD83D\uDCA5") //💣 💥
)
)
data class AvatarOptions(
val enable: Boolean = true,
val urlTemplate: String = "https://mc-heads.net/head/{uuid}",
val systemUserAvatar: String = ""
val urlTemplate: String = "https://visage.surgeplay.com/head/512/{uuid}",
// https://www.freepik.com/free-icon/right-arrow-angle-and-horizontal-down-line-code-signs_732795.htm
val systemUserAvatar: String = "https://image.freepik.com/free-icon/right-arrow-angle-and-horizontal-down-line-code-signs_318-53994.jpg"
)
data class JoinPartOptions(
val enable: Boolean = true,
val joinServer: String = "{username:antiping} has connected to the server",
val partServer: String = "{username:antiping} has disconnected from the server"
val joinServer: String = "{username:antiping} has connected to the platform",
val partServer: String = "{username:antiping} has disconnected from the platform"
)
data class UpdateOptions(
@ -211,36 +129,20 @@ data class BaseConfig(val rootDir: File) {
val jankson = Jankson
.builder()
.registerTypeAdapter {
with(MatterLinkConfig()) {
MatterLinkConfig(
command = it.getOrDefault(
"command",
command,
CommandOptions(),
"User commands"
),
outgoingDefaults = it.getOrDefault(
"outgoingDefaults",
outgoingDefaults,
"default settings for outgoing"
),
incomingDefaults = it.getOrDefault(
"incomingDefaults",
incomingDefaults,
"default settings for incoming"
),
locations = it.getOrPutList(
"locations",
locations,
"list of fixed chat locations"
),
connect = it.getOrDefault(
"connect",
connect,
ConnectOptions(),
"Connection Settings"
),
incoming = it.getOrDefault(
"incoming",
incoming,
IncomingOptions(),
"""
Gateway -> Server
Options all about receiving messages from the API
@ -250,7 +152,7 @@ data class BaseConfig(val rootDir: File) {
),
outgoing = it.getOrDefault(
"outgoing",
outgoing,
OutgoingOptions(),
"""
Server -> Gateway
Options all about sending messages to the API
@ -258,146 +160,11 @@ data class BaseConfig(val rootDir: File) {
),
update = it.getOrDefault(
"update",
update,
UpdateOptions(),
"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 {
with(CommandOptions()) {
CommandOptions(
@ -447,15 +214,15 @@ data class BaseConfig(val rootDir: File) {
authToken,
"Auth token used to connect to the bridge platform"
),
gateway = it.getOrDefault(
"gateway",
gateway,
"MatterBridge gateway"
),
autoConnect = it.getOrDefault(
"autoConnect",
autoConnect,
"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",
"Damage type mapping for death cause"
)
it.mapValues { (key, _) ->
it.mapValues { (key, element) ->
it.getOrDefault(key, damageTypeMapping[key] ?: emptyArray(), key)
.apply { it[key] }.apply {
jsonObj["damageTypeMapping"] = it
@ -584,11 +351,6 @@ data class BaseConfig(val rootDir: File) {
"urlTemplate",
urlTemplate,
"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 ->
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()!!
.build()
}
fun load(): MatterLinkConfig {
@ -686,15 +417,15 @@ data class BaseConfig(val rootDir: File) {
logger.error("error parsing config: ${e.completeMessage} ")
logger.error(e.stackTraceString)
cfgDirectory.resolve("error.matterlink.hjson").writeText(jsonObject.toJson(false, true))
if (::cfg.isInitialized) cfg else MatterLinkConfig()
MatterLinkConfig()
} catch (e: IllegalStateException) {
logger.error(e.stackTraceString)
cfgDirectory.resolve("error.matterlink.hjson").writeText(jsonObject.toJson(false, true))
if (::cfg.isInitialized) cfg else MatterLinkConfig()
MatterLinkConfig()
} catch (e: NullPointerException) {
logger.error("error loading config: ${e.stackTraceString}")
cfgDirectory.resolve("error.matterlink.hjson").writeText(jsonObject.toJson(false, true))
if (::cfg.isInitialized) cfg else MatterLinkConfig()
MatterLinkConfig()
}
// val defaultJsonObject = jankson.load("{}")
@ -703,6 +434,7 @@ data class BaseConfig(val rootDir: File) {
MessageHandlerInst.config.url = tmpCfg.connect.url
MessageHandlerInst.config.token = tmpCfg.connect.authToken
MessageHandlerInst.config.gateway = tmpCfg.connect.gateway
MessageHandlerInst.config.reconnectWait = tmpCfg.connect.reconnectWait
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.JsonPrimitive
import blue.endless.jankson.impl.SyntaxError
import matterlink.*
import matterlink.bridge.command.CommandType
import matterlink.bridge.command.CustomCommand
import matterlink.getOrDefault
import matterlink.logger
import matterlink.registerPrimitiveTypeAdapter
import matterlink.registerTypeAdapter
import java.io.File
import java.io.FileNotFoundException

View File

@ -11,7 +11,7 @@ import matterlink.logger
import matterlink.stackTraceString
import java.io.File
import java.io.FileNotFoundException
import java.util.UUID
import java.util.*
import java.util.concurrent.TimeUnit
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.CacheBuilder
import matterlink.getReified
import matterlink.instance
import matterlink.logger
import java.io.File
import java.io.FileNotFoundException
import java.util.UUID
import java.util.*
import java.util.concurrent.TimeUnit
typealias PermissionMap = Map<String, Double>

View File

@ -1,38 +1,35 @@
package matterlink.handlers
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.bridge.command.BridgeCommandRegistry
import matterlink.config.cfg
import matterlink.instance
import matterlink.logger
import java.util.UUID
import matterlink.stripColorOut
import java.util.*
object ChatProcessor {
/**
* @return cancel message flag
*/
suspend fun sendToBridge(
user: String,
msg: String,
x: Int,
y: Int,
z: Int,
dimension: Int?,
event: ChatEvent,
uuid: UUID? = null
): Boolean {
//TODO: pass message to Locations
logger.info("position: $x $y $z dimension: $dimension")
fun sendToBridge(user: String, msg: String, event: String, uuid: UUID? = null): Boolean {
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 {
message.isNotBlank() -> LocationHandler.sendToLocations(
user = user,
msg = message,
x = x, y = y, z = z, dimension = dimension,
event = event,
cause = "Message from $user",
uuid = uuid
message.isNotBlank() -> MessageHandlerInst.transmit(
ApiMessage(
username = user.stripColorOut,
text = message.stripColorOut,
event = event
).apply {
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'")
}
return false

View File

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

View File

@ -1,16 +1,15 @@
package matterlink.handlers
import matterlink.antiping
import matterlink.api.ApiMessage
import matterlink.api.ApiMessage.Companion.JOIN_LEAVE
import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg
import matterlink.mapFormat
import matterlink.stripColorOut
object JoinLeaveHandler {
suspend fun handleJoin(
player: String,
x: Int, y: Int, z: Int,
dimension: Int
) {
fun handleJoin(player: String) {
if (cfg.outgoing.joinPart.enable) {
val msg = cfg.outgoing.joinPart.joinServer.mapFormat(
mapOf(
@ -18,21 +17,17 @@ object JoinLeaveHandler {
"{username:antiping}" to player.stripColorOut.antiping
)
)
LocationHandler.sendToLocations(
msg = msg,
x = x, y = y, z = z, dimension = dimension,
event = ChatEvent.JOIN,
systemuser = true,
MessageHandlerInst.transmit(
ApiMessage(
text = msg,
event = JOIN_LEAVE
),
cause = "$player joined"
)
}
}
suspend fun handleLeave(
player: String,
x: Int, y: Int, z: Int,
dimension: Int
) {
fun handleLeave(player: String) {
if (cfg.outgoing.joinPart.enable) {
val msg = cfg.outgoing.joinPart.partServer.mapFormat(
mapOf(
@ -40,11 +35,11 @@ object JoinLeaveHandler {
"{username:antiping}" to player.stripColorOut.antiping
)
)
LocationHandler.sendToLocations(
msg = msg,
x = x, y = y, z = z, dimension = dimension,
event = ChatEvent.JOIN,
systemuser = true,
MessageHandlerInst.transmit(
ApiMessage(
text = msg,
event = JOIN_LEAVE
),
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
import matterlink.antiping
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg
import matterlink.stripColorOut
object ProgressHandler {
suspend fun handleProgress(
name: String, message: String, display: String,
x: Int, y: Int, z: Int,
dimension: Int
) {
fun handleProgress(name: String, message: String, display: String) {
if (!cfg.outgoing.advancements) return
val usr = name.stripColorOut.antiping
LocationHandler.sendToLocations(
msg = "$usr $message $display".stripColorOut,
x = x, y = y, z = z, dimension = dimension,
event = ChatEvent.ADVANCEMENT,
cause = "Progress Event by $usr",
systemuser = true
MessageHandlerInst.transmit(
ApiMessage(
text = "$usr $message $display".stripColorOut
),
cause = "Progress Event by $usr"
)
}
}

View File

@ -1,80 +1,26 @@
package matterlink.handlers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.bridge.command.BridgeCommandRegistry
import matterlink.bridge.format
import matterlink.config.cfg
import matterlink.instance
import matterlink.logger
import java.util.UUID
import matterlink.stripColorIn
object ServerChatHandler {
@UseExperimental(ExperimentalCoroutinesApi::class)
val rcvChannel = MessageHandlerInst.broadcast.openSubscription()
/**
* This method must be called every server tick with no arguments.
*/
suspend fun writeIncomingToChat() {
val nextMessage = rcvChannel.poll() ?: return
val defaults = cfg.incomingDefaults
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))
}
fun writeIncomingToChat() {
if (MessageHandlerInst.queue.isNotEmpty())
logger.debug("incoming: " + MessageHandlerInst.queue.toString())
val nextMessage = MessageHandlerInst.queue.poll() ?: null
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)
"" -> {
@ -95,38 +41,8 @@ object ServerChatHandler {
return
}
}
targetUUIDs.forEach {
//TODO: optimize send to all at once
instance.wrappedSendToPlayer(it, message)
instance.wrappedSendToPlayers(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
import matterlink.bridge.MessageHandlerInst
import matterlink.update.UpdateChecker
/**
@ -12,17 +13,17 @@ object TickHandler {
private set
private var accumulator = 0
private const val updateInterval = 12 * 60 * 60 * 20
suspend fun handleTick() {
fun handleTick() {
tickCounter++
// if (tickCounter % 100 == 0) {
// MessageHandlerInst.checkConnection()
// }
if (tickCounter % 100 == 0) {
MessageHandlerInst.checkConnection()
}
ServerChatHandler.writeIncomingToChat()
if (accumulator++ > updateInterval) {
accumulator -= updateInterval
UpdateChecker.check()
UpdateChecker.run()
}
}
}

View File

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

View File

@ -1,11 +1,11 @@
package matterlink.jenkins
import com.github.kittinunf.fuel.httpGet
import com.github.kittinunf.fuel.serialization.kotlinxDeserializerOf
import com.github.kittinunf.result.Result
import kotlinx.serialization.json.JSON
import matterlink.logger
import com.google.gson.Gson
import matterlink.*
import matterlink.Result
import matterlink.header
import matterlink.httpGet
import matterlink.responseString
/**
* Created by nikky on 03/02/18.
@ -21,10 +21,10 @@ data class Build(
val (request, response, result) = "$url/api/json"
.httpGet()
.header("User-Agent" to userAgent)
.responseObject(kotlinxDeserializerOf(loader = BuildWithDetails.serializer(), json = JSON.nonstrict))
return when (result) {
.responseString()
return when(result) {
is Result.Success -> {
result.value
gson.fromJson(result.value, BuildWithDetails::class.java)
}
is Result.Failure -> {
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
import kotlinx.serialization.Serializable
import java.util.Date
import java.util.*
@Serializable
//@JsonIgnoreProperties(ignoreUnknown = true)
data class BuildWithDetails(
val number: Int,
val url: String,
val artifacts: List<Artifact>,
// @JsonFormat(shape=JsonFormat.Shape.NUMBER, pattern="s")
val timestamp: Date
)

View File

@ -1,10 +1,12 @@
package matterlink.jenkins
package voodoo.util.jenkins
import com.github.kittinunf.fuel.httpGet
import com.github.kittinunf.fuel.serialization.kotlinxDeserializerOf
import com.github.kittinunf.result.Result
import kotlinx.serialization.json.JSON
import com.google.gson.Gson
import matterlink.Result
import matterlink.header
import matterlink.httpGet
import matterlink.responseString
import matterlink.jenkins.Job
import matterlink.logger
/**
@ -22,10 +24,10 @@ class JenkinsServer(val url: String) {
val (_, _, result) = requestURL
.httpGet()
.header("User-Agent" to userAgent)
.responseObject(kotlinxDeserializerOf(loader = Job.serializer(), json = JSON.nonstrict))
.responseString()
return when (result) {
is Result.Success -> {
result.value
gson.fromJson(result.value, Job::class.java)
}
is Result.Failure -> {
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
import kotlinx.serialization.Serializable
package matterlink.jenkins;
/**
* Created by nikky on 03/02/18.
* @author Nikky
*/
@Serializable
//@JsonIgnoreProperties(ignoreUnknown = true)
data class Job(
val url: String,
val name: String,

View File

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

View File

@ -1,33 +1,31 @@
package matterlink.update
import com.github.kittinunf.fuel.core.extensions.cUrlString
import com.github.kittinunf.fuel.httpGet
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.serialization.list
import com.google.gson.FieldNamingPolicy
import com.google.gson.GsonBuilder
import matterlink.api.ApiMessage
import matterlink.bridge.MessageHandlerInst
import matterlink.config.cfg
import matterlink.handlers.ChatEvent
import matterlink.handlers.LocationHandler
import matterlink.instance
import matterlink.jenkins.JenkinsServer
import matterlink.logger
import com.github.kittinunf.fuel.serialization.kotlinxDeserializerOf
import com.github.kittinunf.result.Result
import kotlinx.serialization.json.JSON
import voodoo.util.jenkins.JenkinsServer
import java.io.BufferedReader
import java.net.HttpURLConnection
import java.net.URL
object UpdateChecker : CoroutineScope {
override val coroutineContext = Job() + CoroutineName("UpdateChacker")
suspend fun check() {
class UpdateChecker : Thread() {
companion object {
fun run() {
if (cfg.update.enable) {
run()
UpdateChecker().start()
}
}
}
private suspend fun run() {
init {
name = "UpdateCheckerThread"
}
override fun run() {
if (instance.buildNumber > 0) {
val server = JenkinsServer("https://ci.elytradev.com")
val job = server.getJob("elytra/MatterLink/master", "MatterLink/${instance.modVersion}")
@ -44,12 +42,11 @@ object UpdateChecker : CoroutineScope {
when {
number > instance.buildNumber -> {
logger.warn("Mod out of date! New build $number available at $url")
val difference = number - instance.buildNumber
LocationHandler.sendToLocations(
msg = "MatterLink out of date! You are $difference builds behind! Please download new version from $url",
x = 0, y = 0, z = 0, dimension = null,
event = ChatEvent.STATUS,
cause = "MatterLink update notice"
val difference = number - build.number
MessageHandlerInst.transmit(
ApiMessage(
text = "MatterLink out of date! You are $difference builds behind! Please download new version from $url"
)
)
}
number < instance.buildNumber -> logger.error("lastSuccessfulBuild: $number is older than installed build: ${instance.buildNumber}")
@ -63,30 +60,40 @@ object UpdateChecker : CoroutineScope {
return
}
val gson = GsonBuilder()
// .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.create()
logger.info("Checking for new versions...")
val (request, response, result) = with(instance) {
val useragent =
"MatterLink/$modVersion MinecraftForge/$mcVersion-$forgeVersion (https://github.com/elytra/MatterLink)"
val url = URL("https://staging_cursemeta.dries007.net/api/v3/direct/addon/287323/files")
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'")
"https://curse.nikky.moe/api/addon/287323/files".httpGet()
.header("User-Agent" to useragent)
.responseObject(kotlinxDeserializerOf(loader = CurseFile.serializer().list, json = JSON.nonstrict))
con.setRequestProperty("User-Agent", useragent)
}
val apiUpdateList = when(result) {
is Result.Success -> {
result.value
con.connect()
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("cUrl: ${request.cUrlString()}")
logger.error("request: $request")
logger.error("response: $response")
return
}
}
.filter { it.fileStatus == "SemiNormal" && it.gameVersion.contains(instance.mcVersion) }
val modVersionChunks = instance.modVersion
.substringBefore("-dev")
@ -124,13 +131,13 @@ object UpdateChecker : CoroutineScope {
logger.info("Matterlink out of date! You are $count $version behind")
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(
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
modVersion = 1.6.4
forgelinVersion = 1.8.2
kotlinVersion = 1.3.10
coroutinesVersion = 1.0.1
serializationVersion = 0.9.1
shadowVersion = 2.0.2
fuelVersion = 8690665998
resultVersion = 2.0.0
cursegradleVersion = 1.1.2
curseId = 287323
curseReleaseType = beta
mod_name = MatterLink
mod_version = 1.6.3
forgelin_version = 1.6.0
kotlin_version = 1.2.41
shadow_version = 2.0.2
cursegradle_version = 1.0.10
curse_id = 287323
curse_release_type = 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'
include 'core'
include 'core', 'api'
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'