├── .github └── workflows │ └── gradle.yml ├── .gitignore ├── README.md ├── build.gradle ├── dependencies └── BungeeCord.jar ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mineweb_bridge-0.1.0.jar ├── mineweb_bridge-2.0.0.jar ├── mineweb_bridge-3.0.0.jar ├── mineweb_bridge-3.0.1.jar ├── mineweb_bridge-3.0.10.jar ├── mineweb_bridge-3.0.2.jar ├── mineweb_bridge-3.0.3.jar ├── mineweb_bridge-3.0.4.jar ├── mineweb_bridge-3.0.5.jar ├── mineweb_bridge-3.0.6.jar ├── mineweb_bridge-3.0.7.jar ├── mineweb_bridge-3.0.8.jar ├── mineweb_bridge-3.0.9.jar ├── settings.gradle └── src └── main ├── java └── fr │ └── vmarchaud │ └── mineweb │ ├── bukkit │ ├── BukkitCore.java │ ├── BukkitListeners.java │ ├── BukkitNettyInjector.java │ ├── BukkitUtils.java │ ├── dump │ │ └── VolatileField.java │ └── methods │ │ ├── BukkitGetBannedPlayers.java │ │ ├── BukkitGetMOTD.java │ │ ├── BukkitGetMaxPlayers.java │ │ ├── BukkitGetVersion.java │ │ └── BukkitGetWhitelistedPlayers.java │ ├── bungee │ ├── BungeeCore.java │ ├── BungeeListeners.java │ ├── BungeeNettyInjector.java │ └── methods │ │ ├── BungeeGetMOTD.java │ │ ├── BungeeGetMaxPlayers.java │ │ └── BungeeGetVersion.java │ ├── common │ ├── Command.java │ ├── CommandScheduler.java │ ├── ICore.java │ ├── IMethod.java │ ├── MethodHandler.java │ ├── RequestHandler.java │ ├── ScheduledCommand.java │ ├── configuration │ │ ├── PluginConfiguration.java │ │ └── ScheduledStorage.java │ ├── injector │ │ ├── JSONAPIChannelDecoder.java │ │ ├── JSONAPIChannelReadHandler.java │ │ ├── JSONAPIHandler.java │ │ ├── NettyInjector.java │ │ ├── NettyServer.java │ │ ├── WebThread.java │ │ └── router │ │ │ └── RouteMatcher.java │ ├── interactor │ │ ├── requests │ │ │ ├── AskRequest.java │ │ │ └── HandshakeRequest.java │ │ └── responses │ │ │ ├── AskResponse.java │ │ │ ├── HandshakeResponse.java │ │ │ └── RetrieveKeyResponse.java │ └── methods │ │ ├── CommonGetPlayerCount.java │ │ ├── CommonGetPlayerList.java │ │ ├── CommonGetSystemStats.java │ │ ├── CommonGetTimestamp.java │ │ ├── CommonIsConnected.java │ │ ├── CommonPluginType.java │ │ ├── CommonRunCommand.java │ │ ├── CommonScheduledCommand.java │ │ └── CommonSetMotd.java │ └── utils │ ├── BootstrapList.java │ ├── CryptoUtils.java │ ├── CustomLogFormatter.java │ ├── Handler.java │ ├── http │ ├── HttpResponseBuilder.java │ ├── RoutedHttpRequest.java │ └── RoutedHttpResponse.java │ └── regex │ ├── NamedMatchResult.java │ ├── NamedMatcher.java │ └── NamedPattern.java └── resources ├── bungee.yml └── plugin.yml /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Set up JDK 1.8 16 | uses: actions/setup-java@v1 17 | with: 18 | java-version: 1.8 19 | - name: Build with Gradle 20 | run: ./gradlew shadowJar 21 | - uses: webfactory/ssh-agent@v0.1.1 22 | with: 23 | ssh-private-key: ${{ secrets.DEPLOY_SSH_KEY }} 24 | - name: Push jar 25 | run: | 26 | mv ./build/libs/*.jar ./mineweb_bridge-$VERSION.jar 27 | git add mineweb_bridge-$VERSION.jar 28 | git commit -m "build: Add mineweb_bridge-$VERSION.jar" 29 | git push $REPO master 30 | env: 31 | REPO: git@github.com:${{github.repository}}.git 32 | VERSION: "3.0.6" 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .settings/ 3 | .classpath/ 4 | .project 5 | .classpath 6 | *.class 7 | /target/ 8 | .gradle 9 | build/ 10 | /bin/ 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mineweb_bridge 2 | 3 | A bungee/spigot hybrid plugin that inject and expose a http server within the game instance 4 | 5 | ## Debug mode 6 | 7 | You need to set the `DEBUG` env variable to `true`, so just start your minecraft server with `DEBUG=true java -jar minecraft.jar` 8 | The HTTP server will then don't try to decipher request and cipher response, so you'll just need to make a request by serializing the json inside signed : 9 | 10 | ```curl 11 | curl -XPOST http://localhost:25565/ask -d '{ "signed": "[ { \"name\":\"GET_PLAYER_COUNT\", \"args\": [] }]", "iv": "415454a12a1" }' 12 | ``` 13 | You will received response serialized, signed will be ```{ "signed": "[{\"name\":\"GET_PLAYER_COUNT\",\"response\":0}]", "iv": "415454a12a1" }``` 14 | 15 | ## Methods available 16 | 17 | For bukkit check here : https://github.com/vmarchaud/mineweb_bridge/blob/master/src/main/java/fr/vmarchaud/mineweb/bukkit/BukkitCore.java#L191 18 | For bungee check here https://github.com/vmarchaud/mineweb_bridge/blob/master/src/main/java/fr/vmarchaud/mineweb/bungee/BungeeCore.java#L139 19 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | maven { 4 | url "https://plugins.gradle.org/m2/" 5 | } 6 | } 7 | dependencies { 8 | classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0' 9 | } 10 | } 11 | 12 | apply plugin: 'java' 13 | apply plugin: 'com.github.johnrengelman.shadow' 14 | apply plugin: 'eclipse' 15 | 16 | group = 'fr.vmarchaud' 17 | version = '3.0.10' 18 | 19 | description = "mineweb_bridge" 20 | 21 | sourceCompatibility = 1.8 22 | targetCompatibility = 1.8 23 | 24 | repositories { 25 | mavenLocal() 26 | maven { url "https://hub.spigotmc.org/nexus/content/groups/public/" } 27 | maven { url "https://repo.dmulloy2.net/content/groups/public/" } 28 | maven { url 'https://repo.spongepowered.org/maven' } 29 | mavenCentral() 30 | flatDir { 31 | dirs 'dependencies' 32 | } 33 | } 34 | 35 | dependencies { 36 | compileOnly 'com.google.guava:guava:31.1-jre' 37 | compileOnly 'com.google.code.gson:gson:2.9.0' 38 | compileOnly ("com.comphenix.protocol:ProtocolLib:5.0.0") { 39 | exclude group: "com.comphenix.executors" 40 | // For 41 | exclude group: "cglib" 42 | } 43 | implementation 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359' 44 | compileOnly 'org.spongepowered:spongeapi:7.1.0' 45 | compileOnly "org.spigotmc:spigot-api:1.13.2-R0.1-SNAPSHOT" 46 | compileOnly 'org.projectlombok:lombok:1.18.22' 47 | annotationProcessor 'org.projectlombok:lombok:1.18.22' 48 | implementation (group: "io.netty", name: "netty-codec-http", version: '4.1.82.Final') { 49 | exclude group: "io.netty" 50 | } 51 | //compileOnly group: 'io.netty', name: 'netty-all', version: '4.1.82.Final' 52 | compileOnly fileTree(include: ['*.jar'], dir: 'dependencies') 53 | } 54 | 55 | task moveIntoBukkit(type: Copy) { 56 | from "build/libs/mineweb_bridge-${version}.jar" 57 | into '../_SERVER/plugins/' 58 | } 59 | 60 | task moveIntoBungee(type: Copy) { 61 | from "build/libs/mineweb_bridge-${version}.jar" 62 | into '../_BUNGEE/plugins/' 63 | } 64 | -------------------------------------------------------------------------------- /dependencies/BungeeCord.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/dependencies/BungeeCord.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /mineweb_bridge-0.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-0.1.0.jar -------------------------------------------------------------------------------- /mineweb_bridge-2.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-2.0.0.jar -------------------------------------------------------------------------------- /mineweb_bridge-3.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-3.0.0.jar -------------------------------------------------------------------------------- /mineweb_bridge-3.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-3.0.1.jar -------------------------------------------------------------------------------- /mineweb_bridge-3.0.10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-3.0.10.jar -------------------------------------------------------------------------------- /mineweb_bridge-3.0.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-3.0.2.jar -------------------------------------------------------------------------------- /mineweb_bridge-3.0.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-3.0.3.jar -------------------------------------------------------------------------------- /mineweb_bridge-3.0.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-3.0.4.jar -------------------------------------------------------------------------------- /mineweb_bridge-3.0.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-3.0.5.jar -------------------------------------------------------------------------------- /mineweb_bridge-3.0.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-3.0.6.jar -------------------------------------------------------------------------------- /mineweb_bridge-3.0.7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-3.0.7.jar -------------------------------------------------------------------------------- /mineweb_bridge-3.0.8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-3.0.8.jar -------------------------------------------------------------------------------- /mineweb_bridge-3.0.9.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/mineweb_bridge-3.0.9.jar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'mineweb_bridge' 2 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bukkit/BukkitCore.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bukkit; 25 | 26 | import com.google.gson.Gson; 27 | import com.google.gson.GsonBuilder; 28 | import fr.vmarchaud.mineweb.bukkit.methods.*; 29 | import fr.vmarchaud.mineweb.common.CommandScheduler; 30 | import fr.vmarchaud.mineweb.common.ICore; 31 | import fr.vmarchaud.mineweb.common.IMethod; 32 | import fr.vmarchaud.mineweb.common.RequestHandler; 33 | import fr.vmarchaud.mineweb.common.configuration.PluginConfiguration; 34 | import fr.vmarchaud.mineweb.common.configuration.ScheduledStorage; 35 | import fr.vmarchaud.mineweb.common.injector.NettyInjector; 36 | import fr.vmarchaud.mineweb.common.injector.WebThread; 37 | import fr.vmarchaud.mineweb.common.injector.router.RouteMatcher; 38 | import fr.vmarchaud.mineweb.common.methods.*; 39 | import fr.vmarchaud.mineweb.utils.CustomLogFormatter; 40 | import fr.vmarchaud.mineweb.utils.http.HttpResponseBuilder; 41 | 42 | import org.bukkit.Bukkit; 43 | import org.bukkit.ChatColor; 44 | import org.bukkit.command.Command; 45 | import org.bukkit.command.CommandSender; 46 | import org.bukkit.entity.Player; 47 | import org.bukkit.plugin.java.JavaPlugin; 48 | import org.bukkit.scheduler.BukkitTask; 49 | 50 | import java.io.File; 51 | import java.io.IOException; 52 | import java.util.HashMap; 53 | import java.util.HashSet; 54 | import java.util.Map; 55 | import java.util.concurrent.TimeUnit; 56 | import java.util.logging.FileHandler; 57 | import java.util.logging.Level; 58 | import java.util.logging.Logger; 59 | 60 | public class BukkitCore extends JavaPlugin implements ICore { 61 | 62 | public static ICore instance; 63 | public static ICore get() { 64 | return instance; 65 | } 66 | 67 | private RouteMatcher httpRouter; 68 | private NettyInjector injector; 69 | private WebThread nettyServerThread; 70 | private HashMap methods; 71 | private RequestHandler requestHandler; 72 | private PluginConfiguration config; 73 | private ScheduledStorage storage; 74 | private CommandScheduler commandScheduler; 75 | private BukkitTask task; 76 | private boolean protocolLibEnabled; 77 | 78 | /** Cached player list to not rely on Reflection on every request **/ 79 | private HashSet players; 80 | 81 | private Logger logger = Logger.getLogger("Mineweb"); 82 | 83 | private Gson gson = new GsonBuilder().enableComplexMapKeySerialization().serializeNulls().create(); 84 | private FileHandler fileHandler; 85 | 86 | @Override 87 | public void onEnable() { 88 | instance = this; 89 | getDataFolder().mkdirs(); 90 | 91 | // load config 92 | config = PluginConfiguration.load(new File(getDataFolder(), "config.json"), instance); 93 | storage = ScheduledStorage.load(new File(getDataFolder(), "commands.json"), instance); 94 | protocolLibEnabled = Bukkit.getPluginManager().isPluginEnabled("ProtocolLib"); 95 | // setup logger 96 | setupLogger(); 97 | 98 | // Init 99 | logger.info("Loading ..."); 100 | methods = new HashMap(); 101 | players = new HashSet(); 102 | 103 | if(!protocolLibEnabled) 104 | logger.warning("The bridge requires ProtocolLib to run on server's port"); 105 | 106 | if (config.getPort() == null && protocolLibEnabled) { 107 | injector = new BukkitNettyInjector(this); 108 | } else { 109 | nettyServerThread = new WebThread(this); 110 | } 111 | 112 | httpRouter = new RouteMatcher(); 113 | logger.info("Registering route ..."); 114 | registerRoutes(); 115 | getServer().getPluginManager().registerEvents(new BukkitListeners(instance), this); 116 | 117 | // inject when we are ready 118 | if (config.getPort() == null && protocolLibEnabled) { 119 | logger.info("Injecting http server ..."); 120 | injector.inject(); 121 | } else { 122 | logger.info("Start http server ..."); 123 | nettyServerThread.start(); 124 | } 125 | 126 | logger.info("Registering methods ..."); 127 | requestHandler = new RequestHandler(instance); 128 | registerMethods(); 129 | logger.info("Starting CommandScheduler ..."); 130 | commandScheduler = new CommandScheduler(instance, storage); 131 | task = getServer().getScheduler().runTaskTimerAsynchronously(this, commandScheduler, 0, 100); 132 | logger.info("Ready !"); 133 | } 134 | 135 | @Override 136 | public void onDisable() { 137 | if (task != null) task.cancel(); 138 | if (commandScheduler != null) commandScheduler.save(); 139 | if (logger != null) logger.info("Shutting down ..."); 140 | if (fileHandler != null) fileHandler.close(); 141 | } 142 | 143 | @Override 144 | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { 145 | if (cmd.getName().equalsIgnoreCase("mineweb")) { 146 | if (args.length == 1 && args[0].equalsIgnoreCase("reset")) { 147 | if ((sender instanceof Player) && (!sender.isOp()) && (!sender.hasPermission("mineweb.port"))) 148 | return false; 149 | config = new PluginConfiguration(new File(getDataFolder(), "config.json")); 150 | config.save(instance); 151 | sender.sendMessage(ChatColor.GREEN + "MineWebBridge configuration reset!"); 152 | logger.info("MineWebBridge configuration reset!"); 153 | return true; 154 | } 155 | if (args.length == 2 && args[0].equalsIgnoreCase("port")) { 156 | if ((sender instanceof Player) && (!sender.isOp()) && (!sender.hasPermission("mineweb.reset"))) 157 | return false; 158 | config.setPort(Integer.parseInt(args[1])); 159 | config.save(instance); 160 | nettyServerThread = new WebThread(instance); 161 | logger.info("Try to start http server ..."); 162 | nettyServerThread.start(); 163 | 164 | // Wait to check if http is started 165 | try { 166 | TimeUnit.MILLISECONDS.sleep(5); 167 | } catch (Exception e) {} 168 | if(protocolLibEnabled) 169 | sender.sendMessage(ChatColor.GRAY +"If you want to use the default server port, please do not make this command and let the website make the connection and restore with the command /mineweb reset"); 170 | else 171 | sender.sendMessage(ChatColor.GRAY +"If you want to use the default server port, please install the plugin : ProtocolLibs"); 172 | if (!nettyServerThread.isAlive()) { 173 | sender.sendMessage(ChatColor.RED + "MineWebBridge port setup failed!"); 174 | logger.info("HTTP server start failed!"); 175 | return true; 176 | } 177 | sender.sendMessage(ChatColor.GREEN + "MineWebBridge port setup!"); 178 | logger.info("MineWebBridge port setup!"); 179 | return true; 180 | } 181 | } 182 | return false; 183 | } 184 | 185 | public void registerRoutes() { 186 | httpRouter.everyMatch((event) -> { 187 | logger.fine(String.format("[HTTP Request] %d %s on %s", event.getRes().getStatus().code(), 188 | event.getRequest().getMethod().toString(), event.getRequest().getUri())); 189 | return null; 190 | }); 191 | 192 | httpRouter.get("/", (event) -> { 193 | return HttpResponseBuilder.ok(); 194 | }); 195 | } 196 | 197 | public void registerMethods() { 198 | // common methods 199 | methods.put("GET_PLAYER_LIST", new CommonGetPlayerList()); 200 | methods.put("GET_PLAYER_COUNT", new CommonGetPlayerCount()); 201 | methods.put("IS_CONNECTED", new CommonIsConnected()); 202 | methods.put("GET_PLUGIN_TYPE", new CommonPluginType()); 203 | methods.put("GET_SYSTEM_STATS", new CommonGetSystemStats()); 204 | methods.put("RUN_COMMAND", new CommonRunCommand()); 205 | methods.put("RUN_SCHEDULED_COMMAND", new CommonScheduledCommand()); 206 | methods.put("GET_SERVER_TIMESTAMP", new CommonGetTimestamp()); 207 | methods.put("SET_MOTD", new CommonSetMotd()); 208 | 209 | // bukkit methods 210 | methods.put("GET_BANNED_PLAYERS", new BukkitGetBannedPlayers()); 211 | methods.put("GET_MAX_PLAYERS", new BukkitGetMaxPlayers()); 212 | methods.put("GET_MOTD", new BukkitGetMOTD()); 213 | methods.put("GET_VERSION", new BukkitGetVersion()); 214 | methods.put("GET_WHITELISTED_PLAYERS", new BukkitGetWhitelistedPlayers()); 215 | 216 | } 217 | 218 | public void setupLogger() { 219 | try { 220 | logger.setLevel(Level.parse(config.getLogLevel())); 221 | logger.setUseParentHandlers(false); 222 | new File(getDataFolder() + File.separator).mkdirs(); 223 | fileHandler = new FileHandler(getDataFolder() + File.separator + "mineweb.log", true); 224 | fileHandler.setFormatter(new CustomLogFormatter()); 225 | logger.addHandler(fileHandler); 226 | } catch (SecurityException e) { 227 | e.printStackTrace(); 228 | } catch (IOException e) { 229 | e.printStackTrace(); 230 | } 231 | } 232 | 233 | @Override 234 | public void runCommand(String command) { 235 | getServer().getScheduler().runTask(this, () -> { 236 | getServer().dispatchCommand(getServer().getConsoleSender(), command); 237 | }); 238 | } 239 | 240 | @Override 241 | public RouteMatcher getHTTPRouter() { 242 | return httpRouter; 243 | } 244 | 245 | @Override 246 | public Object getPlugin() { 247 | return this; 248 | } 249 | 250 | @Override 251 | public EnumPluginType getType() { 252 | return EnumPluginType.BUKKIT; 253 | } 254 | 255 | @Override 256 | public Object getGameServer() { 257 | return this.getServer(); 258 | } 259 | 260 | @Override 261 | public HashSet getPlayers() { 262 | return players; 263 | } 264 | 265 | @Override 266 | public Logger logger() { 267 | return logger; 268 | } 269 | 270 | @Override 271 | public Gson gson() { 272 | return gson; 273 | } 274 | 275 | @Override 276 | public Map getMethods() { 277 | return methods; 278 | } 279 | 280 | @Override 281 | public PluginConfiguration config() { 282 | return config; 283 | } 284 | 285 | @Override 286 | public RequestHandler requestHandler() { 287 | return requestHandler; 288 | } 289 | 290 | @Override 291 | public CommandScheduler getCommandScheduler() { 292 | return commandScheduler; 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bukkit/BukkitListeners.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/src/main/java/fr/vmarchaud/mineweb/bukkit/BukkitListeners.java -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bukkit/BukkitNettyInjector.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bukkit; 25 | 26 | import java.lang.reflect.Field; 27 | import java.lang.reflect.Method; 28 | import java.util.Collection; 29 | import java.util.List; 30 | import java.lang.reflect.ParameterizedType; 31 | import java.lang.reflect.Type; 32 | import java.util.ArrayList; 33 | 34 | import com.comphenix.protocol.reflect.FuzzyReflection; 35 | import com.comphenix.protocol.utility.MinecraftReflection; 36 | import com.google.common.collect.Lists; 37 | 38 | import fr.vmarchaud.mineweb.bukkit.dump.VolatileField; 39 | import fr.vmarchaud.mineweb.common.ICore; 40 | import fr.vmarchaud.mineweb.common.injector.JSONAPIChannelDecoder; 41 | import fr.vmarchaud.mineweb.common.injector.NettyInjector; 42 | import fr.vmarchaud.mineweb.utils.BootstrapList; 43 | import io.netty.channel.Channel; 44 | import io.netty.channel.ChannelFuture; 45 | import io.netty.channel.ChannelHandler; 46 | import io.netty.channel.ChannelHandlerContext; 47 | import io.netty.channel.ChannelInboundHandler; 48 | import io.netty.channel.ChannelInboundHandlerAdapter; 49 | import io.netty.channel.ChannelInitializer; 50 | 51 | public class BukkitNettyInjector extends NettyInjector { 52 | 53 | // The temporary player factory 54 | protected List bootstrapFields = Lists.newArrayList(); 55 | 56 | // List of network managers 57 | protected volatile Collection networkManagers; 58 | 59 | private ICore api; 60 | 61 | public BukkitNettyInjector(ICore api) { 62 | this.api = api; 63 | } 64 | 65 | @SuppressWarnings("unchecked") 66 | public synchronized void inject() { 67 | if (injected) 68 | throw new IllegalStateException("Cannot inject twice."); 69 | try { 70 | FuzzyReflection fuzzyServer = FuzzyReflection.fromClass(MinecraftReflection.getMinecraftServerClass()); 71 | Method serverConnectionMethod = fuzzyServer.getMethodByReturnTypeAndParameters("getServerConnection", MinecraftReflection.getServerConnectionClass()); 72 | 73 | // Get the server connection 74 | Object server = fuzzyServer.getSingleton(); 75 | Object serverConnection = serverConnectionMethod.invoke(server); 76 | 77 | // Handle connected channels 78 | final ChannelInboundHandler endInitProtocol = new ChannelInitializer() { 79 | @Override 80 | protected void initChannel(Channel channel) throws Exception { 81 | try { 82 | // This can take a while, so we need to stop the main thread from interfering 83 | synchronized (networkManagers) { 84 | injectChannel(channel); 85 | } 86 | } catch (Exception e) { 87 | e.printStackTrace(); 88 | } 89 | } 90 | }; 91 | 92 | // This is executed before Minecraft's channel handler 93 | final ChannelInboundHandler beginInitProtocol = new ChannelInitializer() { 94 | @Override 95 | protected void initChannel(Channel channel) throws Exception { 96 | // Our only job is to add init protocol 97 | channel.pipeline().addLast(endInitProtocol); 98 | } 99 | }; 100 | 101 | // Add our handler to newly created channels 102 | final ChannelHandler connectionHandler = new ChannelInboundHandlerAdapter() { 103 | @Override 104 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 105 | Channel channel = (Channel) msg; 106 | 107 | // Prepare to initialize ths channel 108 | channel.pipeline().addFirst(beginInitProtocol); 109 | ctx.fireChannelRead(msg); 110 | } 111 | }; 112 | 113 | // Get the current NetworkMananger list 114 | 115 | this.networkManagers = getNetworkManagers(serverConnection);; 116 | 117 | // Insert ProtocolLib's connection interceptor 118 | this.bootstrapFields = getBootstrapFields(serverConnection); 119 | 120 | for (VolatileField field : bootstrapFields) { 121 | final List list = (List) field.getValue(); 122 | 123 | // We don't have to override this list 124 | if (list == this.networkManagers) { 125 | continue; 126 | } 127 | 128 | // Synchronize with each list before we attempt to replace them. 129 | field.setValue(new BootstrapList(list, connectionHandler)); 130 | } 131 | 132 | injected = true; 133 | 134 | } catch (Exception e) { 135 | throw new RuntimeException("Unable to inject channel futures.", e); 136 | } 137 | } 138 | 139 | @SuppressWarnings("unchecked") 140 | private List getNetworkManagers(Object serverConnection) throws IllegalAccessException { 141 | Field networkManagersField = getFirstFieldWithListOfNetworkManagers(serverConnection); 142 | List networkManagers = new ArrayList(); 143 | 144 | if ( networkManagersField != null ){ 145 | networkManagers = (List) networkManagersField.get(serverConnection); 146 | } 147 | return networkManagers; 148 | } 149 | 150 | 151 | private Field getFirstFieldWithListOfNetworkManagers(Object serverConnection) { 152 | Field networkManagersField = null; 153 | Class networkManagerClass; 154 | try { 155 | networkManagerClass = Class.forName(MinecraftReflection.getNetworkManagerClass().getCanonicalName()); 156 | for( Field declaredField: serverConnection.getClass().getDeclaredFields() ){ 157 | boolean fieldIsAList = declaredField.getType() == List.class; 158 | if( fieldIsAList ){ 159 | Type typeOfFirstListElement = ((ParameterizedType) declaredField.getGenericType()).getActualTypeArguments()[0]; 160 | if( typeOfFirstListElement == networkManagerClass ) { 161 | networkManagersField = declaredField; 162 | networkManagersField.setAccessible(true); 163 | break; 164 | } 165 | } 166 | } 167 | } catch (ClassNotFoundException e) { 168 | e.printStackTrace(); 169 | } 170 | return networkManagersField; 171 | 172 | } 173 | 174 | @Override 175 | protected void injectChannel(Channel channel) { 176 | channel.pipeline().addFirst(new JSONAPIChannelDecoder(api)); 177 | } 178 | 179 | /** 180 | * Retrieve a list of every field with a list of channel futures. 181 | * @param serverConnection - the connection. 182 | * @return List of fields. 183 | */ 184 | protected List getBootstrapFields(Object serverConnection) { 185 | List result = Lists.newArrayList(); 186 | 187 | // Find and (possibly) proxy every list 188 | for (Field field : FuzzyReflection.fromObject(serverConnection, true).getFieldListByType(List.class)) { 189 | VolatileField volatileField = new VolatileField(field, serverConnection, true).toSynchronized(); 190 | 191 | @SuppressWarnings("unchecked") 192 | List list = (List) volatileField.getValue(); 193 | 194 | if (list.isEmpty() || list.get(0) instanceof ChannelFuture) { 195 | result.add(volatileField); 196 | } 197 | } 198 | return result; 199 | } 200 | 201 | /** 202 | * Clean up any remaning injections. 203 | */ 204 | public synchronized void close() { 205 | if (!closed) { 206 | closed = true; 207 | 208 | for (VolatileField field : bootstrapFields) { 209 | Object value = field.getValue(); 210 | 211 | // Undo the processed channels, if any 212 | if (value instanceof BootstrapList) { 213 | ((BootstrapList) value).close(); 214 | } 215 | field.revertValue(); 216 | } 217 | } 218 | } 219 | 220 | } 221 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bukkit/BukkitUtils.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bukkit; 25 | 26 | import java.lang.reflect.Array; 27 | import java.lang.reflect.Method; 28 | import java.util.ArrayList; 29 | import java.util.Arrays; 30 | import java.util.Collection; 31 | import java.util.List; 32 | 33 | import org.bukkit.Server; 34 | import org.bukkit.entity.Player; 35 | 36 | public class BukkitUtils { 37 | 38 | /** 39 | * Get access to the internal player list of bukkit 40 | * 41 | * @param {@link org.bukkit.Server} instance of the server 42 | * @return internal List of Bukkit 43 | */ 44 | @SuppressWarnings("unchecked") 45 | public static List getPlayerList(Server server) { 46 | List players = new ArrayList(); 47 | try { 48 | Method getCount = server.getClass().getMethod("getOnlinePlayers"); 49 | 50 | // add all player into our list 51 | if (getCount.getReturnType() == Array.class) 52 | players.addAll(Arrays.asList((Player[]) getCount.invoke(server))); 53 | else 54 | players.addAll((Collection) getCount.invoke(server)); 55 | 56 | // silent cause we are sure that at least one exist 57 | } catch (Exception e) {} 58 | 59 | return players; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bukkit/dump/VolatileField.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. 3 | * Copyright (C) 2012 Kristian S. Stangeland 4 | * 5 | * This program is free software; you can redistribute it and/or modify it under the terms of the 6 | * GNU General Public License as published by the Free Software Foundation; either version 2 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 10 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 11 | * See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with this program; 14 | * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 15 | * 02111-1307 USA 16 | */ 17 | package fr.vmarchaud.mineweb.bukkit.dump; 18 | 19 | import java.lang.reflect.Field; 20 | 21 | import com.comphenix.protocol.ProtocolLogger; 22 | import com.comphenix.protocol.reflect.accessors.Accessors; 23 | import com.comphenix.protocol.reflect.accessors.FieldAccessor; 24 | 25 | /** 26 | * Represents a field that will revert to its original state when this class is garbaged collected. 27 | * 28 | * @author Kristian 29 | */ 30 | public class VolatileField { 31 | private final FieldAccessor accessor; 32 | private final Object container; 33 | private Object previous; 34 | private Object current; 35 | private boolean previousLoaded; 36 | private boolean currentSet; 37 | private boolean forceAccess; 38 | 39 | /** 40 | * Initializes a volatile field with an associated object. 41 | * @param field - the field. 42 | * @param container - the object this field belongs to. 43 | * @param forceAccess - whether or not to override any scope restrictions. 44 | */ 45 | public VolatileField(Field field, Object container, boolean forceAccess) { 46 | this.accessor = Accessors.getFieldAccessor(field); 47 | this.container = container; 48 | this.forceAccess = forceAccess; 49 | } 50 | 51 | public VolatileField(FieldAccessor accessor, Object container) { 52 | this.accessor = accessor; 53 | this.container = container; 54 | } 55 | 56 | /** 57 | * Retrieves the current field. 58 | * @return The stored field. 59 | */ 60 | public Field getField() { 61 | return accessor.getField(); 62 | } 63 | 64 | /** 65 | * Retrieves the current field value. 66 | * @return The current field value. 67 | */ 68 | public Object getValue() { 69 | // Retrieve the correct value 70 | if (!currentSet) { 71 | ensureLoaded(); 72 | return previous; 73 | } else { 74 | return current; 75 | } 76 | } 77 | 78 | /** 79 | * Sets the current value. This will be reverted unless saveValue() is called. 80 | * @param newValue - new field value. 81 | */ 82 | public void setValue(Object newValue) { 83 | // Remember to safe the previous value 84 | ensureLoaded(); 85 | 86 | writeFieldValue(newValue); 87 | current = newValue; 88 | currentSet = true; 89 | } 90 | 91 | /** 92 | * Revert to the previously set value. 93 | */ 94 | public void revertValue() { 95 | // Reset value if it hasn't been changed by anyone else 96 | if (currentSet) { 97 | if (getValue() == current) { 98 | setValue(previous); 99 | currentSet = false; 100 | } else { 101 | // This can be a bad sign 102 | ProtocolLogger.log("Unable to switch {0} to {1}. Expected {2}, but got {3}.", getField().toGenericString(), previous, current, getValue()); 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * Retrieve a synchronized version of the current field. 109 | * @return A synchronized volatile field. 110 | */ 111 | public VolatileField toSynchronized() { 112 | return new VolatileField(new SynchronizedFieldAccessor(accessor), container); 113 | } 114 | 115 | private void ensureLoaded() { 116 | // Load the value if we haven't already 117 | if (!previousLoaded) { 118 | previous = readFieldValue(); 119 | previousLoaded = true; 120 | } 121 | } 122 | 123 | /** 124 | * Read the content of the underlying field. 125 | * @return The field value. 126 | */ 127 | private Object readFieldValue() { 128 | return accessor.get(container); 129 | } 130 | 131 | /** 132 | * Write the given value to the underlying field. 133 | * @param newValue - the new value. 134 | */ 135 | private void writeFieldValue(Object newValue) { 136 | accessor.set(container, newValue); 137 | } 138 | 139 | @Override 140 | protected void finalize() throws Throwable { 141 | revertValue(); 142 | } 143 | 144 | @Override 145 | public String toString() { 146 | return "VolatileField [accessor=" + accessor + ", container=" + container + ", previous=" 147 | + previous + ", current=" + current + ", previousLoaded=" + previousLoaded 148 | + ", currentSet=" + currentSet + ", forceAccess=" + forceAccess + "]"; 149 | } 150 | } 151 | 152 | class SynchronizedFieldAccessor implements FieldAccessor { 153 | private final FieldAccessor accessor; 154 | SynchronizedFieldAccessor(FieldAccessor accessor) { 155 | this.accessor = accessor; 156 | } 157 | 158 | @Override 159 | public void set(Object instance, Object value) { 160 | Object lock = accessor.get(instance); 161 | 162 | if (lock != null) { 163 | synchronized (lock) { 164 | accessor.set(instance, value); 165 | } 166 | } else { 167 | accessor.set(instance, value); 168 | } 169 | } 170 | 171 | @Override 172 | public Object get(Object instance) { 173 | return accessor.get(instance); 174 | } 175 | 176 | @Override 177 | public Field getField() { 178 | return accessor.getField(); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bukkit/methods/BukkitGetBannedPlayers.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bukkit.methods; 25 | 26 | import java.util.Set; 27 | import java.util.stream.Collectors; 28 | 29 | import org.bukkit.OfflinePlayer; 30 | import org.bukkit.Server; 31 | 32 | import fr.vmarchaud.mineweb.common.ICore; 33 | import fr.vmarchaud.mineweb.common.IMethod; 34 | import fr.vmarchaud.mineweb.common.MethodHandler; 35 | 36 | @MethodHandler 37 | public class BukkitGetBannedPlayers implements IMethod { 38 | 39 | @Override 40 | public Object execute(ICore instance, Object... inputs) { 41 | Set wlp = ((Server)instance.getGameServer()).getBannedPlayers(); 42 | return wlp.stream().map(OfflinePlayer::getName).collect(Collectors.toList()); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bukkit/methods/BukkitGetMOTD.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bukkit.methods; 25 | 26 | import org.bukkit.Server; 27 | 28 | import fr.vmarchaud.mineweb.common.ICore; 29 | import fr.vmarchaud.mineweb.common.IMethod; 30 | import fr.vmarchaud.mineweb.common.MethodHandler; 31 | 32 | @MethodHandler 33 | public class BukkitGetMOTD implements IMethod { 34 | 35 | @Override 36 | public Object execute(ICore instance, Object... inputs) { 37 | 38 | if (instance.config().getMotd() == null || instance.config().getMotd().length() == 0) 39 | return ((Server)instance.getGameServer()).getMotd(); 40 | return instance.config().getMotd(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bukkit/methods/BukkitGetMaxPlayers.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bukkit.methods; 25 | 26 | import org.bukkit.Server; 27 | 28 | import fr.vmarchaud.mineweb.common.ICore; 29 | import fr.vmarchaud.mineweb.common.IMethod; 30 | import fr.vmarchaud.mineweb.common.MethodHandler; 31 | 32 | @MethodHandler 33 | public class BukkitGetMaxPlayers implements IMethod { 34 | 35 | @Override 36 | public Object execute(ICore instance, Object... inputs) { 37 | return ((Server)instance.getGameServer()).getMaxPlayers(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bukkit/methods/BukkitGetVersion.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bukkit.methods; 25 | 26 | import org.bukkit.Server; 27 | 28 | import fr.vmarchaud.mineweb.common.ICore; 29 | import fr.vmarchaud.mineweb.common.IMethod; 30 | import fr.vmarchaud.mineweb.common.MethodHandler; 31 | 32 | @MethodHandler 33 | public class BukkitGetVersion implements IMethod { 34 | 35 | @Override 36 | public Object execute(ICore instance, Object... inputs) { 37 | return ((Server)instance.getGameServer()).getBukkitVersion(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bukkit/methods/BukkitGetWhitelistedPlayers.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bukkit.methods; 25 | 26 | import java.util.Set; 27 | import java.util.stream.Collectors; 28 | import org.bukkit.OfflinePlayer; 29 | import org.bukkit.Server; 30 | 31 | import fr.vmarchaud.mineweb.common.ICore; 32 | import fr.vmarchaud.mineweb.common.IMethod; 33 | import fr.vmarchaud.mineweb.common.MethodHandler; 34 | 35 | @MethodHandler 36 | public class BukkitGetWhitelistedPlayers implements IMethod { 37 | 38 | @Override 39 | public Object execute(ICore instance, Object... inputs) { 40 | Set wlp = ((Server)instance.getGameServer()).getWhitelistedPlayers(); 41 | return wlp.stream().map(OfflinePlayer::getName).collect(Collectors.toList()); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bungee/BungeeCore.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bungee; 25 | 26 | import java.io.File; 27 | import java.io.IOException; 28 | import java.util.HashMap; 29 | import java.util.HashSet; 30 | import java.util.Map; 31 | import java.util.concurrent.TimeUnit; 32 | import java.util.logging.FileHandler; 33 | import java.util.logging.Level; 34 | import java.util.logging.Logger; 35 | 36 | import com.google.gson.Gson; 37 | import com.google.gson.GsonBuilder; 38 | 39 | import fr.vmarchaud.mineweb.bungee.methods.BungeeGetMOTD; 40 | import fr.vmarchaud.mineweb.bungee.methods.BungeeGetMaxPlayers; 41 | import fr.vmarchaud.mineweb.bungee.methods.BungeeGetVersion; 42 | import fr.vmarchaud.mineweb.common.CommandScheduler; 43 | import fr.vmarchaud.mineweb.common.ICore; 44 | import fr.vmarchaud.mineweb.common.IMethod; 45 | import fr.vmarchaud.mineweb.common.RequestHandler; 46 | import fr.vmarchaud.mineweb.common.configuration.PluginConfiguration; 47 | import fr.vmarchaud.mineweb.common.configuration.ScheduledStorage; 48 | import fr.vmarchaud.mineweb.common.injector.NettyInjector; 49 | import fr.vmarchaud.mineweb.common.injector.WebThread; 50 | import fr.vmarchaud.mineweb.common.injector.router.RouteMatcher; 51 | import fr.vmarchaud.mineweb.common.methods.*; 52 | import fr.vmarchaud.mineweb.utils.CustomLogFormatter; 53 | import fr.vmarchaud.mineweb.utils.http.HttpResponseBuilder; 54 | import net.md_5.bungee.api.plugin.Plugin; 55 | import net.md_5.bungee.api.scheduler.ScheduledTask; 56 | 57 | public class BungeeCore extends Plugin implements ICore { 58 | 59 | public static ICore instance; 60 | public static ICore get() { 61 | return instance; 62 | } 63 | 64 | private RouteMatcher httpRouter; 65 | private NettyInjector injector; 66 | private WebThread nettyServerThread; 67 | private HashMap methods; 68 | private RequestHandler requestHandler; 69 | private PluginConfiguration config; 70 | private ScheduledStorage storage; 71 | private CommandScheduler commandScheduler; 72 | private ScheduledTask task; 73 | 74 | /** Cached player list to not rely on Reflection on every request **/ 75 | private HashSet players; 76 | 77 | private Logger logger = Logger.getLogger("Mineweb"); 78 | 79 | private Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create(); 80 | private FileHandler fileHandler; 81 | 82 | public void onEnable() { 83 | instance = this; 84 | 85 | // load config 86 | config = PluginConfiguration.load(new File(getDataFolder(), "config.json"), instance); 87 | storage = ScheduledStorage.load(new File(getDataFolder(), "commands.json"), instance); 88 | // directly setup logger 89 | setupLogger(); 90 | 91 | // Init 92 | logger.info("Loading ..."); 93 | methods = new HashMap(); 94 | players = new HashSet(); 95 | injector = new BungeeNettyInjector(this); 96 | httpRouter = new RouteMatcher(); 97 | logger.info("Registering route ..."); 98 | registerRoutes(); 99 | getProxy().getPluginManager().registerListener(this, new BungeeListeners(instance)); 100 | 101 | // inject when we are ready 102 | if(config.port == null) { 103 | logger.info("Injecting http server ..."); 104 | injector.inject(); 105 | } else { 106 | logger.info("Starting http server thread ..."); 107 | nettyServerThread = new WebThread(this); 108 | nettyServerThread.start(); 109 | } 110 | logger.info("Registering methods ..."); 111 | requestHandler = new RequestHandler(instance); 112 | registerMethods(); 113 | logger.info("Starting CommandScheduler ..."); 114 | commandScheduler = new CommandScheduler(instance, storage); 115 | task = getProxy().getScheduler().schedule(this, commandScheduler, 5, TimeUnit.SECONDS); 116 | logger.info("Ready !"); 117 | } 118 | 119 | @Override 120 | public void onDisable() { 121 | if (task != null) task.cancel(); 122 | if (commandScheduler != null) commandScheduler.save(); 123 | if (logger != null) logger.info("Shutting down ..."); 124 | if (fileHandler != null) fileHandler.close(); 125 | } 126 | 127 | public void registerRoutes() { 128 | httpRouter.everyMatch((event) -> { 129 | logger.fine(String.format("[HTTP Request] %d %s on %s", event.getRes().getStatus().code(), 130 | event.getRequest().getMethod().toString(), event.getRequest().getUri())); 131 | return null; 132 | }); 133 | 134 | httpRouter.get("/", (event) -> { 135 | return HttpResponseBuilder.ok(); 136 | }); 137 | } 138 | 139 | public void registerMethods() { 140 | // common methods 141 | methods.put("GET_PLAYER_LIST", new CommonGetPlayerList()); 142 | methods.put("GET_PLAYER_COUNT", new CommonGetPlayerCount()); 143 | methods.put("IS_CONNECTED", new CommonIsConnected()); 144 | methods.put("GET_PLUGIN_TYPE", new CommonPluginType()); 145 | methods.put("GET_SYSTEM_STATS", new CommonGetSystemStats()); 146 | methods.put("RUN_COMMAND", new CommonRunCommand()); 147 | methods.put("RUN_SCHEDULED_COMMAND", new CommonScheduledCommand()); 148 | methods.put("GET_SERVER_TIMESTAMP", new CommonGetTimestamp()); 149 | methods.put("SET_MOTD", new CommonSetMotd()); 150 | 151 | // bungee methods 152 | methods.put("GET_MAX_PLAYERS", new BungeeGetMaxPlayers()); 153 | methods.put("GET_MOTD", new BungeeGetMOTD()); 154 | methods.put("GET_VERSION", new BungeeGetVersion()); 155 | } 156 | 157 | public void setupLogger() { 158 | try { 159 | logger.setLevel(Level.parse(config.getLogLevel())); 160 | logger.setUseParentHandlers(false); 161 | new File(getDataFolder() + File.separator).mkdirs(); 162 | fileHandler = new FileHandler(getDataFolder() + File.separator + "mineweb.log", true); 163 | fileHandler.setFormatter(new CustomLogFormatter()); 164 | logger.addHandler(fileHandler); 165 | } catch (SecurityException e) { 166 | e.printStackTrace(); 167 | } catch (IOException e) { 168 | e.printStackTrace(); 169 | } 170 | } 171 | 172 | @Override 173 | public RouteMatcher getHTTPRouter() { 174 | return httpRouter; 175 | } 176 | 177 | @Override 178 | public Object getGameServer() { 179 | return this.getProxy(); 180 | } 181 | 182 | @Override 183 | public Object getPlugin() { 184 | return this; 185 | } 186 | 187 | @Override 188 | public EnumPluginType getType() { 189 | return EnumPluginType.BUNGEE; 190 | } 191 | 192 | @Override 193 | public HashSet getPlayers() { 194 | return players; 195 | } 196 | 197 | @Override 198 | public Logger logger() { 199 | return logger; 200 | } 201 | 202 | @Override 203 | public Gson gson() { 204 | return gson; 205 | } 206 | 207 | @Override 208 | public Map getMethods() { 209 | return methods; 210 | } 211 | 212 | @Override 213 | public PluginConfiguration config() { 214 | return config; 215 | } 216 | 217 | @Override 218 | public RequestHandler requestHandler() { 219 | return requestHandler; 220 | } 221 | 222 | @Override 223 | public void runCommand(String command) { 224 | getProxy().getPluginManager().dispatchCommand(getProxy().getConsole(), command); 225 | } 226 | 227 | @Override 228 | public CommandScheduler getCommandScheduler() { 229 | return commandScheduler; 230 | } 231 | 232 | public NettyInjector getInjector() { 233 | return injector; 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bungee/BungeeListeners.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MineWeb/ServerBridge/154f4bb11a59e9f06cd3118b0496a504e2bd391f/src/main/java/fr/vmarchaud/mineweb/bungee/BungeeListeners.java -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bungee/BungeeNettyInjector.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bungee; 25 | 26 | import fr.vmarchaud.mineweb.common.ICore; 27 | import fr.vmarchaud.mineweb.common.injector.JSONAPIChannelDecoder; 28 | import fr.vmarchaud.mineweb.common.injector.NettyInjector; 29 | import io.netty.channel.Channel; 30 | import io.netty.channel.ChannelInitializer; 31 | import net.md_5.bungee.BungeeCord; 32 | import net.md_5.bungee.api.ProxyServer; 33 | import net.md_5.bungee.api.config.ListenerInfo; 34 | import net.md_5.bungee.connection.InitialHandler; 35 | import net.md_5.bungee.netty.HandlerBoss; 36 | import net.md_5.bungee.netty.PipelineUtils; 37 | import net.md_5.bungee.protocol.*; 38 | 39 | import java.lang.reflect.Field; 40 | import java.lang.reflect.Method; 41 | import java.lang.reflect.Modifier; 42 | 43 | public class BungeeNettyInjector extends NettyInjector { 44 | 45 | private ICore api; 46 | 47 | public BungeeNettyInjector(ICore api) { 48 | this.api = api; 49 | } 50 | 51 | public synchronized void inject() { 52 | if (injected) 53 | throw new IllegalStateException("Cannot inject twice."); 54 | 55 | try { 56 | // get the field that will setup the channel and inject our handler 57 | Class server = PipelineUtils.class; 58 | Field field = server.getDeclaredField("SERVER_CHILD"); 59 | //Field modifiersField = Field.class.getDeclaredField("modifiers"); 60 | 61 | Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); 62 | getDeclaredFields0.setAccessible(true); 63 | Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false); 64 | Field modifiersField = null; 65 | for (Field each : fields) { 66 | if ("modifiers".equals(each.getName())) { 67 | modifiersField = each; 68 | break; 69 | } 70 | } 71 | assert modifiersField != null; 72 | field.setAccessible(true); 73 | modifiersField.setAccessible(true); 74 | modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 75 | 76 | // set the new value 77 | field.set(null, new ChannelInitializer() { 78 | 79 | @SuppressWarnings("deprecation") 80 | protected void initChannel(Channel ch) throws Exception { 81 | // inject here 82 | injectChannel(ch); 83 | 84 | // and let the original code run 85 | ListenerInfo listener = ch.attr( PipelineUtils.LISTENER ).get(); 86 | 87 | PipelineUtils.BASE.initChannel(ch); 88 | ch.pipeline().addBefore(PipelineUtils.FRAME_DECODER, PipelineUtils.LEGACY_DECODER, new LegacyDecoder()); 89 | ch.pipeline().addAfter(PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder(Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion())); 90 | ch.pipeline().addAfter(PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder(Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion())); 91 | ch.pipeline().addBefore(PipelineUtils.FRAME_PREPENDER, PipelineUtils.LEGACY_KICKER, new KickStringWriter()); 92 | ch.pipeline().get(HandlerBoss.class) .setHandler(new InitialHandler(BungeeCord.getInstance(), listener)); 93 | 94 | //if (listener.getClass().getMethod("isProxyProtocol").toString() != null && listener.isProxyProtocol()) 95 | //{ 96 | // ch.pipeline().addFirst(new io.netty.handler.codec.haproxy.HAProxyMessageDecoder.HAProxyMessageDecoder()); 97 | //} 98 | } 99 | }); 100 | injected = true; 101 | 102 | } catch (Exception e) { 103 | throw new RuntimeException("Unable to inject channel futures.", e); 104 | } 105 | } 106 | 107 | @Override 108 | protected void injectChannel(final Channel channel) { 109 | channel.pipeline().addFirst(new JSONAPIChannelDecoder(api)); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bungee/methods/BungeeGetMOTD.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bungee.methods; 25 | 26 | import fr.vmarchaud.mineweb.common.ICore; 27 | import fr.vmarchaud.mineweb.common.IMethod; 28 | import fr.vmarchaud.mineweb.common.MethodHandler; 29 | import fr.vmarchaud.mineweb.common.configuration.PluginConfiguration; 30 | import net.md_5.bungee.api.ProxyServer; 31 | 32 | @MethodHandler 33 | public class BungeeGetMOTD implements IMethod { 34 | 35 | @Override 36 | public Object execute(ICore instance, Object... inputs) { 37 | PluginConfiguration config = instance.config(); 38 | if (config.getMotd() == null || config.getMotd().length() == 0) 39 | return ((ProxyServer) instance.getGameServer()).getConfigurationAdapter().getListeners().iterator().next() 40 | .getMotd(); 41 | return config.getMotd(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bungee/methods/BungeeGetMaxPlayers.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bungee.methods; 25 | 26 | import fr.vmarchaud.mineweb.common.ICore; 27 | import fr.vmarchaud.mineweb.common.IMethod; 28 | import fr.vmarchaud.mineweb.common.MethodHandler; 29 | import net.md_5.bungee.api.ProxyServer; 30 | 31 | @MethodHandler 32 | public class BungeeGetMaxPlayers implements IMethod { 33 | 34 | @Override 35 | public Object execute(ICore instance, Object... inputs) { 36 | return ((ProxyServer)instance.getGameServer()).getConfig().getPlayerLimit(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/bungee/methods/BungeeGetVersion.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.bungee.methods; 25 | 26 | import fr.vmarchaud.mineweb.common.ICore; 27 | import fr.vmarchaud.mineweb.common.IMethod; 28 | import fr.vmarchaud.mineweb.common.MethodHandler; 29 | import net.md_5.bungee.api.ProxyServer; 30 | 31 | @MethodHandler 32 | public class BungeeGetVersion implements IMethod { 33 | 34 | @Override 35 | public Object execute(ICore instance, Object... inputs) { 36 | return ((ProxyServer)instance.getGameServer()).getVersion(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/Command.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data @AllArgsConstructor 7 | public class Command { 8 | private String name; 9 | private Object[] args; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/CommandScheduler.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common; 2 | 3 | import java.util.Date; 4 | import java.util.Iterator; 5 | import java.util.Set; 6 | import java.util.concurrent.ConcurrentLinkedQueue; 7 | import fr.vmarchaud.mineweb.common.configuration.ScheduledStorage; 8 | import lombok.Getter; 9 | 10 | public class CommandScheduler implements Runnable { 11 | 12 | private ICore api; 13 | private ScheduledStorage storage; 14 | private Set commands; 15 | 16 | @Getter 17 | private ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); 18 | 19 | /** 20 | * ScheduledManager 21 | * 22 | * Used to manage the command that are scheduled for the future (check/addition) 23 | * 24 | * @param ICore api 25 | * @param ScheduledStorage scheduled command storage 26 | */ 27 | public CommandScheduler(ICore api, ScheduledStorage storage) { 28 | this.api = api; 29 | this.storage = storage; 30 | this.commands = storage.getCommands(); 31 | } 32 | 33 | @Override 34 | public void run() { 35 | Iterator it = commands.iterator(); 36 | Date now = new Date(); 37 | 38 | while(it.hasNext()) { 39 | ScheduledCommand command = it.next(); 40 | // if the command is in the future, continue 41 | if (new Date(command.getTimestamp()).after(now)) 42 | continue; 43 | // if the command need a player to be online and this player isn't connected, continue 44 | //if (command.getPlayer() != null && !api.getPlayers().contains(command.getPlayer())) 45 | // continue; 46 | // otherwise run the command and remove it from the list 47 | api.runCommand(command.getCommand()); 48 | it.remove(); 49 | } 50 | // add the new command for the next iteration 51 | ScheduledCommand next = null; 52 | while((next = queue.poll()) != null) { 53 | commands.add(next); 54 | } 55 | 56 | // save them on the disk 57 | save(); 58 | } 59 | 60 | /** 61 | * Save the cached commands in FS 62 | */ 63 | public void save() { 64 | storage.setCommands(commands); 65 | storage.save(api); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/ICore.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.common; 25 | 26 | import java.util.Map; 27 | import java.util.Set; 28 | import java.util.logging.Logger; 29 | 30 | import com.google.gson.Gson; 31 | 32 | import fr.vmarchaud.mineweb.common.configuration.PluginConfiguration; 33 | import fr.vmarchaud.mineweb.common.injector.router.RouteMatcher; 34 | 35 | public interface ICore { 36 | 37 | /** 38 | * Get the router that handle http connection 39 | * @return RouteMatcher 40 | */ 41 | public RouteMatcher getHTTPRouter(); 42 | 43 | /** 44 | * Get the game server instance 45 | * @return ProxyServer for bungee OR Server for bukkit 46 | */ 47 | public Object getGameServer(); 48 | 49 | /** 50 | * Get the plugin instance 51 | * @return Plugin instance for bungee OR JavaPlugin for bukkit 52 | */ 53 | public Object getPlugin(); 54 | 55 | /** 56 | * Get the type of plugin that is running 57 | * @return EnumPluginType 58 | */ 59 | public EnumPluginType getType(); 60 | 61 | /** 62 | * Get a cached list of online players 63 | * @return List of online players name 64 | */ 65 | public Set getPlayers(); 66 | 67 | /** 68 | * Get instance of the implemented api 69 | * @return ICore interface of the plugin 70 | */ 71 | public static ICore get() { return null; } 72 | 73 | /** 74 | * Get custom logger of the plugin 75 | * @return Logger instance 76 | */ 77 | public Logger logger(); 78 | 79 | /** 80 | * Get our custom gson instance 81 | * @return 82 | */ 83 | public Gson gson(); 84 | 85 | /** 86 | * Return the configuration used to store data 87 | * @return Configuration instance 88 | */ 89 | public PluginConfiguration config(); 90 | 91 | /** 92 | * Return the request handler that will cipher/decipher request 93 | * @return RequestHandler instance 94 | */ 95 | public RequestHandler requestHandler(); 96 | 97 | /** 98 | * Get all registered methods 99 | * @return Map 100 | */ 101 | public Map getMethods(); 102 | 103 | 104 | /** 105 | * Get the scheduled command Manager 106 | * 107 | * @return ScheduledManager 108 | */ 109 | public CommandScheduler getCommandScheduler(); 110 | 111 | /** 112 | * Run a command on the gameserver 113 | * 114 | * @param the command to execute 115 | */ 116 | public void runCommand(String command); 117 | 118 | public enum EnumPluginType { 119 | BUKKIT, BUNGEE; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/IMethod.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.common; 25 | 26 | @MethodHandler 27 | public interface IMethod { 28 | 29 | /** 30 | * This method will be executed when a request come 31 | * @param instance: API interface 32 | * @return inputs: array of object passed to the method 33 | */ 34 | public Object execute(ICore instance, Object... inputs); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/MethodHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.common; 25 | 26 | import java.lang.annotation.ElementType; 27 | import java.lang.annotation.Retention; 28 | import java.lang.annotation.RetentionPolicy; 29 | import java.lang.annotation.Target; 30 | 31 | @Retention(RetentionPolicy.RUNTIME) 32 | @Target(ElementType.TYPE) 33 | public @interface MethodHandler { 34 | 35 | int inputs() default 0; 36 | 37 | @SuppressWarnings("rawtypes") 38 | Class[] types() default Void.class; 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/RequestHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.common; 25 | 26 | import java.io.StringReader; 27 | import java.lang.reflect.Type; 28 | import java.nio.charset.Charset; 29 | import java.util.List; 30 | 31 | import javax.crypto.spec.SecretKeySpec; 32 | 33 | import com.google.gson.JsonArray; 34 | import com.google.gson.JsonObject; 35 | import com.google.gson.reflect.TypeToken; 36 | import com.google.gson.stream.JsonReader; 37 | 38 | import fr.vmarchaud.mineweb.common.interactor.requests.AskRequest; 39 | import fr.vmarchaud.mineweb.common.interactor.requests.HandshakeRequest; 40 | import fr.vmarchaud.mineweb.common.interactor.responses.AskResponse; 41 | import fr.vmarchaud.mineweb.common.interactor.responses.HandshakeResponse; 42 | import fr.vmarchaud.mineweb.utils.CryptoUtils; 43 | import fr.vmarchaud.mineweb.utils.http.HttpResponseBuilder; 44 | import io.netty.buffer.ByteBuf; 45 | import io.netty.handler.codec.http.FullHttpRequest; 46 | import io.netty.handler.codec.http.FullHttpResponse; 47 | import io.netty.handler.codec.http.HttpResponseStatus; 48 | 49 | public class RequestHandler { 50 | 51 | private ICore api; 52 | private SecretKeySpec key; 53 | private boolean debug; 54 | 55 | /** 56 | * Construct an instance of the RequestHandler that will validate and respond to inbound request 57 | * @param api: instance of the API 58 | */ 59 | public RequestHandler(ICore api) { 60 | this.api = api; 61 | if (api.config().getSecretkey() != null) 62 | this.key = new SecretKeySpec(api.config().getSecretkey().getBytes(), "AES"); 63 | 64 | // register handshake endpoint 65 | api.getHTTPRouter().post("/handshake", (request) -> this.handleHandshake(request.getRequest())); 66 | api.getHTTPRouter().post("/ask", (request) -> this.handle(request.getRequest())); 67 | debug = System.getenv("DEBUG") != null && System.getenv("DEBUG").equals("true"); 68 | } 69 | 70 | /** 71 | * Refresh the secretkey used to cipher and decipher request 72 | * @param secretKey: String containing the 32 bytes key 73 | */ 74 | public void refreshKey(String secretKey) { 75 | this.key = new SecretKeySpec(secretKey.getBytes(), "AES"); 76 | } 77 | 78 | /** 79 | * Handle special request from CMS (handshake) 80 | * Retrieve secret-key -> save it in configuration -> send it back as succesfull handshake. 81 | * 82 | * @param request: HttpRequest that contains data required for handshake 83 | * @return httpResponse: Response object to send back to the client 84 | * @throws Exception 85 | */ 86 | public FullHttpResponse handleHandshake(FullHttpRequest httpRequest) { 87 | ByteBuf buf = httpRequest.content(); 88 | String content = buf.toString(buf.readerIndex(), buf.readableBytes(), Charset.forName("UTF-8")); 89 | HandshakeRequest handshake = api.gson().fromJson(content, HandshakeRequest.class); 90 | 91 | api.logger().info(String.format("New Handshake id: %s (%s, %s)", 92 | handshake.getId(), handshake.getSecretKey(), handshake.getDomain())); 93 | 94 | if (!handshake.isValid()) { 95 | api.logger().info(String.format("Handshake failed id: %s (reason: invalid params)", handshake.getId())); 96 | return new HttpResponseBuilder().code(HttpResponseStatus.BAD_REQUEST).build(); 97 | } 98 | 99 | if (api.config().getSecretkey() != null) { 100 | api.logger().info(String.format("Handshake failed id: %s (reason: already linked)", handshake.getId())); 101 | return new HttpResponseBuilder().code(HttpResponseStatus.FORBIDDEN).build(); 102 | } 103 | try { 104 | HandshakeResponse response = new HandshakeResponse(); 105 | 106 | // save all the stuff inside the configuration 107 | String secret = handshake.getSecretKey(); 108 | api.config().setSecretkey(secret); 109 | api.config().setDomain(handshake.getDomain()); 110 | api.config().save(api); 111 | response.setMsg("Successfully retrieved secret key, now ready !"); 112 | response.setStatus(true); 113 | api.logger().info(String.format("Handshake request %s has been successfully valided (secret: %s)", handshake.getId(), secret)); 114 | this.refreshKey(secret); 115 | 116 | return new HttpResponseBuilder().code(HttpResponseStatus.OK).json(api.gson().toJson(response)).build(); 117 | } catch (Exception e) { 118 | if (debug) e.printStackTrace(); 119 | return new HttpResponseBuilder().code(HttpResponseStatus.INTERNAL_SERVER_ERROR).json(api.gson().toJson(e.getMessage())).build(); 120 | } 121 | } 122 | 123 | /** 124 | * Handle general request from CMS 125 | * 126 | * @param httpRequest: Request object that contains ciphered CMS's request 127 | * @return httpResponse: Response object to send back to the client 128 | */ 129 | public FullHttpResponse handle(FullHttpRequest httpRequest) { 130 | if (api.config().getSecretkey() == null) { 131 | api.logger().severe("Secret key isnt defined, please setup like wrote in the mineweb documentation."); 132 | return new HttpResponseBuilder().code(HttpResponseStatus.NOT_IMPLEMENTED).build(); 133 | } 134 | 135 | ByteBuf buf = httpRequest.content(); 136 | String content = buf.toString(buf.readerIndex(), buf.readableBytes(), Charset.forName("UTF-8")); 137 | 138 | List requests; 139 | AskRequest request; 140 | JsonArray response = new JsonArray(); 141 | Type token = new TypeToken>(){}.getType(); 142 | 143 | try { 144 | // parse json to map 145 | request = api.gson().fromJson(content, AskRequest.class); 146 | // if in debug, request is done in plaintext 147 | String tmp = debug ? request.getSigned() : CryptoUtils.decryptAES(request.getSigned(), this.key, request.getIv()); 148 | JsonReader reader = new JsonReader(new StringReader(tmp)); 149 | reader.setLenient(true); 150 | requests = api.gson().fromJson(reader, token); 151 | } catch (Exception e) { 152 | api.logger().severe(String.format("Cant decipher/parse a request : %s", e.getMessage())); 153 | if (debug) e.printStackTrace(); 154 | return HttpResponseBuilder.status(HttpResponseStatus.INTERNAL_SERVER_ERROR); 155 | } 156 | 157 | for(Command command : requests) { 158 | IMethod method = api.getMethods().get(command.getName()); 159 | Object[] inputs = command.getArgs(); 160 | 161 | // if we didnt found the method just continue 162 | if (method == null) { 163 | JsonObject res = new JsonObject(); 164 | res.addProperty("name", command.getName()); 165 | res.addProperty("response", "NOT_FOUND"); 166 | response.add(res); 167 | continue ; 168 | } 169 | 170 | // verify if the params size are same as requested by the method handler 171 | MethodHandler annot = method.getClass().getDeclaredAnnotation(MethodHandler.class); 172 | if (annot == null) { 173 | JsonObject res = new JsonObject(); 174 | res.addProperty("name", command.getName()); 175 | res.addProperty("response", "INVALID_IMPLEMENTED_METHOD"); 176 | response.add(res); 177 | continue ; 178 | } 179 | 180 | if (annot.inputs() != inputs.length) { 181 | JsonObject res = new JsonObject(); 182 | res.addProperty("name", command.getName()); 183 | res.addProperty("response", "BAD_REQUEST_ARGS_LENGTH"); 184 | response.add(res); 185 | continue ; 186 | } 187 | 188 | // verify class type of input 189 | if (annot.inputs() > 0) { 190 | boolean valid = true; 191 | for(int i = 0; i < annot.types().length; i++) { 192 | if (debug) { 193 | api.logger().fine("Comparing input " + inputs[i] + " of class " 194 | + inputs[i].getClass().getName() + " to class " + annot.types()[i].getName()); 195 | } 196 | if (!inputs[i].getClass().getName().equals(annot.types()[i].getName())) { 197 | valid = false; 198 | break ; 199 | } 200 | } 201 | if (!valid) { 202 | JsonObject res = new JsonObject(); 203 | res.addProperty("name", command.getName()); 204 | res.addProperty("response", "BAD_REQUEST_ARGS_TYPE"); 205 | response.add(res); 206 | continue; 207 | } 208 | } 209 | // execute the method and put the result into the response 210 | Object output = method.execute(api, inputs); 211 | JsonObject res = new JsonObject(); 212 | res.addProperty("name", command.getName()); 213 | res.add("response", api.gson().toJsonTree(output)); 214 | response.add(res); 215 | } 216 | 217 | api.logger().fine(String.format("request %s : %s", httpRequest.hashCode(), api.gson().toJson(requests))); 218 | api.logger().fine(String.format("response %s : %s", httpRequest.hashCode() , api.gson().toJson(response))); 219 | 220 | try { 221 | // try to cipher the data and send it 222 | AskResponse askResponse = new AskResponse(); 223 | String json = api.gson().toJson(response); 224 | // dont cipher in debug mode 225 | askResponse.setSigned(debug ? json : CryptoUtils.encryptAES(json, key, request.getIv())); 226 | askResponse.setIv(request.getIv()); 227 | return new HttpResponseBuilder().json(api.gson().toJson(askResponse)).code(HttpResponseStatus.OK).build(); 228 | } catch (Exception e) { 229 | api.logger().severe(String.format("Cant cipher/serialize a response : %s", e.getMessage())); 230 | return HttpResponseBuilder.status(HttpResponseStatus.INTERNAL_SERVER_ERROR); 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/ScheduledCommand.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data @AllArgsConstructor 7 | public class ScheduledCommand { 8 | 9 | private String command; 10 | private String player; 11 | private Long timestamp; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/configuration/PluginConfiguration.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.configuration; 2 | 3 | import java.io.File; 4 | import java.io.FileReader; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | 8 | import fr.vmarchaud.mineweb.common.ICore; 9 | import lombok.Data; 10 | 11 | @Data 12 | public class PluginConfiguration { 13 | 14 | public transient File path; 15 | 16 | public String logLevel = "FINE"; 17 | public String secretkey; 18 | public String motd; 19 | public String domain; 20 | public Integer port; 21 | 22 | public PluginConfiguration(File path) { 23 | this.path = path; 24 | } 25 | 26 | /** 27 | * Load the configuration from the file 28 | * 29 | * @param path object representing the path of the file 30 | * @param api interface for logging and use gson instance 31 | */ 32 | public static PluginConfiguration load(File path, ICore api) { 33 | if (path.exists()) { 34 | FileReader reader = null; 35 | try { 36 | reader = new FileReader(path); 37 | PluginConfiguration conf = api.gson().fromJson(reader, PluginConfiguration.class); 38 | conf.path = path; 39 | return conf; 40 | } catch (Exception e) { 41 | api.logger().warning("Config file is invalid, replacing with a new one (" + e.getMessage() + ")"); 42 | return new PluginConfiguration(path); 43 | } finally { 44 | if (reader != null) { 45 | try { 46 | reader.close(); 47 | } catch (IOException e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | } 52 | } else { 53 | api.logger().warning("Cant find a config file, creating it"); 54 | PluginConfiguration config = new PluginConfiguration(path); 55 | config.save(api); 56 | return config; 57 | } 58 | } 59 | 60 | /** 61 | * Save the configuration to the file 62 | * 63 | * @param api interface for logging and use gson instance 64 | */ 65 | public void save(ICore api) { 66 | try { 67 | // Create the folder 68 | new File(path.getParent()).mkdirs(); 69 | // Create the file 70 | path.createNewFile(); 71 | // Write it 72 | String config = api.gson().toJson(this); 73 | FileWriter writer = new FileWriter(path); 74 | writer.write(config); 75 | writer.close(); 76 | } catch (IOException e) { 77 | api.logger().severe("Cant save the config file " + e.getMessage()); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/configuration/ScheduledStorage.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.configuration; 2 | 3 | import java.io.File; 4 | import java.io.FileReader; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | import com.google.gson.reflect.TypeToken; 11 | 12 | import fr.vmarchaud.mineweb.common.ICore; 13 | import fr.vmarchaud.mineweb.common.ScheduledCommand; 14 | import lombok.Data; 15 | 16 | @Data 17 | public class ScheduledStorage{ 18 | 19 | public File path; 20 | private Set commands = new HashSet(); 21 | private final static TypeToken> token = new TypeToken>(){}; 22 | 23 | public ScheduledStorage(File path) { 24 | this.path = path; 25 | } 26 | 27 | /** 28 | * Load the configuration from the file 29 | * @param File object representing the path of the file 30 | * @param ICore interface for logging and use gson instance 31 | * 32 | * @throws IllegalAccessException 33 | * @throws InstantiationException 34 | */ 35 | public static ScheduledStorage load(File path, ICore api) { 36 | if (path.exists()) { 37 | FileReader reader = null; 38 | try { 39 | reader = new FileReader(path); 40 | ScheduledStorage conf = api.gson().fromJson(reader, token.getType()); 41 | conf.path = path; 42 | return conf; 43 | } catch (Exception e) { 44 | //api.logger().warning("Config file is invalid, replacing with a new one"); 45 | return new ScheduledStorage(path); 46 | } finally { 47 | if (reader != null) { 48 | try { 49 | reader.close(); 50 | } catch (IOException e) {} 51 | } 52 | } 53 | } else { 54 | //api.logger().warning("Cant find a config file, creating it"); 55 | return new ScheduledStorage(path); 56 | } 57 | } 58 | 59 | /** 60 | * Save the configuration to the file 61 | * @param ICore interface for logging and use gson instance 62 | */ 63 | public void save(ICore api) { 64 | try { 65 | String config = api.gson().toJson(commands); 66 | FileWriter writer = new FileWriter(path); 67 | writer.write(config); 68 | writer.close(); 69 | } catch (IOException e) { 70 | api.logger().severe("Cant save the config file " + e.getMessage()); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/injector/JSONAPIChannelDecoder.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.injector; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelOption; 6 | import io.netty.channel.ChannelPipeline; 7 | import io.netty.handler.codec.ByteToMessageDecoder; 8 | import io.netty.handler.codec.http.HttpObjectAggregator; 9 | import io.netty.handler.codec.http.HttpServerCodec; 10 | 11 | import java.util.List; 12 | import java.util.NoSuchElementException; 13 | 14 | import fr.vmarchaud.mineweb.common.ICore; 15 | 16 | public class JSONAPIChannelDecoder extends ByteToMessageDecoder { 17 | 18 | private ICore api; 19 | 20 | public JSONAPIChannelDecoder(ICore api) { 21 | this.api = api; 22 | } 23 | 24 | @Override 25 | protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List list) throws Exception { 26 | // use 4 bytes to detect HTTP or abort 27 | if (buf.readableBytes() < 4) { 28 | return; 29 | } 30 | buf.retain(2); 31 | 32 | 33 | final int magic1 = buf.getUnsignedByte(buf.readerIndex()); 34 | final int magic2 = buf.getUnsignedByte(buf.readerIndex() + 1); 35 | final int magic3 = buf.getUnsignedByte(buf.readerIndex() + 2); 36 | final int magic4 = buf.getUnsignedByte(buf.readerIndex() + 3); 37 | ChannelPipeline p = ctx.channel().pipeline(); 38 | 39 | if (isHttp(magic1, magic2, magic3, magic4)) { 40 | ByteBuf copy = buf.copy(); 41 | ctx.channel().config().setOption(ChannelOption.TCP_NODELAY, true); 42 | 43 | try { 44 | while (p.removeLast() != null); 45 | } catch (NoSuchElementException e) {} 46 | 47 | p.addLast("codec-http", new HttpServerCodec()); 48 | p.addLast("aggregator", new HttpObjectAggregator(65536)); 49 | p.addLast("handler", new JSONAPIHandler(api)); 50 | 51 | p.fireChannelRead(copy); 52 | buf.release(); 53 | buf.release(); 54 | } else { 55 | // this is not a http channel so just remove our channel handler 56 | try { 57 | p.remove(this); 58 | } catch (NoSuchElementException e) { 59 | // probably okay, it just needs to be off 60 | System.out.println("NoSuchElementException"); 61 | } 62 | 63 | buf.release(); 64 | buf.release(); 65 | } 66 | } 67 | 68 | private boolean isHttp(int magic1, int magic2, int magic3, int magic4) { 69 | return magic1 == 'G' && magic2 == 'E' && magic3 == 'T' && magic4 == ' ' || // GET 70 | magic1 == 'P' && magic2 == 'O' && magic3 == 'S' && magic4 == 'T' || // POST 71 | magic1 == 'P' && magic2 == 'U' && magic3 == 'T' && magic4 == ' ' || // PUT 72 | magic1 == 'H' && magic2 == 'E' && magic3 == 'A' && magic4 == 'D' || // HEAD 73 | magic1 == 'O' && magic2 == 'P' && magic3 == 'T' && magic4 == 'I' || // OPTIONS 74 | magic1 == 'P' && magic2 == 'A' && magic3 == 'T' && magic4 == 'C' || // PATCH 75 | magic1 == 'D' && magic2 == 'E' && magic3 == 'L' && magic4 == 'E' || // DELETE 76 | magic1 == 'T' && magic2 == 'R' && magic3 == 'C' && magic4 == 'C' || // TRACE 77 | magic1 == 'C' && magic2 == 'O' && magic3 == 'N' && magic4 == 'N'; // CONNECT 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/injector/JSONAPIChannelReadHandler.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.injector; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.ByteBufInputStream; 5 | import io.netty.channel.Channel; 6 | import io.netty.channel.ChannelHandler; 7 | import io.netty.channel.ChannelHandler.Sharable; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.channel.ChannelInboundHandlerAdapter; 10 | import io.netty.channel.nio.NioEventLoopGroup; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map.Entry; 14 | 15 | import fr.vmarchaud.mineweb.common.ICore; 16 | 17 | @Sharable 18 | public class JSONAPIChannelReadHandler extends ChannelInboundHandlerAdapter { 19 | List> handlers = new ArrayList>(); 20 | NioEventLoopGroup eventGroup; 21 | ICore api; 22 | 23 | public JSONAPIChannelReadHandler(ICore api, final NioEventLoopGroup eventGroup) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { 24 | this.eventGroup = eventGroup; 25 | this.api = api; 26 | } 27 | 28 | @Override 29 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 30 | Channel child = (Channel) msg; 31 | 32 | child.pipeline().addFirst(new JSONAPIChannelDecoder(api)); 33 | this.eventGroup.register(child); 34 | 35 | ctx.fireChannelRead(msg); 36 | } 37 | 38 | public class HTTPRequest extends ByteBufInputStream { 39 | 40 | public HTTPRequest(ByteBuf buf) { 41 | super(buf); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/injector/JSONAPIHandler.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.injector; 2 | 3 | import io.netty.channel.ChannelFuture; 4 | import io.netty.channel.ChannelFutureListener; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.SimpleChannelInboundHandler; 7 | import io.netty.handler.codec.http.DefaultFullHttpResponse; 8 | import io.netty.handler.codec.http.FullHttpRequest; 9 | import io.netty.handler.codec.http.FullHttpResponse; 10 | import io.netty.handler.codec.http.HttpHeaders; 11 | import io.netty.handler.codec.http.HttpResponseStatus; 12 | import io.netty.handler.codec.http.HttpVersion; 13 | import fr.vmarchaud.mineweb.common.ICore; 14 | 15 | class JSONAPIHandler extends SimpleChannelInboundHandler { 16 | 17 | private ICore api; 18 | 19 | public JSONAPIHandler(ICore api) { 20 | this.api = api; 21 | } 22 | 23 | @Override 24 | protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { 25 | handleHttpRequest(ctx, (FullHttpRequest) msg); 26 | } 27 | 28 | @Override 29 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 30 | ctx.flush(); 31 | } 32 | 33 | private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { 34 | // Handle a bad request. 35 | if (!req.getDecoderResult().isSuccess()) { 36 | sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN)); 37 | return; 38 | } 39 | api.getHTTPRouter().serveRequest(ctx, req); 40 | } 41 | 42 | private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) { 43 | // Send the response and close the connection if necessary. 44 | ChannelFuture f = ctx.channel().writeAndFlush(res); 45 | if (!HttpHeaders.isKeepAlive(req) || res.getStatus().code() != 200) { 46 | f.addListener(ChannelFutureListener.CLOSE); 47 | } 48 | } 49 | 50 | @Override 51 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 52 | cause.printStackTrace(); 53 | ctx.close(); 54 | } 55 | 56 | @Override 57 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 58 | super.channelInactive(ctx); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/injector/NettyInjector.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.common.injector; 25 | 26 | import io.netty.channel.Channel; 27 | 28 | public abstract class NettyInjector { 29 | protected boolean injected; 30 | 31 | protected boolean closed; 32 | 33 | /** 34 | * Inject into the connection class. 35 | */ 36 | public abstract void inject(); 37 | 38 | 39 | /** 40 | * Invoked when a channel is ready to be injected. 41 | * @param channel - the channel to inject. 42 | */ 43 | protected abstract void injectChannel(Channel channel); 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/injector/NettyServer.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.injector; 2 | 3 | import fr.vmarchaud.mineweb.common.ICore; 4 | import io.netty.bootstrap.ServerBootstrap; 5 | import io.netty.channel.ChannelFuture; 6 | import io.netty.channel.ChannelInitializer; 7 | import io.netty.channel.ChannelOption; 8 | import io.netty.channel.ChannelPipeline; 9 | import io.netty.channel.EventLoopGroup; 10 | import io.netty.channel.nio.NioEventLoopGroup; 11 | import io.netty.channel.socket.SocketChannel; 12 | import io.netty.channel.socket.nio.NioServerSocketChannel; 13 | 14 | public class NettyServer { 15 | 16 | private ICore api; 17 | private ChannelFuture f; 18 | private EventLoopGroup bossGroup; 19 | private EventLoopGroup workerGroup; 20 | 21 | public NettyServer(ICore api) { 22 | this.api = api; 23 | } 24 | 25 | public void start() throws Exception { 26 | // Configure the server. 27 | this.bossGroup = new NioEventLoopGroup(1); 28 | this.workerGroup = new NioEventLoopGroup(); 29 | 30 | try { 31 | ServerBootstrap b = new ServerBootstrap(); 32 | b.group(bossGroup, workerGroup) 33 | .channel(NioServerSocketChannel.class) 34 | //.option(ChannelOption.SO_BACKLOG, 100) 35 | //.handler(new LoggingHandler(LogLevel.INFO)) 36 | .childHandler(new ChannelInitializer() { 37 | @Override 38 | public void initChannel(SocketChannel ch) throws Exception { 39 | ChannelPipeline p = ch.pipeline(); 40 | p.addFirst(new JSONAPIChannelDecoder(api)); 41 | } 42 | }); 43 | // Start the server. 44 | this.f = b.bind(api.config().getPort()).sync(); 45 | 46 | // Wait until the server socket is closed. 47 | this.f.channel().closeFuture().sync(); 48 | } finally { 49 | // Shut down all event loops to terminate all threads. 50 | this.bossGroup.shutdownGracefully(); 51 | this.workerGroup.shutdownGracefully(); 52 | } 53 | } 54 | 55 | public void stop() { 56 | try { 57 | f.channel().closeFuture().sync(); 58 | } catch (Exception e) { 59 | } 60 | bossGroup.shutdownGracefully(); 61 | workerGroup.shutdownGracefully(); 62 | } 63 | } -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/injector/WebThread.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.injector; 2 | 3 | import fr.vmarchaud.mineweb.common.ICore; 4 | import lombok.Getter; 5 | 6 | public class WebThread extends Thread { 7 | 8 | private final ICore api; 9 | @Getter 10 | private NettyServer webServer; 11 | 12 | public WebThread (ICore api) { 13 | this.api = api; 14 | this.webServer = new NettyServer(api); 15 | } 16 | 17 | @Override 18 | public void run() { 19 | try { 20 | webServer.start(); 21 | } catch (Exception e) { 22 | api.logger().info("HTTP server start failed! (" + e.getMessage() + ")"); 23 | this.interrupt(); 24 | } 25 | } 26 | 27 | public void stopThread() { 28 | try { 29 | webServer.stop(); 30 | interrupt(); 31 | } catch (Exception e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/injector/router/RouteMatcher.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.injector.router; 2 | 3 | import io.netty.channel.ChannelFuture; 4 | import io.netty.channel.ChannelFutureListener; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.SimpleChannelInboundHandler; 7 | import io.netty.handler.codec.http.DefaultFullHttpResponse; 8 | import io.netty.handler.codec.http.FullHttpRequest; 9 | import io.netty.handler.codec.http.FullHttpResponse; 10 | import io.netty.handler.codec.http.HttpHeaders; 11 | import io.netty.handler.codec.http.HttpMethod; 12 | import io.netty.handler.codec.http.HttpResponseStatus; 13 | import io.netty.handler.codec.http.HttpVersion; 14 | import io.netty.handler.codec.http.QueryStringDecoder; 15 | 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.HashSet; 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.Set; 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | 25 | import fr.vmarchaud.mineweb.utils.Handler; 26 | import fr.vmarchaud.mineweb.utils.http.RoutedHttpRequest; 27 | import fr.vmarchaud.mineweb.utils.http.RoutedHttpResponse; 28 | import fr.vmarchaud.mineweb.utils.regex.NamedMatcher; 29 | import fr.vmarchaud.mineweb.utils.regex.NamedPattern; 30 | 31 | /** 32 | * This class allows you to do route requests based on the HTTP verb and the 33 | * request URI, in a manner similar to Sinatra or Express. 36 | *

37 | * RouteMatcher also lets you extract parameters from the request URI either a 38 | * simple pattern or using regular expressions for more complex matches. Any 39 | * parameters extracted will be added to the requests parameters which will be 40 | * available to you in your request handler. 41 | *

42 | * It's particularly useful when writing REST-ful web applications. 43 | *

44 | * To use a simple pattern to extract parameters simply prefix the parameter 45 | * name in the pattern with a ':' (colon). 46 | *

47 | * Different handlers can be specified for each of the HTTP verbs, GET, POST, 48 | * PUT, DELETE etc. 49 | *

50 | * For more complex matches regular expressions can be used in the pattern. When 51 | * regular expressions are used, the extracted parameters do not have a name, so 52 | * they are put into the HTTP request with names of param0, param1, param2 etc. 53 | *

54 | * Multiple matches can be specified for each HTTP verb. In the case there are 55 | * more than one matching patterns for a particular request, the first matching 56 | * one will be used. 57 | *

58 | * Instances of this class are not thread-safe 59 | *

60 | * 61 | * @author Tim Fox 62 | * @author Alec Gorge 63 | */ 64 | public class RouteMatcher extends SimpleChannelInboundHandler { 65 | 66 | private final List getBindings = new ArrayList(); 67 | private final List putBindings = new ArrayList(); 68 | private final List postBindings = new ArrayList(); 69 | private final List deleteBindings = new ArrayList(); 70 | private final List optionsBindings = new ArrayList(); 71 | private final List headBindings = new ArrayList(); 72 | private final List traceBindings = new ArrayList(); 73 | private final List connectBindings = new ArrayList(); 74 | private final List patchBindings = new ArrayList(); 75 | private Handler noMatchHandler; 76 | private Handler everyMatchHandler; 77 | 78 | @Override 79 | protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { 80 | serveRequest(ctx, request); 81 | } 82 | 83 | List getBindingsForRequest(FullHttpRequest request) { 84 | HttpMethod m = request.getMethod(); 85 | if (m.equals(HttpMethod.GET)) { 86 | return getBindings; 87 | } 88 | else if (m.equals(HttpMethod.PUT)) { 89 | return putBindings; 90 | } 91 | else if (m.equals(HttpMethod.POST)) { 92 | return postBindings; 93 | } 94 | else if (m.equals(HttpMethod.DELETE)) { 95 | return deleteBindings; 96 | } 97 | else if (m.equals(HttpMethod.OPTIONS)) { 98 | return optionsBindings; 99 | } 100 | else if (m.equals(HttpMethod.HEAD)) { 101 | return headBindings; 102 | } 103 | else if (m.equals(HttpMethod.TRACE)) { 104 | return traceBindings; 105 | } 106 | else if (m.equals(HttpMethod.PATCH)) { 107 | return patchBindings; 108 | } 109 | else if (m.equals(HttpMethod.CONNECT)) { 110 | return connectBindings; 111 | } 112 | 113 | return null; 114 | } 115 | 116 | public boolean serveRequest(ChannelHandlerContext ctx, FullHttpRequest request) { 117 | // Handle a bad request. 118 | if (!request.getDecoderResult().isSuccess()) { 119 | sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); 120 | return false; 121 | } 122 | 123 | return route(ctx, request, getBindingsForRequest(request)); 124 | } 125 | 126 | /** 127 | * Specify a handler that will be called for a matching HTTP GET 128 | * 129 | * @param pattern 130 | * The simple pattern 131 | * @param handler 132 | * The handler to call 133 | */ 134 | public RouteMatcher get(String pattern, Handler handler) { 135 | addPattern(pattern, handler, getBindings); 136 | return this; 137 | } 138 | 139 | /** 140 | * Specify a handler that will be called for a matching HTTP PUT 141 | * 142 | * @param pattern 143 | * The simple pattern 144 | * @param handler 145 | * The handler to call 146 | */ 147 | public RouteMatcher put(String pattern, Handler handler) { 148 | addPattern(pattern, handler, putBindings); 149 | return this; 150 | } 151 | 152 | /** 153 | * Specify a handler that will be called for a matching HTTP POST 154 | * 155 | * @param pattern 156 | * The simple pattern 157 | * @param handler 158 | * The handler to call 159 | */ 160 | public RouteMatcher post(String pattern, Handler handler) { 161 | addPattern(pattern, handler, postBindings); 162 | return this; 163 | } 164 | 165 | /** 166 | * Specify a handler that will be called for a matching HTTP DELETE 167 | * 168 | * @param pattern 169 | * The simple pattern 170 | * @param handler 171 | * The handler to call 172 | */ 173 | public RouteMatcher delete(String pattern, Handler handler) { 174 | addPattern(pattern, handler, deleteBindings); 175 | return this; 176 | } 177 | 178 | /** 179 | * Specify a handler that will be called for a matching HTTP OPTIONS 180 | * 181 | * @param pattern 182 | * The simple pattern 183 | * @param handler 184 | * The handler to call 185 | */ 186 | public RouteMatcher options(String pattern, Handler handler) { 187 | addPattern(pattern, handler, optionsBindings); 188 | return this; 189 | } 190 | 191 | /** 192 | * Specify a handler that will be called for a matching HTTP HEAD 193 | * 194 | * @param pattern 195 | * The simple pattern 196 | * @param handler 197 | * The handler to call 198 | */ 199 | public RouteMatcher head(String pattern, Handler handler) { 200 | addPattern(pattern, handler, headBindings); 201 | return this; 202 | } 203 | 204 | /** 205 | * Specify a handler that will be called for a matching HTTP TRACE 206 | * 207 | * @param pattern 208 | * The simple pattern 209 | * @param handler 210 | * The handler to call 211 | */ 212 | public RouteMatcher trace(String pattern, Handler handler) { 213 | addPattern(pattern, handler, traceBindings); 214 | return this; 215 | } 216 | 217 | /** 218 | * Specify a handler that will be called for a matching HTTP CONNECT 219 | * 220 | * @param pattern 221 | * The simple pattern 222 | * @param handler 223 | * The handler to call 224 | */ 225 | public RouteMatcher connect(String pattern, Handler handler) { 226 | addPattern(pattern, handler, connectBindings); 227 | return this; 228 | } 229 | 230 | /** 231 | * Specify a handler that will be called for a matching HTTP PATCH 232 | * 233 | * @param pattern 234 | * The simple pattern 235 | * @param handler 236 | * The handler to call 237 | */ 238 | public RouteMatcher patch(String pattern, Handler handler) { 239 | addPattern(pattern, handler, patchBindings); 240 | return this; 241 | } 242 | 243 | /** 244 | * Specify a handler that will be called for all HTTP methods 245 | * 246 | * @param pattern 247 | * The simple pattern 248 | * @param handler 249 | * The handler to call 250 | */ 251 | public RouteMatcher all(String pattern, Handler handler) { 252 | addPattern(pattern, handler, getBindings); 253 | addPattern(pattern, handler, putBindings); 254 | addPattern(pattern, handler, postBindings); 255 | addPattern(pattern, handler, deleteBindings); 256 | addPattern(pattern, handler, optionsBindings); 257 | addPattern(pattern, handler, headBindings); 258 | addPattern(pattern, handler, traceBindings); 259 | addPattern(pattern, handler, connectBindings); 260 | addPattern(pattern, handler, patchBindings); 261 | return this; 262 | } 263 | 264 | /** 265 | * Specify a handler that will be called for a matching HTTP GET 266 | * 267 | * @param regex 268 | * A regular expression 269 | * @param handler 270 | * The handler to call 271 | */ 272 | public RouteMatcher getWithRegEx(String regex, Handler handler) { 273 | addRegEx(regex, handler, getBindings); 274 | return this; 275 | } 276 | 277 | /** 278 | * Specify a handler that will be called for a matching HTTP PUT 279 | * 280 | * @param regex 281 | * A regular expression 282 | * @param handler 283 | * The handler to call 284 | */ 285 | public RouteMatcher putWithRegEx(String regex, Handler handler) { 286 | addRegEx(regex, handler, putBindings); 287 | return this; 288 | } 289 | 290 | /** 291 | * Specify a handler that will be called for a matching HTTP POST 292 | * 293 | * @param regex 294 | * A regular expression 295 | * @param handler 296 | * The handler to call 297 | */ 298 | public RouteMatcher postWithRegEx(String regex, Handler handler) { 299 | addRegEx(regex, handler, postBindings); 300 | return this; 301 | } 302 | 303 | /** 304 | * Specify a handler that will be called for a matching HTTP DELETE 305 | * 306 | * @param regex 307 | * A regular expression 308 | * @param handler 309 | * The handler to call 310 | */ 311 | public RouteMatcher deleteWithRegEx(String regex, Handler handler) { 312 | addRegEx(regex, handler, deleteBindings); 313 | return this; 314 | } 315 | 316 | /** 317 | * Specify a handler that will be called for a matching HTTP OPTIONS 318 | * 319 | * @param regex 320 | * A regular expression 321 | * @param handler 322 | * The handler to call 323 | */ 324 | public RouteMatcher optionsWithRegEx(String regex, Handler handler) { 325 | addRegEx(regex, handler, optionsBindings); 326 | return this; 327 | } 328 | 329 | /** 330 | * Specify a handler that will be called for a matching HTTP HEAD 331 | * 332 | * @param regex 333 | * A regular expression 334 | * @param handler 335 | * The handler to call 336 | */ 337 | public RouteMatcher headWithRegEx(String regex, Handler handler) { 338 | addRegEx(regex, handler, headBindings); 339 | return this; 340 | } 341 | 342 | /** 343 | * Specify a handler that will be called for a matching HTTP TRACE 344 | * 345 | * @param regex 346 | * A regular expression 347 | * @param handler 348 | * The handler to call 349 | */ 350 | public RouteMatcher traceWithRegEx(String regex, Handler handler) { 351 | addRegEx(regex, handler, traceBindings); 352 | return this; 353 | } 354 | 355 | /** 356 | * Specify a handler that will be called for a matching HTTP CONNECT 357 | * 358 | * @param regex 359 | * A regular expression 360 | * @param handler 361 | * The handler to call 362 | */ 363 | public RouteMatcher connectWithRegEx(String regex, Handler handler) { 364 | addRegEx(regex, handler, connectBindings); 365 | return this; 366 | } 367 | 368 | /** 369 | * Specify a handler that will be called for a matching HTTP PATCH 370 | * 371 | * @param regex 372 | * A regular expression 373 | * @param handler 374 | * The handler to call 375 | */ 376 | public RouteMatcher patchWithRegEx(String regex, Handler handler) { 377 | addRegEx(regex, handler, patchBindings); 378 | return this; 379 | } 380 | 381 | /** 382 | * Specify a handler that will be called for all HTTP methods 383 | * 384 | * @param regex 385 | * A regular expression 386 | * @param handler 387 | * The handler to call 388 | */ 389 | public RouteMatcher allWithRegEx(String regex, Handler handler) { 390 | addRegEx(regex, handler, getBindings); 391 | addRegEx(regex, handler, putBindings); 392 | addRegEx(regex, handler, postBindings); 393 | addRegEx(regex, handler, deleteBindings); 394 | addRegEx(regex, handler, optionsBindings); 395 | addRegEx(regex, handler, headBindings); 396 | addRegEx(regex, handler, traceBindings); 397 | addRegEx(regex, handler, connectBindings); 398 | addRegEx(regex, handler, patchBindings); 399 | return this; 400 | } 401 | 402 | /** 403 | * Specify a handler that will be called when no other handlers match. If 404 | * this handler is not specified default behaviour is to return a 404 405 | */ 406 | public RouteMatcher noMatch(Handler handler) { 407 | noMatchHandler = handler; 408 | return this; 409 | } 410 | 411 | /** 412 | * Specify a handler that will be called when any other handler matchs. 413 | */ 414 | public RouteMatcher everyMatch(Handler handler) { 415 | everyMatchHandler = handler; 416 | return this; 417 | } 418 | 419 | private static void addPattern(String input, Handler handler, List bindings) { 420 | // We need to search for any : tokens in the String and 421 | // replace them with named capture groups 422 | Matcher m = Pattern.compile(":([A-Za-z][A-Za-z0-9_]*)").matcher(input); 423 | StringBuffer sb = new StringBuffer(); 424 | Set groups = new HashSet(); 425 | while (m.find()) { 426 | String group = m.group().substring(1); 427 | if (groups.contains(group)) { 428 | throw new IllegalArgumentException("Cannot use identifier " + group + " more than once in pattern string"); 429 | } 430 | m.appendReplacement(sb, "(?<$1>[^\\/]+)"); 431 | groups.add(group); 432 | } 433 | m.appendTail(sb); 434 | String regex = sb.toString(); 435 | PatternBinding binding = new PatternBinding(NamedPattern.compile(regex), groups, handler); 436 | bindings.add(binding); 437 | } 438 | 439 | private static void addRegEx(String input, Handler handler, List bindings) { 440 | PatternBinding binding = new PatternBinding(NamedPattern.compile(input), null, handler); 441 | bindings.add(binding); 442 | } 443 | 444 | public FullHttpResponse getResponse(ChannelHandlerContext ctx, FullHttpRequest request) { 445 | // Handle a bad request. 446 | FullHttpResponse resp = null; 447 | if (!request.getDecoderResult().isSuccess()) { 448 | resp = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST); 449 | sendHttpResponse(ctx, request, resp); 450 | } 451 | else { 452 | resp = getResponseForRoute(ctx, request, getBindingsForRequest(request)); 453 | } 454 | 455 | return resp; 456 | } 457 | 458 | public FullHttpResponse getResponseForRoute(ChannelHandlerContext ctx, FullHttpRequest request, List bindings) { 459 | RoutedHttpRequest rreq = new RoutedHttpRequest(ctx, request); 460 | 461 | for (PatternBinding binding : bindings) { 462 | QueryStringDecoder uri = new QueryStringDecoder(request.getUri()); 463 | NamedMatcher m = binding.pattern.matcher(uri.path()); 464 | if (m.matches()) { 465 | Map> params = new HashMap>(m.groupCount()); 466 | if (binding.paramNames != null) { 467 | // Named params 468 | for (String param : binding.paramNames) { 469 | List l = new ArrayList(); 470 | l.add(m.group(param)); 471 | params.put(param, l); 472 | } 473 | } 474 | else { 475 | // Un-named params 476 | for (int i = 0; i < m.groupCount(); i++) { 477 | List l = new ArrayList(); 478 | l.add(m.group(i + 1)); 479 | params.put("param" + i, l); 480 | } 481 | } 482 | uri.parameters().putAll(params); 483 | FullHttpResponse res = binding.handler.handle(rreq); 484 | 485 | return res; 486 | } 487 | } 488 | 489 | if (noMatchHandler != null) { 490 | return noMatchHandler.handle(rreq); 491 | } 492 | 493 | return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND); 494 | } 495 | 496 | private boolean route(ChannelHandlerContext ctx, FullHttpRequest request, List bindings) { 497 | FullHttpResponse res = getResponseForRoute(ctx, request, bindings); 498 | sendHttpResponse(ctx, request, res); 499 | 500 | if (everyMatchHandler != null) { 501 | everyMatchHandler.handle(new RoutedHttpResponse(request, res)); 502 | } 503 | 504 | return noMatchHandler != null; 505 | } 506 | 507 | void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) { 508 | if (res == null) { 509 | // no http response, probably upgrading to websocket or something 510 | return; 511 | } 512 | 513 | // Send the response and close the connection if necessary. 514 | ChannelFuture f = ctx.channel().writeAndFlush(res); 515 | if (!HttpHeaders.isKeepAlive(req) || res.getStatus().code() != 200) { 516 | f.addListener(ChannelFutureListener.CLOSE); 517 | } 518 | } 519 | 520 | private static class PatternBinding { 521 | final NamedPattern pattern; 522 | final Handler handler; 523 | final Set paramNames; 524 | 525 | private PatternBinding(NamedPattern pattern, Set paramNames, Handler handler) { 526 | this.pattern = pattern; 527 | this.paramNames = paramNames; 528 | this.handler = handler; 529 | } 530 | } 531 | 532 | } 533 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/interactor/requests/AskRequest.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.interactor.requests; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data @AllArgsConstructor 7 | public class AskRequest { 8 | 9 | private String signed; 10 | private String iv; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/interactor/requests/HandshakeRequest.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.interactor.requests; 2 | 3 | import java.util.UUID; 4 | 5 | import lombok.Data; 6 | 7 | @Data 8 | public class HandshakeRequest { 9 | 10 | private String domain; 11 | private String secretKey; 12 | private transient String id = UUID.randomUUID().toString().substring(0, 8); 13 | 14 | public boolean isValid() { 15 | return domain != null && secretKey != null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/interactor/responses/AskResponse.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.interactor.responses; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class AskResponse { 7 | 8 | private String signed; 9 | private String iv; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/interactor/responses/HandshakeResponse.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.interactor.responses; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class HandshakeResponse { 7 | 8 | private boolean status; 9 | private String msg; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/interactor/responses/RetrieveKeyResponse.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.interactor.responses; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class RetrieveKeyResponse { 7 | 8 | private boolean status; 9 | private String msg; 10 | 11 | // only where if the response is successful 12 | private String secret_key; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/methods/CommonGetPlayerCount.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.common.methods; 25 | 26 | import fr.vmarchaud.mineweb.common.ICore; 27 | import fr.vmarchaud.mineweb.common.IMethod; 28 | import fr.vmarchaud.mineweb.common.MethodHandler; 29 | 30 | @MethodHandler 31 | public class CommonGetPlayerCount implements IMethod { 32 | 33 | @Override 34 | public Object execute(ICore instance, Object... inputs) { 35 | return instance.getPlayers().size(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/methods/CommonGetPlayerList.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.common.methods; 25 | 26 | import fr.vmarchaud.mineweb.common.ICore; 27 | import fr.vmarchaud.mineweb.common.IMethod; 28 | import fr.vmarchaud.mineweb.common.MethodHandler; 29 | 30 | @MethodHandler 31 | public class CommonGetPlayerList implements IMethod { 32 | 33 | @Override 34 | public Object execute(ICore instance, Object... inputs) { 35 | return instance.getPlayers(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/methods/CommonGetSystemStats.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.methods; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | import fr.vmarchaud.mineweb.common.ICore; 6 | import fr.vmarchaud.mineweb.common.IMethod; 7 | import fr.vmarchaud.mineweb.common.MethodHandler; 8 | 9 | @MethodHandler 10 | public class CommonGetSystemStats implements IMethod{ 11 | 12 | @Override 13 | public Object execute(ICore instance, Object... inputs) { 14 | JsonObject data = new JsonObject(); 15 | data.addProperty("ram", String.valueOf((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1000000)); 16 | return data; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/methods/CommonGetTimestamp.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.methods; 2 | 3 | import fr.vmarchaud.mineweb.common.ICore; 4 | import fr.vmarchaud.mineweb.common.IMethod; 5 | import fr.vmarchaud.mineweb.common.MethodHandler; 6 | 7 | @MethodHandler 8 | public class CommonGetTimestamp implements IMethod{ 9 | 10 | @Override 11 | public Object execute(ICore instance, Object... inputs) { 12 | return System.currentTimeMillis(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/methods/CommonIsConnected.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.common.methods; 25 | 26 | import fr.vmarchaud.mineweb.common.ICore; 27 | import fr.vmarchaud.mineweb.common.IMethod; 28 | import fr.vmarchaud.mineweb.common.MethodHandler; 29 | 30 | @MethodHandler(inputs = 1, types = {String.class}) 31 | public class CommonIsConnected implements IMethod { 32 | 33 | @Override 34 | public Object execute(ICore instance, Object... inputs) { 35 | String name = (String)inputs[0]; 36 | 37 | return instance.getPlayers().contains(name); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/methods/CommonPluginType.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.common.methods; 25 | 26 | import fr.vmarchaud.mineweb.common.ICore; 27 | import fr.vmarchaud.mineweb.common.IMethod; 28 | import fr.vmarchaud.mineweb.common.MethodHandler; 29 | 30 | @MethodHandler 31 | public class CommonPluginType implements IMethod{ 32 | 33 | @Override 34 | public Object execute(ICore instance, Object... inputs) { 35 | return instance.getType().toString(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/methods/CommonRunCommand.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.methods; 2 | 3 | import fr.vmarchaud.mineweb.common.ICore; 4 | import fr.vmarchaud.mineweb.common.IMethod; 5 | import fr.vmarchaud.mineweb.common.MethodHandler; 6 | 7 | @MethodHandler(inputs = 1, types = {String.class}) 8 | public class CommonRunCommand implements IMethod { 9 | 10 | @Override 11 | public Object execute(ICore instance, Object... inputs) { 12 | String command = (String) inputs[0]; 13 | instance.runCommand(command); 14 | return true; 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/methods/CommonScheduledCommand.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.methods; 2 | 3 | import fr.vmarchaud.mineweb.common.ICore; 4 | import fr.vmarchaud.mineweb.common.IMethod; 5 | import fr.vmarchaud.mineweb.common.MethodHandler; 6 | import fr.vmarchaud.mineweb.common.ScheduledCommand; 7 | 8 | @MethodHandler(inputs = 3, types = { String.class, String.class, Double.class }) 9 | public class CommonScheduledCommand implements IMethod { 10 | 11 | @Override 12 | public Object execute(ICore instance, Object... inputs) { 13 | String command = (String) inputs[0]; 14 | String player = (String) inputs[1]; 15 | Long time = ((Double)inputs[2]).longValue(); 16 | ScheduledCommand scheduled = new ScheduledCommand(command, player, time); 17 | instance.getCommandScheduler().getQueue().offer(scheduled); 18 | return true; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/common/methods/CommonSetMotd.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.common.methods; 2 | 3 | import fr.vmarchaud.mineweb.common.ICore; 4 | import fr.vmarchaud.mineweb.common.IMethod; 5 | import fr.vmarchaud.mineweb.common.MethodHandler; 6 | 7 | @MethodHandler(inputs = 1, types = { String.class }) 8 | public class CommonSetMotd implements IMethod { 9 | 10 | @Override 11 | public Object execute(ICore instance, Object... inputs) { 12 | String motd = (String) inputs[0]; 13 | instance.config().setMotd(motd); 14 | instance.config().save(instance); 15 | return true; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/utils/BootstrapList.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.utils; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelHandler; 6 | 7 | import java.util.Collection; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | import java.util.ListIterator; 11 | import java.util.concurrent.Callable; 12 | 13 | import com.google.common.collect.Lists; 14 | 15 | public class BootstrapList implements List { 16 | private List delegate; 17 | private ChannelHandler handler; 18 | 19 | /** 20 | * Construct a new bootstrap list. 21 | * @param delegate - the delegate. 22 | * @param handler - the channel handler to add. 23 | */ 24 | public BootstrapList(List delegate, ChannelHandler handler) { 25 | this.delegate = delegate; 26 | this.handler = handler; 27 | 28 | // Process all existing bootstraps 29 | for (Object item : this) { 30 | processElement(item); 31 | } 32 | } 33 | 34 | public synchronized boolean add(Object element) { 35 | processElement(element); 36 | return delegate.add(element); 37 | } 38 | 39 | public synchronized boolean addAll(Collection collection) { 40 | List copy = Lists.newArrayList(collection); 41 | 42 | // Process the collection before we pass it on 43 | for (Object element : copy) { 44 | processElement(element); 45 | } 46 | return delegate.addAll(copy); 47 | } 48 | 49 | public synchronized Object set(int index, Object element) { 50 | Object old = delegate.set(index, element); 51 | 52 | // Handle the old future, and the newly inserted future 53 | if (old != element) { 54 | unprocessElement(old); 55 | processElement(element); 56 | } 57 | return old; 58 | } 59 | 60 | /** 61 | * Process a single element. 62 | * @param element - the element. 63 | */ 64 | protected void processElement(Object element) { 65 | if (element instanceof ChannelFuture) { 66 | processBootstrap((ChannelFuture) element); 67 | } 68 | } 69 | 70 | /** 71 | * Unprocess a single element. 72 | * @param element - the element to unprocess. 73 | */ 74 | protected void unprocessElement(Object element) { 75 | if (element instanceof ChannelFuture) { 76 | unprocessBootstrap((ChannelFuture) element); 77 | } 78 | } 79 | 80 | /** 81 | * Process a single channel future. 82 | * @param future - the future. 83 | */ 84 | protected void processBootstrap(ChannelFuture future) { 85 | // Important: Must be addFirst() 86 | future.channel().pipeline().addFirst(handler); 87 | } 88 | 89 | /** 90 | * Revert any changes we made to the channel future. 91 | * @param future - the future. 92 | */ 93 | protected void unprocessBootstrap(ChannelFuture future) { 94 | final Channel channel = future.channel(); 95 | 96 | // For thread safety - see ChannelInjector.close() 97 | channel.eventLoop().submit(new Callable() { 98 | public Object call() throws Exception { 99 | channel.pipeline().remove(handler); 100 | return null; 101 | } 102 | }); 103 | } 104 | 105 | /** 106 | * Close and revert all changes. 107 | */ 108 | public synchronized void close() { 109 | for (Object element : this) 110 | unprocessElement(element); 111 | } 112 | 113 | // Boiler plate 114 | public synchronized int size() { 115 | return delegate.size(); 116 | } 117 | 118 | public synchronized boolean isEmpty() { 119 | return delegate.isEmpty(); 120 | } 121 | 122 | public boolean contains(Object o) { 123 | return delegate.contains(o); 124 | } 125 | 126 | public synchronized Iterator iterator() { 127 | return delegate.iterator(); 128 | } 129 | 130 | public synchronized Object[] toArray() { 131 | return delegate.toArray(); 132 | } 133 | 134 | public synchronized T[] toArray(T[] a) { 135 | return delegate.toArray(a); 136 | } 137 | 138 | public synchronized boolean remove(Object o) { 139 | return delegate.remove(o); 140 | } 141 | 142 | public synchronized boolean containsAll(Collection c) { 143 | return delegate.containsAll(c); 144 | } 145 | 146 | public synchronized boolean addAll(int index, Collection c) { 147 | return delegate.addAll(index, c); 148 | } 149 | 150 | public synchronized boolean removeAll(Collection c) { 151 | return delegate.removeAll(c); 152 | } 153 | 154 | public synchronized boolean retainAll(Collection c) { 155 | return delegate.retainAll(c); 156 | } 157 | 158 | public synchronized void clear() { 159 | delegate.clear(); 160 | } 161 | 162 | public synchronized Object get(int index) { 163 | return delegate.get(index); 164 | } 165 | 166 | public synchronized void add(int index, Object element) { 167 | delegate.add(index, element); 168 | } 169 | 170 | public synchronized Object remove(int index) { 171 | return delegate.remove(index); 172 | } 173 | 174 | public synchronized int indexOf(Object o) { 175 | return delegate.indexOf(o); 176 | } 177 | 178 | public synchronized int lastIndexOf(Object o) { 179 | return delegate.lastIndexOf(o); 180 | } 181 | 182 | public synchronized ListIterator listIterator() { 183 | return delegate.listIterator(); 184 | } 185 | 186 | public synchronized ListIterator listIterator(int index) { 187 | return delegate.listIterator(index); 188 | } 189 | 190 | public synchronized List subList(int fromIndex, int toIndex) { 191 | return delegate.subList(fromIndex, toIndex); 192 | } 193 | // End boiler plate 194 | } 195 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/utils/CryptoUtils.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.utils; 2 | 3 | import java.security.PublicKey; 4 | 5 | import javax.crypto.Cipher; 6 | import javax.crypto.spec.IvParameterSpec; 7 | import javax.crypto.spec.SecretKeySpec; 8 | import javax.xml.bind.DatatypeConverter; 9 | 10 | public class CryptoUtils { 11 | 12 | /** 13 | * Encrypt a string using AES128 14 | * 15 | * @param data string that will be encrypted 16 | * @param key shared key used to encrypt the string 17 | * @param iv public iv shared with request 18 | * 19 | * @return String base64 encoded data after encryption 20 | * 21 | * @throws Exception 22 | */ 23 | public static String encryptAES(String raw, SecretKeySpec key, String iv) throws Exception { 24 | Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 25 | cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(DatatypeConverter.parseBase64Binary(iv), 0, cipher.getBlockSize())); 26 | byte[] ciphered = cipher.doFinal(raw.getBytes("UTF-8")); 27 | return DatatypeConverter.printBase64Binary(ciphered); 28 | } 29 | 30 | /** 31 | * Decrypt a string using AES128 32 | * 33 | * @param data string that will be decrypted (in base64) 34 | * @param key shared key used to decrypt the string 35 | * @param iv public iv shared with request 36 | * 37 | * @return a String representating the decrypted data 38 | * 39 | * @throws Exception 40 | */ 41 | public static String decryptAES(String raw, SecretKeySpec key, String iv) throws Exception { 42 | Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 43 | cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(DatatypeConverter.parseBase64Binary(iv), 0, cipher.getBlockSize())); 44 | return new String(cipher.doFinal(DatatypeConverter.parseBase64Binary(raw)), "UTF-8"); 45 | 46 | } 47 | 48 | /** 49 | * Encrypt a string using a RSA public key 50 | * 51 | * @param data string that will be encrypted 52 | * @param key shared key used to encrypt the string 53 | * @param iv public iv shared with request 54 | * 55 | * @return String base64 encoded data after encryption 56 | * 57 | * @throws Exception 58 | */ 59 | public static String encryptRSA(String raw, PublicKey key) throws Exception { 60 | Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding"); 61 | cipher.init(Cipher.ENCRYPT_MODE, key); 62 | byte[] ciphered = cipher.doFinal(raw.getBytes("UTF-8")); 63 | return DatatypeConverter.printBase64Binary(ciphered); 64 | } 65 | 66 | /** 67 | * Decrypt a string using a RSA public key 68 | * 69 | * @param data string that will be decrypted (in base64) 70 | * @param key shared key used to decrypt the string 71 | * @param iv public iv shared with request 72 | * 73 | * @return String 74 | * 75 | * @throws Exception 76 | */ 77 | public static String decryptRSA(String raw, PublicKey key) throws Exception { 78 | Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 79 | cipher.init(Cipher.DECRYPT_MODE, key); 80 | return new String(cipher.doFinal(DatatypeConverter.parseBase64Binary(raw))); 81 | } 82 | } -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/utils/CustomLogFormatter.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.utils; 25 | 26 | import java.text.DateFormat; 27 | import java.text.SimpleDateFormat; 28 | import java.util.Date; 29 | import java.util.logging.Formatter; 30 | import java.util.logging.LogRecord; 31 | 32 | public class CustomLogFormatter extends Formatter { 33 | 34 | private final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss"); 35 | 36 | @Override 37 | public String format(LogRecord record) { 38 | return String.format("%s - [%s.%s] - %s - %s%s", dateFormat.format(new Date()), record.getSourceClassName(), 39 | record.getSourceMethodName(), record.getLevel(), formatMessage(record), System.lineSeparator()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/utils/Handler.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.utils; 2 | 3 | /** 4 | * A generic event handler 5 | *

6 | * 7 | * This interface is used heavily throughout vert.x as a handler for all types 8 | * of asynchronous occurrences. 9 | *

10 | * 11 | * @author Tim Fox 12 | * @author Alec Gorge 13 | */ 14 | public interface Handler { 15 | 16 | /** 17 | * Something has happened, so handle it. 18 | */ 19 | R handle(E event); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/utils/http/HttpResponseBuilder.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Valentin 'ThisIsMac' Marchaud 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | *******************************************************************************/ 24 | package fr.vmarchaud.mineweb.utils.http; 25 | 26 | import io.netty.buffer.ByteBuf; 27 | import io.netty.buffer.Unpooled; 28 | import io.netty.handler.codec.http.DefaultFullHttpResponse; 29 | import io.netty.handler.codec.http.FullHttpResponse; 30 | import io.netty.handler.codec.http.HttpResponseStatus; 31 | import io.netty.handler.codec.http.HttpVersion; 32 | import lombok.Getter; 33 | 34 | public class HttpResponseBuilder { 35 | 36 | 37 | private HttpResponseStatus status = HttpResponseStatus.OK; 38 | private byte[] body = new byte[0]; 39 | private EnumContent contentType = EnumContent.JSON; 40 | 41 | /** 42 | * Build a empty response with code 200 43 | * 44 | * @return FullHttpResponse 45 | */ 46 | public FullHttpResponse build() { 47 | ByteBuf buf = Unpooled.copiedBuffer(body); 48 | FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, buf); 49 | response.headers().set("Content-Length", buf.readableBytes()); 50 | response.headers().set("Content-Type", contentType.getMime()); 51 | return response; 52 | } 53 | 54 | /** 55 | * Set the http code that the response will show 56 | * 57 | * @param HttpResponseStatus the http code of the response 58 | * @return Builder instance 59 | */ 60 | public HttpResponseBuilder code(HttpResponseStatus status) { 61 | this.status = status; 62 | return this; 63 | } 64 | 65 | /** 66 | * Set the text that the response will output 67 | * 68 | * @param text The response text 69 | * 70 | * @return Builder instance 71 | */ 72 | public HttpResponseBuilder text(String text) { 73 | this.body = text.getBytes(); 74 | this.contentType = EnumContent.PLAIN; 75 | return this; 76 | } 77 | 78 | /** 79 | * Set the text that the response will output (from json object) 80 | * 81 | * @param Object any object that gson can map 82 | * 83 | * @return Builder instance 84 | */ 85 | public HttpResponseBuilder json(String json) { 86 | this.body = json.getBytes(); 87 | this.contentType = EnumContent.JSON; 88 | return this; 89 | } 90 | 91 | /** 92 | * Set the text that the response will output (from raw bytes) 93 | * @param bytes: raw bytes to respond 94 | * @return Builder instance 95 | */ 96 | public HttpResponseBuilder raw(byte[] bytes) { 97 | this.body = bytes; 98 | this.contentType = EnumContent.BINARY; 99 | return this; 100 | } 101 | 102 | /** 103 | * Build a empty response with code 200 104 | * 105 | * @return FullHttpResponse 106 | */ 107 | public static FullHttpResponse ok() { 108 | ByteBuf buf = Unpooled.EMPTY_BUFFER; 109 | FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); 110 | response.headers().set("Content-Length", buf.readableBytes()); 111 | return response; 112 | } 113 | 114 | /** 115 | * Build a empty response with the code specified 116 | * 117 | * @param HttpResponseStatus http code returned 118 | * 119 | * @return FullHttpResponse 120 | */ 121 | public static FullHttpResponse status(HttpResponseStatus status) { 122 | ByteBuf buf = Unpooled.EMPTY_BUFFER; 123 | FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, buf); 124 | response.headers().set("Content-Length", buf.readableBytes()); 125 | return response; 126 | } 127 | 128 | public enum EnumContent { 129 | JSON("application/json"), 130 | PLAIN("text/plain"), 131 | HTML("text/html"), 132 | BINARY("application/octet-stream"); 133 | 134 | @Getter String mime; 135 | 136 | EnumContent(String mime) { 137 | this.mime = mime; 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/utils/http/RoutedHttpRequest.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.utils.http; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.handler.codec.http.FullHttpRequest; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | 8 | @Data @AllArgsConstructor 9 | public class RoutedHttpRequest { 10 | 11 | ChannelHandlerContext ctx; 12 | FullHttpRequest request; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/utils/http/RoutedHttpResponse.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.utils.http; 2 | 3 | import io.netty.handler.codec.http.FullHttpRequest; 4 | import io.netty.handler.codec.http.FullHttpResponse; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | 8 | @Data @AllArgsConstructor 9 | public class RoutedHttpResponse { 10 | 11 | FullHttpRequest request; 12 | FullHttpResponse res; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/utils/regex/NamedMatchResult.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.utils.regex; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.regex.MatchResult; 6 | 7 | public interface NamedMatchResult extends MatchResult { 8 | 9 | public List orderedGroups(); 10 | 11 | public Map namedGroups(); 12 | 13 | public String group(String groupName); 14 | 15 | public int start(String groupName); 16 | 17 | public int end(String groupName); 18 | 19 | } -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/utils/regex/NamedMatcher.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.utils.regex; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedHashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.regex.MatchResult; 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | public class NamedMatcher implements NamedMatchResult { 12 | 13 | private Matcher matcher; 14 | private NamedPattern parentPattern; 15 | 16 | NamedMatcher() { 17 | } 18 | 19 | NamedMatcher(NamedPattern parentPattern, MatchResult matcher) { 20 | this.parentPattern = parentPattern; 21 | this.matcher = (Matcher) matcher; 22 | } 23 | 24 | NamedMatcher(NamedPattern parentPattern, CharSequence input) { 25 | this.parentPattern = parentPattern; 26 | this.matcher = parentPattern.pattern().matcher(input); 27 | } 28 | 29 | public Pattern standardPattern() { 30 | return matcher.pattern(); 31 | } 32 | 33 | public NamedPattern namedPattern() { 34 | return parentPattern; 35 | } 36 | 37 | public NamedMatcher usePattern(NamedPattern newPattern) { 38 | this.parentPattern = newPattern; 39 | matcher.usePattern(newPattern.pattern()); 40 | return this; 41 | } 42 | 43 | public NamedMatcher reset() { 44 | matcher.reset(); 45 | return this; 46 | } 47 | 48 | public NamedMatcher reset(CharSequence input) { 49 | matcher.reset(input); 50 | return this; 51 | } 52 | 53 | public boolean matches() { 54 | return matcher.matches(); 55 | } 56 | 57 | public NamedMatchResult toMatchResult() { 58 | return new NamedMatcher(this.parentPattern, matcher.toMatchResult()); 59 | } 60 | 61 | public boolean find() { 62 | return matcher.find(); 63 | } 64 | 65 | public boolean find(int start) { 66 | return matcher.find(start); 67 | } 68 | 69 | public boolean lookingAt() { 70 | return matcher.lookingAt(); 71 | } 72 | 73 | public NamedMatcher appendReplacement(StringBuffer sb, String replacement) { 74 | matcher.appendReplacement(sb, replacement); 75 | return this; 76 | } 77 | 78 | public StringBuffer appendTail(StringBuffer sb) { 79 | return matcher.appendTail(sb); 80 | } 81 | 82 | public String group() { 83 | return matcher.group(); 84 | } 85 | 86 | public String group(int group) { 87 | return matcher.group(group); 88 | } 89 | 90 | public int groupCount() { 91 | return matcher.groupCount(); 92 | } 93 | 94 | public List orderedGroups() { 95 | ArrayList groups = new ArrayList(); 96 | for (int i = 1; i <= groupCount(); i++) { 97 | groups.add(group(i)); 98 | } 99 | return groups; 100 | } 101 | 102 | public String group(String groupName) { 103 | return group(groupIndex(groupName)); 104 | } 105 | 106 | public Map namedGroups() { 107 | Map result = new LinkedHashMap(); 108 | 109 | for (int i = 1; i <= groupCount(); i++) { 110 | String groupName = parentPattern.groupNames().get(i-1); 111 | String groupValue = matcher.group(i); 112 | result.put(groupName, groupValue); 113 | } 114 | 115 | return result; 116 | } 117 | 118 | private int groupIndex(String groupName) { 119 | return parentPattern.groupNames().indexOf(groupName) + 1; 120 | } 121 | 122 | public int start() { 123 | return matcher.start(); 124 | } 125 | 126 | public int start(int group) { 127 | return matcher.start(group); 128 | } 129 | 130 | public int start(String groupName) { 131 | return start(groupIndex(groupName)); 132 | } 133 | 134 | public int end() { 135 | return matcher.end(); 136 | } 137 | 138 | public int end(int group) { 139 | return matcher.end(group); 140 | } 141 | 142 | public int end(String groupName) { 143 | return end(groupIndex(groupName)); 144 | } 145 | 146 | public NamedMatcher region(int start, int end) { 147 | matcher.region(start, end); 148 | return this; 149 | } 150 | 151 | public int regionEnd() { 152 | return matcher.regionEnd(); 153 | } 154 | 155 | public int regionStart() { 156 | return matcher.regionStart(); 157 | } 158 | 159 | public boolean hitEnd() { 160 | return matcher.hitEnd(); 161 | } 162 | 163 | public boolean requireEnd() { 164 | return matcher.requireEnd(); 165 | } 166 | 167 | public boolean hasAnchoringBounds() { 168 | return matcher.hasAnchoringBounds(); 169 | } 170 | 171 | public boolean hasTransparentBounds() { 172 | return matcher.hasTransparentBounds(); 173 | } 174 | 175 | public String replaceAll(String replacement) { 176 | return matcher.replaceAll(replacement); 177 | } 178 | 179 | public String replaceFirst(String replacement) { 180 | return matcher.replaceFirst(replacement); 181 | } 182 | 183 | public NamedMatcher useAnchoringBounds(boolean b) { 184 | matcher.useAnchoringBounds(b); 185 | return this; 186 | } 187 | 188 | public NamedMatcher useTransparentBounds(boolean b) { 189 | matcher.useTransparentBounds(b); 190 | return this; 191 | } 192 | 193 | @Override 194 | public boolean equals(Object obj) { 195 | return matcher.equals(obj); 196 | } 197 | 198 | @Override 199 | public int hashCode() { 200 | return matcher.hashCode(); 201 | } 202 | 203 | @Override 204 | public String toString() { 205 | return matcher.toString(); 206 | } 207 | 208 | } -------------------------------------------------------------------------------- /src/main/java/fr/vmarchaud/mineweb/utils/regex/NamedPattern.java: -------------------------------------------------------------------------------- 1 | package fr.vmarchaud.mineweb.utils.regex; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | public class NamedPattern { 9 | 10 | private static final Pattern NAMED_GROUP_PATTERN = Pattern.compile("\\(\\?<(\\w+)>|\\((?!\\?)"); 11 | 12 | private Pattern pattern; 13 | private String namedPattern; 14 | private List groupNames; 15 | 16 | public static NamedPattern compile(String regex) { 17 | return new NamedPattern(regex, 0); 18 | } 19 | 20 | public static NamedPattern compile(String regex, int flags) { 21 | return new NamedPattern(regex, flags); 22 | } 23 | 24 | private NamedPattern(String regex, int i) { 25 | namedPattern = regex; 26 | pattern = buildStandardPattern(regex); 27 | groupNames = extractGroupNames(regex); 28 | } 29 | 30 | public int flags() { 31 | return pattern.flags(); 32 | } 33 | 34 | public NamedMatcher matcher(CharSequence input) { 35 | return new NamedMatcher(this, input); 36 | } 37 | 38 | Pattern pattern() { 39 | return pattern; 40 | } 41 | 42 | public String standardPattern() { 43 | return pattern.pattern(); 44 | } 45 | 46 | public String namedPattern() { 47 | return namedPattern; 48 | } 49 | 50 | public List groupNames() { 51 | return groupNames; 52 | } 53 | 54 | public String[] split(CharSequence input, int limit) { 55 | return pattern.split(input, limit); 56 | } 57 | 58 | public String[] split(CharSequence input) { 59 | return pattern.split(input); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return namedPattern; 65 | } 66 | 67 | static List extractGroupNames(String namedPattern) { 68 | List groupNames = new ArrayList(); 69 | Matcher matcher = NAMED_GROUP_PATTERN.matcher(namedPattern); 70 | while(matcher.find()) { 71 | groupNames.add(matcher.group(1)); 72 | } 73 | return groupNames; 74 | } 75 | 76 | static Pattern buildStandardPattern(String namedPattern) { 77 | return Pattern.compile(NAMED_GROUP_PATTERN.matcher(namedPattern).replaceAll("(")); 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /src/main/resources/bungee.yml: -------------------------------------------------------------------------------- 1 | name: MinewebBridge 2 | main: fr.vmarchaud.mineweb.bungee.BungeeCore 3 | version: 3.0.10 4 | author: ThisIsMac, Shyrogan -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: MinewebBridge 2 | main: fr.vmarchaud.mineweb.bukkit.BukkitCore 3 | version: 3.0.10 4 | api-version: 1.15 5 | authors: 6 | - ThisIsMac 7 | - Shyrogan 8 | softdepend: 9 | - ProtocolLib 10 | load: STARTUP 11 | commands: 12 | mineweb: 13 | description: Commands to manage MineWebBridge 14 | usage: | 15 | Use /help mineweb [subcommand] for more information 16 | /mineweb reset - Reset the link 17 | /mineweb port - Setup custom port 18 | mineweb reset: 19 | description: Reset plugin configuration 20 | usage: /mineweb reset 21 | permission: mineweb.reset 22 | mineweb port: 23 | description: Setup custom port 24 | usage: /mineweb port 25 | permission: mineweb.port --------------------------------------------------------------------------------