diff --git a/.gitignore b/.gitignore index c91c99e..c234125 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,5 @@ run/ \.floo \.flooignore + +test\.sh diff --git a/.gitmodules b/.gitmodules index 19ce018..5454f86 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "api"] - path = api - url = https://github.com/NikkyAI/MatterLinkApi.git [submodule "Jankson"] path = Jankson - url = https://github.com/falkreon/Jankson.git + url = https://github.com/falkreon/Jankson.git \ No newline at end of file diff --git a/1.10.2/build.gradle b/1.10.2/build.gradle index 52ab88c..feed098 100644 --- a/1.10.2/build.gradle +++ b/1.10.2/build.gradle @@ -34,14 +34,17 @@ dependencies { shadowJar { classifier '' - relocate 'blue.endless', 'matterlink.repack.blue.endless' - dependencies { - include project(':core') - include project(':api') - include project(':Jankson') + include project(":core") + include project(":api") + include project(":Jankson") + include dependency(group: 'com.elytradev.concrete', name: 'concrete-common', version: concrete_version) + include dependency(group: 'com.elytradev.concrete', name: 'concrete-rulesengine', version: concrete_version) } + relocate 'blue.endless', 'matterlink.repack.blue.endless' + relocate 'com.elytradev.concrete', 'matterlink.repack.com.elytradev.concrete' + exclude 'dummyThing' } diff --git a/1.11.2/build.gradle b/1.11.2/build.gradle index cb8497c..0b8c3ff 100644 --- a/1.11.2/build.gradle +++ b/1.11.2/build.gradle @@ -39,14 +39,17 @@ dependencies { shadowJar { classifier = '' - relocate 'blue.endless', 'matterlink.repack.blue.endless' - dependencies { - include project(':core') - include project(':api') - include project(':Jankson') + include project(":core") + include project(":api") + include project(":Jankson") + include dependency(group: 'com.elytradev.concrete', name: 'concrete-common', version: concrete_version) + include dependency(group: 'com.elytradev.concrete', name: 'concrete-rulesengine', version: concrete_version) } + relocate 'blue.endless', 'matterlink.repack.blue.endless' + relocate 'com.elytradev.concrete', 'matterlink.repack.com.elytradev.concrete' + exclude 'dummyThing' } diff --git a/1.12.2/build.gradle b/1.12.2/build.gradle index 787ddd3..d967da4 100644 --- a/1.12.2/build.gradle +++ b/1.12.2/build.gradle @@ -37,14 +37,17 @@ dependencies { shadowJar { classifier = '' - relocate 'blue.endless', 'matterlink.repack.blue.endless' - dependencies { - include project(':core') - include project(':api') - include project(':Jankson') + include project(":core") + include project(":api") + include project(":Jankson") + include dependency(group: 'com.elytradev.concrete', name: 'concrete-common', version: concrete_version) + include dependency(group: 'com.elytradev.concrete', name: 'concrete-rulesengine', version: concrete_version) } + relocate 'blue.endless', 'matterlink.repack.blue.endless' + relocate 'com.elytradev.concrete', 'matterlink.repack.com.elytradev.concrete' + exclude 'dummyThing' } diff --git a/1.12.2/src/main/kotlin/matterlink/EventHandler.kt b/1.12.2/src/main/kotlin/matterlink/EventHandler.kt index ffdf3da..791b1e1 100644 --- a/1.12.2/src/main/kotlin/matterlink/EventHandler.kt +++ b/1.12.2/src/main/kotlin/matterlink/EventHandler.kt @@ -1,6 +1,5 @@ package matterlink -import matterlink.api.ApiMessage.Companion.USER_ACTION import matterlink.config.cfg import matterlink.handlers.* import net.minecraft.command.server.CommandBroadcast @@ -28,7 +27,11 @@ object EventHandler { ProgressHandler.handleProgress( name = e.entityPlayer.displayName.unformattedText, message = "has made the advancement", - display = e.advancement.displayText.unformattedText + display = e.advancement.displayText.unformattedText, + x = e.entityPlayer.posX.toInt(), + y = e.entityPlayer.posY.toInt(), + z = e.entityPlayer.posZ.toInt(), + dimension = e.entityPlayer.dimension ) } @@ -36,11 +39,15 @@ object EventHandler { @SubscribeEvent @JvmStatic fun chatEvent(e: ServerChatEvent) { - if(e.isCanceled) return + if (e.isCanceled) return e.isCanceled = ChatProcessor.sendToBridge( user = e.player.displayName.unformattedText, msg = e.message, - event = "", + 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 ) } @@ -49,18 +56,31 @@ object EventHandler { @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.equals("me", true) -> USER_ACTION - e.command is CommandBroadcast -> "" - else -> return + 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 + } } - ChatProcessor.sendToBridge(user = sender, msg = args, event = type) + ChatProcessor.sendToBridge( + user = sender, + msg = args, + event = type, + x = e.sender.position.x, + y = e.sender.position.y, + z = e.sender.position.z, + dimension = when { + e.sender is DedicatedServer -> null + else -> e.sender.commandSenderEntity?.dimension ?: e.sender.entityWorld.provider.dimension + } + ) } //FORGE-DEPENDENT @@ -71,7 +91,11 @@ object EventHandler { DeathHandler.handleDeath( player = e.entityLiving.displayName.unformattedText, deathMessage = e.entityLiving.combatTracker.deathMessage.unformattedText, - damageType = e.source.damageType + damageType = e.source.damageType, + x = e.entityLiving.posX.toInt(), + y = e.entityLiving.posY.toInt(), + z = e.entityLiving.posZ.toInt(), + dimension = e.entityLiving.dimension ) } } @@ -80,14 +104,26 @@ object EventHandler { @SubscribeEvent @JvmStatic fun joinEvent(e: PlayerEvent.PlayerLoggedInEvent) { - JoinLeaveHandler.handleJoin(e.player.displayName.unformattedText) + 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 @JvmStatic fun leaveEvent(e: PlayerEvent.PlayerLoggedOutEvent) { - JoinLeaveHandler.handleLeave(e.player.displayName.unformattedText) + 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 diff --git a/1.12.2/src/main/kotlin/matterlink/MatterLink.kt b/1.12.2/src/main/kotlin/matterlink/MatterLink.kt index 06e8409..70078bc 100644 --- a/1.12.2/src/main/kotlin/matterlink/MatterLink.kt +++ b/1.12.2/src/main/kotlin/matterlink/MatterLink.kt @@ -9,7 +9,9 @@ import matterlink.command.MatterLinkCommandSender import matterlink.config.BaseConfig import matterlink.config.cfg import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.server.MinecraftServer import net.minecraft.util.text.TextComponentString +import net.minecraftforge.common.DimensionManager import net.minecraftforge.common.ForgeVersion import net.minecraftforge.fml.common.FMLCommonHandler import net.minecraftforge.fml.common.Mod @@ -111,6 +113,14 @@ object MatterLink : IMatterLink() { null } + override fun collectPlayers(area: Area): Set { + 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 diff --git a/Jankson b/Jankson index 93ab86e..69564c3 160000 --- a/Jankson +++ b/Jankson @@ -1 +1 @@ -Subproject commit 93ab86ee821380584c22ac60d77737388976e531 +Subproject commit 69564c34e706f5243d8ae212fb58bb982db01a81 diff --git a/api b/api deleted file mode 160000 index 75138ce..0000000 --- a/api +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 75138cec00c66d479709f4b44d78ca3005993474 diff --git a/api/.gitignore b/api/.gitignore new file mode 100644 index 0000000..81ee27f --- /dev/null +++ b/api/.gitignore @@ -0,0 +1,5 @@ +\.idea/ +\.gradle/ +out/ +build/ +run/ diff --git a/api/LICENSE.md b/api/LICENSE.md new file mode 100644 index 0000000..e1c3eab --- /dev/null +++ b/api/LICENSE.md @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/api/README.md b/api/README.md new file mode 100644 index 0000000..805447c --- /dev/null +++ b/api/README.md @@ -0,0 +1 @@ +MatterLinkApi diff --git a/api/build.gradle b/api/build.gradle new file mode 100644 index 0000000..4519178 --- /dev/null +++ b/api/build.gradle @@ -0,0 +1,49 @@ +buildscript { + ext.kotlin_version = '1.2.41' + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} +group 'moe.nikky' +version '1.0-SNAPSHOT' + +apply plugin: 'kotlin' +apply plugin: 'java' +apply plugin: 'idea' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +compileKotlin { + kotlinOptions { + languageVersion = "1.2" + jvmTarget = "1.8" + } +} + +compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + // https://mvnrepository.com/artifact/com.google.code.gson/gson + compile group: 'com.google.code.gson', name: 'gson', version: '+' + compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '+' + + testCompile group: 'junit', name: 'junit', version: '4.12' + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" +} + +idea { + module { + excludeDirs += [file("run")] + } +} diff --git a/api/gradle/wrapper/gradle-wrapper.jar b/api/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..01b8bf6 Binary files /dev/null and b/api/gradle/wrapper/gradle-wrapper.jar differ diff --git a/api/gradle/wrapper/gradle-wrapper.properties b/api/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..933b647 --- /dev/null +++ b/api/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip diff --git a/api/gradlew b/api/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/api/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/api/gradlew.bat b/api/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/api/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/api/settings.gradle b/api/settings.gradle new file mode 100644 index 0000000..a9516ee --- /dev/null +++ b/api/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'MatterLinkApi' + diff --git a/api/src/main/java/matterlink/api/ApiMessage.kt b/api/src/main/java/matterlink/api/ApiMessage.kt new file mode 100644 index 0000000..53ea19b --- /dev/null +++ b/api/src/main/java/matterlink/api/ApiMessage.kt @@ -0,0 +1,92 @@ +package matterlink.api + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName + +/** + * Created by nikky on 07/05/18. + * + * @author Nikky + * @version 1.0 + */ +class ApiMessage ( + username: String? = null, + text: String? = null, + gateway: String? = null, + channel: String? = null, + userid: String? = null, + avatar: String? = null, + account: String? = null, + protocol: String? = null, + event: String? = null, + id: String? = null +) { + @SerializedName("username") private var _username: String? = username + @SerializedName("text") private var _text: String? = text + @SerializedName("gateway") private var _gateway: String? = gateway + @SerializedName("channel") private var _channel: String? = channel + @SerializedName("userid") private var _userid: String? = userid + @SerializedName("avatar") private var _avatar: String? = avatar + @SerializedName("account") private var _account: String? = account + @SerializedName("protocol") private var _protocol: String? = protocol + @SerializedName("event") private var _event: String? = event + @SerializedName("id") private var _id: String? = id + + var username: String + get() = _username ?: "" + set(username) { this._username = username } + + var text: String + get() = _text ?: "" + set(text) { this._text = text } + + var gateway: String + get() = _gateway ?: "" + set(gateway) { this._gateway = gateway } + + var channel: String + get() = _channel ?: "" + set(channel) { this._channel = channel } + + var userid: String + get() = _userid ?: "" + set(userid) { this._userid = userid } + + var avatar: String + get() = _avatar ?: "" + set(avatar) { this._avatar = avatar } + + var account: String + get() = _account ?: "" + set(account) { this._account = account } + + var protocol: String + get() = _protocol ?: "" + set(protocol) { this._protocol = protocol } + + var event: String + get() = _event ?: "" + set(event) { this._event = event } + + var id: String + get() = _id ?: "" + set(id) { this._id = id } + + fun encode(): String { + return gson.toJson(this) + } + + override fun toString(): String = encode() + + companion object { + val USER_ACTION = "user_action" + val JOIN_LEAVE = "join_leave" + + private val gson = GsonBuilder() + .create() + + fun decode(json: String): ApiMessage { + return gson.fromJson(json, ApiMessage::class.java) + } + } +} diff --git a/api/src/main/java/matterlink/api/Config.kt b/api/src/main/java/matterlink/api/Config.kt new file mode 100644 index 0000000..cc9d7fa --- /dev/null +++ b/api/src/main/java/matterlink/api/Config.kt @@ -0,0 +1,16 @@ +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" +) +{ + fun sync(connection: StreamConnection) { + connection.token = token + connection.host = url + } +} \ No newline at end of file diff --git a/api/src/main/java/matterlink/api/MessageHandler.kt b/api/src/main/java/matterlink/api/MessageHandler.kt new file mode 100644 index 0000000..9b63da4 --- /dev/null +++ b/api/src/main/java/matterlink/api/MessageHandler.kt @@ -0,0 +1,201 @@ +package matterlink.api + +import org.apache.logging.log4j.Logger +import java.io.BufferedReader +import java.io.DataOutputStream +import java.io.IOException +import java.io.InputStreamReader +import java.net.HttpURLConnection +import java.net.MalformedURLException +import java.net.ProtocolException +import java.net.URL +import java.util.concurrent.ConcurrentLinkedQueue + +/** + * Created by nikky on 07/05/18. + * + * @author Nikky + * @version 1.0 + */ +open class MessageHandler { + private var enabled = false + + private var connectErrors = 0 + private var reconnectCooldown = 0 + private var sendErrors = 0 + + var config: Config = Config() + set(value) { + field = value.apply { + sync(streamConnection) + } + } + + //TODO: make callbacks: onConnect onDisconnect onError etc + + var queue: ConcurrentLinkedQueue = ConcurrentLinkedQueue() + private set + private var streamConnection: StreamConnection = StreamConnection(queue) + + var logger: Logger + get() = streamConnection.logger + set(l) { + streamConnection.logger = l + } + + + private var nextCheck: Long = 0 + + init { + streamConnection.addOnSuccess { success -> + if (success) { + logger.info("connected successfully") + connectErrors = 0 + reconnectCooldown = 0 + } else { + reconnectCooldown = connectErrors + connectErrors++ + logger.error(String.format("connectErrors: %d", connectErrors)) + } + } + } + + fun stop(message: String? = null) { + if (message != null && config.announceDisconnect) { + sendStatusUpdate(message) + } + enabled = false + streamConnection.close() + } + + + fun start(message: String?, clear: Boolean) { + config.sync(streamConnection) + if (clear) { + clear() + } + + enabled = true + streamConnection.open() + + if (message != null && config.announceConnect) { + sendStatusUpdate(message) + } + } + + + private fun clear() { + try { + val url = URL(config.url + "/api/messages") + val conn = url.openConnection() as HttpURLConnection + + if (!config.token.isEmpty()) { + val bearerAuth = "Bearer " + config.token + conn.setRequestProperty("Authorization", bearerAuth) + } + + conn.requestMethod = "GET" + + BufferedReader(InputStreamReader(conn.inputStream)).forEachLine { line -> + logger.trace("skipping $line") + } + } catch (e: MalformedURLException) { + e.printStackTrace() + } catch (e: ProtocolException) { + e.printStackTrace() + } catch (e: IOException) { + e.printStackTrace() + } + + } + + open fun sendStatusUpdate(message: String) { + transmit(ApiMessage(text = message)) + } + + open fun transmit(msg: ApiMessage) { + if (streamConnection.isConnected || streamConnection.isConnecting) { + if (msg.username.isEmpty()) + msg.username = config.systemUser + if (msg.gateway.isEmpty()) { + logger.fatal("missing gateway on message: $msg") + return + } + logger.debug("Transmitting: $msg") + transmitMessage(msg) + } + } + + private fun transmitMessage(message: ApiMessage) { + try { + val url = URL(config.url + "/api/message") + val conn = url.openConnection() as HttpURLConnection + + if (!config.token.isEmpty()) { + val bearerAuth = "Bearer " + config.token + conn.setRequestProperty("Authorization", bearerAuth) + } + + val postData = message.encode() + logger.trace(postData) + + conn.requestMethod = "POST" + conn.setRequestProperty("Content-Type", "application/json") + conn.setRequestProperty("charset", "utf-8") + conn.setRequestProperty("Content-Length", "" + postData.toByteArray().size) + conn.doOutput = true + conn.doInput = true + + DataOutputStream(conn.outputStream).use { wr -> wr.write(postData.toByteArray()) } + + // conn.getInputStream().close(); + conn.connect() + val code = conn.responseCode + if (code != 200) { + logger.error("Server returned $code") + sendErrors++ + if (sendErrors > 5) { + logger.error("Interrupting Connection to matterbridge API due to status code $code") + stop() + } + } else { + sendErrors = 0 + } + } catch (e: IOException) { + e.printStackTrace() + logger.error("sending message caused $e") + sendErrors++ + if (sendErrors > 5) { + logger.error("Caught too many errors, closing bridge") + stop() + } + } + + } + + /** + * clll this method every tick / cycle to make sure it is reconnecting + */ + fun checkConnection() { + if (enabled && !streamConnection.isConnected && !streamConnection.isConnecting) { + logger.trace("check connection") + logger.trace("next: $nextCheck") + logger.trace("now: " + System.currentTimeMillis()) + if (nextCheck > System.currentTimeMillis()) return + nextCheck = System.currentTimeMillis() + config.reconnectWait + + if (connectErrors >= 10) { + logger.error("Caught too many errors, closing bridge") + stop("Interrupting connection to matterbridge API due to accumulated connection errors") + return + } + + if (reconnectCooldown <= 0) { + logger.info("Trying to reconnect") + start("Reconnecting to matterbridge API after connection error", false) + } else { + reconnectCooldown-- + } + } + } +} diff --git a/api/src/main/java/matterlink/api/StreamConnection.kt b/api/src/main/java/matterlink/api/StreamConnection.kt new file mode 100644 index 0000000..f996d00 --- /dev/null +++ b/api/src/main/java/matterlink/api/StreamConnection.kt @@ -0,0 +1,150 @@ +package matterlink.api + +import org.apache.logging.log4j.LogManager +import java.io.IOException +import java.io.InputStream +import java.net.ConnectException +import java.net.HttpURLConnection +import java.net.MalformedURLException +import java.net.URL +import java.util.Arrays +import java.util.LinkedList +import java.util.concurrent.ConcurrentLinkedQueue + +/** + * Created by nikky on 07/05/18. + * + * @author Nikky + * @version 1.0 + */ +class StreamConnection(private val rcvQueue: ConcurrentLinkedQueue) : Runnable { + private var thread: Thread = createThread() + private var urlConnection: HttpURLConnection? = null + private val onSuccessCallbacks = LinkedList<(Boolean) -> Unit>() + + var logger = LogManager.getLogger("matterlink.api") + var host = "" + var token = "" + + var isConnected = false + private set + var isConnecting = false + private set + var isCancelled = false + private set + + private fun createThread(): Thread { + val thread = Thread(this) + thread.name = "RcvThread" + return thread + } + + fun addOnSuccess(callback: (Boolean) -> Unit) { + onSuccessCallbacks.add(callback) + } + + fun removeOnSuccess(callback: (Boolean) -> Unit) { + onSuccessCallbacks.remove(callback) + } + + private fun onSuccess(success: Boolean) { + isConnecting = false + isConnected = success + for (callback in onSuccessCallbacks) { + callback(success) + } + } + + override fun run() { + try { + val serviceURL = "$host/api/stream" + val myURL: URL + + myURL = URL(serviceURL) + urlConnection = myURL.openConnection() as HttpURLConnection + urlConnection!!.requestMethod = "GET" + if (!token.isEmpty()) { + val bearerAuth = "Bearer $token" + urlConnection!!.setRequestProperty("Authorization", bearerAuth) + } + try { + urlConnection!!.inputStream.use { input -> + logger.info("connection opened") + onSuccess(true) + // BufferedInputStream bufferedInput = new BufferedInputStream(input, 8 * 1024); + val buffer = StringBuilder() + while (!isCancelled) { + val buf = ByteArray(1024) + Thread.sleep(10) + while (input.available() <= 0) { + if (isCancelled) break + Thread.sleep(10) + } + val chars = input.read(buf) + + logger.trace( String.format("read %d chars", chars)) + if (chars > 0) { + val added = String(Arrays.copyOfRange(buf, 0, chars)) + logger.debug("DEBUG", "json: $added") + buffer.append(added) + while (buffer.toString().contains("\n")) { + val index = buffer.indexOf("\n") + val line = buffer.substring(0, index) + buffer.delete(0, index + 1) + rcvQueue.add(ApiMessage.decode(line)) + } + } else if (chars < 0) { + break + } + } + } + } finally { + onClose() + } + } catch (e: MalformedURLException) { + e.printStackTrace() + } catch (e: ConnectException) { + e.printStackTrace() + onSuccess(false) + } catch (e: IOException) { + e.printStackTrace() + onSuccess(false) + } catch (e: InterruptedException) { + e.printStackTrace() + } + + } + + private fun onClose() { + logger.info("Bridge connection closed!") + isConnected = false + isConnecting = false + } + + fun open() { + if (!thread.isAlive) { + thread = createThread() + isConnecting = true + isCancelled = false + thread.start() + logger.info("Starting Connection") + } + if (thread.isAlive) { + logger.info("Bridge is connecting") + } + } + + fun close() { + try { + isCancelled = true + if (urlConnection != null) { + urlConnection!!.disconnect() + } + thread.join() + logger.info("Thread stopped") + } catch (e: InterruptedException) { + e.printStackTrace() + } + + } +} diff --git a/api/src/test/java/matterlink/api/Main.kt b/api/src/test/java/matterlink/api/Main.kt new file mode 100644 index 0000000..5cd886d --- /dev/null +++ b/api/src/test/java/matterlink/api/Main.kt @@ -0,0 +1,80 @@ +package matterlink.api + +import com.google.gson.Gson +import com.google.gson.stream.JsonReader + +import java.io.* +import java.util.Scanner + +/** + * Created by nikky on 07/05/18. + * + * @author Nikky + * @version 1.0 + */ +object Main { + @Throws(IOException::class) + @JvmStatic + fun main(vararg args: String) { + + val handler = MessageHandler() + val queue = handler.queue + val config: Config + + val gson = Gson() + try { + val reader = JsonReader(FileReader("config.json")) + config = gson.fromJson(reader, Config::class.java) + handler.config = config + } catch (e: FileNotFoundException) { + e.printStackTrace() + FileWriter("config.json").use { writer -> gson.toJson(handler.config, writer) } + } + +// handler.logger = { level, msg -> System.out.printf("[%s] %s%n", level, msg) } + handler.start("Connecting..", true) + + Thread { + while (true) { + val next = queue.poll() + if (next != null) { + println(next) + } + try { + Thread.sleep(200) + } catch (e: InterruptedException) { + e.printStackTrace() + } + + } + }.start() + + Thread { + while (true) { + handler.checkConnection() + try { + Thread.sleep(100) + } catch (e: InterruptedException) { + e.printStackTrace() + } + + } + }.start() + + val sc = Scanner(System.`in`) + while (true) { + val input = sc.nextLine() + when (input) { + "start" -> { + handler.start("start", false) + } + "stop" -> { + handler.stop("stop") + } + else -> { + handler.transmit(ApiMessage(text = input)) + } + } + } + } +} diff --git a/build.gradle b/build.gradle index 2bdeb70..6e0c45a 100644 --- a/build.gradle +++ b/build.gradle @@ -36,8 +36,9 @@ subprojects { repositories { jcenter() maven { - name = "unascribed" + name = "elytradev" url = 'http://unascribed.com/maven/releases' + url = 'https://repo.elytradev.com' } maven { name = "shadowfacts" diff --git a/core/build.gradle b/core/build.gradle index 0d4d029..26c6213 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -19,15 +19,15 @@ dependencies { 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: 'org.apache.httpcomponents', name: 'httpclient', version: '+' - compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: project.kotlin_version +// compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: project.kotlin_version + + compile group: 'com.elytradev.concrete', name:'concrete-common', version: concrete_version + compile group: 'com.elytradev.concrete', name:'concrete-rulesengine', version: concrete_version } compileKotlin { @@ -37,10 +37,15 @@ compileKotlin { } shadowJar { - classifier '' + classifier = '' dependencies { include project(":api") include project(":Jankson") + include dependency(group: 'com.elytradev.concrete', name: 'concrete-common', version: concrete_version) + include dependency(group: 'com.elytradev.concrete', name: 'concrete-rulesengine', version: concrete_version) } + + relocate 'blue.endless', 'matterlink.repack.blue.endless' + relocate 'com.elytradev.concrete', 'matterlink.repack.com.elytradev.concrete' } \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/Area.kt b/core/src/main/kotlin/matterlink/Area.kt new file mode 100644 index 0000000..d24acca --- /dev/null +++ b/core/src/main/kotlin/matterlink/Area.kt @@ -0,0 +1,184 @@ +package matterlink + +import blue.endless.jankson.JsonObject +import blue.endless.jankson.impl.SyntaxError +import kotlin.math.sqrt + +fun JsonObject.parseDimensions(): List = 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 + 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 = 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 = 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 = 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 = 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 = 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() +} \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/IMatterLink.kt b/core/src/main/kotlin/matterlink/IMatterLink.kt index 061fdc2..39ee69e 100644 --- a/core/src/main/kotlin/matterlink/IMatterLink.kt +++ b/core/src/main/kotlin/matterlink/IMatterLink.kt @@ -4,6 +4,7 @@ import matterlink.bridge.MessageHandlerInst import matterlink.bridge.command.BridgeCommandRegistry import matterlink.bridge.command.IBridgeCommand import matterlink.bridge.command.IMinecraftCommandSender +import matterlink.config.BaseConfig import matterlink.config.cfg import matterlink.update.UpdateChecker import org.apache.logging.log4j.Logger @@ -98,4 +99,6 @@ abstract class IMatterLink { BridgeCommandRegistry.reloadCommands() } + abstract fun collectPlayers(area: Area): Set + } \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/Util.kt b/core/src/main/kotlin/matterlink/Util.kt index 093610c..6d4b65f 100644 --- a/core/src/main/kotlin/matterlink/Util.kt +++ b/core/src/main/kotlin/matterlink/Util.kt @@ -67,8 +67,10 @@ fun randomString(length: Int = 6): String = java.util.UUID.randomUUID().toString().replace("-", "").take(length) fun JsonObject.getOrDefault(key: String, default: T, comment: String? = null): T { -// logger.info("type: ${default.javaClass.name} key: $key json: >>>${this.getObject(key)?.toJson()}<<< default: $default") - return putDefault(key, default, comment)!! + logger.trace("type: ${default.javaClass.name} key: $key json: >>>${this.getObject(key)?.toJson()}<<< default: $default") + return putDefault(key, default, comment)!!.also { + setComment(key, comment) + } } inline fun Jankson.fromJson(obj: JsonObject): T = this.fromJson(obj, T::class.java) @@ -84,7 +86,15 @@ inline fun Marshaller.registerSerializer(noinline serializer: inline fun Marshaller.registerSerializer(noinline serializer: (T, Marshaller) -> JsonElement) = this.registerSerializer(T::class.java, serializer) -inline fun JsonObject.getReified(key: String): T? = this.get(T::class.java, key) +inline fun JsonObject.getReified(key: String, comment: String? = null): T? = this.get(T::class.java, key) + ?.also { setComment(key, comment) } + +inline fun 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 JsonObject.getList(key: String): List? { return this[key]?.let { array -> @@ -97,4 +107,34 @@ inline fun JsonObject.getList(key: String): List? { else -> null } } +} + +inline fun JsonObject.getOrPutList(key: String, default: List, comment: String?): List { + 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 JsonObject.getOrPutMap(key: String, default: Map, comment: String?): Map { + 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 } \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/bridge/MessageHandlerInst.kt b/core/src/main/kotlin/matterlink/bridge/MessageHandlerInst.kt index a48ed70..02ad2dd 100644 --- a/core/src/main/kotlin/matterlink/bridge/MessageHandlerInst.kt +++ b/core/src/main/kotlin/matterlink/bridge/MessageHandlerInst.kt @@ -4,12 +4,24 @@ import matterlink.* import matterlink.api.ApiMessage import matterlink.api.MessageHandler import matterlink.config.cfg +import matterlink.handlers.ChatEvent +import matterlink.handlers.LocationHandler object MessageHandlerInst : MessageHandler() { override fun transmit(msg: ApiMessage) { transmit(msg, cause = "") } + override 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" + ) + } + fun transmit(msg: ApiMessage, cause: String, maxLines: Int = cfg.outgoing.inlineLimit) { if (msg.username.isEmpty()) { msg.username = cfg.outgoing.systemUser @@ -18,8 +30,10 @@ object MessageHandlerInst : MessageHandler() { msg.avatar = cfg.outgoing.avatar.systemUserAvatar } } - if (msg.gateway.isEmpty()) - msg.gateway = cfg.connect.gateway + if (msg.gateway.isEmpty()) { + logger.error("dropped message '$msg' due to missing gateway") + return + } if (msg.text.lines().count() >= maxLines) { try { @@ -38,6 +52,7 @@ object MessageHandlerInst : MessageHandler() { msg.text = msg.text.substringBefore('\n') .take(25) + "... " + response.link } catch(e: Exception) { + logger.error(cause) logger.error(e.stackTraceString) } } diff --git a/core/src/main/kotlin/matterlink/bridge/command/BridgeCommandRegistry.kt b/core/src/main/kotlin/matterlink/bridge/command/BridgeCommandRegistry.kt index d689197..ed19dc5 100644 --- a/core/src/main/kotlin/matterlink/bridge/command/BridgeCommandRegistry.kt +++ b/core/src/main/kotlin/matterlink/bridge/command/BridgeCommandRegistry.kt @@ -29,7 +29,7 @@ object BridgeCommandRegistry { val uuid = IdentitiesConfig.getUUID(input.account, input.userid) - val env = IBridgeCommand.CommandEnvironment.BridgeEnv(input.username, input.userid, input.account, uuid) + val env = IBridgeCommand.CommandEnvironment.BridgeEnv(input.username, input.userid, input.account, input.gateway, uuid) return commandMap[cmd[0]]?.let { if (!it.reachedTimeout()) { logger.debug("dropped command ${it.alias}") diff --git a/core/src/main/kotlin/matterlink/bridge/command/IBridgeCommand.kt b/core/src/main/kotlin/matterlink/bridge/command/IBridgeCommand.kt index b97034c..b536e92 100644 --- a/core/src/main/kotlin/matterlink/bridge/command/IBridgeCommand.kt +++ b/core/src/main/kotlin/matterlink/bridge/command/IBridgeCommand.kt @@ -23,6 +23,7 @@ abstract class IBridgeCommand { val name: String, val userId: String, val platform: String, + val gateway: String, override val uuid: UUID? ) : CommandEnvironment() { override val username: String? @@ -39,6 +40,7 @@ abstract class IBridgeCommand { is BridgeEnv -> { MessageHandlerInst.transmit( ApiMessage( + gateway = this.gateway, text = text.stripColorOut ), cause = cause diff --git a/core/src/main/kotlin/matterlink/config/BaseConfig.kt b/core/src/main/kotlin/matterlink/config/BaseConfig.kt index dbf0930..dfa763b 100644 --- a/core/src/main/kotlin/matterlink/config/BaseConfig.kt +++ b/core/src/main/kotlin/matterlink/config/BaseConfig.kt @@ -4,13 +4,12 @@ import blue.endless.jankson.Jankson import blue.endless.jankson.JsonObject import blue.endless.jankson.impl.Marshaller import blue.endless.jankson.impl.SyntaxError +import matterlink.* import matterlink.bridge.MessageHandlerInst -import matterlink.getOrDefault -import matterlink.logger -import matterlink.registerTypeAdapter -import matterlink.stackTraceString import java.io.File import java.io.FileNotFoundException +import java.util.* +import kotlin.collections.LinkedHashMap lateinit var cfg: BaseConfig.MatterLinkConfig lateinit var baseCfg: BaseConfig @@ -26,12 +25,82 @@ data class BaseConfig(val rootDir: File) { data class MatterLinkConfig( val connect: ConnectOptions = ConnectOptions(), + val outgoingDefaults: DefaultSettingsOutgoing = DefaultSettingsOutgoing(), + val incomingDefaults: DefaultSettingsIncoming = DefaultSettingsIncoming(), + val locations: List = 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 = 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 = 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 = '!', @@ -45,7 +114,6 @@ 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 ) @@ -104,7 +172,9 @@ 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") //💣 💥 + "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") //🚂 🚃 🚄 🚅 🚇 🚈 🚊 ) ) @@ -117,8 +187,8 @@ data class BaseConfig(val rootDir: File) { data class JoinPartOptions( val enable: Boolean = true, - val joinServer: String = "{username:antiping} has connected to the platform", - val partServer: String = "{username:antiping} has disconnected from the platform" + val joinServer: String = "{username:antiping} has connected to the server", + val partServer: String = "{username:antiping} has disconnected from the server" ) data class UpdateOptions( @@ -129,41 +199,191 @@ data class BaseConfig(val rootDir: File) { val jankson = Jankson .builder() .registerTypeAdapter { - MatterLinkConfig( - command = it.getOrDefault( - "command", - CommandOptions(), - "User commands" - ), - connect = it.getOrDefault( - "connect", - ConnectOptions(), - "Connection Settings" - ), - incoming = it.getOrDefault( - "incoming", - IncomingOptions(), - """ - Gateway -> Server - Options all about receiving messages from the API - Formatting options: - Available variables: {username}, {text}, {gateway}, {channel}, {protocol}, {username:antiping} - """.trimIndent() - ), - outgoing = it.getOrDefault( - "outgoing", - OutgoingOptions(), - """ - Server -> Gateway - Options all about sending messages to the API - """.trimIndent() - ), - update = it.getOrDefault( - "update", - UpdateOptions(), - "Update Settings" - ) - ) + with(MatterLinkConfig()) { + MatterLinkConfig( + command = it.getOrDefault( + "command", + command, + "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, + "Connection Settings" + ), + incoming = it.getOrDefault( + "incoming", + incoming, + """ + Gateway -> Server + Options all about receiving messages from the API + Formatting options: + Available variables: {username}, {text}, {gateway}, {channel}, {protocol}, {username:antiping} + """.trimIndent() + ), + outgoing = it.getOrDefault( + "outgoing", + outgoing, + """ + Server -> Gateway + Options all about sending messages to the API + """.trimIndent() + ), + update = it.getOrDefault( + "update", + update, + "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()) { @@ -214,15 +434,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" ) ) } @@ -389,6 +609,25 @@ data class BaseConfig(val rootDir: File) { ) } } + .registerSerializer { locationSettings: SettingsOutgoing, marshaller: Marshaller -> + val jsonObject = JsonObject() + locationSettings.advancement?.let { + jsonObject["advancements"] = marshaller.serialize(it) + } + locationSettings.death?.let { + jsonObject["death"] = marshaller.serialize(it) + } + locationSettings.join?.let { + jsonObject["joins"] = marshaller.serialize(it) + } + locationSettings.leave?.let { + jsonObject["leaves"] = marshaller.serialize(it) + } + locationSettings.broadcast?.let { + jsonObject["say"] = marshaller.serialize(it) + } + jsonObject + } .build() } @@ -417,15 +656,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)) - MatterLinkConfig() + if (::cfg.isInitialized) cfg else MatterLinkConfig() } catch (e: IllegalStateException) { logger.error(e.stackTraceString) cfgDirectory.resolve("error.matterlink.hjson").writeText(jsonObject.toJson(false, true)) - MatterLinkConfig() + if (::cfg.isInitialized) cfg else MatterLinkConfig() } catch (e: NullPointerException) { logger.error("error loading config: ${e.stackTraceString}") cfgDirectory.resolve("error.matterlink.hjson").writeText(jsonObject.toJson(false, true)) - MatterLinkConfig() + if (::cfg.isInitialized) cfg else MatterLinkConfig() } // val defaultJsonObject = jankson.load("{}") @@ -434,7 +673,6 @@ 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 diff --git a/core/src/main/kotlin/matterlink/handlers/ChatProcessor.kt b/core/src/main/kotlin/matterlink/handlers/ChatProcessor.kt index 4b06f4a..415da99 100644 --- a/core/src/main/kotlin/matterlink/handlers/ChatProcessor.kt +++ b/core/src/main/kotlin/matterlink/handlers/ChatProcessor.kt @@ -4,7 +4,6 @@ import matterlink.api.ApiMessage import matterlink.bridge.MessageHandlerInst import matterlink.bridge.command.BridgeCommandRegistry import matterlink.config.cfg -import matterlink.instance import matterlink.logger import matterlink.stripColorOut import java.util.* @@ -13,23 +12,21 @@ object ChatProcessor { /** * @return cancel message flag */ - fun sendToBridge(user: String, msg: String, event: String, uuid: UUID? = null): Boolean { + fun sendToBridge(user: String, msg: String, x: Int, y: Int, z: Int, dimension: Int?, event: ChatEvent, uuid: UUID? = null): Boolean { + //TODO: pass message to Locations + logger.info("position: $x $y $z dimension: $dimension") val message = msg.trim() - if(uuid != null && BridgeCommandRegistry.handleCommand(message, user, uuid)) return true + if (uuid != null && BridgeCommandRegistry.handleCommand(message, user, uuid)) return true when { - 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()) - } - }, + message.isNotBlank() -> LocationHandler.sendToLocations( + user = user, + msg = message, + x = x, y = y, z = z, dimension = dimension, + event = event, cause = "Message from $user" ) + + else -> logger.warn("WARN: dropped blank message by '$user'") } return false diff --git a/core/src/main/kotlin/matterlink/handlers/DeathHandler.kt b/core/src/main/kotlin/matterlink/handlers/DeathHandler.kt index 49c81c3..a084c8e 100644 --- a/core/src/main/kotlin/matterlink/handlers/DeathHandler.kt +++ b/core/src/main/kotlin/matterlink/handlers/DeathHandler.kt @@ -13,7 +13,9 @@ object DeathHandler { fun handleDeath( player: String, deathMessage: String, - damageType: String + damageType: String, + x: Int, y: Int, z: Int, + dimension: Int ) { if (cfg.outgoing.death.enable) { var msg = deathMessage.stripColorOut.replace(player, player.stripColorOut.antiping) @@ -23,7 +25,13 @@ object DeathHandler { val damageEmoji = emojis[random.nextInt(emojis.size)] msg += " $damageEmoji" } - MessageHandlerInst.transmit(ApiMessage(text = msg), cause = "Death Event of $player") + LocationHandler.sendToLocations( + msg = msg, + x = x, y = y, z = z, dimension = dimension, + event = ChatEvent.DEATH, + cause = "Death Event of $player", + systemuser = true + ) } } } diff --git a/core/src/main/kotlin/matterlink/handlers/JoinLeaveHandler.kt b/core/src/main/kotlin/matterlink/handlers/JoinLeaveHandler.kt index 5614192..2b639b6 100644 --- a/core/src/main/kotlin/matterlink/handlers/JoinLeaveHandler.kt +++ b/core/src/main/kotlin/matterlink/handlers/JoinLeaveHandler.kt @@ -9,7 +9,9 @@ import matterlink.mapFormat import matterlink.stripColorOut object JoinLeaveHandler { - fun handleJoin(player: String) { + fun handleJoin(player: String, + x: Int, y: Int, z: Int, + dimension: Int) { if (cfg.outgoing.joinPart.enable) { val msg = cfg.outgoing.joinPart.joinServer.mapFormat( mapOf( @@ -17,17 +19,19 @@ object JoinLeaveHandler { "{username:antiping}" to player.stripColorOut.antiping ) ) - MessageHandlerInst.transmit( - ApiMessage( - text = msg, - event = JOIN_LEAVE - ), + LocationHandler.sendToLocations( + msg = msg, + x = x, y = y, z = z, dimension = dimension, + event = ChatEvent.JOIN, + systemuser = true, cause = "$player joined" ) } } - fun handleLeave(player: String) { + fun handleLeave(player: String, + x: Int, y: Int, z: Int, + dimension: Int) { if (cfg.outgoing.joinPart.enable) { val msg = cfg.outgoing.joinPart.partServer.mapFormat( mapOf( @@ -35,11 +39,11 @@ object JoinLeaveHandler { "{username:antiping}" to player.stripColorOut.antiping ) ) - MessageHandlerInst.transmit( - ApiMessage( - text = msg, - event = JOIN_LEAVE - ), + LocationHandler.sendToLocations( + msg = msg, + x = x, y = y, z = z, dimension = dimension, + event = ChatEvent.JOIN, + systemuser = true, cause = "$player left" ) } diff --git a/core/src/main/kotlin/matterlink/handlers/LocationHandler.kt b/core/src/main/kotlin/matterlink/handlers/LocationHandler.kt new file mode 100644 index 0000000..9433bde --- /dev/null +++ b/core/src/main/kotlin/matterlink/handlers/LocationHandler.kt @@ -0,0 +1,107 @@ +package matterlink.handlers + +import matterlink.api.ApiMessage +import matterlink.bridge.MessageHandlerInst +import matterlink.config.cfg +import matterlink.logger +import matterlink.stripColorOut +import java.util.* + + +enum class ChatEvent { + PLAIN, ACTION, DEATH, JOIN, LEAVE, ADVANCEMENT, BROADCAST, STATUS +} + +object LocationHandler { + + fun sendToLocations( + user: String = cfg.outgoing.systemUser, + msg: String, + x: Int, y: Int, z: Int, + dimension: Int?, + event: ChatEvent, + systemuser: Boolean = false, + uuid: UUID? = null, + cause: String + ): Boolean { + val defaults = cfg.outgoingDefaults + var handled = false + val skips = mutableSetOf() + logger.info("locations: ${cfg.locations.map { it.label }}") + for (location in cfg.locations) { + val label = location.label + if(skips.contains(label)) { + logger.info("skipping $label (contained in in $skips)") + continue + } + if(!location.area.testForDim(dimension)) { + logger.info("location: $label dropped message '$msg' from $user due to mismatched dimension") + continue + } + if(!location.area.testInBounds(x, y, z)) { + logger.info("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.info("location: $label dropped message '$msg' from user: '$user', event not enabled") + 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.info("sent message through location: $label, cause: $cause") + handled = true + } + return handled + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/handlers/ProgressHandler.kt b/core/src/main/kotlin/matterlink/handlers/ProgressHandler.kt index 77da1bf..9e74533 100644 --- a/core/src/main/kotlin/matterlink/handlers/ProgressHandler.kt +++ b/core/src/main/kotlin/matterlink/handlers/ProgressHandler.kt @@ -8,14 +8,17 @@ import matterlink.stripColorOut object ProgressHandler { - fun handleProgress(name: String, message: String, display: String) { + fun handleProgress(name: String, message: String, display: String, + x: Int, y: Int, z: Int, + dimension: Int) { if (!cfg.outgoing.advancements) return val usr = name.stripColorOut.antiping - MessageHandlerInst.transmit( - ApiMessage( - text = "$usr $message $display".stripColorOut - ), - cause = "Progress Event by $usr" + 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 ) } } \ No newline at end of file diff --git a/core/src/main/kotlin/matterlink/handlers/ServerChatHandler.kt b/core/src/main/kotlin/matterlink/handlers/ServerChatHandler.kt index 85de8fb..b9e7ec4 100644 --- a/core/src/main/kotlin/matterlink/handlers/ServerChatHandler.kt +++ b/core/src/main/kotlin/matterlink/handlers/ServerChatHandler.kt @@ -1,12 +1,13 @@ package matterlink.handlers +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 matterlink.stripColorIn +import java.util.* object ServerChatHandler { @@ -16,33 +17,116 @@ object ServerChatHandler { fun writeIncomingToChat() { if (MessageHandlerInst.queue.isNotEmpty()) logger.debug("incoming: " + MessageHandlerInst.queue.toString()) - val nextMessage = MessageHandlerInst.queue.poll() ?: null + val nextMessage = MessageHandlerInst.queue.poll() ?: return - 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) + val defaults = cfg.incomingDefaults + + val sourceGateway = nextMessage.gateway + + // find all areas to send to + + val targetUUIDs = mutableSetOf() + val skips = mutableSetOf() + + 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.fatal("unknwon 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)) + } + + 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 + } + } + + targetUUIDs.forEach { + //TODO: optimize send to all at once + instance.wrappedSendToPlayer(it, message) + } + + + +// if (nextMessage?.gateway == cfg.connect.gateway) { +// if (!nextMessage.text.isBlank()) { +// nextMessage.text = nextMessage.text.stripColorIn +// val message = when (nextMessage.event) { +// "user_action" -> nextMessage.format(cfg.incoming.action) +// "" -> { +// // try to handle command and do not handle as a chat message +// if (BridgeCommandRegistry.handleCommand(nextMessage)) return +// nextMessage.format(cfg.incoming.chat) +// } +// "join_leave" -> nextMessage.format(cfg.incoming.joinPart) +// else -> { +// val user = nextMessage.username +// val text = nextMessage.text +// val json = nextMessage.encode() +// logger.debug("Threw out message with unhandled event: ${nextMessage.event}") +// logger.debug(" Message contents:") +// logger.debug(" User: $user") +// logger.debug(" Text: $text") +// logger.debug(" JSON: $json") +// return +// } +// } +// instance.wrappedSendToPlayers(message) +// } +// } } } diff --git a/core/src/main/kotlin/matterlink/jenkins/Artifact.kt b/core/src/main/kotlin/matterlink/jenkins/Artifact.kt index 7e24641..ea8226e 100644 --- a/core/src/main/kotlin/matterlink/jenkins/Artifact.kt +++ b/core/src/main/kotlin/matterlink/jenkins/Artifact.kt @@ -1,7 +1,5 @@ package matterlink.jenkins -import com.google.gson.annotations.SerializedName - /** * Created by nikky on 03/02/18. * @author Nikky diff --git a/core/src/main/kotlin/matterlink/jenkins/JenkinsServer.kt b/core/src/main/kotlin/matterlink/jenkins/JenkinsServer.kt index 5758847..a44fa42 100644 --- a/core/src/main/kotlin/matterlink/jenkins/JenkinsServer.kt +++ b/core/src/main/kotlin/matterlink/jenkins/JenkinsServer.kt @@ -1,4 +1,4 @@ -package voodoo.util.jenkins +package matterlink.jenkins import com.google.gson.Gson @@ -6,7 +6,6 @@ import matterlink.Result import matterlink.header import matterlink.httpGet import matterlink.responseString -import matterlink.jenkins.Job import matterlink.logger /** diff --git a/core/src/main/kotlin/matterlink/jenkins/Job.kt b/core/src/main/kotlin/matterlink/jenkins/Job.kt index fb687e9..29178af 100644 --- a/core/src/main/kotlin/matterlink/jenkins/Job.kt +++ b/core/src/main/kotlin/matterlink/jenkins/Job.kt @@ -1,4 +1,4 @@ -package matterlink.jenkins; +package matterlink.jenkins /** * Created by nikky on 03/02/18. diff --git a/core/src/main/kotlin/matterlink/update/UpdateChecker.kt b/core/src/main/kotlin/matterlink/update/UpdateChecker.kt index 1e7f163..744dfb5 100644 --- a/core/src/main/kotlin/matterlink/update/UpdateChecker.kt +++ b/core/src/main/kotlin/matterlink/update/UpdateChecker.kt @@ -1,13 +1,12 @@ package matterlink.update -import com.google.gson.FieldNamingPolicy import com.google.gson.GsonBuilder import matterlink.api.ApiMessage import matterlink.bridge.MessageHandlerInst import matterlink.config.cfg import matterlink.instance import matterlink.logger -import voodoo.util.jenkins.JenkinsServer +import matterlink.jenkins.JenkinsServer import java.io.BufferedReader import java.net.HttpURLConnection import java.net.URL diff --git a/gradle.properties b/gradle.properties index ea673a9..41a684a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,7 @@ mod_name = MatterLink mod_version = 1.6.3 forgelin_version = 1.6.0 +concrete_version = 0.3.8-SNAPSHOT kotlin_version = 1.2.41 shadow_version = 2.0.2 cursegradle_version = 1.0.10