├── .gitignore ├── .travis.yml ├── README.md ├── apply.sh ├── build.sh ├── format-patches.sh ├── patches ├── 0001-Update-.gitignore-Add-.orig-from-merge-tools-and-bun.patch ├── 0002-Rebrand-to-flexpipe-while-keeping-plugins-functionin.patch ├── 0003-Remove-nasty-space-at-end-of-line-from-InitialHandle.patch ├── 0004-Apply-join-throttle-logic-a-bit-earlier.patch ├── 0005-Add-tab-complete-throttling.patch ├── 0006-Optional-server-list-ping-logging-Add-IPs-to-the-log.patch ├── 0007-Add-origin-and-patcher-git-hash-instead-of-patched-g.patch ├── 0008-Allow-configuring-module-source-uri-scheme-set-ci.ja.patch ├── 0009-Add-option-to-disable-module-downloading-disabled-by.patch ├── 0010-Old-build-warning-Update-download-source-add-likely-.patch ├── 0011-Edit-messages-without-modifying-the-JAR-file-more-ea.patch ├── 0012-Don-t-create-a-new-KickStringWriter-for-each-new-con.patch ├── 0013-Use-java-8-and-java-8-api.patch ├── 0014-Don-t-create-a-data-input-stream-for-every-plugin-me.patch ├── 0015-Avoid-regex-in-getLocale.patch ├── 0016-Resolve-sendData-deadlocks.patch ├── 0017-PluginManager.dispatchCommand-avoids-regex-while-spl.patch ├── 0018-Micro-optimize-mojang-uuid-to-uuid-object-conversion.patch ├── 0019-Performance-log-sanity-No-exception-on-invalid-packe.patch ├── 0020-Set-TCP_NODELAY-option-suggested-by-PunKeel.patch ├── 0021-Don-t-allow-channel-buffers-to-grow-beyond-a-reasona.patch ├── 0022-Improve-connection-closing-removes-need-of-kick-dela.patch ├── 0023-Use-multiple-EventLoopGroups.patch ├── 0024-Do-not-attempt-to-download-module-on-custom-build-wi.patch ├── 0025-Restore-1.8-like-ping-motd-description-output-for-co.patch ├── 0026-Require-compressed-packets-to-be-bigger-than-compres.patch ├── 0027-Improve-performance-of-message-translations.patch ├── 0028-Stricter-chat-validation.patch ├── 0029-Use-String.regionMatches-.-instead-of-.toLowerCase-..patch ├── 0030-Use-ASM-or-MethodHandles-for-event-execution.patch ├── 0031-Add-SuspiciousPlayerBehaviourEvent.patch ├── 0032-Ensure-kick-messages-can-be-sent-while-being-at-prot.patch ├── 0033-Overwrite-netty-ByteToMessageDecoder-class-which-use.patch ├── 0034-Slice-packets-in-MinecraftDecoder-where-possible-bas.patch ├── 0035-Calculate-the-version-only-once.patch ├── 0036-Presize-the-HTTP-response-buffer.patch ├── 0037-Intern-common-strings-of-teams-which-might-be-send-m.patch ├── 0038-Ignore-server-disconnect-reason-instead-of-failing.patch ├── 0039-Allow-the-console-to-tab-complete-commands.patch ├── 0040-Use-ASM-to-generate-packet-constructors-instead-of-u.patch └── 0041-Shorten-the-version-support-output-to-1.8-1.10.patch ├── reset-apply-build.sh └── reset.sh /.gitignore: -------------------------------------------------------------------------------- 1 | #Script / building directories 2 | BungeeCord/ 3 | /target/ 4 | 5 | #Left merge files 6 | *.orig 7 | 8 | # Mac filesystem dust 9 | .DS_Store/ 10 | 11 | #Windows 12 | desktop.ini 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: java 3 | script: 4 | - rm -rfv BungeeCord/.git/rebase-apply 5 | - ./reset-apply-build.sh 6 | - rm -rfv target 7 | - rm -rfv BungeeCord/**/target 8 | - rm -rfv $HOME/.m2/repository/me/minotopia/flexpipe 9 | - rm -rfv BungeeCord/.git/rebase-apply 10 | jdk: 11 | - oraclejdk8 12 | notifications: 13 | email: false 14 | cache: 15 | directories: 16 | - $HOME/.m2 17 | - BungeeCord 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project is no longer maintained. Use https://github.com/PaperMC/Waterfall instead. 2 | 3 | # FlexPipe 4 | **The flexible pipe to coordinate masses of players to the right spigot.** 5 | 6 | Build upon [BungeeCord](https://github.com/SpigotMC/BungeeCord), FlexPipe is created to provide optimizations, more 7 | stability and security. Originally built for minotopia.me (no longer operating), FlexPipe's changes to BungeeCord 8 | are well tested and properly documented. Due to the patch system, updates of BungeeCord are automatically reflected in 9 | the newest builds. 10 | 11 | ## What is it? 12 | BungeeCord and therefore FlexPipe is a server portal system that can be used to let players teleport 13 | between multiple minecraft servers, preferably spigot servers (and derivates). It does so by letting the client think 14 | its a world change while the system disconnects from the previous minecraft server and connects to the target minecraft 15 | server. 16 | 17 | ### Features 18 | [Complete patch list](https://github.com/minotopiame/FlexPipe/tree/master/patches) 19 | 20 | Here is a list of notable changes done in FlexPipe, see individual patches for people who helped out: 21 | 22 | * Join throttle applied as early as possible 23 | * Throttle tab completion as some plugins might get overloaded if its spammed 24 | * Differentiate between pings ad joins in logging, possibility to turn off ping logging 25 | * Allow modification of the module source url 26 | * Option to turn off module downloading / updating 27 | * Tweaking netty values to be optimal 28 | * Ability to edit bungeecord messages without modifying the jar 29 | * Resolve possible deadlock 30 | * Little optimizations 31 | * Lower log output on invalid packet order 32 | * Close connections without a half-second delay 33 | * Keep compatibility with older pinging tools for version 1.8 34 | * Validate compressed packets against compression threshold 35 | * Be more strict when validating chat 36 | * API for unusual behaviour detection 37 | * Speed up event handling through usage of dynamically created classes (faster than optimized reflection) 38 | * Don't copy memory where possible 39 | * Massively reduce memory usage when scoreboard plugins create many teams 40 | * Allow the console to tab-complete 41 | 42 | ## Download 43 | Not available, project is no longer maintained. 44 | 45 | ## Usage 46 | Simply download your FlexPipe.jar and start it with java once. Then you can find additional configuration options in 47 | your config.yml file. I suggest to turn on automatic module updates to recieve module updates automatically after 48 | downloading a new FlexPipe.jar 49 | 50 | ## Compilation 51 | This project uses maven to handle its dependencies. 52 | 53 | The compilation of FlexPipe is fairly easy. At first you need to clone this repository with git. Then you need to 54 | execute ```./reset-apply-build.sh``` in a bash environment. You can get it on windows mostly by right-clicking in the 55 | file explorer and selecting "Git Bash". The final files are copied into the ```target``` folder. 56 | 57 | **Be careful** if you have done changes in the BungeeCord folder which are not saved as a patch yet. In that case, **never** run ```./reset-apply-build.sh``` or ```./reset.sh```, these will **reset** your changes to BungeeCord. You should then only run ```./build.sh``` instead to compile flexpipe. 58 | 59 | ## Contribution 60 | We would really appreciate it if you take part in developing FlexPipe. Here is how to do it: 61 | 62 | 1. Fork FlexPipe. 63 | 2. Compile the project once (see above). 64 | 3. Go into the bungeecord folder. 65 | 4. Edit what you want, but 66 | - before you edit, start an interactive rebase on ```origin/master``` to edit the commit/patch you want to edit, *or* 67 | - change anything that's not related directly to a previous commit and commit your changes. 68 | 5. Run ```./format-patches.sh``` 69 | 6. Commit the changes of the patch files to your FlexPipe fork and create a pull request to FlexPipe. Optionally you can exclude any patch file where only line numbers and git hashes changed. 70 | 7. Thanks! 71 | -------------------------------------------------------------------------------- /apply.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | echo "" 3 | echo "============ Apply patches to BungeeCord" 4 | echo "" 5 | 6 | cd BungeeCord 7 | 8 | if [[ -z $(git config user.name) ]]; then 9 | git config --local user.name "flexpipe build script" 10 | fi 11 | 12 | if [[ -z $(git config user.email) ]]; then 13 | git config --local user.email "flexpipe_build_script@null.flexpipe.minotopia.me" 14 | fi 15 | 16 | git am -3 ../patches/*.patch 17 | 18 | cd ../ 19 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | patcher_commit=`git rev-parse --short HEAD` 4 | 5 | cd BungeeCord 6 | 7 | echo "" 8 | echo "============ Recieving maven version..." 9 | 10 | maven_version=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version -B -N | grep '^[[:digit:]]' | tail -1) 11 | 12 | origin_commit=`git rev-parse --short origin/master` 13 | 14 | if [ -z "$BUILD_NUMBER" ]; then 15 | BUILD_NUMBER="0" 16 | echo "============ Set BUILD_NUMBER to 0, as it was not set"; 17 | fi 18 | 19 | echo "============ Building FlexPipe version git:$maven_version:origin-$origin_commit:patcher-$patcher_commit:$BUILD_NUMBER" 20 | echo "" 21 | 22 | mvn clean install -Dorigin_commit=$origin_commit -Dpatcher_commit=$patcher_commit -Dbuild.number=$BUILD_NUMBER -B 23 | 24 | cd ../ 25 | 26 | echo "" 27 | echo "============ FlexPipe build finished" 28 | echo "============ Copy final jars" 29 | echo "" 30 | 31 | mkdir -p target/modules/ || true 32 | 33 | cp BungeeCord/bootstrap/target/FlexPipe.jar ./target/ 34 | 35 | cp BungeeCord/module/*/target/*.jar ./target/modules/ 36 | 37 | echo "" 38 | echo "============ FlexPipe and module jars copied to ./target/" 39 | -------------------------------------------------------------------------------- /format-patches.sh: -------------------------------------------------------------------------------- 1 | echo "============ Formatting patches from repo" 2 | 3 | cd BungeeCord 4 | 5 | git format-patch -k -o ../patches origin/master 6 | 7 | cd .. 8 | 9 | echo "============ Done formatting patches." 10 | -------------------------------------------------------------------------------- /patches/0001-Update-.gitignore-Add-.orig-from-merge-tools-and-bun.patch: -------------------------------------------------------------------------------- 1 | From 5b3a3db7210d649a036409d42fea8a12ff7f3ab5 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Mon, 15 Feb 2016 21:28:29 +0100 4 | Subject: Update .gitignore: Add *.orig from merge tools and bungeecord files 5 | and windows' desktop.ini 6 | 7 | --- 8 | .gitignore | 13 +++++++++++++ 9 | 1 file changed, 13 insertions(+) 10 | 11 | diff --git a/.gitignore b/.gitignore 12 | index a34de59..6e23c9b 100644 13 | --- a/.gitignore 14 | +++ b/.gitignore 15 | @@ -37,3 +37,16 @@ manifest.mf 16 | 17 | # delombok 18 | */src/main/lombok 19 | + 20 | +#Windows 21 | +desktop.ini 22 | + 23 | +#Left merge files 24 | +*.orig 25 | + 26 | +#BungeeCord files 27 | +/config.yml 28 | +/messages.properties 29 | +/plugins/ 30 | +/modules/ 31 | +/locations.yml 32 | -- 33 | 1.9.5.msysgit.0 34 | 35 | -------------------------------------------------------------------------------- /patches/0003-Remove-nasty-space-at-end-of-line-from-InitialHandle.patch: -------------------------------------------------------------------------------- 1 | From fe8de45a40377373c385028b29b19103e8b3bf3f Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Mon, 21 Mar 2016 02:54:10 +0100 4 | Subject: Remove nasty space at end of line from InitialHandler 5 | 6 | --- 7 | proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java | 2 +- 8 | 1 file changed, 1 insertion(+), 1 deletion(-) 9 | 10 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 11 | index 3fe1d23..84e12df 100644 12 | --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 13 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 14 | @@ -258,7 +258,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection 15 | this.handshake = handshake; 16 | ch.setVersion( handshake.getProtocolVersion() ); 17 | 18 | - // Starting with FML 1.8, a "\0FML\0" token is appended to the handshake. This interferes 19 | + // Starting with FML 1.8, a "\0FML\0" token is appended to the handshake. This interferes 20 | // with Bungee's IP forwarding, so we detect it, and remove it from the host string, for now. 21 | // We know FML appends \00FML\00. However, we need to also consider that other systems might 22 | // add their own data to the end of the string. So, we just take everything from the \0 character 23 | -- 24 | 1.9.5.msysgit.0 25 | 26 | -------------------------------------------------------------------------------- /patches/0004-Apply-join-throttle-logic-a-bit-earlier.patch: -------------------------------------------------------------------------------- 1 | From c2f909386eddbc0c206e36cf6a014cfc01c31af7 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Wed, 7 Oct 2015 21:53:26 +0200 4 | Subject: Apply join throttle logic a bit earlier 5 | 6 | --- 7 | .../java/net/md_5/bungee/connection/InitialHandler.java | 14 ++++++++++---- 8 | 1 file changed, 10 insertions(+), 4 deletions(-) 9 | 10 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 11 | index 84e12df..e9e2422 100644 12 | --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 13 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 14 | @@ -258,6 +258,16 @@ public class InitialHandler extends PacketHandler implements PendingConnection 15 | this.handshake = handshake; 16 | ch.setVersion( handshake.getProtocolVersion() ); 17 | 18 | + if ( handshake.getRequestedProtocol() == 2 && bungee.getConnectionThrottle() != null && bungee.getConnectionThrottle().throttle( getAddress().getAddress() ) ) 19 | + { 20 | + // setting thisState to username to stop possible code execution on repeated handshakes 21 | + thisState = State.USERNAME; 22 | + // setting protocol to login so we can send the kick message which is actually supported by the minecraft client after it sent the handshake 23 | + ch.setProtocol( Protocol.LOGIN ); 24 | + disconnect( bungee.getTranslation( "join_throttle_kick", TimeUnit.MILLISECONDS.toSeconds( BungeeCord.getInstance().getConfig().getThrottle() ) ) ); 25 | + return; 26 | + } 27 | + 28 | // Starting with FML 1.8, a "\0FML\0" token is appended to the handshake. This interferes 29 | // with Bungee's IP forwarding, so we detect it, and remove it from the host string, for now. 30 | // We know FML appends \00FML\00. However, we need to also consider that other systems might 31 | @@ -305,10 +315,6 @@ public class InitialHandler extends PacketHandler implements PendingConnection 32 | return; 33 | } 34 | 35 | - if ( bungee.getConnectionThrottle() != null && bungee.getConnectionThrottle().throttle( getAddress().getAddress() ) ) 36 | - { 37 | - disconnect( bungee.getTranslation( "join_throttle_kick", TimeUnit.MILLISECONDS.toSeconds( bungee.getConfig().getThrottle() ) ) ); 38 | - } 39 | break; 40 | default: 41 | throw new IllegalArgumentException( "Cannot request protocol " + handshake.getRequestedProtocol() ); 42 | -- 43 | 1.9.5.msysgit.0 44 | 45 | -------------------------------------------------------------------------------- /patches/0005-Add-tab-complete-throttling.patch: -------------------------------------------------------------------------------- 1 | From 9bff726f66e1b48be6db703b371ebf426a86e60c Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Fri, 9 Oct 2015 16:59:23 +0200 4 | Subject: Add tab-complete throttling 5 | 6 | Adapted from pull request https://github.com/SpigotMC/BungeeCord/pull/1522 7 | 8 | To enable tab complete throttling you need to set the 'minotopia.tab_complete_throttle' value in the configuration. As the connection throttle, this is measured in milliseconds and a negative value equals no tab throttling. 9 | --- 10 | api/src/main/java/net/md_5/bungee/api/ProxyConfig.java | 5 +++++ 11 | proxy/src/main/java/net/md_5/bungee/conf/Configuration.java | 2 ++ 12 | .../main/java/net/md_5/bungee/connection/UpstreamBridge.java | 10 ++++++++++ 13 | 3 files changed, 17 insertions(+) 14 | 15 | diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java b/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java 16 | index edd82c1..3d4cfc1 100644 17 | --- a/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java 18 | +++ b/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java 19 | @@ -62,6 +62,11 @@ public interface ProxyConfig 20 | int getThrottle(); 21 | 22 | /** 23 | + * The tab complete throttle delay. 24 | + */ 25 | + int getTabCompleteThrottle(); 26 | + 27 | + /** 28 | * Whether the proxy will parse IPs with spigot or not 29 | */ 30 | @Deprecated 31 | diff --git a/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java b/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java 32 | index 25d87d9..0445dfa 100644 33 | --- a/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java 34 | +++ b/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java 35 | @@ -57,6 +57,7 @@ public class Configuration implements ProxyConfig 36 | private boolean ipForward; 37 | private Favicon favicon; 38 | private int compressionThreshold = 256; 39 | + private int tabCompleteThrottle = -1; 40 | 41 | public void load() 42 | { 43 | @@ -82,6 +83,7 @@ public class Configuration implements ProxyConfig 44 | logCommands = adapter.getBoolean( "log_commands", logCommands ); 45 | playerLimit = adapter.getInt( "player_limit", playerLimit ); 46 | throttle = adapter.getInt( "connection_throttle", throttle ); 47 | + tabCompleteThrottle = adapter.getInt( "tab_complete_throttle", tabCompleteThrottle ); 48 | ipForward = adapter.getBoolean( "ip_forward", ipForward ); 49 | compressionThreshold = adapter.getInt( "network_compression_threshold", compressionThreshold ); 50 | 51 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 52 | index 7e1917f..f807f17 100644 53 | --- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 54 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 55 | @@ -28,6 +28,7 @@ import net.md_5.bungee.protocol.packet.TabCompleteResponse; 56 | public class UpstreamBridge extends PacketHandler 57 | { 58 | 59 | + private long lastTabComplete = -1; 60 | private final ProxyServer bungee; 61 | private final UserConnection con; 62 | 63 | @@ -127,6 +128,15 @@ public class UpstreamBridge extends PacketHandler 64 | @Override 65 | public void handle(TabCompleteRequest tabComplete) throws Exception 66 | { 67 | + if ( bungee.getConfig().getTabCompleteThrottle() > 0 ) 68 | + { 69 | + long now = System.currentTimeMillis(); 70 | + if ( lastTabComplete != -1 && ( now - lastTabComplete ) <= bungee.getConfig().getTabCompleteThrottle() ) 71 | + { 72 | + throw CancelSendSignal.INSTANCE; 73 | + } 74 | + lastTabComplete = now; 75 | + } 76 | List suggestions = new ArrayList<>(); 77 | 78 | if ( tabComplete.getCursor().startsWith( "/" ) ) 79 | -- 80 | 1.9.5.msysgit.0 81 | 82 | -------------------------------------------------------------------------------- /patches/0006-Optional-server-list-ping-logging-Add-IPs-to-the-log.patch: -------------------------------------------------------------------------------- 1 | From f3284e2b67a1c94421fb06093a44635b8702b601 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Sat, 12 Dec 2015 23:43:30 +0100 4 | Subject: Optional server list ping logging; Add IPs to the log where user 5 | names are shown. 6 | 7 | Server list pings are only logged if the log_server_list_pings config.yml option is true, defaults to false 8 | 9 | The log format is like this: [/***.***.***.***|Username] 10 | If the user's name is unknown, its just the ip in the brackets as before. 11 | --- 12 | api/src/main/java/net/md_5/bungee/api/ProxyConfig.java | 5 +++++ 13 | proxy/src/main/java/net/md_5/bungee/ServerConnector.java | 2 +- 14 | .../src/main/java/net/md_5/bungee/conf/Configuration.java | 5 +++++ 15 | .../java/net/md_5/bungee/connection/DownstreamBridge.java | 2 +- 16 | .../java/net/md_5/bungee/connection/InitialHandler.java | 14 ++++++++++++-- 17 | .../java/net/md_5/bungee/connection/UpstreamBridge.java | 2 +- 18 | 6 files changed, 25 insertions(+), 5 deletions(-) 19 | 20 | diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java b/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java 21 | index 3d4cfc1..46f5050 100644 22 | --- a/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java 23 | +++ b/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java 24 | @@ -46,6 +46,11 @@ public interface ProxyConfig 25 | boolean isLogCommands(); 26 | 27 | /** 28 | + * Whether we log server list pings 29 | + */ 30 | + boolean isLogServerListPing(); 31 | + 32 | + /** 33 | * Returns the player max. 34 | */ 35 | int getPlayerLimit(); 36 | diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java 37 | index 976636a..81d62ba 100644 38 | --- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java 39 | +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java 40 | @@ -344,6 +344,6 @@ public class ServerConnector extends PacketHandler 41 | @Override 42 | public String toString() 43 | { 44 | - return "[" + user.getName() + "] <-> ServerConnector [" + target.getName() + "]"; 45 | + return '[' + user.getAddress().toString() + '|' + user.getName() + "] <-> ServerConnector [" + target.getName() + ']'; 46 | } 47 | } 48 | diff --git a/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java b/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java 49 | index 0445dfa..bff0ccf 100644 50 | --- a/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java 51 | +++ b/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java 52 | @@ -51,6 +51,10 @@ public class Configuration implements ProxyConfig 53 | * Whether we log proxy commands to the proxy log 54 | */ 55 | private boolean logCommands; 56 | + /** 57 | + * Whether we log server list pings 58 | + */ 59 | + private boolean logServerListPing; 60 | private int playerLimit = -1; 61 | private Collection disabledCommands; 62 | private int throttle = 4000; 63 | @@ -81,6 +85,7 @@ public class Configuration implements ProxyConfig 64 | uuid = adapter.getString( "stats", uuid ); 65 | onlineMode = adapter.getBoolean( "online_mode", onlineMode ); 66 | logCommands = adapter.getBoolean( "log_commands", logCommands ); 67 | + logServerListPing = adapter.getBoolean( "log_server_list_ping", logServerListPing ); 68 | playerLimit = adapter.getInt( "player_limit", playerLimit ); 69 | throttle = adapter.getInt( "connection_throttle", throttle ); 70 | tabCompleteThrottle = adapter.getInt( "tab_complete_throttle", tabCompleteThrottle ); 71 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java 72 | index 288d602..d139e03 100644 73 | --- a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java 74 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java 75 | @@ -507,6 +507,6 @@ public class DownstreamBridge extends PacketHandler 76 | @Override 77 | public String toString() 78 | { 79 | - return "[" + con.getName() + "] <-> DownstreamBridge <-> [" + server.getInfo().getName() + "]"; 80 | + return '[' + con.getAddress().toString() + '|' + con.getName() + "] <-> DownstreamBridge <-> [" + server.getInfo().getName() + ']'; 81 | } 82 | } 83 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 84 | index e9e2422..2077e31 100644 85 | --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 86 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 87 | @@ -287,7 +287,6 @@ public class InitialHandler extends PacketHandler implements PendingConnection 88 | } 89 | 90 | this.virtualHost = InetSocketAddress.createUnresolved( handshake.getHost(), handshake.getPort() ); 91 | - bungee.getLogger().log( Level.INFO, "{0} has connected", this ); 92 | 93 | bungee.getPluginManager().callEvent( new PlayerHandshakeEvent( InitialHandler.this, handshake ) ); 94 | 95 | @@ -295,11 +294,16 @@ public class InitialHandler extends PacketHandler implements PendingConnection 96 | { 97 | case 1: 98 | // Ping 99 | + if ( BungeeCord.getInstance().getConfig().isLogServerListPing() ) 100 | + { 101 | + bungee.getLogger().log( Level.INFO, "{0} has pinged", this ); 102 | + } 103 | thisState = State.STATUS; 104 | ch.setProtocol( Protocol.STATUS ); 105 | break; 106 | case 2: 107 | // Login 108 | + bungee.getLogger().log( Level.INFO, "{0} has connected", this ); 109 | thisState = State.USERNAME; 110 | ch.setProtocol( Protocol.LOGIN ); 111 | 112 | @@ -621,7 +625,13 @@ public class InitialHandler extends PacketHandler implements PendingConnection 113 | @Override 114 | public String toString() 115 | { 116 | - return "[" + ( ( getName() != null ) ? getName() : getAddress() ) + "] <-> InitialHandler"; 117 | + if ( getName() != null ) 118 | + { 119 | + return '[' + getAddress().toString() + '|' + getName() + "] <-> InitialHandler"; 120 | + } else 121 | + { 122 | + return '[' + getAddress().toString() + "] <-> InitialHandler"; 123 | + } 124 | } 125 | 126 | @Override 127 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 128 | index f807f17..df139d3 100644 129 | --- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 130 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 131 | @@ -210,6 +210,6 @@ public class UpstreamBridge extends PacketHandler 132 | @Override 133 | public String toString() 134 | { 135 | - return "[" + con.getName() + "] -> UpstreamBridge"; 136 | + return '[' + con.getAddress().toString() + '|' + con.getName() + "] -> UpstreamBridge"; 137 | } 138 | } 139 | -- 140 | 1.9.5.msysgit.0 141 | 142 | -------------------------------------------------------------------------------- /patches/0007-Add-origin-and-patcher-git-hash-instead-of-patched-g.patch: -------------------------------------------------------------------------------- 1 | From ba35569bc2f909d23758efa89f3de0e1a7dc851e Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Sun, 17 Jan 2016 17:22:05 +0100 4 | Subject: Add origin and patcher git hash instead of patched git hash to 5 | version by using env variables which are set in build.sh 6 | 7 | Remove scriptus maven plugin, as its no longer needed. 8 | --- 9 | pom.xml | 19 +++---------------- 10 | 1 file changed, 3 insertions(+), 16 deletions(-) 11 | 12 | diff --git a/pom.xml b/pom.xml 13 | index f334496..babc3bf 100644 14 | --- a/pom.xml 15 | +++ b/pom.xml 16 | @@ -76,6 +76,9 @@ 17 | 1.7 18 | 1.7 19 | UTF-8 20 | + unknown 21 | + unknown 22 | + git:FlexPipe:${project.version}:origin-${origin_commit}-patcher-${patcher_commit}:${build.number} 23 | 24 | 25 | 26 | @@ -108,22 +111,6 @@ 27 | 28 | 29 | 30 | - net.md-5 31 | - scriptus 32 | - 0.3.1 33 | - 34 | - git:${project.name}:${project.version}:%s:${build.number} 35 | - 36 | - 37 | - 38 | - initialize 39 | - 40 | - describe 41 | - 42 | - 43 | - 44 | - 45 | - 46 | org.codehaus.mojo 47 | animal-sniffer-maven-plugin 48 | 1.13 49 | -- 50 | 1.9.5.msysgit.0 51 | 52 | -------------------------------------------------------------------------------- /patches/0008-Allow-configuring-module-source-uri-scheme-set-ci.ja.patch: -------------------------------------------------------------------------------- 1 | From 1e3f18a9c4113d2eee009fbc81b0cabd4592d508 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Sun, 17 Jan 2016 18:25:52 +0100 4 | Subject: Allow configuring module source uri scheme, set ci.janmm14.de as 5 | default 6 | 7 | --- 8 | .../src/main/java/net/md_5/bungee/BungeeCord.java | 2 +- 9 | .../bungee/module/ConfigurableModuleSource.java | 52 ++++++++++++++++++++++ 10 | .../java/net/md_5/bungee/module/ModuleManager.java | 2 +- 11 | 3 files changed, 54 insertions(+), 2 deletions(-) 12 | create mode 100644 proxy/src/main/java/net/md_5/bungee/module/ConfigurableModuleSource.java 13 | 14 | diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 15 | index 562196c..2f9a8c7 100644 16 | --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 17 | +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 18 | @@ -249,6 +249,7 @@ public class BungeeCord extends ProxyServer 19 | eventLoops = PipelineUtils.newEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty IO Thread #%1$d" ).build() ); 20 | 21 | File moduleDirectory = new File( "modules" ); 22 | + config.load(); 23 | moduleManager.load( this, moduleDirectory ); 24 | pluginManager.detectPlugins( moduleDirectory ); 25 | 26 | @@ -256,7 +257,6 @@ public class BungeeCord extends ProxyServer 27 | pluginManager.detectPlugins( pluginsFolder ); 28 | 29 | pluginManager.loadPlugins(); 30 | - config.load(); 31 | 32 | registerChannel( ForgeConstants.FML_TAG ); 33 | registerChannel( ForgeConstants.FML_HANDSHAKE_TAG ); 34 | diff --git a/proxy/src/main/java/net/md_5/bungee/module/ConfigurableModuleSource.java b/proxy/src/main/java/net/md_5/bungee/module/ConfigurableModuleSource.java 35 | new file mode 100644 36 | index 0000000..dab44c7 37 | --- /dev/null 38 | +++ b/proxy/src/main/java/net/md_5/bungee/module/ConfigurableModuleSource.java 39 | @@ -0,0 +1,52 @@ 40 | +package net.md_5.bungee.module; 41 | + 42 | +import com.google.common.io.ByteStreams; 43 | +import com.google.common.io.Files; 44 | +import java.io.IOException; 45 | +import java.net.URL; 46 | +import java.net.URLConnection; 47 | +import lombok.Data; 48 | +import net.md_5.bungee.Util; 49 | +import net.md_5.bungee.api.ProxyServer; 50 | + 51 | +@Data 52 | +public class ConfigurableModuleSource implements ModuleSource 53 | +{ 54 | + 55 | + private String moduleSourceScheme; 56 | + 57 | + private String getModuleSource(ModuleSpec module, ModuleVersion version) 58 | + { 59 | + if ( moduleSourceScheme == null ) 60 | + { 61 | + moduleSourceScheme = ProxyServer.getInstance().getConfigurationAdapter() 62 | + .getString( "module_source_scheme", "http://ci.janmm14.de/job/public~server~FlexPipe/$build_number$/artifact/BungeeCord/module/$maven_module_name$/target/$jar_name$.jar" ); 63 | + } 64 | + return moduleSourceScheme 65 | + .replace( "$build_number$", version.getBuild() ) 66 | + .replace( "$maven_module_name$", module.getName().replace( '_', '-' ) ) 67 | + .replace( "$jar_name$", module.getName() ); 68 | + } 69 | + 70 | + @Override 71 | + public void retrieve(ModuleSpec module, ModuleVersion version) 72 | + { 73 | + String moduleSource = getModuleSource( module, version ); 74 | + System.out.println( "Attempting to get module " + module.getName() + " b" + version.getBuild() + " from " + moduleSource ); 75 | + try 76 | + { 77 | + URL website = new URL( moduleSource ); 78 | + URLConnection con = website.openConnection(); 79 | + // 15 second timeout at various stages 80 | + con.setConnectTimeout( 15000 ); 81 | + con.setReadTimeout( 15000 ); 82 | + 83 | + 84 | + Files.write( ByteStreams.toByteArray( con.getInputStream() ), module.getFile() ); 85 | + System.out.println( "Download complete" ); 86 | + } catch ( IOException ex ) 87 | + { 88 | + System.out.println( "Failed to download: " + Util.exception( ex ) ); 89 | + } 90 | + } 91 | +} 92 | diff --git a/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java b/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java 93 | index 901fc5a..c61d67c 100644 94 | --- a/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java 95 | +++ b/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java 96 | @@ -28,7 +28,7 @@ public class ModuleManager 97 | 98 | public ModuleManager() 99 | { 100 | - knownSources.put( "jenkins", new JenkinsModuleSource() ); 101 | + knownSources.put( "jenkins", new ConfigurableModuleSource() ); 102 | } 103 | 104 | @SuppressFBWarnings( 105 | -- 106 | 1.9.5.msysgit.0 107 | 108 | -------------------------------------------------------------------------------- /patches/0009-Add-option-to-disable-module-downloading-disabled-by.patch: -------------------------------------------------------------------------------- 1 | From c650f815ee3c41aef48d1fad241d3b63fa1a1f06 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Sun, 17 Jan 2016 20:04:26 +0100 4 | Subject: Add option to disable module downloading (disabled by default), warn 5 | if no modules are present and module downloading is disabled. 6 | 7 | --- 8 | .../java/net/md_5/bungee/module/ModuleManager.java | 20 +++++++++++++++++++- 9 | 1 file changed, 19 insertions(+), 1 deletion(-) 10 | 11 | diff --git a/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java b/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java 12 | index c61d67c..97d6b5e 100644 13 | --- a/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java 14 | +++ b/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java 15 | @@ -5,6 +5,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 16 | import java.io.File; 17 | import java.io.FileInputStream; 18 | import java.io.FileWriter; 19 | +import java.io.FilenameFilter; 20 | import java.io.InputStream; 21 | import java.net.URI; 22 | import java.util.ArrayList; 23 | @@ -35,10 +36,27 @@ public class ModuleManager 24 | { 25 | "SF_SWITCH_FALLTHROUGH", "SF_SWITCH_NO_DEFAULT" 26 | }) 27 | - public void load(ProxyServer proxy, File moduleDirectory) throws Exception 28 | + public void load(ProxyServer proxy, final File moduleDirectory) throws Exception 29 | { 30 | moduleDirectory.mkdir(); 31 | 32 | + if ( !proxy.getConfigurationAdapter().getBoolean( "module_download", false ) ) 33 | + { 34 | + System.out.println( "Module download is disabled. Skipping." ); 35 | + if ( !Boolean.parseBoolean( System.getProperty( "me.minotopia.flexpipe.ignoreNoModues", "false" ) ) && moduleDirectory.list( new FilenameFilter() 36 | + { 37 | + @Override 38 | + public boolean accept(File dir, String name) 39 | + { 40 | + return dir.equals( moduleDirectory ) && name.toLowerCase().endsWith( ".jar" ); 41 | + } 42 | + } ).length == 0 ) 43 | + { 44 | + proxy.getLogger().warning( "As you have no modules, download some manually or enable automatic download in config.yml" ); 45 | + } 46 | + return; 47 | + } 48 | + 49 | ModuleVersion bungeeVersion = ModuleVersion.parse( proxy.getVersion() ); 50 | if ( bungeeVersion == null ) 51 | { 52 | -- 53 | 1.9.5.msysgit.0 54 | 55 | -------------------------------------------------------------------------------- /patches/0010-Old-build-warning-Update-download-source-add-likely-.patch: -------------------------------------------------------------------------------- 1 | From bb9669abc3aae6dae47ffabb03616c10ebbc6a4e Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Sun, 17 Jan 2016 20:06:14 +0100 4 | Subject: Old build warning: Update download source, add "likely", as its no 5 | real update check. 6 | 7 | --- 8 | bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java | 4 ++-- 9 | 1 file changed, 2 insertions(+), 2 deletions(-) 10 | 11 | diff --git a/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java b/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java 12 | index c0bf70e..32496dd 100644 13 | --- a/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java 14 | +++ b/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java 15 | @@ -41,8 +41,8 @@ public class BungeeCordLauncher 16 | deadline.add( Calendar.WEEK_OF_YEAR, -4 ); 17 | if ( buildDate.before( deadline.getTime() ) ) 18 | { 19 | - System.err.println( "*** Warning, this build is outdated ***" ); 20 | - System.err.println( "*** Please download a new build from http://ci.md-5.net/job/BungeeCord ***" ); 21 | + System.err.println( "*** Warning, this build is likely outdated ***" ); 22 | + System.err.println( "*** Please download a new build from http://ci.janmm14.de/job/public~server~FlexPipe/ ***" ); 23 | System.err.println( "*** You will get NO support regarding this build ***" ); 24 | System.err.println( "*** Server will start in 10 seconds ***" ); 25 | Thread.sleep( TimeUnit.SECONDS.toMillis( 10 ) ); 26 | -- 27 | 1.9.5.msysgit.0 28 | 29 | -------------------------------------------------------------------------------- /patches/0011-Edit-messages-without-modifying-the-JAR-file-more-ea.patch: -------------------------------------------------------------------------------- 1 | From 59a3ff7c2394d52414796e054d07a363c0bbfc4a Mon Sep 17 00:00:00 2001 2 | From: Minecrell 3 | Date: Mon, 28 Mar 2016 23:47:10 +0200 4 | Subject: Edit messages without modifying the JAR file more easily. 5 | 6 | Adds a new messages.properties file to customize them. 7 | Instead of just looking for the properties file, this patch creates it and allows reloading the messages on a bungee reload. 8 | 9 | This also reverts "#901: Better custom messages support", commit f265f7c5944d195ffa070016a63d893e9a2d8cb1. 10 | 11 | Signed-off-by: Janmm14 12 | --- 13 | .../main/java/net/md_5/bungee/api/ProxyServer.java | 5 ++ 14 | .../src/main/java/net/md_5/bungee/BungeeCord.java | 81 +++++++++++++++++----- 15 | .../net/md_5/bungee/command/CommandReload.java | 1 + 16 | 3 files changed, 70 insertions(+), 17 deletions(-) 17 | 18 | diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java 19 | index 11c5b68..28ca52f 100644 20 | --- a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java 21 | +++ b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java 22 | @@ -129,6 +129,11 @@ public abstract class ProxyServer 23 | public abstract void setConfigurationAdapter(ConfigurationAdapter adapter); 24 | 25 | /** 26 | + * Reload the proxy server messages from the configuration file. 27 | + */ 28 | + public abstract void reloadMessages(); 29 | + 30 | + /** 31 | * Get the currently in use reconnect handler. 32 | * 33 | * @return the in use reconnect handler 34 | diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 35 | index 2f9a8c7..8dec98f 100644 36 | --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 37 | +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 38 | @@ -8,6 +8,12 @@ import com.google.common.collect.Iterables; 39 | import com.google.common.collect.Lists; 40 | import com.google.common.collect.Sets; 41 | import com.google.gson.GsonBuilder; 42 | +import java.io.FileInputStream; 43 | +import java.io.FileOutputStream; 44 | +import java.io.InputStream; 45 | +import java.io.OutputStream; 46 | +import java.util.Properties; 47 | +import java.util.PropertyResourceBundle; 48 | import net.md_5.bungee.api.Favicon; 49 | import net.md_5.bungee.api.ServerPing; 50 | import net.md_5.bungee.api.Title; 51 | @@ -33,7 +39,6 @@ import io.netty.channel.EventLoopGroup; 52 | import io.netty.util.ResourceLeakDetector; 53 | import net.md_5.bungee.conf.Configuration; 54 | import java.io.File; 55 | -import java.io.FileReader; 56 | import java.io.IOException; 57 | import java.io.PrintStream; 58 | import java.net.InetSocketAddress; 59 | @@ -46,7 +51,6 @@ import java.util.HashSet; 60 | import java.util.Locale; 61 | import java.util.Map; 62 | import java.util.MissingResourceException; 63 | -import java.util.PropertyResourceBundle; 64 | import java.util.ResourceBundle; 65 | import java.util.Timer; 66 | import java.util.TimerTask; 67 | @@ -102,8 +106,7 @@ public class BungeeCord extends ProxyServer 68 | /** 69 | * Localization bundle. 70 | */ 71 | - private ResourceBundle baseBundle; 72 | - private ResourceBundle customBundle; 73 | + public ResourceBundle bundle; 74 | public EventLoopGroup eventLoops; 75 | /** 76 | * locations.yml save thread. 77 | @@ -151,7 +154,8 @@ public class BungeeCord extends ProxyServer 78 | private ConnectionThrottle connectionThrottle; 79 | private final ModuleManager moduleManager = new ModuleManager(); 80 | 81 | - 82 | + private final File messagesFile = new File ( "messages.properties" ); 83 | + 84 | { 85 | // TODO: Proper fallback when we interface the manager 86 | getPluginManager().registerCommand( null, new CommandReload() ); 87 | @@ -178,18 +182,10 @@ public class BungeeCord extends ProxyServer 88 | 89 | try 90 | { 91 | - baseBundle = ResourceBundle.getBundle( "messages" ); 92 | + bundle = ResourceBundle.getBundle( "messages" ); 93 | } catch ( MissingResourceException ex ) 94 | { 95 | - baseBundle = ResourceBundle.getBundle( "messages", Locale.ENGLISH ); 96 | - } 97 | - File file = new File( "messages.properties" ); 98 | - if ( file.isFile() ) 99 | - { 100 | - try ( FileReader rd = new FileReader( file ) ) 101 | - { 102 | - customBundle = new PropertyResourceBundle( rd ); 103 | - } 104 | + bundle = ResourceBundle.getBundle( "messages", Locale.ENGLISH ); 105 | } 106 | 107 | // This is a workaround for quite possibly the weirdest bug I have ever encountered in my life! 108 | @@ -227,6 +223,57 @@ public class BungeeCord extends ProxyServer 109 | logger.info( "Using standard Java compressor. To enable zero copy compression, run on 64 bit Linux" ); 110 | } 111 | } 112 | + 113 | + reloadMessages(); 114 | + } 115 | + 116 | + @Override 117 | + public void reloadMessages() 118 | + { 119 | + try // Make sure the translation file is up to date 120 | + { 121 | + Properties messages = new Properties(); 122 | + 123 | + if ( messagesFile.exists() ) 124 | + { 125 | + try ( InputStream is = new FileInputStream( messagesFile ) ) 126 | + { 127 | + messages.load( is ); 128 | + } 129 | + } 130 | + 131 | + // Check for new entries 132 | + int newEntries = 0; 133 | + for ( String key : bundle.keySet() ) 134 | + { 135 | + if ( !messages.containsKey( key ) ) 136 | + { 137 | + messages.put( key, bundle.getObject( key ) ); 138 | + newEntries++; 139 | + } 140 | + } 141 | + 142 | + if ( newEntries > 0 ) 143 | + { 144 | + // We need to save the file to add the new entries 145 | + try ( OutputStream os = new FileOutputStream( messagesFile ) ) 146 | + { 147 | + messages.store( os, "FlexPipe messages, last updated for " + getVersion() ); 148 | + } 149 | + } 150 | + } catch ( Exception ex ) 151 | + { 152 | + getLogger().log( Level.SEVERE, "Could not update messages", ex ); 153 | + } 154 | + 155 | + // Load the messages from the configuration file 156 | + try ( InputStream is = new FileInputStream( messagesFile ) ) 157 | + { 158 | + bundle = new PropertyResourceBundle( is ); 159 | + } catch ( Exception ex ) 160 | + { 161 | + getLogger().log( Level.SEVERE, "Could not reload messages", ex ); 162 | + } 163 | } 164 | 165 | /** 166 | @@ -472,7 +519,7 @@ public class BungeeCord extends ProxyServer 167 | String translation = ""; 168 | try 169 | { 170 | - translation = MessageFormat.format( customBundle != null && customBundle.containsKey( name ) ? customBundle.getString( name ) : baseBundle.getString( name ), args ); 171 | + translation = MessageFormat.format( bundle.getString( name ), args ); 172 | } catch ( MissingResourceException ex ) 173 | { 174 | } 175 | @@ -592,7 +639,7 @@ public class BungeeCord extends ProxyServer 176 | @Override 177 | public String getGameVersion() 178 | { 179 | - return Joiner.on( ", " ).join( ProtocolConstants.SUPPORTED_VERSIONS ); 180 | + return Joiner.on(", ").join(ProtocolConstants.SUPPORTED_VERSIONS); 181 | } 182 | 183 | @Override 184 | diff --git a/proxy/src/main/java/net/md_5/bungee/command/CommandReload.java b/proxy/src/main/java/net/md_5/bungee/command/CommandReload.java 185 | index cbbe03c..771ef5d 100644 186 | --- a/proxy/src/main/java/net/md_5/bungee/command/CommandReload.java 187 | +++ b/proxy/src/main/java/net/md_5/bungee/command/CommandReload.java 188 | @@ -18,6 +18,7 @@ public class CommandReload extends Command 189 | public void execute(CommandSender sender, String[] args) 190 | { 191 | BungeeCord.getInstance().config.load(); 192 | + BungeeCord.getInstance().reloadMessages(); 193 | BungeeCord.getInstance().stopListeners(); 194 | BungeeCord.getInstance().startListeners(); 195 | BungeeCord.getInstance().getPluginManager().callEvent( new ProxyReloadEvent( sender ) ); 196 | -- 197 | 1.9.5.msysgit.0 198 | 199 | -------------------------------------------------------------------------------- /patches/0012-Don-t-create-a-new-KickStringWriter-for-each-new-con.patch: -------------------------------------------------------------------------------- 1 | From 78f286b79f291abf50c201b167c5170e7eef5373 Mon Sep 17 00:00:00 2001 2 | From: Harry 3 | Date: Tue, 16 Feb 2016 23:42:49 +0100 4 | Subject: Don't create a new KickStringWriter for each new connection. 5 | 6 | Signed-off-by: Janmm14 7 | --- 8 | protocol/src/main/java/net/md_5/bungee/protocol/KickStringWriter.java | 2 ++ 9 | proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java | 4 +++- 10 | 2 files changed, 5 insertions(+), 1 deletion(-) 11 | 12 | diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/KickStringWriter.java b/protocol/src/main/java/net/md_5/bungee/protocol/KickStringWriter.java 13 | index eda9571..e2e34a3 100644 14 | --- a/protocol/src/main/java/net/md_5/bungee/protocol/KickStringWriter.java 15 | +++ b/protocol/src/main/java/net/md_5/bungee/protocol/KickStringWriter.java 16 | @@ -1,9 +1,11 @@ 17 | package net.md_5.bungee.protocol; 18 | 19 | import io.netty.buffer.ByteBuf; 20 | +import io.netty.channel.ChannelHandler; 21 | import io.netty.channel.ChannelHandlerContext; 22 | import io.netty.handler.codec.MessageToByteEncoder; 23 | 24 | +@ChannelHandler.Sharable 25 | public class KickStringWriter extends MessageToByteEncoder 26 | { 27 | 28 | diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java 29 | index 621a06c..19b3dfc 100644 30 | --- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java 31 | +++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java 32 | @@ -53,7 +53,7 @@ public class PipelineUtils 33 | ch.pipeline().addBefore( FRAME_DECODER, LEGACY_DECODER, new LegacyDecoder() ); 34 | ch.pipeline().addAfter( FRAME_DECODER, PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) ); 35 | ch.pipeline().addAfter( FRAME_PREPENDER, PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) ); 36 | - ch.pipeline().addBefore( FRAME_PREPENDER, LEGACY_KICKER, new KickStringWriter() ); 37 | + ch.pipeline().addBefore( FRAME_PREPENDER, LEGACY_KICKER, KICK_STRING_WRITER ); 38 | ch.pipeline().get( HandlerBoss.class ).setHandler( new InitialHandler( BungeeCord.getInstance(), ch.attr( LISTENER ).get() ) ); 39 | } 40 | }; 41 | @@ -70,6 +70,8 @@ public class PipelineUtils 42 | public static final String LEGACY_DECODER = "legacy-decoder"; 43 | public static final String LEGACY_KICKER = "legacy-kick"; 44 | 45 | + private static final KickStringWriter KICK_STRING_WRITER = new KickStringWriter(); 46 | + 47 | private static boolean epoll; 48 | 49 | static 50 | -- 51 | 1.9.5.msysgit.0 52 | 53 | -------------------------------------------------------------------------------- /patches/0014-Don-t-create-a-data-input-stream-for-every-plugin-me.patch: -------------------------------------------------------------------------------- 1 | From 95feee6fbaf1575a4d08c16575b7cd00b6d216c2 Mon Sep 17 00:00:00 2001 2 | From: Tux 3 | Date: Wed, 17 Feb 2016 00:20:59 +0100 4 | Subject: Don't create a data input stream for every plugin message we get from 5 | servers. 6 | 7 | Signed-off-by: Janmm14 8 | --- 9 | proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java | 3 ++- 10 | 1 file changed, 2 insertions(+), 1 deletion(-) 11 | 12 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java 13 | index d139e03..7ab1b94 100644 14 | --- a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java 15 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java 16 | @@ -221,7 +221,6 @@ public class DownstreamBridge extends PacketHandler 17 | @Override 18 | public void handle(PluginMessage pluginMessage) throws Exception 19 | { 20 | - DataInput in = pluginMessage.getStream(); 21 | PluginMessageEvent event = new PluginMessageEvent( con.getServer(), con, pluginMessage.getTag(), pluginMessage.getData().clone() ); 22 | 23 | if ( bungee.getPluginManager().callEvent( event ).isCancelled() ) 24 | @@ -248,6 +247,8 @@ public class DownstreamBridge extends PacketHandler 25 | 26 | if ( pluginMessage.getTag().equals( "BungeeCord" ) ) 27 | { 28 | + DataInput in = pluginMessage.getStream(); 29 | + 30 | ByteArrayDataOutput out = ByteStreams.newDataOutput(); 31 | String subChannel = in.readUTF(); 32 | 33 | -- 34 | 1.9.5.msysgit.0 35 | 36 | -------------------------------------------------------------------------------- /patches/0015-Avoid-regex-in-getLocale.patch: -------------------------------------------------------------------------------- 1 | From 3a13cc299c40ed9f0efe77ecccf4c63851c3b6f8 Mon Sep 17 00:00:00 2001 2 | From: Tux 3 | Date: Wed, 17 Feb 2016 00:29:46 +0100 4 | Subject: Avoid regex in getLocale() 5 | 6 | Signed-off-by: Janmm14 7 | --- 8 | proxy/src/main/java/net/md_5/bungee/UserConnection.java | 2 +- 9 | 1 file changed, 1 insertion(+), 1 deletion(-) 10 | 11 | diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java 12 | index 00d9719..f2c361d 100644 13 | --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java 14 | +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java 15 | @@ -549,7 +549,7 @@ public final class UserConnection implements ProxiedPlayer 16 | @Override 17 | public Locale getLocale() 18 | { 19 | - return ( locale == null && settings != null ) ? locale = Locale.forLanguageTag( settings.getLocale().replaceAll( "_", "-" ) ) : locale; 20 | + return ( locale == null && settings != null ) ? locale = Locale.forLanguageTag( settings.getLocale().replace( "_", "-" ) ) : locale; 21 | } 22 | 23 | @Override 24 | -- 25 | 1.9.5.msysgit.0 26 | 27 | -------------------------------------------------------------------------------- /patches/0016-Resolve-sendData-deadlocks.patch: -------------------------------------------------------------------------------- 1 | From d95616079507e4ca224fa71b819c194f287f307f Mon Sep 17 00:00:00 2001 2 | From: Tux 3 | Date: Wed, 17 Feb 2016 00:34:15 +0100 4 | Subject: Resolve sendData() deadlocks 5 | 6 | Signed-off-by: Janmm14 7 | --- 8 | .../java/net/md_5/bungee/BungeeServerInfo.java | 22 ++++++++++++++-------- 9 | 1 file changed, 14 insertions(+), 8 deletions(-) 10 | 11 | diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java 12 | index bc33465..d6d38f8 100644 13 | --- a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java 14 | +++ b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java 15 | @@ -101,19 +101,25 @@ public class BungeeServerInfo implements ServerInfo 16 | Preconditions.checkNotNull( channel, "channel" ); 17 | Preconditions.checkNotNull( data, "data" ); 18 | 19 | - synchronized ( packetQueue ) 20 | + Server server; 21 | + 22 | + synchronized ( players ) 23 | { 24 | - Server server = ( players.isEmpty() ) ? null : players.iterator().next().getServer(); 25 | - if ( server != null ) 26 | - { 27 | - server.sendData( channel, data ); 28 | - return true; 29 | - } else if ( queue ) 30 | + server = players.isEmpty() ? null : players.iterator().next().getServer(); 31 | + } 32 | + 33 | + if ( server != null ) 34 | + { 35 | + server.sendData( channel, data ); 36 | + return true; 37 | + } else if ( queue ) 38 | + { 39 | + synchronized ( packetQueue ) 40 | { 41 | packetQueue.add( new PluginMessage( channel, data, false ) ); 42 | } 43 | - return false; 44 | } 45 | + return false; 46 | } 47 | 48 | @Override 49 | -- 50 | 1.9.5.msysgit.0 51 | 52 | -------------------------------------------------------------------------------- /patches/0017-PluginManager.dispatchCommand-avoids-regex-while-spl.patch: -------------------------------------------------------------------------------- 1 | From 254c274d9b1125951773c13ea943013c8d98f19e Mon Sep 17 00:00:00 2001 2 | From: Tux 3 | Date: Wed, 17 Feb 2016 00:37:40 +0100 4 | Subject: PluginManager.dispatchCommand() avoids regex while splitting 5 | commands. 6 | 7 | Java 7 introduced an optimized String.split() that should be used instead (affects command dispatch). 8 | 9 | Signed-off-by: Janmm14 10 | --- 11 | api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java | 3 +-- 12 | 1 file changed, 1 insertion(+), 2 deletions(-) 13 | 14 | diff --git a/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java b/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java 15 | index 71a5a15..04a61b1 100644 16 | --- a/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java 17 | +++ b/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java 18 | @@ -42,7 +42,6 @@ import org.yaml.snakeyaml.introspector.PropertyUtils; 19 | public class PluginManager 20 | { 21 | 22 | - private static final Pattern argsSplit = Pattern.compile( " " ); 23 | /*========================================================================*/ 24 | private final ProxyServer proxy; 25 | /*========================================================================*/ 26 | @@ -126,7 +125,7 @@ public class PluginManager 27 | */ 28 | public boolean dispatchCommand(CommandSender sender, String commandLine, List tabResults) 29 | { 30 | - String[] split = argsSplit.split( commandLine, -1 ); 31 | + String[] split = commandLine.split( " ", -1 ); 32 | // Check for chat that only contains " " 33 | if ( split.length == 0 ) 34 | { 35 | -- 36 | 1.9.5.msysgit.0 37 | 38 | -------------------------------------------------------------------------------- /patches/0018-Micro-optimize-mojang-uuid-to-uuid-object-conversion.patch: -------------------------------------------------------------------------------- 1 | From 85e8c16eea6e3822feba33de7258f57dcee7ab44 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Thu, 18 Feb 2016 15:59:37 +0100 4 | Subject: Micro-optimize mojang uuid to uuid object conversion (and other way 5 | round). 6 | 7 | Code should take half to one forth the time. 8 | 9 | Based on work by @Minecrell, see here https://github.com/WaterfallMC/Waterfall/commit/a5c670f548ef55367b20df2d343652ceb0247efb#commitcomment-16083290 and https://github.com/WaterfallMC/Waterfall/commit/c29574d262032657ae6e7c9dad49a99caeb415f4 10 | --- 11 | api/src/main/java/net/md_5/bungee/Util.java | 127 ++++++++++++++++++++- 12 | .../main/java/net/md_5/bungee/api/ServerPing.java | 2 +- 13 | .../net/md_5/bungee/connection/InitialHandler.java | 2 +- 14 | 3 files changed, 128 insertions(+), 3 deletions(-) 15 | 16 | diff --git a/api/src/main/java/net/md_5/bungee/Util.java b/api/src/main/java/net/md_5/bungee/Util.java 17 | index 86a0055..7b4532b 100644 18 | --- a/api/src/main/java/net/md_5/bungee/Util.java 19 | +++ b/api/src/main/java/net/md_5/bungee/Util.java 20 | @@ -4,6 +4,7 @@ import com.google.common.base.Joiner; 21 | import java.net.InetSocketAddress; 22 | import java.net.URI; 23 | import java.net.URISyntaxException; 24 | +import java.util.Arrays; 25 | import java.util.UUID; 26 | 27 | /** 28 | @@ -78,6 +79,130 @@ public class Util 29 | */ 30 | public static UUID getUUID(String uuid) 31 | { 32 | - return UUID.fromString( uuid.substring( 0, 8 ) + "-" + uuid.substring( 8, 12 ) + "-" + uuid.substring( 12, 16 ) + "-" + uuid.substring( 16, 20 ) + "-" + uuid.substring( 20, 32 ) ); 33 | + return new UUID( Long.parseUnsignedLong( uuid.substring( 0, 16 ), 16 ), Long.parseUnsignedLong( uuid.substring( 16 ), 16 ) ); 34 | + } 35 | + 36 | + /** 37 | + * Converts a UUID to a Mojang UUID 38 | + * 39 | + * @param uuid The string to be converted 40 | + * @return The result 41 | + */ 42 | + @SuppressWarnings( "Duplicates" ) 43 | + public static String getMojangUUID(UUID uuid) 44 | + { 45 | + // return toHexBits( uuid.getMostSignificantBits() ) + toHexBits( uuid.getLeastSignificantBits() ); 46 | + // inlined method and Strings.padStart() for optimization with StringBuilder - saves 1/3 up to 1/2 time 47 | + 48 | + StringBuilder stringBuilder = new StringBuilder( 32 ); 49 | + 50 | + UUIDUtil.padStartMax16Chars( stringBuilder, UUIDUtil.toUnsignedStringRadix16_chararray( uuid.getMostSignificantBits() ) ); 51 | + 52 | + // Add missing leading zeros (if necessary) 53 | + UUIDUtil.padStartMax16Chars( stringBuilder, UUIDUtil.toUnsignedStringRadix16_chararray( uuid.getLeastSignificantBits()) ); 54 | + 55 | + return stringBuilder.toString(); 56 | + } 57 | + 58 | + /** 59 | + * provides optimized methods of library methods for creating a mojang uuid only due to removed checks and fixed values 60 | + * 61 | + * yeah this is micro-optimization to the largest extend 62 | + */ 63 | + private static final class UUIDUtil 64 | + { 65 | + /** 66 | + * pads a string infront with a '0' to achieve its 16 chars long, make sure the string is not longer than 16 chars or it won't work 67 | + * 68 | + * @param sb the string builder to append the result to 69 | + * @param toPrefix the string to optionally pad and 70 | + * @see com.google.common.base.Strings#padStart(String, int, char) Strings.padStart(toPrefix, 16, '0') 71 | + */ 72 | + private static void padStartMax16Chars(StringBuilder sb, char[] toPrefix) 73 | + { 74 | + // Add missing leading zeros (if necessary) 75 | + if ( toPrefix.length != 16 ) 76 | + { 77 | + for ( int i = toPrefix.length; i < 16; i++ ) 78 | + { 79 | + sb.append( '0' ); 80 | + } 81 | + } 82 | + sb.append( toPrefix ); 83 | + } 84 | + 85 | + //internal java methods simplified 86 | + 87 | + private static char[] toUnsignedStringRadix16_chararray(long i) 88 | + { 89 | + if ( i >= 0 ) 90 | + { 91 | + return longToStringRadix16NotNegative( i ); 92 | + } else 93 | + { 94 | + return longToHexString( i ); 95 | + } 96 | + } 97 | + 98 | + private static final char[] integer_digits = { 99 | + '0', '1', '2', '3', '4', '5', 100 | + '6', '7', '8', '9', 'a', 'b', 101 | + 'c', 'd', 'e', 'f', 'g', 'h', 102 | + 'i', 'j', 'k', 'l', 'm', 'n', 103 | + 'o', 'p', 'q', 'r', 's', 't', 104 | + 'u', 'v', 'w', 'x', 'y', 'z' 105 | + }; 106 | + 107 | + private static char[] longToHexString(long i) 108 | + { 109 | + // Long.toUnsignedString0(i, 4) 110 | + int mag = Long.SIZE - Long.numberOfLeadingZeros( i ); 111 | + int chars = Math.max( ( ( mag + ( 4 - 1 ) ) / 4 ), 1 ); 112 | + char[] buf = new char[ chars ]; 113 | + 114 | + formatUnsignedLong( i, 4, buf, chars ); 115 | + return buf; 116 | + } 117 | + 118 | + /** 119 | + * Format a long (treated as unsigned) into a character buffer. 120 | + * 121 | + * @param val the unsigned long to format 122 | + * @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary) 123 | + * @param buf the character buffer to write to 124 | + * @param len the number of characters to write 125 | + * @return the lowest character location used 126 | + */ 127 | + private static int formatUnsignedLong(long val, int shift, char[] buf, int len) 128 | + { 129 | + int charPos = len; 130 | + int radix = 1 << shift; 131 | + int mask = radix - 1; 132 | + do 133 | + { 134 | + buf[ --charPos ] = integer_digits[ ( ( int ) val ) & mask ]; 135 | + val >>>= shift; 136 | + } while ( val != 0 && charPos > 0 ); 137 | + 138 | + return charPos; 139 | + } 140 | + 141 | + private static char[] longToStringRadix16NotNegative(long i) 142 | + { 143 | + int radix = 16; 144 | + char[] buf = new char[ 65 ]; 145 | + int charPos = 64; 146 | + 147 | + i = -i; 148 | + 149 | + while ( i <= -radix ) 150 | + { 151 | + buf[ charPos-- ] = integer_digits[ ( int ) ( -( i % radix ) ) ]; 152 | + i = i / radix; 153 | + } 154 | + buf[ charPos ] = integer_digits[ ( int ) ( -i ) ]; 155 | + 156 | + return Arrays.copyOfRange( buf, charPos, 65 ); 157 | + } 158 | } 159 | } 160 | diff --git a/api/src/main/java/net/md_5/bungee/api/ServerPing.java b/api/src/main/java/net/md_5/bungee/api/ServerPing.java 161 | index 27b5184..bc9d7cb 100644 162 | --- a/api/src/main/java/net/md_5/bungee/api/ServerPing.java 163 | +++ b/api/src/main/java/net/md_5/bungee/api/ServerPing.java 164 | @@ -75,7 +75,7 @@ public class ServerPing 165 | 166 | public String getId() 167 | { 168 | - return uniqueId.toString().replaceAll( "-", "" ); 169 | + return Util.getMojangUUID( uniqueId ); 170 | } 171 | } 172 | 173 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 174 | index 8cbb9de..b350b52 100644 175 | --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 176 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 177 | @@ -577,7 +577,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection 178 | @Override 179 | public String getUUID() 180 | { 181 | - return uniqueId.toString().replaceAll( "-", "" ); 182 | + return Util.getMojangUUID( uniqueId ); 183 | } 184 | 185 | @Override 186 | -- 187 | 1.9.5.msysgit.0 188 | 189 | -------------------------------------------------------------------------------- /patches/0019-Performance-log-sanity-No-exception-on-invalid-packe.patch: -------------------------------------------------------------------------------- 1 | From 59b003aaf848a3804a6a4f328ef91db3e937820d Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Sun, 21 Feb 2016 22:01:12 +0100 4 | Subject: Performance, log sanity: No exception on invalid packet order, just 5 | disconnect and one line notice. 6 | 7 | --- 8 | .../net/md_5/bungee/connection/InitialHandler.java | 43 +++++++++++++++++++--- 9 | 1 file changed, 38 insertions(+), 5 deletions(-) 10 | 11 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 12 | index b350b52..388d3b0 100644 13 | --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 14 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 15 | @@ -188,10 +188,31 @@ public class InitialHandler extends PacketHandler implements PendingConnection 16 | return pos == -1 ? str : str.substring( 0, pos ); 17 | } 18 | 19 | + /** 20 | + *

Checks whether the current state of this connection equals the expected state.

21 | + * If states do not fit, this method gives a log information and disconnects without delay. 22 | + * 23 | + * @param expectedState the state this InitialHandler has to be in 24 | + * @return {@code false} if the states do not equal and whether the handling of the packet should be aborted 25 | + */ 26 | + private boolean checkState(State expectedState) 27 | + { 28 | + if (thisState != expectedState) 29 | + { 30 | + bungee.getLogger().log( Level.WARNING, "{0} Expected {1}, but was on {2}", new Object[]{ this, expectedState, thisState } ); 31 | + disconnect( "Expected " + expectedState + ", but was on " + thisState ); 32 | + return false; 33 | + } 34 | + return true; 35 | + } 36 | + 37 | @Override 38 | public void handle(StatusRequest statusRequest) throws Exception 39 | { 40 | - Preconditions.checkState( thisState == State.STATUS, "Not expecting STATUS" ); 41 | + if ( !checkState( State.STATUS ) ) 42 | + { 43 | + return; 44 | + } 45 | 46 | ServerInfo forced = AbstractReconnectHandler.getForcedHost( this ); 47 | final String motd = ( forced != null ) ? forced.getMotd() : listener.getMotd(); 48 | @@ -231,7 +252,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection 49 | @Override 50 | public void handle(PingPacket ping) throws Exception 51 | { 52 | - Preconditions.checkState( thisState == State.PING, "Not expecting PING" ); 53 | + if ( !checkState( State.PING ) ) 54 | + { 55 | + return; 56 | + } 57 | unsafe.sendPacket( ping ); 58 | disconnect( "" ); 59 | } 60 | @@ -239,7 +263,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection 61 | @Override 62 | public void handle(Handshake handshake) throws Exception 63 | { 64 | - Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" ); 65 | + if ( !checkState( State.HANDSHAKE ) ) 66 | + { 67 | + return; 68 | + } 69 | this.handshake = handshake; 70 | ch.setVersion( handshake.getProtocolVersion() ); 71 | 72 | @@ -313,7 +340,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection 73 | @Override 74 | public void handle(LoginRequest loginRequest) throws Exception 75 | { 76 | - Preconditions.checkState( thisState == State.USERNAME, "Not expecting USERNAME" ); 77 | + if ( !checkState( State.USERNAME ) ) 78 | + { 79 | + return; 80 | + } 81 | this.loginRequest = loginRequest; 82 | 83 | if ( getName().contains( "." ) ) 84 | @@ -370,7 +400,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection 85 | @Override 86 | public void handle(final EncryptionResponse encryptResponse) throws Exception 87 | { 88 | - Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" ); 89 | + if ( !checkState( State.ENCRYPT ) ) 90 | + { 91 | + return; 92 | + } 93 | 94 | SecretKey sharedKey = EncryptionUtil.getSecret( encryptResponse, request ); 95 | BungeeCipher decrypt = EncryptionUtil.getCipher( false, sharedKey ); 96 | -- 97 | 1.9.5.msysgit.0 98 | 99 | -------------------------------------------------------------------------------- /patches/0020-Set-TCP_NODELAY-option-suggested-by-PunKeel.patch: -------------------------------------------------------------------------------- 1 | From 552ec92416328a6c62a0db5d2aa36ca747de05ee Mon Sep 17 00:00:00 2001 2 | From: Harry 3 | Date: Sat, 27 Feb 2016 12:04:53 +0100 4 | Subject: Set TCP_NODELAY option, suggested by @PunKeel 5 | 6 | Code adapted from pr https://github.com/WaterfallMC/Waterfall/pull/36 by @Harry5573, thanks @minecrafter for research 7 | 8 | Signed-off-by: Janmm14 9 | --- 10 | proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java | 3 +++ 11 | 1 file changed, 3 insertions(+) 12 | 13 | diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java 14 | index 19b3dfc..e6e74f4 100644 15 | --- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java 16 | +++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java 17 | @@ -123,6 +123,9 @@ public class PipelineUtils 18 | { 19 | // IP_TOS is not supported (Windows XP / Windows Server 2003) 20 | } 21 | + // Minecraft packets are too big to benefit from Nagle's algorithm, which we disable here, says @minecrafter 22 | + // CraftBukkit / Spigot use this option also according to 23 | + ch.config().setOption( ChannelOption.TCP_NODELAY, true ); 24 | ch.config().setAllocator( PooledByteBufAllocator.DEFAULT ); 25 | 26 | ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) ); 27 | -- 28 | 1.9.5.msysgit.0 29 | 30 | -------------------------------------------------------------------------------- /patches/0021-Don-t-allow-channel-buffers-to-grow-beyond-a-reasona.patch: -------------------------------------------------------------------------------- 1 | From c9c4bcb072a42fdc58b1ab7e4c4ab2171d4c7968 Mon Sep 17 00:00:00 2001 2 | From: Iceee 3 | Date: Sat, 27 Feb 2016 12:07:18 +0100 4 | Subject: Don't allow channel buffers to grow beyond a reasonable limit, thanks 5 | to @Iceee for this improvement 6 | 7 | Adapted from https://github.com/SpigotMC/BungeeCord/commit/1a997bf95c3832b65aa0a08160a83890594e650d 8 | 9 | Signed-off-by: Janmm14 10 | --- 11 | proxy/src/main/java/net/md_5/bungee/BungeeCord.java | 3 +++ 12 | 1 file changed, 3 insertions(+) 13 | 14 | diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 15 | index eb5a3d8..c32562d 100644 16 | --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 17 | +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 18 | @@ -351,6 +351,9 @@ public class BungeeCord extends ProxyServer 19 | new ServerBootstrap() 20 | .channel( PipelineUtils.getServerChannel() ) 21 | .option( ChannelOption.SO_REUSEADDR, true ) // TODO: Move this elsewhere! 22 | + // the next two options help against jvm gc spikes, says @Iceee 23 | + .childOption( ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 1024 * 1024 * 10 ) 24 | + .childOption( ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 1024 * 1024 ) 25 | .childAttr( PipelineUtils.LISTENER, info ) 26 | .childHandler( PipelineUtils.SERVER_CHILD ) 27 | .group( eventLoops ) 28 | -- 29 | 1.9.5.msysgit.0 30 | 31 | -------------------------------------------------------------------------------- /patches/0022-Improve-connection-closing-removes-need-of-kick-dela.patch: -------------------------------------------------------------------------------- 1 | From c8cd6f010d52ce69b4b6e5ca1418bb1db107e97d Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Mon, 29 Feb 2016 22:14:58 +0100 4 | Subject: Improve connection closing, removes need of kick delay. 5 | 6 | Removes not longer needed disconnect variable in InitialHandler and let the ChannelWrapper handle it again as we removed the delay. 7 | 8 | Adapted from https://github.com/SpigotMC/BungeeCord/pull/1706 by @kamcio96, he claimed that these channel closing changes are removing the need of delayed kick packets. 9 | 10 | Also adapted void promise usage from https://github.com/WaterfallMC/Waterfall/commit/b88c7de33abc0b2ea220b324dd77985628ea81d4 by @minecrafter 11 | --- 12 | .../java/net/md_5/bungee/ServerConnection.java | 2 +- 13 | .../main/java/net/md_5/bungee/ServerConnector.java | 7 +++--- 14 | .../main/java/net/md_5/bungee/UserConnection.java | 13 ++--------- 15 | .../net/md_5/bungee/connection/InitialHandler.java | 23 +++++++----------- 16 | .../net/md_5/bungee/connection/UpstreamBridge.java | 2 +- 17 | .../java/net/md_5/bungee/netty/ChannelWrapper.java | 27 ++++++++++++++++------ 18 | 6 files changed, 36 insertions(+), 38 deletions(-) 19 | 20 | diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java 21 | index 555a193..2304bd8 100644 22 | --- a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java 23 | +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java 24 | @@ -54,7 +54,7 @@ public class ServerConnection implements Server 25 | 26 | if ( !ch.isClosed() ) 27 | { 28 | - ch.getHandle().eventLoop().schedule( ( Runnable ) () -> ch.getHandle().close(), 100, TimeUnit.MILLISECONDS ); 29 | + ch.getHandle().eventLoop().schedule( ( Runnable ) ch::close, 100, TimeUnit.MILLISECONDS ); 30 | } 31 | } 32 | 33 | diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java 34 | index 81d62ba..9263e90 100644 35 | --- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java 36 | +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java 37 | @@ -99,7 +99,7 @@ public class ServerConnector extends PacketHandler 38 | copiedHandshake.setHost( newHost ); 39 | } else if ( !user.getExtraDataInHandshake().isEmpty() ) 40 | { 41 | - // Only restore the extra data if IP forwarding is off. 42 | + // Only restore the extra data if IP forwarding is off. 43 | // TODO: Add support for this data with IP forwarding. 44 | copiedHandshake.setHost( copiedHandshake.getHost() + user.getExtraDataInHandshake() ); 45 | } 46 | @@ -230,13 +230,14 @@ public class ServerConnector extends PacketHandler 47 | user.unsafe().sendPacket( new Respawn( login.getDimension(), login.getDifficulty(), login.getGameMode(), login.getLevelType() ) ); 48 | 49 | // Remove from old servers 50 | - user.getServer().disconnect( "Quitting" ); 51 | + user.getServer().setObsolete( true ); 52 | + user.getServer().disconnect(); 53 | } 54 | 55 | // TODO: Fix this? 56 | if ( !user.isActive() ) 57 | { 58 | - server.disconnect( "Quitting" ); 59 | + server.disconnect(); 60 | // Silly server admins see stack trace and die 61 | bungee.getLogger().warning( "No client connected for pending server!" ); 62 | return; 63 | diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java 64 | index f2c361d..1f41298 100644 65 | --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java 66 | +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java 67 | @@ -365,20 +365,11 @@ public final class UserConnection implements ProxiedPlayer 68 | getName(), BaseComponent.toLegacyText( reason ) 69 | } ); 70 | 71 | - // Why do we have to delay this you might ask? Well the simple reason is MOJANG. 72 | - // Despite many a bug report posted, ever since the 1.7 protocol rewrite, the client STILL has a race condition upon switching protocols. 73 | - // As such, despite the protocol switch packets already having been sent, there is the possibility of a client side exception 74 | - // To help combat this we will wait half a second before actually sending the disconnected packet so that whoever is on the other 75 | - // end has a somewhat better chance of receiving the proper packet. 76 | - ch.getHandle().eventLoop().schedule( ( Runnable ) () -> { 77 | - unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) ); 78 | - ch.close(); 79 | - }, 500, TimeUnit.MILLISECONDS ); 80 | - 81 | + ch.close( new Kick( ComponentSerializer.toString( reason ) ) ); 82 | 83 | if ( server != null ) 84 | { 85 | - server.disconnect( "Quitting" ); 86 | + server.disconnect(); //reason is ignored anyway 87 | } 88 | } 89 | } 90 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 91 | index 388d3b0..7ed4d3a 100644 92 | --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 93 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 94 | @@ -96,12 +96,11 @@ public class InitialHandler extends PacketHandler implements PendingConnection 95 | private boolean legacy; 96 | @Getter 97 | private String extraDataInHandshake = ""; 98 | - private boolean disconnecting; 99 | 100 | @Override 101 | public boolean shouldHandle(PacketWrapper packet) throws Exception 102 | { 103 | - return !disconnecting; 104 | + return !ch.isClosed(); 105 | } 106 | 107 | private enum State 108 | @@ -541,21 +540,15 @@ public class InitialHandler extends PacketHandler implements PendingConnection 109 | @Override 110 | public void disconnect(final BaseComponent... reason) 111 | { 112 | - if ( !disconnecting || !ch.isClosed() ) 113 | + if ( !ch.isClosed() ) 114 | { 115 | - disconnecting = true; 116 | - // Why do we have to delay this you might ask? Well the simple reason is MOJANG. 117 | - // Despite many a bug report posted, ever since the 1.7 protocol rewrite, the client STILL has a race condition upon switching protocols. 118 | - // As such, despite the protocol switch packets already having been sent, there is the possibility of a client side exception 119 | - // To help combat this we will wait half a second before actually sending the disconnected packet so that whoever is on the other 120 | - // end has a somewhat better chance of receiving the proper packet. 121 | - ch.getHandle().eventLoop().schedule( ( Runnable ) () -> { 122 | - if ( thisState != State.STATUS && thisState != State.PING ) 123 | - { 124 | - unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) ); 125 | - } 126 | + if ( thisState != State.STATUS && thisState != State.PING ) 127 | + { 128 | + ch.close( new Kick( ComponentSerializer.toString( reason ) ) ); 129 | + } else 130 | + { 131 | ch.close(); 132 | - }, 500, TimeUnit.MILLISECONDS ); 133 | + } 134 | } 135 | } 136 | 137 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 138 | index df139d3..9ece928 100644 139 | --- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 140 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 141 | @@ -77,7 +77,7 @@ public class UpstreamBridge extends PacketHandler 142 | { 143 | player.unsafe().sendPacket( packet ); 144 | } 145 | - con.getServer().disconnect( "Quitting" ); 146 | + con.getServer().disconnect(); 147 | } 148 | } 149 | 150 | diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java 151 | index 06d19c3..4514a21 100644 152 | --- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java 153 | +++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java 154 | @@ -1,5 +1,6 @@ 155 | package net.md_5.bungee.netty; 156 | 157 | +import io.netty.channel.ChannelFutureListener; 158 | import net.md_5.bungee.compress.PacketCompressor; 159 | import net.md_5.bungee.compress.PacketDecompressor; 160 | import net.md_5.bungee.protocol.PacketWrapper; 161 | @@ -38,30 +39,42 @@ public class ChannelWrapper 162 | 163 | public void write(Object packet) 164 | { 165 | - if ( !closed ) 166 | + if ( !isClosed() ) 167 | { 168 | if ( packet instanceof PacketWrapper ) 169 | { 170 | ( (PacketWrapper) packet ).setReleased( true ); 171 | - ch.write( ( (PacketWrapper) packet ).buf, ch.voidPromise() ); 172 | + ch.writeAndFlush( ( (PacketWrapper) packet ).buf, ch.voidPromise()); 173 | } else 174 | { 175 | - ch.write( packet, ch.voidPromise() ); 176 | + ch.writeAndFlush( packet, ch.voidPromise() ); 177 | } 178 | - ch.flush(); 179 | } 180 | } 181 | 182 | public void close() 183 | { 184 | - if ( !closed ) 185 | + if ( !isClosed() ) 186 | { 187 | closed = true; 188 | - ch.flush(); 189 | - ch.close(); 190 | + ch.flush().close(); 191 | } 192 | } 193 | 194 | + public void close(Object packet) 195 | + { 196 | + if ( !isClosed() ) 197 | + { 198 | + closed = true; 199 | + ch.writeAndFlush( packet ).addListeners( ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE, ChannelFutureListener.CLOSE ); 200 | + } 201 | + } 202 | + 203 | + public boolean isClosed() 204 | + { 205 | + return closed || !ch.isActive(); 206 | + } 207 | + 208 | public void addBefore(String baseName, String name, ChannelHandler handler) 209 | { 210 | Preconditions.checkState( ch.eventLoop().inEventLoop(), "cannot add handler outside of event loop" ); 211 | -- 212 | 1.9.5.msysgit.0 213 | 214 | -------------------------------------------------------------------------------- /patches/0023-Use-multiple-EventLoopGroups.patch: -------------------------------------------------------------------------------- 1 | From de6bc4def812568c5d9abcd23cced544b3c4f6a6 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Mon, 29 Feb 2016 20:44:17 +0100 4 | Subject: Use multiple EventLoopGroups. 5 | 6 | Adapted from https://github.com/SpigotMC/BungeeCord/pull/1706 by @kamcio96 7 | 8 | Use a boss and a worker EventLoopGroup for the Minecraft connection handling and a separate one for query connections (if query is enabled). 9 | 10 | Usage of boss and a worker EventLoopGroups is the recommended setup for netty servers. 11 | --- 12 | .../src/main/java/net/md_5/bungee/BungeeCord.java | 34 ++++++++++++++++++---- 13 | .../java/net/md_5/bungee/BungeeServerInfo.java | 2 +- 14 | 2 files changed, 29 insertions(+), 7 deletions(-) 15 | 16 | diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 17 | index c32562d..3de1dc2 100644 18 | --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 19 | +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 20 | @@ -108,7 +108,7 @@ public class BungeeCord extends ProxyServer 21 | * Localization bundle. 22 | */ 23 | public ResourceBundle bundle; 24 | - public EventLoopGroup eventLoops; 25 | + public EventLoopGroup bossEventLoopGroup, workerEventLoopGroup, queryEventLoopGroup; 26 | /** 27 | * locations.yml save thread. 28 | */ 29 | @@ -294,7 +294,8 @@ public class BungeeCord extends ProxyServer 30 | ResourceLeakDetector.setLevel( ResourceLeakDetector.Level.DISABLED ); // Eats performance 31 | } 32 | 33 | - eventLoops = PipelineUtils.newEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty IO Thread #%1$d" ).build() ); 34 | + bossEventLoopGroup = PipelineUtils.newEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty Boss IO Thread #%1$d" ).build() ); 35 | + workerEventLoopGroup = PipelineUtils.newEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty Worker IO Thread #%1$d" ).build() ); 36 | 37 | File moduleDirectory = new File( "modules" ); 38 | config.load(); 39 | @@ -356,12 +357,13 @@ public class BungeeCord extends ProxyServer 40 | .childOption( ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 1024 * 1024 ) 41 | .childAttr( PipelineUtils.LISTENER, info ) 42 | .childHandler( PipelineUtils.SERVER_CHILD ) 43 | - .group( eventLoops ) 44 | + .group( bossEventLoopGroup, workerEventLoopGroup ) 45 | .localAddress( info.getHost() ) 46 | .bind().addListener( listener ); 47 | 48 | if ( info.isQueryEnabled() ) 49 | { 50 | + queryEventLoopGroup = PipelineUtils.newEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty Query IO Thread #%1$d" ).build() ); 51 | ChannelFutureListener bindListener = future -> { 52 | if ( future.isSuccess() ) 53 | { 54 | @@ -372,7 +374,7 @@ public class BungeeCord extends ProxyServer 55 | getLogger().log( Level.WARNING, "Could not bind to host " + info.getHost(), future.cause() ); 56 | } 57 | }; 58 | - new RemoteQuery( this, info ).start( PipelineUtils.getDatagramChannel(), new InetSocketAddress( info.getHost().getAddress(), info.getQueryPort() ), eventLoops, bindListener ); 59 | + new RemoteQuery( this, info ).start( PipelineUtils.getDatagramChannel(), new InetSocketAddress( info.getHost().getAddress(), info.getQueryPort() ), queryEventLoopGroup, bindListener ); 60 | } 61 | } 62 | } 63 | @@ -428,13 +430,33 @@ public class BungeeCord extends ProxyServer 64 | } 65 | 66 | getLogger().info( "Closing IO threads" ); 67 | - eventLoops.shutdownGracefully(); 68 | + bossEventLoopGroup.shutdownGracefully(); 69 | + workerEventLoopGroup.shutdownGracefully(); 70 | + if (queryEventLoopGroup != null) 71 | + { 72 | + queryEventLoopGroup.shutdownGracefully(); 73 | + } 74 | + try 75 | + { 76 | + bossEventLoopGroup.awaitTermination( Long.MAX_VALUE, TimeUnit.NANOSECONDS ); 77 | + } catch ( InterruptedException ex ) 78 | + { 79 | + } 80 | try 81 | { 82 | - eventLoops.awaitTermination( Long.MAX_VALUE, TimeUnit.NANOSECONDS ); 83 | + workerEventLoopGroup.awaitTermination( Long.MAX_VALUE, TimeUnit.NANOSECONDS ); 84 | } catch ( InterruptedException ex ) 85 | { 86 | } 87 | + if (queryEventLoopGroup != null) 88 | + { 89 | + try 90 | + { 91 | + queryEventLoopGroup.awaitTermination( Long.MAX_VALUE, TimeUnit.NANOSECONDS ); 92 | + } catch ( InterruptedException ex ) 93 | + { 94 | + } 95 | + } 96 | 97 | if ( reconnectHandler != null ) 98 | { 99 | diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java 100 | index d6d38f8..d074b7e 100644 101 | --- a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java 102 | +++ b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java 103 | @@ -143,7 +143,7 @@ public class BungeeServerInfo implements ServerInfo 104 | }; 105 | new Bootstrap() 106 | .channel( PipelineUtils.getChannel() ) 107 | - .group( BungeeCord.getInstance().eventLoops ) 108 | + .group( BungeeCord.getInstance().workerEventLoopGroup ) 109 | .handler( PipelineUtils.BASE ) 110 | .option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable 111 | .remoteAddress( getAddress() ) 112 | -- 113 | 1.9.5.msysgit.0 114 | 115 | -------------------------------------------------------------------------------- /patches/0024-Do-not-attempt-to-download-module-on-custom-build-wi.patch: -------------------------------------------------------------------------------- 1 | From eca6b918a1c435569eb30fbfeb3b78b97db75644 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Wed, 2 Mar 2016 15:09:09 +0100 4 | Subject: Do not attempt to download module on custom build with default 5 | version number 6 | 7 | --- 8 | proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java | 6 ++++++ 9 | 1 file changed, 6 insertions(+) 10 | 11 | diff --git a/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java b/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java 12 | index 7a42094..f84298f 100644 13 | --- a/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java 14 | +++ b/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java 15 | @@ -57,6 +57,12 @@ public class ModuleManager 16 | return; 17 | } 18 | 19 | + if ( bungeeVersion.getBuild().equals("0") ) 20 | + { 21 | + proxy.getLogger().info( "Default build version '0' detected, custom FlexPipe build. Module download is skipped."); 22 | + return; 23 | + } 24 | + 25 | List modules = new ArrayList<>(); 26 | File configFile = new File( "modules.yml" ); 27 | // Start Yaml 28 | -- 29 | 1.9.5.msysgit.0 30 | 31 | -------------------------------------------------------------------------------- /patches/0025-Restore-1.8-like-ping-motd-description-output-for-co.patch: -------------------------------------------------------------------------------- 1 | From 1b9364c4728046cf78e458d75b71b25ded4660db Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Mon, 7 Mar 2016 13:33:26 +0100 4 | Subject: Restore 1.8-like ping motd description output for compability to 5 | various pinging tools. 6 | 7 | This commit also changes BungeeCord.getInstance().gson to have a getter with an optional ProtocolVersion argument. 8 | --- 9 | .../net/md_5/bungee/chat/LegacyMotdSerializer.java | 52 ++++++++++++++++++++++ 10 | .../md_5/bungee/protocol/ProtocolConstants.java | 1 + 11 | .../src/main/java/net/md_5/bungee/BungeeCord.java | 33 +++++++++++++- 12 | .../java/net/md_5/bungee/PlayerInfoSerializer.java | 18 +++++++- 13 | .../main/java/net/md_5/bungee/ServerConnector.java | 2 +- 14 | .../net/md_5/bungee/connection/InitialHandler.java | 5 ++- 15 | .../net/md_5/bungee/connection/PingHandler.java | 2 +- 16 | 7 files changed, 105 insertions(+), 8 deletions(-) 17 | create mode 100644 chat/src/main/java/net/md_5/bungee/chat/LegacyMotdSerializer.java 18 | 19 | diff --git a/chat/src/main/java/net/md_5/bungee/chat/LegacyMotdSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/LegacyMotdSerializer.java 20 | new file mode 100644 21 | index 0000000..3f0c64e 22 | --- /dev/null 23 | +++ b/chat/src/main/java/net/md_5/bungee/chat/LegacyMotdSerializer.java 24 | @@ -0,0 +1,52 @@ 25 | +package net.md_5.bungee.chat; 26 | + 27 | +import com.google.gson.JsonDeserializationContext; 28 | +import com.google.gson.JsonDeserializer; 29 | +import com.google.gson.JsonElement; 30 | +import com.google.gson.JsonParseException; 31 | +import com.google.gson.JsonPrimitive; 32 | +import com.google.gson.JsonSerializationContext; 33 | +import com.google.gson.JsonSerializer; 34 | +import java.lang.reflect.Type; 35 | +import net.md_5.bungee.api.ChatColor; 36 | +import net.md_5.bungee.api.chat.BaseComponent; 37 | +import net.md_5.bungee.api.chat.TextComponent; 38 | + 39 | + 40 | +/** 41 | + * This class converts TextComponents (with all its subcomponents) to legacy text and reverse. 42 | + * This is required for various pinging tools which can't properly handle json motds. It just 43 | + * needs to handle TextComponents due to everything is wrapped in a Textcomponent due to a 44 | + * minecraft client bug. 45 | + * 46 | + * This class should only be used for 1.7.x and 1.8.x clients pinging as for 1.9 the pinging 47 | + * tools need to update as vanilla minecraft servers output json motds. 48 | + */ 49 | +public class LegacyMotdSerializer implements JsonDeserializer, JsonSerializer 50 | +{ 51 | + @Override 52 | + public TextComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException 53 | + { 54 | + TextComponent component = new TextComponent( ComponentSerializer.parse( json.toString() ) ); 55 | + 56 | + //While this class restores function of various pinging tools, the default chat color turns to white for some 57 | + //reason. This fixes it by setting the default color to GRAY, which is nearly the default color, which is 58 | + //between GRAY and DARK_GRAY 59 | + if (component.getColorRaw() == null) 60 | + { 61 | + component.setColor( ChatColor.GRAY ); 62 | + } 63 | + return component; 64 | + } 65 | + 66 | + @Override 67 | + public JsonElement serialize(TextComponent src, Type typeOfSrc, JsonSerializationContext context) 68 | + { 69 | + if (src.getColorRaw() == null) 70 | + { 71 | + src.setColor( ChatColor.GRAY ); 72 | + } 73 | + 74 | + return new JsonPrimitive( BaseComponent.toLegacyText( src ) ); 75 | + } 76 | +} 77 | diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java b/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java 78 | index 8a99ff9..bb93f30 100644 79 | --- a/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java 80 | +++ b/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java 81 | @@ -6,6 +6,7 @@ import java.util.List; 82 | public class ProtocolConstants 83 | { 84 | 85 | + public static final int MINECRAFT_1_7_2 = 4; 86 | public static final int MINECRAFT_1_8 = 47; 87 | public static final int MINECRAFT_1_9 = 107; 88 | public static final int MINECRAFT_1_9_1 = 108; 89 | diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 90 | index 3de1dc2..4691434 100644 91 | --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 92 | +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 93 | @@ -19,6 +19,7 @@ import net.md_5.bungee.api.Favicon; 94 | import net.md_5.bungee.api.ServerPing; 95 | import net.md_5.bungee.api.Title; 96 | import net.md_5.bungee.api.chat.TranslatableComponent; 97 | +import net.md_5.bungee.chat.LegacyMotdSerializer; 98 | import net.md_5.bungee.chat.TextComponentSerializer; 99 | import net.md_5.bungee.chat.TranslatableComponentSerializer; 100 | import net.md_5.bungee.module.ModuleManager; 101 | @@ -145,12 +146,40 @@ public class BungeeCord extends ProxyServer 102 | private final ConsoleReader consoleReader; 103 | @Getter 104 | private final Logger logger; 105 | - public final Gson gson = new GsonBuilder() 106 | + private final Gson gson = new GsonBuilder() 107 | .registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ) 108 | .registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ) 109 | .registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ) 110 | - .registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer() ) 111 | + .registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer( ProtocolConstants.MINECRAFT_1_9 ) ) 112 | .registerTypeAdapter( Favicon.class, Favicon.getFaviconTypeAdapter() ).create(); 113 | + private final Gson gson1_7_2 = new GsonBuilder() 114 | + .registerTypeAdapter( BaseComponent.class, new LegacyMotdSerializer() ) 115 | + .registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer( ProtocolConstants.MINECRAFT_1_7_2 ) ) 116 | + .registerTypeAdapter( Favicon.class, Favicon.getFaviconTypeAdapter() ).create(); 117 | + private final Gson gson1_8 = new GsonBuilder() 118 | + .registerTypeAdapter( BaseComponent.class, new LegacyMotdSerializer() ) 119 | + .registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer( ProtocolConstants.MINECRAFT_1_8 ) ) 120 | + .registerTypeAdapter( Favicon.class, Favicon.getFaviconTypeAdapter() ).create(); 121 | + 122 | + public final Gson getLatestGson() 123 | + { 124 | + return gson; 125 | + } 126 | + 127 | + public final Gson getGson(int protocol) 128 | + { 129 | + if ( protocol <= ProtocolConstants.MINECRAFT_1_7_2 ) 130 | + { 131 | + return BungeeCord.getInstance().gson1_7_2; 132 | + } else if ( protocol <= ProtocolConstants.MINECRAFT_1_8 ) 133 | + { 134 | + return BungeeCord.getInstance().gson1_8; 135 | + } else 136 | + { 137 | + return BungeeCord.getInstance().gson; 138 | + } 139 | + } 140 | + 141 | @Getter 142 | private ConnectionThrottle connectionThrottle; 143 | private final ModuleManager moduleManager = new ModuleManager(); 144 | diff --git a/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java b/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java 145 | index dc49bb6..5dc78a8 100644 146 | --- a/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java 147 | +++ b/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java 148 | @@ -11,17 +11,25 @@ import net.md_5.bungee.api.ServerPing; 149 | 150 | import java.lang.reflect.Type; 151 | import java.util.UUID; 152 | +import net.md_5.bungee.protocol.ProtocolConstants; 153 | 154 | public class PlayerInfoSerializer implements JsonSerializer, JsonDeserializer 155 | { 156 | 157 | + private final int protocol; 158 | + 159 | + public PlayerInfoSerializer(int protocol) 160 | + { 161 | + this.protocol = protocol; 162 | + } 163 | + 164 | @Override 165 | public ServerPing.PlayerInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException 166 | { 167 | JsonObject js = json.getAsJsonObject(); 168 | ServerPing.PlayerInfo info = new ServerPing.PlayerInfo( js.get( "name" ).getAsString(), (UUID) null ); 169 | String id = js.get( "id" ).getAsString(); 170 | - if ( !id.contains( "-" ) ) 171 | + if ( protocol == ProtocolConstants.MINECRAFT_1_7_2 || !id.contains( "-" ) ) 172 | { 173 | info.setId( id ); 174 | } else 175 | @@ -36,7 +44,13 @@ public class PlayerInfoSerializer implements JsonSerializer 0 ) 197 | { 198 | - newHost += "\00" + BungeeCord.getInstance().gson.toJson( profile.getProperties() ); 199 | + newHost += "\00" + BungeeCord.getInstance().getLatestGson().toJson( profile.getProperties() ); 200 | } 201 | copiedHandshake.setHost( newHost ); 202 | } else if ( !user.getExtraDataInHandshake().isEmpty() ) 203 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 204 | index 7ed4d3a..3643bb9 100644 205 | --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 206 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 207 | @@ -225,7 +225,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection 208 | } 209 | 210 | Callback callback = (pingResult, error1) -> { 211 | - Gson gson = BungeeCord.getInstance().gson; 212 | + Gson gson = BungeeCord.getInstance().getGson( handshake.getProtocolVersion() ); 213 | + 214 | unsafe.sendPacket( new StatusResponse( gson.toJson( pingResult.getResponse() ) ) ); 215 | }; 216 | 217 | @@ -427,7 +428,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection 218 | Callback handler = (result, error) -> { 219 | if ( error == null ) 220 | { 221 | - LoginResult obj = BungeeCord.getInstance().gson.fromJson( result, LoginResult.class ); 222 | + LoginResult obj = BungeeCord.getInstance().getLatestGson().fromJson( result, LoginResult.class ); 223 | if ( obj != null ) 224 | { 225 | loginProfile = obj; 226 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java 227 | index 30fc756..56fded2 100644 228 | --- a/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java 229 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java 230 | @@ -53,7 +53,7 @@ public class PingHandler extends PacketHandler 231 | @SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR") 232 | public void handle(StatusResponse statusResponse) throws Exception 233 | { 234 | - Gson gson = BungeeCord.getInstance().gson; 235 | + Gson gson = BungeeCord.getInstance().getLatestGson(); 236 | callback.done( gson.fromJson( statusResponse.getResponse(), ServerPing.class ), null ); 237 | channel.close(); 238 | } 239 | -- 240 | 1.9.5.msysgit.0 241 | 242 | -------------------------------------------------------------------------------- /patches/0026-Require-compressed-packets-to-be-bigger-than-compres.patch: -------------------------------------------------------------------------------- 1 | From 8b862f17b2241837bb3141dc9fe533bc1714c7e6 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Fri, 18 Mar 2016 18:29:29 +0100 4 | Subject: Require compressed packets to be bigger than compression threshold 5 | (circumvents a dos attack partly) 6 | 7 | Fixes SpigotMC/BungeeCord#1762 8 | --- 9 | proxy/src/main/java/net/md_5/bungee/compress/PacketDecompressor.java | 5 +++++ 10 | proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java | 2 +- 11 | 2 files changed, 6 insertions(+), 1 deletion(-) 12 | 13 | diff --git a/proxy/src/main/java/net/md_5/bungee/compress/PacketDecompressor.java b/proxy/src/main/java/net/md_5/bungee/compress/PacketDecompressor.java 14 | index 21b3ea2..49c8d71 100644 15 | --- a/proxy/src/main/java/net/md_5/bungee/compress/PacketDecompressor.java 16 | +++ b/proxy/src/main/java/net/md_5/bungee/compress/PacketDecompressor.java 17 | @@ -5,14 +5,18 @@ import io.netty.buffer.ByteBuf; 18 | import io.netty.channel.ChannelHandlerContext; 19 | import io.netty.handler.codec.MessageToMessageDecoder; 20 | import java.util.List; 21 | +import lombok.RequiredArgsConstructor; 22 | import net.md_5.bungee.jni.zlib.BungeeZlib; 23 | import net.md_5.bungee.protocol.DefinedPacket; 24 | 25 | +@RequiredArgsConstructor 26 | public class PacketDecompressor extends MessageToMessageDecoder 27 | { 28 | 29 | private final BungeeZlib zlib = CompressFactory.zlib.newInstance(); 30 | 31 | + private final int compressionThreshold; 32 | + 33 | @Override 34 | public void handlerAdded(ChannelHandlerContext ctx) throws Exception 35 | { 36 | @@ -35,6 +39,7 @@ public class PacketDecompressor extends MessageToMessageDecoder 37 | in.skipBytes( in.readableBytes() ); 38 | } else 39 | { 40 | + Preconditions.checkState( size >= compressionThreshold, "Decompressed size %s less than compression threshold %s", size, compressionThreshold ); 41 | ByteBuf decompressed = ctx.alloc().directBuffer(); 42 | 43 | try 44 | diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java 45 | index 4514a21..9481678 100644 46 | --- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java 47 | +++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java 48 | @@ -103,7 +103,7 @@ public class ChannelWrapper 49 | 50 | if ( ch.pipeline().get( PacketDecompressor.class ) == null && compressionThreshold != -1 ) 51 | { 52 | - addBefore( PipelineUtils.PACKET_DECODER, "decompress", new PacketDecompressor() ); 53 | + addBefore( PipelineUtils.PACKET_DECODER, "decompress", new PacketDecompressor(compressionThreshold) ); 54 | } 55 | if ( compressionThreshold == -1 ) 56 | { 57 | -- 58 | 1.9.5.msysgit.0 59 | 60 | -------------------------------------------------------------------------------- /patches/0027-Improve-performance-of-message-translations.patch: -------------------------------------------------------------------------------- 1 | From a3c7723076e0471f306d5646303e4413c69c463a Mon Sep 17 00:00:00 2001 2 | From: Mystiflow 3 | Date: Sat, 19 Mar 2016 16:44:10 +0100 4 | Subject: Improve performance of message translations 5 | 6 | MessageFormat#format will look to replace the pattern with the arguments provided regardless of array length, however a lot of the translations in BungeeCord do not require an argument. 7 | By skipping replacement if there are no arguments we can reduce object creation and remove unnecessary logic. 8 | 9 | Adapted from WaterfallMC/Waterfall#39 with a micro-optimization 10 | 11 | Signed-off-by: Janmm14 12 | --- 13 | proxy/src/main/java/net/md_5/bungee/BungeeCord.java | 7 ++++--- 14 | 1 file changed, 4 insertions(+), 3 deletions(-) 15 | 16 | diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 17 | index 4691434..08f9614 100644 18 | --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 19 | +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 20 | @@ -561,14 +561,15 @@ public class BungeeCord extends ProxyServer 21 | @Override 22 | public String getTranslation(String name, Object... args) 23 | { 24 | - String translation = ""; 25 | try 26 | { 27 | - translation = MessageFormat.format( bundle.getString( name ), args ); 28 | + String translationPattern = bundle.getString( name ); 29 | + 30 | + return args.length == 0 ? translationPattern : MessageFormat.format( translationPattern, args ); 31 | } catch ( MissingResourceException ex ) 32 | { 33 | + return ""; 34 | } 35 | - return translation; 36 | } 37 | 38 | @Override 39 | -- 40 | 1.9.5.msysgit.0 41 | 42 | -------------------------------------------------------------------------------- /patches/0028-Stricter-chat-validation.patch: -------------------------------------------------------------------------------- 1 | From 6a378babfe4be92c056f3d40b16677966eaaaea2 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Mon, 21 Mar 2016 03:25:13 +0100 4 | Subject: Stricter chat validation. 5 | 6 | Allow chatting only after user has actually joined a server. Forbid empty or whitespace messages. These are not sent by the vanilla minecraft client. 7 | 8 | This prevents some joinbot attacks where bots start chatting before they actually joined the target server (real minecraft client would be at some login screen). As the vanilla minecraft client does not send empty or whitespace chat messages, but some bot tools do, this further prevents attacks from badly configured bots. 9 | --- 10 | .../net/md_5/bungee/connection/InitialHandler.java | 11 +++++++++++ 11 | .../net/md_5/bungee/connection/UpstreamBridge.java | 21 +++++++++++++++------ 12 | 2 files changed, 26 insertions(+), 6 deletions(-) 13 | 14 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 15 | index 3643bb9..1c8689b 100644 16 | --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 17 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 18 | @@ -20,6 +20,7 @@ import net.md_5.bungee.api.ChatColor; 19 | import net.md_5.bungee.api.Favicon; 20 | import net.md_5.bungee.api.ServerPing; 21 | import net.md_5.bungee.api.chat.BaseComponent; 22 | +import net.md_5.bungee.api.chat.ComponentBuilder; 23 | import net.md_5.bungee.api.chat.TextComponent; 24 | import net.md_5.bungee.api.config.ListenerInfo; 25 | import net.md_5.bungee.api.config.ServerInfo; 26 | @@ -39,6 +40,7 @@ import net.md_5.bungee.netty.cipher.CipherEncoder; 27 | import net.md_5.bungee.protocol.DefinedPacket; 28 | import net.md_5.bungee.protocol.PacketWrapper; 29 | import net.md_5.bungee.protocol.ProtocolConstants; 30 | +import net.md_5.bungee.protocol.packet.Chat; 31 | import net.md_5.bungee.protocol.packet.Handshake; 32 | import net.md_5.bungee.protocol.packet.PluginMessage; 33 | import net.md_5.bungee.protocol.packet.EncryptionResponse; 34 | @@ -624,4 +626,13 @@ public class InitialHandler extends PacketHandler implements PendingConnection 35 | { 36 | return !ch.isClosed(); 37 | } 38 | + 39 | + private static final BaseComponent[] CHAT_TOO_EARLY_DISCONNECT_REASON = new ComponentBuilder( "You may not chat right now." ).color( ChatColor.RED ).create(); 40 | + 41 | + @Override 42 | + public void handle(Chat chat) throws Exception 43 | + { 44 | + // I don't know whether anyone is so dump to send chat messages that early and whether it gets stopped earlier, but this code does not hurt 45 | + disconnect( CHAT_TOO_EARLY_DISCONNECT_REASON ); 46 | + } 47 | } 48 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 49 | index 9ece928..9bbb4cc 100644 50 | --- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 51 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 52 | @@ -2,9 +2,13 @@ package net.md_5.bungee.connection; 53 | 54 | import com.google.common.base.Preconditions; 55 | import net.md_5.bungee.BungeeCord; 56 | +import net.md_5.bungee.ServerConnection; 57 | import net.md_5.bungee.UserConnection; 58 | import net.md_5.bungee.Util; 59 | +import net.md_5.bungee.api.ChatColor; 60 | import net.md_5.bungee.api.ProxyServer; 61 | +import net.md_5.bungee.api.chat.BaseComponent; 62 | +import net.md_5.bungee.api.chat.ComponentBuilder; 63 | import net.md_5.bungee.api.connection.ProxiedPlayer; 64 | import net.md_5.bungee.api.event.ChatEvent; 65 | import net.md_5.bungee.api.event.PlayerDisconnectEvent; 66 | @@ -82,12 +86,6 @@ public class UpstreamBridge extends PacketHandler 67 | } 68 | 69 | @Override 70 | - public boolean shouldHandle(PacketWrapper packet) throws Exception 71 | - { 72 | - return con.getServer() != null || packet.packet instanceof PluginMessage; 73 | - } 74 | - 75 | - @Override 76 | public void handle(PacketWrapper packet) throws Exception 77 | { 78 | if ( con.getServer() != null ) 79 | @@ -108,11 +106,22 @@ public class UpstreamBridge extends PacketHandler 80 | } 81 | } 82 | 83 | + private static final BaseComponent[] CHAT_TOO_EARLY_DISCONNECT_REASON = new ComponentBuilder( "You may not chat right now." ).color( ChatColor.RED ).create(); 84 | + 85 | @Override 86 | public void handle(Chat chat) throws Exception 87 | { 88 | + Preconditions.checkArgument( !chat.getMessage().trim().isEmpty(), "Chat message is invalid" ); // Yes, I'm sorry but these are actually sent out by some bot tools 89 | Preconditions.checkArgument( chat.getMessage().length() <= 100, "Chat message too long" ); // Mojang limit, check on updates 90 | 91 | + // Users can't chat if they were never on a server yet after joining 92 | + // Users also would get kicked if the command is not handled by the bungee with an npe, but this stops some unneccessary cpu cycles 93 | + if(con.getServer() == null) 94 | + { 95 | + con.disconnect( CHAT_TOO_EARLY_DISCONNECT_REASON ); 96 | + return; 97 | + } 98 | + 99 | ChatEvent chatEvent = new ChatEvent( con, con.getServer(), chat.getMessage() ); 100 | if ( !bungee.getPluginManager().callEvent( chatEvent ).isCancelled() ) 101 | { 102 | -- 103 | 1.9.5.msysgit.0 104 | 105 | -------------------------------------------------------------------------------- /patches/0029-Use-String.regionMatches-.-instead-of-.toLowerCase-..patch: -------------------------------------------------------------------------------- 1 | From 2b0c57b2585f84f9829ab467df039352f914bc30 Mon Sep 17 00:00:00 2001 2 | From: Tux 3 | Date: Mon, 21 Mar 2016 22:29:05 +0100 4 | Subject: Use String.regionMatches(...) instead of 5 | .toLowerCase().startWith(...) 6 | 7 | Based on https://github.com/SpigotMC/BungeeCord/commit/c6936c19f6f89c7f5d8fb579b181fc700a15e172 by @minecrafter 8 | --- 9 | .../md_5/bungee/module/cmd/server/CommandServer.java | 18 +++++++++++++++--- 10 | proxy/src/main/java/net/md_5/bungee/BungeeCord.java | 6 ++++-- 11 | 2 files changed, 19 insertions(+), 5 deletions(-) 12 | 13 | diff --git a/module/cmd-server/src/main/java/net/md_5/bungee/module/cmd/server/CommandServer.java b/module/cmd-server/src/main/java/net/md_5/bungee/module/cmd/server/CommandServer.java 14 | index f93ae19..9d5c938 100644 15 | --- a/module/cmd-server/src/main/java/net/md_5/bungee/module/cmd/server/CommandServer.java 16 | +++ b/module/cmd-server/src/main/java/net/md_5/bungee/module/cmd/server/CommandServer.java 17 | @@ -6,6 +6,7 @@ import com.google.common.collect.Iterables; 18 | import java.util.Collections; 19 | import java.util.Map; 20 | import java.util.stream.Collectors; 21 | +import java.util.stream.Stream; 22 | import net.md_5.bungee.api.CommandSender; 23 | import net.md_5.bungee.api.ProxyServer; 24 | import net.md_5.bungee.api.config.ServerInfo; 25 | @@ -76,13 +77,24 @@ public class CommandServer extends Command implements TabExecutor 26 | } 27 | } 28 | 29 | + @SuppressWarnings("unchecked") 30 | @Override 31 | public Iterable onTabComplete(final CommandSender sender, final String[] args) 32 | { 33 | - final String lower = ( args.length == 0 ) ? "" : args[0].toLowerCase(); 34 | + if ( args.length > 1 ) 35 | + { 36 | + return Collections.EMPTY_LIST; 37 | + } 38 | + 39 | + Stream stream = ProxyServer.getInstance().getServers().values().stream(); 40 | 41 | - return ( args.length > 1 ) ? Collections.EMPTY_LIST : ProxyServer.getInstance().getServers().values().stream() 42 | - .filter( input -> input.getName().toLowerCase().startsWith( lower ) && input.canAccess( sender ) ) 43 | + if (args.length == 0 ) 44 | + { 45 | + stream = stream.filter( input -> input.canAccess( sender ) ); 46 | + } else { 47 | + stream = stream.filter( input -> input.getName().regionMatches( true, 0, args[ 0 ], 0, args[ 0 ].length() ) && input.canAccess( sender ) ); 48 | + } 49 | + return stream 50 | .map( ServerInfo::getName ) 51 | .collect( Collectors.toList() ); 52 | } 53 | diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 54 | index 08f9614..a135964 100644 55 | --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 56 | +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 57 | @@ -63,6 +63,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; 58 | import java.util.logging.Handler; 59 | import java.util.logging.Level; 60 | import java.util.logging.Logger; 61 | +import java.util.stream.Collectors; 62 | + 63 | import jline.console.ConsoleReader; 64 | import lombok.Getter; 65 | import lombok.Setter; 66 | @@ -768,8 +770,8 @@ public class BungeeCord extends ProxyServer 67 | } 68 | 69 | return getPlayers().stream() 70 | - .filter( input -> input != null && input.getName().toLowerCase().startsWith( partialName.toLowerCase() ) ) 71 | - .collect( Collectors.toSet() ); 72 | + .filter(p -> p.getName().regionMatches(true, 0, partialName, 0, partialName.length())) 73 | + .collect(Collectors.toSet()); 74 | } 75 | 76 | @Override 77 | -- 78 | 1.9.5.msysgit.0 79 | 80 | -------------------------------------------------------------------------------- /patches/0030-Use-ASM-or-MethodHandles-for-event-execution.patch: -------------------------------------------------------------------------------- 1 | From 6486bf462ee2e0d79c3976a0313608a47c741586 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Tue, 22 Mar 2016 00:11:46 +0100 4 | Subject: Use ASM or MethodHandles for event execution 5 | 6 | Nearly the same as if you applied https://github.com/WaterfallMC/Waterfall/commit/1692934370735165f128fa8ece9a24fff5211094 , https://github.com/WaterfallMC/Waterfall/commit/88eb9065f95b1ae95bbab381dcc5907a1795d265 , https://github.com/WaterfallMC/Waterfall/commit/a0df6a2e737f6db21780fc461e3cf360ae518688 and https://github.com/WaterfallMC/Waterfall/commit/500ab62afa82653fa0c8c605a82ea7be31d63637 by @Techcable and https://github.com/WaterfallMC/Waterfall/commit/52fbf2ff34ffc481a8c0400a37f37042c8ceafb9 by @minecrafter 7 | --- 8 | event/pom.xml | 8 +++ 9 | .../main/java/net/md_5/bungee/event/EventBus.java | 7 +- 10 | .../java/net/md_5/bungee/event/EventExecutor.java | 51 ++++++++++++++ 11 | .../net/md_5/bungee/event/EventHandlerMethod.java | 11 +++- 12 | .../bungee/event/MethodHandleEventExecutor.java | 38 +++++++++++ 13 | .../event/StaticMethodHandleEventExecutor.java | 41 ++++++++++++ 14 | .../event/asm/ASMEventExecutorGenerator.java | 51 ++++++++++++++ 15 | .../net/md_5/bungee/event/asm/ClassDefiner.java | 38 +++++++++++ 16 | .../md_5/bungee/event/asm/SafeClassDefiner.java | 77 ++++++++++++++++++++++ 17 | 9 files changed, 314 insertions(+), 8 deletions(-) 18 | create mode 100644 event/src/main/java/net/md_5/bungee/event/EventExecutor.java 19 | create mode 100644 event/src/main/java/net/md_5/bungee/event/MethodHandleEventExecutor.java 20 | create mode 100644 event/src/main/java/net/md_5/bungee/event/StaticMethodHandleEventExecutor.java 21 | create mode 100644 event/src/main/java/net/md_5/bungee/event/asm/ASMEventExecutorGenerator.java 22 | create mode 100644 event/src/main/java/net/md_5/bungee/event/asm/ClassDefiner.java 23 | create mode 100644 event/src/main/java/net/md_5/bungee/event/asm/SafeClassDefiner.java 24 | 25 | diff --git a/event/pom.xml b/event/pom.xml 26 | index c0be4ac..fa9c5ed 100644 27 | --- a/event/pom.xml 28 | +++ b/event/pom.xml 29 | @@ -15,4 +15,12 @@ 30 | 31 | FlexPipe-Event 32 | Generic java event dispatching API intended for use with FlexPipe 33 | + 34 | + 35 | + 36 | + org.ow2.asm 37 | + asm-all 38 | + 5.0.4 39 | + 40 | + 41 | 42 | diff --git a/event/src/main/java/net/md_5/bungee/event/EventBus.java b/event/src/main/java/net/md_5/bungee/event/EventBus.java 43 | index 5b5d420..8f0aa96 100644 44 | --- a/event/src/main/java/net/md_5/bungee/event/EventBus.java 45 | +++ b/event/src/main/java/net/md_5/bungee/event/EventBus.java 46 | @@ -44,13 +44,10 @@ public class EventBus 47 | try 48 | { 49 | method.invoke( event ); 50 | - } catch ( IllegalAccessException ex ) 51 | - { 52 | - throw new Error( "Method became inaccessible: " + event, ex ); 53 | - } catch ( IllegalArgumentException ex ) 54 | + } catch ( ClassCastException ex ) 55 | { 56 | throw new Error( "Method rejected target/argument: " + event, ex ); 57 | - } catch ( InvocationTargetException ex ) 58 | + } catch ( Throwable ex ) 59 | { 60 | logger.log( Level.WARNING, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() ); 61 | } 62 | diff --git a/event/src/main/java/net/md_5/bungee/event/EventExecutor.java b/event/src/main/java/net/md_5/bungee/event/EventExecutor.java 63 | new file mode 100644 64 | index 0000000..3af2fa3 65 | --- /dev/null 66 | +++ b/event/src/main/java/net/md_5/bungee/event/EventExecutor.java 67 | @@ -0,0 +1,51 @@ 68 | +package net.md_5.bungee.event; 69 | + 70 | +import java.lang.reflect.Method; 71 | +import java.lang.reflect.Modifier; 72 | + 73 | +import com.google.common.base.Preconditions; 74 | + 75 | +import net.md_5.bungee.event.asm.ASMEventExecutorGenerator; 76 | +import net.md_5.bungee.event.asm.ClassDefiner; 77 | + 78 | +import org.objectweb.asm.Type; 79 | + 80 | +public interface EventExecutor 81 | +{ 82 | + 83 | + /** 84 | + * Invoke the method on the listener with the given event as an argument 85 | + * 86 | + * @param listener the listener to invoke 87 | + * @param event the event 88 | + * @throws ClassCastException if the listener or event is not of an appropriate type 89 | + * @throws Throwable any throwable thrown by the underlying method 90 | + */ 91 | + public void invoke(Object listener, Object event) throws Throwable; 92 | + 93 | + public static EventExecutor create(Method m) 94 | + { 95 | + Preconditions.checkNotNull( m, "Null method" ); 96 | + Preconditions.checkArgument( m.getParameterCount() != 0, "Incorrect number of arguments %s", m.getParameterCount() ); 97 | + ClassDefiner definer = ClassDefiner.getInstance(); 98 | + if ( Modifier.isStatic( m.getModifiers() ) ) 99 | + { 100 | + return new StaticMethodHandleEventExecutor( m ); 101 | + } else if ( definer.isBypassAccessChecks() || ( Modifier.isPublic( m.getDeclaringClass().getModifiers() ) && Modifier.isPublic( m.getModifiers() ) ) ) 102 | + { 103 | + String name = ASMEventExecutorGenerator.generateName(); 104 | + byte[] classData = ASMEventExecutorGenerator.generateEventExecutor( m, name ); 105 | + Class c = definer.defineClass( m.getDeclaringClass().getClassLoader(), Type.getObjectType( name ), classData ).asSubclass( EventExecutor.class ); 106 | + try 107 | + { 108 | + return c.newInstance(); 109 | + } catch ( InstantiationException | IllegalAccessException e ) 110 | + { 111 | + throw new AssertionError( "Unable to initialize generated event executor", e ); 112 | + } 113 | + } else 114 | + { 115 | + return new MethodHandleEventExecutor( m ); 116 | + } 117 | + } 118 | +} 119 | diff --git a/event/src/main/java/net/md_5/bungee/event/EventHandlerMethod.java b/event/src/main/java/net/md_5/bungee/event/EventHandlerMethod.java 120 | index ad19c02..226461e 100644 121 | --- a/event/src/main/java/net/md_5/bungee/event/EventHandlerMethod.java 122 | +++ b/event/src/main/java/net/md_5/bungee/event/EventHandlerMethod.java 123 | @@ -12,10 +12,15 @@ public class EventHandlerMethod 124 | @Getter 125 | private final Object listener; 126 | @Getter 127 | - private final Method method; 128 | + private final EventExecutor executor; 129 | 130 | - public void invoke(Object event) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException 131 | + public EventHandlerMethod(Object listener, Method m) 132 | { 133 | - method.invoke( listener, event ); 134 | + this( listener, EventExecutor.create( m ) ); 135 | + } 136 | + 137 | + public void invoke(Object event) throws Throwable 138 | + { 139 | + executor.invoke( listener, event ); 140 | } 141 | } 142 | diff --git a/event/src/main/java/net/md_5/bungee/event/MethodHandleEventExecutor.java b/event/src/main/java/net/md_5/bungee/event/MethodHandleEventExecutor.java 143 | new file mode 100644 144 | index 0000000..95f4937 145 | --- /dev/null 146 | +++ b/event/src/main/java/net/md_5/bungee/event/MethodHandleEventExecutor.java 147 | @@ -0,0 +1,38 @@ 148 | +package net.md_5.bungee.event; 149 | + 150 | +import lombok.RequiredArgsConstructor; 151 | + 152 | +import java.lang.invoke.MethodHandle; 153 | +import java.lang.invoke.MethodHandles; 154 | +import java.lang.invoke.WrongMethodTypeException; 155 | +import java.lang.reflect.Method; 156 | + 157 | +@RequiredArgsConstructor 158 | +public class MethodHandleEventExecutor implements EventExecutor 159 | +{ 160 | + private final MethodHandle handle; 161 | + 162 | + public MethodHandleEventExecutor(Method method) 163 | + { 164 | + try 165 | + { 166 | + method.setAccessible( true ); 167 | + this.handle = MethodHandles.lookup().unreflect( method ); 168 | + } catch ( IllegalAccessException e ) 169 | + { 170 | + throw new AssertionError( e ); 171 | + } 172 | + } 173 | + 174 | + @Override 175 | + public void invoke(Object listener, Object event) throws Throwable 176 | + { 177 | + try 178 | + { 179 | + handle.invoke( listener, event ); 180 | + } catch ( ClassCastException | WrongMethodTypeException e ) 181 | + { 182 | + throw new IllegalArgumentException( e.getMessage() ); 183 | + } 184 | + } 185 | +} 186 | diff --git a/event/src/main/java/net/md_5/bungee/event/StaticMethodHandleEventExecutor.java b/event/src/main/java/net/md_5/bungee/event/StaticMethodHandleEventExecutor.java 187 | new file mode 100644 188 | index 0000000..dd20542 189 | --- /dev/null 190 | +++ b/event/src/main/java/net/md_5/bungee/event/StaticMethodHandleEventExecutor.java 191 | @@ -0,0 +1,41 @@ 192 | +package net.md_5.bungee.event; 193 | + 194 | +import java.lang.invoke.MethodHandle; 195 | +import java.lang.invoke.MethodHandles; 196 | +import java.lang.invoke.WrongMethodTypeException; 197 | +import java.lang.reflect.Method; 198 | +import java.lang.reflect.Modifier; 199 | + 200 | +import com.google.common.base.Preconditions; 201 | +import lombok.RequiredArgsConstructor; 202 | + 203 | +@RequiredArgsConstructor 204 | +public class StaticMethodHandleEventExecutor implements EventExecutor 205 | +{ 206 | + private final MethodHandle handle; 207 | + 208 | + public StaticMethodHandleEventExecutor(Method m) 209 | + { 210 | + Preconditions.checkArgument( Modifier.isStatic( m.getModifiers() ), "Not a static method: %s", m ); 211 | + try 212 | + { 213 | + m.setAccessible( true ); 214 | + this.handle = MethodHandles.lookup().unreflect( m ); 215 | + } catch ( IllegalAccessException e ) 216 | + { 217 | + throw new AssertionError( e ); 218 | + } 219 | + } 220 | + 221 | + @Override 222 | + public void invoke(Object listener, Object event) throws Throwable 223 | + { 224 | + try 225 | + { 226 | + handle.invoke( event ); 227 | + } catch ( ClassCastException | WrongMethodTypeException e ) 228 | + { 229 | + throw new IllegalArgumentException( e.getMessage() ); 230 | + } 231 | + } 232 | +} 233 | diff --git a/event/src/main/java/net/md_5/bungee/event/asm/ASMEventExecutorGenerator.java b/event/src/main/java/net/md_5/bungee/event/asm/ASMEventExecutorGenerator.java 234 | new file mode 100644 235 | index 0000000..fca42d3 236 | --- /dev/null 237 | +++ b/event/src/main/java/net/md_5/bungee/event/asm/ASMEventExecutorGenerator.java 238 | @@ -0,0 +1,51 @@ 239 | +package net.md_5.bungee.event.asm; 240 | + 241 | +import java.lang.reflect.Method; 242 | +import java.util.concurrent.atomic.AtomicInteger; 243 | + 244 | +import net.md_5.bungee.event.EventExecutor; 245 | + 246 | +import org.objectweb.asm.ClassWriter; 247 | +import org.objectweb.asm.Type; 248 | +import org.objectweb.asm.commons.GeneratorAdapter; 249 | + 250 | +import static org.objectweb.asm.Opcodes.*; 251 | + 252 | +public class ASMEventExecutorGenerator 253 | +{ 254 | + public static byte[] generateEventExecutor(Method m, String name) 255 | + { 256 | + ClassWriter writer = new ClassWriter( ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS ); 257 | + writer.visit( V1_8, ACC_PUBLIC, name, null, Type.getInternalName( Object.class ), new String[]{ Type.getInternalName( EventExecutor.class ) } ); 258 | + // Generate constructor 259 | + GeneratorAdapter methodGenerator = new GeneratorAdapter( writer.visitMethod( ACC_PUBLIC, "", "()V", null, null ), ACC_PUBLIC, "", "()V" ); 260 | + methodGenerator.loadThis(); 261 | + methodGenerator.visitMethodInsn( INVOKESPECIAL, Type.getInternalName( Object.class ), "", "()V", false ); // Invoke the super class (Object) constructor 262 | + methodGenerator.returnValue(); 263 | + methodGenerator.endMethod(); 264 | + // Generate the execute method 265 | + methodGenerator = new GeneratorAdapter( writer.visitMethod( ACC_PUBLIC, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null ), ACC_PUBLIC, "", "(Ljava/lang/Object;Ljava/lang/Object;)V" ); 266 | + ; 267 | + methodGenerator.loadArg( 0 ); 268 | + methodGenerator.checkCast( Type.getType( m.getDeclaringClass() ) ); 269 | + methodGenerator.loadArg( 1 ); 270 | + methodGenerator.checkCast( Type.getType( m.getParameterTypes()[ 0 ] ) ); 271 | + methodGenerator.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName( m.getDeclaringClass() ), m.getName(), Type.getMethodDescriptor( m ), m.getDeclaringClass().isInterface() ); 272 | + if ( m.getReturnType() != void.class ) 273 | + { 274 | + methodGenerator.pop(); 275 | + } 276 | + methodGenerator.returnValue(); 277 | + methodGenerator.endMethod(); 278 | + writer.visitEnd(); 279 | + return writer.toByteArray(); 280 | + } 281 | + 282 | + private static final AtomicInteger NEXT_ID = new AtomicInteger( 1 ); 283 | + 284 | + public static String generateName() 285 | + { 286 | + int id = NEXT_ID.getAndIncrement(); 287 | + return "net/md_5/bungee/event/asm/generated/GeneratedEventExecutor" + id; 288 | + } 289 | +} 290 | diff --git a/event/src/main/java/net/md_5/bungee/event/asm/ClassDefiner.java b/event/src/main/java/net/md_5/bungee/event/asm/ClassDefiner.java 291 | new file mode 100644 292 | index 0000000..2315f26 293 | --- /dev/null 294 | +++ b/event/src/main/java/net/md_5/bungee/event/asm/ClassDefiner.java 295 | @@ -0,0 +1,38 @@ 296 | +package net.md_5.bungee.event.asm; 297 | + 298 | + 299 | +import java.util.concurrent.atomic.AtomicInteger; 300 | + 301 | +import org.objectweb.asm.ClassWriter; 302 | +import org.objectweb.asm.Type; 303 | + 304 | +public interface ClassDefiner 305 | +{ 306 | + 307 | + /** 308 | + * Returns if the defined classes can bypass access checks 309 | + * 310 | + * @return if classes bypass access checks 311 | + */ 312 | + default boolean isBypassAccessChecks() 313 | + { 314 | + return false; 315 | + } 316 | + 317 | + /** 318 | + * Define a class 319 | + * 320 | + * @param parentLoader the parent classloader 321 | + * @param name the name of the class 322 | + * @param data the class data to load 323 | + * @return the defined class 324 | + * @throws ClassFormatError if the class data is invalid 325 | + * @throws NullPointerException if any of the arguments are null 326 | + */ 327 | + Class defineClass(ClassLoader parentLoader, Type name, byte[] data); 328 | + 329 | + static ClassDefiner getInstance() 330 | + { 331 | + return SafeClassDefiner.INSTANCE; 332 | + } 333 | +} 334 | diff --git a/event/src/main/java/net/md_5/bungee/event/asm/SafeClassDefiner.java b/event/src/main/java/net/md_5/bungee/event/asm/SafeClassDefiner.java 335 | new file mode 100644 336 | index 0000000..a98b77b 337 | --- /dev/null 338 | +++ b/event/src/main/java/net/md_5/bungee/event/asm/SafeClassDefiner.java 339 | @@ -0,0 +1,77 @@ 340 | +package net.md_5.bungee.event.asm; 341 | + 342 | +import lombok.*; 343 | + 344 | +import java.util.concurrent.ConcurrentHashMap; 345 | +import java.util.concurrent.ConcurrentMap; 346 | + 347 | +import com.google.common.base.Preconditions; 348 | + 349 | +import org.objectweb.asm.Type; 350 | + 351 | +@NoArgsConstructor(access = AccessLevel.PACKAGE) 352 | +public class SafeClassDefiner implements ClassDefiner 353 | +{ 354 | + static final SafeClassDefiner INSTANCE = new SafeClassDefiner(); 355 | + 356 | + private final ConcurrentMap loaders = new ConcurrentHashMap<>(); 357 | + 358 | + @Override 359 | + public Class defineClass(ClassLoader parentLoader, Type type, byte[] data) 360 | + { 361 | + GeneratedClassLoader loader = loaders.computeIfAbsent( parentLoader, GeneratedClassLoader::new ); 362 | + String name = type.getClassName(); 363 | + synchronized ( loader.getClassLoadingLock( name ) ) 364 | + { 365 | + Preconditions.checkState( !loader.hasClass( name ), "%s already defined", name ); 366 | + Class c = loader.define( name, data ); 367 | + assert c.getName().equals( name ); 368 | + return c; 369 | + } 370 | + } 371 | + 372 | + private static class GeneratedClassLoader extends ClassLoader 373 | + { 374 | + static 375 | + { 376 | + ClassLoader.registerAsParallelCapable(); 377 | + } 378 | + 379 | + protected GeneratedClassLoader(ClassLoader parent) 380 | + { 381 | + super( parent ); 382 | + } 383 | + 384 | + private Class define(String name, byte[] data) 385 | + { 386 | + synchronized ( getClassLoadingLock( name ) ) 387 | + { 388 | + assert !hasClass( name ); 389 | + Class c = defineClass( name, data, 0, data.length ); 390 | + resolveClass( c ); 391 | + return c; 392 | + } 393 | + } 394 | + 395 | + @Override 396 | + public Object getClassLoadingLock(String name) 397 | + { 398 | + return super.getClassLoadingLock( name ); 399 | + } 400 | + 401 | + public boolean hasClass(String name) 402 | + { 403 | + synchronized ( getClassLoadingLock( name ) ) 404 | + { 405 | + try 406 | + { 407 | + Class.forName( name ); 408 | + return true; 409 | + } catch ( ClassNotFoundException e ) 410 | + { 411 | + return false; 412 | + } 413 | + } 414 | + } 415 | + } 416 | +} 417 | -- 418 | 1.9.5.msysgit.0 419 | 420 | -------------------------------------------------------------------------------- /patches/0031-Add-SuspiciousPlayerBehaviourEvent.patch: -------------------------------------------------------------------------------- 1 | From e27b910cb81f84d6baa74fae8c487998418323ce Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Thu, 24 Mar 2016 14:29:14 +0100 4 | Subject: Add SuspiciousPlayerBehaviourEvent 5 | 6 | The event gets called after the connection was closed and reports certain actions to plugins so they can primarily identify bot joins. 7 | --- 8 | .../api/event/SuspiciousPlayerBehaviourEvent.java | 36 ++++++++++++++++++++++ 9 | .../net/md_5/bungee/connection/InitialHandler.java | 7 +++-- 10 | .../net/md_5/bungee/connection/UpstreamBridge.java | 22 ++++++++++--- 11 | 3 files changed, 59 insertions(+), 6 deletions(-) 12 | create mode 100644 api/src/main/java/me/minotopia/flexpipe/api/event/SuspiciousPlayerBehaviourEvent.java 13 | 14 | diff --git a/api/src/main/java/me/minotopia/flexpipe/api/event/SuspiciousPlayerBehaviourEvent.java b/api/src/main/java/me/minotopia/flexpipe/api/event/SuspiciousPlayerBehaviourEvent.java 15 | new file mode 100644 16 | index 0000000..39dfb24 17 | --- /dev/null 18 | +++ b/api/src/main/java/me/minotopia/flexpipe/api/event/SuspiciousPlayerBehaviourEvent.java 19 | @@ -0,0 +1,36 @@ 20 | +package me.minotopia.flexpipe.api.event; 21 | + 22 | +import lombok.EqualsAndHashCode; 23 | +import lombok.Getter; 24 | +import lombok.RequiredArgsConstructor; 25 | +import lombok.ToString; 26 | + 27 | +import net.md_5.bungee.api.connection.Connection; 28 | +import net.md_5.bungee.api.connection.PendingConnection; 29 | +import net.md_5.bungee.api.connection.ProxiedPlayer; 30 | +import net.md_5.bungee.api.plugin.Event; 31 | + 32 | +@Getter 33 | +@ToString(callSuper = false) 34 | +@EqualsAndHashCode(callSuper = false) 35 | +@RequiredArgsConstructor 36 | +public class SuspiciousPlayerBehaviourEvent extends Event 37 | +{ 38 | + private final Connection connection; 39 | + private final Check checkFailed; 40 | + 41 | + @Getter 42 | + @RequiredArgsConstructor 43 | + public enum Check 44 | + { 45 | + CHAT_TOO_EARLY( Connection.class ), 46 | + CHAT_EMPTY( ProxiedPlayer.class ), 47 | + CHAT_TOO_LONG( ProxiedPlayer.class ), 48 | + JOIN_THROTTLE_TRIGGERED( PendingConnection.class ); 49 | + 50 | + /** 51 | + * The class you can safely cast the connection to without any instanceof check. 52 | + */ 53 | + private final Class connectionClass; 54 | + } 55 | +} 56 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 57 | index 1c8689b..4e5553d 100644 58 | --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 59 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 60 | @@ -1,5 +1,7 @@ 61 | package net.md_5.bungee.connection; 62 | 63 | +import me.minotopia.flexpipe.api.event.SuspiciousPlayerBehaviourEvent; 64 | + 65 | import com.google.common.base.Charsets; 66 | import com.google.common.base.Preconditions; 67 | import java.math.BigInteger; 68 | @@ -20,7 +22,6 @@ import net.md_5.bungee.api.ChatColor; 69 | import net.md_5.bungee.api.Favicon; 70 | import net.md_5.bungee.api.ServerPing; 71 | import net.md_5.bungee.api.chat.BaseComponent; 72 | -import net.md_5.bungee.api.chat.ComponentBuilder; 73 | import net.md_5.bungee.api.chat.TextComponent; 74 | import net.md_5.bungee.api.config.ListenerInfo; 75 | import net.md_5.bungee.api.config.ServerInfo; 76 | @@ -279,6 +280,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection 77 | // setting protocol to login so we can send the kick message which is actually supported by the minecraft client after it sent the handshake 78 | ch.setProtocol( Protocol.LOGIN ); 79 | disconnect( bungee.getTranslation( "join_throttle_kick", TimeUnit.MILLISECONDS.toSeconds( BungeeCord.getInstance().getConfig().getThrottle() ) ) ); 80 | + BungeeCord.getInstance().getPluginManager().callEvent( new SuspiciousPlayerBehaviourEvent( this, SuspiciousPlayerBehaviourEvent.Check.JOIN_THROTTLE_TRIGGERED ) ); 81 | return; 82 | } 83 | 84 | @@ -627,12 +629,13 @@ public class InitialHandler extends PacketHandler implements PendingConnection 85 | return !ch.isClosed(); 86 | } 87 | 88 | - private static final BaseComponent[] CHAT_TOO_EARLY_DISCONNECT_REASON = new ComponentBuilder( "You may not chat right now." ).color( ChatColor.RED ).create(); 89 | + private static final BaseComponent[] CHAT_TOO_EARLY_DISCONNECT_REASON = new BaseComponent[]{ new TextComponent( "Outdated Client!" ) }; 90 | 91 | @Override 92 | public void handle(Chat chat) throws Exception 93 | { 94 | // I don't know whether anyone is so dump to send chat messages that early and whether it gets stopped earlier, but this code does not hurt 95 | disconnect( CHAT_TOO_EARLY_DISCONNECT_REASON ); 96 | + BungeeCord.getInstance().getPluginManager().callEvent( new SuspiciousPlayerBehaviourEvent( this, SuspiciousPlayerBehaviourEvent.Check.CHAT_TOO_EARLY ) ); 97 | } 98 | } 99 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 100 | index 9bbb4cc..638483d 100644 101 | --- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 102 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 103 | @@ -1,5 +1,7 @@ 104 | package net.md_5.bungee.connection; 105 | 106 | +import me.minotopia.flexpipe.api.event.SuspiciousPlayerBehaviourEvent; 107 | + 108 | import com.google.common.base.Preconditions; 109 | import net.md_5.bungee.BungeeCord; 110 | import net.md_5.bungee.ServerConnection; 111 | @@ -9,6 +11,7 @@ import net.md_5.bungee.api.ChatColor; 112 | import net.md_5.bungee.api.ProxyServer; 113 | import net.md_5.bungee.api.chat.BaseComponent; 114 | import net.md_5.bungee.api.chat.ComponentBuilder; 115 | +import net.md_5.bungee.api.chat.TextComponent; 116 | import net.md_5.bungee.api.connection.ProxiedPlayer; 117 | import net.md_5.bungee.api.event.ChatEvent; 118 | import net.md_5.bungee.api.event.PlayerDisconnectEvent; 119 | @@ -106,19 +109,30 @@ public class UpstreamBridge extends PacketHandler 120 | } 121 | } 122 | 123 | - private static final BaseComponent[] CHAT_TOO_EARLY_DISCONNECT_REASON = new ComponentBuilder( "You may not chat right now." ).color( ChatColor.RED ).create(); 124 | + private static final BaseComponent[] CHAT_INVALID_DISCONNECT_REASON = new BaseComponent[]{ new TextComponent( "Outdated Client!" ) }; 125 | 126 | @Override 127 | public void handle(Chat chat) throws Exception 128 | { 129 | - Preconditions.checkArgument( !chat.getMessage().trim().isEmpty(), "Chat message is invalid" ); // Yes, I'm sorry but these are actually sent out by some bot tools 130 | - Preconditions.checkArgument( chat.getMessage().length() <= 100, "Chat message too long" ); // Mojang limit, check on updates 131 | + if (chat.getMessage().trim().isEmpty()) // Yes, I'm sorry but these are actually sent out by some bot tools 132 | + { 133 | + con.disconnect( CHAT_INVALID_DISCONNECT_REASON ); 134 | + BungeeCord.getInstance().getPluginManager().callEvent( new SuspiciousPlayerBehaviourEvent( con, SuspiciousPlayerBehaviourEvent.Check.CHAT_EMPTY ) ); 135 | + return; 136 | + } 137 | + if (chat.getMessage().length() > 100) // Mojang limit, check on updates 138 | + { 139 | + con.disconnect( CHAT_INVALID_DISCONNECT_REASON ); 140 | + BungeeCord.getInstance().getPluginManager().callEvent( new SuspiciousPlayerBehaviourEvent( con, SuspiciousPlayerBehaviourEvent.Check.CHAT_TOO_LONG ) ); 141 | + return; 142 | + } 143 | 144 | // Users can't chat if they were never on a server yet after joining 145 | // Users also would get kicked if the command is not handled by the bungee with an npe, but this stops some unneccessary cpu cycles 146 | if(con.getServer() == null) 147 | { 148 | - con.disconnect( CHAT_TOO_EARLY_DISCONNECT_REASON ); 149 | + con.disconnect( CHAT_INVALID_DISCONNECT_REASON ); 150 | + BungeeCord.getInstance().getPluginManager().callEvent( new SuspiciousPlayerBehaviourEvent( con, SuspiciousPlayerBehaviourEvent.Check.CHAT_TOO_EARLY ) ); 151 | return; 152 | } 153 | 154 | -- 155 | 1.9.5.msysgit.0 156 | 157 | -------------------------------------------------------------------------------- /patches/0032-Ensure-kick-messages-can-be-sent-while-being-at-prot.patch: -------------------------------------------------------------------------------- 1 | From 08aac7c97d2f2c4ab312008be699666d2c32692e Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Mon, 4 Apr 2016 10:13:15 +0200 4 | Subject: Ensure kick messages can be sent while being at protocol HANDSHAKE by 5 | setting the protocol to LOGIN at kick. 6 | 7 | --- 8 | .../java/net/md_5/bungee/protocol/MinecraftDecoder.java | 2 ++ 9 | .../java/net/md_5/bungee/connection/InitialHandler.java | 15 ++++++++++++--- 10 | .../main/java/net/md_5/bungee/netty/ChannelWrapper.java | 5 +++++ 11 | 3 files changed, 19 insertions(+), 3 deletions(-) 12 | 13 | diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java 14 | index e7cb380..4846ec6 100644 15 | --- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java 16 | +++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java 17 | @@ -5,12 +5,14 @@ import io.netty.channel.ChannelHandlerContext; 18 | import io.netty.handler.codec.MessageToMessageDecoder; 19 | import java.util.List; 20 | import lombok.AllArgsConstructor; 21 | +import lombok.Getter; 22 | import lombok.Setter; 23 | 24 | @AllArgsConstructor 25 | public class MinecraftDecoder extends MessageToMessageDecoder 26 | { 27 | 28 | + @Getter 29 | @Setter 30 | private Protocol protocol; 31 | private final boolean server; 32 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 33 | index 4e5553d..1ca4b2c 100644 34 | --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 35 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 36 | @@ -277,8 +277,6 @@ public class InitialHandler extends PacketHandler implements PendingConnection 37 | { 38 | // setting thisState to username to stop possible code execution on repeated handshakes 39 | thisState = State.USERNAME; 40 | - // setting protocol to login so we can send the kick message which is actually supported by the minecraft client after it sent the handshake 41 | - ch.setProtocol( Protocol.LOGIN ); 42 | disconnect( bungee.getTranslation( "join_throttle_kick", TimeUnit.MILLISECONDS.toSeconds( BungeeCord.getInstance().getConfig().getThrottle() ) ) ); 43 | BungeeCord.getInstance().getPluginManager().callEvent( new SuspiciousPlayerBehaviourEvent( this, SuspiciousPlayerBehaviourEvent.Check.JOIN_THROTTLE_TRIGGERED ) ); 44 | return; 45 | @@ -549,7 +547,18 @@ public class InitialHandler extends PacketHandler implements PendingConnection 46 | { 47 | if ( thisState != State.STATUS && thisState != State.PING ) 48 | { 49 | - ch.close( new Kick( ComponentSerializer.toString( reason ) ) ); 50 | + if ( ch.getProtocol() == Protocol.HANDSHAKE ) 51 | + { 52 | + ch.setProtocol( Protocol.LOGIN ); 53 | + } 54 | + try 55 | + { 56 | + ch.close( new Kick( ComponentSerializer.toString( reason ) ) ); 57 | + } catch ( IllegalArgumentException ex ) 58 | + { 59 | + BungeeCord.getInstance().getLogger().log( Level.WARNING, "{0} Error while kicking: ", ex ); 60 | + ch.close(); 61 | + } 62 | } else 63 | { 64 | ch.close(); 65 | diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java 66 | index 9481678..67dff4f 100644 67 | --- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java 68 | +++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java 69 | @@ -24,6 +24,11 @@ public class ChannelWrapper 70 | { 71 | this.ch = ctx.channel(); 72 | } 73 | + 74 | + public Protocol getProtocol() 75 | + { 76 | + return ch.pipeline().get( MinecraftDecoder.class ).getProtocol(); 77 | + } 78 | 79 | public void setProtocol(Protocol protocol) 80 | { 81 | -- 82 | 1.9.5.msysgit.0 83 | 84 | -------------------------------------------------------------------------------- /patches/0033-Overwrite-netty-ByteToMessageDecoder-class-which-use.patch: -------------------------------------------------------------------------------- 1 | From ccedfe182d97956c515beef32f3830b49065c5d4 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Thu, 7 Apr 2016 17:21:41 +0200 4 | Subject: Overwrite netty ByteToMessageDecoder class, which used a heap buffer 5 | and no direct buffer. 6 | 7 | Waiting for netty 4.0.37.Final for fix: https://github.com/netty/netty/issues/5093 8 | 9 | Additionally a debug stack trace is shown when we detect a memory-addressless buffer. 10 | --- 11 | .../netty/handler/codec/ByteToMessageDecoder.java | 455 +++++++++++++++++++++ 12 | .../md_5/bungee/protocol/Varint21FrameDecoder.java | 4 + 13 | 2 files changed, 459 insertions(+) 14 | create mode 100644 bootstrap/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java 15 | 16 | diff --git a/bootstrap/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java b/bootstrap/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java 17 | new file mode 100644 18 | index 0000000..88e9895 19 | --- /dev/null 20 | +++ b/bootstrap/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java 21 | @@ -0,0 +1,455 @@ 22 | +/* 23 | + * Copyright 2012 The Netty Project 24 | + * 25 | + * The Netty Project licenses this file to you under the Apache License, 26 | + * version 2.0 (the "License"); you may not use this file except in compliance 27 | + * with the License. You may obtain a copy of the License at: 28 | + * 29 | + * http://www.apache.org/licenses/LICENSE-2.0 30 | + * 31 | + * Unless required by applicable law or agreed to in writing, software 32 | + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 33 | + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 34 | + * License for the specific language governing permissions and limitations 35 | + * under the License. 36 | + */ 37 | +package io.netty.handler.codec; 38 | + 39 | +import io.netty.buffer.ByteBuf; 40 | +import io.netty.buffer.ByteBufAllocator; 41 | +import io.netty.buffer.CompositeByteBuf; 42 | +import io.netty.buffer.Unpooled; 43 | +import io.netty.channel.ChannelHandlerContext; 44 | +import io.netty.channel.ChannelInboundHandlerAdapter; 45 | +import io.netty.util.internal.RecyclableArrayList; 46 | +import io.netty.util.internal.StringUtil; 47 | + 48 | +import java.util.List; 49 | + 50 | +/** 51 | + * {@link ChannelInboundHandlerAdapter} which decodes bytes in a stream-like fashion from one {@link ByteBuf} to an 52 | + * other Message type. 53 | + * 54 | + * For example here is an implementation which reads all readable bytes from 55 | + * the input {@link ByteBuf} and create a new {@link ByteBuf}. 56 | + * 57 | + *
 58 | + *     public class SquareDecoder extends {@link ByteToMessageDecoder} {
 59 | + *         {@code @Override}
 60 | + *         public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in, List<Object> out)
 61 | + *                 throws {@link Exception} {
 62 | + *             out.add(in.readBytes(in.readableBytes()));
 63 | + *         }
 64 | + *     }
 65 | + * 
66 | + * 67 | + *

Frame detection

68 | + *

69 | + * Generally frame detection should be handled earlier in the pipeline by adding a 70 | + * {@link DelimiterBasedFrameDecoder}, {@link FixedLengthFrameDecoder}, {@link LengthFieldBasedFrameDecoder}, 71 | + * or {@link LineBasedFrameDecoder}. 72 | + *

73 | + * If a custom frame decoder is required, then one needs to be careful when implementing 74 | + * one with {@link ByteToMessageDecoder}. Ensure there are enough bytes in the buffer for a 75 | + * complete frame by checking {@link ByteBuf#readableBytes()}. If there are not enough bytes 76 | + * for a complete frame, return without modifying the reader index to allow more bytes to arrive. 77 | + *

78 | + * To check for complete frames without modifying the reader index, use methods like {@link ByteBuf#getInt(int)}. 79 | + * One MUST use the reader index when using methods like {@link ByteBuf#getInt(int)}. 80 | + * For example calling in.getInt(0) is assuming the frame starts at the beginning of the buffer, which 81 | + * is not always the case. Use in.getInt(in.readerIndex()) instead. 82 | + *

Pitfalls

83 | + *

84 | + * Be aware that sub-classes of {@link ByteToMessageDecoder} MUST NOT 85 | + * annotated with {@link @Sharable}. 86 | + *

87 | + * Some methods such as {@link ByteBuf#readBytes(int)} will cause a memory leak if the returned buffer 88 | + * is not released or added to the out {@link List}. Use derived buffers like {@link ByteBuf#readSlice(int)} 89 | + * to avoid leaking memory. 90 | + */ 91 | +public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter { 92 | + 93 | + /** 94 | + * Cumulate {@link ByteBuf}s by merge them into one {@link ByteBuf}'s, using memory copies. 95 | + */ 96 | + public static final Cumulator MERGE_CUMULATOR = new Cumulator() { 97 | + @Override 98 | + public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) { 99 | + ByteBuf buffer; 100 | + if (cumulation.writerIndex() > cumulation.maxCapacity() - in.readableBytes() 101 | + || cumulation.refCnt() > 1) { 102 | + // Expand cumulation (by replace it) when either there is not more room in the buffer 103 | + // or if the refCnt is greater then 1 which may happen when the user use slice().retain() or 104 | + // duplicate().retain(). 105 | + // 106 | + // See: 107 | + // - https://github.com/netty/netty/issues/2327 108 | + // - https://github.com/netty/netty/issues/1764 109 | + buffer = expandCumulation(alloc, cumulation, in.readableBytes()); 110 | + } else { 111 | + buffer = cumulation; 112 | + } 113 | + buffer.writeBytes(in); 114 | + in.release(); 115 | + return buffer; 116 | + } 117 | + }; 118 | + 119 | + /** 120 | + * Cumulate {@link ByteBuf}s by add them to a {@link CompositeByteBuf} and so do no memory copy whenever possible. 121 | + * Be aware that {@link CompositeByteBuf} use a more complex indexing implementation so depending on your use-case 122 | + * and the decoder implementation this may be slower then just use the {@link #MERGE_CUMULATOR}. 123 | + */ 124 | + public static final Cumulator COMPOSITE_CUMULATOR = new Cumulator() { 125 | + @Override 126 | + public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) { 127 | + ByteBuf buffer; 128 | + if (cumulation.refCnt() > 1) { 129 | + // Expand cumulation (by replace it) when the refCnt is greater then 1 which may happen when the user 130 | + // use slice().retain() or duplicate().retain(). 131 | + // 132 | + // See: 133 | + // - https://github.com/netty/netty/issues/2327 134 | + // - https://github.com/netty/netty/issues/1764 135 | + buffer = expandCumulation(alloc, cumulation, in.readableBytes()); 136 | + buffer.writeBytes(in); 137 | + in.release(); 138 | + } else { 139 | + CompositeByteBuf composite; 140 | + if (cumulation instanceof CompositeByteBuf) { 141 | + composite = (CompositeByteBuf) cumulation; 142 | + } else { 143 | + int readable = cumulation.readableBytes(); 144 | + composite = alloc.compositeBuffer(); 145 | + composite.addComponent(cumulation).writerIndex(readable); 146 | + } 147 | + composite.addComponent(in).writerIndex(composite.writerIndex() + in.readableBytes()); 148 | + buffer = composite; 149 | + } 150 | + return buffer; 151 | + } 152 | + }; 153 | + 154 | + ByteBuf cumulation; 155 | + private Cumulator cumulator = MERGE_CUMULATOR; 156 | + private boolean singleDecode; 157 | + private boolean decodeWasNull; 158 | + private boolean first; 159 | + private int discardAfterReads = 16; 160 | + private int numReads; 161 | + 162 | + protected ByteToMessageDecoder() { 163 | + CodecUtil.ensureNotSharable(this); 164 | + } 165 | + 166 | + /** 167 | + * If set then only one message is decoded on each {@link #channelRead(ChannelHandlerContext, Object)} 168 | + * call. This may be useful if you need to do some protocol upgrade and want to make sure nothing is mixed up. 169 | + * 170 | + * Default is {@code false} as this has performance impacts. 171 | + */ 172 | + public void setSingleDecode(boolean singleDecode) { 173 | + this.singleDecode = singleDecode; 174 | + } 175 | + 176 | + /** 177 | + * If {@code true} then only one message is decoded on each 178 | + * {@link #channelRead(ChannelHandlerContext, Object)} call. 179 | + * 180 | + * Default is {@code false} as this has performance impacts. 181 | + */ 182 | + public boolean isSingleDecode() { 183 | + return singleDecode; 184 | + } 185 | + 186 | + /** 187 | + * Set the {@link Cumulator} to use for cumulate the received {@link ByteBuf}s. 188 | + */ 189 | + public void setCumulator(Cumulator cumulator) { 190 | + if (cumulator == null) { 191 | + throw new NullPointerException("cumulator"); 192 | + } 193 | + this.cumulator = cumulator; 194 | + } 195 | + 196 | + /** 197 | + * Set the number of reads after which {@link ByteBuf#discardSomeReadBytes()} are called and so free up memory. 198 | + * The default is {@code 16}. 199 | + */ 200 | + public void setDiscardAfterReads(int discardAfterReads) { 201 | + if (discardAfterReads <= 0) { 202 | + throw new IllegalArgumentException("discardAfterReads must be > 0"); 203 | + } 204 | + this.discardAfterReads = discardAfterReads; 205 | + } 206 | + 207 | + /** 208 | + * Returns the actual number of readable bytes in the internal cumulative 209 | + * buffer of this decoder. You usually do not need to rely on this value 210 | + * to write a decoder. Use it only when you must use it at your own risk. 211 | + * This method is a shortcut to {@link #internalBuffer() internalBuffer().readableBytes()}. 212 | + */ 213 | + protected int actualReadableBytes() { 214 | + return internalBuffer().readableBytes(); 215 | + } 216 | + 217 | + /** 218 | + * Returns the internal cumulative buffer of this decoder. You usually 219 | + * do not need to access the internal buffer directly to write a decoder. 220 | + * Use it only when you must use it at your own risk. 221 | + */ 222 | + protected ByteBuf internalBuffer() { 223 | + if (cumulation != null) { 224 | + return cumulation; 225 | + } else { 226 | + return Unpooled.EMPTY_BUFFER; 227 | + } 228 | + } 229 | + 230 | + @Override 231 | + public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception { 232 | + ByteBuf buf = internalBuffer(); 233 | + int readable = buf.readableBytes(); 234 | + if (readable > 0) { 235 | + //FlexPipe - use direct buffer 236 | + 237 | + /* //old code 238 | + ByteBuf bytes = buf.readBytes(readable); 239 | + buf.release(); 240 | + ctx.fireChannelRead(bytes); 241 | + */ 242 | + 243 | + ByteBuf bytes = ctx.alloc().buffer( readable ); 244 | + 245 | + buf.readBytes(bytes, readable); 246 | + buf.release(); 247 | + 248 | + try { 249 | + ctx.fireChannelRead(bytes); 250 | + } finally { 251 | + if (bytes.refCnt() > 0) { 252 | + bytes.release(); 253 | + new Throwable( "buffer was not released through channel read" ).printStackTrace(); 254 | + } 255 | + } 256 | + } else { 257 | + buf.release(); 258 | + } 259 | + cumulation = null; 260 | + numReads = 0; 261 | + ctx.fireChannelReadComplete(); 262 | + handlerRemoved0(ctx); 263 | + } 264 | + 265 | + /** 266 | + * Gets called after the {@link ByteToMessageDecoder} was removed from the actual context and it doesn't handle 267 | + * events anymore. 268 | + */ 269 | + protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { } 270 | + 271 | + @Override 272 | + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 273 | + if (msg instanceof ByteBuf) { 274 | + RecyclableArrayList out = RecyclableArrayList.newInstance(); 275 | + try { 276 | + ByteBuf data = (ByteBuf) msg; 277 | + first = cumulation == null; 278 | + if (first) { 279 | + cumulation = data; 280 | + } else { 281 | + cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data); 282 | + } 283 | + callDecode(ctx, cumulation, out); 284 | + } catch (DecoderException e) { 285 | + throw e; 286 | + } catch (Throwable t) { 287 | + throw new DecoderException(t); 288 | + } finally { 289 | + if (cumulation != null && !cumulation.isReadable()) { 290 | + numReads = 0; 291 | + cumulation.release(); 292 | + cumulation = null; 293 | + } else if (++ numReads >= discardAfterReads) { 294 | + // We did enough reads already try to discard some bytes so we not risk to see a OOME. 295 | + // See https://github.com/netty/netty/issues/4275 296 | + numReads = 0; 297 | + discardSomeReadBytes(); 298 | + } 299 | + 300 | + int size = out.size(); 301 | + decodeWasNull = !out.insertSinceRecycled(); 302 | + fireChannelRead(ctx, out, size); 303 | + out.recycle(); 304 | + } 305 | + } else { 306 | + ctx.fireChannelRead(msg); 307 | + } 308 | + } 309 | + 310 | + /** 311 | + * Get {@code numElements} out of the {@link List} and forward these through the pipeline. 312 | + */ 313 | + static void fireChannelRead(ChannelHandlerContext ctx, List msgs, int numElements) { 314 | + for (int i = 0; i < numElements; i ++) { 315 | + ctx.fireChannelRead(msgs.get(i)); 316 | + } 317 | + } 318 | + 319 | + @Override 320 | + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 321 | + numReads = 0; 322 | + discardSomeReadBytes(); 323 | + if (decodeWasNull) { 324 | + decodeWasNull = false; 325 | + if (!ctx.channel().config().isAutoRead()) { 326 | + ctx.read(); 327 | + } 328 | + } 329 | + ctx.fireChannelReadComplete(); 330 | + } 331 | + 332 | + protected final void discardSomeReadBytes() { 333 | + if (cumulation != null && !first && cumulation.refCnt() == 1) { 334 | + // discard some bytes if possible to make more room in the 335 | + // buffer but only if the refCnt == 1 as otherwise the user may have 336 | + // used slice().retain() or duplicate().retain(). 337 | + // 338 | + // See: 339 | + // - https://github.com/netty/netty/issues/2327 340 | + // - https://github.com/netty/netty/issues/1764 341 | + cumulation.discardSomeReadBytes(); 342 | + } 343 | + } 344 | + 345 | + @Override 346 | + public void channelInactive(ChannelHandlerContext ctx) throws Exception { 347 | + RecyclableArrayList out = RecyclableArrayList.newInstance(); 348 | + try { 349 | + if (cumulation != null) { 350 | + callDecode(ctx, cumulation, out); 351 | + decodeLast(ctx, cumulation, out); 352 | + } else { 353 | + decodeLast(ctx, Unpooled.EMPTY_BUFFER, out); 354 | + } 355 | + } catch (DecoderException e) { 356 | + throw e; 357 | + } catch (Exception e) { 358 | + throw new DecoderException(e); 359 | + } finally { 360 | + try { 361 | + if (cumulation != null) { 362 | + cumulation.release(); 363 | + cumulation = null; 364 | + } 365 | + int size = out.size(); 366 | + fireChannelRead(ctx, out, size); 367 | + if (size > 0) { 368 | + // Something was read, call fireChannelReadComplete() 369 | + ctx.fireChannelReadComplete(); 370 | + } 371 | + ctx.fireChannelInactive(); 372 | + } finally { 373 | + // recycle in all cases 374 | + out.recycle(); 375 | + } 376 | + } 377 | + } 378 | + 379 | + /** 380 | + * Called once data should be decoded from the given {@link ByteBuf}. This method will call 381 | + * {@link #decode(ChannelHandlerContext, ByteBuf, List)} as long as decoding should take place. 382 | + * 383 | + * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to 384 | + * @param in the {@link ByteBuf} from which to read data 385 | + * @param out the {@link List} to which decoded messages should be added 386 | + */ 387 | + protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List out) { 388 | + try { 389 | + while (in.isReadable()) { 390 | + int outSize = out.size(); 391 | + 392 | + if (outSize > 0) { 393 | + fireChannelRead(ctx, out, outSize); 394 | + out.clear(); 395 | + outSize = 0; 396 | + } 397 | + 398 | + int oldInputLength = in.readableBytes(); 399 | + decode(ctx, in, out); 400 | + 401 | + // Check if this handler was removed before continuing the loop. 402 | + // If it was removed, it is not safe to continue to operate on the buffer. 403 | + // 404 | + // See https://github.com/netty/netty/issues/1664 405 | + if (ctx.isRemoved()) { 406 | + break; 407 | + } 408 | + 409 | + if (outSize == out.size()) { 410 | + if (oldInputLength == in.readableBytes()) { 411 | + break; 412 | + } else { 413 | + continue; 414 | + } 415 | + } 416 | + 417 | + if (oldInputLength == in.readableBytes()) { 418 | + throw new DecoderException( 419 | + StringUtil.simpleClassName(getClass()) + 420 | + ".decode() did not read anything but decoded a message."); 421 | + } 422 | + 423 | + if (isSingleDecode()) { 424 | + break; 425 | + } 426 | + } 427 | + } catch (DecoderException e) { 428 | + throw e; 429 | + } catch (Throwable cause) { 430 | + throw new DecoderException(cause); 431 | + } 432 | + } 433 | + 434 | + /** 435 | + * Decode the from one {@link ByteBuf} to an other. This method will be called till either the input 436 | + * {@link ByteBuf} has nothing to read when return from this method or till nothing was read from the input 437 | + * {@link ByteBuf}. 438 | + * 439 | + * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to 440 | + * @param in the {@link ByteBuf} from which to read data 441 | + * @param out the {@link List} to which decoded messages should be added 442 | + * @throws Exception is thrown if an error accour 443 | + */ 444 | + protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception; 445 | + 446 | + /** 447 | + * Is called one last time when the {@link ChannelHandlerContext} goes in-active. Which means the 448 | + * {@link #channelInactive(ChannelHandlerContext)} was triggered. 449 | + * 450 | + * By default this will just call {@link #decode(ChannelHandlerContext, ByteBuf, List)} but sub-classes may 451 | + * override this for some special cleanup operation. 452 | + */ 453 | + protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 454 | + decode(ctx, in, out); 455 | + } 456 | + 457 | + static ByteBuf expandCumulation(ByteBufAllocator alloc, ByteBuf cumulation, int readable) { 458 | + ByteBuf oldCumulation = cumulation; 459 | + cumulation = alloc.buffer(oldCumulation.readableBytes() + readable); 460 | + cumulation.writeBytes(oldCumulation); 461 | + oldCumulation.release(); 462 | + return cumulation; 463 | + } 464 | + 465 | + /** 466 | + * Cumulate {@link ByteBuf}s. 467 | + */ 468 | + public interface Cumulator { 469 | + /** 470 | + * Cumulate the given {@link ByteBuf}s and return the {@link ByteBuf} that holds the cumulated bytes. 471 | + * The implementation is responsible to correctly handle the life-cycle of the given {@link ByteBuf}s and so 472 | + * call {@link ByteBuf#release()} if a {@link ByteBuf} is fully consumed. 473 | + */ 474 | + ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in); 475 | + } 476 | +} 477 | diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java 478 | index 29e54db..d7e4020 100644 479 | --- a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java 480 | +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java 481 | @@ -16,6 +16,10 @@ public class Varint21FrameDecoder extends ByteToMessageDecoder 482 | @Override 483 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception 484 | { 485 | + if ( !in.hasMemoryAddress() ) 486 | + { 487 | + new Throwable( "A ByteBuf without a memory address was used: " + in ).printStackTrace(); 488 | + } 489 | in.markReaderIndex(); 490 | 491 | final byte[] buf = new byte[ 3 ]; 492 | -- 493 | 1.9.5.msysgit.0 494 | 495 | -------------------------------------------------------------------------------- /patches/0034-Slice-packets-in-MinecraftDecoder-where-possible-bas.patch: -------------------------------------------------------------------------------- 1 | From 727b6b4d6903581dcc2c8540325d8b873324ec3d Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Fri, 8 Apr 2016 00:04:13 +0200 4 | Subject: Slice packets in MinecraftDecoder where possible, based on packet Id 5 | 6 | Give MinecraftDecoder a SliceDecider which links through the HandlerBoss handler to the EntityMap, which then looks whether the packetId is either registered to be changed or handled in the version-specific rewrites. 7 | 8 | Additionally it removes some overhead by the MessageToMessageEncoder from MinecraftDecoder. 9 | --- 10 | .../net/md_5/bungee/protocol/MinecraftDecoder.java | 67 +++++++++++++++++++--- 11 | .../md_5/bungee/protocol/util/SliceDecider.java | 6 ++ 12 | .../main/java/net/md_5/bungee/UserConnection.java | 4 +- 13 | .../md_5/bungee/connection/DownstreamBridge.java | 14 ++++- 14 | .../net/md_5/bungee/connection/PingHandler.java | 4 +- 15 | .../md_5/bungee/connection/SliceDeciderImpl.java | 18 ++++++ 16 | .../net/md_5/bungee/connection/UpstreamBridge.java | 13 ++++- 17 | .../java/net/md_5/bungee/entitymap/EntityMap.java | 26 ++++++++- 18 | .../net/md_5/bungee/entitymap/EntityMap_1_10.java | 12 ++++ 19 | .../net/md_5/bungee/entitymap/EntityMap_1_8.java | 12 ++++ 20 | .../net/md_5/bungee/entitymap/EntityMap_1_9.java | 12 ++++ 21 | .../net/md_5/bungee/entitymap/EntityMap_1_9_4.java | 12 ++++ 22 | .../java/net/md_5/bungee/netty/HandlerBoss.java | 3 + 23 | .../java/net/md_5/bungee/netty/PacketHandler.java | 5 ++ 24 | .../java/net/md_5/bungee/netty/PipelineUtils.java | 4 +- 25 | 15 files changed, 196 insertions(+), 16 deletions(-) 26 | create mode 100644 protocol/src/main/java/net/md_5/bungee/protocol/util/SliceDecider.java 27 | create mode 100644 proxy/src/main/java/net/md_5/bungee/connection/SliceDeciderImpl.java 28 | 29 | diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java 30 | index 4846ec6..4b914ad 100644 31 | --- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java 32 | +++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java 33 | @@ -1,5 +1,9 @@ 34 | package net.md_5.bungee.protocol; 35 | 36 | +import io.netty.channel.ChannelInboundHandlerAdapter; 37 | +import io.netty.handler.codec.DecoderException; 38 | +import net.md_5.bungee.protocol.util.SliceDecider; 39 | + 40 | import io.netty.buffer.ByteBuf; 41 | import io.netty.channel.ChannelHandlerContext; 42 | import io.netty.handler.codec.MessageToMessageDecoder; 43 | @@ -9,7 +13,7 @@ import lombok.Getter; 44 | import lombok.Setter; 45 | 46 | @AllArgsConstructor 47 | -public class MinecraftDecoder extends MessageToMessageDecoder 48 | +public class MinecraftDecoder extends ChannelInboundHandlerAdapter 49 | { 50 | 51 | @Getter 52 | @@ -18,17 +22,28 @@ public class MinecraftDecoder extends MessageToMessageDecoder 53 | private final boolean server; 54 | @Setter 55 | private int protocolVersion; 56 | + private final SliceDecider sliceDecider; 57 | 58 | - @Override 59 | - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception 60 | + private Object decode(ByteBuf in) throws Exception 61 | { 62 | Protocol.DirectionData prot = ( server ) ? protocol.TO_SERVER : protocol.TO_CLIENT; 63 | - ByteBuf slice = in.copy(); // Can't slice this one due to EntityMap :( 64 | + ByteBuf newBuf = null; 65 | 66 | try 67 | { 68 | + int originalReaderIndex = in.readerIndex(); 69 | + int originalReadableBytes = in.readableBytes(); 70 | + 71 | int packetId = DefinedPacket.readVarInt( in ); 72 | 73 | + if ( sliceDecider.shouldNotSlice( packetId ) ) 74 | + { 75 | + newBuf = in.copy( originalReaderIndex, originalReadableBytes ); 76 | + } else 77 | + { 78 | + newBuf = in.slice( originalReaderIndex, originalReadableBytes ).retain(); 79 | + } 80 | + 81 | DefinedPacket packet = prot.createPacket( packetId, protocolVersion ); 82 | if ( packet != null ) 83 | { 84 | @@ -43,13 +58,49 @@ public class MinecraftDecoder extends MessageToMessageDecoder 85 | in.skipBytes( in.readableBytes() ); 86 | } 87 | 88 | - out.add( new PacketWrapper( packet, slice ) ); 89 | - slice = null; 90 | + PacketWrapper packetWrapper = new PacketWrapper( packet, newBuf ); 91 | + newBuf = null; 92 | + return packetWrapper; 93 | + } finally 94 | + { 95 | + if ( newBuf != null ) 96 | + { 97 | + newBuf.release(); 98 | + } 99 | + } 100 | + } 101 | + 102 | + @Override 103 | + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception 104 | + { 105 | + Object out = null; 106 | + try 107 | + { 108 | + if ( msg instanceof ByteBuf ) 109 | + { 110 | + ByteBuf cast = ( ByteBuf ) msg; 111 | + try 112 | + { 113 | + out = decode( cast ); 114 | + } finally 115 | + { 116 | + cast.release(); 117 | + } 118 | + } else 119 | + { 120 | + out = msg; 121 | + } 122 | + } catch ( DecoderException e ) 123 | + { 124 | + throw e; 125 | + } catch ( Exception e ) 126 | + { 127 | + throw new DecoderException( e ); 128 | } finally 129 | { 130 | - if ( slice != null ) 131 | + if ( out != null ) 132 | { 133 | - slice.release(); 134 | + ctx.fireChannelRead( out ); 135 | } 136 | } 137 | } 138 | diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/util/SliceDecider.java b/protocol/src/main/java/net/md_5/bungee/protocol/util/SliceDecider.java 139 | new file mode 100644 140 | index 0000000..93c3d40 141 | --- /dev/null 142 | +++ b/protocol/src/main/java/net/md_5/bungee/protocol/util/SliceDecider.java 143 | @@ -0,0 +1,6 @@ 144 | +package net.md_5.bungee.protocol.util; 145 | + 146 | +public interface SliceDecider 147 | +{ 148 | + boolean shouldNotSlice(int packetId); 149 | +} 150 | diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java 151 | index 1f41298..2655a9a 100644 152 | --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java 153 | +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java 154 | @@ -1,5 +1,7 @@ 155 | package net.md_5.bungee; 156 | 157 | +import net.md_5.bungee.connection.SliceDeciderImpl; 158 | + 159 | import com.google.common.base.Objects; 160 | import com.google.common.base.Preconditions; 161 | import com.google.common.collect.ImmutableMap; 162 | @@ -294,7 +296,7 @@ public final class UserConnection implements ProxiedPlayer 163 | protected void initChannel(Channel ch) throws Exception 164 | { 165 | PipelineUtils.BASE.initChannel( ch ); 166 | - ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) ); 167 | + ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion(), new SliceDeciderImpl( ch.pipeline().get( HandlerBoss.class ) ) ) ); 168 | ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) ); 169 | ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) ); 170 | } 171 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java 172 | index 7ab1b94..460d14b 100644 173 | --- a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java 174 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java 175 | @@ -1,5 +1,7 @@ 176 | package net.md_5.bungee.connection; 177 | 178 | +import io.netty.buffer.SlicedByteBuf; 179 | + 180 | import com.google.common.base.Objects; 181 | import com.google.common.base.Preconditions; 182 | import com.google.common.io.ByteArrayDataOutput; 183 | @@ -99,11 +101,21 @@ public class DownstreamBridge extends PacketHandler 184 | @Override 185 | public void handle(PacketWrapper packet) throws Exception 186 | { 187 | - con.getEntityRewrite().rewriteClientbound( packet.buf, con.getServerEntityId(), con.getClientEntityId() ); 188 | + // if we get a SlicedByteBuf we know that entity rewrite is not needed - see MinecraftDecoder 189 | + if ( !( packet.buf instanceof SlicedByteBuf ) ) 190 | + { 191 | + con.getEntityRewrite().rewriteClientbound( packet.buf, con.getServerEntityId(), con.getClientEntityId() ); 192 | + } 193 | con.sendPacket( packet ); 194 | } 195 | 196 | @Override 197 | + public boolean isEntityRewritePossiblyNeeded(int packetId) 198 | + { 199 | + return con.getEntityRewrite().isRewriteClientbound( packetId ); 200 | + } 201 | + 202 | + @Override 203 | public void handle(KeepAlive alive) throws Exception 204 | { 205 | con.setSentPingId( alive.getRandomId() ); 206 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java 207 | index 56fded2..160cc0d 100644 208 | --- a/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java 209 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java 210 | @@ -1,5 +1,7 @@ 211 | package net.md_5.bungee.connection; 212 | 213 | +import net.md_5.bungee.netty.HandlerBoss; 214 | + 215 | import com.google.gson.Gson; 216 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 217 | import lombok.RequiredArgsConstructor; 218 | @@ -34,7 +36,7 @@ public class PingHandler extends PacketHandler 219 | this.channel = channel; 220 | MinecraftEncoder encoder = new MinecraftEncoder( Protocol.HANDSHAKE, false, protocol ); 221 | 222 | - channel.getHandle().pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.STATUS, false, ProxyServer.getInstance().getProtocolVersion() ) ); 223 | + channel.getHandle().pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.STATUS, false, ProxyServer.getInstance().getProtocolVersion(), new SliceDeciderImpl( channel.getHandle().pipeline().get( HandlerBoss.class ) ) ) ); 224 | channel.getHandle().pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, encoder ); 225 | 226 | channel.write( new Handshake( protocol, target.getAddress().getHostString(), target.getAddress().getPort(), 1 ) ); 227 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/SliceDeciderImpl.java b/proxy/src/main/java/net/md_5/bungee/connection/SliceDeciderImpl.java 228 | new file mode 100644 229 | index 0000000..2b6a5c7 230 | --- /dev/null 231 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/SliceDeciderImpl.java 232 | @@ -0,0 +1,18 @@ 233 | +package net.md_5.bungee.connection; 234 | + 235 | +import lombok.RequiredArgsConstructor; 236 | + 237 | +import net.md_5.bungee.netty.HandlerBoss; 238 | +import net.md_5.bungee.protocol.util.SliceDecider; 239 | + 240 | +@RequiredArgsConstructor 241 | +public class SliceDeciderImpl implements SliceDecider 242 | +{ 243 | + private final HandlerBoss handlerBoss; 244 | + 245 | + @Override 246 | + public boolean shouldNotSlice(int packetId) 247 | + { 248 | + return handlerBoss != null && handlerBoss.getHandler().isEntityRewritePossiblyNeeded( packetId ); 249 | + } 250 | +} 251 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 252 | index 638483d..faefa38 100644 253 | --- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 254 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java 255 | @@ -1,5 +1,6 @@ 256 | package net.md_5.bungee.connection; 257 | 258 | +import io.netty.buffer.SlicedByteBuf; 259 | import me.minotopia.flexpipe.api.event.SuspiciousPlayerBehaviourEvent; 260 | 261 | import com.google.common.base.Preconditions; 262 | @@ -91,14 +92,24 @@ public class UpstreamBridge extends PacketHandler 263 | @Override 264 | public void handle(PacketWrapper packet) throws Exception 265 | { 266 | - if ( con.getServer() != null ) 267 | + // if we get a SlicedByteBuf we know that entity rewrite is not needed - see MinecraftDecoder 268 | + if ( !( packet.buf instanceof SlicedByteBuf ) ) 269 | { 270 | con.getEntityRewrite().rewriteServerbound( packet.buf, con.getClientEntityId(), con.getServerEntityId() ); 271 | + } 272 | + 273 | + if ( con.getServer() != null) { 274 | con.getServer().getCh().write( packet ); 275 | } 276 | } 277 | 278 | @Override 279 | + public boolean isEntityRewritePossiblyNeeded(int packetId) 280 | + { 281 | + return con.getEntityRewrite().isRewriteServerbound( packetId ); 282 | + } 283 | + 284 | + @Override 285 | public void handle(KeepAlive alive) throws Exception 286 | { 287 | if ( alive.getRandomId() == con.getSentPingId() ) 288 | diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java 289 | index 7eb48e0..e51c452 100644 290 | --- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java 291 | +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java 292 | @@ -61,12 +61,18 @@ public abstract class EntityMap 293 | 294 | public void rewriteServerbound(ByteBuf packet, int oldId, int newId) 295 | { 296 | - rewrite( packet, oldId, newId, serverboundInts, serverboundVarInts ); 297 | + if ( oldId != newId ) 298 | + { 299 | + rewrite( packet, oldId, newId, serverboundInts, serverboundVarInts ); 300 | + } 301 | } 302 | 303 | public void rewriteClientbound(ByteBuf packet, int oldId, int newId) 304 | { 305 | - rewrite( packet, oldId, newId, clientboundInts, clientboundVarInts ); 306 | + if ( oldId != newId ) 307 | + { 308 | + rewrite( packet, oldId, newId, clientboundInts, clientboundVarInts ); 309 | + } 310 | } 311 | 312 | protected static void rewriteInt(ByteBuf packet, int oldId, int newId, int offset) 313 | @@ -86,7 +92,7 @@ public abstract class EntityMap 314 | { 315 | // Need to rewrite the packet because VarInts are variable length 316 | int readId = DefinedPacket.readVarInt( packet ); 317 | - int readIdLength = packet.readerIndex() - offset; 318 | + //int readIdLength = packet.readerIndex() - offset; 319 | if ( readId == oldId || readId == newId ) 320 | { 321 | ByteBuf data = packet.copy(); 322 | @@ -114,4 +120,18 @@ public abstract class EntityMap 323 | } 324 | packet.readerIndex( readerIndex ); 325 | } 326 | + 327 | + protected abstract boolean isRewriteServerbound0(int packetId); 328 | + 329 | + protected abstract boolean isRewriteClientbound0(int packetId); 330 | + 331 | + public final boolean isRewriteServerbound(int packetId) 332 | + { 333 | + return ( serverboundInts[ packetId ] || serverboundVarInts[ packetId ] ) || isRewriteServerbound0( packetId ); 334 | + } 335 | + 336 | + public final boolean isRewriteClientbound(int packetId) 337 | + { 338 | + return ( clientboundInts[ packetId ] || clientboundVarInts[ packetId ] ) || isRewriteClientbound0( packetId ); 339 | + } 340 | } 341 | diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_10.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_10.java 342 | index a89ea2e..9e1adf7 100644 343 | --- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_10.java 344 | +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_10.java 345 | @@ -175,4 +175,16 @@ class EntityMap_1_10 extends EntityMap 346 | } 347 | packet.readerIndex( readerIndex ); 348 | } 349 | + 350 | + @Override 351 | + protected boolean isRewriteClientbound0(int packetId) 352 | + { 353 | + return packetId == 0x48 || packetId == 0x40 || packetId == 0x30 || packetId == 0x00 || packetId == 0x05 || packetId == 0x2C; 354 | + } 355 | + 356 | + @Override 357 | + protected boolean isRewriteServerbound0(int packetId) 358 | + { 359 | + return packetId == 0x1B; 360 | + } 361 | } 362 | diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_8.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_8.java 363 | index 4229fef..3e1fdfa 100644 364 | --- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_8.java 365 | +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_8.java 366 | @@ -173,4 +173,16 @@ class EntityMap_1_8 extends EntityMap 367 | } 368 | packet.readerIndex( readerIndex ); 369 | } 370 | + 371 | + @Override 372 | + protected boolean isRewriteClientbound0(int packetId) 373 | + { 374 | + return packetId == 0x0D || packetId == 0x1B || packetId == 0x13 || packetId == 0x0E || packetId == 0x0C || packetId == 0x42; 375 | + } 376 | + 377 | + @Override 378 | + protected boolean isRewriteServerbound0(int packetId) 379 | + { 380 | + return packetId == 0x18; 381 | + } 382 | } 383 | diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_9.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_9.java 384 | index 19d9677..cb56f0f 100644 385 | --- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_9.java 386 | +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_9.java 387 | @@ -175,4 +175,16 @@ class EntityMap_1_9 extends EntityMap 388 | } 389 | packet.readerIndex( readerIndex ); 390 | } 391 | + 392 | + @Override 393 | + protected boolean isRewriteClientbound0(int packetId) 394 | + { 395 | + return packetId == 0x49 || packetId == 0x40 || packetId == 0x30 || packetId == 0x00 || packetId == 0x05 || packetId == 0x2C; 396 | + } 397 | + 398 | + @Override 399 | + protected boolean isRewriteServerbound0(int packetId) 400 | + { 401 | + return packetId == 0x1B; 402 | + } 403 | } 404 | diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_9_4.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_9_4.java 405 | index 412ab4e..a54f2bb 100644 406 | --- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_9_4.java 407 | +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_9_4.java 408 | @@ -175,4 +175,16 @@ class EntityMap_1_9_4 extends EntityMap 409 | } 410 | packet.readerIndex( readerIndex ); 411 | } 412 | + 413 | + @Override 414 | + protected boolean isRewriteClientbound0(int packetId) 415 | + { 416 | + return packetId == 0x48 || packetId == 0x40 || packetId == 0x30 || packetId == 0x00 || packetId == 0x05 || packetId == 0x2C; 417 | + } 418 | + 419 | + @Override 420 | + protected boolean isRewriteServerbound0(int packetId) 421 | + { 422 | + return packetId == 0x1B; 423 | + } 424 | } 425 | diff --git a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java 426 | index 7a49d21..8c81442 100644 427 | --- a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java 428 | +++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java 429 | @@ -1,5 +1,7 @@ 430 | package net.md_5.bungee.netty; 431 | 432 | +import lombok.Getter; 433 | + 434 | import net.md_5.bungee.protocol.PacketWrapper; 435 | import com.google.common.base.Preconditions; 436 | import io.netty.channel.ChannelHandlerContext; 437 | @@ -24,6 +26,7 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter 438 | { 439 | 440 | private ChannelWrapper channel; 441 | + @Getter 442 | private PacketHandler handler; 443 | 444 | public void setHandler(PacketHandler handler) 445 | diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PacketHandler.java b/proxy/src/main/java/net/md_5/bungee/netty/PacketHandler.java 446 | index 7958eaa..bd50826 100644 447 | --- a/proxy/src/main/java/net/md_5/bungee/netty/PacketHandler.java 448 | +++ b/proxy/src/main/java/net/md_5/bungee/netty/PacketHandler.java 449 | @@ -28,4 +28,9 @@ public abstract class PacketHandler extends net.md_5.bungee.protocol.AbstractPac 450 | public void disconnected(ChannelWrapper channel) throws Exception 451 | { 452 | } 453 | + 454 | + public boolean isEntityRewritePossiblyNeeded(int packetId) 455 | + { 456 | + return false; 457 | + } 458 | } 459 | diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java 460 | index e6e74f4..b35dea6 100644 461 | --- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java 462 | +++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java 463 | @@ -1,5 +1,7 @@ 464 | package net.md_5.bungee.netty; 465 | 466 | +import net.md_5.bungee.connection.SliceDeciderImpl; 467 | + 468 | import io.netty.buffer.PooledByteBufAllocator; 469 | import io.netty.channel.Channel; 470 | import io.netty.channel.ChannelException; 471 | @@ -51,7 +53,7 @@ public class PipelineUtils 472 | { 473 | BASE.initChannel( ch ); 474 | ch.pipeline().addBefore( FRAME_DECODER, LEGACY_DECODER, new LegacyDecoder() ); 475 | - ch.pipeline().addAfter( FRAME_DECODER, PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) ); 476 | + ch.pipeline().addAfter( FRAME_DECODER, PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion(), new SliceDeciderImpl( ch.pipeline().get( HandlerBoss.class ) ) ) ); 477 | ch.pipeline().addAfter( FRAME_PREPENDER, PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) ); 478 | ch.pipeline().addBefore( FRAME_PREPENDER, LEGACY_KICKER, KICK_STRING_WRITER ); 479 | ch.pipeline().get( HandlerBoss.class ).setHandler( new InitialHandler( BungeeCord.getInstance(), ch.attr( LISTENER ).get() ) ); 480 | -- 481 | 1.9.5.msysgit.0 482 | 483 | -------------------------------------------------------------------------------- /patches/0035-Calculate-the-version-only-once.patch: -------------------------------------------------------------------------------- 1 | From 6701f9c552071cbda85320514b1a6bb507251b5f Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Sun, 24 Apr 2016 14:16:18 +0200 4 | Subject: Calculate the version only once. 5 | 6 | --- 7 | proxy/src/main/java/net/md_5/bungee/BungeeCord.java | 3 ++- 8 | 1 file changed, 2 insertions(+), 1 deletion(-) 9 | 10 | diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 11 | index a135964..02a52c0 100644 12 | --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 13 | +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 14 | @@ -98,6 +98,7 @@ import org.fusesource.jansi.AnsiConsole; 15 | public class BungeeCord extends ProxyServer 16 | { 17 | 18 | + private static final String VERSION = ( BungeeCord.class.getPackage().getImplementationVersion() == null ) ? "unknown" : BungeeCord.class.getPackage().getImplementationVersion(); 19 | /** 20 | * Current operation state. 21 | */ 22 | @@ -557,7 +558,7 @@ public class BungeeCord extends ProxyServer 23 | @Override 24 | public String getVersion() 25 | { 26 | - return ( BungeeCord.class.getPackage().getImplementationVersion() == null ) ? "unknown" : BungeeCord.class.getPackage().getImplementationVersion(); 27 | + return VERSION; 28 | } 29 | 30 | @Override 31 | -- 32 | 1.9.5.msysgit.0 33 | 34 | -------------------------------------------------------------------------------- /patches/0036-Presize-the-HTTP-response-buffer.patch: -------------------------------------------------------------------------------- 1 | From a976b40049c01f21f0aceeaba72850b7f1bb4549 Mon Sep 17 00:00:00 2001 2 | From: Tux 3 | Date: Sun, 24 Apr 2016 15:09:43 +0200 4 | Subject: Presize the HTTP response buffer 5 | 6 | 16 characters is extremely small, and all responses start around 256 bytes. 640 characters seems to be good (covering skins and capes), based on sampling profile API responses. 7 | 8 | Signed-off-by: Janmm14 9 | --- 10 | proxy/src/main/java/net/md_5/bungee/http/HttpHandler.java | 2 +- 11 | 1 file changed, 1 insertion(+), 1 deletion(-) 12 | 13 | diff --git a/proxy/src/main/java/net/md_5/bungee/http/HttpHandler.java b/proxy/src/main/java/net/md_5/bungee/http/HttpHandler.java 14 | index 96d0a71..93565cf 100644 15 | --- a/proxy/src/main/java/net/md_5/bungee/http/HttpHandler.java 16 | +++ b/proxy/src/main/java/net/md_5/bungee/http/HttpHandler.java 17 | @@ -16,7 +16,7 @@ public class HttpHandler extends SimpleChannelInboundHandler 18 | { 19 | 20 | private final Callback callback; 21 | - private final StringBuilder buffer = new StringBuilder(); 22 | + private final StringBuilder buffer = new StringBuilder( 640 ); 23 | 24 | @Override 25 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception 26 | -- 27 | 1.9.5.msysgit.0 28 | 29 | -------------------------------------------------------------------------------- /patches/0037-Intern-common-strings-of-teams-which-might-be-send-m.patch: -------------------------------------------------------------------------------- 1 | From 531fb32e5dd8c3df5f9570f08bebdce14da08f48 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Tue, 26 Apr 2016 20:44:44 +0200 4 | Subject: Intern common strings of teams which might be send many times due to 5 | errourneus scoreboard bukkit plugins. 6 | 7 | Based on research done by @Techcable in https://github.com/WaterfallMC/Waterfall/issues/53#issuecomment-214636828 8 | --- 9 | .../main/java/net/md_5/bungee/protocol/packet/Team.java | 14 +++++++------- 10 | 1 file changed, 7 insertions(+), 7 deletions(-) 11 | 12 | diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java 13 | index b470579..cdd5635 100644 14 | --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java 15 | +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java 16 | @@ -42,18 +42,18 @@ public class Team extends DefinedPacket 17 | @Override 18 | public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) 19 | { 20 | - name = readString( buf ); 21 | + name = readString( buf ).intern(); 22 | mode = buf.readByte(); 23 | if ( mode == 0 || mode == 2 ) 24 | { 25 | - displayName = readString( buf ); 26 | - prefix = readString( buf ); 27 | - suffix = readString( buf ); 28 | + displayName = readString( buf ).intern(); 29 | + prefix = readString( buf ).intern(); 30 | + suffix = readString( buf ).intern(); 31 | friendlyFire = buf.readByte(); 32 | - nameTagVisibility = readString( buf ); 33 | + nameTagVisibility = readString( buf ).intern(); 34 | if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_9 ) 35 | { 36 | - collisionRule = readString( buf ); 37 | + collisionRule = readString( buf ).intern(); 38 | } 39 | color = buf.readByte(); 40 | } 41 | @@ -63,7 +63,7 @@ public class Team extends DefinedPacket 42 | players = new String[ len ]; 43 | for ( int i = 0; i < len; i++ ) 44 | { 45 | - players[i] = readString( buf ); 46 | + players[i] = readString( buf ).intern(); 47 | } 48 | } 49 | } 50 | -- 51 | 1.9.5.msysgit.0 52 | 53 | -------------------------------------------------------------------------------- /patches/0038-Ignore-server-disconnect-reason-instead-of-failing.patch: -------------------------------------------------------------------------------- 1 | From f15af063944c3599f68d95e9a5f3d4df8dd5ae1c Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Wed, 27 Apr 2016 16:46:09 +0200 4 | Subject: Ignore server disconnect reason instead of failing 5 | 6 | --- 7 | proxy/src/main/java/net/md_5/bungee/ServerConnection.java | 5 ++++- 8 | 1 file changed, 4 insertions(+), 1 deletion(-) 9 | 10 | diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java 11 | index 2304bd8..60a85af 100644 12 | --- a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java 13 | +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java 14 | @@ -50,8 +50,10 @@ public class ServerConnection implements Server 15 | @Override 16 | public void disconnect(BaseComponent... reason) 17 | { 18 | - Preconditions.checkArgument( reason.length == 0, "Server cannot have disconnect reason" ); 19 | + disconnect(); 20 | + } 21 | 22 | + public void disconnect() { 23 | if ( !ch.isClosed() ) 24 | { 25 | ch.getHandle().eventLoop().schedule( ( Runnable ) ch::close, 100, TimeUnit.MILLISECONDS ); 26 | @@ -59,6 +61,7 @@ public class ServerConnection implements Server 27 | } 28 | 29 | @Override 30 | + @Deprecated 31 | public void disconnect(BaseComponent reason) 32 | { 33 | disconnect(); 34 | -- 35 | 1.9.5.msysgit.0 36 | 37 | -------------------------------------------------------------------------------- /patches/0039-Allow-the-console-to-tab-complete-commands.patch: -------------------------------------------------------------------------------- 1 | From 22dc885eb168c8749ba917e33aa594674f3af1c5 Mon Sep 17 00:00:00 2001 2 | From: DoctorDark 3 | Date: Fri, 10 Jun 2016 00:27:17 +0200 4 | Subject: Allow the console to tab complete commands 5 | 6 | Signed-off-by: Janmm14 7 | --- 8 | .../src/main/java/net/md_5/bungee/BungeeCord.java | 1 + 9 | .../bungee/command/ConsoleCommandCompleter.java | 49 ++++++++++++++++++++++ 10 | 2 files changed, 50 insertions(+) 11 | create mode 100644 proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandCompleter.java 12 | 13 | diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 14 | index 02a52c0..c5ed99c 100644 15 | --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 16 | +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java 17 | @@ -234,6 +234,7 @@ public class BungeeCord extends ProxyServer 18 | AnsiConsole.systemInstall(); 19 | consoleReader = new ConsoleReader(); 20 | consoleReader.setExpandEvents( false ); 21 | + consoleReader.addCompleter( new ConsoleCommandCompleter( this ) ); 22 | 23 | logger = new BungeeLogger( "FlexPipe", "proxy.log", consoleReader ); 24 | System.setErr( new PrintStream( new LoggingOutputStream( logger, Level.SEVERE ), true ) ); 25 | diff --git a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandCompleter.java b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandCompleter.java 26 | new file mode 100644 27 | index 0000000..b993b4e 28 | --- /dev/null 29 | +++ b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandCompleter.java 30 | @@ -0,0 +1,49 @@ 31 | +package net.md_5.bungee.command; 32 | + 33 | +import com.google.common.collect.Iterables; 34 | +import java.util.ArrayList; 35 | +import java.util.List; 36 | +import java.util.logging.Level; 37 | +import jline.console.completer.Completer; 38 | +import net.md_5.bungee.api.ProxyServer; 39 | + 40 | +public class ConsoleCommandCompleter implements Completer 41 | +{ 42 | + 43 | + private final ProxyServer proxy; 44 | + 45 | + public ConsoleCommandCompleter(ProxyServer proxy) 46 | + { 47 | + this.proxy = proxy; 48 | + } 49 | + 50 | + @Override 51 | + public int complete(String buffer, int cursor, List candidates) 52 | + { 53 | + List offers = new ArrayList<>(); 54 | + try 55 | + { 56 | + proxy.getPluginManager().dispatchCommand( proxy.getConsole(), buffer, offers ); 57 | + } catch ( Exception ex ) 58 | + { 59 | + proxy.getLogger().log( Level.WARNING, "Unhandled exception when tab completing", ex ); 60 | + return cursor; 61 | + } 62 | + 63 | + if ( offers.isEmpty() ) 64 | + { 65 | + return cursor; 66 | + } 67 | + 68 | + Iterables.addAll( candidates, offers ); 69 | + 70 | + final int lastSpace = buffer.lastIndexOf( ' ' ); 71 | + if ( lastSpace == -1 ) 72 | + { 73 | + return cursor - buffer.length(); 74 | + } else 75 | + { 76 | + return cursor - ( buffer.length() - lastSpace - 1 ); 77 | + } 78 | + } 79 | +} 80 | -- 81 | 1.9.5.msysgit.0 82 | 83 | -------------------------------------------------------------------------------- /patches/0040-Use-ASM-to-generate-packet-constructors-instead-of-u.patch: -------------------------------------------------------------------------------- 1 | From 9ba1b6175bf8e0d49ae7b5991419477fb3d77174 Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Fri, 10 Jun 2016 16:52:09 +0200 4 | Subject: Use ASM to generate packet constructors instead of using reflection 5 | 6 | --- 7 | protocol/pom.xml | 5 ++ 8 | .../java/net/md_5/bungee/protocol/Protocol.java | 20 +++-- 9 | .../constructor/ASMPacketConstructorGenerator.java | 87 ++++++++++++++++++++++ 10 | .../protocol/constructor/PacketConstructor.java | 8 ++ 11 | .../constructor/ReflectivePacketConstructor.java | 17 +++++ 12 | 5 files changed, 132 insertions(+), 5 deletions(-) 13 | create mode 100644 protocol/src/main/java/net/md_5/bungee/protocol/constructor/ASMPacketConstructorGenerator.java 14 | create mode 100644 protocol/src/main/java/net/md_5/bungee/protocol/constructor/PacketConstructor.java 15 | create mode 100644 protocol/src/main/java/net/md_5/bungee/protocol/constructor/ReflectivePacketConstructor.java 16 | 17 | diff --git a/protocol/pom.xml b/protocol/pom.xml 18 | index 627b6db..7ce9618 100644 19 | --- a/protocol/pom.xml 20 | +++ b/protocol/pom.xml 21 | @@ -35,5 +35,10 @@ 22 | 3.0.3 23 | compile 24 | 25 | + 26 | + org.javassist 27 | + javassist 28 | + 3.19.0-GA 29 | + 30 | 31 | 32 | diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java 33 | index d15d4ed..8b2278c 100644 34 | --- a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java 35 | +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java 36 | @@ -8,9 +8,13 @@ import gnu.trove.map.hash.TIntObjectHashMap; 37 | import gnu.trove.map.hash.TObjectIntHashMap; 38 | import java.lang.reflect.Constructor; 39 | import java.util.Arrays; 40 | +import java.util.Collections; 41 | import java.util.List; 42 | import lombok.Getter; 43 | import lombok.RequiredArgsConstructor; 44 | +import net.md_5.bungee.protocol.constructor.ASMPacketConstructorGenerator; 45 | +import net.md_5.bungee.protocol.constructor.PacketConstructor; 46 | +import net.md_5.bungee.protocol.constructor.ReflectivePacketConstructor; 47 | import net.md_5.bungee.protocol.packet.BossBar; 48 | import net.md_5.bungee.protocol.packet.Chat; 49 | import net.md_5.bungee.protocol.packet.ClientSettings; 50 | @@ -227,7 +231,7 @@ public enum Protocol 51 | 52 | private final int protocolVersion; 53 | private final TObjectIntMap> packetMap = new TObjectIntHashMap<>( MAX_PACKET_ID ); 54 | - private final TIntObjectMap> packetConstructors = new TIntObjectHashMap<>( MAX_PACKET_ID ); 55 | + private final TIntObjectMap packetConstructors = new TIntObjectHashMap<>( MAX_PACKET_ID ); 56 | } 57 | 58 | @RequiredArgsConstructor 59 | @@ -254,7 +258,7 @@ public enum Protocol 60 | } 61 | private final TIntObjectMap> linkedProtocols = new TIntObjectHashMap<>(); 62 | { 63 | - linkedProtocols.put( ProtocolConstants.MINECRAFT_1_8, Arrays.asList( 64 | + linkedProtocols.put( ProtocolConstants.MINECRAFT_1_8, Collections.singletonList( 65 | ProtocolConstants.MINECRAFT_1_9 66 | ) ); 67 | linkedProtocols.put( ProtocolConstants.MINECRAFT_1_9, Arrays.asList( 68 | @@ -290,11 +294,11 @@ public enum Protocol 69 | throw new BadPacketException( "Packet with id " + id + " outside of range " ); 70 | } 71 | 72 | - Constructor constructor = protocolData.packetConstructors.get( id ); 73 | + PacketConstructor constructor = protocolData.packetConstructors.get( id ); 74 | try 75 | { 76 | return ( constructor == null ) ? null : constructor.newInstance(); 77 | - } catch ( ReflectiveOperationException ex ) 78 | + } catch ( Exception ex ) 79 | { 80 | throw new BadPacketException( "Could not construct packet with id " + id, ex ); 81 | } 82 | @@ -309,7 +313,13 @@ public enum Protocol 83 | { 84 | ProtocolData data = protocols.get( mapping.protocolVersion ); 85 | data.packetMap.put( packetClass, mapping.packetID ); 86 | - data.packetConstructors.put( mapping.packetID, constructor ); 87 | + PacketConstructor packetConstructor = ASMPacketConstructorGenerator.generatePacketConstructor( constructor ); 88 | + if ( packetConstructor == null ) 89 | + { 90 | + System.out.println( "Could not generate asm packet constructor for " + constructor + ", using reflection" ); 91 | + packetConstructor = new ReflectivePacketConstructor( constructor ); 92 | + } 93 | + data.packetConstructors.put( mapping.packetID, packetConstructor ); 94 | 95 | List links = linkedProtocols.get( mapping.protocolVersion ); 96 | if ( links != null ) 97 | diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/constructor/ASMPacketConstructorGenerator.java b/protocol/src/main/java/net/md_5/bungee/protocol/constructor/ASMPacketConstructorGenerator.java 98 | new file mode 100644 99 | index 0000000..e588c23 100 | --- /dev/null 101 | +++ b/protocol/src/main/java/net/md_5/bungee/protocol/constructor/ASMPacketConstructorGenerator.java 102 | @@ -0,0 +1,87 @@ 103 | +package net.md_5.bungee.protocol.constructor; 104 | + 105 | +import java.lang.reflect.Constructor; 106 | +import java.util.HashMap; 107 | +import java.util.Map; 108 | +import java.util.concurrent.atomic.AtomicInteger; 109 | +import javassist.ByteArrayClassPath; 110 | +import javassist.CannotCompileException; 111 | +import javassist.ClassPool; 112 | +import javassist.NotFoundException; 113 | +import net.md_5.bungee.protocol.DefinedPacket; 114 | +import org.objectweb.asm.ClassWriter; 115 | +import org.objectweb.asm.Type; 116 | +import org.objectweb.asm.commons.GeneratorAdapter; 117 | +import org.objectweb.asm.commons.Method; 118 | + 119 | +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; 120 | +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; 121 | +import static org.objectweb.asm.Opcodes.V1_8; 122 | + 123 | +public class ASMPacketConstructorGenerator 124 | +{ 125 | + 126 | + private static Map generatedConstructors = new HashMap<>(); 127 | + 128 | + public static PacketConstructor generatePacketConstructor(Constructor constructor) 129 | + { 130 | + Class packetClass = constructor.getDeclaringClass(); 131 | + PacketConstructor packetConstructor = generatedConstructors.get( packetClass.getName() ); 132 | + if ( packetConstructor != null ) 133 | + { 134 | + return packetConstructor; 135 | + } 136 | + try 137 | + { 138 | + ClassWriter writer = new ClassWriter( ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS ); 139 | + String bytecodeName = generateName( packetClass ); 140 | + writer.visit( V1_8, ACC_PUBLIC, bytecodeName, null, Type.getInternalName( Object.class ), new String[]{ Type.getInternalName( PacketConstructor.class ) } ); 141 | + generateMethods( constructor, packetClass, writer ); 142 | + writer.visitEnd(); 143 | + 144 | + String javaName = bytecodeName.replace( '/', '.' ); 145 | + byte[] classfile = writer.toByteArray(); 146 | + 147 | + packetConstructor = ( PacketConstructor ) loadClass( javaName, classfile ).getConstructor().newInstance(); 148 | + generatedConstructors.put( packetClass.getName(), packetConstructor ); 149 | + return packetConstructor; 150 | + } catch ( CannotCompileException | NotFoundException | ReflectiveOperationException e ) 151 | + { 152 | + e.printStackTrace(); 153 | + return null; 154 | + } 155 | + } 156 | + 157 | + private static void generateMethods(Constructor constructor, Class packetClass, ClassWriter writer) 158 | + { 159 | + //Generate constructor 160 | + GeneratorAdapter methodGenerator = new GeneratorAdapter( writer.visitMethod( ACC_PUBLIC, "", "()V", null, null ), ACC_PUBLIC, "", "()V" ); 161 | + methodGenerator.loadThis(); 162 | + methodGenerator.visitMethodInsn( INVOKESPECIAL, Type.getInternalName( Object.class ), "", "()V", false ); // Invoke the super class (Object) constructor 163 | + methodGenerator.returnValue(); 164 | + methodGenerator.endMethod(); 165 | + //Generate newInstance method 166 | + String methodDesc = Type.getMethodDescriptor( Type.getType( DefinedPacket.class ) ); 167 | + methodGenerator = new GeneratorAdapter( writer.visitMethod( ACC_PUBLIC, "newInstance", methodDesc, null, null ), ACC_PUBLIC, "newInstance", methodDesc ); 168 | + methodGenerator.newInstance( Type.getType( packetClass ) ); 169 | + methodGenerator.dup(); 170 | + methodGenerator.invokeConstructor( Type.getType( packetClass ), Method.getMethod( constructor ) ); 171 | + methodGenerator.returnValue(); 172 | + methodGenerator.endMethod(); 173 | + } 174 | + 175 | + private static Class loadClass(String javaName, byte[] classfile) throws CannotCompileException, NotFoundException 176 | + { 177 | + ByteArrayClassPath classPath = new ByteArrayClassPath( javaName, classfile ); 178 | + ClassPool classPool = new ClassPool(); 179 | + classPool.appendClassPath( classPath ); 180 | + return classPool.get( javaName ).toClass(); 181 | + } 182 | + 183 | + private static final AtomicInteger NEXT_ID = new AtomicInteger( 1 ); 184 | + 185 | + private static String generateName(Class packetClass) 186 | + { 187 | + return "net/md_5/bungee/protocol/constructor/generated/GeneratedPacketConstructor" + NEXT_ID.getAndIncrement() + packetClass.getSimpleName(); 188 | + } 189 | +} 190 | diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/constructor/PacketConstructor.java b/protocol/src/main/java/net/md_5/bungee/protocol/constructor/PacketConstructor.java 191 | new file mode 100644 192 | index 0000000..2f9b7b9 193 | --- /dev/null 194 | +++ b/protocol/src/main/java/net/md_5/bungee/protocol/constructor/PacketConstructor.java 195 | @@ -0,0 +1,8 @@ 196 | +package net.md_5.bungee.protocol.constructor; 197 | + 198 | +import net.md_5.bungee.protocol.DefinedPacket; 199 | + 200 | +public interface PacketConstructor 201 | +{ 202 | + public DefinedPacket newInstance() throws Exception; 203 | +} 204 | diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/constructor/ReflectivePacketConstructor.java b/protocol/src/main/java/net/md_5/bungee/protocol/constructor/ReflectivePacketConstructor.java 205 | new file mode 100644 206 | index 0000000..85ac8df 207 | --- /dev/null 208 | +++ b/protocol/src/main/java/net/md_5/bungee/protocol/constructor/ReflectivePacketConstructor.java 209 | @@ -0,0 +1,17 @@ 210 | +package net.md_5.bungee.protocol.constructor; 211 | + 212 | +import java.lang.reflect.Constructor; 213 | +import lombok.RequiredArgsConstructor; 214 | +import net.md_5.bungee.protocol.DefinedPacket; 215 | + 216 | +@RequiredArgsConstructor 217 | +public class ReflectivePacketConstructor implements PacketConstructor 218 | +{ 219 | + private final Constructor constructor; 220 | + 221 | + @Override 222 | + public DefinedPacket newInstance() throws Exception 223 | + { 224 | + return constructor.newInstance(); 225 | + } 226 | +} 227 | -- 228 | 1.9.5.msysgit.0 229 | 230 | -------------------------------------------------------------------------------- /patches/0041-Shorten-the-version-support-output-to-1.8-1.10.patch: -------------------------------------------------------------------------------- 1 | From 84f4b03e4e3f983254aba48869530b6f7064483c Mon Sep 17 00:00:00 2001 2 | From: Janmm14 3 | Date: Sat, 11 Jun 2016 15:59:47 +0200 4 | Subject: Shorten the version support output to '1.8 - 1.10' 5 | 6 | --- 7 | proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java | 2 +- 8 | 1 file changed, 1 insertion(+), 1 deletion(-) 9 | 10 | diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 11 | index 1ca4b2c..ee7c4a8 100644 12 | --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 13 | +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java 14 | @@ -243,7 +243,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection 15 | { 16 | int protocol = ( ProtocolConstants.SUPPORTED_VERSION_IDS.contains( handshake.getProtocolVersion() ) ) ? handshake.getProtocolVersion() : bungee.getProtocolVersion(); 17 | pingBack.done( new ServerPing( 18 | - new ServerPing.Protocol( bungee.getName() + " " + bungee.getGameVersion(), protocol ), 19 | + new ServerPing.Protocol( bungee.getName() + " 1.8 - 1.10", protocol ), 20 | new ServerPing.Players( listener.getMaxPlayers(), bungee.getOnlineCount(), null ), 21 | motd, BungeeCord.getInstance().config.getFaviconObject() ), 22 | null ); 23 | -- 24 | 1.9.5.msysgit.0 25 | 26 | -------------------------------------------------------------------------------- /reset-apply-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | echo "============================================================" 3 | echo " ____ _ ____ _ _ ___ _ ___ ____ " 4 | echo " |___ | |___ \/ |__] | |__] |___ " 5 | echo " | |___ |___ _/\_ | | | |___ " 6 | echo " " 7 | echo "============================================================" 8 | echo "== Patching BungeeCord to create FlexPipe and building it ==" 9 | echo "============================================================" 10 | 11 | ./reset.sh 12 | ./apply.sh 13 | ./build.sh 14 | 15 | echo "" 16 | echo "============ Done" -------------------------------------------------------------------------------- /reset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | echo "" 3 | echo "============ Setting up base BungeeCord" 4 | echo "" 5 | if [ -d "BungeeCord" ] && [[ $(ls BungeeCord) ]]; then 6 | cd BungeeCord 7 | git fetch origin 8 | git reset --hard origin/master 9 | cd ../ 10 | echo "" 11 | echo "============ BungeeCord updated to latest version" 12 | else 13 | if [ -d "BungeeCord" ]; then 14 | rm -rfd BungeeCord || rm -rf BungeeCord 15 | fi 16 | git clone https://github.com/SpigotMC/BungeeCord.git BungeeCord 17 | echo "============ BungeeCord newly cloned" 18 | fi --------------------------------------------------------------------------------