├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── paper ├── build.gradle.kts └── src │ └── main │ ├── java │ └── rs │ │ └── jamie │ │ └── serverpatches │ │ ├── ServerPatchesCommand.java │ │ ├── ServerPatchesPlugin.java │ │ ├── api │ │ ├── CrashEvent.java │ │ ├── CrashEventDispatch.java │ │ ├── CrashEventListener.java │ │ └── CrashType.java │ │ ├── crashes │ │ ├── CrashManager.java │ │ └── listeners │ │ │ ├── BundleSelectCrashListener.java │ │ │ ├── CrashListener.java │ │ │ ├── DataCommandCrashListener.java │ │ │ └── SwapCrashListener.java │ │ ├── update │ │ └── VersionCheckListener.java │ │ └── utils │ │ ├── InvalidPacketKicker.java │ │ ├── TextUtil.java │ │ └── UpdateUtil.java │ └── resources │ ├── LICENSE │ ├── config.yml │ └── plugin.yml ├── settings.gradle.kts └── velocity ├── build.gradle.kts └── src └── main ├── java └── rs │ └── jamie │ └── serverpatches │ ├── ServerPatchesPlugin.java │ ├── api │ ├── CrashEvent.java │ ├── CrashEventDispatch.java │ ├── CrashEventListener.java │ └── CrashType.java │ ├── crashes │ ├── CrashManager.java │ └── listeners │ │ ├── BundleSelectCrashListener.java │ │ ├── CrashListener.java │ │ ├── DataCommandCrashListener.java │ │ └── SwapCrashListener.java │ └── utils │ ├── InvalidPacketKicker.java │ ├── TextUtil.java │ └── UpdateUtil.java └── resources └── config.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | run/ 8 | 9 | # maven 10 | 11 | target/ 12 | 13 | # fleet 14 | 15 | .fleet/ 16 | 17 | # idea 18 | 19 | .idea/ 20 | *.iml 21 | *.ipr 22 | *.iws 23 | 24 | # vscode 25 | 26 | .settings/ 27 | .vscode/ 28 | bin/ 29 | .classpath 30 | .project 31 | 32 | # macOS 33 | 34 | .DS_Store 35 | .AppleDouble 36 | .LSOverride 37 | 38 | # Compiled class file 39 | *.class 40 | 41 | # Log file 42 | *.log 43 | 44 | # BlueJ files 45 | *.ctxt 46 | 47 | # Package Files # 48 | *.jar 49 | *.war 50 | *.nar 51 | *.ear 52 | *.zip 53 | *.tar.gz 54 | *.rar 55 | 56 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 57 | hs_err_pid* 58 | 59 | *~ 60 | 61 | # temporary files which can be created if a process still has a handle open of a deleted file 62 | .fuse_hidden* 63 | 64 | # KDE directory preferences 65 | .directory 66 | 67 | # Linux trash folder which might appear on any partition or disk 68 | .Trash-* 69 | 70 | # .nfs files are created when an open file is removed but is still being accessed 71 | .nfs* 72 | 73 | # Icon must end with two \r 74 | Icon 75 | 76 | # Thumbnails 77 | ._* 78 | 79 | # Files that might appear in the root of a volume 80 | .DocumentRevisions-V100 81 | .fseventsd 82 | .Spotlight-V100 83 | .TemporaryItems 84 | .Trashes 85 | .VolumeIcon.icns 86 | .com.apple.timemachine.donotpresent 87 | 88 | # Directories potentially created on remote AFP share 89 | .AppleDB 90 | .AppleDesktop 91 | Network Trash Folder 92 | Temporary Items 93 | .apdisk 94 | 95 | # Windows thumbnail cache files 96 | Thumbs.db 97 | Thumbs.db:encryptable 98 | ehthumbs.db 99 | ehthumbs_vista.db 100 | 101 | # Dump file 102 | *.stackdump 103 | 104 | # Folder config file 105 | [Dd]esktop.ini 106 | 107 | # Recycle Bin used on file shares 108 | $RECYCLE.BIN/ 109 | 110 | # Windows Installer files 111 | *.cab 112 | *.msi 113 | *.msix 114 | *.msm 115 | *.msp 116 | 117 | # Windows shortcuts 118 | *.lnk 119 | 120 | pom.xml.tag 121 | pom.xml.releaseBackup 122 | pom.xml.versionsBackup 123 | pom.xml.next 124 | 125 | release.properties 126 | dependency-reduced-pom.xml 127 | buildNumber.properties 128 | .mvn/timing.properties 129 | .mvn/wrapper/maven-wrapper.jar 130 | .flattened-pom.xml 131 | 132 | # Common working directory 133 | run/!/src/test/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Jamie Hildreth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ServerPatches 2 | ServerPatches simply fixes crashes and other important exploits for older minecraft versions since paper doesn't have backwards compatibility. For 1.17-1.20+ 3 | 4 | > [!IMPORTANT] 5 | > We have no plans to write support for any version below 1.17 and support for 1.19 and below will be dropped on September 22, 2024. After this date the API version will be updated to 1.20 and no patches will be written for versions prior to this, in addition all fixes for any crash prior to 1.20 could be removed at any time. **Private forks can be made for clients but you will be charged, email contact@jamie.rs if you are interested in this**. Thank you for your continued support <3 6 | 7 | ## Features: 8 | - Fixes out of bounds button integers for inventory slot swap packets - EXAMPLE N/A 9 | - Fixed data command exploits with invalid NBT, Caused by a [Paper patch breaking something](https://github.com/PaperMC/Paper/blob/9e171ef8ff0a0ec57ebc75772fc9de578c987059/patches/server/0647-Check-requirement-before-suggesting-root-nodes.patch#L22) 10 | - Fixes a crash with the select bundle packet [Paper Patch](https://github.com/PaperMC/Paper/commit/a838a886dcbc93664283034a41673e802a6b3098) 11 | 12 | ## Credits: 13 | - [Paper](https://github.com/PaperMC/) - Amazing community & Reference for multiple crash fixes 14 | - [PacketEvents](https://github.com/retrooper/packetevents) - Amazing library we use to handle packets 15 | - [Boosted-Yaml](https://github.com/dejvokep/boosted-yaml) - Cool library we use for configuration stuff 16 | 17 | ## Notes: 18 | - If you know of any other exploits please open a [new issue](https://github.com/summiner/ServerPatches/issues/new) and give detail about the exploit 19 | - In the provided examples under Features we do not directly or indirectly endorse the people who have made the videos/posts, they are simply to demonstrate the exploit 20 | 21 | ## BStats: 22 | ![bstats data](https://bstats.org/signatures/bukkit/ServerPatches.svg) 23 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 2 | 3 | plugins { 4 | id("com.github.johnrengelman.shadow") version "8.1.1" apply false 5 | `java-library` 6 | } 7 | 8 | subprojects { 9 | apply { 10 | plugin("java-library") 11 | plugin("com.github.johnrengelman.shadow") 12 | } 13 | 14 | version = "1.1.0" 15 | java.sourceCompatibility = JavaVersion.VERSION_16 16 | 17 | repositories { 18 | mavenLocal() 19 | maven { 20 | url = uri("https://repo.maven.apache.org/maven2/") 21 | } 22 | maven { 23 | url = uri("https://repo.papermc.io/repository/maven-public/") 24 | } 25 | maven { 26 | url = uri("https://repo.codemc.io/repository/maven-releases/") 27 | } 28 | } 29 | 30 | tasks { 31 | val shadowJar = named("shadowJar") { 32 | configurations = listOf(project.configurations.getByName("shadow")) 33 | archiveFileName.set("ServerPatches.jar") 34 | } 35 | 36 | build { 37 | dependsOn(shadowJar) 38 | } 39 | } 40 | 41 | tasks.withType { 42 | options.encoding = "UTF-8" 43 | } 44 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | group = "Server-Patches" 2 | description = "ServerPatches" -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | # This file was generated by the Gradle 'init' task. 2 | # https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format 3 | 4 | [versions] 5 | com-comphenix-protocol-protocollib = "4.7.0" 6 | com-velocitypowered-velocity-api = "3.1.1" 7 | org-bstats-bstats-bukkit = "3.0.2" 8 | org-spigotmc-spigot = "1.17.1-R0.1-SNAPSHOT" 9 | 10 | [libraries] 11 | com-comphenix-protocol-protocollib = { module = "com.comphenix.protocol:ProtocolLib", version.ref = "com-comphenix-protocol-protocollib" } 12 | com-velocitypowered-velocity-api = { module = "com.velocitypowered:velocity-api", version.ref = "com-velocitypowered-velocity-api" } 13 | org-bstats-bstats-bukkit = { module = "org.bstats:bstats-bukkit", version.ref = "org-bstats-bstats-bukkit" } 14 | org-spigotmc-spigot = { module = "org.spigotmc:spigot", version.ref = "org-spigotmc-spigot" } 15 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /paper/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("xyz.jpenilla.run-paper") version "2.3.1" 3 | } 4 | 5 | configurations.named("shadow") { 6 | extendsFrom(configurations.getByName("implementation")) 7 | } 8 | 9 | dependencies { 10 | shadow("org.bstats:bstats-bukkit:3.0.2") 11 | shadow("dev.dejvokep:boosted-yaml:1.3") 12 | shadow("com.github.retrooper:packetevents-spigot:2.7.0") 13 | compileOnly("io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT") 14 | } 15 | 16 | tasks { 17 | processResources { 18 | inputs.property("version", project.version) 19 | filesMatching("plugin.yml") { 20 | expand(getProperties()) 21 | expand(mutableMapOf("version" to project.version)) 22 | } 23 | } 24 | 25 | shadowJar { 26 | archiveFileName.set("ServerPatches-Paper.jar") 27 | relocate("dev.dejvokep.boostedyaml", "rs.jamie.serverpatches.libs.boostedyaml") 28 | relocate("org.bstats", "rs.jamie.serverpatches.libs.bstats") 29 | relocate("com.github.retrooper", "rs.jamie.serverpatches.libs.retrooper.com") 30 | relocate("io.github.retrooper", "rs.jamie.serverpatches.libs.retrooper.io") 31 | dependencies { 32 | exclude("net.kyori") 33 | } 34 | } 35 | 36 | runServer { 37 | minecraftVersion("1.20.4") 38 | } 39 | } 40 | 41 | java { 42 | sourceCompatibility = JavaVersion.VERSION_17 43 | targetCompatibility = JavaVersion.VERSION_17 44 | } 45 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/ServerPatchesCommand.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | import net.kyori.adventure.text.Component; 5 | import net.kyori.adventure.text.format.NamedTextColor; 6 | import org.bukkit.command.CommandExecutor; 7 | import org.bukkit.command.CommandSender; 8 | import org.jetbrains.annotations.NotNull; 9 | import rs.jamie.serverpatches.crashes.CrashManager; 10 | import rs.jamie.serverpatches.utils.TextUtil; 11 | 12 | import java.io.IOException; 13 | import java.util.logging.Logger; 14 | 15 | public class ServerPatchesCommand implements CommandExecutor { 16 | private final static Logger logger = Logger.getLogger("ServerPatchesCommand"); 17 | 18 | private final YamlDocument config; 19 | private final CrashManager crashManager; 20 | 21 | public ServerPatchesCommand(YamlDocument config, CrashManager crashManager) { 22 | this.config = config; 23 | this.crashManager = crashManager; 24 | } 25 | 26 | @Override 27 | public boolean onCommand(@NotNull CommandSender sender, @NotNull org.bukkit.command.Command command, @NotNull String s, @NotNull String[] strings) { 28 | if (!sender.hasPermission("serverpatches.command")) { 29 | sender.sendMessage(TextUtil.formatColor(config.getString("Misc.no_permission"))); 30 | return true; 31 | } 32 | if (strings.length == 0 || !strings[0].equalsIgnoreCase("reload")) { 33 | sender.sendMessage(TextUtil.formatColor("&7Please try with &d/spatches reload")); 34 | return true; 35 | } 36 | try { 37 | config.reload(); 38 | } catch (IOException e) { 39 | logger.severe("Failed to reload config:"); 40 | e.printStackTrace(); 41 | sender.sendMessage(Component.text("Something went wrong while reloading the config, please check the console", NamedTextColor.RED)); 42 | return true; 43 | } 44 | crashManager.reload(); 45 | sender.sendMessage(TextUtil.formatColor(config.getString("Misc.config_reloaded"))); 46 | return true; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/ServerPatchesPlugin.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; 5 | import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; 6 | import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; 7 | import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; 8 | import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; 9 | import org.bstats.bukkit.Metrics; 10 | import org.bukkit.Bukkit; 11 | import rs.jamie.serverpatches.crashes.CrashManager; 12 | import rs.jamie.serverpatches.update.VersionCheckListener; 13 | import org.bukkit.plugin.java.JavaPlugin; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | 18 | public class ServerPatchesPlugin extends JavaPlugin { 19 | private static CrashManager crashManager; 20 | 21 | @SuppressWarnings("DataFlowIssue") 22 | @Override 23 | public void onEnable() { 24 | YamlDocument config; 25 | try { 26 | config = YamlDocument.create(new File(this.getDataFolder()+ "/config.yml"), getResource("config.yml"), 27 | GeneralSettings.DEFAULT, LoaderSettings.builder().setAutoUpdate(true).build(), DumperSettings.DEFAULT, 28 | UpdaterSettings.builder().setVersioning(new BasicVersioning("config-version")).build()); 29 | } catch (IOException e) { 30 | throw new RuntimeException(e); 31 | } 32 | crashManager = new CrashManager(this, config); 33 | crashManager.reload(); 34 | new Metrics(this, 20975); 35 | 36 | getCommand("spatches").setExecutor(new ServerPatchesCommand(config, crashManager)); 37 | if (config.getBoolean("misc.version-check")) { 38 | Bukkit.getPluginManager().registerEvents(new VersionCheckListener(config, this), this); 39 | } 40 | getLogger().info("Plugin finished loading (Paper)"); 41 | } 42 | 43 | @Override 44 | public void onDisable() { 45 | crashManager.unload(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/api/CrashEvent.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.api; 2 | 3 | import com.github.retrooper.packetevents.protocol.player.User; 4 | 5 | public class CrashEvent { 6 | 7 | private final User user; 8 | private final CrashType type; 9 | 10 | public CrashEvent(User user, CrashType type) { 11 | this.user = user; 12 | this.type = type; 13 | } 14 | 15 | public User getUser() { 16 | return user; 17 | } 18 | 19 | public CrashType getType() { 20 | return type; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/api/CrashEventDispatch.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.api; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class CrashEventDispatch { 7 | 8 | private final List listeners = new ArrayList<>(); 9 | 10 | public void addListener(CrashEventListener listener) { 11 | listeners.add(listener); 12 | } 13 | 14 | public void dispatchEvent(CrashEvent event) { 15 | for (CrashEventListener listener : listeners) listener.onCrashEvent(event); 16 | } 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/api/CrashEventListener.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.api; 2 | 3 | public interface CrashEventListener { 4 | void onCrashEvent(CrashEvent event); 5 | } 6 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/api/CrashType.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.api; 2 | 3 | public enum CrashType { 4 | DATA_COMMAND_CRASH, 5 | SWAP_CRASH, 6 | BUNDLE_SELECT_CRASH 7 | } -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/crashes/CrashManager.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.crashes; 2 | 3 | import com.github.retrooper.packetevents.PacketEvents; 4 | import com.github.retrooper.packetevents.event.PacketListenerCommon; 5 | import dev.dejvokep.boostedyaml.YamlDocument; 6 | import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder; 7 | import org.bukkit.plugin.Plugin; 8 | import rs.jamie.serverpatches.api.CrashEventDispatch; 9 | import rs.jamie.serverpatches.crashes.listeners.BundleSelectCrashListener; 10 | import rs.jamie.serverpatches.crashes.listeners.DataCommandCrashListener; 11 | import rs.jamie.serverpatches.crashes.listeners.SwapCrashListener; 12 | import rs.jamie.serverpatches.utils.InvalidPacketKicker; 13 | 14 | import java.util.HashMap; 15 | import java.util.concurrent.CompletableFuture; 16 | 17 | public class CrashManager { 18 | 19 | private final YamlDocument config; 20 | private final HashMap listeners = new HashMap<>(); 21 | private final CrashEventDispatch eventDispatch = new CrashEventDispatch(); 22 | 23 | private final InvalidPacketKicker invalidPacketKicker; 24 | 25 | public CrashManager(Plugin plugin, YamlDocument config) { 26 | this.config = config; 27 | PacketEvents.setAPI(SpigotPacketEventsBuilder.build(plugin)); 28 | PacketEvents.getAPI().getSettings() 29 | .reEncodeByDefault(false) 30 | .checkForUpdates(false) 31 | .kickOnPacketException(true) 32 | .bStats(true); 33 | PacketEvents.getAPI().load(); 34 | 35 | invalidPacketKicker = new InvalidPacketKicker(plugin); 36 | } 37 | 38 | public void unload() { 39 | listeners.forEach((key,value) -> PacketEvents.getAPI().getEventManager().unregisterListener(value)); 40 | listeners.clear(); 41 | } 42 | 43 | public CompletableFuture reload() { 44 | return CompletableFuture.runAsync(() -> { 45 | unload(); 46 | if (config.getBoolean("click-swap-exploit.enabled")) listeners.put("exploits_crash_swap", new SwapCrashListener(invalidPacketKicker, config, eventDispatch)); 47 | if (config.getBoolean("data-command-filter.enabled")) listeners.put("exploits_crash_datacmd", new DataCommandCrashListener(invalidPacketKicker, config, eventDispatch)); 48 | if (config.getBoolean("bundle-select-exploit.enabled")) listeners.put("exploits_crash_bundle_select", new BundleSelectCrashListener(invalidPacketKicker, config, eventDispatch)); 49 | listeners.forEach((key,value) -> PacketEvents.getAPI().getEventManager().registerListener(value)); 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/crashes/listeners/BundleSelectCrashListener.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.crashes.listeners; 2 | 3 | import com.github.retrooper.packetevents.event.PacketListenerPriority; 4 | import com.github.retrooper.packetevents.event.simple.PacketPlayReceiveEvent; 5 | import com.github.retrooper.packetevents.protocol.packettype.PacketType; 6 | import com.github.retrooper.packetevents.protocol.player.User; 7 | import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientSelectBundleItem; 8 | import dev.dejvokep.boostedyaml.YamlDocument; 9 | import rs.jamie.serverpatches.api.CrashEvent; 10 | import rs.jamie.serverpatches.api.CrashEventDispatch; 11 | import rs.jamie.serverpatches.api.CrashType; 12 | import rs.jamie.serverpatches.utils.InvalidPacketKicker; 13 | 14 | public class BundleSelectCrashListener extends CrashListener { 15 | 16 | public BundleSelectCrashListener(InvalidPacketKicker packetKicker, YamlDocument config, CrashEventDispatch eventDispatch) { 17 | super(PacketListenerPriority.NORMAL, packetKicker, config, eventDispatch); 18 | } 19 | 20 | @Override 21 | public void onPacketPlayReceive(PacketPlayReceiveEvent event) { 22 | if (event.getPacketType() != PacketType.Play.Client.SELECT_BUNDLE_ITEM) return; 23 | User user = event.getUser(); 24 | WrapperPlayClientSelectBundleItem packet = new WrapperPlayClientSelectBundleItem(event); 25 | if(packet.getSelectedItemIndex() >= -1) return; 26 | event.setCancelled(true); 27 | packetKicker.kickUser(user, config.getString("bundle-select-exploit.kick-message")); 28 | eventDispatch.dispatchEvent(new CrashEvent(user, CrashType.BUNDLE_SELECT_CRASH)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/crashes/listeners/CrashListener.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.crashes.listeners; 2 | 3 | import com.github.retrooper.packetevents.event.PacketListenerPriority; 4 | import com.github.retrooper.packetevents.event.SimplePacketListenerAbstract; 5 | import dev.dejvokep.boostedyaml.YamlDocument; 6 | import rs.jamie.serverpatches.api.CrashEventDispatch; 7 | import rs.jamie.serverpatches.utils.InvalidPacketKicker; 8 | 9 | public class CrashListener extends SimplePacketListenerAbstract { 10 | 11 | protected final InvalidPacketKicker packetKicker; 12 | protected final YamlDocument config; 13 | protected final CrashEventDispatch eventDispatch; 14 | 15 | public CrashListener(PacketListenerPriority priority, InvalidPacketKicker invalidPacketKicker, YamlDocument config, CrashEventDispatch eventDispatch) { 16 | super(priority); 17 | this.packetKicker = invalidPacketKicker; 18 | this.config = config; 19 | this.eventDispatch = eventDispatch; 20 | } 21 | 22 | public boolean run() { 23 | return true; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/crashes/listeners/DataCommandCrashListener.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.crashes.listeners; 2 | 3 | import com.github.retrooper.packetevents.event.PacketListenerPriority; 4 | import com.github.retrooper.packetevents.event.simple.PacketPlayReceiveEvent; 5 | import com.github.retrooper.packetevents.protocol.packettype.PacketType; 6 | import com.github.retrooper.packetevents.protocol.player.User; 7 | import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientTabComplete; 8 | import dev.dejvokep.boostedyaml.YamlDocument; 9 | import org.apache.commons.lang3.StringUtils; 10 | import rs.jamie.serverpatches.api.CrashEvent; 11 | import rs.jamie.serverpatches.api.CrashEventDispatch; 12 | import rs.jamie.serverpatches.api.CrashType; 13 | import rs.jamie.serverpatches.utils.InvalidPacketKicker; 14 | 15 | public class DataCommandCrashListener extends CrashListener { 16 | 17 | public DataCommandCrashListener(InvalidPacketKicker packetKicker, YamlDocument config, CrashEventDispatch eventDispatch) { 18 | super(PacketListenerPriority.NORMAL, packetKicker, config, eventDispatch); 19 | } 20 | 21 | @Override 22 | public void onPacketPlayReceive(PacketPlayReceiveEvent event) { 23 | if (event.getPacketType() != PacketType.Play.Client.TAB_COMPLETE) return; 24 | User user = event.getUser(); 25 | WrapperPlayClientTabComplete tabComplete = new WrapperPlayClientTabComplete(event); 26 | String str = tabComplete.getText(); 27 | if (str.length() > 256 || 28 | str.contains("nbt") && StringUtils.countMatches(str, "[") > 15 || 29 | StringUtils.countMatches(str, "{") > 25) { 30 | event.setCancelled(true); 31 | packetKicker.kickUser(user, config.getString("data-command-filter.kick-message")); 32 | eventDispatch.dispatchEvent(new CrashEvent(user, CrashType.DATA_COMMAND_CRASH)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/crashes/listeners/SwapCrashListener.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.crashes.listeners; 2 | 3 | import com.github.retrooper.packetevents.event.PacketListenerPriority; 4 | import com.github.retrooper.packetevents.event.simple.PacketPlayReceiveEvent; 5 | import com.github.retrooper.packetevents.protocol.packettype.PacketType; 6 | import com.github.retrooper.packetevents.protocol.player.User; 7 | import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow; 8 | import dev.dejvokep.boostedyaml.YamlDocument; 9 | import rs.jamie.serverpatches.api.CrashEvent; 10 | import rs.jamie.serverpatches.api.CrashEventDispatch; 11 | import rs.jamie.serverpatches.api.CrashType; 12 | import rs.jamie.serverpatches.utils.InvalidPacketKicker; 13 | 14 | public class SwapCrashListener extends CrashListener { 15 | 16 | public SwapCrashListener(InvalidPacketKicker packetKicker, YamlDocument config, CrashEventDispatch eventDispatch) { 17 | super(PacketListenerPriority.NORMAL, packetKicker, config, eventDispatch); 18 | } 19 | 20 | @Override 21 | public void onPacketPlayReceive(PacketPlayReceiveEvent event) { 22 | if (event.getPacketType() != PacketType.Play.Client.CLICK_WINDOW) return; 23 | User user = event.getUser(); 24 | int button = new WrapperPlayClientClickWindow(event).getButton(); 25 | if (button < 0 || button > 40) { 26 | event.setCancelled(true); 27 | packetKicker.kickUser(user, config.getString("click-swap-exploit.kick-message")); 28 | eventDispatch.dispatchEvent(new CrashEvent(user, CrashType.SWAP_CRASH)); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/update/VersionCheckListener.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.update; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | import net.kyori.adventure.text.event.ClickEvent; 5 | import org.bukkit.event.EventHandler; 6 | import org.bukkit.event.Listener; 7 | import org.bukkit.event.player.PlayerJoinEvent; 8 | import org.bukkit.plugin.Plugin; 9 | import rs.jamie.serverpatches.utils.TextUtil; 10 | import rs.jamie.serverpatches.utils.UpdateUtil; 11 | 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | public class VersionCheckListener implements Listener { 15 | private final YamlDocument config; 16 | private Boolean hasUpdate; 17 | 18 | public VersionCheckListener(YamlDocument config, Plugin plugin) { 19 | this.config = config; 20 | CompletableFuture.runAsync(() -> { 21 | this.hasUpdate = UpdateUtil.hasUpdate(plugin); 22 | }); 23 | } 24 | 25 | @EventHandler 26 | public void onPacketLoginReceive(PlayerJoinEvent event) { 27 | if (!event.getPlayer().hasPermission("serverpatches.updates")) return; 28 | if(hasUpdate) { 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/utils/InvalidPacketKicker.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.utils; 2 | 3 | import com.github.retrooper.packetevents.protocol.player.User; 4 | import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDisconnect; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.plugin.Plugin; 7 | 8 | public class InvalidPacketKicker { 9 | private final Plugin plugin; 10 | 11 | public InvalidPacketKicker(Plugin plugin) { 12 | this.plugin = plugin; 13 | } 14 | 15 | public void kickUser(User user, String reason) { 16 | user.sendPacket(new WrapperPlayServerDisconnect(TextUtil.formatColor(reason))); 17 | user.closeConnection(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/utils/TextUtil.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.utils; 2 | 3 | import io.github.retrooper.packetevents.adventure.serializer.legacy.LegacyComponentSerializer; 4 | import net.kyori.adventure.text.Component; 5 | import net.kyori.adventure.text.TextReplacementConfig; 6 | import org.intellij.lang.annotations.RegExp; 7 | 8 | public class TextUtil { 9 | public static Component formatColor(String str) { 10 | return LegacyComponentSerializer.legacyAmpersand().deserialize(str); 11 | } 12 | 13 | public static Component replaceText(Component str, @RegExp String toReplace, String replaceWith) { 14 | return str.replaceText(TextReplacementConfig.builder().match(toReplace).replacement(replaceWith).build()); 15 | } 16 | 17 | public static Component replaceText(Component str, @RegExp String toReplace, Component replaceWith) { 18 | return str.replaceText(TextReplacementConfig.builder().match(toReplace).replacement(replaceWith).build()); 19 | } 20 | 21 | public static Integer parseInt(String s) { 22 | try { 23 | return Integer.parseInt(s); 24 | } catch(NumberFormatException ignored) { 25 | return 0; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /paper/src/main/java/rs/jamie/serverpatches/utils/UpdateUtil.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.utils; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonParser; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.plugin.Plugin; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.IOException; 10 | import java.io.InputStreamReader; 11 | import java.net.HttpURLConnection; 12 | import java.net.URL; 13 | import java.util.logging.Level; 14 | 15 | public class UpdateUtil { 16 | public static Boolean hasUpdate(Plugin plugin) { 17 | try { 18 | URL url = new URL("https://api.github.com/repos/summiner/ServerPatches/releases/latest"); 19 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 20 | connection.setRequestMethod("GET"); 21 | connection.setRequestProperty("Accept", "application/json"); 22 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream())); 23 | JsonObject jsonObject = JsonParser.parseReader(bufferedReader).getAsJsonObject(); 24 | String tag = jsonObject.get("tag_name").getAsString().replaceAll("[V|\\-release|\\-HOTFIX|\\-Release]" , ""); 25 | bufferedReader.close(); 26 | connection.disconnect(); 27 | return checkVersion(plugin.getDescription().getVersion(), tag); 28 | } catch (IOException e) { 29 | Bukkit.getLogger().log(Level.WARNING, e.toString()); 30 | } 31 | return null; 32 | } 33 | 34 | public static boolean checkVersion(String current, String compare) { 35 | if (current == null || compare == null) return false; 36 | int[] i1 = splitVersion(compare); 37 | int[] i2 = splitVersion(current); 38 | return i1[0] > i2[0] || i1[1] > i2[1] || i1[2] > i2[2]; 39 | } 40 | 41 | private static int[] splitVersion(String version) { 42 | String[] spl = version == null ? null : version.split("\\."); 43 | if (spl == null || spl.length < 3) { 44 | return new int[0]; 45 | } 46 | int[] arr = new int[spl.length]; 47 | for (int i = 0; i < spl.length; i++) { 48 | arr[i] = TextUtil.parseInt(spl[i]); 49 | } 50 | return arr; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /paper/src/main/resources/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Jamie Hildreth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /paper/src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | config-version: 3 2 | 3 | misc: 4 | kick: true 5 | version-check: true 6 | no-permission: "&cYou do not have permission to run this command." 7 | config-reloaded: "&aSuccessfully reloaded the config file" 8 | 9 | click-swap-exploit: 10 | enabled: true 11 | kick-message: "§8[§dServerPatches§8] §fFailed packet filter - ClickSwapExploit" 12 | 13 | bundle-select-exploit: 14 | enabled: true 15 | kick-message: "§8[§dServerPatches§8] §fFailed packet filter - BundleSelectExploit" 16 | 17 | data-command-filter: 18 | enabled: true 19 | kick-message: "§8[§dServerPatches§8] §fFailed packet filter - DataCommandFilter" 20 | -------------------------------------------------------------------------------- /paper/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ServerPatches 2 | api-version: 1.17 3 | main: rs.jamie.serverpatches.ServerPatchesPlugin 4 | version: '${version}' 5 | authors: [Summiner] 6 | softdepend: 7 | - ProtocolLib 8 | - ProtocolSupport 9 | - ViaVersion 10 | - ViaBackwards 11 | - ViaRewind 12 | - Geyser-Spigot 13 | commands: 14 | spatches: 15 | description: Adds the ability to reload ServerPatches config options, maybe more in the future 16 | usage: /spatches reload -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "Server-Patches" 2 | include("paper") 3 | include("velocity") -------------------------------------------------------------------------------- /velocity/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("xyz.jpenilla.run-velocity") version "2.3.1" 3 | } 4 | 5 | configurations.named("shadow") { 6 | extendsFrom(configurations.getByName("implementation")) 7 | } 8 | 9 | dependencies { 10 | compileOnly("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT") 11 | annotationProcessor("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT") 12 | shadow("org.bstats:bstats-velocity:3.0.2") 13 | shadow("dev.dejvokep:boosted-yaml:1.3") 14 | shadow("com.github.retrooper:packetevents-velocity:2.7.0") 15 | implementation("org.apache.commons:commons-lang3:3.17.0") 16 | } 17 | 18 | tasks { 19 | processResources { 20 | inputs.property("version", project.version) 21 | filesMatching("ServerPatchesPlugin.java") { 22 | expand(getProperties()) 23 | expand(mutableMapOf("version" to project.version)) 24 | } 25 | } 26 | 27 | shadowJar { 28 | archiveFileName.set("ServerPatches-Velocity.jar") 29 | relocate("dev.dejvokep.boostedyaml", "rs.jamie.serverpatches.libs.boostedyaml") 30 | relocate("org.bstats", "rs.jamie.serverpatches.libs.bstats") 31 | relocate("com.github.retrooper", "rs.jamie.serverpatches.libs.retrooper.com") 32 | relocate("io.github.retrooper", "rs.jamie.serverpatches.libs.retrooper.io") 33 | dependencies { 34 | exclude("net.kyori") 35 | } 36 | } 37 | 38 | runVelocity { 39 | velocityVersion("3.4.0-SNAPSHOT") 40 | } 41 | } 42 | 43 | java { 44 | sourceCompatibility = JavaVersion.VERSION_17 45 | targetCompatibility = JavaVersion.VERSION_17 46 | } 47 | -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/ServerPatchesPlugin.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches; 2 | 3 | import com.google.inject.Inject; 4 | import com.velocitypowered.api.event.player.ServerPostConnectEvent; 5 | import com.velocitypowered.api.event.player.ServerPreConnectEvent; 6 | import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; 7 | import com.velocitypowered.api.event.Subscribe; 8 | import com.velocitypowered.api.plugin.Plugin; 9 | import com.velocitypowered.api.plugin.PluginContainer; 10 | import com.velocitypowered.api.proxy.ProxyServer; 11 | import com.velocitypowered.api.proxy.server.RegisteredServer; 12 | import dev.dejvokep.boostedyaml.YamlDocument; 13 | import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; 14 | import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; 15 | import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; 16 | import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; 17 | import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; 18 | import net.kyori.adventure.text.event.ClickEvent; 19 | import org.bstats.velocity.Metrics; 20 | import org.jetbrains.annotations.NotNull; 21 | import org.jetbrains.annotations.Nullable; 22 | import org.slf4j.Logger; 23 | import rs.jamie.serverpatches.crashes.CrashManager; 24 | import rs.jamie.serverpatches.utils.TextUtil; 25 | import rs.jamie.serverpatches.utils.UpdateUtil; 26 | 27 | import java.io.File; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.net.URL; 31 | import java.net.URLConnection; 32 | import java.nio.file.Path; 33 | import java.util.concurrent.CompletableFuture; 34 | 35 | @Plugin( 36 | id = "serverpatches", 37 | name = "ServerPatches", 38 | version = "1.1.0" 39 | ,url = "https://jamie.rs" 40 | ,authors = {"Summiner"} 41 | ) 42 | public class ServerPatchesPlugin { 43 | 44 | // TODO: Implement common classes between paper/velocity 45 | 46 | @Inject private Logger logger; 47 | @Inject private ProxyServer server; 48 | @Inject private Metrics.Factory metricsFactory; 49 | 50 | private final ClassLoader classLoader = this.getClass().getClassLoader(); 51 | private final Path folderPath = Path.of(System.getProperty("user.dir")+"/plugins/ServerPatches"); 52 | private final File folder = new File(folderPath.toString()); 53 | private YamlDocument config; 54 | private Boolean hasUpdate; 55 | 56 | 57 | @Subscribe 58 | public void onProxyInitialization(ProxyInitializeEvent event) { 59 | folder.mkdirs(); 60 | PluginContainer plugin = server.getPluginManager().getPlugin("serverpatches").get(); 61 | try { 62 | config = YamlDocument.create(new File(folder+ "/config.yml"), getResource("config.yml"), 63 | GeneralSettings.DEFAULT, LoaderSettings.builder().setAutoUpdate(true).build(), DumperSettings.DEFAULT, 64 | UpdaterSettings.builder().setVersioning(new BasicVersioning("config-version")).build()); 65 | } catch (IOException e) { 66 | throw new RuntimeException(e); 67 | } 68 | CrashManager crashManager = new CrashManager(config, server, plugin, logger, folderPath); 69 | crashManager.reload(); 70 | metricsFactory.make(this, 20976); 71 | CompletableFuture.runAsync(() -> { 72 | this.hasUpdate = UpdateUtil.hasUpdate(plugin); 73 | }); 74 | } 75 | 76 | @Subscribe 77 | public void onServerConnect(ServerPostConnectEvent event) { 78 | if(!config.getBoolean("misc.version-check")) return; 79 | if(!hasUpdate) return; 80 | event.getPlayer().sendMessage(TextUtil.formatColor("&fA newer version of &dServerPatches &fis available. Download it from: ") 81 | .append(TextUtil.formatColor("&7https://github.com/Summiner/ServerPatches").clickEvent(ClickEvent.openUrl("https://github.com/Summiner/ServerPatches/")))); 82 | } 83 | 84 | // Bukkit 85 | public @Nullable InputStream getResource(@NotNull String filename) { 86 | try { 87 | URL url = classLoader.getResource(filename); 88 | if (url == null) { 89 | return null; 90 | } else { 91 | URLConnection connection = url.openConnection(); 92 | connection.setUseCaches(false); 93 | return connection.getInputStream(); 94 | } 95 | } catch (IOException var4) { 96 | return null; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/api/CrashEvent.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.api; 2 | 3 | import com.github.retrooper.packetevents.protocol.player.User; 4 | 5 | public class CrashEvent { 6 | 7 | private final User user; 8 | private final CrashType type; 9 | 10 | public CrashEvent(User user, CrashType type) { 11 | this.user = user; 12 | this.type = type; 13 | } 14 | 15 | public User getUser() { 16 | return user; 17 | } 18 | 19 | public CrashType getType() { 20 | return type; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/api/CrashEventDispatch.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.api; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class CrashEventDispatch { 7 | 8 | private final List listeners = new ArrayList<>(); 9 | 10 | public void addListener(CrashEventListener listener) { 11 | listeners.add(listener); 12 | } 13 | 14 | public void dispatchEvent(CrashEvent event) { 15 | for (CrashEventListener listener : listeners) listener.onCrashEvent(event); 16 | } 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/api/CrashEventListener.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.api; 2 | 3 | public interface CrashEventListener { 4 | void onCrashEvent(CrashEvent event); 5 | } 6 | -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/api/CrashType.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.api; 2 | 3 | public enum CrashType { 4 | DATA_COMMAND_CRASH, 5 | SWAP_CRASH, 6 | BUNDLE_SELECT_CRASH 7 | } -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/crashes/CrashManager.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.crashes; 2 | 3 | import com.github.retrooper.packetevents.PacketEvents; 4 | import com.github.retrooper.packetevents.event.PacketListenerCommon; 5 | import com.velocitypowered.api.plugin.PluginContainer; 6 | import com.velocitypowered.api.proxy.ProxyServer; 7 | import dev.dejvokep.boostedyaml.YamlDocument; 8 | import io.github.retrooper.packetevents.velocity.factory.VelocityPacketEventsBuilder; 9 | import org.slf4j.Logger; 10 | import rs.jamie.serverpatches.api.CrashEventDispatch; 11 | import rs.jamie.serverpatches.crashes.listeners.BundleSelectCrashListener; 12 | import rs.jamie.serverpatches.crashes.listeners.DataCommandCrashListener; 13 | import rs.jamie.serverpatches.crashes.listeners.SwapCrashListener; 14 | import rs.jamie.serverpatches.utils.InvalidPacketKicker; 15 | 16 | import java.nio.file.Path; 17 | import java.util.HashMap; 18 | import java.util.concurrent.CompletableFuture; 19 | 20 | public class CrashManager { 21 | 22 | private final YamlDocument config; 23 | private final HashMap listeners = new HashMap<>(); 24 | private final CrashEventDispatch eventDispatch = new CrashEventDispatch(); 25 | 26 | private final InvalidPacketKicker invalidPacketKicker; 27 | 28 | public CrashManager(YamlDocument config, ProxyServer server, PluginContainer plugin, Logger logger, Path dataDirectory) { 29 | this.config = config; 30 | PacketEvents.setAPI(VelocityPacketEventsBuilder.build(server, plugin, logger, dataDirectory)); 31 | PacketEvents.getAPI().getSettings() 32 | .reEncodeByDefault(false) 33 | .checkForUpdates(false) 34 | .kickOnPacketException(true) 35 | .bStats(true); 36 | PacketEvents.getAPI().load(); 37 | 38 | invalidPacketKicker = new InvalidPacketKicker(); 39 | } 40 | 41 | public void unload() { 42 | listeners.forEach((key,value) -> PacketEvents.getAPI().getEventManager().unregisterListener(value)); 43 | listeners.clear(); 44 | } 45 | 46 | public CompletableFuture reload() { 47 | return CompletableFuture.runAsync(() -> { 48 | unload(); 49 | if (config.getBoolean("click-swap-exploit.enabled")) listeners.put("exploits_crash_swap", new SwapCrashListener(invalidPacketKicker, config, eventDispatch)); 50 | if (config.getBoolean("data-command-filter.enabled")) listeners.put("exploits_crash_datacmd", new DataCommandCrashListener(invalidPacketKicker, config, eventDispatch)); 51 | if (config.getBoolean("bundle-select-exploit.enabled")) listeners.put("exploits_crash_bundle_select", new BundleSelectCrashListener(invalidPacketKicker, config, eventDispatch)); 52 | listeners.forEach((key,value) -> PacketEvents.getAPI().getEventManager().registerListener(value)); 53 | }); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/crashes/listeners/BundleSelectCrashListener.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.crashes.listeners; 2 | 3 | import com.github.retrooper.packetevents.event.PacketListenerPriority; 4 | import com.github.retrooper.packetevents.event.simple.PacketPlayReceiveEvent; 5 | import com.github.retrooper.packetevents.protocol.packettype.PacketType; 6 | import com.github.retrooper.packetevents.protocol.player.User; 7 | import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientSelectBundleItem; 8 | import dev.dejvokep.boostedyaml.YamlDocument; 9 | import rs.jamie.serverpatches.api.CrashEvent; 10 | import rs.jamie.serverpatches.api.CrashEventDispatch; 11 | import rs.jamie.serverpatches.api.CrashType; 12 | import rs.jamie.serverpatches.utils.InvalidPacketKicker; 13 | 14 | public class BundleSelectCrashListener extends CrashListener { 15 | 16 | public BundleSelectCrashListener(InvalidPacketKicker packetKicker, YamlDocument config, CrashEventDispatch eventDispatch) { 17 | super(PacketListenerPriority.NORMAL, packetKicker, config, eventDispatch); 18 | } 19 | 20 | @Override 21 | public void onPacketPlayReceive(PacketPlayReceiveEvent event) { 22 | if (event.getPacketType() != PacketType.Play.Client.SELECT_BUNDLE_ITEM) return; 23 | User user = event.getUser(); 24 | WrapperPlayClientSelectBundleItem packet = new WrapperPlayClientSelectBundleItem(event); 25 | if(packet.getSelectedItemIndex() >= -1) return; 26 | event.setCancelled(true); 27 | packetKicker.kickUser(user, config.getString("bundle-select-exploit.kick-message")); 28 | eventDispatch.dispatchEvent(new CrashEvent(user, CrashType.BUNDLE_SELECT_CRASH)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/crashes/listeners/CrashListener.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.crashes.listeners; 2 | 3 | import com.github.retrooper.packetevents.event.PacketListenerPriority; 4 | import com.github.retrooper.packetevents.event.SimplePacketListenerAbstract; 5 | import dev.dejvokep.boostedyaml.YamlDocument; 6 | import rs.jamie.serverpatches.api.CrashEventDispatch; 7 | import rs.jamie.serverpatches.utils.InvalidPacketKicker; 8 | 9 | public class CrashListener extends SimplePacketListenerAbstract { 10 | 11 | protected final InvalidPacketKicker packetKicker; 12 | protected final YamlDocument config; 13 | protected final CrashEventDispatch eventDispatch; 14 | 15 | public CrashListener(PacketListenerPriority priority, InvalidPacketKicker invalidPacketKicker, YamlDocument config, CrashEventDispatch eventDispatch) { 16 | super(priority); 17 | this.packetKicker = invalidPacketKicker; 18 | this.config = config; 19 | this.eventDispatch = eventDispatch; 20 | } 21 | 22 | public boolean run() { 23 | return true; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/crashes/listeners/DataCommandCrashListener.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.crashes.listeners; 2 | 3 | import com.github.retrooper.packetevents.event.PacketListenerPriority; 4 | import com.github.retrooper.packetevents.event.simple.PacketPlayReceiveEvent; 5 | import com.github.retrooper.packetevents.protocol.packettype.PacketType; 6 | import com.github.retrooper.packetevents.protocol.player.User; 7 | import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientTabComplete; 8 | import dev.dejvokep.boostedyaml.YamlDocument; 9 | import org.apache.commons.lang3.StringUtils; 10 | import rs.jamie.serverpatches.api.CrashEvent; 11 | import rs.jamie.serverpatches.api.CrashEventDispatch; 12 | import rs.jamie.serverpatches.api.CrashType; 13 | import rs.jamie.serverpatches.utils.InvalidPacketKicker; 14 | 15 | public class DataCommandCrashListener extends CrashListener { 16 | 17 | public DataCommandCrashListener(InvalidPacketKicker packetKicker, YamlDocument config, CrashEventDispatch eventDispatch) { 18 | super(PacketListenerPriority.NORMAL, packetKicker, config, eventDispatch); 19 | } 20 | 21 | @Override 22 | public void onPacketPlayReceive(PacketPlayReceiveEvent event) { 23 | if (event.getPacketType() != PacketType.Play.Client.TAB_COMPLETE) return; 24 | User user = event.getUser(); 25 | WrapperPlayClientTabComplete tabComplete = new WrapperPlayClientTabComplete(event); 26 | String str = tabComplete.getText(); 27 | if (str.length() > 256 || 28 | str.contains("nbt") && StringUtils.countMatches(str, "[") > 15 || 29 | StringUtils.countMatches(str, "{") > 25) { 30 | event.setCancelled(true); 31 | packetKicker.kickUser(user, config.getString("data-command-filter.kick-message")); 32 | eventDispatch.dispatchEvent(new CrashEvent(user, CrashType.DATA_COMMAND_CRASH)); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/crashes/listeners/SwapCrashListener.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.crashes.listeners; 2 | 3 | import com.github.retrooper.packetevents.event.PacketListenerPriority; 4 | import com.github.retrooper.packetevents.event.simple.PacketPlayReceiveEvent; 5 | import com.github.retrooper.packetevents.protocol.packettype.PacketType; 6 | import com.github.retrooper.packetevents.protocol.player.User; 7 | import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow; 8 | import dev.dejvokep.boostedyaml.YamlDocument; 9 | import rs.jamie.serverpatches.api.CrashEvent; 10 | import rs.jamie.serverpatches.api.CrashEventDispatch; 11 | import rs.jamie.serverpatches.api.CrashType; 12 | import rs.jamie.serverpatches.utils.InvalidPacketKicker; 13 | 14 | public class SwapCrashListener extends CrashListener { 15 | 16 | public SwapCrashListener(InvalidPacketKicker packetKicker, YamlDocument config, CrashEventDispatch eventDispatch) { 17 | super(PacketListenerPriority.NORMAL, packetKicker, config, eventDispatch); 18 | } 19 | 20 | @Override 21 | public void onPacketPlayReceive(PacketPlayReceiveEvent event) { 22 | if (event.getPacketType() != PacketType.Play.Client.CLICK_WINDOW) return; 23 | User user = event.getUser(); 24 | int button = new WrapperPlayClientClickWindow(event).getButton(); 25 | if (button < 0 || button > 40) { 26 | event.setCancelled(true); 27 | packetKicker.kickUser(user, config.getString("click-swap-exploit.kick-message")); 28 | eventDispatch.dispatchEvent(new CrashEvent(user, CrashType.SWAP_CRASH)); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/utils/InvalidPacketKicker.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.utils; 2 | 3 | import com.github.retrooper.packetevents.protocol.player.User; 4 | import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDisconnect; 5 | 6 | public class InvalidPacketKicker { 7 | public void kickUser(User user, String reason) { 8 | user.sendPacket(new WrapperPlayServerDisconnect(TextUtil.formatColor(reason))); 9 | user.closeConnection(); 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/utils/TextUtil.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.utils; 2 | 3 | import io.github.retrooper.packetevents.adventure.serializer.legacy.LegacyComponentSerializer; 4 | import net.kyori.adventure.text.Component; 5 | import net.kyori.adventure.text.TextReplacementConfig; 6 | import org.intellij.lang.annotations.RegExp; 7 | 8 | public class TextUtil { 9 | public static Component formatColor(String str) { 10 | return LegacyComponentSerializer.legacyAmpersand().deserialize(str); 11 | } 12 | 13 | public static Component replaceText(Component str, @RegExp String toReplace, String replaceWith) { 14 | return str.replaceText(TextReplacementConfig.builder().match(toReplace).replacement(replaceWith).build()); 15 | } 16 | 17 | public static Component replaceText(Component str, @RegExp String toReplace, Component replaceWith) { 18 | return str.replaceText(TextReplacementConfig.builder().match(toReplace).replacement(replaceWith).build()); 19 | } 20 | 21 | public static Integer parseInt(String s) { 22 | try { 23 | return Integer.parseInt(s); 24 | } catch(NumberFormatException ignored) { 25 | return 0; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /velocity/src/main/java/rs/jamie/serverpatches/utils/UpdateUtil.java: -------------------------------------------------------------------------------- 1 | package rs.jamie.serverpatches.utils; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonParser; 5 | import com.velocitypowered.api.plugin.PluginContainer; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | import java.net.HttpURLConnection; 11 | import java.net.URL; 12 | 13 | public class UpdateUtil { 14 | 15 | public static Boolean hasUpdate(PluginContainer plugin) { 16 | try { 17 | URL url = new URL("https://api.github.com/repos/summiner/ServerPatches/releases/latest"); 18 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 19 | connection.setRequestMethod("GET"); 20 | connection.setRequestProperty("Accept", "application/json"); 21 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream())); 22 | JsonObject jsonObject = JsonParser.parseReader(bufferedReader).getAsJsonObject(); 23 | String tag = jsonObject.get("tag_name").getAsString().replaceAll("[V|\\-release|\\-HOTFIX|\\-Release]" , ""); 24 | bufferedReader.close(); 25 | connection.disconnect(); 26 | return checkVersion(plugin.getDescription().getVersion().get(), tag); 27 | } catch (IOException e) { 28 | e.printStackTrace(); 29 | } 30 | return null; 31 | } 32 | 33 | public static boolean checkVersion(String current, String compare) { 34 | if (current == null || compare == null) return false; 35 | int[] i1 = splitVersion(compare); 36 | int[] i2 = splitVersion(current); 37 | if(i2[0] > i1[0] || i2[1] > i1[1] || i2[2] > i1[2]) return false; 38 | return (i1[0] > i2[0] || i1[1] > i2[1] || i1[2] > i2[2]); 39 | } 40 | 41 | private static int[] splitVersion(String version) { 42 | String[] spl = version == null ? null : version.split("\\."); 43 | if (spl == null || spl.length < 3) { 44 | return new int[0]; 45 | } 46 | int[] arr = new int[spl.length]; 47 | for (int i = 0; i < spl.length; i++) { 48 | arr[i] = TextUtil.parseInt(spl[i]); 49 | } 50 | return arr; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /velocity/src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | config-version: 3 2 | 3 | misc: 4 | kick: true 5 | version-check: true 6 | no-permission: "&cYou do not have permission to run this command." 7 | config-reloaded: "&aSuccessfully reloaded the config file" 8 | 9 | click-swap-exploit: 10 | enabled: true 11 | kick-message: "§8[§dServerPatches§8] §fFailed packet filter - ClickSwapExploit" 12 | 13 | bundle-select-exploit: 14 | enabled: true 15 | kick-message: "§8[§dServerPatches§8] §fFailed packet filter - BundleSelectExploit" 16 | 17 | data-command-filter: 18 | enabled: true 19 | kick-message: "§8[§dServerPatches§8] §fFailed packet filter - DataCommandFilter" 20 | --------------------------------------------------------------------------------