Browse Source

update 1.12.2 with locations

master
nikky 3 years ago
parent
commit
e30e8132ca
  1. 2
      .gitignore
  2. 5
      .gitmodules
  3. 13
      1.10.2/build.gradle
  4. 13
      1.11.2/build.gradle
  5. 13
      1.12.2/build.gradle
  6. 62
      1.12.2/src/main/kotlin/matterlink/EventHandler.kt
  7. 10
      1.12.2/src/main/kotlin/matterlink/MatterLink.kt
  8. 2
      Jankson
  9. 1
      api
  10. 5
      api/.gitignore
  11. 22
      api/LICENSE.md
  12. 1
      api/README.md
  13. 49
      api/build.gradle
  14. BIN
      api/gradle/wrapper/gradle-wrapper.jar
  15. 5
      api/gradle/wrapper/gradle-wrapper.properties
  16. 172
      api/gradlew
  17. 84
      api/gradlew.bat
  18. 2
      api/settings.gradle
  19. 92
      api/src/main/java/matterlink/api/ApiMessage.kt
  20. 16
      api/src/main/java/matterlink/api/Config.kt
  21. 201
      api/src/main/java/matterlink/api/MessageHandler.kt
  22. 150
      api/src/main/java/matterlink/api/StreamConnection.kt
  23. 80
      api/src/test/java/matterlink/api/Main.kt
  24. 3
      build.gradle
  25. 15
      core/build.gradle
  26. 184
      core/src/main/kotlin/matterlink/Area.kt
  27. 3
      core/src/main/kotlin/matterlink/IMatterLink.kt
  28. 46
      core/src/main/kotlin/matterlink/Util.kt
  29. 19
      core/src/main/kotlin/matterlink/bridge/MessageHandlerInst.kt
  30. 2
      core/src/main/kotlin/matterlink/bridge/command/BridgeCommandRegistry.kt
  31. 2
      core/src/main/kotlin/matterlink/bridge/command/IBridgeCommand.kt
  32. 342
      core/src/main/kotlin/matterlink/config/BaseConfig.kt
  33. 25
      core/src/main/kotlin/matterlink/handlers/ChatProcessor.kt
  34. 12
      core/src/main/kotlin/matterlink/handlers/DeathHandler.kt
  35. 28
      core/src/main/kotlin/matterlink/handlers/JoinLeaveHandler.kt
  36. 107
      core/src/main/kotlin/matterlink/handlers/LocationHandler.kt
  37. 15
      core/src/main/kotlin/matterlink/handlers/ProgressHandler.kt
  38. 136
      core/src/main/kotlin/matterlink/handlers/ServerChatHandler.kt
  39. 2
      core/src/main/kotlin/matterlink/jenkins/Artifact.kt
  40. 3
      core/src/main/kotlin/matterlink/jenkins/JenkinsServer.kt
  41. 2
      core/src/main/kotlin/matterlink/jenkins/Job.kt
  42. 3
      core/src/main/kotlin/matterlink/update/UpdateChecker.kt
  43. 1
      gradle.properties

2
.gitignore

@ -102,3 +102,5 @@ run/
\.floo
\.flooignore
test\.sh

5
.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

13
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'
}

13
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'
}

13
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'
}

62
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

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

2
Jankson

@ -1 +1 @@
Subproject commit 93ab86ee821380584c22ac60d77737388976e531
Subproject commit 69564c34e706f5243d8ae212fb58bb982db01a81

1
api

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

5
api/.gitignore

@ -0,0 +1,5 @@
\.idea/
\.gradle/
out/
build/
run/

22
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.

1
api/README.md

@ -0,0 +1 @@
MatterLinkApi

49
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")]
}
}

BIN
api/gradle/wrapper/gradle-wrapper.jar

5
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

172
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" "$@"

84
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

2
api/settings.gradle

@ -0,0 +1,2 @@
rootProject.name = 'MatterLinkApi'

92
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)
}
}
}

16
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
}
}

201
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<ApiMessage> = 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--
}
}
}
}

150
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<ApiMessage>) : 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()
}
}
}

80
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))
}
}
}
}
}

3
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"

15
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'
}

184
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<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()
}

3
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<UUID>
}

46
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 <T : Any> 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 <reified T : Any> Jankson.fromJson(obj: JsonObject): T = this.fromJson(obj, T::class.java)
@ -84,7 +86,15 @@ inline fun <reified T : Any> Marshaller.registerSerializer(noinline 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): T? = this.get(T::class.java, key)
inline fun <reified T : Any> JsonObject.getReified(key: String, comment: String? = null): T? = this.get(T::class.java, key)
?.also { setComment(key, comment) }
inline fun <reified T : Any> JsonObject.getReifiedOrDelete(key: String, comment: String? = null): T? = this.get(T::class.java, key)
?.also { setComment(key, comment) }
?: run {
this.remove(key)
null
}
inline fun <reified T : Any> JsonObject.getList(key: String): List<T>? {
return this[key]?.let { array ->
@ -97,4 +107,34 @@ inline fun <reified T : Any> JsonObject.getList(key: String): List<T>? {
else -> null
}
}
}
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
}

19
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)
}
}

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

2
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?
)