diff --git a/.gitignore b/.gitignore index ae23022..e9b9b10 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,4 @@ FlameCord-Proxy out/ .project -.settings - -pom.xml - -Waterfall-Proxy-Patches \ No newline at end of file +.settings \ No newline at end of file diff --git a/Waterfall-Proxy-Patches/0001-FlameCord-POM-Changes.patch b/Waterfall-Proxy-Patches/0001-FlameCord-POM-Changes.patch new file mode 100644 index 0000000..2da0b2f --- /dev/null +++ b/Waterfall-Proxy-Patches/0001-FlameCord-POM-Changes.patch @@ -0,0 +1,146 @@ +From 01758bf18170f703930757151d86c9440e8793da Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 17:59:15 +0800 +Subject: [PATCH] FlameCord POM Changes + + +diff --git a/flamecord/pom.xml b/flamecord/pom.xml +new file mode 100644 +index 00000000..20edd900 +--- /dev/null ++++ b/flamecord/pom.xml +@@ -0,0 +1,44 @@ ++ ++ 4.0.0 ++ ++ ++ io.github.waterfallmc ++ waterfall-parent ++ 1.19-R0.1-SNAPSHOT ++ ../pom.xml ++ ++ ++ dev._2lstudios ++ waterfall-flamecord ++ 1.19-R0.1-SNAPSHOT ++ jar ++ ++ Waterfall-FlameCord ++ FlameCord adds security essentials and new configuration options ++ ++ ++ ++ io.github.waterfallmc ++ waterfall-config ++ ${project.version} ++ compile ++ ++ ++ io.github.waterfallmc ++ waterfall-chat ++ ${project.version} ++ compile ++ ++ ++ ++ ++ ${project.name} ++ ++ ++ true ++ ${basedir}/src/main/resources ++ ++ ++ ++ +diff --git a/pom.xml b/pom.xml +index 2b544c23..d940b21c 100644 +--- a/pom.xml ++++ b/pom.xml +@@ -63,6 +63,7 @@ + query + + native ++ flamecord + + + +diff --git a/protocol/pom.xml b/protocol/pom.xml +index 93ae5952..d6222d16 100644 +--- a/protocol/pom.xml ++++ b/protocol/pom.xml +@@ -64,5 +64,14 @@ + 1.3.0 + compile + ++ ++ ++ ++ dev._2lstudios ++ waterfall-flamecord ++ ${project.version} ++ compile ++ ++ + + +diff --git a/proxy/pom.xml b/proxy/pom.xml +index 1036a2e7..10fe411d 100644 +--- a/proxy/pom.xml ++++ b/proxy/pom.xml +@@ -157,6 +157,51 @@ + runtime + + ++ ++ ++ ++ dev._2lstudios ++ waterfall-flamecord ++ ${project.version} ++ compile ++ ++ ++ io.github.waterfallmc ++ waterfall-module-cmd-alert ++ ${project.version} ++ compile ++ ++ ++ io.github.waterfallmc ++ waterfall-module-cmd-find ++ ${project.version} ++ compile ++ ++ ++ io.github.waterfallmc ++ waterfall-module-cmd-list ++ ${project.version} ++ compile ++ ++ ++ io.github.waterfallmc ++ waterfall-module-cmd-send ++ ${project.version} ++ compile ++ ++ ++ io.github.waterfallmc ++ waterfall-module-cmd-server ++ ${project.version} ++ compile ++ ++ ++ net.md-5 ++ bungeecord-module-reconnect-yaml ++ ${project.version} ++ compile ++ ++ + + + +-- +2.37.0.windows.1 + diff --git a/Waterfall-Proxy-Patches/0002-Rename-references-from-Waterfall-to-FlameCord.patch b/Waterfall-Proxy-Patches/0002-Rename-references-from-Waterfall-to-FlameCord.patch new file mode 100644 index 0000000..a5690f8 --- /dev/null +++ b/Waterfall-Proxy-Patches/0002-Rename-references-from-Waterfall-to-FlameCord.patch @@ -0,0 +1,122 @@ +From 5b40e2e22fb37fb5aa2b34d3a6b14bd91e7e72fa Mon Sep 17 00:00:00 2001 +From: Techcable +Date: Mon, 6 Jun 2016 13:47:46 -0600 +Subject: [PATCH] Rename references from Waterfall to FlameCord + + +diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml +index 688f1b995..b0b0d33eb 100644 +--- a/bootstrap/pom.xml ++++ b/bootstrap/pom.xml +@@ -37,7 +37,7 @@ + + + +- Waterfall ++ FlameCord + + + org.apache.maven.plugins +diff --git a/bootstrap/src/main/java/net/md_5/bungee/Bootstrap.java b/bootstrap/src/main/java/net/md_5/bungee/Bootstrap.java +index a4516ed96..9838f5c55 100644 +--- a/bootstrap/src/main/java/net/md_5/bungee/Bootstrap.java ++++ b/bootstrap/src/main/java/net/md_5/bungee/Bootstrap.java +@@ -7,7 +7,7 @@ public class Bootstrap + { + if ( Float.parseFloat( System.getProperty( "java.class.version" ) ) < 52.0 ) + { +- System.err.println( "*** ERROR *** Waterfall requires Java 8 or above to function! Please download and install it!" ); ++ System.err.println( "*** ERROR *** FlameCord requires Java 8 or above to function! Please download and install it!" ); + System.out.println( "You can check your Java version with the command: java -version" ); + return; + } +diff --git a/log/src/main/java/net/md_5/bungee/log/LogDispatcher.java b/log/src/main/java/net/md_5/bungee/log/LogDispatcher.java +index d703d6d24..49dce84ba 100644 +--- a/log/src/main/java/net/md_5/bungee/log/LogDispatcher.java ++++ b/log/src/main/java/net/md_5/bungee/log/LogDispatcher.java +@@ -12,7 +12,7 @@ public class LogDispatcher extends Thread + + public LogDispatcher(BungeeLogger logger) + { +- super( "Waterfall Logger Thread" ); ++ super( "FlameCord Logger Thread" ); + this.logger = logger; + } + +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +index e0fcd8fbe..8617cdc7a 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +@@ -189,7 +189,7 @@ public class BungeeCord extends ProxyServer + public BungeeCord() throws IOException + { + // Java uses ! to indicate a resource inside of a jar/zip/other container. Running Bungee from within a directory that has a ! will cause this to muck up. +- Preconditions.checkState( new File( "." ).getAbsolutePath().indexOf( '!' ) == -1, "Cannot use Waterfall in directory with ! in path." ); ++ Preconditions.checkState( new File( "." ).getAbsolutePath().indexOf( '!' ) == -1, "Cannot use FlameCord in directory with ! in path." ); + + reloadMessages(); + +@@ -545,7 +545,7 @@ public class BungeeCord extends ProxyServer + @Override + public String getName() + { +- return "Waterfall"; ++ return "FlameCord"; + } + + @Override +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCordLauncher.java b/proxy/src/main/java/net/md_5/bungee/BungeeCordLauncher.java +index 96a9998d1..463bc3d53 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCordLauncher.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCordLauncher.java +@@ -63,7 +63,7 @@ public class BungeeCordLauncher + + BungeeCord bungee = new BungeeCord(); + ProxyServer.setInstance( bungee ); +- bungee.getLogger().info( "Enabled Waterfall version " + bungee.getVersion() ); ++ bungee.getLogger().info( "Enabled FlameCord version " + bungee.getVersion() ); + bungee.start(); + + if ( !options.has( "noconsole" ) ) +diff --git a/proxy/src/main/java/net/md_5/bungee/command/CommandBungee.java b/proxy/src/main/java/net/md_5/bungee/command/CommandBungee.java +index b26035cf9..820f7b03c 100644 +--- a/proxy/src/main/java/net/md_5/bungee/command/CommandBungee.java ++++ b/proxy/src/main/java/net/md_5/bungee/command/CommandBungee.java +@@ -16,6 +16,6 @@ public class CommandBungee extends Command + @Override + public void execute(CommandSender sender, String[] args) + { +- sender.sendMessage( ChatColor.BLUE + "This server is running Waterfall version " + ProxyServer.getInstance().getVersion() + " by md_5" ); ++ sender.sendMessage( ChatColor.BLUE + "This server is running FlameCord version " + ProxyServer.getInstance().getVersion() + " by md_5" ); + } + } +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 +index 720d0c3b5..5ab4db189 100644 +--- a/proxy/src/main/java/net/md_5/bungee/command/CommandReload.java ++++ b/proxy/src/main/java/net/md_5/bungee/command/CommandReload.java +@@ -23,7 +23,7 @@ public class CommandReload extends Command + BungeeCord.getInstance().startListeners(); + BungeeCord.getInstance().getPluginManager().callEvent( new ProxyReloadEvent( sender ) ); + +- sender.sendMessage( ChatColor.BOLD.toString() + ChatColor.RED.toString() + "Waterfall has been reloaded." +- + " This is NOT advisable and you will not be supported with any issues that arise! Please restart Waterfall ASAP." ); ++ sender.sendMessage( ChatColor.BOLD.toString() + ChatColor.RED.toString() + "FlameCord has been reloaded." ++ + " This is NOT advisable and you will not be supported with any issues that arise! Please restart FlameCord ASAP." ); + } + } +diff --git a/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java b/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java +index 0dd69778b..9abae0b61 100644 +--- a/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java ++++ b/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java +@@ -227,7 +227,7 @@ public class YamlConfig implements ConfigurationAdapter + Map val = entry.getValue(); + String name = entry.getKey(); + String addr = get( "address", "localhost:25565", val ); +- String motd = ChatColor.translateAlternateColorCodes( '&', get( "motd", "&1Just another Waterfall - Forced Host", val ) ); ++ String motd = ChatColor.translateAlternateColorCodes( '&', get( "motd", "&1Just another FlameCord - Forced Host", val ) ); + boolean restricted = get( "restricted", false, val ); + SocketAddress address = Util.getAddr( addr ); + ServerInfo info = ProxyServer.getInstance().constructServerInfo( name, address, motd, restricted ); +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0003-FlameCord-rebrand.patch b/Waterfall-Proxy-Patches/0003-FlameCord-rebrand.patch new file mode 100644 index 0000000..29c2ad9 --- /dev/null +++ b/Waterfall-Proxy-Patches/0003-FlameCord-rebrand.patch @@ -0,0 +1,63 @@ +From 84cae2e73501b000c7f15d260d4ec736d7025ea3 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Tue, 8 Jun 2021 22:24:27 -0300 +Subject: [PATCH] FlameCord rebrand + + +diff --git a/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java b/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java +index da0efa36f..966d2442b 100644 +--- a/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java ++++ b/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java +@@ -68,7 +68,8 @@ public class WaterfallConfiguration extends Configuration { + YamlConfig config = new YamlConfig(new File("waterfall.yml")); + config.load(false); // Load, but no permissions + logInitialHandlerConnections = config.getBoolean( "log_initial_handler_connections", logInitialHandlerConnections ); +- gameVersion = config.getString("game_version", "").isEmpty() ? Joiner.on(", ").join(ProtocolConstants.SUPPORTED_VERSIONS) : config.getString("game_version", ""); ++ // FlameCord - Make the version look better ++ gameVersion = config.getString("game_version", "").isEmpty() ? ProtocolConstants.SUPPORTED_VERSIONS.get(0) + "-" + ProtocolConstants.SUPPORTED_VERSIONS.get(ProtocolConstants.SUPPORTED_VERSIONS.size() - 1) : config.getString("game_version", ""); + useNettyDnsResolver = config.getBoolean("use_netty_dns_resolver", useNettyDnsResolver); + // Throttling options + tabThrottle = config.getInt("throttling.tab_complete", tabThrottle); +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +index 8617cdc7a..0ed479c8d 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +@@ -551,7 +551,7 @@ public class BungeeCord extends ProxyServer + @Override + public String getVersion() + { +- return ( BungeeCord.class.getPackage().getImplementationVersion() == null ) ? "unknown" : BungeeCord.class.getPackage().getImplementationVersion(); ++ return "1.3.6"; + } + + public final void reloadMessages() +diff --git a/proxy/src/main/java/net/md_5/bungee/command/CommandBungee.java b/proxy/src/main/java/net/md_5/bungee/command/CommandBungee.java +index 820f7b03c..aeda79639 100644 +--- a/proxy/src/main/java/net/md_5/bungee/command/CommandBungee.java ++++ b/proxy/src/main/java/net/md_5/bungee/command/CommandBungee.java +@@ -16,6 +16,7 @@ public class CommandBungee extends Command + @Override + public void execute(CommandSender sender, String[] args) + { +- sender.sendMessage( ChatColor.BLUE + "This server is running FlameCord version " + ProxyServer.getInstance().getVersion() + " by md_5" ); ++ // FlameCord - Use custom version message ++ sender.sendMessage( ChatColor.translateAlternateColorCodes( '&', "&eThis server is running &c" + ProxyServer.getInstance().getName() + "&e version &a" + ProxyServer.getInstance().getVersion() + "&e by &bLinsaFTW & Sammwy&e." ) ); + } + } +diff --git a/query/src/main/java/net/md_5/bungee/query/QueryHandler.java b/query/src/main/java/net/md_5/bungee/query/QueryHandler.java +index 0c1ecfb8e..b2b199969 100644 +--- a/query/src/main/java/net/md_5/bungee/query/QueryHandler.java ++++ b/query/src/main/java/net/md_5/bungee/query/QueryHandler.java +@@ -103,7 +103,8 @@ public class QueryHandler extends SimpleChannelInboundHandler + // Waterfall start + List players = bungee.getPlayers().stream().map(ProxiedPlayer::getName).collect(Collectors.toList()); + +- ProxyQueryEvent event = new ProxyQueryEvent(listener, new QueryResult(listener.getMotd(), "SMP", "Waterfall_Proxy", ++ // FlameCord - Rebrand ++ ProxyQueryEvent event = new ProxyQueryEvent(listener, new QueryResult(listener.getMotd(), "SMP", "FlameCord_Proxy", + bungee.getOnlineCount(), listener.getMaxPlayers(), listener.getHost().getPort(), + listener.getHost().getHostString(), "MINECRAFT", players, bungee.getGameVersion())); + QueryResult result = bungee.getPluginManager().callEvent(event).getResult(); +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0004-1.7.x-support.patch b/Waterfall-Proxy-Patches/0004-1.7.x-support.patch new file mode 100644 index 0000000..ceb18cf --- /dev/null +++ b/Waterfall-Proxy-Patches/0004-1.7.x-support.patch @@ -0,0 +1,1620 @@ +From c6b956bee70e03b5983a6c83a083ba9426258667 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Thu, 30 Sep 2021 19:54:33 -0300 +Subject: [PATCH] 1.7.x support + + +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java b/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java +index 59f2cb73d..f33ce913a 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java +@@ -415,4 +415,36 @@ public abstract class DefinedPacket + return 0; + } + // Waterfall end ++ ++ // FlameCord start - 1.7.x support ++ public static byte[] v17readArray(ByteBuf buf) ++ { ++ // Read in a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for Forge only) ++ // No vanilla packet should give a 3 byte packet, this method will still retain vanilla behaviour. ++ int len = readVarShort( buf ); ++ ++ // (Integer.MAX_VALUE & 0x1FFF9A ) = 2097050 - Forge's current upper limit ++ Preconditions.checkArgument( len <= ( Integer.MAX_VALUE & 0x1FFF9A ), "Cannot receive array longer than 2097050 (got %s bytes)", len ); ++ ++ byte[] ret = new byte[ len ]; ++ buf.readBytes( ret ); ++ return ret; ++ } ++ ++ public static void v17writeArray(byte[] b, ByteBuf buf, boolean allowExtended) ++ { ++ // (Integer.MAX_VALUE & 0x1FFF9A ) = 2097050 - Forge's current upper limit ++ if ( allowExtended ) ++ { ++ Preconditions.checkArgument( b.length <= ( Integer.MAX_VALUE & 0x1FFF9A ), "Cannot send array longer than 2097050 (got %s bytes)", b.length ); ++ } else ++ { ++ Preconditions.checkArgument( b.length <= Short.MAX_VALUE, "Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.length ); ++ } ++ // Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for Forge only) ++ // No vanilla packet should give a 3 byte packet, this method will still retain vanilla behaviour. ++ writeVarShort( buf, b.length ); ++ buf.writeBytes( b ); ++ } ++ // FlameCord end - 1.7.x support + } +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 +index 2357f3801..c9e45b915 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java +@@ -66,7 +66,7 @@ public enum Protocol + TO_SERVER.registerPacket( + Handshake.class, + Handshake::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x00 ) ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x00 ) // FlameCord - 1.7.x support + ); + } + }, +@@ -78,7 +78,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + KeepAlive.class, + KeepAlive::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x00 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x00 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x1F ), + map( ProtocolConstants.MINECRAFT_1_13, 0x21 ), + map( ProtocolConstants.MINECRAFT_1_14, 0x20 ), +@@ -93,7 +93,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + Login.class, + Login::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x01 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x01 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x23 ), + map( ProtocolConstants.MINECRAFT_1_13, 0x25 ), + map( ProtocolConstants.MINECRAFT_1_15, 0x26 ), +@@ -106,7 +106,7 @@ public enum Protocol + ); + TO_CLIENT.registerPacket( Chat.class, + Chat::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x02 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x02 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x0F ), + map( ProtocolConstants.MINECRAFT_1_13, 0x0E ), + map( ProtocolConstants.MINECRAFT_1_15, 0x0F ), +@@ -117,7 +117,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + Respawn.class, + Respawn::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x07 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x07 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x33 ), + map( ProtocolConstants.MINECRAFT_1_12, 0x34 ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x35 ), +@@ -144,20 +144,20 @@ public enum Protocol + TO_CLIENT.registerPacket( + EntityEffect.class, + EntityEffect::new, +- map(ProtocolConstants.MINECRAFT_1_8, 0x1D), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x1D ), // FlameCord - 1.7.x support + map(ProtocolConstants.MINECRAFT_1_9, -1) + ); + TO_CLIENT.registerPacket( + EntityRemoveEffect.class, + EntityRemoveEffect::new, +- map(ProtocolConstants.MINECRAFT_1_8, 0x1E), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x1E ), // FlameCord - 1.7.x support + map(ProtocolConstants.MINECRAFT_1_9, -1) + ); + // Waterfall end + TO_CLIENT.registerPacket( + PlayerListItem.class, // PlayerInfo + PlayerListItem::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x38 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x38 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x2D ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x2E ), + map( ProtocolConstants.MINECRAFT_1_13, 0x30 ), +@@ -173,7 +173,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + TabCompleteResponse.class, + TabCompleteResponse::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x3A ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x3A ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x0E ), + map( ProtocolConstants.MINECRAFT_1_13, 0x10 ), + map( ProtocolConstants.MINECRAFT_1_15, 0x11 ), +@@ -186,7 +186,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + ScoreboardObjective.class, + ScoreboardObjective::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x3B ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x3B ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x3F ), + map( ProtocolConstants.MINECRAFT_1_12, 0x41 ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x42 ), +@@ -200,7 +200,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + ScoreboardScore.class, + ScoreboardScore::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x3C ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x3C ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x42 ), + map( ProtocolConstants.MINECRAFT_1_12, 0x44 ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x45 ), +@@ -214,7 +214,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + ScoreboardDisplay.class, + ScoreboardDisplay::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x3D ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x3D ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x38 ), + map( ProtocolConstants.MINECRAFT_1_12, 0x3A ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x3B ), +@@ -228,7 +228,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + Team.class, + Team::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x3E ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x3E ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x41 ), + map( ProtocolConstants.MINECRAFT_1_12, 0x43 ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x44 ), +@@ -242,7 +242,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + PluginMessage.class, + PluginMessage::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x3F ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x3F ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x18 ), + map( ProtocolConstants.MINECRAFT_1_13, 0x19 ), + map( ProtocolConstants.MINECRAFT_1_14, 0x18 ), +@@ -257,7 +257,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + Kick.class, + Kick::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x40 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x40 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x1A ), + map( ProtocolConstants.MINECRAFT_1_13, 0x1B ), + map( ProtocolConstants.MINECRAFT_1_14, 0x1A ), +@@ -272,7 +272,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + Title.class, + Title::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x45 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x45 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_12, 0x47 ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x48 ), + map( ProtocolConstants.MINECRAFT_1_13, 0x4B ), +@@ -317,7 +317,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + PlayerListHeaderFooter.class, + PlayerListHeaderFooter::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x47 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x47 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x48 ), + map( ProtocolConstants.MINECRAFT_1_9_4, 0x47 ), + map( ProtocolConstants.MINECRAFT_1_12, 0x49 ), +@@ -335,7 +335,7 @@ public enum Protocol + TO_CLIENT.registerPacket( + EntityStatus.class, + EntityStatus::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x1A ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x1A ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x1B ), + map( ProtocolConstants.MINECRAFT_1_13, 0x1C ), + map( ProtocolConstants.MINECRAFT_1_14, 0x1B ), +@@ -401,7 +401,7 @@ public enum Protocol + TO_SERVER.registerPacket( + KeepAlive.class, + KeepAlive::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x00 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x00 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x0B ), + map( ProtocolConstants.MINECRAFT_1_12, 0x0C ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x0B ), +@@ -415,7 +415,7 @@ public enum Protocol + ); + TO_SERVER.registerPacket( Chat.class, + Chat::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x01 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x01 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x02 ), + map( ProtocolConstants.MINECRAFT_1_12, 0x03 ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x02 ), +@@ -437,7 +437,7 @@ public enum Protocol + TO_SERVER.registerPacket( + TabCompleteRequest.class, + TabCompleteRequest::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x14 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x14 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x01 ), + map( ProtocolConstants.MINECRAFT_1_12, 0x02 ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x01 ), +@@ -450,7 +450,7 @@ public enum Protocol + TO_SERVER.registerPacket( + ClientSettings.class, + ClientSettings::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x15 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x15 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x04 ), + map( ProtocolConstants.MINECRAFT_1_12, 0x05 ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x04 ), +@@ -462,7 +462,7 @@ public enum Protocol + TO_SERVER.registerPacket( + PluginMessage.class, + PluginMessage::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x17 ), ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x17 ), // FlameCord - 1.7.x support + map( ProtocolConstants.MINECRAFT_1_9, 0x09 ), + map( ProtocolConstants.MINECRAFT_1_12, 0x0A ), + map( ProtocolConstants.MINECRAFT_1_12_1, 0x09 ), +@@ -483,23 +483,23 @@ public enum Protocol + TO_CLIENT.registerPacket( + StatusResponse.class, + StatusResponse::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x00 ) ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x00 ) // FlameCord - 1.7.x support + ); + TO_CLIENT.registerPacket( + PingPacket.class, + PingPacket::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x01 ) ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x01 ) // FlameCord - 1.7.x support + ); + + TO_SERVER.registerPacket( + StatusRequest.class, + StatusRequest::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x00 ) ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x00 ) // FlameCord - 1.7.x support + ); + TO_SERVER.registerPacket( + PingPacket.class, + PingPacket::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x01 ) ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x01 ) // FlameCord - 1.7.x support + ); + } + }, +@@ -511,22 +511,22 @@ public enum Protocol + TO_CLIENT.registerPacket( + Kick.class, + Kick::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x00 ) ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x00 ) // FlameCord - 1.7.x support + ); + TO_CLIENT.registerPacket( + EncryptionRequest.class, + EncryptionRequest::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x01 ) ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x01 ) // FlameCord - 1.7.x support + ); + TO_CLIENT.registerPacket( + LoginSuccess.class, + LoginSuccess::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x02 ) ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x02 ) // FlameCord - 1.7.x support + ); + TO_CLIENT.registerPacket( + SetCompression.class, + SetCompression::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x03 ) ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x03 ) // FlameCord - 1.7.x support + ); + TO_CLIENT.registerPacket( + LoginPayloadRequest.class, +@@ -537,12 +537,12 @@ public enum Protocol + TO_SERVER.registerPacket( + LoginRequest.class, + LoginRequest::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x00 ) ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x00 ) // FlameCord - 1.7.x support + ); + TO_SERVER.registerPacket( + EncryptionResponse.class, + EncryptionResponse::new, +- map( ProtocolConstants.MINECRAFT_1_8, 0x01 ) ++ map( ProtocolConstants.MINECRAFT_1_7_2, 0x01 ) // FlameCord - 1.7.x support + ); + TO_SERVER.registerPacket( + LoginPayloadResponse.class, +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 +index 85f1b0b46..b5b3b2890 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java +@@ -7,6 +7,10 @@ public class ProtocolConstants + { + + private static final boolean SNAPSHOT_SUPPORT = Boolean.getBoolean( "net.md_5.bungee.protocol.snapshot" ); ++ // FlameCord start - 1.7.x support ++ public static final int MINECRAFT_1_7_2 = 4; ++ public static final int MINECRAFT_1_7_6 = 5; ++ // FlameCord end - 1.7.x support + public static final int MINECRAFT_1_8 = 47; + public static final int MINECRAFT_1_9 = 107; + public static final int MINECRAFT_1_9_1 = 108; +@@ -47,6 +51,7 @@ public class ProtocolConstants + static + { + ImmutableList.Builder supportedVersions = ImmutableList.builder().add( ++ "1.7.x", // FlameCord - 1.7.x support + "1.8.x", + "1.9.x", + "1.10.x", +@@ -61,6 +66,10 @@ public class ProtocolConstants + "1.19.x" + ); + ImmutableList.Builder supportedVersionIds = ImmutableList.builder().add( ++ // FlameCord start - 1.7.x support ++ ProtocolConstants.MINECRAFT_1_7_2, ++ ProtocolConstants.MINECRAFT_1_7_6, ++ // FlameCord end - 1.7.x support + ProtocolConstants.MINECRAFT_1_8, + ProtocolConstants.MINECRAFT_1_9, + ProtocolConstants.MINECRAFT_1_9_1, +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java +index dc9866097..230018aea 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java +@@ -41,6 +41,7 @@ public class Chat extends DefinedPacket + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + message = readString( buf, ( direction == ProtocolConstants.Direction.TO_CLIENT ) ? 262144 : ( protocolVersion >= ProtocolConstants.MINECRAFT_1_11 ? 256 : 100 ) ); ++ if ( ProtocolConstants.isAfterOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_8 ) ) // FlameCord - 1.7.x support + if ( direction == ProtocolConstants.Direction.TO_CLIENT ) + { + position = buf.readByte(); +@@ -55,6 +56,7 @@ public class Chat extends DefinedPacket + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + writeString( message, buf, ( direction == ProtocolConstants.Direction.TO_CLIENT ) ? 262144 : ( protocolVersion >= ProtocolConstants.MINECRAFT_1_11 ? 256 : 100 ) ); ++ if ( ProtocolConstants.isAfterOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_8 ) ) // FlameCord - 1.7.x support + if ( direction == ProtocolConstants.Direction.TO_CLIENT ) + { + buf.writeByte( position ); +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientSettings.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientSettings.java +index d7d4e6ab4..570a89f0a 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientSettings.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientSettings.java +@@ -34,6 +34,12 @@ public class ClientSettings extends DefinedPacket + chatFlags = protocolVersion >= ProtocolConstants.MINECRAFT_1_9 ? DefinedPacket.readVarInt( buf ) : buf.readUnsignedByte(); + chatColours = buf.readBoolean(); + skinParts = buf.readByte(); ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ difficulty = buf.readByte(); ++ } ++ // FlameCord end - 1.7.x support + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_9 ) + { + mainHand = DefinedPacket.readVarInt( buf ); +@@ -51,6 +57,19 @@ public class ClientSettings extends DefinedPacket + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ writeString( locale, buf ); ++ buf.writeByte( viewDistance ); ++ buf.writeByte( chatFlags ); ++ buf.writeBoolean( chatColours ); ++ buf.writeByte( skinParts ); ++ buf.writeByte( difficulty ); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + writeString( locale, buf ); + buf.writeByte( viewDistance ); + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_9 ) +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java +index a29524ca8..e78519964 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java +@@ -23,6 +23,16 @@ public class EncryptionRequest extends DefinedPacket + @Override + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ serverId = readString( buf ); ++ publicKey = v17readArray( buf ); ++ verifyToken = v17readArray( buf ); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + serverId = readString( buf ); + publicKey = readArray( buf ); + verifyToken = readArray( buf ); +@@ -31,6 +41,16 @@ public class EncryptionRequest extends DefinedPacket + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ writeString( serverId, buf ); ++ v17writeArray( publicKey, buf, false ); ++ v17writeArray( verifyToken, buf, false ); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + writeString( serverId, buf ); + writeArray( publicKey, buf ); + writeArray( verifyToken, buf ); +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java +index 545eec72f..eacdbe78a 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java +@@ -22,6 +22,15 @@ public class EncryptionResponse extends DefinedPacket + + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ sharedSecret = v17readArray( buf ); ++ verifyToken = v17readArray( buf ); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + sharedSecret = readArray( buf, 128 ); + if ( protocolVersion < ProtocolConstants.MINECRAFT_1_19 || protocolVersion >= ProtocolConstants.MINECRAFT_1_19_3 || buf.readBoolean() ) + { +@@ -35,6 +44,15 @@ public class EncryptionResponse extends DefinedPacket + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ v17writeArray( sharedSecret, buf, false ); ++ v17writeArray( verifyToken, buf, false ); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + writeArray( sharedSecret, buf ); + if ( verifyToken != null ) + { +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityEffect.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityEffect.java +index d11a9ea9d..0ed78a8c4 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityEffect.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityEffect.java +@@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode; + import lombok.NoArgsConstructor; + import net.md_5.bungee.protocol.AbstractPacketHandler; + import net.md_5.bungee.protocol.DefinedPacket; ++import net.md_5.bungee.protocol.ProtocolConstants; + + @Data + @NoArgsConstructor +@@ -21,20 +22,41 @@ public class EntityEffect extends DefinedPacket { + private boolean hideParticles; + + @Override +- public void read(ByteBuf buf) { +- this.entityId = readVarInt(buf); ++ public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { ++ this.entityId = protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ? readVarInt(buf) : buf.readInt(); // FlameCord - 1.7.x support + this.effectId = buf.readUnsignedByte(); + this.amplifier = buf.readUnsignedByte(); +- this.duration = readVarInt(buf); +- this.hideParticles = buf.readBoolean(); ++ this.duration = protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ? readVarInt(buf) : buf.readShort(); // FlameCord - 1.7.x support ++ // FlameCord start - 1.7.x support ++ if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ) ++ { ++ this.hideParticles = buf.readBoolean(); ++ } ++ // FlameCord end - 1.7.x support + } + + @Override +- public void write(ByteBuf buf) { +- writeVarInt(this.entityId, buf); ++ public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { ++ // FlameCord start - 1.7.x support ++ if (protocolVersion >= ProtocolConstants.MINECRAFT_1_8) ++ { ++ writeVarInt(this.entityId, buf); ++ } else ++ { ++ buf.writeInt(effectId); ++ } ++ // FlameCord end - 1.7.x support + buf.writeByte(this.effectId); + buf.writeByte(this.amplifier); +- writeVarInt(this.duration, buf); ++ // FlameCord start - 1.7.x support ++ if (protocolVersion >= ProtocolConstants.MINECRAFT_1_8) ++ { ++ writeVarInt(this.duration, buf); ++ } else ++ { ++ buf.writeShort(duration); ++ } ++ // FlameCord end - 1.7.x support + buf.writeBoolean(this.hideParticles); + } + +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityRemoveEffect.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityRemoveEffect.java +index 7ed2dc3ab..435b85789 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityRemoveEffect.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityRemoveEffect.java +@@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode; + import lombok.NoArgsConstructor; + import net.md_5.bungee.protocol.AbstractPacketHandler; + import net.md_5.bungee.protocol.DefinedPacket; ++import net.md_5.bungee.protocol.ProtocolConstants; + + @Data + @NoArgsConstructor +@@ -18,14 +19,22 @@ public class EntityRemoveEffect extends DefinedPacket { + private int effectId; + + @Override +- public void read(ByteBuf buf) { +- this.entityId = readVarInt(buf); ++ public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { ++ this.entityId = protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ? readVarInt(buf) : buf.readInt(); // FlameCord - 1.7.x support + this.effectId = buf.readUnsignedByte(); + } + + @Override +- public void write(ByteBuf buf) { +- writeVarInt(this.entityId, buf); ++ public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { ++ // FlameCord start - 1.7.x support ++ if (protocolVersion >= ProtocolConstants.MINECRAFT_1_8) ++ { ++ writeVarInt(this.entityId, buf); ++ } else ++ { ++ buf.writeInt(entityId); ++ } ++ // FlameCord end - 1.7.x support + buf.writeByte(effectId); + } + +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java +index b004bc416..a8c3e7736 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java +@@ -21,12 +21,28 @@ public class KeepAlive extends DefinedPacket + @Override + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ randomId = buf.readInt(); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + randomId = ( protocolVersion >= ProtocolConstants.MINECRAFT_1_12_2 ) ? buf.readLong() : readVarInt( buf ); + } + + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ buf.writeInt((int) randomId); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_12_2 ) + { + buf.writeLong( randomId ); +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginSuccess.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginSuccess.java +index 07fb3d79a..7d03cd10a 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginSuccess.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginSuccess.java +@@ -25,6 +25,11 @@ public class LoginSuccess extends DefinedPacket + @Override + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( protocolVersion <= ProtocolConstants.MINECRAFT_1_7_2 ) { ++ uuid = readUndashedUUID( buf ); ++ } else ++ // FlameCord end - 1.7.x support + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16 ) + { + uuid = readUUID( buf ); +@@ -42,6 +47,11 @@ public class LoginSuccess extends DefinedPacket + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( protocolVersion <= ProtocolConstants.MINECRAFT_1_7_2 ) { ++ writeUndashedUUID( uuid.toString(), buf ); ++ } else ++ // FlameCord end - 1.7.x support + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16 ) + { + writeUUID( uuid, buf ); +@@ -61,4 +71,14 @@ public class LoginSuccess extends DefinedPacket + { + handler.handle( this ); + } ++ ++ // FlameCord start - 1.7.x support ++ private static UUID readUndashedUUID(ByteBuf buf) { ++ return UUID.fromString( new StringBuilder( readString( buf ) ).insert( 20, '-' ).insert( 16, '-' ).insert( 12, '-' ).insert( 8, '-' ).toString() ); ++ } ++ ++ private static void writeUndashedUUID(String uuid, ByteBuf buf) { ++ writeString( new StringBuilder( 32 ).append( uuid, 0, 8 ).append( uuid, 9, 13 ).append( uuid, 14, 18 ).append( uuid, 19, 23 ).append( uuid, 24, 36 ).toString(), buf ); ++ } ++ // FlameCord end - 1.7.x support + } +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java +index 9b9c412da..4c50d2378 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java +@@ -23,6 +23,18 @@ public class PlayerListItem extends DefinedPacket + @Override + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ items = new Item[ 1 ]; ++ Item item = items[ 0 ] = new Item(); ++ item.displayName = item.username = readString( buf ); ++ action = !buf.readBoolean() ? Action.REMOVE_PLAYER : Action.ADD_PLAYER; ++ item.ping = (int) buf.readShort(); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + action = Action.values()[DefinedPacket.readVarInt( buf )]; + items = new Item[ DefinedPacket.readVarInt( buf ) ]; + for ( int i = 0; i < items.length; i++ ) +@@ -63,6 +75,17 @@ public class PlayerListItem extends DefinedPacket + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ Item item = items[0]; // Only one at a time ++ writeString( item.displayName, buf ); // TODO: Server unique only! ++ buf.writeBoolean( action != Action.REMOVE_PLAYER ); ++ buf.writeShort( item.ping ); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + DefinedPacket.writeVarInt( action.ordinal(), buf ); + DefinedPacket.writeVarInt( items.length, buf ); + for ( Item item : items ) +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java +index 91f71c095..57a82d29e 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java +@@ -73,6 +73,15 @@ public class PluginMessage extends DefinedPacket + @Override + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ tag = readString( buf ); ++ data = v17readArray( buf ); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + tag = ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 ) ? MODERNISE.apply( readString( buf ) ) : readString( buf, 20 ); + int maxSize = ( direction == ProtocolConstants.Direction.TO_SERVER ) ? Short.MAX_VALUE : 0x100000; + Preconditions.checkArgument( buf.readableBytes() <= maxSize, "Payload too large" ); +@@ -83,6 +92,15 @@ public class PluginMessage extends DefinedPacket + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ writeString( tag, buf ); ++ v17writeArray( data, buf, allowExtendedPacket ); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + writeString( ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 ) ? MODERNISE.apply( tag ) : tag, buf ); + buf.writeBytes( data ); + } +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java +index 3c7905d54..75b371e9c 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java +@@ -28,6 +28,16 @@ public class ScoreboardObjective extends DefinedPacket + @Override + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ name = readString( buf ); ++ value = readString( buf ); ++ action = buf.readByte(); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + name = readString( buf ); + action = buf.readByte(); + if ( action == 0 || action == 2 ) +@@ -46,6 +56,16 @@ public class ScoreboardObjective extends DefinedPacket + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ writeString( name, buf ); ++ writeString( value, buf ); ++ buf.writeByte( action ); ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + writeString( name, buf ); + buf.writeByte( action ); + if ( action == 0 || action == 2 ) +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java +index 0b27fc86b..a812441d9 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java +@@ -27,6 +27,20 @@ public class ScoreboardScore extends DefinedPacket + @Override + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ itemName = readString( buf ); ++ action = buf.readByte(); ++ if ( action != 1 ) ++ { ++ scoreName = readString( buf ); ++ value = buf.readInt(); ++ } ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + itemName = readString( buf ); + action = buf.readByte(); + scoreName = readString( buf ); +@@ -39,6 +53,20 @@ public class ScoreboardScore extends DefinedPacket + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ writeString( itemName, buf ); ++ buf.writeByte( action ); ++ if ( action != 1 ) ++ { ++ writeString( scoreName, buf ); ++ buf.writeInt( value ); ++ } ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + writeString( itemName, buf ); + buf.writeByte( action ); + writeString( scoreName, buf ); +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java +index 80e4f85af..cab28b999 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java +@@ -43,6 +43,7 @@ public class TabCompleteRequest extends DefinedPacket + } + cursor = readString( buf, ( protocolVersion > ProtocolConstants.MINECRAFT_1_13 ? 32500 : ( protocolVersion == ProtocolConstants.MINECRAFT_1_13 ? 256 : 32767 ) ) ); + ++ if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ) // FlameCord - 1.7.x support + if ( protocolVersion < ProtocolConstants.MINECRAFT_1_13 ) + { + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_9 ) +@@ -66,6 +67,7 @@ public class TabCompleteRequest extends DefinedPacket + } + writeString( cursor, buf ); + ++ if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ) // FlameCord - 1.7.x support + if ( protocolVersion < ProtocolConstants.MINECRAFT_1_13 ) + { + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_9 ) +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 +index a5555f6af..1eb458b61 100644 +--- 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 +@@ -55,12 +55,22 @@ public class Team extends DefinedPacket + suffix = readString( buf ); + } + friendlyFire = buf.readByte(); +- nameTagVisibility = readString( buf ); ++ // FlameCord start - 1.7.x support ++ if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ) ++ { ++ nameTagVisibility = readString( buf ); ++ } ++ // FlameCord end - 1.7.x support + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_9 ) + { + collisionRule = readString( buf ); + } +- color = ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 ) ? readVarInt( buf ) : buf.readByte(); ++ // FlameCord start - 1.7.x support ++ if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ) ++ { ++ color = ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 ) ? readVarInt( buf ) : buf.readByte(); ++ } ++ // FlameCord end - 1.7.x support + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 ) + { + prefix = readString( buf ); +@@ -69,7 +79,7 @@ public class Team extends DefinedPacket + } + if ( mode == 0 || mode == 3 || mode == 4 ) + { +- int len = readVarInt( buf ); ++ int len = protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ? readVarInt( buf ) : buf.readShort(); // FlameCord - 1.7.x support + players = new String[ len ]; + for ( int i = 0; i < len; i++ ) + { +@@ -81,6 +91,30 @@ public class Team extends DefinedPacket + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isBeforeOrEq( protocolVersion, ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ writeString( name, buf ); ++ buf.writeByte( mode ); ++ if ( mode == 0 || mode == 2 ) ++ { ++ writeString( displayName, buf ); ++ writeString( prefix, buf ); ++ writeString( suffix, buf ); ++ buf.writeByte( friendlyFire ); ++ } ++ if ( mode == 0 || mode == 3 || mode == 4 ) ++ { ++ buf.writeShort( players.length ); ++ for ( String player : players ) ++ { ++ writeString( player, buf ); ++ } ++ } ++ return; ++ } ++ // FlameCord end - 1.7.x support ++ + writeString( name, buf ); + buf.writeByte( mode ); + if ( mode == 0 || mode == 2 ) +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +index ca1dad0e5..e11e30402 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +@@ -171,6 +171,14 @@ public class BungeeCord extends ProxyServer + .registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() ) + .registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer() ) + .registerTypeAdapter( Favicon.class, Favicon.getFaviconTypeAdapter() ).create(); ++ // FlameCord start - 1.7.x support ++ public final Gson gsonLegacy = new GsonBuilder() ++ .registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ) ++ .registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ) ++ .registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ) ++ .registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer( ProtocolConstants.MINECRAFT_1_7_2 ) ) ++ .registerTypeAdapter( Favicon.class, Favicon.getFaviconTypeAdapter() ).create(); ++ // FlameCord end - 1.7.x support + @Getter + private ConnectionThrottle connectionThrottle; + private final ModuleManager moduleManager = new ModuleManager(); +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeTitle.java b/proxy/src/main/java/net/md_5/bungee/BungeeTitle.java +index 1d377a655..0ea5f90b4 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeTitle.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeTitle.java +@@ -183,6 +183,7 @@ public class BungeeTitle implements Title + @Override + public Title send(ProxiedPlayer player) + { ++ if ( ProtocolConstants.isBeforeOrEq( player.getPendingConnection().getVersion(), ProtocolConstants.MINECRAFT_1_7_6 ) ) return this; // FlameCord - 1.7.x support + sendPacket( player, clear ); + sendPacket( player, reset ); + sendPacket( player, times ); +diff --git a/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java b/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java +index 491cf1a16..6cd8ec3b4 100644 +--- a/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java ++++ b/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java +@@ -10,9 +10,23 @@ import com.google.gson.JsonSerializer; + import java.lang.reflect.Type; + import java.util.UUID; + import net.md_5.bungee.api.ServerPing; ++import net.md_5.bungee.protocol.ProtocolConstants; + + public class PlayerInfoSerializer implements JsonSerializer, JsonDeserializer + { ++ // FlameCord start - 1.7.x support ++ private final int protocol; ++ ++ public PlayerInfoSerializer() ++ { ++ this.protocol = ProtocolConstants.MINECRAFT_1_7_6; ++ } ++ ++ public PlayerInfoSerializer(int protocol) ++ { ++ this.protocol = protocol; ++ } ++ // FlameCord end - 1.7.x support + + @Override + public ServerPing.PlayerInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException +@@ -20,7 +34,7 @@ public class PlayerInfoSerializer implements JsonSerializer= ProtocolConstants.MINECRAFT_1_13 ? "minecraft:brand" : "MC|Brand", brand, handshakeHandler.isServerForge() ) ); +- brand.release(); ++ // FlameCord start - 1.7.x support ++ String brandString = bungee.getName() + " (" + bungee.getVersion() + ")"; ++ ++ if ( ProtocolConstants.isBeforeOrEq( user.getPendingConnection().getVersion(), ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ user.unsafe().sendPacket( new PluginMessage( "MC|Brand", brandString.getBytes( StandardCharsets.UTF_8 ), handshakeHandler.isServerForge() ) ); ++ } else ++ { ++ ByteBuf brand = ByteBufAllocator.DEFAULT.heapBuffer(); ++ DefinedPacket.writeString( bungee.getName() + " (" + bungee.getVersion() + ")", brand ); ++ user.unsafe().sendPacket( new PluginMessage( user.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_13 ? "minecraft:brand" : "MC|Brand", brand, handshakeHandler.isServerForge() ) ); ++ brand.release(); ++ } ++ // FlameCord end - 1.7.x support + } + + user.setDimension( login.getDimension() ); +@@ -296,7 +306,7 @@ public class ServerConnector extends PacketHandler + if ( !user.isDisableEntityMetadataRewrite() ) { // Waterfall + for ( Objective objective : serverScoreboard.getObjectives() ) + { +- user.unsafe().sendPacket( new ScoreboardObjective( objective.getName(), objective.getValue(), ScoreboardObjective.HealthDisplay.fromString( objective.getType() ), (byte) 1 ) ); ++ user.unsafe().sendPacket( new ScoreboardObjective( objective.getName(), objective.getValue(), objective.getType() == null ? null : ScoreboardObjective.HealthDisplay.fromString(objective.getType()), (byte) 1 ) ); // FlameCord - 1.7 support + } + for ( Score score : serverScoreboard.getScores() ) + { +@@ -466,7 +476,13 @@ public class ServerConnector extends PacketHandler + if ( pluginMessage.getTag().equals( ForgeConstants.FML_HANDSHAKE_TAG ) || pluginMessage.getTag().equals( ForgeConstants.FORGE_REGISTER ) ) + { + this.handshakeHandler.handle( pluginMessage ); +- ++ // FlameCord start - 1.7.x support ++ if ( user.getForgeClientHandler().checkUserOutdated() ) ++ { ++ ch.close(); ++ user.getPendingConnects().remove(target); ++ } ++ // FlameCord end - 1.7.x support + // We send the message as part of the handler, so don't send it here. + throw CancelSendSignal.INSTANCE; + } +diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +index b44d13e59..2d72e26ea 100644 +--- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java ++++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +@@ -194,6 +194,8 @@ public final class UserConnection implements ProxiedPlayer + public void setDisplayName(String name) + { + Preconditions.checkNotNull( name, "displayName" ); ++ // Its "spigot" responsability to choose wether to support 1.7 or not. ++ //Preconditions.checkArgument( name.length() <= 16, "Display name cannot be longer than 16 characters" ); // FlameCord - 1.7.x support + displayName = name; + } + +@@ -524,7 +526,7 @@ public final class UserConnection implements ProxiedPlayer + // transform score components + message = ChatComponentTransformer.getInstance().transform( this, true, message ); + +- if ( position == ChatMessageType.ACTION_BAR && getPendingConnection().getVersion() < ProtocolConstants.MINECRAFT_1_17 ) ++ if ( position == ChatMessageType.ACTION_BAR && getPendingConnection().getVersion() < ProtocolConstants.MINECRAFT_1_17 && getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_8 ) // FlameCord - 1.7.x support + { + // Versions older than 1.11 cannot send the Action bar with the new JSON formattings + // Fix by converting to a legacy message, see https://bugs.mojang.com/browse/MC-119145 +@@ -720,6 +722,7 @@ public final class UserConnection implements ProxiedPlayer + @Override + public void setTabHeader(BaseComponent header, BaseComponent footer) + { ++ if ( ProtocolConstants.isBeforeOrEq( pendingConnection.getVersion(), ProtocolConstants.MINECRAFT_1_7_6 ) ) return; // FlameCord + header = ChatComponentTransformer.getInstance().transform( this, true, header )[0]; + footer = ChatComponentTransformer.getInstance().transform( this, true, footer )[0]; + +@@ -732,6 +735,7 @@ public final class UserConnection implements ProxiedPlayer + @Override + public void setTabHeader(BaseComponent[] header, BaseComponent[] footer) + { ++ if ( ProtocolConstants.isBeforeOrEq( pendingConnection.getVersion(), ProtocolConstants.MINECRAFT_1_7_6 ) ) return; // FlameCord + header = ChatComponentTransformer.getInstance().transform( this, true, header ); + footer = ChatComponentTransformer.getInstance().transform( this, true, footer ); + +@@ -761,6 +765,7 @@ public final class UserConnection implements ProxiedPlayer + + public void setCompressionThreshold(int compressionThreshold) + { ++ if ( ProtocolConstants.isBeforeOrEq( pendingConnection.getVersion(), ProtocolConstants.MINECRAFT_1_7_6 ) ) return; // FlameCord + if ( !ch.isClosing() && this.compressionThreshold == -1 && compressionThreshold >= 0 ) + { + this.compressionThreshold = compressionThreshold; +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 +index 89b98b360..0ac22ad38 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java +@@ -18,6 +18,7 @@ import io.netty.buffer.Unpooled; + import io.netty.channel.unix.DomainSocketAddress; + import java.io.DataInput; + import java.net.InetSocketAddress; ++import java.nio.charset.StandardCharsets; + import java.util.ArrayList; + import java.util.HashMap; // Waterfall + import java.util.List; +@@ -201,7 +202,7 @@ public class DownstreamBridge extends PacketHandler + switch ( objective.getAction() ) + { + case 0: +- serverScoreboard.addObjective( new Objective( objective.getName(), objective.getValue(), objective.getType().toString() ) ); ++ serverScoreboard.addObjective( new Objective( objective.getName(), objective.getValue(), objective.getType() != null ? objective.getType().toString() : null) ); // FlameCord - 1.7.x support + break; + case 1: + serverScoreboard.removeObjective( objective.getName() ); +@@ -211,7 +212,7 @@ public class DownstreamBridge extends PacketHandler + if ( oldObjective != null ) + { + oldObjective.setValue( objective.getValue() ); +- oldObjective.setType( objective.getType().toString() ); ++ oldObjective.setType( objective.getType() != null ? objective.getType().toString() : null ); // FlameCord - 1.7.x support + } + break; + default: +@@ -309,16 +310,28 @@ public class DownstreamBridge extends PacketHandler + + if ( pluginMessage.getTag().equals( con.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_13 ? "minecraft:brand" : "MC|Brand" ) ) + { +- ByteBuf brand = Unpooled.wrappedBuffer( pluginMessage.getData() ); +- String serverBrand = DefinedPacket.readString( brand ); +- brand.release(); +- +- Preconditions.checkState( !serverBrand.contains( bungee.getName() ), "Cannot connect proxy to itself!" ); +- +- brand = ByteBufAllocator.DEFAULT.heapBuffer(); +- DefinedPacket.writeString( bungee.getName() + " <- " + serverBrand, brand ); // Waterfall +- pluginMessage.setData( brand ); +- brand.release(); ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isAfterOrEq( con.getPendingConnection().getVersion(), ProtocolConstants.MINECRAFT_1_8 ) ) ++ { ++ try ++ { ++ ByteBuf brand = Unpooled.wrappedBuffer(pluginMessage.getData()); ++ String serverBrand = DefinedPacket.readString(brand); ++ brand.release(); ++ brand = ByteBufAllocator.DEFAULT.heapBuffer(); ++ DefinedPacket.writeString(bungee.getName() + " <- " + serverBrand, brand ); // Waterfall ++ pluginMessage.setData(brand); ++ brand.release(); ++ } catch (Exception ProtocolHacksSuck) ++ { ++ return; ++ } ++ } else ++ { ++ String serverBrand = new String( pluginMessage.getData(), StandardCharsets.UTF_8); ++ pluginMessage.setData( ( bungee.getName() + " <- " + serverBrand ).getBytes(StandardCharsets.UTF_8) ); // FlameCord - 1.7.x support ++ } ++ // FlameCord end - 1.7.x support + // changes in the packet are ignored so we need to send it manually + con.unsafe().sendPacket( pluginMessage ); + throw CancelSendSignal.INSTANCE; +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 +index a19bbdd0f..de417ecae 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -3,6 +3,9 @@ package net.md_5.bungee.connection; + import com.google.common.base.Charsets; + import com.google.common.base.Preconditions; + import com.google.gson.Gson; ++import com.google.gson.JsonElement; ++import com.google.gson.JsonObject; ++ + import java.math.BigInteger; + import java.net.InetSocketAddress; + import java.net.SocketAddress; +@@ -280,8 +283,23 @@ public class InitialHandler extends PacketHandler implements PendingConnection + @Override + public void done(ProxyPingEvent pingResult, Throwable error) + { +- Gson gson = BungeeCord.getInstance().gson; +- unsafe.sendPacket( new StatusResponse( gson.toJson( pingResult.getResponse() ) ) ); ++ // FlameCord start - 1.7.x support ++ Gson gson = handshake.getProtocolVersion() == ProtocolConstants.MINECRAFT_1_7_2 ? BungeeCord.getInstance().gsonLegacy : BungeeCord.getInstance().gson; ++ if ( ProtocolConstants.isBeforeOrEq( handshake.getProtocolVersion() , ProtocolConstants.MINECRAFT_1_8 ) ) ++ { ++ // Minecraft < 1.9 doesn't send string server descriptions as chat components. Older 1.7+ // clients even crash when encountering a chat component instead of a string. To be on the ++ // safe side, always send legacy descriptions for < 1.9 clients. ++ JsonElement element = gson.toJsonTree(pingResult.getResponse()); ++ Preconditions.checkArgument(element.isJsonObject(), "Response is not a JSON object"); ++ JsonObject object = element.getAsJsonObject(); ++ object.addProperty("description", pingResult.getResponse().getDescription()); ++ ++ unsafe.sendPacket(new StatusResponse(gson.toJson(element))); ++ } else ++ { ++ unsafe.sendPacket( new StatusResponse( gson.toJson( pingResult.getResponse() ) ) ); ++ } ++ // FlameCord end - 1.7.x support + if ( bungee.getConnectionThrottle() != null ) + { + bungee.getConnectionThrottle().unthrottle( getSocketAddress() ); +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 +index 6df3f3dd9..a409d440b 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java +@@ -16,6 +16,7 @@ import net.md_5.bungee.protocol.MinecraftDecoder; + import net.md_5.bungee.protocol.MinecraftEncoder; + import net.md_5.bungee.protocol.PacketWrapper; + import net.md_5.bungee.protocol.Protocol; ++import net.md_5.bungee.protocol.ProtocolConstants; + import net.md_5.bungee.protocol.packet.Handshake; + import net.md_5.bungee.protocol.packet.StatusRequest; + import net.md_5.bungee.protocol.packet.StatusResponse; +@@ -65,7 +66,7 @@ public class PingHandler extends PacketHandler + @SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR") + public void handle(StatusResponse statusResponse) throws Exception + { +- Gson gson = BungeeCord.getInstance().gson; ++ Gson gson = protocol == ProtocolConstants.MINECRAFT_1_7_2 ? BungeeCord.getInstance().gsonLegacy : BungeeCord.getInstance().gson; // FlameCord - 1.7.x support + ServerPing serverPing = gson.fromJson( statusResponse.getResponse(), ServerPing.class ); + ( (BungeeServerInfo) target ).cachePing( serverPing ); + callback.done( serverPing, null ); +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 +index d72208e75..66332af4e 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java +@@ -101,7 +101,12 @@ public class UpstreamBridge extends PacketHandler + player.unsafe().sendPacket( newPacket ); + } else + { +- player.unsafe().sendPacket( oldPacket ); ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isAfterOrEq( player.getPendingConnection().getVersion(), ProtocolConstants.MINECRAFT_1_8 ) ) ++ { ++ player.unsafe().sendPacket( oldPacket ); ++ } ++ // FlameCord end - 1.7.x support + } + } + con.getServer().disconnect( "Quitting" ); +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 +index ad951df05..68916db79 100644 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java ++++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java +@@ -34,6 +34,12 @@ public abstract class EntityMap + // Waterfall end + switch ( version ) + { ++ // FlameCord start - 1.7.x support ++ case ProtocolConstants.MINECRAFT_1_7_2: ++ return EntityMap_1_7_2.INSTANCE; ++ case ProtocolConstants.MINECRAFT_1_7_6: ++ return EntityMap_1_7_6.INSTANCE; ++ // FlameCord end - 1.7.x support + case ProtocolConstants.MINECRAFT_1_8: + return EntityMap_1_8.INSTANCE; + case ProtocolConstants.MINECRAFT_1_9: +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_2.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_2.java +new file mode 100644 +index 000000000..cdc07dc45 +--- /dev/null ++++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_2.java +@@ -0,0 +1,102 @@ ++// FlameCord start - 1.7.x support ++package net.md_5.bungee.entitymap; ++ ++import io.netty.buffer.ByteBuf; ++import net.md_5.bungee.protocol.DefinedPacket; ++import net.md_5.bungee.protocol.ProtocolConstants; ++ ++class EntityMap_1_7_2 extends EntityMap ++{ ++ ++ static final EntityMap INSTANCE = new EntityMap_1_7_2(); ++ ++ EntityMap_1_7_2() ++ { ++ addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Equipment ++ addRewrite( 0x0A, ProtocolConstants.Direction.TO_CLIENT, false ); // Use bed ++ addRewrite( 0x0B, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation ++ addRewrite( 0x0C, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player ++ addRewrite( 0x0D, ProtocolConstants.Direction.TO_CLIENT, false ); // Collect Item ++ addRewrite( 0x0E, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object ++ addRewrite( 0x0F, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob ++ addRewrite( 0x10, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting ++ addRewrite( 0x11, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb ++ addRewrite( 0x12, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Velocity ++ addRewrite( 0x14, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity ++ addRewrite( 0x15, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Relative Move ++ addRewrite( 0x16, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Look ++ addRewrite( 0x17, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Look and Relative Move ++ addRewrite( 0x18, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Teleport ++ addRewrite( 0x19, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Head Look ++ addRewrite( 0x1A, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status ++ addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity ++ addRewrite( 0x1C, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Metadata ++ addRewrite( 0x1D, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Effect ++ addRewrite( 0x1E, ProtocolConstants.Direction.TO_CLIENT, false ); // Remove Entity Effect ++ addRewrite( 0x20, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Properties ++ addRewrite( 0x25, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation ++ addRewrite( 0x2C, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Global Entity ++ ++ addRewrite( 0x02, ProtocolConstants.Direction.TO_SERVER, false ); // Use Entity ++ addRewrite( 0x0A, ProtocolConstants.Direction.TO_SERVER, false ); // Animation ++ addRewrite( 0x0B, ProtocolConstants.Direction.TO_SERVER, false ); // Entity Action ++ } ++ ++ @Override ++ public void rewriteClientbound(ByteBuf packet, int oldId, int newId) ++ { ++ super.rewriteClientbound( packet, oldId, newId ); ++ ++ //Special cases ++ int readerIndex = packet.readerIndex(); ++ int packetId = DefinedPacket.readVarInt( packet ); ++ int packetIdLength = packet.readerIndex() - readerIndex; ++ if ( packetId == 0x0D /* Collect Item */ || packetId == 0x1B /* Attach Entity */ ) ++ { ++ rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); ++ } else if ( packetId == 0x13 /* Destroy Entities */ ) ++ { ++ int count = packet.getByte( packetIdLength ); ++ for ( int i = 0; i < count; i++ ) ++ { ++ rewriteInt( packet, oldId, newId, packetIdLength + 1 + i * 4 ); ++ } ++ } else if ( packetId == 0x0E /* Spawn Object */ ) ++ { ++ DefinedPacket.readVarInt( packet ); ++ int type = packet.readUnsignedByte(); ++ ++ if ( type == 60 || type == 90 ) ++ { ++ packet.skipBytes( 14 ); ++ int position = packet.readerIndex(); ++ int readId = packet.readInt(); ++ int changedId = -1; ++ if ( readId == oldId ) ++ { ++ packet.setInt( position, newId ); ++ changedId = newId; ++ } else if ( readId == newId ) ++ { ++ packet.setInt( position, oldId ); ++ changedId = oldId; ++ } ++ if ( changedId != -1 ) ++ { ++ if ( changedId == 0 && readId != 0 ) ++ { // Trim off the extra data ++ packet.readerIndex( readerIndex ); ++ packet.writerIndex( packet.readableBytes() - 6 ); ++ } else if ( changedId != 0 && readId == 0 ) ++ { // Add on the extra data ++ packet.readerIndex( readerIndex ); ++ packet.capacity( packet.readableBytes() + 6 ); ++ packet.writerIndex( packet.readableBytes() + 6 ); ++ } ++ } ++ } ++ } ++ packet.readerIndex( readerIndex ); ++ } ++} ++// FlameCord end - 1.7.x support +\ No newline at end of file +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_6.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_6.java +new file mode 100644 +index 000000000..cb9174b35 +--- /dev/null ++++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_6.java +@@ -0,0 +1,62 @@ ++// FlameCord start - 1.7.x support ++package net.md_5.bungee.entitymap; ++ ++import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; ++import io.netty.buffer.ByteBuf; ++import net.md_5.bungee.BungeeCord; ++import net.md_5.bungee.UserConnection; ++import net.md_5.bungee.connection.LoginResult; ++import net.md_5.bungee.protocol.DefinedPacket; ++ ++class EntityMap_1_7_6 extends EntityMap_1_7_2 ++{ ++ ++ static final EntityMap_1_7_6 INSTANCE = new EntityMap_1_7_6(); ++ ++ @Override ++ @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") ++ public void rewriteClientbound(ByteBuf packet, int oldId, int newId) ++ { ++ super.rewriteClientbound( packet, oldId, newId ); ++ ++ int readerIndex = packet.readerIndex(); ++ int packetId = DefinedPacket.readVarInt( packet ); ++ int packetIdLength = packet.readerIndex() - readerIndex; ++ if ( packetId == 0x0C /* Spawn Player */ ) ++ { ++ DefinedPacket.readVarInt( packet ); ++ int idLength = packet.readerIndex() - readerIndex - packetIdLength; ++ String uuid = DefinedPacket.readString( packet ); ++ String username = DefinedPacket.readString( packet ); ++ int props = DefinedPacket.readVarInt( packet ); ++ if ( props == 0 ) ++ { ++ UserConnection player = (UserConnection) BungeeCord.getInstance().getPlayer( username ); ++ if ( player != null ) ++ { ++ LoginResult profile = player.getPendingConnection().getLoginProfile(); ++ if ( profile != null && profile.getProperties() != null ++ && profile.getProperties().length >= 1 ) ++ { ++ ByteBuf rest = packet.copy(); ++ packet.readerIndex( readerIndex ); ++ packet.writerIndex( readerIndex + packetIdLength + idLength ); ++ DefinedPacket.writeString( player.getUniqueId().toString(), packet ); ++ DefinedPacket.writeString( username, packet ); ++ DefinedPacket.writeVarInt( profile.getProperties().length, packet ); ++ for ( net.md_5.bungee.protocol.Property property : profile.getProperties() ) ++ { ++ DefinedPacket.writeString( property.getName(), packet ); ++ DefinedPacket.writeString( property.getValue(), packet ); ++ DefinedPacket.writeString( property.getSignature(), packet ); ++ } ++ packet.writeBytes( rest ); ++ rest.release(); ++ } ++ } ++ } ++ } ++ packet.readerIndex( readerIndex ); ++ } ++} ++// FlameCord end - 1.7.x support +\ No newline at end of file +diff --git a/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java b/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java +index bea2bbff9..caed43849 100644 +--- a/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java +@@ -8,6 +8,7 @@ import lombok.Getter; + import lombok.NonNull; + import lombok.RequiredArgsConstructor; + import lombok.Setter; ++import net.md_5.bungee.BungeeCord; + import net.md_5.bungee.UserConnection; + import net.md_5.bungee.protocol.ProtocolConstants; + import net.md_5.bungee.protocol.packet.EntityRemoveEffect; +@@ -23,6 +24,12 @@ public class ForgeClientHandler + @NonNull + private final UserConnection con; + ++ // FlameCord start - 1.7.x support ++ @Getter ++ @Setter(AccessLevel.PACKAGE) ++ private boolean forgeOutdated = false; ++ // FlameCord end - 1.7.x support ++ + /** + * The users' mod list. + */ +@@ -175,4 +182,21 @@ public class ForgeClientHandler + { + return fmlTokenInHandshake || clientModList != null; + } ++ ++ // FlameCord start - 1.7.x support ++ /** ++ * Checks to see if a user is using an outdated FML build, and takes ++ * appropriate action on the User side. This should only be called during a ++ * server connection, by the ServerConnector ++ * ++ * @return true if the user's FML build is outdated, otherwise ++ * false ++ */ ++ public boolean checkUserOutdated() { ++ if (forgeOutdated) { ++ con.disconnect( BungeeCord.getInstance().getTranslation("connect_kick_outdated_forge") ); ++ } ++ return forgeOutdated; ++ } ++ // FlameCord end - 1.7.x support + } +diff --git a/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandshakeState.java b/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandshakeState.java +index 5e02f8c8a..85bc21b9f 100644 +--- a/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandshakeState.java ++++ b/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandshakeState.java +@@ -3,6 +3,7 @@ package net.md_5.bungee.forge; + import java.util.Map; + import net.md_5.bungee.ServerConnector; + import net.md_5.bungee.UserConnection; ++import net.md_5.bungee.protocol.ProtocolConstants; + import net.md_5.bungee.protocol.packet.PluginMessage; + + /** +@@ -84,6 +85,22 @@ enum ForgeClientHandshakeState implements IForgeClientPacketHandler clientModList = ForgeUtils.readModList( message ); + con.getForgeClientHandler().setClientModList( clientModList ); ++ // FlameCord start - 1.7.x support ++ // If the user is below 1.8, we need to check the version of FML - it's not always an OK version. ++ if ( ProtocolConstants.isBeforeOrEq( con.getPendingConnection().getVersion(), ProtocolConstants.MINECRAFT_1_7_6 ) ) ++ { ++ // Get the version from the mod list. ++ int buildNumber = ForgeUtils.getFmlBuildNumber( clientModList ); ++ ++ // If we get 0, we're probably using a testing build, so let it though. Otherwise, check the build number. ++ if ( buildNumber < ForgeConstants.FML_MIN_BUILD_VERSION && buildNumber != 0 ) ++ { ++ // Mark the user as an old Forge user. This will then cause any Forge ServerConnectors to cancel any ++ // connections to it. ++ con.getForgeClientHandler().setForgeOutdated( true ); ++ } ++ } ++ // FlameCord end - 1.7.x support + } + + return WAITINGSERVERDATA; +diff --git a/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java b/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java +index f70d357a3..df6a4b12c 100644 +--- a/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java ++++ b/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java +@@ -13,6 +13,7 @@ public class ServerUnique extends TabList + { + + private final Collection uuids = new HashSet<>(); ++ private final Collection usernames = new HashSet<>(); // FlameCord - 1.7.x support + + public ServerUnique(ProxiedPlayer player) + { +@@ -26,10 +27,26 @@ public class ServerUnique extends TabList + { + if ( playerListItem.getAction() == PlayerListItem.Action.ADD_PLAYER ) + { +- uuids.add( item.getUuid() ); ++ // FlameCord start - 1.7.x support ++ if ( item.getUuid() != null ) ++ { ++ uuids.add( item.getUuid() ); ++ } else ++ { ++ usernames.add( item.getUsername() ); ++ } ++ // FlameCord end - 1.7.x support + } else if ( playerListItem.getAction() == PlayerListItem.Action.REMOVE_PLAYER ) + { +- uuids.remove( item.getUuid() ); ++ // FlameCord start - 1.7.x support ++ if ( item.getUuid() != null ) ++ { ++ uuids.remove( item.getUuid() ); ++ } else ++ { ++ usernames.remove( item.getUsername() ); ++ } ++ // FlameCord end - 1.7.x support + } + } + player.unsafe().sendPacket( playerListItem ); +@@ -79,17 +96,48 @@ public class ServerUnique extends TabList + { + PlayerListItem packet = new PlayerListItem(); + packet.setAction( PlayerListItem.Action.REMOVE_PLAYER ); +- PlayerListItem.Item[] items = new PlayerListItem.Item[ uuids.size() ]; ++ PlayerListItem.Item[] items = new PlayerListItem.Item[ uuids.size() + usernames.size() ]; // FlameCord - 1.7.x support + int i = 0; ++ ++ // FlameCord start - 1.7.x support ++ for ( String username : usernames ) ++ { ++ PlayerListItem.Item item = items[i++] = new PlayerListItem.Item(); ++ item.setUsername( username ); ++ item.setDisplayName( username ); ++ item.setPing( 0 ); ++ } ++ // FlameCord end - 1.7.x support ++ + for ( UUID uuid : uuids ) + { + PlayerListItem.Item item = items[i++] = new PlayerListItem.Item(); + item.setUuid( uuid ); + } + packet.setItems( items ); +- player.unsafe().sendPacket( packet ); ++ // FlameCord start - 1.7.x support ++ if ( ProtocolConstants.isAfterOrEq( player.getPendingConnection().getVersion(), ProtocolConstants.MINECRAFT_1_8 ) ) ++ { ++ player.unsafe().sendPacket( packet ); ++ } else ++ { ++ // Split up the packet ++ for ( PlayerListItem.Item item : packet.getItems() ) ++ { ++ PlayerListItem p2 = new PlayerListItem(); ++ p2.setAction( packet.getAction() ); ++ ++ p2.setItems( new PlayerListItem.Item[] ++ { ++ item ++ } ); ++ player.unsafe().sendPacket( p2 ); ++ } ++ } ++ // FlameCord end - 1.7.x support + } + uuids.clear(); ++ usernames.clear(); // FlameCord - 1.7.x support + } + + @Override +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0005-Make-PlayerHandshakeEvent-cancellable.patch b/Waterfall-Proxy-Patches/0005-Make-PlayerHandshakeEvent-cancellable.patch new file mode 100644 index 0000000..8e8d901 --- /dev/null +++ b/Waterfall-Proxy-Patches/0005-Make-PlayerHandshakeEvent-cancellable.patch @@ -0,0 +1,56 @@ +From 2a6878cb7a8c16bb76b79012db51d81f112636c9 Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 17:57:23 +0800 +Subject: [PATCH] Make PlayerHandshakeEvent cancellable + + +diff --git a/api/src/main/java/net/md_5/bungee/api/event/PlayerHandshakeEvent.java b/api/src/main/java/net/md_5/bungee/api/event/PlayerHandshakeEvent.java +index 2f7b38d9e..e29b0ed35 100644 +--- a/api/src/main/java/net/md_5/bungee/api/event/PlayerHandshakeEvent.java ++++ b/api/src/main/java/net/md_5/bungee/api/event/PlayerHandshakeEvent.java +@@ -2,8 +2,11 @@ package net.md_5.bungee.api.event; + + import lombok.Data; + import lombok.EqualsAndHashCode; ++import lombok.Getter; ++import lombok.Setter; + import lombok.ToString; + import net.md_5.bungee.api.connection.PendingConnection; ++import net.md_5.bungee.api.plugin.Cancellable; + import net.md_5.bungee.api.plugin.Event; + import net.md_5.bungee.protocol.packet.Handshake; + +@@ -14,8 +17,11 @@ import net.md_5.bungee.protocol.packet.Handshake; + @Data + @ToString(callSuper = false) + @EqualsAndHashCode(callSuper = false) +-public class PlayerHandshakeEvent extends Event +-{ ++// FlameCord - Implement cancellable ++public class PlayerHandshakeEvent extends Event implements Cancellable { ++ @Getter ++ @Setter ++ private boolean cancelled = false; + + /** + * Connection attempting to login. +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 +index de417ecae..c3543a18d 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -362,7 +362,11 @@ public class InitialHandler extends PacketHandler implements PendingConnection + + this.virtualHost = InetSocketAddress.createUnresolved( handshake.getHost(), handshake.getPort() ); + +- bungee.getPluginManager().callEvent( new PlayerHandshakeEvent( InitialHandler.this, handshake ) ); ++ // FlameCord - Make PlayerHandshakeEvent cancellable ++ if (bungee.getPluginManager().callEvent(new PlayerHandshakeEvent(InitialHandler.this, handshake)).isCancelled()) { ++ ch.close(); ++ return; ++ } + + switch ( handshake.getRequestedProtocol() ) + { +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0006-Make-a-getter-for-the-callback.patch b/Waterfall-Proxy-Patches/0006-Make-a-getter-for-the-callback.patch new file mode 100644 index 0000000..f3bb074 --- /dev/null +++ b/Waterfall-Proxy-Patches/0006-Make-a-getter-for-the-callback.patch @@ -0,0 +1,23 @@ +From c44b23dcab5a544424e6835060638e1bbf053ae2 Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 17:58:18 +0800 +Subject: [PATCH] Make a getter for the callback + + +diff --git a/api/src/main/java/net/md_5/bungee/api/event/AsyncEvent.java b/api/src/main/java/net/md_5/bungee/api/event/AsyncEvent.java +index cf85ca06..8a945a99 100644 +--- a/api/src/main/java/net/md_5/bungee/api/event/AsyncEvent.java ++++ b/api/src/main/java/net/md_5/bungee/api/event/AsyncEvent.java +@@ -25,7 +25,8 @@ import net.md_5.bungee.api.plugin.Plugin; + @EqualsAndHashCode(callSuper = true) + public class AsyncEvent extends Event + { +- ++ // FlameCord - Make a getter for the callback ++ @Getter + private final Callback done; + private final Map intents = new ConcurrentHashMap<>(); + private final AtomicBoolean fired = new AtomicBoolean(); +-- +2.32.0 + diff --git a/Waterfall-Proxy-Patches/0007-Disable-update-checker-Use-bungee-name.patch b/Waterfall-Proxy-Patches/0007-Disable-update-checker-Use-bungee-name.patch new file mode 100644 index 0000000..79c9392 --- /dev/null +++ b/Waterfall-Proxy-Patches/0007-Disable-update-checker-Use-bungee-name.patch @@ -0,0 +1,73 @@ +From 2edaf016d49ec4c769eebaf7383f3a7e6dccc19c Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 18:00:49 +0800 +Subject: [PATCH] Disable update checker & Use bungee name + + +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCordLauncher.java b/proxy/src/main/java/net/md_5/bungee/BungeeCordLauncher.java +index 463bc3d53..00243fdb9 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCordLauncher.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCordLauncher.java +@@ -45,25 +45,10 @@ public class BungeeCordLauncher + return; + } + +- if ( BungeeCord.class.getPackage().getSpecificationVersion() != null && System.getProperty( "IReallyKnowWhatIAmDoingISwear" ) == null ) +- { +- Date buildDate = new SimpleDateFormat( "yyyyMMdd" ).parse( BungeeCord.class.getPackage().getSpecificationVersion() ); +- +- Calendar deadline = Calendar.getInstance(); +- deadline.add( Calendar.WEEK_OF_YEAR, -8 ); +- if ( buildDate.before( deadline.getTime() ) ) +- { +- System.err.println( "*** Hey! This build is potentially outdated :( ***" ); +- System.err.println( "*** Please check for a new build from https://papermc.io/downloads ***" ); +- System.err.println( "*** Should this build be outdated, you will get NO support for it. ***" ); +- //System.err.println( "*** Server will start in 10 seconds ***" ); +- //Thread.sleep( TimeUnit.SECONDS.toMillis( 10 ) ); +- } +- } +- + BungeeCord bungee = new BungeeCord(); + ProxyServer.setInstance( bungee ); +- bungee.getLogger().info( "Enabled FlameCord version " + bungee.getVersion() ); ++ // FlameCord - Use BungeeCord name ++ bungee.getLogger().info( "Enabled " + bungee.getName() + " version " + bungee.getVersion() ); + bungee.start(); + + if ( !options.has( "noconsole" ) ) +diff --git a/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java b/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java +index 9abae0b61..e8232c0bd 100644 +--- a/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java ++++ b/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java +@@ -22,6 +22,7 @@ import java.util.Locale; + import java.util.Map; + import java.util.logging.Level; + import lombok.RequiredArgsConstructor; ++import net.md_5.bungee.BungeeCord; + import net.md_5.bungee.Util; + import net.md_5.bungee.api.ChatColor; + import net.md_5.bungee.api.ProxyServer; +@@ -227,7 +228,8 @@ public class YamlConfig implements ConfigurationAdapter + Map val = entry.getValue(); + String name = entry.getKey(); + String addr = get( "address", "localhost:25565", val ); +- String motd = ChatColor.translateAlternateColorCodes( '&', get( "motd", "&1Just another FlameCord - Forced Host", val ) ); ++ // FlameCord - Use Bungee name instead ++ String motd = ChatColor.translateAlternateColorCodes( '&', get( "motd", "&1Just another " + BungeeCord.getInstance().getName() + " - Forced Host", val ) ); + boolean restricted = get( "restricted", false, val ); + SocketAddress address = Util.getAddr( addr ); + ServerInfo info = ProxyServer.getInstance().constructServerInfo( name, address, motd, restricted ); +@@ -253,7 +255,8 @@ public class YamlConfig implements ConfigurationAdapter + + for ( Map val : base ) + { +- String motd = get( "motd", "&1Another Bungee server", val ); ++ // FlameCord - Use Bungee name instead ++ String motd = get( "motd", "&1Another " + BungeeCord.getInstance().getName() + " server", val ); + motd = ChatColor.translateAlternateColorCodes( '&', motd ); + + int maxPlayers = get( "max_players", 1, val ); +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0008-Change-replaceAll-to-replace.patch b/Waterfall-Proxy-Patches/0008-Change-replaceAll-to-replace.patch new file mode 100644 index 0000000..bc20733 --- /dev/null +++ b/Waterfall-Proxy-Patches/0008-Change-replaceAll-to-replace.patch @@ -0,0 +1,23 @@ +From dca52858df4acbd78c9128f6e40e447b86824150 Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 18:01:37 +0800 +Subject: [PATCH] Change replaceAll() to replace() + + +diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +index d672c8ef..9067b806 100644 +--- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java ++++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +@@ -129,7 +129,8 @@ public class ServerConnector extends PacketHandler + newp[newp.length - 2] = new net.md_5.bungee.protocol.Property( ForgeConstants.FML_LOGIN_PROFILE, "true", null ); + + // If we do not perform the replacement, then the IP Forwarding code in Spigot et. al. will try to split on this prematurely. +- newp[newp.length - 1] = new net.md_5.bungee.protocol.Property( ForgeConstants.EXTRA_DATA, user.getExtraDataInHandshake().replaceAll( "\0", "\1"), "" ); ++ // FlameCord - Change replaceAll() to replace() ++ newp[newp.length - 1] = new net.md_5.bungee.protocol.Property( ForgeConstants.EXTRA_DATA, user.getExtraDataInHandshake().replace( "\0", "\1"), "" ); + + // All done. + properties = newp; +-- +2.36.1.windows.1 + diff --git a/Waterfall-Proxy-Patches/0009-Use-duplicate-for-MinecraftDecoder.patch b/Waterfall-Proxy-Patches/0009-Use-duplicate-for-MinecraftDecoder.patch new file mode 100644 index 0000000..251aaab --- /dev/null +++ b/Waterfall-Proxy-Patches/0009-Use-duplicate-for-MinecraftDecoder.patch @@ -0,0 +1,32 @@ +From 510085f25ef21678828f5b3026662f51a94c1137 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Wed, 21 Dec 2022 21:43:05 -0300 +Subject: [PATCH] Use duplicate() for MinecraftDecoder + + +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 +index ac83e3253..c31ec1ce7 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java +@@ -38,7 +38,7 @@ public class MinecraftDecoder extends MessageToMessageDecoder + } + + Protocol.DirectionData prot = ( server ) ? protocol.TO_SERVER : protocol.TO_CLIENT; +- ByteBuf slice = in.copy(); // Can't slice this one due to EntityMap :( ++ ByteBuf slice = in.duplicate(); // FlameCord - Duplicate buf instead of Copy + + Object packetTypeInfo = null; + try +@@ -73,7 +73,8 @@ public class MinecraftDecoder extends MessageToMessageDecoder + in.skipBytes( in.readableBytes() ); + } + +- out.add( new PacketWrapper( packet, slice ) ); ++ // FlameCord - Duplicate buf instead of Copy ++ out.add( new PacketWrapper( packet, slice.retain() ) ); + slice = null; + } catch (BadPacketException | IndexOutOfBoundsException e) { + // Waterfall start: Additional DoS mitigations +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0010-Close-connections-Don-t-flush-if-not-necessary.patch b/Waterfall-Proxy-Patches/0010-Close-connections-Don-t-flush-if-not-necessary.patch new file mode 100644 index 0000000..1922c3a --- /dev/null +++ b/Waterfall-Proxy-Patches/0010-Close-connections-Don-t-flush-if-not-necessary.patch @@ -0,0 +1,131 @@ +From 9c6dd2dc6ee18d5429d098b5a5d1963cfd404856 Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 18:06:17 +0800 +Subject: [PATCH] Close connections & Don't flush if not necessary + + +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 +index c3543a18..8ed26e5c 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -208,6 +208,13 @@ public class InitialHandler extends PacketHandler implements PendingConnection + } + + ServerPing legacy = result.getResponse(); ++ ++ // FlameCord - Close and return if legacy == null ++ if (legacy == null) { ++ ch.close(); ++ return; ++ } ++ + String kickMessage; + + if ( v1_5 ) +@@ -283,6 +290,17 @@ public class InitialHandler extends PacketHandler implements PendingConnection + @Override + public void done(ProxyPingEvent pingResult, Throwable error) + { ++ // FlameCord - Close if response is null ++ if (pingResult.getResponse() == null) { ++ ch.close(); ++ return; ++ } ++ ++ // FlameCord - Return if connection is closed ++ if (ch.isClosed()) { ++ return; ++ } ++ + // FlameCord start - 1.7.x support + Gson gson = handshake.getProtocolVersion() == ProtocolConstants.MINECRAFT_1_7_2 ? BungeeCord.getInstance().gsonLegacy : BungeeCord.getInstance().gson; + if ( ProtocolConstants.isBeforeOrEq( handshake.getProtocolVersion() , ProtocolConstants.MINECRAFT_1_8 ) ) +@@ -327,11 +345,13 @@ public class InitialHandler extends PacketHandler implements PendingConnection + @Override + public void handle(PingPacket ping) throws Exception + { +- if (!ACCEPT_INVALID_PACKETS) { +- Preconditions.checkState(thisState == State.PING, "Not expecting PING"); +- } ++ // FlameCord - Never accept invalid packets ++ Preconditions.checkState( thisState == State.PING, "Not expecting PING" ); ++ + unsafe.sendPacket( ping ); +- disconnect( "" ); ++ ++ // FlameCord - Close instead of disconnect ++ ch.close(); + } + + @Override +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 +index 6dc5633f..8b0fac0a 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +@@ -80,10 +80,11 @@ public class ChannelWrapper + + if ( packet != null && ch.isActive() ) + { +- ch.writeAndFlush( packet ).addListeners( ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE, ChannelFutureListener.CLOSE ); ++ // FlameCord - Remove the firing of exceptions on failure ++ ch.writeAndFlush( packet ).addListeners( ChannelFutureListener.CLOSE ); + } else + { +- ch.flush(); ++ // FlameCord - Don't flush just close + ch.close(); + } + } +@@ -113,7 +114,7 @@ public class ChannelWrapper + public void addBefore(String baseName, String name, ChannelHandler handler) + { + Preconditions.checkState( ch.eventLoop().inEventLoop(), "cannot add handler outside of event loop" ); +- ch.pipeline().flush(); ++ // FlameCord - Don't flush if not necessary + ch.pipeline().addBefore( baseName, name, handler ); + } + +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 +index 2a21243b..a95193ba 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java +@@ -5,6 +5,7 @@ import io.github.waterfallmc.waterfall.event.ConnectionInitEvent; + import io.netty.buffer.PooledByteBufAllocator; + import io.netty.channel.Channel; + import io.netty.channel.ChannelException; ++import io.netty.channel.ChannelHandlerContext; + import io.netty.channel.ChannelInitializer; + import io.netty.channel.ChannelOption; + import io.netty.channel.EventLoopGroup; +@@ -98,6 +99,14 @@ public class PipelineUtils + + BungeeCord.getInstance().getPluginManager().callEvent(connectionInitEvent); + } ++ ++ // FlameCord - Close on exception caught ++ @Override ++ public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception { ++ cause.printStackTrace(); ++ ++ ctx.close(); ++ } + }; + public static final Base BASE = new Base(); + private static final KickStringWriter legacyKicker = new KickStringWriter(); +@@ -192,5 +201,13 @@ public class PipelineUtils + + ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() ); + } ++ ++ // FlameCord - Close on exception caught ++ @Override ++ public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception { ++ cause.printStackTrace(); ++ ++ ctx.close(); ++ } + } + } +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0011-Packet-Checks.patch b/Waterfall-Proxy-Patches/0011-Packet-Checks.patch new file mode 100644 index 0000000..af75e87 --- /dev/null +++ b/Waterfall-Proxy-Patches/0011-Packet-Checks.patch @@ -0,0 +1,53 @@ +From cf87130274644325adbf7fabb7b95d95e976ee57 Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 18:10:30 +0800 +Subject: [PATCH] Packet Checks + + +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 +index c31ec1ce7..640f0ea26 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java +@@ -38,6 +38,19 @@ public class MinecraftDecoder extends MessageToMessageDecoder + } + + Protocol.DirectionData prot = ( server ) ? protocol.TO_SERVER : protocol.TO_CLIENT; ++ ++ // FlameCord - Check size before decoding ++ if (prot == protocol.TO_SERVER) { ++ final int readableBytes = in.readableBytes(); ++ final int capacity = in.capacity(); ++ ++ if (readableBytes > 2097152) { ++ throw new FastDecoderException("Error decoding packet with too many readableBytes: " + readableBytes); ++ } else if (capacity > 2097152) { ++ throw new FastDecoderException("Error decoding packet with too big capacity: " + capacity); ++ } ++ } ++ + ByteBuf slice = in.duplicate(); // FlameCord - Duplicate buf instead of Copy + + Object packetTypeInfo = null; +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 +index 29cf4b4fc..4820267b3 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java +@@ -78,6 +78,15 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception + { ++ // FlameCord - Return if channel isn't active ++ if (!ctx.channel().isActive()) { ++ if (msg instanceof PacketWrapper) { ++ ((PacketWrapper) msg).trySingleRelease(); ++ } ++ ++ return; ++ } ++ + if ( msg instanceof HAProxyMessage ) + { + HAProxyMessage proxy = (HAProxyMessage) msg; +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0012-Change-default-timeout.patch b/Waterfall-Proxy-Patches/0012-Change-default-timeout.patch new file mode 100644 index 0000000..9be72fe --- /dev/null +++ b/Waterfall-Proxy-Patches/0012-Change-default-timeout.patch @@ -0,0 +1,23 @@ +From f790d57423c57a21d642db20b6de9d3515fd3e93 Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 18:14:02 +0800 +Subject: [PATCH] Change default timeout + + +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 +index 8911bee85..b67195ec8 100644 +--- a/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java ++++ b/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java +@@ -35,7 +35,8 @@ public abstract class Configuration implements ProxyConfig + /** + * Time before users are disconnected due to no network activity. + */ +- private int timeout = 30000; ++ // FlameCord - Modify default timeout ++ private int timeout = 17000; + /** + * UUID used for metrics. + */ +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0013-Don-t-allow-name-containing-dot.patch b/Waterfall-Proxy-Patches/0013-Don-t-allow-name-containing-dot.patch new file mode 100644 index 0000000..e6063a4 --- /dev/null +++ b/Waterfall-Proxy-Patches/0013-Don-t-allow-name-containing-dot.patch @@ -0,0 +1,23 @@ +From cbec17576f5c5472cf0037f9962ca5c806133200 Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 18:25:51 +0800 +Subject: [PATCH] Don't allow name containing dot + + +diff --git a/proxy/src/main/java/net/md_5/bungee/util/AllowedCharacters.java b/proxy/src/main/java/net/md_5/bungee/util/AllowedCharacters.java +index 1a4242a2e..d947fad7d 100644 +--- a/proxy/src/main/java/net/md_5/bungee/util/AllowedCharacters.java ++++ b/proxy/src/main/java/net/md_5/bungee/util/AllowedCharacters.java +@@ -21,7 +21,8 @@ public final class AllowedCharacters + } else + { + // Don't allow spaces, Yaml config doesn't support them +- return isChatAllowedCharacter( c ) && c != ' '; ++ // FlameCord - Don't allow dots ++ return isChatAllowedCharacter( c ) && c != ' ' && c != '.'; + } + } + +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0014-Use-pipeline-to-reduce-redundancy.patch b/Waterfall-Proxy-Patches/0014-Use-pipeline-to-reduce-redundancy.patch new file mode 100644 index 0000000..d056e72 --- /dev/null +++ b/Waterfall-Proxy-Patches/0014-Use-pipeline-to-reduce-redundancy.patch @@ -0,0 +1,78 @@ +From 33c937b0b20e6ad8994c21258841d5330443c2fe Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 18:30:07 +0800 +Subject: [PATCH] Use pipeline to reduce redundancy + + +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 +index 8b0fac0a..b3fa4835 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +@@ -5,6 +5,8 @@ import io.netty.channel.Channel; + import io.netty.channel.ChannelFutureListener; + import io.netty.channel.ChannelHandler; + import io.netty.channel.ChannelHandlerContext; ++import io.netty.channel.ChannelPipeline; ++ + import java.net.SocketAddress; + import java.util.concurrent.TimeUnit; + import lombok.Getter; +@@ -37,14 +39,18 @@ public class ChannelWrapper + + public void setProtocol(Protocol protocol) + { +- ch.pipeline().get( MinecraftDecoder.class ).setProtocol( protocol ); +- ch.pipeline().get( MinecraftEncoder.class ).setProtocol( protocol ); ++ // FlameCord - Use pipeline to reduce redundancy ++ final ChannelPipeline pipeline = ch.pipeline(); ++ pipeline.get( MinecraftDecoder.class ).setProtocol( protocol ); ++ pipeline.get( MinecraftEncoder.class ).setProtocol( protocol ); + } + + public void setVersion(int protocol) + { +- ch.pipeline().get( MinecraftDecoder.class ).setProtocolVersion( protocol ); +- ch.pipeline().get( MinecraftEncoder.class ).setProtocolVersion( protocol ); ++ // FlameCord - Use pipeline to reduce redundancy ++ final ChannelPipeline pipeline = ch.pipeline(); ++ pipeline.get( MinecraftDecoder.class ).setProtocolVersion( protocol ); ++ pipeline.get( MinecraftEncoder.class ).setProtocolVersion( protocol ); + } + + public void write(Object packet) +@@ -125,25 +131,27 @@ public class ChannelWrapper + + public void setCompressionThreshold(int compressionThreshold) + { +- if ( ch.pipeline().get( PacketCompressor.class ) == null && compressionThreshold != -1 ) ++ // FlameCord - Use pipeline to reduce redundancy ++ final ChannelPipeline pipeline = ch.pipeline(); ++ if ( pipeline.get( PacketCompressor.class ) == null && compressionThreshold != -1 ) + { + addBefore( PipelineUtils.PACKET_ENCODER, "compress", new PacketCompressor() ); + } + if ( compressionThreshold != -1 ) + { +- ch.pipeline().get( PacketCompressor.class ).setThreshold( compressionThreshold ); ++ pipeline.get( PacketCompressor.class ).setThreshold( compressionThreshold ); + } else + { +- ch.pipeline().remove( "compress" ); ++ pipeline.remove( "compress" ); + } + +- if ( ch.pipeline().get( PacketDecompressor.class ) == null && compressionThreshold != -1 ) ++ if ( pipeline.get( PacketDecompressor.class ) == null && compressionThreshold != -1 ) + { + addBefore( PipelineUtils.PACKET_DECODER, "decompress", new PacketDecompressor(compressionThreshold) ); + } + if ( compressionThreshold == -1 ) + { +- ch.pipeline().remove( "decompress" ); ++ pipeline.remove( "decompress" ); + } + } + } +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0015-Allow-custom-uuids-even-if-onlineMode-is-true.patch b/Waterfall-Proxy-Patches/0015-Allow-custom-uuids-even-if-onlineMode-is-true.patch new file mode 100644 index 0000000..7bc2ea2 --- /dev/null +++ b/Waterfall-Proxy-Patches/0015-Allow-custom-uuids-even-if-onlineMode-is-true.patch @@ -0,0 +1,22 @@ +From 047ea04ae1cf8277e6c2eed87dc0d5fd901efe91 Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 18:35:33 +0800 +Subject: [PATCH] Allow custom uuids even if onlineMode is true + + +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 +index 8ed26e5c..4242b9c8 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -769,7 +769,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection + public void setUniqueId(UUID uuid) + { + Preconditions.checkState( thisState == State.USERNAME, "Can only set uuid while state is username" ); +- Preconditions.checkState( !onlineMode, "Can only set uuid when online mode is false" ); ++ // FlameCord - Allow custom uuids even if onlineMode is true + this.uniqueId = uuid; + } + +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0016-Change-IllegalStateException-to-QuietException-and-e.patch b/Waterfall-Proxy-Patches/0016-Change-IllegalStateException-to-QuietException-and-e.patch new file mode 100644 index 0000000..6d5cf4c --- /dev/null +++ b/Waterfall-Proxy-Patches/0016-Change-IllegalStateException-to-QuietException-and-e.patch @@ -0,0 +1,24 @@ +From 0f07c38f3d16849626c28ae710616788f30b00fc Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 18:40:55 +0800 +Subject: [PATCH] Change IllegalStateException to QuietException and explain + that is a plugin + + +diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +index 2d72e26ea..94702434d 100644 +--- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java ++++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +@@ -313,7 +313,8 @@ public final class UserConnection implements ProxiedPlayer + + if ( getServer() == null && !ch.isClosing() ) + { +- throw new IllegalStateException( "Cancelled ServerConnectEvent with no server or disconnect." ); ++ // FlameCord - Change IllegalStateException to QuietException and explain that is a plugin ++ throw new QuietException("A plugin cancelled ServerConnectEvent with no server or disconnect."); + } + return; + } +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0017-Don-t-declare-uuid-unless-it-s-null.patch b/Waterfall-Proxy-Patches/0017-Don-t-declare-uuid-unless-it-s-null.patch new file mode 100644 index 0000000..9bc71fe --- /dev/null +++ b/Waterfall-Proxy-Patches/0017-Don-t-declare-uuid-unless-it-s-null.patch @@ -0,0 +1,25 @@ +From ac09d06618510ed85aadc83a5a6bb812bf109218 Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Wed, 16 Dec 2020 18:43:17 +0800 +Subject: [PATCH] Don't declare uuid unless it's null + + +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 +index 8715821fe..1c10b5a93 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -559,7 +559,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection + { + loginProfile = obj; + name = obj.getName(); +- uniqueId = Util.getUUID( obj.getId() ); ++ // FlameCord - Don't declare uuid unless it's null ++ if (uniqueId == null) { ++ uniqueId = Util.getUUID(obj.getId()); ++ } + finish(); + return; + } +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0018-FlameCord-General-Patch.patch b/Waterfall-Proxy-Patches/0018-FlameCord-General-Patch.patch new file mode 100644 index 0000000..396cf54 --- /dev/null +++ b/Waterfall-Proxy-Patches/0018-FlameCord-General-Patch.patch @@ -0,0 +1,377 @@ +From f4ab4a93d462da9ff8375c432ea7824a189ca59a Mon Sep 17 00:00:00 2001 +From: Juan Cruz Linsalata +Date: Mon, 12 Oct 2020 15:40:53 -0300 +Subject: [PATCH] FlameCord General Patch + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java +new file mode 100644 +index 000000000..f3d5c0290 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java +@@ -0,0 +1,31 @@ ++package dev._2lstudios.flamecord; ++ ++import java.util.Collection; ++import java.util.logging.Logger; ++ ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++import lombok.Getter; ++import net.md_5.bungee.config.ConfigurationProvider; ++import net.md_5.bungee.config.YamlConfiguration; ++ ++public class FlameCord { ++ @Getter ++ private static FlameCord instance; ++ ++ public static void initialize(final Logger logger, final Collection whitelistedAddresses) { ++ if (FlameCord.instance == null) { ++ FlameCord.instance = new FlameCord(); ++ } ++ ++ instance.reload(logger, whitelistedAddresses); ++ } ++ ++ @Getter ++ private FlameCordConfiguration flameCordConfiguration; ++ ++ public void reload(final Logger logger, final Collection whitelistedAddresses) { ++ final ConfigurationProvider configurationProvider = ConfigurationProvider.getProvider(YamlConfiguration.class); ++ ++ this.flameCordConfiguration = new FlameCordConfiguration(configurationProvider, whitelistedAddresses); ++ } ++} +\ No newline at end of file +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameConfig.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameConfig.java +new file mode 100644 +index 000000000..e6c660d15 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameConfig.java +@@ -0,0 +1,74 @@ ++package dev._2lstudios.flamecord.configuration; ++ ++import java.io.File; ++import java.io.IOException; ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.HashSet; ++ ++import net.md_5.bungee.config.Configuration; ++import net.md_5.bungee.config.ConfigurationProvider; ++import net.md_5.bungee.config.YamlConfiguration; ++ ++public class FlameConfig { ++ Configuration load(File file) { ++ ConfigurationProvider configurationProvider = ConfigurationProvider.getProvider(YamlConfiguration.class); ++ ++ if (file.exists() && file.isFile()) { ++ try { ++ return configurationProvider.load(file); ++ } catch (IOException ex) { ++ // Failed to load, do nothing ++ } ++ } ++ ++ return new Configuration(); ++ } ++ ++ void save(Configuration config, File file) { ++ ConfigurationProvider configurationProvider = ConfigurationProvider.getProvider(YamlConfiguration.class); ++ ++ try { ++ configurationProvider.save(config, file); ++ } catch (IOException ex) { ++ // Failed to save, do nothing ++ } ++ } ++ ++ double setIfUnexistant(final String arg1, final double arg2, final Configuration configuration) { ++ return (double) setIfUnexistant(arg1, (Object) arg2, configuration); ++ } ++ ++ int setIfUnexistant(final String arg1, final int arg2, final Configuration configuration) { ++ return (int) setIfUnexistant(arg1, (Object) arg2, configuration); ++ } ++ ++ String setIfUnexistant(final String arg1, final String arg2, final Configuration configuration) { ++ return (String) setIfUnexistant(arg1, (Object) arg2, configuration); ++ } ++ ++ boolean setIfUnexistant(final String arg1, final boolean arg2, final Configuration configuration) { ++ return (boolean) setIfUnexistant(arg1, (Object) arg2, configuration); ++ } ++ ++ Object setIfUnexistant(final String arg1, final Object arg2, final Configuration configuration) { ++ if (!configuration.contains(arg1)) { ++ configuration.set(arg1, arg2); ++ ++ return arg2; ++ } else { ++ return configuration.get(arg1); ++ } ++ } ++ ++ Collection setIfUnexistant(final String arg1, final Collection arg2, ++ final Configuration configuration) { ++ if (!configuration.contains(arg1)) { ++ configuration.set(arg1, new ArrayList<>(arg2)); ++ ++ return arg2; ++ } else { ++ return new HashSet<>(configuration.getStringList(arg1)); ++ } ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +new file mode 100644 +index 000000000..241e8ed9c +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +@@ -0,0 +1,20 @@ ++package dev._2lstudios.flamecord.configuration; ++ ++import java.io.File; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.Collection; ++import java.util.HashSet; ++ ++import lombok.Getter; ++import net.md_5.bungee.config.Configuration; ++import net.md_5.bungee.config.ConfigurationProvider; ++ ++public class FlameCordConfiguration extends FlameConfig { ++ public FlameCordConfiguration(final ConfigurationProvider configurationProvider, final Collection whitelistedAddresses) { ++ final File configurationFile = new File("./flamecord.yml"); ++ final Configuration configuration = load(configurationFile); ++ ++ save(configuration, configurationFile); ++ } ++} +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 +index 640f0ea26..70a1192e7 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java +@@ -1,5 +1,6 @@ + package net.md_5.bungee.protocol; + ++import dev._2lstudios.flamecord.FlameCord; + import io.netty.buffer.ByteBuf; + import io.netty.buffer.ByteBufUtil; + import io.netty.channel.ChannelHandlerContext; +diff --git a/proxy/src/main/java/dev/_2lstudios/flamecord/commands/FlameCordCommand.java b/proxy/src/main/java/dev/_2lstudios/flamecord/commands/FlameCordCommand.java +new file mode 100644 +index 000000000..021b38d43 +--- /dev/null ++++ b/proxy/src/main/java/dev/_2lstudios/flamecord/commands/FlameCordCommand.java +@@ -0,0 +1,58 @@ ++package dev._2lstudios.flamecord.commands; ++ ++import java.util.Collection; ++import java.util.HashSet; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import net.md_5.bungee.BungeeCord; ++import net.md_5.bungee.api.CommandSender; ++import net.md_5.bungee.api.chat.TextComponent; ++import net.md_5.bungee.api.config.ServerInfo; ++import net.md_5.bungee.api.plugin.Command; ++ ++public class FlameCordCommand extends Command { ++private final BungeeCord bungeeCord; ++ ++ public FlameCordCommand(final BungeeCord bungeeCord) { ++ super("flamecord"); ++ ++ this.bungeeCord = bungeeCord; ++ } ++ ++ @Override ++ public void execute(final CommandSender sender, final String[] args) { ++ final FlameCord flameCord = FlameCord.getInstance(); ++ ++ if (sender.hasPermission("flamecord.usage")) { ++ if (args.length > 0) { ++ final String arg0 = args[0]; ++ ++ switch (arg0) { ++ case "reload": { ++ // FlameCord - Collect ips from servers ++ final Collection whitelistedAddresses = new HashSet<>(); ++ ++ for (final ServerInfo serverInfo : bungeeCord.getServers().values()) { ++ whitelistedAddresses.add(serverInfo.getSocketAddress().toString()); ++ } ++ ++ FlameCord.initialize(bungeeCord.getLogger(), whitelistedAddresses); ++ sender.sendMessage(TextComponent ++ .fromLegacyText("flamecord_reload")); ++ break; ++ } ++ default: { ++ sender.sendMessage(TextComponent.fromLegacyText("flamecord_help")); ++ break; ++ } ++ } ++ } else { ++ sender.sendMessage(TextComponent ++ .fromLegacyText("flamecord_help")); ++ } ++ } else { ++ sender.sendMessage(TextComponent ++ .fromLegacyText("flamecord_nopermission")); ++ } ++ } ++} +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +index 27d41dd00..0d8f02d25 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +@@ -9,6 +9,8 @@ import com.google.common.collect.Sets; + import com.google.common.util.concurrent.ThreadFactoryBuilder; + import com.google.gson.Gson; + import com.google.gson.GsonBuilder; ++ ++import dev._2lstudios.flamecord.FlameCord; + import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import io.github.waterfallmc.waterfall.conf.WaterfallConfiguration; + import io.github.waterfallmc.waterfall.event.ProxyExceptionEvent; +@@ -295,6 +297,15 @@ public class BungeeCord extends ProxyServer + pluginManager.loadPlugins(); + config.load(); + ++ // FlameCord - Renew and register modules ++ final Collection whitelistedAddresses = new HashSet<>(); ++ ++ for (final ServerInfo serverInfo : getServers().values()) { ++ whitelistedAddresses.add(serverInfo.getSocketAddress().toString()); ++ } ++ ++ FlameCord.initialize(logger, whitelistedAddresses); ++ + if ( config.isForgeSupport() ) + { + registerChannel( ForgeConstants.FML_TAG ); +diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +index da688839c..3a07c7c8a 100644 +--- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java ++++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +@@ -2,6 +2,8 @@ package net.md_5.bungee; + + import com.google.common.base.Joiner; + import com.google.common.base.Preconditions; ++ ++import dev._2lstudios.flamecord.FlameCord; + import io.netty.buffer.ByteBuf; + import io.netty.buffer.ByteBufAllocator; + import java.net.InetSocketAddress; +@@ -165,7 +167,9 @@ public class ServerConnector extends PacketHandler + { + if ( packet.packet == null ) + { +- throw new QuietException( "Unexpected packet received during server login process!\n" + BufUtil.dump( packet.buf, 16 ) ); ++ // FlameCord - FlameCord General Patch ++ // More specific invalid packet exceptions ++ throw new QuietException( "Unexpected packet received during server connector process!\n" + BufUtil.dump(packet.buf, 16) ); + } + } + +diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +index 94702434d..e48f77131 100644 +--- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java ++++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +@@ -35,6 +35,7 @@ import net.md_5.bungee.api.SkinConfiguration; + import net.md_5.bungee.api.Title; + import net.md_5.bungee.api.chat.BaseComponent; + import net.md_5.bungee.api.chat.TextComponent; ++import net.md_5.bungee.api.config.ListenerInfo; + import net.md_5.bungee.api.config.ServerInfo; + import net.md_5.bungee.api.connection.ProxiedPlayer; + import net.md_5.bungee.api.event.PermissionCheckEvent; +@@ -66,6 +67,7 @@ import net.md_5.bungee.tab.ServerUnique; + import net.md_5.bungee.tab.TabList; + import net.md_5.bungee.util.CaseInsensitiveSet; + import net.md_5.bungee.util.ChatComponentTransformer; ++import net.md_5.bungee.util.QuietException; + + @RequiredArgsConstructor + public final class UserConnection implements ProxiedPlayer +@@ -393,9 +395,11 @@ public final class UserConnection implements ProxiedPlayer + .option( ChannelOption.CONNECT_TIMEOUT_MILLIS, request.getConnectTimeout() ) + .remoteAddress( target.getAddress() ); + // Windows is bugged, multi homed users will just have to live with random connecting IPs +- if ( getPendingConnection().getListener().isSetLocalAddress() && !PlatformDependent.isWindows() && getPendingConnection().getListener().getSocketAddress() instanceof InetSocketAddress ) ++ // FlameCord - Use listenerInfo ++ final ListenerInfo listenerInfo = getPendingConnection().getListener(); ++ if ( listenerInfo.isSetLocalAddress() && !PlatformDependent.isWindows() && listenerInfo.getSocketAddress() instanceof InetSocketAddress ) + { +- b.localAddress( getPendingConnection().getListener().getHost().getHostString(), 0 ); ++ b.localAddress( listenerInfo.getHost().getHostString(), 0 ); + } + b.connect().addListener( listener ); + } +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 +index 3b1bffe6c..22688a045 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -21,6 +21,8 @@ import java.util.logging.Level; + import javax.crypto.SecretKey; + import javax.crypto.spec.SecretKeySpec; + ++import dev._2lstudios.flamecord.FlameCord; ++ + import lombok.Getter; + import lombok.RequiredArgsConstructor; + import net.md_5.bungee.BungeeCord; +@@ -159,7 +161,9 @@ public class InitialHandler extends PacketHandler implements PendingConnection + { + if ( packet.packet == null ) + { +- throw new QuietException( "Unexpected packet received during login process! " + BufUtil.dump( packet.buf, 16 ) ); ++ // FlameCord - FlameCord General Patch ++ // More specific invalid packet exceptions ++ throw new QuietException( "Unexpected packet received during initial handler process!\n" + BufUtil.dump(packet.buf, 16) ); + } + } + +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 +index a409d440b..3503c089d 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java +@@ -1,6 +1,7 @@ + package net.md_5.bungee.connection; + + import com.google.gson.Gson; ++ + import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import lombok.RequiredArgsConstructor; + import net.md_5.bungee.BungeeCord; +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 +index 4820267b3..694ecdb01 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java +@@ -1,6 +1,8 @@ + package net.md_5.bungee.netty; + + import com.google.common.base.Preconditions; ++ ++import dev._2lstudios.flamecord.FlameCord; + import io.netty.channel.ChannelHandlerContext; + import io.netty.channel.ChannelInboundHandlerAdapter; + import io.netty.handler.codec.CorruptedFrameException; +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 +index a95193ba7..a4c3bd710 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java +@@ -1,6 +1,8 @@ + package net.md_5.bungee.netty; + + import com.google.common.base.Preconditions; ++ ++import dev._2lstudios.flamecord.FlameCord; + import io.github.waterfallmc.waterfall.event.ConnectionInitEvent; + import io.netty.buffer.PooledByteBufAllocator; + import io.netty.channel.Channel; +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0019-FlameCord-module-system.patch b/Waterfall-Proxy-Patches/0019-FlameCord-module-system.patch new file mode 100644 index 0000000..5721771 --- /dev/null +++ b/Waterfall-Proxy-Patches/0019-FlameCord-module-system.patch @@ -0,0 +1,194 @@ +From 681805834a7f83cccb8de7878b8ffacbdb4b6d0d Mon Sep 17 00:00:00 2001 +From: linsaftw <25271111+linsaftw@users.noreply.github.com> +Date: Sat, 1 May 2021 14:17:48 -0300 +Subject: [PATCH] FlameCord module system + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java +index f3d5c0290..fb3d8ca2e 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java +@@ -4,6 +4,7 @@ import java.util.Collection; + import java.util.logging.Logger; + + import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++import dev._2lstudios.flamecord.configuration.ModulesConfiguration; + import lombok.Getter; + import net.md_5.bungee.config.ConfigurationProvider; + import net.md_5.bungee.config.YamlConfiguration; +@@ -23,9 +24,16 @@ public class FlameCord { + @Getter + private FlameCordConfiguration flameCordConfiguration; + ++ // FlameCord - Module System ++ @Getter ++ private ModulesConfiguration modulesConfiguration; ++ + public void reload(final Logger logger, final Collection whitelistedAddresses) { + final ConfigurationProvider configurationProvider = ConfigurationProvider.getProvider(YamlConfiguration.class); + + this.flameCordConfiguration = new FlameCordConfiguration(configurationProvider, whitelistedAddresses); ++ ++ // FlameCord - Module System ++ this.modulesConfiguration = new ModulesConfiguration(configurationProvider); + } + } +\ No newline at end of file +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/ModulesConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/ModulesConfiguration.java +new file mode 100644 +index 000000000..a6cb2638f +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/ModulesConfiguration.java +@@ -0,0 +1,35 @@ ++package dev._2lstudios.flamecord.configuration; ++ ++import java.io.File; ++ ++import net.md_5.bungee.config.Configuration; ++import net.md_5.bungee.config.ConfigurationProvider; ++ ++public class ModulesConfiguration extends FlameConfig { ++ public boolean reconnectEnabled = false; ++ public boolean alertEnabled = true; ++ public boolean findEnabled = true; ++ public boolean ipEnabled = true; ++ public boolean listEnabled = true; ++ public boolean permsEnabled = true; ++ public boolean reloadEnabled = true; ++ public boolean sendEnabled = true; ++ public boolean serverEnabled = true; ++ ++ public ModulesConfiguration(final ConfigurationProvider configurationProvider) { ++ final File configurationFile = new File("./modules.yml"); ++ final Configuration configuration = load(configurationFile); ++ ++ this.alertEnabled = setIfUnexistant("alert.enabled", this.alertEnabled, configuration); ++ this.findEnabled = setIfUnexistant("find.enabled", this.findEnabled, configuration); ++ this.ipEnabled = setIfUnexistant("ip.enabled", this.ipEnabled, configuration); ++ this.listEnabled = setIfUnexistant("list.enabled", this.listEnabled, configuration); ++ this.permsEnabled = setIfUnexistant("perms.enabled", this.permsEnabled, configuration); ++ this.reloadEnabled = setIfUnexistant("reload.enabled", this.reloadEnabled, configuration); ++ this.sendEnabled = setIfUnexistant("send.enabled", this.sendEnabled, configuration); ++ this.serverEnabled = setIfUnexistant("server.enabled", this.serverEnabled, configuration); ++ this.reconnectEnabled = setIfUnexistant("reconnect.enabled", this.reconnectEnabled, configuration); ++ ++ save(configuration, configurationFile); ++ } ++} +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +index 0d8f02d25..d58227fd9 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +@@ -11,6 +11,8 @@ import com.google.gson.Gson; + import com.google.gson.GsonBuilder; + + import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.commands.FlameCordCommand; ++import dev._2lstudios.flamecord.configuration.ModulesConfiguration; + import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import io.github.waterfallmc.waterfall.conf.WaterfallConfiguration; + import io.github.waterfallmc.waterfall.event.ProxyExceptionEvent; +@@ -90,6 +92,13 @@ import net.md_5.bungee.conf.Configuration; + import net.md_5.bungee.conf.YamlConfig; + import net.md_5.bungee.forge.ForgeConstants; + import net.md_5.bungee.module.ModuleManager; ++import net.md_5.bungee.module.cmd.alert.CommandAlert; ++import net.md_5.bungee.module.cmd.alert.CommandAlertRaw; ++import net.md_5.bungee.module.cmd.find.CommandFind; ++import net.md_5.bungee.module.cmd.list.CommandList; ++import net.md_5.bungee.module.cmd.send.CommandSend; ++import net.md_5.bungee.module.cmd.server.CommandServer; ++import net.md_5.bungee.module.reconnect.yaml.YamlReconnectHandler; + import net.md_5.bungee.netty.PipelineUtils; + import net.md_5.bungee.protocol.DefinedPacket; + import net.md_5.bungee.protocol.ProtocolConstants; +@@ -244,11 +253,6 @@ public class BungeeCord extends ProxyServer + // Waterfall end + + pluginManager = new PluginManager( this ); +- getPluginManager().registerCommand( null, new CommandReload() ); +- getPluginManager().registerCommand( null, new CommandEnd() ); +- getPluginManager().registerCommand( null, new CommandIP() ); +- getPluginManager().registerCommand( null, new CommandBungee() ); +- getPluginManager().registerCommand( null, new CommandPerms() ); + + if ( !Boolean.getBoolean( "net.md_5.bungee.native.disable" ) ) + { +@@ -287,9 +291,10 @@ public class BungeeCord extends ProxyServer + bossEventLoopGroup = PipelineUtils.newEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty Boss IO Thread #%1$d" ).build() ); + workerEventLoopGroup = PipelineUtils.newEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty Worker IO Thread #%1$d" ).build() ); + +- File moduleDirectory = new File( "modules" ); ++ // FlameCord - Use own module system ++ /* File moduleDirectory = new File( "modules" ); + moduleManager.load( this, moduleDirectory ); +- pluginManager.detectPlugins( moduleDirectory ); ++ pluginManager.detectPlugins( moduleDirectory ); */ + + pluginsFolder.mkdir(); + pluginManager.detectPlugins( pluginsFolder ); +@@ -315,6 +320,7 @@ public class BungeeCord extends ProxyServer + + isRunning = true; + ++ loadModules(); + pluginManager.enablePlugins(); + + if ( config.getThrottle() > 0 ) +@@ -853,4 +859,53 @@ public class BungeeCord extends ProxyServer + { + return new BungeeTitle(); + } ++ ++ // FlameCord - FlameCord Module System ++ public void loadModules() { ++ final ModulesConfiguration modulesConfiguration = FlameCord.getInstance().getModulesConfiguration(); ++ ++ // Bungeecord Commands ++ pluginManager.registerCommand(null, new CommandEnd()); ++ pluginManager.registerCommand(null, new CommandBungee()); ++ ++ if (modulesConfiguration.reloadEnabled) { ++ pluginManager.registerCommand(null, new CommandReload()); ++ } ++ if (modulesConfiguration.ipEnabled) { ++ pluginManager.registerCommand(null, new CommandIP()); ++ } ++ if (modulesConfiguration.permsEnabled) { ++ pluginManager.registerCommand(null, new CommandPerms()); ++ } ++ ++ // Modules Commands ++ if (modulesConfiguration.alertEnabled) { ++ pluginManager.registerCommand(null, new CommandAlert()); ++ pluginManager.registerCommand(null, new CommandAlertRaw()); ++ } ++ if (modulesConfiguration.findEnabled) { ++ pluginManager.registerCommand(null, new CommandFind()); ++ } ++ if (modulesConfiguration.listEnabled) { ++ pluginManager.registerCommand(null, new CommandList()); ++ } ++ if (modulesConfiguration.sendEnabled) { ++ pluginManager.registerCommand(null, new CommandSend()); ++ } ++ if (modulesConfiguration.serverEnabled) { ++ pluginManager.registerCommand(null, new CommandServer()); ++ } ++ ++ if (modulesConfiguration.reconnectEnabled) { ++ for (ListenerInfo info : getConfig().getListeners()) { ++ if (!info.isForceDefault() && getReconnectHandler() == null) { ++ setReconnectHandler(new YamlReconnectHandler()); ++ break; ++ } ++ } ++ } ++ ++ // Flamecord Command ++ pluginManager.registerCommand(null, new FlameCordCommand(this)); ++ } + } +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0020-FlameCord-message-configuration.patch b/Waterfall-Proxy-Patches/0020-FlameCord-message-configuration.patch new file mode 100644 index 0000000..9d030c6 --- /dev/null +++ b/Waterfall-Proxy-Patches/0020-FlameCord-message-configuration.patch @@ -0,0 +1,224 @@ +From 168e63822c7f19251ff48a96fb341b230d662143 Mon Sep 17 00:00:00 2001 +From: linsaftw <25271111+linsaftw@users.noreply.github.com> +Date: Sat, 1 May 2021 13:40:39 -0300 +Subject: [PATCH] FlameCord message configuration + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java +index fb3d8ca2e..676ba95b9 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java +@@ -4,6 +4,7 @@ import java.util.Collection; + import java.util.logging.Logger; + + import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++import dev._2lstudios.flamecord.configuration.MessagesConfiguration; + import dev._2lstudios.flamecord.configuration.ModulesConfiguration; + import lombok.Getter; + import net.md_5.bungee.config.ConfigurationProvider; +@@ -27,6 +28,8 @@ public class FlameCord { + // FlameCord - Module System + @Getter + private ModulesConfiguration modulesConfiguration; ++ @Getter ++ private MessagesConfiguration messagesConfiguration; + + public void reload(final Logger logger, final Collection whitelistedAddresses) { + final ConfigurationProvider configurationProvider = ConfigurationProvider.getProvider(YamlConfiguration.class); +@@ -35,5 +38,6 @@ public class FlameCord { + + // FlameCord - Module System + this.modulesConfiguration = new ModulesConfiguration(configurationProvider); ++ this.messagesConfiguration = new MessagesConfiguration(logger, configurationProvider); + } + } +\ No newline at end of file +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java +new file mode 100644 +index 000000000..ae1794385 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java +@@ -0,0 +1,102 @@ ++package dev._2lstudios.flamecord.configuration; ++ ++import java.io.File; ++import java.text.MessageFormat; ++import java.util.HashMap; ++import java.util.Map; ++import java.util.logging.Logger; ++ ++import net.md_5.bungee.api.ChatColor; ++import net.md_5.bungee.config.Configuration; ++import net.md_5.bungee.config.ConfigurationProvider; ++ ++public class MessagesConfiguration extends FlameConfig { ++ private final Logger logger; ++ private final Map messages = new HashMap<>(); ++ ++ public MessagesConfiguration(final Logger logger, final ConfigurationProvider configurationProvider) { ++ this.logger = logger; ++ ++ final File configurationFile = new File("./messages.yml"); ++ final Configuration configuration = load(configurationFile); ++ ++ // FlameCord ++ setIfUnexistant("flamecord_reload", "&aAll files had been successfully reloaded!", configuration); ++ setIfUnexistant("flamecord_help", ++ "&aFlameCord&b {0}&a by&b LinsaFTW\n&e /flamecord reload&7 >&b Reloads FlameCord files!\n&e /flamecord firewall &7 >&b Firewall certain ips!\n&e /bplugins&7 >&b Show the plugin list!\n&e /bip &7 >&b Show the ip and info of a player!\n&e /flamecord help&7 >&b Shows this message!", ++ configuration); ++ setIfUnexistant("flamecord_nopermission", "&cYou don't have permission to do this!", configuration); ++ ++ // BungeeCord ++ setIfUnexistant("alert", "&8[&4Alert&8]&r ", configuration); ++ setIfUnexistant("already_connected", "&cYou are already connected to this server!", configuration); ++ setIfUnexistant("already_connected_proxy", "&cYou are already connected to this proxy!", configuration); ++ setIfUnexistant("already_connecting", "&cAlready connecting to this server!", configuration); ++ setIfUnexistant("command_list", "&a[{0}] &e({1}): &r{2}", configuration); ++ setIfUnexistant("connect_kick", "&cKicked whilst connecting to {0}: {1}", configuration); ++ setIfUnexistant("current_server", "&6You are currently connected to {0}.", configuration); ++ setIfUnexistant("fallback_kick", ++ "&cCould not connect to a default or fallback server, please try again later: {0}", configuration); ++ setIfUnexistant("fallback_lobby", ++ "&cCould not connect to target server, you have been moved to a fallback server.", configuration); ++ setIfUnexistant("lost_connection", "[Proxy] Lost connection to server.", configuration); ++ setIfUnexistant("mojang_fail", "Error occurred while contacting login servers, are they down?", ++ configuration); ++ setIfUnexistant("no_permission", "&cYou do not have permission to execute this command!", configuration); ++ setIfUnexistant("no_server", "&cThe specified server does not exist.", configuration); ++ setIfUnexistant("no_server_permission", "&cYou don't have permission to access this server.", ++ configuration); ++ setIfUnexistant("outdated_client", "Outdated client! Please use {0}", configuration); ++ setIfUnexistant("outdated_server", "Outdated server! I'm still on {0}", configuration); ++ setIfUnexistant("proxy_full", "Server is full!", configuration); ++ setIfUnexistant("restart", "[Proxy] Proxy restarting.", configuration); ++ setIfUnexistant("server_list", "&6You may connect to the following servers at this time: ", configuration); ++ setIfUnexistant("server_went_down", ++ "&cThe server you were previously on went down, you have been connected to a fallback server", ++ configuration); ++ setIfUnexistant("total_players", "Total players online: {0}", configuration); ++ setIfUnexistant("name_invalid", "Username contains invalid characters.", configuration); ++ setIfUnexistant("ping_cannot_connect", "&c[Bungee] Can't connect to server.", configuration); ++ setIfUnexistant("offline_mode_player", "Not authenticated with Minecraft.net", configuration); ++ setIfUnexistant("secure_profile_required", "A secure profile is required to join this server.", configuration); ++ setIfUnexistant("secure_profile_expired", "Secure profile expired.", configuration); ++ setIfUnexistant("secure_profile_invalid", "Secure profile invalid.", configuration); ++ setIfUnexistant("message_needed", "&cYou must supply a message.", configuration); ++ setIfUnexistant("error_occurred_player", ++ "&cAn error occurred while parsing your message. (Hover for details)", configuration); ++ setIfUnexistant("error_occurred_console", "&cAn error occurred while parsing your message: {0}", ++ configuration); ++ setIfUnexistant("server_command_hover_players", "{0} players", configuration); ++ setIfUnexistant("click_to_connect", "Click to connect to the server", configuration); ++ setIfUnexistant("username_needed", "&cPlease follow this command by a user name.", configuration); ++ setIfUnexistant("user_not_online", "&cThat user is not online.", configuration); ++ setIfUnexistant("user_online_at", "&a{0} &ris online at {1}", configuration); ++ setIfUnexistant("send_cmd_usage", ++ "&cNot enough arguments, usage: /send ", configuration); ++ setIfUnexistant("player_only", "&cOnly in game players can use this command", configuration); ++ setIfUnexistant("you_got_summoned", "&6Summoned to {0} by {1}", configuration); ++ setIfUnexistant("command_perms_groups", "&6You have the following groups: {0}", configuration); ++ setIfUnexistant("command_perms_permission", "&9- {0}", configuration); ++ setIfUnexistant("command_ip", "&9IP of {0} is {1}", configuration); ++ setIfUnexistant("illegal_chat_characters", "&cIllegal characters in chat ({0})", configuration); ++ ++ for (final String key : configuration.getKeys()) { ++ final Object value = configuration.get(key); ++ ++ if (value instanceof String) { ++ this.messages.put(key, ChatColor.translateAlternateColorCodes('&', (String) value)); ++ } ++ } ++ ++ save(configuration, configurationFile); ++ } ++ ++ public String getTranslation(final String name, final Object... args) { ++ if (!messages.containsKey(name)) { ++ logger.warning("[FlameCord] Tried to get translation '" + name ++ + "' from messages.yml file but wasn't found. Please try resetting this file or report to a developer."); ++ } ++ ++ return MessageFormat.format(messages.getOrDefault(name, ""), args); ++ } ++} +\ No newline at end of file +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 +index 698b420f4..e4ad7e389 100644 +--- 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 +@@ -50,7 +50,9 @@ public class CommandServer extends Command implements TabExecutor + int count = server.getPlayers().size(); + serverTextComponent.setHoverEvent( new HoverEvent( + HoverEvent.Action.SHOW_TEXT, +- new ComponentBuilder( count + ( count == 1 ? " player" : " players" ) + "\n" ).appendLegacy( ProxyServer.getInstance().getTranslation( "click_to_connect" ) ).create() ) ++ // FlameCord - FlameCord messages configuration ++ // Make player count customizable ++ new ComponentBuilder( ProxyServer.getInstance().getTranslation( "server_command_hover_players", count ) + "\n" ).appendLegacy( ProxyServer.getInstance().getTranslation( "click_to_connect" ) ).create() ) + ); + serverTextComponent.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/server " + server.getName() ) ); + serverList.append( serverTextComponent ); +diff --git a/proxy/src/main/java/dev/_2lstudios/flamecord/commands/FlameCordCommand.java b/proxy/src/main/java/dev/_2lstudios/flamecord/commands/FlameCordCommand.java +index 021b38d43..fb81adee0 100644 +--- a/proxy/src/main/java/dev/_2lstudios/flamecord/commands/FlameCordCommand.java ++++ b/proxy/src/main/java/dev/_2lstudios/flamecord/commands/FlameCordCommand.java +@@ -4,6 +4,7 @@ import java.util.Collection; + import java.util.HashSet; + + import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.MessagesConfiguration; + import net.md_5.bungee.BungeeCord; + import net.md_5.bungee.api.CommandSender; + import net.md_5.bungee.api.chat.TextComponent; +@@ -22,6 +23,7 @@ private final BungeeCord bungeeCord; + @Override + public void execute(final CommandSender sender, final String[] args) { + final FlameCord flameCord = FlameCord.getInstance(); ++ final MessagesConfiguration messagesConfiguration = flameCord.getMessagesConfiguration(); + + if (sender.hasPermission("flamecord.usage")) { + if (args.length > 0) { +@@ -38,21 +40,22 @@ private final BungeeCord bungeeCord; + + FlameCord.initialize(bungeeCord.getLogger(), whitelistedAddresses); + sender.sendMessage(TextComponent +- .fromLegacyText("flamecord_reload")); ++ .fromLegacyText(messagesConfiguration.getTranslation("flamecord_reload"))); + break; + } + default: { +- sender.sendMessage(TextComponent.fromLegacyText("flamecord_help")); ++ sender.sendMessage(TextComponent.fromLegacyText( ++ messagesConfiguration.getTranslation("flamecord_help", bungeeCord.getVersion()))); + break; + } + } + } else { + sender.sendMessage(TextComponent +- .fromLegacyText("flamecord_help")); ++ .fromLegacyText(messagesConfiguration.getTranslation("flamecord_help", bungeeCord.getVersion()))); + } + } else { + sender.sendMessage(TextComponent +- .fromLegacyText("flamecord_nopermission")); ++ .fromLegacyText(messagesConfiguration.getTranslation("flamecord_nopermission"))); + } + } + } +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +index d58227fd9..8994454c7 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +@@ -620,8 +620,8 @@ public class BungeeCord extends ProxyServer + @Override + public String getTranslation(String name, Object... args) + { +- Format format = messageFormats.get( name ); +- return ( format != null ) ? format.format( args ) : ""; ++ // FlameCord - Use own translation system ++ return FlameCord.getInstance().getMessagesConfiguration().getTranslation(name, args); + } + + @Override +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0021-Fix-ByteBuf-memory-leaks.patch b/Waterfall-Proxy-Patches/0021-Fix-ByteBuf-memory-leaks.patch new file mode 100644 index 0000000..b77c30d --- /dev/null +++ b/Waterfall-Proxy-Patches/0021-Fix-ByteBuf-memory-leaks.patch @@ -0,0 +1,68 @@ +From 4121bc5ef2215f0621d449c5cf2073c47ed93947 Mon Sep 17 00:00:00 2001 +From: linsaftw +Date: Sat, 20 Mar 2021 12:36:25 -0300 +Subject: [PATCH] Fix ByteBuf memory leaks + + +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 +index 68916db79..688ff72d2 100644 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java ++++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java +@@ -155,11 +155,15 @@ public abstract class EntityMap + if ( readId == oldId || readId == newId ) + { + ByteBuf data = packet.copy(); +- packet.readerIndex( offset ); +- packet.writerIndex( offset ); +- DefinedPacket.writeVarInt( readId == oldId ? newId : oldId, packet ); +- packet.writeBytes( data ); +- data.release(); ++ ++ try { ++ packet.readerIndex( offset ); ++ packet.writerIndex( offset ); ++ DefinedPacket.writeVarInt( readId == oldId ? newId : oldId, packet ); ++ packet.writeBytes( data ); ++ } finally { ++ data.release(); ++ } + } + } + +diff --git a/proxy/src/main/java/net/md_5/bungee/forge/ForgeUtils.java b/proxy/src/main/java/net/md_5/bungee/forge/ForgeUtils.java +index cefa0206e..8d4439ab8 100644 +--- a/proxy/src/main/java/net/md_5/bungee/forge/ForgeUtils.java ++++ b/proxy/src/main/java/net/md_5/bungee/forge/ForgeUtils.java +@@ -38,16 +38,22 @@ public class ForgeUtils + { + Map modTags = new HashMap<>(); + ByteBuf payload = Unpooled.wrappedBuffer( pluginMessage.getData() ); +- byte discriminator = payload.readByte(); +- if ( discriminator == 2 ) // ModList +- { +- ByteBuf buffer = payload.slice(); +- int modCount = DefinedPacket.readVarInt( buffer, 2 ); +- for ( int i = 0; i < modCount; i++ ) ++ ++ try { ++ byte discriminator = payload.readByte(); ++ if ( discriminator == 2 ) // ModList + { +- modTags.put( DefinedPacket.readString( buffer ), DefinedPacket.readString( buffer ) ); ++ ByteBuf buffer = payload.slice(); ++ int modCount = DefinedPacket.readVarInt( buffer, 2 ); ++ for ( int i = 0; i < modCount; i++ ) ++ { ++ modTags.put( DefinedPacket.readString( buffer ), DefinedPacket.readString( buffer ) ); ++ } + } ++ } finally { ++ payload.release(); + } ++ + return modTags; + } + +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0022-Check-if-packetID-is-invalid.patch b/Waterfall-Proxy-Patches/0022-Check-if-packetID-is-invalid.patch new file mode 100644 index 0000000..07bc7ec --- /dev/null +++ b/Waterfall-Proxy-Patches/0022-Check-if-packetID-is-invalid.patch @@ -0,0 +1,27 @@ +From 7e20c899237e194fbda629aa6be4444fbe293b7d Mon Sep 17 00:00:00 2001 +From: PermisosDev <55111245+PermisosDev@users.noreply.github.com> +Date: Thu, 15 Apr 2021 08:34:57 -0300 +Subject: [PATCH] Check if packetID is invalid. + + +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 +index 70a1192e7..6316143f7 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java +@@ -65,7 +65,12 @@ public class MinecraftDecoder extends MessageToMessageDecoder + + int packetId = DefinedPacket.readVarInt( in ); + packetTypeInfo = packetId; +- ++ // FlameCord Start - Check if packetId is invalid. ++ if (packetId < 0 || packetId > Protocol.MAX_PACKET_ID) ++ { ++ throw new FastDecoderException( "[" + ctx.channel().remoteAddress() + "] <-> MinecraftDecoder received invalid packet #1, id " + packetId ); ++ } ++ // Flamecord End + DefinedPacket packet = prot.createPacket( packetId, protocolVersion, supportsForge ); + if ( packet != null ) + { +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0023-FlameCord-logger-options.patch b/Waterfall-Proxy-Patches/0023-FlameCord-logger-options.patch new file mode 100644 index 0000000..6f49bf5 --- /dev/null +++ b/Waterfall-Proxy-Patches/0023-FlameCord-logger-options.patch @@ -0,0 +1,140 @@ +From 1d9ee842e043c343a64b8fe7ef6962ee0fb53041 Mon Sep 17 00:00:00 2001 +From: linsaftw <25271111+linsaftw@users.noreply.github.com> +Date: Fri, 30 Apr 2021 23:51:51 -0300 +Subject: [PATCH] FlameCord logger options + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +index 241e8ed9c..b563cbced 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +@@ -11,10 +11,27 @@ import net.md_5.bungee.config.Configuration; + import net.md_5.bungee.config.ConfigurationProvider; + + public class FlameCordConfiguration extends FlameConfig { ++ @Getter ++ private boolean loggerInitialhandler = false; ++ @Getter ++ private boolean loggerExceptions = false; ++ @Getter ++ private boolean loggerDump = false; ++ @Getter ++ private boolean loggerHaProxy = false; ++ @Getter ++ private boolean loggerDetailedConnection = true; ++ + public FlameCordConfiguration(final ConfigurationProvider configurationProvider, final Collection whitelistedAddresses) { + final File configurationFile = new File("./flamecord.yml"); + final Configuration configuration = load(configurationFile); + ++ this.loggerInitialhandler = setIfUnexistant("logger.initialhandler", this.loggerInitialhandler, configuration); ++ this.loggerExceptions = setIfUnexistant("logger.exceptions", this.loggerExceptions, configuration); ++ this.loggerDump = setIfUnexistant("logger.dump", this.loggerDump, configuration); ++ this.loggerHaProxy = setIfUnexistant("logger.haproxy", this.loggerHaProxy, configuration); ++ this.loggerDetailedConnection = setIfUnexistant("logger.detailed-connect-errors", this.loggerDetailedConnection, configuration); ++ + save(configuration, configurationFile); + } + } +diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +index e48f77131..909227970 100644 +--- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java ++++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +@@ -4,6 +4,7 @@ import com.google.common.base.Preconditions; + import com.google.common.collect.HashMultimap; + import com.google.common.collect.ImmutableMap; + import com.google.common.collect.Multimap; ++import dev._2lstudios.flamecord.FlameCord; + import io.netty.bootstrap.Bootstrap; + import io.netty.channel.Channel; + import io.netty.channel.ChannelFuture; +@@ -406,7 +407,10 @@ public final class UserConnection implements ProxiedPlayer + + private String connectionFailMessage(Throwable cause) + { +- bungee.getLogger().log(Level.WARNING, "Error occurred processing connection for " + this.name + " " + Util.exception( cause, false )); // Waterfall ++ // FlameCord - Allow for toggle the logging of connection failures ++ if(FlameCord.getInstance().getFlameCordConfiguration().isLoggerDetailedConnection()) { ++ bungee.getLogger().log(Level.WARNING, "Error occurred processing connection for " + this.name + " " + Util.exception(cause, false)); // Waterfall ++ } + return ""; // Waterfall + } + +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 +index 22688a045..66a347542 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -396,7 +396,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection + { + case 1: + // Ping +- if ( bungee.getConfig().isLogPings() ) ++ // FlameCord - Option to log initialhandler ++ if ( bungee.getConfig().isLogPings() && FlameCord.getInstance().getFlameCordConfiguration().isLoggerInitialhandler() ) + { + bungee.getLogger().log( Level.INFO, "{0} has pinged", this ); + } +@@ -405,7 +406,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection + break; + case 2: + // Login +- if (BungeeCord.getInstance().getConfig().isLogInitialHandlerConnections() ) // Waterfall ++ // FlameCord - Option to log initialhandler ++ if (BungeeCord.getInstance().getConfig().isLogInitialHandlerConnections() && FlameCord.getInstance().getFlameCordConfiguration().isLoggerInitialhandler() ) // Waterfall + { + bungee.getLogger().log( Level.INFO, "{0} has connected", this ); + } +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 +index 694ecdb01..14e3004fc 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java +@@ -46,7 +46,8 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter + channel = new ChannelWrapper( ctx ); + handler.connected( channel ); + +- if ( !( handler instanceof InitialHandler || handler instanceof PingHandler ) ) ++ // FlameCord - Option to log initialhandler ++ if ( !( handler instanceof InitialHandler || handler instanceof PingHandler ) && FlameCord.getInstance().getFlameCordConfiguration().isLoggerInitialhandler() ) + { + ProxyServer.getInstance().getLogger().log( Level.INFO, "{0} has connected", handler ); + } +@@ -61,7 +62,8 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter + channel.markClosed(); + handler.disconnected( channel ); + +- if ( !( handler instanceof InitialHandler || handler instanceof PingHandler ) ) ++ // FlameCord - Option to log initialhandler ++ if ( !( handler instanceof InitialHandler || handler instanceof PingHandler ) && FlameCord.getInstance().getFlameCordConfiguration().isLoggerInitialhandler() ) + { + ProxyServer.getInstance().getLogger().log( Level.INFO, "{0} has disconnected", handler ); + } +@@ -98,10 +100,12 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter + { + InetSocketAddress newAddress = new InetSocketAddress( proxy.sourceAddress(), proxy.sourcePort() ); + +- ProxyServer.getInstance().getLogger().log( Level.FINE, "Set remote address via PROXY {0} -> {1}", new Object[] +- { +- channel.getRemoteAddress(), newAddress +- } ); ++ // FlameCord - Option to log haproxy ++ if ( FlameCord.getInstance().getFlameCordConfiguration().isLoggerHaProxy() ) ++ ProxyServer.getInstance().getLogger().log( Level.FINE, "Set remote address via PROXY {0} -> {1}", new Object[] ++ { ++ channel.getRemoteAddress(), newAddress ++ } ); + + channel.setRemoteAddress( newAddress ); + } +@@ -146,6 +150,9 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter + { + boolean logExceptions = !( handler instanceof PingHandler ); + ++ // FlameCord - Option to log exceptions ++ logExceptions = FlameCord.getInstance().getFlameCordConfiguration().isLoggerExceptions() ? logExceptions : false; ++ + if ( logExceptions ) + { + if ( cause instanceof ReadTimeoutException ) +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0024-Added-an-api-method-to-unload-Plugins.patch b/Waterfall-Proxy-Patches/0024-Added-an-api-method-to-unload-Plugins.patch new file mode 100644 index 0000000..9464018 --- /dev/null +++ b/Waterfall-Proxy-Patches/0024-Added-an-api-method-to-unload-Plugins.patch @@ -0,0 +1,86 @@ +From 664f35c5a6155c5e41aa10d1cd15cf590cc011a0 Mon Sep 17 00:00:00 2001 +From: abhiram +Date: Thu, 13 May 2021 01:05:03 +0530 +Subject: [PATCH] Added an api method to unload Plugins + +Format Code to Bungee's code style + +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 +index 90031156f..a190dfae8 100644 +--- 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 +@@ -25,6 +25,7 @@ import java.util.Set; + import java.util.Stack; + import java.util.jar.JarEntry; + import java.util.jar.JarFile; ++import java.util.logging.Handler; + import java.util.logging.Level; + import io.github.waterfallmc.waterfall.event.ProxyExceptionEvent; // Waterfall + import io.github.waterfallmc.waterfall.exception.ProxyCommandException; // Waterfall +@@ -63,6 +64,7 @@ public final class PluginManager + private Map toLoad = new HashMap<>(); + private final Multimap commandsByPlugin = ArrayListMultimap.create(); + private final Multimap listenersByPlugin = ArrayListMultimap.create(); ++ private final HashMap pluginloaders = new HashMap<>(); + + @SuppressWarnings("unchecked") + public PluginManager(ProxyServer proxy) +@@ -291,6 +293,45 @@ public final class PluginManager + return plugins.get( name ); + } + ++ ++ /** ++ * This will Unload the given plugin ++ * ++ * @param plugin to unload ++ */ ++ // FlameCord start - Adds a method to unload plugin from proxy ++ public void unloadPlugin(Plugin plugin) ++ { ++ plugin.onDisable(); ++ this.unregisterListeners( plugin ); ++ this.unregisterCommands( plugin ); ++ this.proxy.getScheduler().cancel( plugin ); ++ ++ for ( Handler handler : plugin.getLogger().getHandlers() ) ++ { ++ handler.close(); ++ } ++ ++ try ++ { ++ pluginloaders.get( plugin.getDescription().getName() ).close(); ++ }catch (Exception exception) ++ { ++ exception.printStackTrace(); ++ } ++ ++ if( this.plugins.containsKey( plugin.getDescription().getName() ) ) ++ { ++ this.plugins.remove( plugin.getDescription().getName() ); ++ } ++ ++ if( this.pluginloaders.containsKey( plugin.getDescription().getName() ) ) ++ { ++ this.pluginloaders.remove( plugin.getDescription().getName() ); ++ } ++ } ++ // FlameCord end ++ + public void loadPlugins() + { + Map pluginStatuses = new HashMap<>(); +@@ -395,6 +436,9 @@ public final class PluginManager + Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance(); + + plugins.put( plugin.getName(), clazz ); ++ // FlameCord start - cache url classloaders to unload plugins ++ pluginloaders.put( plugin.getName(),loader ); ++ // FlameCord end + clazz.onLoad(); + ProxyServer.getInstance().getLogger().log( Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[] + { +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0025-Apply-packet-limits.patch b/Waterfall-Proxy-Patches/0025-Apply-packet-limits.patch new file mode 100644 index 0000000..17fe687 --- /dev/null +++ b/Waterfall-Proxy-Patches/0025-Apply-packet-limits.patch @@ -0,0 +1,69 @@ +From 2caae71d8c0bdc5a325610ef1c77775f9ba4ee69 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Thu, 10 Jun 2021 11:30:19 -0300 +Subject: [PATCH] Apply packet limits + + +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java +index e78519964..222285cc1 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java +@@ -61,4 +61,14 @@ public class EncryptionRequest extends DefinedPacket + { + handler.handle( this ); + } ++ ++ @Override ++ public int expectedMaxLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { ++ return 20 + 256 + 256; // FlameCord - Apply packet limits ++ } ++ ++ @Override ++ public int expectedMinLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { ++ return 20 + 1 + 1; // FlameCord - Apply packet limits ++ } + } +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Handshake.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Handshake.java +index 7dbbfd3cd..b78420556 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Handshake.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Handshake.java +@@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode; + import lombok.NoArgsConstructor; + import net.md_5.bungee.protocol.AbstractPacketHandler; + import net.md_5.bungee.protocol.DefinedPacket; ++import net.md_5.bungee.protocol.ProtocolConstants; + + @Data + @NoArgsConstructor +@@ -43,4 +44,14 @@ public class Handshake extends DefinedPacket + { + handler.handle( this ); + } ++ ++ @Override ++ public int expectedMaxLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { ++ return 1036 + 256; // FlameCord - Apply packet limits ++ } ++ ++ @Override ++ public int expectedMinLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { ++ return 5; // FlameCord - Apply packet limits ++ } + } +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java +index 3224b0d2b..763d34c2f 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java +@@ -70,7 +70,8 @@ public class LoginRequest extends DefinedPacket + public int expectedMaxLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { + // Accommodate the rare (but likely malicious) use of UTF-8 usernames, since it is technically + // legal on the protocol level. +- if (protocolVersion >= ProtocolConstants.MINECRAFT_1_19) return -1; ++ // FlameCord - Apply packet limits ++ if (protocolVersion >= ProtocolConstants.MINECRAFT_1_19) return 1 + (16 * 4) + 1024; + return 1 + (16 * 4); + } + // Waterfall end +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0026-Custom-motd-system.patch b/Waterfall-Proxy-Patches/0026-Custom-motd-system.patch new file mode 100644 index 0000000..2ecfe04 --- /dev/null +++ b/Waterfall-Proxy-Patches/0026-Custom-motd-system.patch @@ -0,0 +1,676 @@ +From 2072d8d33f059abfdb95c16158792887f3f701d3 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Thu, 7 Oct 2021 21:37:24 -0300 +Subject: [PATCH] Custom motd system + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +index b563cbced..b41ee92cc 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +@@ -2,15 +2,94 @@ package dev._2lstudios.flamecord.configuration; + + import java.io.File; + import java.util.ArrayList; ++import java.util.Collections; + import java.util.Arrays; + import java.util.Collection; + import java.util.HashSet; ++import java.util.List; ++import java.util.Random; + ++import dev._2lstudios.flamecord.utils.ColorUtil; + import lombok.Getter; + import net.md_5.bungee.config.Configuration; + import net.md_5.bungee.config.ConfigurationProvider; + + public class FlameCordConfiguration extends FlameConfig { ++ public String getMOTD(int maxPlayers, int onlinePlayers, int protocol) { ++ String motd; ++ ++ if (protocol >= 735) { ++ motd = hexMotds.get(new Random().nextInt(hexMotds.size())); ++ } else { ++ motd = motds.get(new Random().nextInt(motds.size())); ++ } ++ ++ motd = motd.replace("%maxplayers%", String.valueOf(maxPlayers)).replace("%onlineplayers%", String.valueOf(onlinePlayers)); ++ ++ return motd; ++ } ++ ++ public String[] getSample(int maxPlayers, int onlinePlayers, int protocol) { ++ String sample; ++ ++ if (protocol >= 735) { ++ sample = hexSamples.get(new Random().nextInt(hexSamples.size())); ++ } else { ++ sample = samples.get(new Random().nextInt(samples.size())); ++ } ++ ++ sample = sample.replace("%maxplayers%", String.valueOf(maxPlayers)).replace("%onlineplayers%", String.valueOf(onlinePlayers)); ++ ++ return sample.split("\n"); ++ } ++ ++ public String getProtocolName(int maxPlayers, int onlinePlayers) { ++ return protocolName.replace("%maxplayers%", String.valueOf(maxPlayers)).replace("%onlineplayers%", String.valueOf(onlinePlayers)); ++ } ++ ++ public int getFakePlayersAmount(final int players) { ++ switch (fakePlayersMode) { ++ case "STATIC": ++ return fakePlayersAmount; ++ case "RANDOM": ++ return (int) (Math.floor(Math.random() * fakePlayersAmount) + 1); ++ case "DIVISION": ++ return players / fakePlayersAmount; ++ default: ++ return 0; ++ } ++ } ++ ++ @Getter ++ private boolean motdEnabled = false; ++ private List motds = Collections.singletonList("&eDefault &cFlameCord&e server &7(%onlineplayers%/%maxplayers%)\n&eEdit on &cflamecord.yml&7 (IridiumColorAPI HEX)"); ++ private List hexMotds; ++ ++ @Getter ++ private boolean sampleEnabled = false; ++ private List samples = Collections.singletonList("&eDefault &cFlameCord&e server &7(%onlineplayers%/%maxplayers%)\n&eEdit on &cflamecord.yml&7 (IridiumColorAPI HEX)"); ++ private List hexSamples; ++ ++ @Getter ++ private boolean protocolEnabled = false; ++ @Getter ++ private String protocolName = "&c&lMaintenance"; ++ @Getter ++ private boolean protocolAlwaysShow = false; ++ ++ @Getter ++ private boolean maxPlayersEnabled = false; ++ @Getter ++ private int maxPlayersAmount = 1000; ++ @Getter ++ private boolean maxPlayersOneMore = false; ++ ++ @Getter ++ private boolean fakePlayersEnabled = false; ++ @Getter ++ private int fakePlayersAmount = 3; ++ private String fakePlayersMode = "DIVISION"; ++ + @Getter + private boolean loggerInitialhandler = false; + @Getter +@@ -32,6 +111,22 @@ public class FlameCordConfiguration extends FlameConfig { + this.loggerHaProxy = setIfUnexistant("logger.haproxy", this.loggerHaProxy, configuration); + this.loggerDetailedConnection = setIfUnexistant("logger.detailed-connect-errors", this.loggerDetailedConnection, configuration); + ++ this.motdEnabled = setIfUnexistant("custom-motd.motd.enabled", this.motdEnabled, configuration); ++ this.hexMotds = ColorUtil.hexColor(new ArrayList<>(setIfUnexistant("custom-motd.motd.motds", this.motds, configuration)), 735); ++ this.motds = ColorUtil.hexColor(new ArrayList<>(setIfUnexistant("custom-motd.motd.motds", this.motds, configuration)), 734); ++ this.sampleEnabled = setIfUnexistant("custom-motd.sample.enabled", this.sampleEnabled, configuration); ++ this.hexSamples = ColorUtil.hexColor(new ArrayList<>(setIfUnexistant("custom-motd.sample.samples", this.samples, configuration)), 735); ++ this.samples = ColorUtil.hexColor(new ArrayList<>(setIfUnexistant("custom-motd.sample.samples", this.samples, configuration)), 734); ++ this.protocolEnabled = setIfUnexistant("custom-motd.protocol.enabled", this.protocolEnabled, configuration); ++ this.protocolName = ColorUtil.hexColor(setIfUnexistant("custom-motd.protocol.name", this.protocolName, configuration), 735); ++ this.protocolAlwaysShow = setIfUnexistant("custom-motd.protocol.always-show", this.protocolAlwaysShow, configuration); ++ this.maxPlayersEnabled = setIfUnexistant("custom-motd.maxplayers.enabled", this.maxPlayersEnabled, configuration); ++ this.maxPlayersAmount = setIfUnexistant("custom-motd.maxplayers.amount", this.maxPlayersAmount, configuration); ++ this.maxPlayersOneMore = setIfUnexistant("custom-motd.maxplayers.justonemore", this.maxPlayersOneMore, configuration); ++ this.fakePlayersEnabled = setIfUnexistant("custom-motd.fakeplayers.enabled", this.fakePlayersEnabled, configuration); ++ this.fakePlayersAmount = setIfUnexistant("custom-motd.fakeplayers.amount", this.fakePlayersAmount, configuration); ++ this.fakePlayersMode = setIfUnexistant("custom-motd.fakeplayers.mode", this.fakePlayersMode, configuration); ++ + save(configuration, configurationFile); + } + } +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/ColorUtil.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/ColorUtil.java +new file mode 100644 +index 000000000..9587bccda +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/ColorUtil.java +@@ -0,0 +1,48 @@ ++package dev._2lstudios.flamecord.utils; ++ ++import java.util.List; ++ ++import dev._2lstudios.flamecord.utils.iridiumcolorapi.IridiumColorAPI; ++import net.md_5.bungee.api.ChatColor; ++ ++public class ColorUtil { ++ public static String color(String text) { ++ return ChatColor.translateAlternateColorCodes('&', text); ++ } ++ ++ public static List color(final List texts) { ++ for (int i = 0; i < texts.size(); i++) { ++ texts.set(i, color(texts.get(i))); ++ } ++ ++ return texts; ++ } ++ ++ public static String hex(String text, int protocol) { ++ if (text == null) { ++ return text; ++ } ++ ++ return IridiumColorAPI.process(text, protocol); ++ } ++ ++ public static String hexColor(String text, int protocol) { ++ if (text != null) { ++ text = color(text); ++ ++ if (text != null) { ++ text = hex(text, protocol); ++ } ++ } ++ ++ return text; ++ } ++ ++ public static List hexColor(final List texts, int protocol) { ++ for (int i = 0; i < texts.size(); i++) { ++ texts.set(i, hexColor(texts.get(i), protocol)); ++ } ++ ++ return texts; ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/IridiumColorAPI.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/IridiumColorAPI.java +new file mode 100644 +index 000000000..6c386e16f +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/IridiumColorAPI.java +@@ -0,0 +1,275 @@ ++package dev._2lstudios.flamecord.utils.iridiumcolorapi; ++ ++import java.awt.Color; ++import java.util.Arrays; ++import java.util.Collection; ++import java.util.List; ++import java.util.Map; ++import java.util.stream.Collectors; ++ ++import javax.annotation.Nonnull; ++ ++import com.google.common.collect.ImmutableMap; ++ ++import dev._2lstudios.flamecord.utils.iridiumcolorapi.patterns.GradientPattern; ++import dev._2lstudios.flamecord.utils.iridiumcolorapi.patterns.Pattern; ++import dev._2lstudios.flamecord.utils.iridiumcolorapi.patterns.RainbowPattern; ++import dev._2lstudios.flamecord.utils.iridiumcolorapi.patterns.SolidPattern; ++import net.md_5.bungee.api.ChatColor; ++ ++public class IridiumColorAPI { ++ ++ /** ++ * Cached result if the server version is after the v1.16 RGB update. ++ * ++ * @since 1.0.0 ++ */ ++ private static final boolean SUPPORTS_RGB(int protocol) { ++ return protocol >= 735; ++ } ++ ++ private static final List SPECIAL_COLORS = Arrays.asList("&l", "&n", "&o", "&k", "&m", "§l", "§n", "§o", "§k", "§m"); ++ ++ /** ++ * Cached result of all legacy colors. ++ * ++ * @since 1.0.0 ++ */ ++ private static final Map COLORS = ImmutableMap.builder() ++ .put(new Color(0), ChatColor.getByChar('0')) ++ .put(new Color(170), ChatColor.getByChar('1')) ++ .put(new Color(43520), ChatColor.getByChar('2')) ++ .put(new Color(43690), ChatColor.getByChar('3')) ++ .put(new Color(11141120), ChatColor.getByChar('4')) ++ .put(new Color(11141290), ChatColor.getByChar('5')) ++ .put(new Color(16755200), ChatColor.getByChar('6')) ++ .put(new Color(11184810), ChatColor.getByChar('7')) ++ .put(new Color(5592405), ChatColor.getByChar('8')) ++ .put(new Color(5592575), ChatColor.getByChar('9')) ++ .put(new Color(5635925), ChatColor.getByChar('a')) ++ .put(new Color(5636095), ChatColor.getByChar('b')) ++ .put(new Color(16733525), ChatColor.getByChar('c')) ++ .put(new Color(16733695), ChatColor.getByChar('d')) ++ .put(new Color(16777045), ChatColor.getByChar('e')) ++ .put(new Color(16777215), ChatColor.getByChar('f')).build(); ++ ++ /** ++ * Cached result of patterns. ++ * ++ * @since 1.0.2 ++ */ ++ private static final List PATTERNS = Arrays.asList(new GradientPattern(), new SolidPattern(), new RainbowPattern()); ++ ++ /** ++ * Processes a string to add color to it. ++ * Thanks to Distressing for helping with the regex <3 ++ * ++ * @param string The string we want to process ++ * @since 1.0.0 ++ */ ++ @Nonnull ++ public static String process(@Nonnull String string, int protocol) { ++ for (Pattern pattern : PATTERNS) { ++ string = pattern.process(string, protocol); ++ } ++ ++ string = ChatColor.translateAlternateColorCodes('&', string); ++ return string; ++ } ++ ++ /** ++ * Processes multiple strings in a collection. ++ * ++ * @param strings The collection of the strings we are processing ++ * @return The list of processed strings ++ * @since 1.0.3 ++ */ ++ @Nonnull ++ public static List process(@Nonnull Collection strings, int protocol) { ++ return strings.stream() ++ .map(s -> IridiumColorAPI.process(s, protocol)) ++ .collect(Collectors.toList()); ++ } ++ ++ /** ++ * Colors a String. ++ * ++ * @param string The string we want to color ++ * @param color The color we want to set it to ++ * @since 1.0.0 ++ */ ++ @Nonnull ++ public static String color(@Nonnull String string, @Nonnull Color color, int protocol) { ++ return (SUPPORTS_RGB(protocol) ? ChatColor.of(color) : getClosestColor(color)) + string; ++ } ++ ++ /** ++ * Colors a String with a gradiant. ++ * ++ * @param string The string we want to color ++ * @param start The starting gradiant ++ * @param end The ending gradiant ++ * @since 1.0.0 ++ */ ++ @Nonnull ++ public static String color(@Nonnull String string, @Nonnull Color start, @Nonnull Color end, int protocol) { ++ String originalString = string; ++ ++ ChatColor[] colors = createGradient(start, end, withoutSpecialChar(string).length(), protocol); ++ return apply(originalString, colors); ++ } ++ ++ /** ++ * Colors a String with rainbow colors. ++ * ++ * @param string The string which should have rainbow colors ++ * @param saturation The saturation of the rainbow colors ++ * @since 1.0.3 ++ */ ++ @Nonnull ++ public static String rainbow(@Nonnull String string, float saturation, int protocol) { ++ String originalString = string; ++ ++ ChatColor[] colors = createRainbow(withoutSpecialChar(string).length(), saturation, protocol); ++ return apply(originalString, colors); ++ } ++ ++ /** ++ * Gets a color from hex code. ++ * ++ * @param string The hex code of the color ++ * @since 1.0.0 ++ */ ++ @Nonnull ++ public static ChatColor getColor(@Nonnull String string, int protocol) { ++ return SUPPORTS_RGB(protocol) ? ChatColor.of(new Color(Integer.parseInt(string, 16))) ++ : getClosestColor(new Color(Integer.parseInt(string, 16))); ++ } ++ ++ /** ++ * Removes all color codes from the provided String, including IridiumColorAPI ++ * patterns. ++ * ++ * @param string The String which should be stripped ++ * @return The stripped string without color codes ++ * @since 1.0.5 ++ */ ++ @Nonnull ++ public static String stripColorFormatting(@Nonnull String string) { ++ return string.replaceAll("<#[0-9A-F]{6}>|[&§][a-f0-9lnokm]|<[/]?[A-Z]{5,8}(:[0-9A-F]{6})?[0-9]*>", ""); ++ } ++ ++ @Nonnull ++ private static String apply(@Nonnull String source, ChatColor[] colors) { ++ StringBuilder specialColors = new StringBuilder(); ++ StringBuilder stringBuilder = new StringBuilder(); ++ String[] characters = source.split(""); ++ int outIndex = 0; ++ for (int i = 0; i < characters.length; i++) { ++ if (characters[i].equals("&") || characters[i].equals("§")) { ++ if (i + 1 < characters.length) { ++ if (characters[i + 1].equals("r")) { ++ specialColors.setLength(0); ++ } else { ++ specialColors.append(characters[i]); ++ specialColors.append(characters[i + 1]); ++ } ++ i++; ++ } else ++ stringBuilder.append(colors[outIndex++]).append(specialColors).append(characters[i]); ++ } else ++ stringBuilder.append(colors[outIndex++]).append(specialColors).append(characters[i]); ++ } ++ return stringBuilder.toString(); ++ } ++ ++ @Nonnull ++ private static String withoutSpecialChar(@Nonnull String source) { ++ String workingString = source; ++ for (String color : SPECIAL_COLORS) { ++ if (workingString.contains(color)) { ++ workingString = workingString.replace(color, ""); ++ } ++ } ++ return workingString; ++ } ++ ++ /** ++ * Returns a rainbow array of chat colors. ++ * ++ * @param step How many colors we return ++ * @param saturation The saturation of the rainbow ++ * @return The array of colors ++ * @since 1.0.3 ++ */ ++ @Nonnull ++ private static ChatColor[] createRainbow(int step, float saturation, int protocol) { ++ ChatColor[] colors = new ChatColor[step]; ++ double colorStep = (1.00 / step); ++ ++ for (int i = 0; i < step; i++) { ++ Color color = Color.getHSBColor((float) (colorStep * i), saturation, saturation); ++ if (SUPPORTS_RGB(protocol)) { ++ colors[i] = ChatColor.of(color); ++ } else { ++ colors[i] = getClosestColor(color); ++ } ++ } ++ ++ return colors; ++ } ++ ++ /** ++ * Returns a gradient array of chat colors. ++ * ++ * @param start The starting color. ++ * @param end The ending color. ++ * @param step How many colors we return. ++ * @author TheViperShow ++ * @since 1.0.0 ++ */ ++ @Nonnull ++ private static ChatColor[] createGradient(@Nonnull Color start, @Nonnull Color end, int step, int protocol) { ++ ChatColor[] colors = new ChatColor[step]; ++ int stepR = Math.abs(start.getRed() - end.getRed()) / (step - 1); ++ int stepG = Math.abs(start.getGreen() - end.getGreen()) / (step - 1); ++ int stepB = Math.abs(start.getBlue() - end.getBlue()) / (step - 1); ++ int[] direction = new int[] { ++ start.getRed() < end.getRed() ? +1 : -1, ++ start.getGreen() < end.getGreen() ? +1 : -1, ++ start.getBlue() < end.getBlue() ? +1 : -1 ++ }; ++ ++ for (int i = 0; i < step; i++) { ++ Color color = new Color(start.getRed() + ((stepR * i) * direction[0]), start.getGreen() + ((stepG * i) * direction[1]), start.getBlue() + ((stepB * i) * direction[2])); ++ if (SUPPORTS_RGB(protocol)) { ++ colors[i] = ChatColor.of(color); ++ } else { ++ colors[i] = getClosestColor(color); ++ } ++ } ++ ++ return colors; ++ } ++ ++ /** ++ * Returns the closest legacy color from an rgb color ++ * ++ * @param color The color we want to transform ++ * @since 1.0.0 ++ */ ++ @Nonnull ++ private static ChatColor getClosestColor(Color color) { ++ Color nearestColor = null; ++ double nearestDistance = Integer.MAX_VALUE; ++ ++ for (Color constantColor : COLORS.keySet()) { ++ double distance = Math.pow(color.getRed() - constantColor.getRed(), 2) + Math.pow(color.getGreen() - constantColor.getGreen(), 2) + Math.pow(color.getBlue() - constantColor.getBlue(), 2); ++ if (nearestDistance > distance) { ++ nearestColor = constantColor; ++ nearestDistance = distance; ++ } ++ } ++ return COLORS.get(nearestColor); ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/GradientPattern.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/GradientPattern.java +new file mode 100644 +index 000000000..c1f05cd0a +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/GradientPattern.java +@@ -0,0 +1,33 @@ ++package dev._2lstudios.flamecord.utils.iridiumcolorapi.patterns; ++ ++import java.awt.*; ++import java.util.regex.Matcher; ++ ++import dev._2lstudios.flamecord.utils.iridiumcolorapi.IridiumColorAPI; ++ ++/** ++ * Represents a gradient color pattern which can be applied to a String. ++ */ ++public class GradientPattern implements Pattern { ++ ++ java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("(.*?)"); ++ ++ /** ++ * Applies a gradient pattern to the provided String. ++ * Output might me the same as the input if this pattern is not present. ++ * ++ * @param string The String to which this pattern should be applied to ++ * @return The new String with applied pattern ++ */ ++ public String process(String string, int protocol) { ++ Matcher matcher = pattern.matcher(string); ++ while (matcher.find()) { ++ String start = matcher.group(1); ++ String end = matcher.group(3); ++ String content = matcher.group(2); ++ string = string.replace(matcher.group(), IridiumColorAPI.color(content, new Color(Integer.parseInt(start, 16)), new Color(Integer.parseInt(end, 16)), protocol)); ++ } ++ return string; ++ } ++ ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/Pattern.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/Pattern.java +new file mode 100644 +index 000000000..5457ead64 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/Pattern.java +@@ -0,0 +1,17 @@ ++package dev._2lstudios.flamecord.utils.iridiumcolorapi.patterns; ++ ++/** ++ * Represents a color pattern which can be applied to a String. ++ */ ++public interface Pattern { ++ ++ /** ++ * Applies this pattern to the provided String. ++ * Output might me the same as the input if this pattern is not present. ++ * ++ * @param string The String to which this pattern should be applied to ++ * @return The new String with applied pattern ++ */ ++ String process(String string, int protocol); ++ ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/RainbowPattern.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/RainbowPattern.java +new file mode 100644 +index 000000000..792e9f987 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/RainbowPattern.java +@@ -0,0 +1,28 @@ ++package dev._2lstudios.flamecord.utils.iridiumcolorapi.patterns; ++ ++import java.util.regex.Matcher; ++ ++import dev._2lstudios.flamecord.utils.iridiumcolorapi.IridiumColorAPI; ++ ++public class RainbowPattern implements Pattern { ++ ++ java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("(.*?)"); ++ ++ /** ++ * Applies a rainbow pattern to the provided String. ++ * Output might me the same as the input if this pattern is not present. ++ * ++ * @param string The String to which this pattern should be applied to ++ * @return The new String with applied pattern ++ */ ++ public String process(String string, int protocol) { ++ Matcher matcher = pattern.matcher(string); ++ while (matcher.find()) { ++ String saturation = matcher.group(1); ++ String content = matcher.group(2); ++ string = string.replace(matcher.group(), IridiumColorAPI.rainbow(content, Float.parseFloat(saturation), protocol)); ++ } ++ return string; ++ } ++ ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/SolidPattern.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/SolidPattern.java +new file mode 100644 +index 000000000..0b039c797 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/iridiumcolorapi/patterns/SolidPattern.java +@@ -0,0 +1,29 @@ ++package dev._2lstudios.flamecord.utils.iridiumcolorapi.patterns; ++ ++import java.util.regex.Matcher; ++ ++import dev._2lstudios.flamecord.utils.iridiumcolorapi.IridiumColorAPI; ++ ++public class SolidPattern implements Pattern { ++ ++ java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("|#\\{([0-9A-Fa-f]{6})}"); ++ ++ /** ++ * Applies a solid RGB color to the provided String. ++ * Output might me the same as the input if this pattern is not present. ++ * ++ * @param string The String to which this pattern should be applied to ++ * @return The new String with applied pattern ++ */ ++ public String process(String string, int protocol) { ++ Matcher matcher = pattern.matcher(string); ++ while (matcher.find()) { ++ String color = matcher.group(1); ++ if (color == null) color = matcher.group(2); ++ ++ string = string.replace(matcher.group(), IridiumColorAPI.getColor(color, protocol) + ""); ++ } ++ return string; ++ } ++ ++} +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 +index 66a347542..58be68b02 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -23,6 +23,8 @@ import javax.crypto.spec.SecretKeySpec; + + import dev._2lstudios.flamecord.FlameCord; + ++import dev._2lstudios.flamecord.configuration.FlameConfig; ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; + import lombok.Getter; + import lombok.RequiredArgsConstructor; + import net.md_5.bungee.BungeeCord; +@@ -275,7 +277,6 @@ public class InitialHandler extends PacketHandler implements PendingConnection + Preconditions.checkState( thisState == State.STATUS, "Not expecting STATUS" ); + + ServerInfo forced = AbstractReconnectHandler.getForcedHost( this ); +- final String motd = ( forced != null ) ? forced.getMotd() : listener.getMotd(); + final int protocol = ( ProtocolConstants.SUPPORTED_VERSION_IDS.contains( handshake.getProtocolVersion() ) ) ? handshake.getProtocolVersion() : bungee.getProtocolVersion(); + + Callback pingBack = new Callback() +@@ -338,7 +339,56 @@ public class InitialHandler extends PacketHandler implements PendingConnection + ( (BungeeServerInfo) forced ).ping( pingBack, handshake.getProtocolVersion() ); + } else + { +- pingBack.done( getPingInfo( motd, protocol ), null ); ++ // FlameCord - Custom motd system ++ // Get the configuration ++ final FlameCordConfiguration config = FlameCord.getInstance().getFlameCordConfiguration(); ++ ++ String motd; ++ String protocolName; ++ ++ ServerPing.PlayerInfo[] sample = null; ++ ++ int maxPlayers = listener.getMaxPlayers(); ++ int onlinePlayers = bungee.getOnlineCount(); ++ ++ if (config.isFakePlayersEnabled()) { ++ onlinePlayers += config.getFakePlayersAmount(onlinePlayers); ++ } ++ ++ if (config.isMaxPlayersEnabled()) { ++ maxPlayers = config.isMaxPlayersOneMore() ? onlinePlayers + 1 : config.getMaxPlayersAmount(); ++ } ++ ++ if (config.isMotdEnabled()) { ++ motd = config.getMOTD(maxPlayers, onlinePlayers, protocol); ++ } else { ++ motd = ( forced != null ) ? forced.getMotd() : listener.getMotd(); ++ } ++ ++ if (config.isProtocolEnabled()) { ++ protocolName = config.getProtocolName(maxPlayers, onlinePlayers); ++ } else { ++ protocolName = bungee.getName() + " " + bungee.getGameVersion(); ++ } ++ ++ int customProtocol = config.isProtocolEnabled() && config.isProtocolAlwaysShow() ? -1 : protocol; ++ ++ if (config.isSampleEnabled()) { ++ final UUID fakeUuid = new UUID(0, 0); ++ final String[] sampleString = config.getSample(maxPlayers, onlinePlayers, protocol); ++ ++ sample = new ServerPing.PlayerInfo[sampleString.length]; ++ ++ for (int i = 0; i < sampleString.length; i++) { ++ sample[i] = new ServerPing.PlayerInfo(sampleString[i], fakeUuid); ++ } ++ } ++ ++ pingBack.done( new ServerPing( ++ new ServerPing.Protocol( protocolName, customProtocol ), ++ new ServerPing.Players( maxPlayers, onlinePlayers, sample ), ++ motd, BungeeCord.getInstance().config.getFaviconObject() ), null ); ++ // FlameCord end - Custom motd system + } + + thisState = State.PING; +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0027-TCP-Fast-Open.patch b/Waterfall-Proxy-Patches/0027-TCP-Fast-Open.patch new file mode 100644 index 0000000..5e56fbf --- /dev/null +++ b/Waterfall-Proxy-Patches/0027-TCP-Fast-Open.patch @@ -0,0 +1,46 @@ +From 62bf8051431b5035171c900d729a1c175fcda639 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Thu, 24 Feb 2022 23:41:57 -0300 +Subject: [PATCH] TCP Fast Open + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +index b41ee92cc..14665b1f6 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +@@ -15,6 +15,10 @@ import net.md_5.bungee.config.Configuration; + import net.md_5.bungee.config.ConfigurationProvider; + + public class FlameCordConfiguration extends FlameConfig { ++ // FlameCord - TCP Fast Open ++ @Getter ++ private int tcpFastOpen = 3; ++ + public String getMOTD(int maxPlayers, int onlinePlayers, int protocol) { + String motd; + +@@ -127,6 +131,8 @@ public class FlameCordConfiguration extends FlameConfig { + this.fakePlayersAmount = setIfUnexistant("custom-motd.fakeplayers.amount", this.fakePlayersAmount, configuration); + this.fakePlayersMode = setIfUnexistant("custom-motd.fakeplayers.mode", this.fakePlayersMode, configuration); + ++ this.tcpFastOpen = setIfUnexistant("tcp-fast-open", this.tcpFastOpen, configuration); ++ + save(configuration, configurationFile); + } + } +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 +index a4c3bd710..ffea15992 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java +@@ -194,6 +194,8 @@ public class PipelineUtils + // IP_TOS is not supported (Windows XP / Windows Server 2003) + } + ch.config().setOption( ChannelOption.TCP_NODELAY, true ); ++ // FlameCord - TCP Fast Open ++ ch.config().setOption( ChannelOption.TCP_FASTOPEN, FlameCord.getInstance().getFlameCordConfiguration().getTcpFastOpen() ); + ch.config().setAllocator( PooledByteBufAllocator.DEFAULT ); + ch.config().setWriteBufferWaterMark( MARK ); + +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0028-Antibot-System.patch b/Waterfall-Proxy-Patches/0028-Antibot-System.patch new file mode 100644 index 0000000..ca0779d --- /dev/null +++ b/Waterfall-Proxy-Patches/0028-Antibot-System.patch @@ -0,0 +1,1989 @@ +From 405f365b733a8d8a322b0426ef103cdc7774cffe Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Fri, 4 Mar 2022 13:35:53 -0300 +Subject: [PATCH] Antibot System + + +diff --git a/flamecord/pom.xml b/flamecord/pom.xml +index 20edd9001..f48f1e60b 100644 +--- a/flamecord/pom.xml ++++ b/flamecord/pom.xml +@@ -30,6 +30,17 @@ + ${project.version} + compile + ++ ++ com.maxmind.db ++ maxmind-db ++ 2.0.0 ++ ++ ++ io.netty ++ netty-handler ++ ${netty.version} ++ compile ++ + + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java +index 676ba95b9..3824cc533 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java +@@ -1,8 +1,16 @@ + package dev._2lstudios.flamecord; + ++import java.io.IOException; + import java.util.Collection; ++import java.util.Iterator; ++import java.util.concurrent.ConcurrentHashMap; ++import java.util.logging.Level; + import java.util.logging.Logger; + ++import dev._2lstudios.flamecord.antibot.AddressDataManager; ++import dev._2lstudios.flamecord.antibot.CheckManager; ++import dev._2lstudios.flamecord.antibot.LoggerWrapper; ++import dev._2lstudios.flamecord.antibot.StatsData; + import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; + import dev._2lstudios.flamecord.configuration.MessagesConfiguration; + import dev._2lstudios.flamecord.configuration.ModulesConfiguration; +@@ -13,8 +21,13 @@ import net.md_5.bungee.config.YamlConfiguration; + public class FlameCord { + @Getter + private static FlameCord instance; ++ private static Runtime runtime; + + public static void initialize(final Logger logger, final Collection whitelistedAddresses) { ++ if (FlameCord.runtime == null) { ++ FlameCord.runtime = Runtime.getRuntime(); ++ } ++ + if (FlameCord.instance == null) { + FlameCord.instance = new FlameCord(); + } +@@ -31,6 +44,16 @@ public class FlameCord { + @Getter + private MessagesConfiguration messagesConfiguration; + ++ // FlameCord - Antibot System ++ @Getter ++ private AddressDataManager addressDataManager; ++ @Getter ++ private CheckManager checkManager; ++ @Getter ++ private StatsData statsData; ++ @Getter ++ private LoggerWrapper loggerWrapper; ++ + public void reload(final Logger logger, final Collection whitelistedAddresses) { + final ConfigurationProvider configurationProvider = ConfigurationProvider.getProvider(YamlConfiguration.class); + +@@ -39,5 +62,106 @@ public class FlameCord { + // FlameCord - Module System + this.modulesConfiguration = new ModulesConfiguration(configurationProvider); + this.messagesConfiguration = new MessagesConfiguration(logger, configurationProvider); ++ ++ // FlameCord - Antibot System ++ if (checkManager != null) checkManager.unload(); ++ this.loggerWrapper = new LoggerWrapper(Logger.getLogger("BungeeCord")); ++ this.addressDataManager = new AddressDataManager(); ++ this.checkManager = new CheckManager(addressDataManager, flameCordConfiguration); ++ this.statsData = new StatsData(); ++ ++ // Initialize antibot firewall ipset ++ if (flameCordConfiguration.isAntibotFirewallIpset()) { ++ runLinuxCommand("apt install iptables -y"); // Install iptables ++ runLinuxCommand("apt install ipset -y"); // Install ipset ++ runLinuxCommand("ipset destroy flamecord-firewall"); // Delete old FlameCord set ++ runLinuxCommand("ipset create flamecord-firewall hash:ip timeout 60"); // Create new FlameCord set ++ runLinuxCommand("iptables -I INPUT -m set --match-set flamecord-firewall src -j DROP"); // Create iptables rule to DROP ++ ++ // Thread to run queued firewall linux commands ++ new Thread(() -> { ++ FlameCord flameCord = FlameCord.getInstance(); ++ ++ while (!flameCord.isShuttingDown()) { ++ flameCord.runQueuedLinuxCommands(); ++ ++ try { ++ Thread.sleep(1000); ++ } catch (InterruptedException e) { ++ // Ignore ++ } ++ } ++ }).start(); ++ } ++ } ++ ++ private boolean shutdown = false; ++ ++ public boolean isShuttingDown() { ++ return shutdown; ++ } ++ ++ public void shutdown() { ++ this.shutdown = true; ++ } ++ ++ /** ++ * Utility to run a Linux commands for iptables. ++ * @param command ++ */ ++ public void runLinuxCommand(String command) { ++ try { ++ runtime.exec("/bin/sh -c " + command); ++ } catch (IOException e) { ++ /* ++ * Windows throws exception. ++ */ ++ } ++ } ++ ++ // Commands to execute in the queue ++ private Collection commandQueue = ConcurrentHashMap.newKeySet(); ++ ++ // Boolean for when the commands are processing ++ private boolean processing = false; ++ ++ /** ++ * Add command to the linux command queue ++ * @param command ++ */ ++ public void queueLinuxCommand(String command) { ++ commandQueue.add(command); ++ } ++ ++ /** ++ * Run the queued linux commands ++ */ ++ public void runQueuedLinuxCommands() { ++ if (!commandQueue.isEmpty() && !processing) { ++ processing = true; ++ ++ try { ++ Iterator iterator = commandQueue.iterator(); ++ StringBuilder commands = new StringBuilder(); ++ int ranCommands = 0; ++ ++ while (iterator.hasNext()) { ++ String command = iterator.next(); ++ ++ if (ranCommands++ > 0) { ++ commands.append(" && "); ++ } ++ ++ commands.append(command); ++ iterator.remove(); ++ } ++ ++ runLinuxCommand(commands.toString()); ++ ++ getLoggerWrapper().log(Level.INFO, "Blacklisted " + ranCommands + " ips from the kernel with IPSet"); ++ } finally { ++ processing = false; ++ } ++ } + } + } +\ No newline at end of file +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/AccountsCheck.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/AccountsCheck.java +new file mode 100644 +index 000000000..0295ea89d +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/AccountsCheck.java +@@ -0,0 +1,48 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.net.SocketAddress; ++import java.util.Collection; ++import java.util.logging.Level; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++ ++public class AccountsCheck { ++ private FlameCordConfiguration config; ++ private LoggerWrapper logger; ++ private AddressDataManager addressDataManager; ++ ++ public AccountsCheck(final AddressDataManager addressDataManager) { ++ this.config = FlameCord.getInstance().getFlameCordConfiguration(); ++ this.logger = FlameCord.getInstance().getLoggerWrapper(); ++ this.addressDataManager = addressDataManager; ++ } ++ ++ public boolean check(final SocketAddress remoteAddress, final String nickname) { ++ if (config.getAntibotAccountsWhitelist().contains(nickname)) { ++ return false; ++ } ++ ++ if (config.isAntibotAccountsEnabled()) { ++ final AddressData addressData = addressDataManager.getAddressData(remoteAddress); ++ final Collection nicknames = addressData.getNicknames(); ++ ++ if (nicknames.size() > config.getAntibotAccountsLimit()) { ++ nicknames.remove(nickname); ++ ++ if ( config.isAntibotAccountsLog() ) ++ { ++ logger.log( Level.INFO, "[FlameCord] [{0}] has too many accounts", remoteAddress ); ++ } ++ ++ if (config.isAntibotAccountsFirewall()) { ++ addressData.firewall("Too many accounts"); ++ } ++ ++ return true; ++ } ++ } ++ ++ return false; ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/AddressData.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/AddressData.java +new file mode 100644 +index 000000000..7c7899830 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/AddressData.java +@@ -0,0 +1,165 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.util.Collection; ++import java.util.HashSet; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++ ++public class AddressData { ++ private FlameCordConfiguration config; ++ private StatsData statsData; ++ private Collection nicknames = null; ++ private final String hostString; ++ private String lastNickname = ""; ++ private String country = null; ++ private String firewallReason = null; ++ private long lastPing = 0; ++ private long penultimateConnection = 0; ++ private long lastConnection = 0; ++ private long lastFirewall = 0; ++ private int pingsSecond = 0; ++ private int totalPings = 0; ++ private int connectionsSecond = 0; ++ private int totalConnections = 0; ++ ++ public AddressData(final String hostString) { ++ this.config = FlameCord.getInstance().getFlameCordConfiguration(); ++ this.statsData = FlameCord.getInstance().getStatsData(); ++ this.hostString = hostString; ++ } ++ ++ public Collection getNicknames() { ++ if (nicknames == null) nicknames = new HashSet<>(); ++ ++ return nicknames; ++ } ++ ++ public String getLastNickname() { ++ return lastNickname; ++ } ++ ++ public void addNickname(final String nickname) { ++ if (nicknames == null) nicknames = new HashSet<>(); ++ ++ if (!lastNickname.equals(nickname)) { ++ this.lastNickname = nickname; ++ this.totalConnections = 1; ++ } ++ ++ this.nicknames.add(nickname); ++ } ++ ++ public long getPenultimateConnection() { ++ return penultimateConnection; ++ } ++ ++ public long getTimeSincePenultimateConnection() { ++ return System.currentTimeMillis() - penultimateConnection; ++ } ++ ++ public long getLastConnection() { ++ return lastConnection; ++ } ++ ++ public long getTimeSinceLastConnection() { ++ return System.currentTimeMillis() - lastConnection; ++ } ++ ++ private void updatePingsSecond() { ++ if (System.currentTimeMillis() - lastPing >= 1000) { ++ pingsSecond = 0; ++ } ++ } ++ ++ public int getPingsSecond() { ++ updatePingsSecond(); ++ return pingsSecond; ++ } ++ ++ public void addPing() { ++ statsData.addPing(); ++ updatePingsSecond(); ++ lastPing = System.currentTimeMillis(); ++ pingsSecond++; ++ totalPings++; ++ } ++ ++ public int getTotalPings() { ++ return totalPings; ++ } ++ ++ private void updateConnectionsSecond() { ++ if (System.currentTimeMillis() - lastConnection >= 1000) { ++ connectionsSecond = 0; ++ } ++ } ++ ++ public int getConnectionsSecond() { ++ updateConnectionsSecond(); ++ return connectionsSecond; ++ } ++ ++ public void addConnection() { ++ final long currentTime = System.currentTimeMillis(); ++ ++ statsData.addConnection(); ++ updateConnectionsSecond(); ++ penultimateConnection = lastConnection == 0 ? currentTime : lastConnection; ++ lastConnection = currentTime; ++ connectionsSecond++; ++ totalConnections++; ++ } ++ ++ public int getTotalConnections() { ++ return totalConnections; ++ } ++ ++ public String getHostString() { ++ return hostString; ++ } ++ ++ public boolean isFirewalled() { ++ return System.currentTimeMillis() - lastFirewall < config ++ .getAntibotFirewallExpire() * 1000; ++ } ++ ++ public void firewall(String reason) { ++ if (!FlameCord.getInstance().getFlameCordConfiguration().getAntibotFirewallWhitelist().contains(hostString)) { ++ this.lastFirewall = System.currentTimeMillis(); ++ this.firewallReason = reason; ++ ++ // Queue the firewall as a ipset linux command ++ FlameCord.getInstance().queueLinuxCommand("ipset add flamecord-firewall " + hostString); ++ } ++ } ++ ++ public void unfirewall() { ++ this.lastFirewall = 0; ++ this.firewallReason = null; ++ } ++ ++ public String getFirewallReason() { ++ if (isFirewalled()) { ++ return firewallReason; ++ } ++ ++ return null; ++ } ++ ++ public void setTotalConnections(final int totalConnections) { ++ this.totalConnections = totalConnections; ++ } ++ ++ public String setCountry(final String country) { ++ return this.country = country; ++ } ++ ++ public String getCountry() { ++ return country; ++ } ++ ++ public boolean hasNickname(final String nickname) { ++ return nicknames.contains(nickname); ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/AddressDataManager.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/AddressDataManager.java +new file mode 100644 +index 000000000..bd14f56a1 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/AddressDataManager.java +@@ -0,0 +1,48 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.net.InetSocketAddress; ++import java.net.SocketAddress; ++import java.util.HashMap; ++import java.util.Map; ++ ++public class AddressDataManager { ++ private Map addressData = new HashMap<>(); ++ ++ public String sanitizeAddress(String text) { ++ // Remove the port ++ text = text.split(":")[0]; ++ ++ // Check if first character is a slash ++ if (text.startsWith("/")) { ++ // Remove the first character ++ text = text.substring(1); ++ } ++ ++ return text; ++ } ++ ++ public AddressData getAddressData(String addressString) { ++ addressString = sanitizeAddress(addressString); ++ ++ if (addressData.containsKey(addressString)) { ++ return addressData.get(addressString); ++ } else { ++ AddressData data = new AddressData(addressString); ++ ++ addressData.put(addressString, data); ++ ++ return data; ++ } ++ } ++ ++ public AddressData getAddressData(final SocketAddress address) { ++ final InetSocketAddress iNetSocketAddress = (InetSocketAddress) address; ++ final String addressString = iNetSocketAddress.getHostString(); ++ ++ return getAddressData(addressString); ++ } ++ ++ public int getAddresCount() { ++ return addressData.size(); ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/CheckManager.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/CheckManager.java +new file mode 100644 +index 000000000..1503ac954 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/CheckManager.java +@@ -0,0 +1,40 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++import lombok.Getter; ++ ++public class CheckManager { ++ @Getter ++ private final AccountsCheck accountsCheck; ++ @Getter ++ private final CountryCheck countryCheck; ++ @Getter ++ private final FastChatCheck fastChatCheck; ++ @Getter ++ private final NicknameCheck nicknameCheck; ++ @Getter ++ private final PasswordCheck passwordCheck; ++ @Getter ++ private final RatelimitCheck ratelimitCheck; ++ @Getter ++ private final ReconnectCheck reconnectCheck; ++ @Getter ++ private final PacketsCheck packetsCheck; ++ ++ public CheckManager(final AddressDataManager addressDataManager, final FlameCordConfiguration flameCordConfiguration) { ++ this.accountsCheck = new AccountsCheck(addressDataManager); ++ this.countryCheck = new CountryCheck(addressDataManager); ++ this.fastChatCheck = new FastChatCheck(addressDataManager); ++ this.nicknameCheck = new NicknameCheck(addressDataManager); ++ this.passwordCheck = new PasswordCheck(addressDataManager); ++ this.ratelimitCheck = new RatelimitCheck(addressDataManager); ++ this.reconnectCheck = new ReconnectCheck(addressDataManager); ++ this.packetsCheck = new PacketsCheck(); ++ ++ this.countryCheck.load(); ++ } ++ ++ public void unload() { ++ this.countryCheck.unload(); ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/CountryCheck.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/CountryCheck.java +new file mode 100644 +index 000000000..fdfd9c8d7 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/CountryCheck.java +@@ -0,0 +1,148 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.io.File; ++import java.io.IOException; ++import java.io.InputStream; ++import java.net.InetAddress; ++import java.net.InetSocketAddress; ++import java.net.SocketAddress; ++import java.net.URL; ++import java.nio.file.Files; ++import java.util.logging.Level; ++ ++import com.maxmind.db.CHMCache; ++import com.maxmind.db.MaxMindDbConstructor; ++import com.maxmind.db.MaxMindDbParameter; ++import com.maxmind.db.Reader; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++ ++public class CountryCheck { ++ private FlameCordConfiguration config; ++ private LoggerWrapper logger; ++ private AddressDataManager addressDataManager; ++ private Reader maxMindReader; ++ ++ public CountryCheck(final AddressDataManager addressDataManager) { ++ this.config = FlameCord.getInstance().getFlameCordConfiguration(); ++ this.logger = FlameCord.getInstance().getLoggerWrapper(); ++ this.addressDataManager = addressDataManager; ++ } ++ ++ public void download(final URL url, final File file) throws Exception { ++ try (InputStream in = url.openStream()) { ++ Files.copy(in, file.toPath()); ++ } ++ } ++ ++ public void load() { ++ final File file = new File("GeoLite2-Country.mmdb"); ++ ++ try { ++ if (!file.exists()) { ++ System.out.println("Starting download of MaxMindDB (This will take some seconds...)"); ++ download(new URL("https://git.io/GeoLite2-Country.mmdb"), file); ++ } ++ ++ this.maxMindReader = new Reader(file, new CHMCache()); ++ } catch (final Exception exception) { ++ System.out.println("MaxMindDB was not able to download!"); ++ } ++ } ++ ++ public void unload() { ++ try { ++ if (this.maxMindReader != null) { ++ this.maxMindReader.close(); ++ } ++ } catch (final IOException ex) { ++ // Ignored ++ } ++ } ++ ++ private boolean isBlacklisted(final FlameCordConfiguration config, final String isoCode) { ++ for (final String blacklisted : config.getAntibotCountryBlacklist()) { ++ if (isoCode.contains(blacklisted)) { ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ ++ public static class LookupResult { ++ private final Country country; ++ ++ @MaxMindDbConstructor ++ public LookupResult(@MaxMindDbParameter(name = "country") final Country country) { ++ this.country = country; ++ } ++ ++ public Country getCountry() { ++ return this.country; ++ } ++ } ++ ++ public static class Country { ++ private final String isoCode; ++ ++ @MaxMindDbConstructor ++ public Country(@MaxMindDbParameter(name = "iso_code") final String isoCode) { ++ this.isoCode = isoCode; ++ } ++ ++ public String getIsoCode() { ++ return this.isoCode; ++ } ++ } ++ ++ public String getIsoCode(final InetAddress address) { ++ try { ++ final LookupResult lookupResult = maxMindReader.get(address, LookupResult.class); ++ ++ if (lookupResult == null) { ++ return "LOCAL"; ++ } else { ++ final Country country = lookupResult.getCountry(); ++ final String isoCode = country.getIsoCode(); ++ ++ return isoCode; ++ } ++ } catch (final Exception exception) { ++ // Ignored ++ } ++ ++ return null; ++ } ++ ++ public boolean check(final SocketAddress remoteAddress) { ++ if (config.isAntibotCountryEnabled()) { ++ final AddressData addressData = addressDataManager.getAddressData(remoteAddress); ++ final String addressCountry = addressData.getCountry(); ++ final String country; ++ ++ if (addressCountry != null) { ++ country = addressCountry; ++ } else { ++ country = getIsoCode(((InetSocketAddress) remoteAddress).getAddress()); ++ addressData.setCountry(country); ++ } ++ ++ if (country != null && isBlacklisted(config, country)) { ++ if (config.isAntibotCountryLog()) { ++ logger.log(Level.INFO, ++ "[FlameCord] [{0}] has his country blocked from the server", remoteAddress); ++ } ++ ++ if (config.isAntibotCountryFirewall()) { ++ addressData.firewall("Blacklisted country"); ++ } ++ ++ return true; ++ } ++ } ++ ++ return false; ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/FastChatCheck.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/FastChatCheck.java +new file mode 100644 +index 000000000..e21408873 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/FastChatCheck.java +@@ -0,0 +1,39 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.net.SocketAddress; ++import java.util.logging.Level; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++ ++public class FastChatCheck { ++ private FlameCordConfiguration config; ++ private LoggerWrapper logger; ++ private final AddressDataManager addressDataManager; ++ ++ public FastChatCheck(final AddressDataManager addressDataManager) { ++ this.config = FlameCord.getInstance().getFlameCordConfiguration(); ++ this.logger = FlameCord.getInstance().getLoggerWrapper(); ++ this.addressDataManager = addressDataManager; ++ } ++ ++ public boolean check(final SocketAddress remoteAddress) { ++ if (config.isAntibotFastChatEnabled()) { ++ final AddressData addressData = addressDataManager.getAddressData(remoteAddress); ++ ++ if (addressData.getTimeSinceLastConnection() <= config.getAntibotFastChatTime()) { ++ if (config.isAntibotFastChatLog()) { ++ logger.log(Level.INFO, "[FlameCord] [{0}] is chatting too fast", remoteAddress); ++ } ++ ++ if (config.isAntibotFastChatFirewall()) { ++ addressData.firewall("Too fast chatting"); ++ } ++ ++ return true; ++ } ++ } ++ ++ return false; ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/LoggerWrapper.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/LoggerWrapper.java +new file mode 100644 +index 000000000..df26a3634 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/LoggerWrapper.java +@@ -0,0 +1,28 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.util.logging.Level; ++import java.util.logging.Logger; ++ ++public class LoggerWrapper { ++ private Logger logger; ++ ++ // Last time a log was done ++ private long lastLog = System.currentTimeMillis(); ++ ++ public LoggerWrapper(Logger logger) { ++ this.logger = logger; ++ } ++ ++ public void log(Level level, String msg, Object ...params) { ++ long currentTime = System.currentTimeMillis(); ++ ++ // Throttle logs by 100ms ++ if (currentTime - lastLog > 100) { ++ // Set the last log ++ lastLog = currentTime; ++ ++ // Log the text ++ logger.log(level, msg, params); ++ } ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/NicknameCheck.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/NicknameCheck.java +new file mode 100644 +index 000000000..1b35fba5d +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/NicknameCheck.java +@@ -0,0 +1,52 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.net.SocketAddress; ++import java.util.logging.Level; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++ ++public class NicknameCheck { ++ private FlameCordConfiguration config; ++ private LoggerWrapper logger; ++ private AddressDataManager addressDataManager; ++ ++ public NicknameCheck(final AddressDataManager addressDataManager) { ++ this.config = FlameCord.getInstance().getFlameCordConfiguration(); ++ this.logger = FlameCord.getInstance().getLoggerWrapper(); ++ this.addressDataManager = addressDataManager; ++ } ++ ++ private boolean isBlacklisted(final FlameCordConfiguration config, final String nickname) { ++ String lowerNickname = nickname.toLowerCase(); ++ ++ for (final String blacklisted : config.getAntibotNicknameBlacklist()) { ++ if (lowerNickname.contains(blacklisted)) { ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ ++ public boolean check(final SocketAddress remoteAddress) { ++ if (config.isAntibotNicknameEnabled()) { ++ final AddressData addressData = addressDataManager.getAddressData(remoteAddress); ++ final String nickname = addressData.getLastNickname(); ++ ++ if (isBlacklisted(config, nickname)) { ++ if (config.isAntibotNicknameLog()) { ++ logger.log(Level.INFO, "[FlameCord] [{0}] has a blacklisted nickname (" + nickname + ")", remoteAddress); ++ } ++ ++ if (config.isAntibotNicknameFirewall()) { ++ addressData.firewall("Blacklisted nickname [" + nickname + "]"); ++ } ++ ++ return true; ++ } ++ } ++ ++ return false; ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/PacketsCheck.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/PacketsCheck.java +new file mode 100644 +index 000000000..a7bd9f153 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/PacketsCheck.java +@@ -0,0 +1,69 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.net.SocketAddress; ++import java.util.HashMap; ++import java.util.Map; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++import dev._2lstudios.flamecord.enums.PacketsCheckResult; ++import dev._2lstudios.flamecord.enums.PacketsViolationReason; ++import dev._2lstudios.flamecord.utils.ProtocolUtil; ++import io.netty.buffer.ByteBuf; ++ ++public class PacketsCheck { ++ private Map packetsData = new HashMap<>(); ++ ++ // Time since the last clear passed ++ private long lastClear = System.currentTimeMillis(); ++ ++ public PacketsData getData(SocketAddress address) { ++ String ip = address.toString(); ++ ++ if (System.currentTimeMillis() - lastClear >= 60000) { ++ packetsData.clear(); ++ this.lastClear = System.currentTimeMillis(); ++ } ++ ++ PacketsData data; ++ ++ if (packetsData.containsKey(ip)) { ++ data = packetsData.get(ip); ++ } else { ++ data = new PacketsData(address); ++ packetsData.put(ip, data); ++ } ++ ++ return data; ++ } ++ ++ public PacketsCheckResult check(SocketAddress socketAddress, ByteBuf byteBuf) { ++ FlameCordConfiguration config = FlameCord.getInstance().getFlameCordConfiguration(); ++ ++ if (!config.isAntibotPacketsEnabled()) { ++ return PacketsCheckResult.NONE; ++ } ++ ++ PacketsData packetsData = getData(socketAddress); ++ int length = byteBuf.readableBytes(); ++ int index = byteBuf.readerIndex(); ++ int packetId = ProtocolUtil.readVarInt(byteBuf); ++ byteBuf.readerIndex(index); ++ ++ packetsData.addVls(length * config.getAntibotPacketsVlsPerByte(), PacketsViolationReason.SIZE, packetId); ++ packetsData.addVls(config.getAntibotPacketsVlsPerPacket(), PacketsViolationReason.RATE, packetId); ++ ++ double vls = packetsData.getPacketsVls(); ++ ++ if (vls >= config.getAntibotPacketsVlsToKick()) { ++ return PacketsCheckResult.KICK; ++ } else if (vls >= config.getAntibotPacketsVlsToCancel()) { ++ return PacketsCheckResult.CANCEL; ++ } else { ++ return PacketsCheckResult.NONE; ++ } ++ } ++ ++ public void printPackets(SocketAddress remoteAddress) { ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/PacketsData.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/PacketsData.java +new file mode 100644 +index 000000000..7189a7f37 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/PacketsData.java +@@ -0,0 +1,115 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.net.SocketAddress; ++import java.util.HashMap; ++import java.util.Map; ++import java.util.Map.Entry; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.enums.PacketsViolationReason; ++ ++public class PacketsData { ++ // The address related to this data ++ private SocketAddress address; ++ ++ // The vls of the current address ++ private double packetsVls = 0; ++ ++ // The vls of the current address because of size ++ private double packetsVlsSize = 0; ++ ++ // The vls of the current address because of rate ++ private double packetsVlsRate = 0; ++ ++ // The last time vls was calculated ++ private long lastVlsCalculated = System.currentTimeMillis(); ++ ++ // The vls by packet ids ++ private Map vlsByPacketId = new HashMap<>(); ++ ++ // If cancellation was printed ++ private boolean cancelPrinted = false; ++ ++ public PacketsData(SocketAddress address) { ++ this.address = address; ++ } ++ ++ public double simplify(double number) { ++ return (double) (int) (number * 1000) / 1000; ++ } ++ ++ public void printKick() { ++ if (FlameCord.getInstance().getFlameCordConfiguration().isAntibotPacketsLog()) { ++ System.out ++ .println("[FlameCord] [" + address ++ + "] was kicked because of too many packets (Total: " + simplify(packetsVls) + "vls Size: " ++ + simplify(packetsVlsSize) + "vls Rate: " + simplify(packetsVlsRate) + "vls)"); ++ } ++ } ++ ++ public void printCancel() { ++ if (FlameCord.getInstance().getFlameCordConfiguration().isAntibotPacketsLog() && !cancelPrinted) { ++ System.out ++ .println("[FlameCord] [" + address ++ + "] was cancelled because of too many packets (Total: " + simplify(packetsVls) + "vls Size: " ++ + simplify(packetsVlsSize) + "vls Rate: " + simplify(packetsVlsRate) + "vls)"); ++ this.cancelPrinted = true; ++ } ++ } ++ ++ public void printPackets() { ++ if (FlameCord.getInstance().getFlameCordConfiguration().isAntibotPacketsDebug() ++ && simplify(this.packetsVls) > 0) { ++ System.out ++ .println("[FlameCord] [" + address ++ + "] debug is enabled, showing stats (Total: " + simplify(packetsVls) + "vls Size: " ++ + simplify(packetsVlsSize) + "vls Rate: " + simplify(packetsVlsRate) + "vls)"); ++ for (Entry entry : this.vlsByPacketId.entrySet()) { ++ System.out.print(entry.getKey() + "-" + simplify(entry.getValue()) + "vls, "); ++ } ++ System.out.println(""); ++ } ++ } ++ ++ public double getPacketsVls() { ++ if (System.currentTimeMillis() - lastVlsCalculated >= 1000) { ++ printPackets(); ++ ++ this.cancelPrinted = false; ++ this.packetsVls = 0; ++ this.packetsVlsSize = 0; ++ this.packetsVlsRate = 0; ++ this.vlsByPacketId.clear(); ++ this.lastVlsCalculated = System.currentTimeMillis(); ++ } ++ ++ return packetsVls; ++ } ++ ++ public void addVls(double packetsVls, PacketsViolationReason reason, int packetId) { ++ this.packetsVls += packetsVls; ++ this.vlsByPacketId.put(packetId, this.vlsByPacketId.getOrDefault(packetId, 0.0) + packetsVls); ++ ++ switch (reason) { ++ case SIZE: { ++ this.packetsVlsSize += packetsVls; ++ break; ++ } ++ case RATE: { ++ this.packetsVlsRate += packetsVls; ++ break; ++ } ++ default: { ++ break; ++ } ++ } ++ } ++ ++ public double getPacketsVlsSize() { ++ return packetsVlsSize; ++ } ++ ++ public double getPacketsVlsRate() { ++ return packetsVlsRate; ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/PasswordCheck.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/PasswordCheck.java +new file mode 100644 +index 000000000..68555de34 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/PasswordCheck.java +@@ -0,0 +1,69 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.net.SocketAddress; ++import java.util.logging.Level; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++ ++public class PasswordCheck { ++ private FlameCordConfiguration config; ++ private AddressDataManager addressDataManager; ++ private LoggerWrapper logger; ++ private String lastNickname = ""; ++ private String lastPassword = ""; ++ private int repeatCount = 0; ++ ++ public PasswordCheck(final AddressDataManager addressDataManager) { ++ this.config = FlameCord.getInstance().getFlameCordConfiguration(); ++ this.logger = FlameCord.getInstance().getLoggerWrapper(); ++ this.addressDataManager = addressDataManager; ++ } ++ ++ private void updatePassword(final FlameCordConfiguration config, final String nickname, final String password) { ++ if (!nickname.equals(lastNickname)) { ++ if (password.equals(lastPassword)) { ++ if (repeatCount < config.getAntibotPasswordLimit()) { ++ repeatCount++; ++ } ++ } else if (repeatCount > 0) { ++ repeatCount--; ++ } ++ } ++ ++ lastNickname = nickname; ++ lastPassword = password; ++ } ++ ++ public boolean check(final SocketAddress remoteAddress, final String passwordMessage) { ++ if (config.isAntibotPasswordEnabled()) { ++ if (passwordMessage.contains("/login ") || passwordMessage.contains("/l ") ++ || passwordMessage.contains("/register ") ++ || passwordMessage.contains("/reg ")) { ++ final AddressData addressData = addressDataManager.getAddressData(remoteAddress); ++ final String nickname = addressData.getLastNickname(); ++ final String password = passwordMessage.split(" ")[1]; ++ ++ updatePassword(config, nickname, password); ++ ++ if (repeatCount >= config.getAntibotPasswordLimit()) { ++ if (config.isAntibotPasswordLog()) { ++ logger.log(Level.INFO, "[FlameCord] [{0}] has entered a repeated password", remoteAddress); ++ } ++ ++ if (config.isAntibotPasswordFirewall()) { ++ addressData.firewall("Repeated password"); ++ } ++ ++ return true; ++ } ++ } ++ } ++ ++ return false; ++ } ++ ++ public int getRepeatCount() { ++ return repeatCount; ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/RatelimitCheck.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/RatelimitCheck.java +new file mode 100644 +index 000000000..51c1866ec +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/RatelimitCheck.java +@@ -0,0 +1,52 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.net.SocketAddress; ++import java.util.logging.Level; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++ ++public class RatelimitCheck { ++ private FlameCordConfiguration config; ++ private LoggerWrapper logger; ++ private AddressDataManager addressDataManager; ++ ++ public RatelimitCheck(final AddressDataManager addressDataManager) { ++ this.config = FlameCord.getInstance().getFlameCordConfiguration(); ++ this.logger = FlameCord.getInstance().getLoggerWrapper(); ++ this.addressDataManager = addressDataManager; ++ } ++ ++ public boolean check(final SocketAddress remoteAddress, int protocol) { ++ if (config.isAntibotRatelimitEnabled()) { ++ AddressData addressData = addressDataManager.getAddressData(remoteAddress); ++ ++ if (config.getAntibotRatelimitWhitelist().contains(addressData.getHostString())) { ++ return false; ++ } ++ ++ if (addressData.getConnectionsSecond() >= config.getAntibotRatelimitConnectionsPerSecond() ++ || addressData.getPingsSecond() >= config.getAntibotRatelimitPingsPerSecond()) { ++ if (config.isAntibotRatelimitLog()) { ++ if (protocol == 1) { ++ logger.log(Level.INFO, "[FlameCord] [{0}] is pinging too fast", remoteAddress); ++ } else { ++ logger.log(Level.INFO, "[FlameCord] [{0}] is connecting too fast", remoteAddress); ++ } ++ } ++ ++ if (config.isAntibotRatelimitFirewall()) { ++ if (protocol == 1) { ++ addressData.firewall("Too many pings"); ++ } else { ++ addressData.firewall("Too many connections"); ++ } ++ } ++ ++ return true; ++ } ++ } ++ ++ return false; ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/ReconnectCheck.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/ReconnectCheck.java +new file mode 100644 +index 000000000..d38ffc9e3 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/ReconnectCheck.java +@@ -0,0 +1,53 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++import java.net.SocketAddress; ++import java.util.logging.Level; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++ ++public class ReconnectCheck { ++ private FlameCordConfiguration config; ++ private LoggerWrapper logger; ++ private AddressDataManager addressDataManager; ++ private int connections = 0; ++ private long lastConnection = 0; ++ ++ public ReconnectCheck(final AddressDataManager addressDataManager) { ++ this.config = FlameCord.getInstance().getFlameCordConfiguration(); ++ this.logger = FlameCord.getInstance().getLoggerWrapper(); ++ this.addressDataManager = addressDataManager; ++ } ++ ++ public boolean check(final SocketAddress remoteAddress) { ++ if (config.isAntibotReconnectEnabled()) { ++ final long currentTime = System.currentTimeMillis(); ++ ++ if (currentTime - lastConnection > config.getAntibotReconnectConnectionThresholdLimit()) { ++ lastConnection = currentTime; ++ connections = 0; ++ } ++ ++ if (++connections > config.getAntibotReconnectConnectionThreshold()) { ++ final AddressData addressData = addressDataManager.getAddressData(remoteAddress); ++ final boolean needsAttempts = addressData.getTotalConnections() < config.getAntibotReconnectAttempts() ++ || addressData.getTotalPings() < config.getAntibotReconnectPings(); ++ final boolean tooSlow = addressData.getTimeSincePenultimateConnection() > config ++ .getAntibotReconnectMaxTime(); ++ ++ if (tooSlow) { ++ if (config.isAntibotReconnectLog()) { ++ logger.log(Level.INFO, "[FlameCord] [{0}] has to reconnect to join", remoteAddress); ++ } ++ ++ addressData.setTotalConnections(0); ++ return true; ++ } else { ++ return needsAttempts; ++ } ++ } ++ } ++ ++ return false; ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/StatsData.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/StatsData.java +new file mode 100644 +index 000000000..44d773abe +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/antibot/StatsData.java +@@ -0,0 +1,75 @@ ++package dev._2lstudios.flamecord.antibot; ++ ++public class StatsData { ++ // Time of the last second ++ private long lastSecond = System.currentTimeMillis(); ++ ++ // Total pings and connections ++ private int totalPings = 0; ++ private int totalConnections = 0; ++ ++ // Current second connections and pings ++ private int currentPings = 0; ++ private int currentConnections = 0; ++ ++ // Last second connections and pings ++ private int lastPings = 0; ++ private int lastConnections = 0; ++ ++ public void resetData() { ++ // Get current time ++ long currentTime = System.currentTimeMillis(); ++ ++ // Check if one second passed ++ if (currentTime - lastSecond > 1000) { ++ // Set the last second to this one ++ lastSecond = currentTime; ++ ++ // Reset the data ++ lastPings = currentPings; ++ lastConnections = currentConnections; ++ currentPings = 0; ++ currentConnections = 0; ++ } ++ } ++ ++ public void addPing() { ++ resetData(); ++ currentPings++; ++ totalPings++; ++ } ++ ++ public void addConnection() { ++ resetData(); ++ currentConnections++; ++ totalConnections++; ++ } ++ ++ public int getCurrentPings() { ++ resetData(); ++ return currentPings; ++ } ++ ++ public int getCurrentConnections() { ++ resetData(); ++ return currentConnections; ++ } ++ ++ public int getLastPings() { ++ resetData(); ++ return lastPings; ++ } ++ ++ public int getLastConnections() { ++ resetData(); ++ return lastConnections; ++ } ++ ++ public int getTotalPings() { ++ return totalPings; ++ } ++ ++ public int getTotalConnections() { ++ return totalConnections; ++ } ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +index 14665b1f6..0f12f4ae6 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +@@ -15,6 +15,187 @@ import net.md_5.bungee.config.Configuration; + import net.md_5.bungee.config.ConfigurationProvider; + + public class FlameCordConfiguration extends FlameConfig { ++ // Antibot accounts ++ @Getter ++ private boolean antibotAccountsEnabled = true; ++ @Getter ++ private boolean antibotAccountsFirewall = true; ++ @Getter ++ private int antibotAccountsLimit = 3; ++ @Getter ++ private boolean antibotAccountsLog = true; ++ @Getter ++ private Collection antibotAccountsWhitelist = Arrays.asList("Nickname"); ++ ++ // Antibot country ++ @Getter ++ private boolean antibotCountryEnabled = true; ++ @Getter ++ private boolean antibotCountryFirewall = true; ++ @Getter ++ private Collection antibotCountryBlacklist = Arrays.asList("CN", "HK", "RU", "IN", "TH", "ID", "DZ", "VN", "IR", "PK"); ++ @Getter ++ private Collection antibotFirewalledExceptions = Arrays.asList("BadPacketException", "QuietException", "IllegalStateConfig", "FastException"); ++ @Getter ++ private boolean antibotCountryLog = true; ++ ++ // Antibot fastchat ++ @Getter ++ private boolean antibotFastChatEnabled = true; ++ @Getter ++ private boolean antibotFastChatFirewall = true; ++ @Getter ++ private int antibotFastChatTime = 1000; ++ @Getter ++ private boolean antibotFastChatLog = true; ++ ++ // Antibot firewall ++ @Getter ++ private boolean antibotFirewallEnabled = true; ++ @Getter ++ private int antibotFirewallExpire = 60; ++ @Getter ++ private boolean antibotFirewallLog = true; ++ @Getter ++ private boolean antibotFirewallIpset = true; ++ @Getter ++ private Collection antibotFirewallWhitelist = Arrays.asList("127.0.0.1"); ++ ++ // Antibot nickname ++ @Getter ++ private boolean antibotNicknameEnabled = true; ++ @Getter ++ private boolean antibotNicknameFirewall = true; ++ @Getter ++ private Collection antibotNicknameBlacklist = Arrays.asList("mcstorm", "mcdown", "mcbot", "theresa_bot", "dropbot", "kingbot"); ++ @Getter ++ private boolean antibotNicknameLog = true; ++ ++ // Antibot password ++ @Getter ++ private boolean antibotPasswordEnabled = true; ++ @Getter ++ private boolean antibotPasswordFirewall = true; ++ @Getter ++ private int antibotPasswordLimit = 3; ++ @Getter ++ private boolean antibotPasswordLog = true; ++ ++ // Antibot ratelimit ++ @Getter ++ private boolean antibotRatelimitEnabled = true; ++ @Getter ++ private boolean antibotRatelimitFirewall = true; ++ @Getter ++ private int antibotRatelimitConnectionsPerSecond = 3; ++ @Getter ++ private int antibotRatelimitPingsPerSecond = 8; ++ @Getter ++ private boolean antibotRatelimitLog = true; ++ @Getter ++ private Collection antibotRatelimitWhitelist = Arrays.asList("127.0.0.1"); ++ ++ // Antibot reconnect ++ @Getter ++ private boolean antibotReconnectEnabled = true; ++ @Getter ++ private int antibotReconnectAttempts = 2; ++ @Getter ++ private int antibotReconnectPings = 0; ++ @Getter ++ private int antibotReconnectMaxTime = 10000; ++ @Getter ++ private int antibotReconnectConnectionThreshold = 1; ++ @Getter ++ private int antibotReconnectConnectionThresholdLimit = 8000; ++ @Getter ++ private boolean antibotReconnectLog = true; ++ ++ // Antibot packets ++ @Getter ++ private boolean antibotPacketsEnabled = true; ++ @Getter ++ private boolean antibotPacketsLog = true; ++ @Getter ++ private boolean antibotPacketsDebug = false; ++ @Getter ++ private double antibotPacketsVlsPerByte = 0.0017; ++ @Getter ++ private double antibotPacketsVlsPerPacket = 0.1; ++ @Getter ++ private double antibotPacketsVlsToKick = 100; ++ @Getter ++ private double antibotPacketsVlsToCancel = 25; ++ ++ public void loadAntibot(final Configuration config, final Collection whitelistedAddresses) { ++ // Antibot accounts ++ this.antibotAccountsEnabled = setIfUnexistant("antibot.accounts.enabled", this.antibotAccountsEnabled, config); ++ this.antibotAccountsFirewall = setIfUnexistant("antibot.accounts.firewall", this.antibotAccountsFirewall, config); ++ this.antibotAccountsLimit = setIfUnexistant("antibot.accounts.limit", this.antibotAccountsLimit, config); ++ this.antibotAccountsLog = setIfUnexistant("antibot.accounts.log", this.antibotAccountsLog, config); ++ this.antibotAccountsWhitelist = setIfUnexistant("antibot.accounts.whitelist", this.antibotAccountsWhitelist, config); ++ ++ // Antibot country ++ this.antibotCountryEnabled = setIfUnexistant("antibot.country.enabled", this.antibotCountryEnabled, config); ++ this.antibotCountryFirewall = setIfUnexistant("antibot.country.firewall", this.antibotCountryFirewall, config); ++ this.antibotCountryBlacklist = setIfUnexistant("antibot.country.blacklist", this.antibotCountryBlacklist, config); ++ this.antibotCountryLog = setIfUnexistant("antibot.country.log", this.antibotCountryLog, config); ++ ++ // Antibot fastchat ++ this.antibotFastChatEnabled = setIfUnexistant("antibot.fastchat.enabled", this.antibotFastChatEnabled, config); ++ this.antibotFastChatFirewall = setIfUnexistant("antibot.fastchat.firewall", this.antibotFastChatFirewall, config); ++ this.antibotFastChatTime = setIfUnexistant("antibot.fastchat.time", this.antibotFastChatTime, config); ++ this.antibotFastChatLog = setIfUnexistant("antibot.fastchat.log", this.antibotFastChatLog, config); ++ ++ // Antibot firewall ++ this.antibotFirewallEnabled = setIfUnexistant("antibot.firewall.enabled", this.antibotFirewallEnabled, config); ++ this.antibotFirewalledExceptions = setIfUnexistant("antibot.firewall.exceptions", this.antibotFirewalledExceptions, config); ++ this.antibotFirewallExpire = setIfUnexistant("antibot.firewall.time", this.antibotFirewallExpire, config); ++ this.antibotFirewallLog = setIfUnexistant("antibot.firewall.log", this.antibotFirewallLog, config); ++ this.antibotFirewallWhitelist = new HashSet<>( setIfUnexistant("antibot.firewall.whitelist", this.antibotFirewallWhitelist, config )); ++ this.antibotFirewallIpset = setIfUnexistant("antibot.firewall.ipset", this.antibotFirewallIpset, config); ++ ++ // Add local server ips to whitelist ++ this.antibotFirewallWhitelist.addAll(whitelistedAddresses); ++ ++ // Antibot nickname ++ this.antibotNicknameEnabled = setIfUnexistant("antibot.nickname.enabled", this.antibotNicknameEnabled, config); ++ this.antibotNicknameFirewall = setIfUnexistant("antibot.nickname.firewall", this.antibotNicknameFirewall, config); ++ this.antibotNicknameBlacklist = setIfUnexistant("antibot.nickname.blacklist", this.antibotNicknameBlacklist, config); ++ this.antibotNicknameLog = setIfUnexistant("antibot.nickname.log", this.antibotNicknameLog, config); ++ ++ // Antibot password ++ this.antibotPasswordEnabled = setIfUnexistant("antibot.password.enabled", this.antibotPasswordEnabled, config); ++ this.antibotPasswordFirewall = setIfUnexistant("antibot.password.firewall", this.antibotPasswordFirewall, config); ++ this.antibotPasswordLimit = setIfUnexistant("antibot.password.limit", this.antibotPasswordLimit, config); ++ this.antibotPasswordLog = setIfUnexistant("antibot.password.log", this.antibotPasswordLog, config); ++ ++ // Antibot ratelimit ++ this.antibotRatelimitEnabled = setIfUnexistant("antibot.ratelimit.enabled", this.antibotRatelimitEnabled, config); ++ this.antibotRatelimitFirewall = setIfUnexistant("antibot.ratelimit.firewall", this.antibotRatelimitFirewall, config); ++ this.antibotRatelimitConnectionsPerSecond = setIfUnexistant("antibot.ratelimit.connections-per-second", this.antibotRatelimitConnectionsPerSecond, config); ++ this.antibotRatelimitPingsPerSecond = setIfUnexistant("antibot.ratelimit.pings-per-second", this.antibotRatelimitPingsPerSecond, config); ++ this.antibotRatelimitLog = setIfUnexistant("antibot.ratelimit.log", this.antibotRatelimitLog, config); ++ this.antibotRatelimitWhitelist = new HashSet<>( setIfUnexistant("antibot.ratelimit.whitelist", this.antibotRatelimitWhitelist, config )); ++ ++ // Antibot reconnect ++ this.antibotReconnectEnabled = setIfUnexistant("antibot.reconnect.enabled", this.antibotReconnectEnabled, config); ++ this.antibotReconnectAttempts = setIfUnexistant("antibot.reconnect.attempts", this.antibotReconnectAttempts, config); ++ this.antibotReconnectPings = setIfUnexistant("antibot.reconnect.pings", this.antibotReconnectPings, config); ++ this.antibotReconnectMaxTime = setIfUnexistant("antibot.reconnect.max-time", this.antibotReconnectMaxTime, config); ++ this.antibotReconnectConnectionThreshold = setIfUnexistant("antibot.reconnect.connection-threshold", this.antibotReconnectConnectionThreshold, config); ++ this.antibotReconnectConnectionThresholdLimit = setIfUnexistant("antibot.reconnect.connection-threshold-limit", this.antibotReconnectConnectionThresholdLimit, config); ++ this.antibotReconnectLog = setIfUnexistant("antibot.reconnect.log", this.antibotReconnectLog, config); ++ ++ // Antibot packets ++ this.antibotPacketsEnabled = setIfUnexistant("antibot.packets.enabled", this.antibotPacketsEnabled, config); ++ this.antibotPacketsLog = setIfUnexistant("antibot.packets.log", this.antibotPacketsLog, config); ++ this.antibotPacketsDebug = setIfUnexistant("antibot.packets.debug", this.antibotPacketsDebug, config); ++ this.antibotPacketsVlsPerByte = setIfUnexistant("antibot.packets.vls-per-byte", this.antibotPacketsVlsPerByte, config); ++ this.antibotPacketsVlsPerPacket = setIfUnexistant("antibot.packets.vls-per-packet", this.antibotPacketsVlsPerPacket, config); ++ this.antibotPacketsVlsToKick = setIfUnexistant("antibot.packets.vls-to-kick", this.antibotPacketsVlsToKick, config); ++ } ++ + // FlameCord - TCP Fast Open + @Getter + private int tcpFastOpen = 3; +@@ -132,6 +313,9 @@ public class FlameCordConfiguration extends FlameConfig { + this.fakePlayersMode = setIfUnexistant("custom-motd.fakeplayers.mode", this.fakePlayersMode, configuration); + + this.tcpFastOpen = setIfUnexistant("tcp-fast-open", this.tcpFastOpen, configuration); ++ ++ // FlameCord - Antibot System ++ loadAntibot(configuration, whitelistedAddresses); + + save(configuration, configurationFile); + } +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java +index ae1794385..6175d8e2d 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java +@@ -80,6 +80,22 @@ public class MessagesConfiguration extends FlameConfig { + setIfUnexistant("command_ip", "&9IP of {0} is {1}", configuration); + setIfUnexistant("illegal_chat_characters", "&cIllegal characters in chat ({0})", configuration); + ++ // FlameCord start - Antibot System ++ setIfUnexistant("antibot_accounts", "&c&lFlameCord\n\n&cYou have too many accounts! ({0})\n\n&cError? Contact us on discord.gg/gF36AT3", configuration); ++ setIfUnexistant("antibot_fastchat", "&c&lFlameCord\n\n&cYou are chatting too fast!\n\n&cError? Contact us on discord.gg/gF36AT3", configuration); ++ setIfUnexistant("antibot_firewall", "&c&lFlameCord\n\n&cYou are blocked from this server!\n\n&cError? Contact us on discord.gg/gF36AT3", configuration); ++ setIfUnexistant("antibot_nickname", "&c&lFlameCord\n\n&cYour nickname was detected as bot! ({0})\n\n&cError? Contact us on discord.gg/gF36AT3", configuration); ++ setIfUnexistant("antibot_password", "&c&lFlameCord\n\n&cYour password is used by other players! ({0})\n\n&cError? Contact us on discord.gg/gF36AT3", configuration); ++ setIfUnexistant("antibot_ratelimit", "&c&lFlameCord\n\n&cYou are connecting too fast! ({0})\n\n&cError? Contact us on discord.gg/gF36AT3", configuration); ++ setIfUnexistant("antibot_reconnect", "&c&lFlameCord\n\n&cReconnect {0} more times to enter!\n\n&cError? Contact us on discord.gg/gF36AT3", configuration); ++ setIfUnexistant("antibot_country", "&c&lFlameCord\n\n&cYour country {0} is blacklisted!\n\n&cError? Contact us on discord.gg/gF36AT3", configuration); ++ setIfUnexistant("antibot_stats", "&c&lFlameCord Antibot Stats\n &7■ Total Pings: &a{0}\n &7■ Total Connections: &b{1}\n\n &7■ Current Pings: &a{2}\n &7■ Current Connections: &b{3}", configuration); ++ ++ setIfUnexistant("flamecord_firewall_help", "&c/flamecord firewall ", configuration); ++ setIfUnexistant("flamecord_firewall_add", "&cThe ip {0} was added to the firewall!", configuration); ++ setIfUnexistant("flamecord_firewall_remove", "&cThe ip {0} was removed from the firewall!", configuration); ++ // FlameCord end - Antibot System ++ + for (final String key : configuration.getKeys()) { + final Object value = configuration.get(key); + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/enums/PacketsCheckResult.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/enums/PacketsCheckResult.java +new file mode 100644 +index 000000000..285810ea1 +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/enums/PacketsCheckResult.java +@@ -0,0 +1,5 @@ ++package dev._2lstudios.flamecord.enums; ++ ++public enum PacketsCheckResult { ++ KICK, CANCEL, NONE ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/enums/PacketsViolationReason.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/enums/PacketsViolationReason.java +new file mode 100644 +index 000000000..c19c4965d +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/enums/PacketsViolationReason.java +@@ -0,0 +1,5 @@ ++package dev._2lstudios.flamecord.enums; ++ ++public enum PacketsViolationReason { ++ SIZE, RATE ++} +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/ProtocolUtil.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/ProtocolUtil.java +new file mode 100644 +index 000000000..7f26e7a0d +--- /dev/null ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/utils/ProtocolUtil.java +@@ -0,0 +1,27 @@ ++package dev._2lstudios.flamecord.utils; ++ ++import io.netty.buffer.ByteBuf; ++ ++public class ProtocolUtil { ++ private static int SEGMENT_BITS = 0x7F; ++ private static int CONTINUE_BIT = 0x80; ++ ++ public static int readVarInt(ByteBuf byteBuf) { ++ int value = 0; ++ int position = 0; ++ byte currentByte; ++ ++ while (byteBuf.isReadable()) { ++ currentByte = byteBuf.readByte(); ++ value |= (currentByte & SEGMENT_BITS) << position; ++ ++ if ((currentByte & CONTINUE_BIT) == 0) break; ++ ++ position += 7; ++ ++ if (position >= 32) throw new RuntimeException("VarInt is too big"); ++ } ++ ++ return value; ++ } ++} +\ No newline at end of file +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 +index 6316143f7..aa538f748 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java +@@ -51,12 +51,38 @@ public class MinecraftDecoder extends MessageToMessageDecoder + throw new FastDecoderException("Error decoding packet with too big capacity: " + capacity); + } + } ++ ++ // FlameCord start - Antibot Packet Check ++ if (prot == protocol.TO_SERVER) { ++ dev._2lstudios.flamecord.antibot.PacketsCheck packetsCheck = FlameCord.getInstance().getCheckManager().getPacketsCheck(); ++ dev._2lstudios.flamecord.enums.PacketsCheckResult result = packetsCheck.check(ctx.channel().remoteAddress(), in); ++ ++ switch (result) { ++ case KICK: ++ packetsCheck.getData(ctx.channel().remoteAddress()).printKick(); ++ ++ in.skipBytes(in.readableBytes()); ++ ctx.close(); ++ return; ++ case CANCEL: ++ packetsCheck.getData(ctx.channel().remoteAddress()).printCancel(); ++ ++ in.skipBytes(in.readableBytes()); ++ return; ++ default: ++ break; ++ } ++ } ++ // FlameCord end - Antibot Packet Check + + ByteBuf slice = in.duplicate(); // FlameCord - Duplicate buf instead of Copy + + Object packetTypeInfo = null; + try + { ++ // FlameCord - Duplicate buf instead of Copy ++ slice = in.duplicate(); // Can't slice this one due to EntityMap :( ++ + // Waterfall start + if (in.readableBytes() == 0 && !server) { + return; +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 +index c9e45b915..022f94b2d 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java +@@ -723,4 +723,8 @@ public enum Protocol + // Waterfall end + } + } ++ ++ public DirectionData getToServer() { ++ return TO_SERVER; ++ } + } +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 +index c0d371426..403ccba98 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java +@@ -5,11 +5,11 @@ import io.netty.buffer.Unpooled; + import io.netty.channel.ChannelHandlerContext; + import io.netty.handler.codec.ByteToMessageDecoder; + import io.netty.handler.codec.CorruptedFrameException; ++ + import java.util.List; + + public class Varint21FrameDecoder extends ByteToMessageDecoder + { +- + private static boolean DIRECT_WARNING; + + @Override +diff --git a/proxy/src/main/java/dev/_2lstudios/flamecord/commands/FlameCordCommand.java b/proxy/src/main/java/dev/_2lstudios/flamecord/commands/FlameCordCommand.java +index fb81adee0..173b47f33 100644 +--- a/proxy/src/main/java/dev/_2lstudios/flamecord/commands/FlameCordCommand.java ++++ b/proxy/src/main/java/dev/_2lstudios/flamecord/commands/FlameCordCommand.java +@@ -4,6 +4,7 @@ import java.util.Collection; + import java.util.HashSet; + + import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.antibot.StatsData; + import dev._2lstudios.flamecord.configuration.MessagesConfiguration; + import net.md_5.bungee.BungeeCord; + import net.md_5.bungee.api.CommandSender; +@@ -27,9 +28,7 @@ private final BungeeCord bungeeCord; + + if (sender.hasPermission("flamecord.usage")) { + if (args.length > 0) { +- final String arg0 = args[0]; +- +- switch (arg0) { ++ switch (args[0]) { + case "reload": { + // FlameCord - Collect ips from servers + final Collection whitelistedAddresses = new HashSet<>(); +@@ -43,9 +42,44 @@ private final BungeeCord bungeeCord; + .fromLegacyText(messagesConfiguration.getTranslation("flamecord_reload"))); + break; + } ++ case "stats": { ++ StatsData statsData = FlameCord.getInstance().getStatsData(); ++ int totalPings = statsData.getTotalPings(); ++ int totalConnections = statsData.getTotalConnections(); ++ int lastPings = statsData.getLastPings(); ++ int lastConnections = statsData.getLastConnections(); ++ ++ sender.sendMessage(TextComponent.fromLegacyText(messagesConfiguration.getTranslation("antibot_stats", totalPings, totalConnections, lastPings, lastConnections))); ++ break; ++ } ++ case "firewall": { ++ if (args.length > 2) { ++ String ip = args[2]; ++ ++ switch (args[1]) { ++ case "add": { ++ FlameCord.getInstance().getAddressDataManager().getAddressData(ip).firewall("Blacklisted by command"); ++ sender.sendMessage(TextComponent.fromLegacyText(messagesConfiguration.getTranslation("flamecord_firewall_add", ip))); ++ break; ++ } ++ case "remove": { ++ FlameCord.getInstance().getAddressDataManager().getAddressData(ip).unfirewall(); ++ sender.sendMessage(TextComponent.fromLegacyText(messagesConfiguration.getTranslation("flamecord_firewall_remove", ip))); ++ break; ++ } ++ default: { ++ sender.sendMessage(TextComponent.fromLegacyText(messagesConfiguration.getTranslation("flamecord_firewall_help"))); ++ break; ++ } ++ } ++ } else { ++ sender.sendMessage(TextComponent.fromLegacyText(messagesConfiguration.getTranslation("flamecord_firewall_help"))); ++ } ++ ++ break; ++ } + default: { +- sender.sendMessage(TextComponent.fromLegacyText( +- messagesConfiguration.getTranslation("flamecord_help", bungeeCord.getVersion()))); ++ sender.sendMessage(TextComponent.fromLegacyText(messagesConfiguration.getTranslation("flamecord_help", bungeeCord.getVersion()))); + break; + } + } +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +index 8994454c7..62712330e 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +@@ -533,6 +533,11 @@ public class BungeeCord extends ProxyServer + } catch (InterruptedException ignored) {} + } + ++ // FlameCord start - Antibot System ++ FlameCord.getInstance().shutdown(); ++ getLogger().info( "Shutting down FlameCord linux command thread" ); ++ // FlameCord end - Antibot System ++ + getLogger().info( "Thank you and goodbye" ); + // Need to close loggers after last message! + org.apache.logging.log4j.LogManager.shutdown(); // Waterfall +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 +index 58be68b02..b88e64526 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -22,8 +22,8 @@ import javax.crypto.SecretKey; + import javax.crypto.spec.SecretKeySpec; + + import dev._2lstudios.flamecord.FlameCord; +- +-import dev._2lstudios.flamecord.configuration.FlameConfig; ++import dev._2lstudios.flamecord.antibot.AddressData; ++import dev._2lstudios.flamecord.antibot.CheckManager; + import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; + import lombok.Getter; + import lombok.RequiredArgsConstructor; +@@ -412,6 +412,22 @@ public class InitialHandler extends PacketHandler implements PendingConnection + public void handle(Handshake handshake) throws Exception + { + Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" ); ++ ++ // FlameCord start - Antibot System ++ // Close and firewall on invalid protocol ++ int protocol = handshake.getRequestedProtocol(); ++ ++ if (protocol != 1 && protocol != 2) { ++ if ( FlameCord.getInstance().getFlameCordConfiguration().isAntibotRatelimitFirewall() ) ++ { ++ FlameCord.getInstance().getAddressDataManager().getAddressData(ch.getChannel().remoteAddress()).firewall("Invalid handshake protocol"); ++ } ++ ++ ch.close(); ++ return; ++ } ++ // FlameCord end - Antibot System ++ + this.handshake = handshake; + ch.setVersion( handshake.getProtocolVersion() ); + ch.getHandle().pipeline().remove( PipelineUtils.LEGACY_KICKER ); +@@ -442,6 +458,11 @@ public class InitialHandler extends PacketHandler implements PendingConnection + return; + } + ++ // FlameCord start - Antibot System ++ AddressData addressData = FlameCord.getInstance().getAddressDataManager().getAddressData( ch.getRemoteAddress() ); ++ CheckManager checkManager = FlameCord.getInstance().getCheckManager(); ++ // FlameCord end - Antibot System ++ + switch ( handshake.getRequestedProtocol() ) + { + case 1: +@@ -453,6 +474,17 @@ public class InitialHandler extends PacketHandler implements PendingConnection + } + thisState = State.STATUS; + ch.setProtocol( Protocol.STATUS ); ++ ++ // FlameCord start - Antibot System ++ addressData.addPing(); ++ ++ if ( checkManager.getRatelimitCheck().check( ch.getRemoteAddress(), 2 ) ) ++ { ++ disconnect( bungee.getTranslation( "antibot_ratelimit", addressData.getPingsSecond() ) ); ++ return; ++ } ++ // FlameCord end - Antibot System ++ + break; + case 2: + // Login +@@ -464,6 +496,16 @@ public class InitialHandler extends PacketHandler implements PendingConnection + thisState = State.USERNAME; + ch.setProtocol( Protocol.LOGIN ); + ++ // FlameCord start - Antibot System ++ addressData.addConnection(); ++ ++ if ( checkManager.getRatelimitCheck().check( ch.getRemoteAddress(), 2 ) ) ++ { ++ disconnect( bungee.getTranslation( "antibot_ratelimit", addressData.getConnectionsSecond() ) ); ++ return; ++ } ++ // FlameCord end - Antibot System ++ + if ( !ProtocolConstants.SUPPORTED_VERSION_IDS.contains( handshake.getProtocolVersion() ) ) + { + if ( handshake.getProtocolVersion() > bungee.getProtocolVersion() ) +@@ -527,6 +569,38 @@ public class InitialHandler extends PacketHandler implements PendingConnection + return; + } + ++ // FlameCord start - Antibot System ++ CheckManager checkManager = FlameCord.getInstance().getCheckManager(); ++ AddressData addressData = FlameCord.getInstance().getAddressDataManager().getAddressData( ch.getRemoteAddress() ); ++ String nickname = loginRequest.getData(); ++ ++ addressData.addNickname( nickname ); ++ ++ if ( checkManager.getAccountsCheck().check( ch.getRemoteAddress(), nickname ) ) ++ { ++ disconnect( bungee.getTranslation( "antibot_accounts", addressData.getNicknames().size() ) ); ++ return; ++ } ++ ++ if ( checkManager.getNicknameCheck().check( ch.getRemoteAddress() ) ) ++ { ++ disconnect( bungee.getTranslation( "antibot_nickname", loginRequest.getData() ) ); ++ return; ++ } ++ ++ if ( checkManager.getReconnectCheck().check( ch.getRemoteAddress() ) ) ++ { ++ disconnect( bungee.getTranslation( "antibot_reconnect", FlameCord.getInstance().getFlameCordConfiguration().getAntibotReconnectAttempts() - addressData.getTotalConnections() ) ); ++ return; ++ } ++ ++ if ( checkManager.getCountryCheck().check( ch.getRemoteAddress() ) ) ++ { ++ disconnect( bungee.getTranslation( "antibot_country", addressData.getCountry() ) ); ++ return; ++ } ++ // FlameCord end - Antibot System ++ + // If offline mode and they are already on, don't allow connect + // We can just check by UUID here as names are based on UUID + if ( !isOnlineMode() && bungee.getPlayer( getUniqueId() ) != null ) +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 +index 66332af4e..c16e28c80 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java +@@ -4,11 +4,15 @@ import com.google.common.base.Preconditions; + import com.mojang.brigadier.context.StringRange; + import com.mojang.brigadier.suggestion.Suggestion; + import com.mojang.brigadier.suggestion.Suggestions; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.antibot.CheckManager; + import io.netty.channel.Channel; + import java.util.ArrayList; + import java.util.LinkedList; + import java.util.List; + import java.util.UUID; ++ + import net.md_5.bungee.BungeeCord; + import net.md_5.bungee.ServerConnection.KeepAliveData; + import net.md_5.bungee.UserConnection; +@@ -207,6 +211,22 @@ public class UpstreamBridge extends PacketHandler + } + Preconditions.checkArgument(!empty, "Chat message is empty"); + ++ // FlameCord start - Antibot System ++ final CheckManager checkManager = FlameCord.getInstance().getCheckManager(); ++ ++ if ( checkManager.getFastChatCheck().check( con.getCh().getRemoteAddress() ) ) ++ { ++ con.disconnect( bungee.getTranslation( "antibot_fastchat" ) ); ++ throw CancelSendSignal.INSTANCE; ++ } ++ ++ if ( checkManager.getPasswordCheck().check( con.getCh().getRemoteAddress(), message ) ) ++ { ++ con.disconnect( bungee.getTranslation( "antibot_password", checkManager.getPasswordCheck().getRepeatCount() ) ); ++ throw CancelSendSignal.INSTANCE; ++ } ++ // FlameCord end - Antibot System ++ + ChatEvent chatEvent = new ChatEvent( con, con.getServer(), message ); + if ( !bungee.getPluginManager().callEvent( chatEvent ).isCancelled() ) + { +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 +index e2911d5e4..1e3608fa4 100644 +--- a/proxy/src/main/java/net/md_5/bungee/http/HttpHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/http/HttpHandler.java +@@ -8,7 +8,11 @@ import io.netty.handler.codec.http.HttpResponse; + import io.netty.handler.codec.http.HttpResponseStatus; + import io.netty.handler.codec.http.LastHttpContent; + import java.nio.charset.Charset; ++import java.util.logging.Level; ++ ++import dev._2lstudios.flamecord.FlameCord; + import lombok.RequiredArgsConstructor; ++import net.md_5.bungee.BungeeCord; + import net.md_5.bungee.api.Callback; + + @RequiredArgsConstructor +@@ -21,6 +25,15 @@ public class HttpHandler extends SimpleChannelInboundHandler + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception + { ++ // Flamecord start - Antibot System ++ String reason = cause.getClass().getSimpleName(); ++ if (FlameCord.getInstance().getFlameCordConfiguration().getAntibotFirewalledExceptions().contains(reason)) ++ { ++ FlameCord.getInstance().getAddressDataManager().getAddressData(ctx.channel().remoteAddress()).firewall(reason); ++ BungeeCord.getInstance().getLogger().log( Level.INFO, "[FlameCord] [{0}] was firewalled because of " + reason, ctx.channel().remoteAddress() ); ++ } ++ // Flamecord end - Antibot System ++ + try + { + callback.done( null, cause ); +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 +index b3fa48355..6f1d8336d 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +@@ -154,4 +154,11 @@ public class ChannelWrapper + pipeline.remove( "decompress" ); + } + } ++ ++ // FlameCord start - Antibot System ++ // Make the channel accessible ++ public Channel getChannel() { ++ return ch; ++ } ++ // FlameCord end - Antibot System + } +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 +index 14e3004fc..3fce5ff11 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java +@@ -12,6 +12,8 @@ import io.netty.handler.timeout.ReadTimeoutException; + import java.io.IOException; + import java.net.InetSocketAddress; + import java.util.logging.Level; ++ ++import net.md_5.bungee.BungeeCord; + import net.md_5.bungee.api.ProxyServer; + import net.md_5.bungee.connection.CancelSendSignal; + import net.md_5.bungee.connection.InitialHandler; +@@ -146,6 +148,15 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception + { ++ // Flamecord start - Antibot System ++ String reason = cause.getClass().getSimpleName(); ++ if (FlameCord.getInstance().getFlameCordConfiguration().getAntibotFirewalledExceptions().contains(reason)) ++ { ++ FlameCord.getInstance().getAddressDataManager().getAddressData(ctx.channel().remoteAddress()).firewall(reason); ++ FlameCord.getInstance().getLoggerWrapper().log( Level.INFO, "[FlameCord] [{0}] was firewalled because of " + reason, ctx.channel().remoteAddress() ); ++ } ++ // Flamecord end - Antibot System ++ + if ( ctx.channel().isActive() ) + { + boolean logExceptions = !( handler instanceof PingHandler ); +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 +index ffea15992..f04fdd9ce 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java +@@ -59,6 +59,20 @@ public class PipelineUtils + { + SocketAddress remoteAddress = ( ch.remoteAddress() == null ) ? ch.parent().localAddress() : ch.remoteAddress(); + ++ // FlameCord start - Antibot System ++ String firewallReason = FlameCord.getInstance().getAddressDataManager().getAddressData(ch.remoteAddress()).getFirewallReason(); ++ if ( firewallReason != null ) ++ { ++ if ( FlameCord.getInstance().getFlameCordConfiguration().isAntibotFirewallLog() ) ++ { ++ FlameCord.getInstance().getLoggerWrapper().log( Level.INFO, "[FlameCord] [{0}] is firewalled from the server. ({1})", new Object[]{ ch.remoteAddress(), firewallReason } ); ++ } ++ ++ ch.close(); ++ return; ++ } ++ // FlameCord end - Antibot System ++ + if ( BungeeCord.getInstance().getConnectionThrottle() != null && BungeeCord.getInstance().getConnectionThrottle().throttle( remoteAddress ) ) + { + ch.close(); +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0029-Allow-Invalid-Names.patch b/Waterfall-Proxy-Patches/0029-Allow-Invalid-Names.patch new file mode 100644 index 0000000..2f46e92 --- /dev/null +++ b/Waterfall-Proxy-Patches/0029-Allow-Invalid-Names.patch @@ -0,0 +1,47 @@ +From 1e3a2975964623026cc90326efc27ccafb47ae51 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Fri, 4 Mar 2022 14:09:35 -0300 +Subject: [PATCH] Allow Invalid Names + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +index 0f12f4ae6..6f463b9c6 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +@@ -15,6 +15,10 @@ import net.md_5.bungee.config.Configuration; + import net.md_5.bungee.config.ConfigurationProvider; + + public class FlameCordConfiguration extends FlameConfig { ++ // FlameCord - Allow Invalid Names ++ @Getter ++ private boolean allowInvalidNames = false; ++ + // Antibot accounts + @Getter + private boolean antibotAccountsEnabled = true; +@@ -317,6 +321,9 @@ public class FlameCordConfiguration extends FlameConfig { + // FlameCord - Antibot System + loadAntibot(configuration, whitelistedAddresses); + ++ // FlameCord - Allow Invalid Names ++ this.allowInvalidNames = setIfUnexistant("allow-invalid-names", this.allowInvalidNames, configuration); ++ + save(configuration, configurationFile); + } + } +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 +index b88e64526..e8c14c552 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -528,7 +528,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection + { + Preconditions.checkState( thisState == State.USERNAME, "Not expecting USERNAME" ); + +- if ( !AllowedCharacters.isValidName( loginRequest.getData(), onlineMode ) ) ++ if ( !FlameCord.getInstance().getFlameCordConfiguration().isAllowInvalidNames() && !AllowedCharacters.isValidName( loginRequest.getData(), onlineMode ) ) + { + disconnect( bungee.getTranslation( "name_invalid" ) ); + return; +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0030-Disable-entity-Metadata-Rewrite.patch b/Waterfall-Proxy-Patches/0030-Disable-entity-Metadata-Rewrite.patch new file mode 100644 index 0000000..d80f3db --- /dev/null +++ b/Waterfall-Proxy-Patches/0030-Disable-entity-Metadata-Rewrite.patch @@ -0,0 +1,3240 @@ +From 1a4b10d4833f4c2c0f827ed75897f5187e6ec8ed Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Thu, 10 Mar 2022 20:23:55 -0300 +Subject: [PATCH] Disable entity Metadata Rewrite + + +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 +index 469fe0e1..97bd384b 100644 +--- a/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java ++++ b/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java +@@ -251,11 +251,6 @@ public interface ProxyConfig + */ + boolean isDisableModernTabLimiter(); + +- /** +- * @return Should we disable entity metadata rewriting? +- */ +- boolean isDisableEntityMetadataRewrite(); +- + /** + * Whether tablist rewriting should be disabled or not + * @return {@code true} if tablist rewriting is disabled, {@code false} otherwise +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java b/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java +index 0c334afc..88865b85 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java +@@ -10,8 +10,6 @@ import net.md_5.bungee.protocol.packet.ClientStatus; + import net.md_5.bungee.protocol.packet.Commands; + import net.md_5.bungee.protocol.packet.EncryptionRequest; + import net.md_5.bungee.protocol.packet.EncryptionResponse; +-import net.md_5.bungee.protocol.packet.EntityEffect; // Waterfall +-import net.md_5.bungee.protocol.packet.EntityRemoveEffect; // Waterfall + import net.md_5.bungee.protocol.packet.EntityStatus; + import net.md_5.bungee.protocol.packet.GameState; + import net.md_5.bungee.protocol.packet.Handshake; +@@ -225,14 +223,4 @@ public abstract class AbstractPacketHandler + public void handle(ServerData serverData) throws Exception + { + } +- +- // Waterfall start +- public void handle(EntityEffect entityEffect) throws Exception +- { +- } +- +- public void handle(EntityRemoveEffect removeEffect) throws Exception +- { +- } +- // Waterfall end + } +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 +index 022f94b2..970011e8 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java +@@ -22,8 +22,6 @@ import net.md_5.bungee.protocol.packet.EncryptionRequest; + import net.md_5.bungee.protocol.packet.EncryptionResponse; + import net.md_5.bungee.protocol.packet.EntityStatus; + import net.md_5.bungee.protocol.packet.GameState; +-import net.md_5.bungee.protocol.packet.EntityEffect; +-import net.md_5.bungee.protocol.packet.EntityRemoveEffect; + import net.md_5.bungee.protocol.packet.Handshake; + import net.md_5.bungee.protocol.packet.KeepAlive; + import net.md_5.bungee.protocol.packet.Kick; +@@ -140,20 +138,6 @@ public enum Protocol + map( ProtocolConstants.MINECRAFT_1_17, 0x0D ), + map( ProtocolConstants.MINECRAFT_1_19, 0x0A ) + ); +- // Waterfall start +- TO_CLIENT.registerPacket( +- EntityEffect.class, +- EntityEffect::new, +- map( ProtocolConstants.MINECRAFT_1_7_2, 0x1D ), // FlameCord - 1.7.x support +- map(ProtocolConstants.MINECRAFT_1_9, -1) +- ); +- TO_CLIENT.registerPacket( +- EntityRemoveEffect.class, +- EntityRemoveEffect::new, +- map( ProtocolConstants.MINECRAFT_1_7_2, 0x1E ), // FlameCord - 1.7.x support +- map(ProtocolConstants.MINECRAFT_1_9, -1) +- ); +- // Waterfall end + TO_CLIENT.registerPacket( + PlayerListItem.class, // PlayerInfo + PlayerListItem::new, +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityEffect.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityEffect.java +deleted file mode 100644 +index 0ed78a8c..00000000 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityEffect.java ++++ /dev/null +@@ -1,67 +0,0 @@ +-package net.md_5.bungee.protocol.packet; +- +-import io.netty.buffer.ByteBuf; +-import lombok.AllArgsConstructor; +-import lombok.Data; +-import lombok.EqualsAndHashCode; +-import lombok.NoArgsConstructor; +-import net.md_5.bungee.protocol.AbstractPacketHandler; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-@Data +-@NoArgsConstructor +-@AllArgsConstructor +-@EqualsAndHashCode(callSuper = false) +-public class EntityEffect extends DefinedPacket { +- +- private int entityId; +- private int effectId; +- private int amplifier; +- private int duration; +- private boolean hideParticles; +- +- @Override +- public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { +- this.entityId = protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ? readVarInt(buf) : buf.readInt(); // FlameCord - 1.7.x support +- this.effectId = buf.readUnsignedByte(); +- this.amplifier = buf.readUnsignedByte(); +- this.duration = protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ? readVarInt(buf) : buf.readShort(); // FlameCord - 1.7.x support +- // FlameCord start - 1.7.x support +- if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ) +- { +- this.hideParticles = buf.readBoolean(); +- } +- // FlameCord end - 1.7.x support +- } +- +- @Override +- public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { +- // FlameCord start - 1.7.x support +- if (protocolVersion >= ProtocolConstants.MINECRAFT_1_8) +- { +- writeVarInt(this.entityId, buf); +- } else +- { +- buf.writeInt(effectId); +- } +- // FlameCord end - 1.7.x support +- buf.writeByte(this.effectId); +- buf.writeByte(this.amplifier); +- // FlameCord start - 1.7.x support +- if (protocolVersion >= ProtocolConstants.MINECRAFT_1_8) +- { +- writeVarInt(this.duration, buf); +- } else +- { +- buf.writeShort(duration); +- } +- // FlameCord end - 1.7.x support +- buf.writeBoolean(this.hideParticles); +- } +- +- @Override +- public void handle(AbstractPacketHandler handler) throws Exception { +- handler.handle(this); +- } +-} +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityRemoveEffect.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityRemoveEffect.java +deleted file mode 100644 +index 435b8578..00000000 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityRemoveEffect.java ++++ /dev/null +@@ -1,45 +0,0 @@ +-package net.md_5.bungee.protocol.packet; +- +-import io.netty.buffer.ByteBuf; +-import lombok.AllArgsConstructor; +-import lombok.Data; +-import lombok.EqualsAndHashCode; +-import lombok.NoArgsConstructor; +-import net.md_5.bungee.protocol.AbstractPacketHandler; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-@Data +-@NoArgsConstructor +-@AllArgsConstructor +-@EqualsAndHashCode(callSuper = false) +-public class EntityRemoveEffect extends DefinedPacket { +- +- private int entityId; +- private int effectId; +- +- @Override +- public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { +- this.entityId = protocolVersion >= ProtocolConstants.MINECRAFT_1_8 ? readVarInt(buf) : buf.readInt(); // FlameCord - 1.7.x support +- this.effectId = buf.readUnsignedByte(); +- } +- +- @Override +- public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { +- // FlameCord start - 1.7.x support +- if (protocolVersion >= ProtocolConstants.MINECRAFT_1_8) +- { +- writeVarInt(this.entityId, buf); +- } else +- { +- buf.writeInt(entityId); +- } +- // FlameCord end - 1.7.x support +- buf.writeByte(effectId); +- } +- +- @Override +- public void handle(AbstractPacketHandler handler) throws Exception { +- handler.handle(this); +- } +-} +diff --git a/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java b/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java +index 966d2442..be337a68 100644 +--- a/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java ++++ b/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java +@@ -42,7 +42,6 @@ public class WaterfallConfiguration extends Configuration { + private int tabThrottle = 1000; + private boolean disableModernTabLimiter = true; + +- private boolean disableEntityMetadataRewrite = false; + private boolean disableTabListRewrite = true; + + /* +@@ -74,7 +73,6 @@ public class WaterfallConfiguration extends Configuration { + // Throttling options + tabThrottle = config.getInt("throttling.tab_complete", tabThrottle); + disableModernTabLimiter = config.getBoolean("disable_modern_tab_limiter", disableModernTabLimiter); +- disableEntityMetadataRewrite = config.getBoolean("disable_entity_metadata_rewrite", disableEntityMetadataRewrite); + disableTabListRewrite = config.getBoolean("disable_tab_list_rewrite", disableTabListRewrite); + pluginChannelLimit = config.getInt("registered_plugin_channels_limit", pluginChannelLimit); + pluginChannelNameLimit = config.getInt("plugin_channel_name_limit", pluginChannelNameLimit); +@@ -105,11 +103,6 @@ public class WaterfallConfiguration extends Configuration { + return disableModernTabLimiter; + } + +- @Override +- public boolean isDisableEntityMetadataRewrite() { +- return disableEntityMetadataRewrite; +- } +- + @Override + public boolean isDisableTabListRewrite() { + return disableTabListRewrite; +diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +index 3a07c7c8..f51c668d 100644 +--- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java ++++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +@@ -245,11 +245,6 @@ public class ServerConnector extends PacketHandler + ch.write( new PluginMessage( user.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_13 ? "minecraft:register" : "REGISTER", Joiner.on( "\0" ).join( registeredChannels ).getBytes( StandardCharsets.UTF_8 ), false ) ); + } + +- if (!user.isDisableEntityMetadataRewrite() && user.getSettings() != null ) +- { +- ch.write( user.getSettings() ); +- } +- + if ( user.getForgeClientHandler().getClientModList() == null && !user.getForgeClientHandler().isHandshakeComplete() ) // Vanilla + { + user.getForgeClientHandler().setHandshakeComplete(); +@@ -308,20 +303,6 @@ public class ServerConnector extends PacketHandler + user.getTabListHandler().onServerChange(); + + Scoreboard serverScoreboard = user.getServerSentScoreboard(); +- if ( !user.isDisableEntityMetadataRewrite() ) { // Waterfall +- for ( Objective objective : serverScoreboard.getObjectives() ) +- { +- user.unsafe().sendPacket( new ScoreboardObjective( objective.getName(), objective.getValue(), objective.getType() == null ? null : ScoreboardObjective.HealthDisplay.fromString(objective.getType()), (byte) 1 ) ); // FlameCord - 1.7 support +- } +- for ( Score score : serverScoreboard.getScores() ) +- { +- user.unsafe().sendPacket( new ScoreboardScore( score.getItemName(), (byte) 1, score.getScoreName(), score.getValue() ) ); +- } +- for ( Team team : serverScoreboard.getTeams() ) +- { +- user.unsafe().sendPacket( new net.md_5.bungee.protocol.packet.Team( team.getName() ) ); +- } +- } // Waterfall + serverScoreboard.clear(); + + for ( UUID bossbar : user.getSentBossBars() ) +@@ -340,33 +321,27 @@ public class ServerConnector extends PacketHandler + } + + user.setDimensionChange( true ); +- if ( !user.isDisableEntityMetadataRewrite() && login.getDimension() == user.getDimension() ) // Waterfall - defer +- { +- user.unsafe().sendPacket( new Respawn( (Integer) login.getDimension() >= 0 ? -1 : 0, login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false, login.getDeathLocation() ) ); +- } + + user.setServerEntityId( login.getEntityId() ); + + // Waterfall start +- if ( user.isDisableEntityMetadataRewrite() ) { +- // Ensure that we maintain consistency +- user.setClientEntityId( login.getEntityId() ); ++ // Ensure that we maintain consistency ++ user.setClientEntityId( login.getEntityId() ); + +- // Only send if we are not in the same dimension +- if ( login.getDimension() != user.getDimension() ) // Waterfall - defer +- { +- user.unsafe().sendPacket( new Respawn( (Integer) user.getDimension() >= 0 ? -1 : 0, login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false, login.getDeathLocation() ) ); +- } ++ // Only send if we are not in the same dimension ++ if ( login.getDimension() != user.getDimension() ) // Waterfall - defer ++ { ++ user.unsafe().sendPacket( new Respawn( (Integer) user.getDimension() >= 0 ? -1 : 0, login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false, login.getDeathLocation() ) ); ++ } + +- Login modLogin = new Login( login.getEntityId(), login.isHardcore(), login.getGameMode(), login.getPreviousGameMode(), login.getWorldNames(), login.getDimensions(), login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(), +- (byte) user.getPendingConnection().getListener().getTabListSize(), login.getLevelType(), login.getViewDistance(), login.getSimulationDistance(), login.isReducedDebugInfo(), login.isNormalRespawn(), login.isDebug(), login.isFlat(), login.getDeathLocation() ); +- user.unsafe().sendPacket(modLogin); ++ Login modLogin = new Login( login.getEntityId(), login.isHardcore(), login.getGameMode(), login.getPreviousGameMode(), login.getWorldNames(), login.getDimensions(), login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(), ++ (byte) user.getPendingConnection().getListener().getTabListSize(), login.getLevelType(), login.getViewDistance(), login.getSimulationDistance(), login.isReducedDebugInfo(), login.isNormalRespawn(), login.isDebug(), login.isFlat(), login.getDeathLocation() ); ++ user.unsafe().sendPacket(modLogin); + +- // Only send if we're in the same dimension +- if ( login.getDimension() == user.getDimension() ) // Waterfall - defer +- { +- user.unsafe().sendPacket( new Respawn( (Integer) login.getDimension() >= 0 ? -1 : 0, login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false, login.getDeathLocation() ) ); +- } ++ // Only send if we're in the same dimension ++ if ( login.getDimension() == user.getDimension() ) // Waterfall - defer ++ { ++ user.unsafe().sendPacket( new Respawn( (Integer) login.getDimension() >= 0 ? -1 : 0, login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false, login.getDeathLocation() ) ); + } + // Waterfall end + user.unsafe().sendPacket( new Respawn( login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false, login.getDeathLocation() ) ); +diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +index 90922797..f3d60253 100644 +--- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java ++++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +@@ -44,7 +44,6 @@ import net.md_5.bungee.api.event.ServerConnectEvent; + import net.md_5.bungee.api.score.Scoreboard; + import net.md_5.bungee.chat.ComponentSerializer; + import net.md_5.bungee.connection.InitialHandler; +-import net.md_5.bungee.entitymap.EntityMap; + import net.md_5.bungee.forge.ForgeClientHandler; + import net.md_5.bungee.forge.ForgeConstants; + import net.md_5.bungee.forge.ForgeServerHandler; +@@ -137,8 +136,6 @@ public final class UserConnection implements ProxiedPlayer + /*========================================================================*/ + @Getter + private String displayName; +- @Getter +- private EntityMap entityRewrite; + private Locale locale; + /*========================================================================*/ + @Getter +@@ -159,8 +156,6 @@ public final class UserConnection implements ProxiedPlayer + + public void init() + { +- this.entityRewrite = EntityMap.getEntityMap( getPendingConnection().getVersion() ); +- + this.displayName = name; + + tabListHandler = new ServerUnique( this ); +@@ -794,10 +789,4 @@ public final class UserConnection implements ProxiedPlayer + { + return serverSentScoreboard; + } +- +- // Waterfall start +- public boolean isDisableEntityMetadataRewrite() { +- return entityRewrite == net.md_5.bungee.entitymap.EntityMap_Dummy.INSTANCE; +- } +- // Waterfall end + } +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 +index 0ac22ad3..6e425764 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java +@@ -46,7 +46,6 @@ import net.md_5.bungee.api.score.Score; + import net.md_5.bungee.api.score.Scoreboard; + import net.md_5.bungee.api.score.Team; + import net.md_5.bungee.chat.ComponentSerializer; +-import net.md_5.bungee.entitymap.EntityMap; + import net.md_5.bungee.netty.ChannelWrapper; + import net.md_5.bungee.netty.PacketHandler; + import net.md_5.bungee.protocol.DefinedPacket; +@@ -54,8 +53,6 @@ import net.md_5.bungee.protocol.PacketWrapper; + import net.md_5.bungee.protocol.ProtocolConstants; + import net.md_5.bungee.protocol.packet.BossBar; + import net.md_5.bungee.protocol.packet.Commands; +-import net.md_5.bungee.protocol.packet.EntityEffect; +-import net.md_5.bungee.protocol.packet.EntityRemoveEffect; + import net.md_5.bungee.protocol.packet.KeepAlive; + import net.md_5.bungee.protocol.packet.Kick; + import net.md_5.bungee.protocol.packet.PlayerListItem; +@@ -150,11 +147,6 @@ public class DownstreamBridge extends PacketHandler + @Override + public void handle(PacketWrapper packet) throws Exception + { +- EntityMap rewrite = con.getEntityRewrite(); +- if ( rewrite != null ) +- { +- rewrite.rewriteClientbound( packet.buf, con.getServerEntityId(), con.getClientEntityId(), con.getPendingConnection().getVersion() ); +- } + con.sendPacket( packet ); + } + +@@ -705,34 +697,6 @@ public class DownstreamBridge extends PacketHandler + } + } + +- // Waterfall start +- @Override +- public void handle(EntityEffect entityEffect) throws Exception +- { +- if (con.isDisableEntityMetadataRewrite()) return; // Waterfall +- // Don't send any potions when switching between servers (which involves a handshake), which can trigger a race +- // condition on the client. +- if (this.con.getForgeClientHandler().isForgeUser() && !this.con.getForgeClientHandler().isHandshakeComplete()) { +- throw CancelSendSignal.INSTANCE; +- } +- con.getPotions().put(rewriteEntityId(entityEffect.getEntityId()), entityEffect.getEffectId()); +- } +- +- @Override +- public void handle(EntityRemoveEffect removeEffect) throws Exception +- { +- if (con.isDisableEntityMetadataRewrite()) return; // Waterfall +- con.getPotions().remove(rewriteEntityId(removeEffect.getEntityId()), removeEffect.getEffectId()); +- } +- +- private int rewriteEntityId(int entityId) { +- if (entityId == con.getServerEntityId()) { +- return con.getClientEntityId(); +- } +- return entityId; +- } +- // Waterfall end +- + @Override + public void handle(Respawn respawn) + { +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 +index c16e28c8..5a7f3880 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java +@@ -24,7 +24,6 @@ import net.md_5.bungee.api.event.PlayerDisconnectEvent; + import net.md_5.bungee.api.event.PluginMessageEvent; + import net.md_5.bungee.api.event.SettingsChangedEvent; + import net.md_5.bungee.api.event.TabCompleteEvent; +-import net.md_5.bungee.entitymap.EntityMap; + import net.md_5.bungee.forge.ForgeConstants; + import net.md_5.bungee.netty.ChannelWrapper; + import net.md_5.bungee.netty.PacketHandler; +@@ -144,11 +143,6 @@ public class UpstreamBridge extends PacketHandler + { + if ( con.getServer() != null ) + { +- EntityMap rewrite = con.getEntityRewrite(); +- if ( rewrite != null ) +- { +- rewrite.rewriteServerbound( packet.buf, con.getClientEntityId(), con.getServerEntityId(), con.getPendingConnection().getVersion() ); +- } + con.getServer().getCh().write( packet ); + } + } +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 +deleted file mode 100644 +index 688ff72d..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java ++++ /dev/null +@@ -1,372 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import io.netty.buffer.ByteBufInputStream; +-import java.io.DataInputStream; +-import lombok.AccessLevel; +-import lombok.NoArgsConstructor; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +-import se.llbit.nbt.NamedTag; +-import se.llbit.nbt.Tag; +- +-/** +- * Class to rewrite integers within packets. +- */ +-@NoArgsConstructor(access = AccessLevel.PACKAGE) +-public abstract class EntityMap +-{ +- +- private final boolean[] clientboundInts = new boolean[ 256 ]; +- private final boolean[] clientboundVarInts = new boolean[ 256 ]; +- +- private final boolean[] serverboundInts = new boolean[ 256 ]; +- private final boolean[] serverboundVarInts = new boolean[ 256 ]; +- +- // Returns the correct entity map for the protocol version +- public static EntityMap getEntityMap(int version) +- { +- // Waterfall start +- if (net.md_5.bungee.api.ProxyServer.getInstance().getConfig().isDisableEntityMetadataRewrite()) { +- return EntityMap_Dummy.INSTANCE; +- } +- // Waterfall end +- switch ( version ) +- { +- // FlameCord start - 1.7.x support +- case ProtocolConstants.MINECRAFT_1_7_2: +- return EntityMap_1_7_2.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_7_6: +- return EntityMap_1_7_6.INSTANCE; +- // FlameCord end - 1.7.x support +- case ProtocolConstants.MINECRAFT_1_8: +- return EntityMap_1_8.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_9: +- case ProtocolConstants.MINECRAFT_1_9_1: +- case ProtocolConstants.MINECRAFT_1_9_2: +- return EntityMap_1_9.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_9_4: +- return EntityMap_1_9_4.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_10: +- return EntityMap_1_10.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_11: +- case ProtocolConstants.MINECRAFT_1_11_1: +- return EntityMap_1_11.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_12: +- return EntityMap_1_12.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_12_1: +- case ProtocolConstants.MINECRAFT_1_12_2: +- return EntityMap_1_12_1.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_13: +- case ProtocolConstants.MINECRAFT_1_13_1: +- case ProtocolConstants.MINECRAFT_1_13_2: +- return EntityMap_1_13.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_14: +- case ProtocolConstants.MINECRAFT_1_14_1: +- case ProtocolConstants.MINECRAFT_1_14_2: +- case ProtocolConstants.MINECRAFT_1_14_3: +- case ProtocolConstants.MINECRAFT_1_14_4: +- return EntityMap_1_14.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_15: +- case ProtocolConstants.MINECRAFT_1_15_1: +- case ProtocolConstants.MINECRAFT_1_15_2: +- return EntityMap_1_15.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_16: +- case ProtocolConstants.MINECRAFT_1_16_1: +- return EntityMap_1_16.INSTANCE; +- case ProtocolConstants.MINECRAFT_1_16_2: +- case ProtocolConstants.MINECRAFT_1_16_3: +- case ProtocolConstants.MINECRAFT_1_16_4: +- return EntityMap_1_16_2.INSTANCE_1_16_2; +- case ProtocolConstants.MINECRAFT_1_17: +- case ProtocolConstants.MINECRAFT_1_17_1: +- return EntityMap_1_16_2.INSTANCE_1_17; +- case ProtocolConstants.MINECRAFT_1_18: +- case ProtocolConstants.MINECRAFT_1_18_2: +- return EntityMap_1_16_2.INSTANCE_1_18; +- case ProtocolConstants.MINECRAFT_1_19: +- return EntityMap_1_16_2.INSTANCE_1_19; +- case ProtocolConstants.MINECRAFT_1_19_1: +- case ProtocolConstants.MINECRAFT_1_19_3: +- return EntityMap_1_16_2.INSTANCE_1_19_1; +- } +- throw new RuntimeException( "Version " + version + " has no entity map" ); +- } +- +- protected void addRewrite(int id, ProtocolConstants.Direction direction, boolean varint) +- { +- if ( direction == ProtocolConstants.Direction.TO_CLIENT ) +- { +- if ( varint ) +- { +- clientboundVarInts[id] = true; +- } else +- { +- clientboundInts[id] = true; +- } +- } else if ( varint ) +- { +- serverboundVarInts[id] = true; +- } else +- { +- serverboundInts[id] = true; +- } +- } +- +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- rewrite( packet, oldId, newId, serverboundInts, serverboundVarInts ); +- } +- +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId, int protocolVersion) +- { +- rewriteServerbound( packet, oldId, newId ); +- } +- +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId) +- { +- rewrite( packet, oldId, newId, clientboundInts, clientboundVarInts ); +- } +- +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId, int protocolVersion) +- { +- rewriteClientbound( packet, oldId, newId ); +- } +- +- protected static void rewriteInt(ByteBuf packet, int oldId, int newId, int offset) +- { +- int readId = packet.getInt( offset ); +- if ( readId == oldId ) +- { +- packet.setInt( offset, newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( offset, oldId ); +- } +- } +- +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- protected static void rewriteVarInt(ByteBuf packet, int oldId, int newId, int offset) +- { +- // Need to rewrite the packet because VarInts are variable length +- int readId = DefinedPacket.readVarInt( packet ); +- int readIdLength = packet.readerIndex() - offset; +- if ( readId == oldId || readId == newId ) +- { +- ByteBuf data = packet.copy(); +- +- try { +- packet.readerIndex( offset ); +- packet.writerIndex( offset ); +- DefinedPacket.writeVarInt( readId == oldId ? newId : oldId, packet ); +- packet.writeBytes( data ); +- } finally { +- data.release(); +- } +- } +- } +- +- protected static void rewriteMetaVarInt(ByteBuf packet, int oldId, int newId, int metaIndex) +- { +- rewriteMetaVarInt( packet, oldId, newId, metaIndex, -1 ); +- } +- +- protected static void rewriteMetaVarInt(ByteBuf packet, int oldId, int newId, int metaIndex, int protocolVersion) +- { +- int readerIndex = packet.readerIndex(); +- +- short index; +- while ( ( index = packet.readUnsignedByte() ) != 0xFF ) +- { +- int type = DefinedPacket.readVarInt( packet ); +- if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 ) +- { +- switch ( type ) +- { +- case 5: // optional chat +- if ( packet.readBoolean() ) +- { +- DefinedPacket.readString( packet ); +- } +- continue; +- case 15: // particle +- int particleId = DefinedPacket.readVarInt( packet ); +- +- if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_14 ) +- { +- switch ( particleId ) +- { +- case 3: // minecraft:block +- case 23: // minecraft:falling_dust +- DefinedPacket.readVarInt( packet ); // block state +- break; +- case 14: // minecraft:dust +- packet.skipBytes( 16 ); // float, float, float, flat +- break; +- case 32: // minecraft:item +- readSkipSlot( packet, protocolVersion ); +- break; +- } +- } else +- { +- switch ( particleId ) +- { +- case 3: // minecraft:block +- case 20: // minecraft:falling_dust +- DefinedPacket.readVarInt( packet ); // block state +- break; +- case 11: // minecraft:dust +- packet.skipBytes( 16 ); // float, float, float, flat +- break; +- case 27: // minecraft:item +- readSkipSlot( packet, protocolVersion ); +- break; +- } +- } +- continue; +- default: +- if ( type >= 6 ) +- { +- type--; +- } +- break; +- } +- } +- +- switch ( type ) +- { +- case 0: +- packet.skipBytes( 1 ); // byte +- break; +- case 1: +- if ( index == metaIndex ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, position ); +- packet.readerIndex( position ); +- } +- DefinedPacket.readVarInt( packet ); +- break; +- case 2: +- packet.skipBytes( 4 ); // float +- break; +- case 3: +- case 4: +- DefinedPacket.readString( packet ); +- break; +- case 5: +- readSkipSlot( packet, protocolVersion ); +- break; +- case 6: +- packet.skipBytes( 1 ); // boolean +- break; +- case 7: +- packet.skipBytes( 12 ); // float, float, float +- break; +- case 8: +- packet.readLong(); +- break; +- case 9: +- if ( packet.readBoolean() ) +- { +- packet.skipBytes( 8 ); // long +- } +- break; +- case 10: +- DefinedPacket.readVarInt( packet ); +- break; +- case 11: +- if ( packet.readBoolean() ) +- { +- packet.skipBytes( 16 ); // long, long +- } +- break; +- case 12: +- DefinedPacket.readVarInt( packet ); +- break; +- case 13: +- Tag tag = NamedTag.read( new DataInputStream( new ByteBufInputStream( packet ) ) ); +- if ( tag.isError() ) +- { +- throw new RuntimeException( tag.error() ); +- } +- break; +- case 15: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readVarInt( packet ); +- break; +- case 16: +- if ( index == metaIndex ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId + 1, newId + 1, position ); +- packet.readerIndex( position ); +- } +- DefinedPacket.readVarInt( packet ); +- break; +- case 17: +- DefinedPacket.readVarInt( packet ); +- break; +- default: +- // Waterfall start - Don't lie +- if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 ) +- { +- type++; +- } +- throw new IllegalArgumentException( "Unknown meta type " + type + ": Using mods? refer to disable_entity_metadata_rewrite in waterfall.yml" ); +- // Waterfall end +- } +- } +- +- packet.readerIndex( readerIndex ); +- } +- +- private static void readSkipSlot(ByteBuf packet, int protocolVersion) +- { +- if ( ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13_2 ) ? packet.readBoolean() : packet.readShort() != -1 ) +- { +- if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13_2 ) +- { +- DefinedPacket.readVarInt( packet ); +- } +- packet.skipBytes( ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 ) ? 1 : 3 ); // byte vs byte, short +- +- int position = packet.readerIndex(); +- if ( packet.readByte() != 0 ) +- { +- packet.readerIndex( position ); +- +- Tag tag = NamedTag.read( new DataInputStream( new ByteBufInputStream( packet ) ) ); +- if ( tag.isError() ) +- { +- throw new RuntimeException( tag.error() ); +- } +- } +- } +- } +- +- // Handles simple packets +- private static void rewrite(ByteBuf packet, int oldId, int newId, boolean[] ints, boolean[] varints) +- { +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if (packetId < 0 || packetId > ints.length || packetId > varints.length) { // Invalid packet id +- // Ignore these invalid packets for compatibility reasons +- packet.readerIndex( readerIndex ); +- return; +- } +- +- if ( ints[packetId] ) +- { +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength ); +- } else if ( varints[packetId] ) +- { +- rewriteVarInt( packet, oldId, newId, readerIndex + packetIdLength ); +- } +- packet.readerIndex( readerIndex ); +- } +-} +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 +deleted file mode 100644 +index 6db530c3..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_10.java ++++ /dev/null +@@ -1,182 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_10 extends EntityMap +-{ +- +- static final EntityMap_1_10 INSTANCE = new EntityMap_1_10(); +- +- EntityMap_1_10() +- { +- addRewrite( 0x00, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object : PacketPlayOutSpawnEntity +- addRewrite( 0x01, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb : PacketPlayOutSpawnEntityExperienceOrb +- addRewrite( 0x03, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob : PacketPlayOutSpawnEntityLiving +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting : PacketPlayOutSpawnEntityPainting +- addRewrite( 0x05, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player : PacketPlayOutNamedEntitySpawn +- addRewrite( 0x06, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation : PacketPlayOutAnimation +- addRewrite( 0x08, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation : PacketPlayOutBlockBreakAnimation +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status : PacketPlayOutEntityStatus +- addRewrite( 0x25, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Relative Move : PacketPlayOutRelEntityMove +- addRewrite( 0x26, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look and Relative Move : PacketPlayOutRelEntityMoveLook +- addRewrite( 0x27, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look : PacketPlayOutEntityLook +- addRewrite( 0x28, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity : PacketPlayOutEntity +- addRewrite( 0x2F, ProtocolConstants.Direction.TO_CLIENT, true ); // Use bed : PacketPlayOutBed +- addRewrite( 0x31, ProtocolConstants.Direction.TO_CLIENT, true ); // Remove Entity Effect : PacketPlayOutRemoveEntityEffect +- addRewrite( 0x34, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Head Look : PacketPlayOutEntityHeadRotation +- addRewrite( 0x36, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera : PacketPlayOutCamera +- addRewrite( 0x39, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Metadata : PacketPlayOutEntityMetadata +- addRewrite( 0x3A, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity : PacketPlayOutAttachEntity +- addRewrite( 0x3B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Velocity : PacketPlayOutEntityVelocity +- addRewrite( 0x3C, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment : PacketPlayOutEntityEquipment +- addRewrite( 0x40, ProtocolConstants.Direction.TO_CLIENT, true ); // Attach Entity : PacketPlayOutMount +- addRewrite( 0x48, ProtocolConstants.Direction.TO_CLIENT, true ); // Collect Item : PacketPlayOutCollect +- addRewrite( 0x49, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Teleport : PacketPlayOutEntityTeleport +- addRewrite( 0x4A, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Properties : PacketPlayOutUpdateAttributes +- addRewrite( 0x4B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Effect : PacketPlayOutEntityEffect +- +- addRewrite( 0x0A, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity : PacketPlayInUseEntity +- addRewrite( 0x14, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action : PacketPlayInEntityAction +- } +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- int jumpIndex = packet.readerIndex(); +- switch ( packetId ) +- { +- case 0x3A /* Attach Entity : PacketPlayOutAttachEntity */: +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- break; +- case 0x48 /* Collect Item : PacketPlayOutCollect */: +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- case 0x40 /* Attach Entity : PacketPlayOutMount */: +- DefinedPacket.readVarInt( packet ); +- jumpIndex = packet.readerIndex(); +- // Fall through on purpose to int array of IDs +- case 0x30 /* Destroy Entities : PacketPlayOutEntityDestroy */: +- int count = DefinedPacket.readVarInt( packet ); +- int[] ids = new int[ count ]; +- for ( int i = 0; i < count; i++ ) +- { +- ids[i] = DefinedPacket.readVarInt( packet ); +- } +- packet.readerIndex( jumpIndex ); +- packet.writerIndex( jumpIndex ); +- DefinedPacket.writeVarInt( count, packet ); +- for ( int id : ids ) +- { +- if ( id == oldId ) +- { +- id = newId; +- } else if ( id == newId ) +- { +- id = oldId; +- } +- DefinedPacket.writeVarInt( id, packet ); +- } +- break; +- case 0x00 /* Spawn Object : PacketPlayOutSpawnEntity */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readUUID( packet ); +- int type = packet.readUnsignedByte(); +- +- if ( type == 60 || type == 90 || type == 91 ) +- { +- if ( type == 60 || type == 91 ) +- { +- oldId = oldId + 1; +- newId = newId + 1; +- } +- +- packet.skipBytes( 26 ); // double, double, double, byte, byte +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- if ( readId == oldId ) +- { +- packet.setInt( position, newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( position, oldId ); +- } +- } +- break; +- case 0x05 /* Spawn Player : PacketPlayOutNamedEntitySpawn */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- break; +- case 0x2C /* Combat Event : PacketPlayOutCombatEvent */: +- int event = packet.readUnsignedByte(); +- if ( event == 1 /* End Combat*/ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( event == 2 /* Entity Dead */ ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- packet.readerIndex( position ); +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } +- break; +- case 0x39 /* EntityMetadata : PacketPlayOutEntityMetadata */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- rewriteMetaVarInt( packet, oldId + 1, newId + 1, 6 ); // fishing hook +- rewriteMetaVarInt( packet, oldId, newId, 13 ); // guardian beam +- break; +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteServerbound( packet, oldId, newId ); +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == 0x1B /* Spectate : PacketPlayInSpectate */ && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_11.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_11.java +deleted file mode 100644 +index 36822127..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_11.java ++++ /dev/null +@@ -1,183 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_11 extends EntityMap +-{ +- +- static final EntityMap_1_11 INSTANCE = new EntityMap_1_11(); +- +- EntityMap_1_11() +- { +- addRewrite( 0x00, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object : PacketPlayOutSpawnEntity +- addRewrite( 0x01, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb : PacketPlayOutSpawnEntityExperienceOrb +- addRewrite( 0x03, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob : PacketPlayOutSpawnEntityLiving +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting : PacketPlayOutSpawnEntityPainting +- addRewrite( 0x05, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player : PacketPlayOutNamedEntitySpawn +- addRewrite( 0x06, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation : PacketPlayOutAnimation +- addRewrite( 0x08, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation : PacketPlayOutBlockBreakAnimation +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status : PacketPlayOutEntityStatus +- addRewrite( 0x25, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Relative Move : PacketPlayOutRelEntityMove +- addRewrite( 0x26, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look and Relative Move : PacketPlayOutRelEntityMoveLook +- addRewrite( 0x27, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look : PacketPlayOutEntityLook +- addRewrite( 0x28, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity : PacketPlayOutEntity +- addRewrite( 0x2F, ProtocolConstants.Direction.TO_CLIENT, true ); // Use bed : PacketPlayOutBed +- addRewrite( 0x31, ProtocolConstants.Direction.TO_CLIENT, true ); // Remove Entity Effect : PacketPlayOutRemoveEntityEffect +- addRewrite( 0x34, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Head Look : PacketPlayOutEntityHeadRotation +- addRewrite( 0x36, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera : PacketPlayOutCamera +- addRewrite( 0x39, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Metadata : PacketPlayOutEntityMetadata +- addRewrite( 0x3A, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity : PacketPlayOutAttachEntity +- addRewrite( 0x3B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Velocity : PacketPlayOutEntityVelocity +- addRewrite( 0x3C, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment : PacketPlayOutEntityEquipment +- addRewrite( 0x40, ProtocolConstants.Direction.TO_CLIENT, true ); // Attach Entity : PacketPlayOutMount +- addRewrite( 0x48, ProtocolConstants.Direction.TO_CLIENT, true ); // Collect Item : PacketPlayOutCollect +- addRewrite( 0x49, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Teleport : PacketPlayOutEntityTeleport +- addRewrite( 0x4A, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Properties : PacketPlayOutUpdateAttributes +- addRewrite( 0x4B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Effect : PacketPlayOutEntityEffect +- +- addRewrite( 0x0A, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity : PacketPlayInUseEntity +- addRewrite( 0x14, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action : PacketPlayInEntityAction +- } +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- int jumpIndex = packet.readerIndex(); +- switch ( packetId ) +- { +- case 0x3A /* Attach Entity : PacketPlayOutAttachEntity */: +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- break; +- case 0x48 /* Collect Item : PacketPlayOutCollect */: +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- case 0x40 /* Attach Entity : PacketPlayOutMount */: +- DefinedPacket.readVarInt( packet ); +- jumpIndex = packet.readerIndex(); +- // Fall through on purpose to int array of IDs +- case 0x30 /* Destroy Entities : PacketPlayOutEntityDestroy */: +- int count = DefinedPacket.readVarInt( packet ); +- int[] ids = new int[ count ]; +- for ( int i = 0; i < count; i++ ) +- { +- ids[i] = DefinedPacket.readVarInt( packet ); +- } +- packet.readerIndex( jumpIndex ); +- packet.writerIndex( jumpIndex ); +- DefinedPacket.writeVarInt( count, packet ); +- for ( int id : ids ) +- { +- if ( id == oldId ) +- { +- id = newId; +- } else if ( id == newId ) +- { +- id = oldId; +- } +- DefinedPacket.writeVarInt( id, packet ); +- } +- break; +- case 0x00 /* Spawn Object : PacketPlayOutSpawnEntity */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readUUID( packet ); +- int type = packet.readUnsignedByte(); +- +- if ( type == 60 || type == 90 || type == 91 ) +- { +- if ( type == 60 || type == 91 ) +- { +- oldId = oldId + 1; +- newId = newId + 1; +- } +- +- packet.skipBytes( 26 ); // double, double, double, byte, byte +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- if ( readId == oldId ) +- { +- packet.setInt( position, newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( position, oldId ); +- } +- } +- break; +- case 0x05 /* Spawn Player : PacketPlayOutNamedEntitySpawn */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- break; +- case 0x2C /* Combat Event : PacketPlayOutCombatEvent */: +- int event = packet.readUnsignedByte(); +- if ( event == 1 /* End Combat*/ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( event == 2 /* Entity Dead */ ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- packet.readerIndex( position ); +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } +- break; +- case 0x39 /* EntityMetadata : PacketPlayOutEntityMetadata */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- rewriteMetaVarInt( packet, oldId + 1, newId + 1, 6 ); // fishing hook +- rewriteMetaVarInt( packet, oldId, newId, 7 ); // fireworks (et al) +- rewriteMetaVarInt( packet, oldId, newId, 13 ); // guardian beam +- break; +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteServerbound( packet, oldId, newId ); +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == 0x1B /* Spectate : PacketPlayInSpectate */ && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_12.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_12.java +deleted file mode 100644 +index 38e12ce4..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_12.java ++++ /dev/null +@@ -1,183 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_12 extends EntityMap +-{ +- +- static final EntityMap_1_12 INSTANCE = new EntityMap_1_12(); +- +- EntityMap_1_12() +- { +- addRewrite( 0x00, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object : PacketPlayOutSpawnEntity +- addRewrite( 0x01, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb : PacketPlayOutSpawnEntityExperienceOrb +- addRewrite( 0x03, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob : PacketPlayOutSpawnEntityLiving +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting : PacketPlayOutSpawnEntityPainting +- addRewrite( 0x05, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player : PacketPlayOutNamedEntitySpawn +- addRewrite( 0x06, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation : PacketPlayOutAnimation +- addRewrite( 0x08, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation : PacketPlayOutBlockBreakAnimation +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status : PacketPlayOutEntityStatus +- addRewrite( 0x25, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity : PacketPlayOutEntity +- addRewrite( 0x26, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Relative Move : PacketPlayOutRelEntityMove +- addRewrite( 0x27, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look and Relative Move : PacketPlayOutRelEntityMoveLook +- addRewrite( 0x28, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look : PacketPlayOutEntityLook +- addRewrite( 0x2F, ProtocolConstants.Direction.TO_CLIENT, true ); // Use bed : PacketPlayOutBed +- addRewrite( 0x32, ProtocolConstants.Direction.TO_CLIENT, true ); // Remove Entity Effect : PacketPlayOutRemoveEntityEffect +- addRewrite( 0x35, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Head Look : PacketPlayOutEntityHeadRotation +- addRewrite( 0x38, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera : PacketPlayOutCamera +- addRewrite( 0x3B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Metadata : PacketPlayOutEntityMetadata +- addRewrite( 0x3C, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity : PacketPlayOutAttachEntity +- addRewrite( 0x3D, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Velocity : PacketPlayOutEntityVelocity +- addRewrite( 0x3E, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment : PacketPlayOutEntityEquipment +- addRewrite( 0x42, ProtocolConstants.Direction.TO_CLIENT, true ); // Set Passengers : PacketPlayOutMount +- addRewrite( 0x4A, ProtocolConstants.Direction.TO_CLIENT, true ); // Collect Item : PacketPlayOutCollect +- addRewrite( 0x4B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Teleport : PacketPlayOutEntityTeleport +- addRewrite( 0x4D, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Properties : PacketPlayOutUpdateAttributes +- addRewrite( 0x4E, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Effect : PacketPlayOutEntityEffect +- +- addRewrite( 0x0B, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity : PacketPlayInUseEntity +- addRewrite( 0x15, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action : PacketPlayInEntityAction +- } +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- int jumpIndex = packet.readerIndex(); +- switch ( packetId ) +- { +- case 0x3C /* Attach Entity : PacketPlayOutAttachEntity */: +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- break; +- case 0x4A /* Collect Item : PacketPlayOutCollect */: +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- case 0x42 /* Attach Entity : PacketPlayOutMount */: +- DefinedPacket.readVarInt( packet ); +- jumpIndex = packet.readerIndex(); +- // Fall through on purpose to int array of IDs +- case 0x31 /* Destroy Entities : PacketPlayOutEntityDestroy */: +- int count = DefinedPacket.readVarInt( packet ); +- int[] ids = new int[ count ]; +- for ( int i = 0; i < count; i++ ) +- { +- ids[i] = DefinedPacket.readVarInt( packet ); +- } +- packet.readerIndex( jumpIndex ); +- packet.writerIndex( jumpIndex ); +- DefinedPacket.writeVarInt( count, packet ); +- for ( int id : ids ) +- { +- if ( id == oldId ) +- { +- id = newId; +- } else if ( id == newId ) +- { +- id = oldId; +- } +- DefinedPacket.writeVarInt( id, packet ); +- } +- break; +- case 0x00 /* Spawn Object : PacketPlayOutSpawnEntity */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readUUID( packet ); +- int type = packet.readUnsignedByte(); +- +- if ( type == 60 || type == 90 || type == 91 ) +- { +- if ( type == 60 || type == 91 ) +- { +- oldId = oldId + 1; +- newId = newId + 1; +- } +- +- packet.skipBytes( 26 ); // double, double, double, byte, byte +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- if ( readId == oldId ) +- { +- packet.setInt( position, newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( position, oldId ); +- } +- } +- break; +- case 0x05 /* Spawn Player : PacketPlayOutNamedEntitySpawn */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- break; +- case 0x2C /* Combat Event : PacketPlayOutCombatEvent */: +- int event = packet.readUnsignedByte(); +- if ( event == 1 /* End Combat*/ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( event == 2 /* Entity Dead */ ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- packet.readerIndex( position ); +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } +- break; +- case 0x3B /* EntityMetadata : PacketPlayOutEntityMetadata */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- rewriteMetaVarInt( packet, oldId + 1, newId + 1, 6 ); // fishing hook +- rewriteMetaVarInt( packet, oldId, newId, 7 ); // fireworks (et al) +- rewriteMetaVarInt( packet, oldId, newId, 13 ); // guardian beam +- break; +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteServerbound( packet, oldId, newId ); +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == 0x1E /* Spectate : PacketPlayInSpectate */ && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_12_1.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_12_1.java +deleted file mode 100644 +index 5f296839..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_12_1.java ++++ /dev/null +@@ -1,183 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_12_1 extends EntityMap +-{ +- +- static final EntityMap_1_12_1 INSTANCE = new EntityMap_1_12_1(); +- +- EntityMap_1_12_1() +- { +- addRewrite( 0x00, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object : PacketPlayOutSpawnEntity +- addRewrite( 0x01, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb : PacketPlayOutSpawnEntityExperienceOrb +- addRewrite( 0x03, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob : PacketPlayOutSpawnEntityLiving +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting : PacketPlayOutSpawnEntityPainting +- addRewrite( 0x05, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player : PacketPlayOutNamedEntitySpawn +- addRewrite( 0x06, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation : PacketPlayOutAnimation +- addRewrite( 0x08, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation : PacketPlayOutBlockBreakAnimation +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status : PacketPlayOutEntityStatus +- addRewrite( 0x25, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity : PacketPlayOutEntity +- addRewrite( 0x26, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Relative Move : PacketPlayOutRelEntityMove +- addRewrite( 0x27, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look and Relative Move : PacketPlayOutRelEntityMoveLook +- addRewrite( 0x28, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look : PacketPlayOutEntityLook +- addRewrite( 0x30, ProtocolConstants.Direction.TO_CLIENT, true ); // Use bed : PacketPlayOutBed +- addRewrite( 0x33, ProtocolConstants.Direction.TO_CLIENT, true ); // Remove Entity Effect : PacketPlayOutRemoveEntityEffect +- addRewrite( 0x36, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Head Look : PacketPlayOutEntityHeadRotation +- addRewrite( 0x39, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera : PacketPlayOutCamera +- addRewrite( 0x3C, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Metadata : PacketPlayOutEntityMetadata +- addRewrite( 0x3D, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity : PacketPlayOutAttachEntity +- addRewrite( 0x3E, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Velocity : PacketPlayOutEntityVelocity +- addRewrite( 0x3F, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment : PacketPlayOutEntityEquipment +- addRewrite( 0x43, ProtocolConstants.Direction.TO_CLIENT, true ); // Set Passengers : PacketPlayOutMount +- addRewrite( 0x4B, ProtocolConstants.Direction.TO_CLIENT, true ); // Collect Item : PacketPlayOutCollect +- addRewrite( 0x4C, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Teleport : PacketPlayOutEntityTeleport +- addRewrite( 0x4E, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Properties : PacketPlayOutUpdateAttributes +- addRewrite( 0x4F, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Effect : PacketPlayOutEntityEffect +- +- addRewrite( 0x0A, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity : PacketPlayInUseEntity +- addRewrite( 0x15, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action : PacketPlayInEntityAction +- } +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- int jumpIndex = packet.readerIndex(); +- switch ( packetId ) +- { +- case 0x3D /* Attach Entity : PacketPlayOutAttachEntity */: +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- break; +- case 0x4B /* Collect Item : PacketPlayOutCollect */: +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- case 0x43 /* Attach Entity : PacketPlayOutMount */: +- DefinedPacket.readVarInt( packet ); +- jumpIndex = packet.readerIndex(); +- // Fall through on purpose to int array of IDs +- case 0x32 /* Destroy Entities : PacketPlayOutEntityDestroy */: +- int count = DefinedPacket.readVarInt( packet ); +- int[] ids = new int[ count ]; +- for ( int i = 0; i < count; i++ ) +- { +- ids[i] = DefinedPacket.readVarInt( packet ); +- } +- packet.readerIndex( jumpIndex ); +- packet.writerIndex( jumpIndex ); +- DefinedPacket.writeVarInt( count, packet ); +- for ( int id : ids ) +- { +- if ( id == oldId ) +- { +- id = newId; +- } else if ( id == newId ) +- { +- id = oldId; +- } +- DefinedPacket.writeVarInt( id, packet ); +- } +- break; +- case 0x00 /* Spawn Object : PacketPlayOutSpawnEntity */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readUUID( packet ); +- int type = packet.readUnsignedByte(); +- +- if ( type == 60 || type == 90 || type == 91 ) +- { +- if ( type == 60 || type == 91 ) +- { +- oldId = oldId + 1; +- newId = newId + 1; +- } +- +- packet.skipBytes( 26 ); // double, double, double, byte, byte +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- if ( readId == oldId ) +- { +- packet.setInt( position, newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( position, oldId ); +- } +- } +- break; +- case 0x05 /* Spawn Player : PacketPlayOutNamedEntitySpawn */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- break; +- case 0x2D /* Combat Event : PacketPlayOutCombatEvent */: +- int event = packet.readUnsignedByte(); +- if ( event == 1 /* End Combat*/ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( event == 2 /* Entity Dead */ ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- packet.readerIndex( position ); +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } +- break; +- case 0x3C /* EntityMetadata : PacketPlayOutEntityMetadata */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- rewriteMetaVarInt( packet, oldId + 1, newId + 1, 6 ); // fishing hook +- rewriteMetaVarInt( packet, oldId, newId, 7 ); // fireworks (et al) +- rewriteMetaVarInt( packet, oldId, newId, 13 ); // guardian beam +- break; +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteServerbound( packet, oldId, newId ); +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == 0x1E /* Spectate : PacketPlayInSpectate */ && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_13.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_13.java +deleted file mode 100644 +index f3372f08..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_13.java ++++ /dev/null +@@ -1,183 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_13 extends EntityMap +-{ +- +- static final EntityMap_1_13 INSTANCE = new EntityMap_1_13(); +- +- EntityMap_1_13() +- { +- addRewrite( 0x00, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object : PacketPlayOutSpawnEntity +- addRewrite( 0x01, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb : PacketPlayOutSpawnEntityExperienceOrb +- addRewrite( 0x03, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob : PacketPlayOutSpawnEntityLiving +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting : PacketPlayOutSpawnEntityPainting +- addRewrite( 0x05, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player : PacketPlayOutNamedEntitySpawn +- addRewrite( 0x06, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation : PacketPlayOutAnimation +- addRewrite( 0x08, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation : PacketPlayOutBlockBreakAnimation +- addRewrite( 0x1C, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status : PacketPlayOutEntityStatus +- addRewrite( 0x27, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity : PacketPlayOutEntity +- addRewrite( 0x28, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Relative Move : PacketPlayOutRelEntityMove +- addRewrite( 0x29, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look and Relative Move : PacketPlayOutRelEntityMoveLook +- addRewrite( 0x2A, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look : PacketPlayOutEntityLook +- addRewrite( 0x33, ProtocolConstants.Direction.TO_CLIENT, true ); // Use bed : PacketPlayOutBed +- addRewrite( 0x36, ProtocolConstants.Direction.TO_CLIENT, true ); // Remove Entity Effect : PacketPlayOutRemoveEntityEffect +- addRewrite( 0x39, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Head Look : PacketPlayOutEntityHeadRotation +- addRewrite( 0x3C, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera : PacketPlayOutCamera +- addRewrite( 0x3F, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Metadata : PacketPlayOutEntityMetadata +- addRewrite( 0x40, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity : PacketPlayOutAttachEntity +- addRewrite( 0x41, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Velocity : PacketPlayOutEntityVelocity +- addRewrite( 0x42, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment : PacketPlayOutEntityEquipment +- addRewrite( 0x46, ProtocolConstants.Direction.TO_CLIENT, true ); // Set Passengers : PacketPlayOutMount +- addRewrite( 0x4F, ProtocolConstants.Direction.TO_CLIENT, true ); // Collect Item : PacketPlayOutCollect +- addRewrite( 0x50, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Teleport : PacketPlayOutEntityTeleport +- addRewrite( 0x52, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Properties : PacketPlayOutUpdateAttributes +- addRewrite( 0x53, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Effect : PacketPlayOutEntityEffect +- +- addRewrite( 0x0D, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity : PacketPlayInUseEntity +- addRewrite( 0x19, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action : PacketPlayInEntityAction +- } +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId, int protocolVersion) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- int jumpIndex = packet.readerIndex(); +- switch ( packetId ) +- { +- case 0x40 /* Attach Entity : PacketPlayOutAttachEntity */: +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- break; +- case 0x4F /* Collect Item : PacketPlayOutCollect */: +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- case 0x46 /* Set Passengers : PacketPlayOutMount */: +- DefinedPacket.readVarInt( packet ); +- jumpIndex = packet.readerIndex(); +- // Fall through on purpose to int array of IDs +- case 0x35 /* Destroy Entities : PacketPlayOutEntityDestroy */: +- int count = DefinedPacket.readVarInt( packet ); +- int[] ids = new int[ count ]; +- for ( int i = 0; i < count; i++ ) +- { +- ids[i] = DefinedPacket.readVarInt( packet ); +- } +- packet.readerIndex( jumpIndex ); +- packet.writerIndex( jumpIndex ); +- DefinedPacket.writeVarInt( count, packet ); +- for ( int id : ids ) +- { +- if ( id == oldId ) +- { +- id = newId; +- } else if ( id == newId ) +- { +- id = oldId; +- } +- DefinedPacket.writeVarInt( id, packet ); +- } +- break; +- case 0x00 /* Spawn Object : PacketPlayOutSpawnEntity */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readUUID( packet ); +- int type = packet.readUnsignedByte(); +- +- if ( type == 60 || type == 90 || type == 91 ) +- { +- if ( type == 60 || type == 91 ) +- { +- oldId = oldId + 1; +- newId = newId + 1; +- } +- +- packet.skipBytes( 26 ); // double, double, double, byte, byte +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- if ( readId == oldId ) +- { +- packet.setInt( position, newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( position, oldId ); +- } +- } +- break; +- case 0x05 /* Spawn Player : PacketPlayOutNamedEntitySpawn */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- break; +- case 0x2F /* Combat Event : PacketPlayOutCombatEvent */: +- int event = packet.readUnsignedByte(); +- if ( event == 1 /* End Combat*/ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( event == 2 /* Entity Dead */ ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- packet.readerIndex( position ); +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } +- break; +- case 0x3F /* EntityMetadata : PacketPlayOutEntityMetadata */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- rewriteMetaVarInt( packet, oldId + 1, newId + 1, 6, protocolVersion ); // fishing hook +- rewriteMetaVarInt( packet, oldId, newId, 7, protocolVersion ); // fireworks (et al) +- rewriteMetaVarInt( packet, oldId, newId, 13, protocolVersion ); // guardian beam +- break; +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteServerbound( packet, oldId, newId ); +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == 0x28 /* Spectate : PacketPlayInSpectate */ && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_14.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_14.java +deleted file mode 100644 +index 8210b0a7..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_14.java ++++ /dev/null +@@ -1,187 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_14 extends EntityMap +-{ +- +- static final EntityMap_1_14 INSTANCE = new EntityMap_1_14(); +- +- EntityMap_1_14() +- { +- addRewrite( 0x00, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object : PacketPlayOutSpawnEntity +- addRewrite( 0x01, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb : PacketPlayOutSpawnEntityExperienceOrb +- addRewrite( 0x03, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob : PacketPlayOutSpawnEntityLiving +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting : PacketPlayOutSpawnEntityPainting +- addRewrite( 0x05, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player : PacketPlayOutNamedEntitySpawn +- addRewrite( 0x06, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation : PacketPlayOutAnimation +- addRewrite( 0x08, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation : PacketPlayOutBlockBreakAnimation +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status : PacketPlayOutEntityStatus +- addRewrite( 0x28, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Relative Move : PacketPlayOutRelEntityMove +- addRewrite( 0x29, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look and Relative Move : PacketPlayOutRelEntityMoveLook +- addRewrite( 0x2A, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look : PacketPlayOutEntityLook +- addRewrite( 0x2B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity : PacketPlayOutEntity +- addRewrite( 0x38, ProtocolConstants.Direction.TO_CLIENT, true ); // Remove Entity Effect : PacketPlayOutRemoveEntityEffect +- addRewrite( 0x3B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Head Look : PacketPlayOutEntityHeadRotation +- addRewrite( 0x3E, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera : PacketPlayOutCamera +- addRewrite( 0x43, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Metadata : PacketPlayOutEntityMetadata +- addRewrite( 0x44, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity : PacketPlayOutAttachEntity +- addRewrite( 0x45, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Velocity : PacketPlayOutEntityVelocity +- addRewrite( 0x46, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment : PacketPlayOutEntityEquipment +- addRewrite( 0x4A, ProtocolConstants.Direction.TO_CLIENT, true ); // Set Passengers : PacketPlayOutMount +- addRewrite( 0x55, ProtocolConstants.Direction.TO_CLIENT, true ); // Collect Item : PacketPlayOutCollect +- addRewrite( 0x56, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Teleport : PacketPlayOutEntityTeleport +- addRewrite( 0x58, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Properties : PacketPlayOutUpdateAttributes +- addRewrite( 0x59, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Effect : PacketPlayOutEntityEffect +- +- addRewrite( 0x0E, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity : PacketPlayInUseEntity +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action : PacketPlayInEntityAction +- } +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId, int protocolVersion) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- int jumpIndex = packet.readerIndex(); +- switch ( packetId ) +- { +- case 0x44 /* Attach Entity : PacketPlayOutAttachEntity */: +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- break; +- case 0x55 /* Collect Item : PacketPlayOutCollect */: +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- case 0x4A /* Set Passengers : PacketPlayOutMount */: +- DefinedPacket.readVarInt( packet ); +- jumpIndex = packet.readerIndex(); +- // Fall through on purpose to int array of IDs +- case 0x37 /* Destroy Entities : PacketPlayOutEntityDestroy */: +- int count = DefinedPacket.readVarInt( packet ); +- int[] ids = new int[ count ]; +- for ( int i = 0; i < count; i++ ) +- { +- ids[i] = DefinedPacket.readVarInt( packet ); +- } +- packet.readerIndex( jumpIndex ); +- packet.writerIndex( jumpIndex ); +- DefinedPacket.writeVarInt( count, packet ); +- for ( int id : ids ) +- { +- if ( id == oldId ) +- { +- id = newId; +- } else if ( id == newId ) +- { +- id = oldId; +- } +- DefinedPacket.writeVarInt( id, packet ); +- } +- break; +- case 0x00 /* Spawn Object : PacketPlayOutSpawnEntity */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readUUID( packet ); +- int type = DefinedPacket.readVarInt( packet ); +- +- if ( type == 2 || type == 101 || type == 71 ) // arrow, fishing_bobber or spectral_arrow +- { +- if ( type == 2 || type == 71 ) // arrow or spectral_arrow +- { +- oldId = oldId + 1; +- newId = newId + 1; +- } +- +- packet.skipBytes( 26 ); // double, double, double, byte, byte +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- if ( readId == oldId ) +- { +- packet.setInt( position, newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( position, oldId ); +- } +- } +- break; +- case 0x05 /* Spawn Player : PacketPlayOutNamedEntitySpawn */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- break; +- case 0x32 /* Combat Event : PacketPlayOutCombatEvent */: +- int event = packet.readUnsignedByte(); +- if ( event == 1 /* End Combat*/ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( event == 2 /* Entity Dead */ ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- packet.readerIndex( position ); +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } +- break; +- case 0x43 /* EntityMetadata : PacketPlayOutEntityMetadata */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- rewriteMetaVarInt( packet, oldId + 1, newId + 1, 7, protocolVersion ); // fishing hook +- rewriteMetaVarInt( packet, oldId, newId, 8, protocolVersion ); // fireworks (et al) +- rewriteMetaVarInt( packet, oldId, newId, 15, protocolVersion ); // guardian beam +- break; +- case 0x50 /* Entity Sound Effect : PacketPlayOutEntitySound */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteServerbound( packet, oldId, newId ); +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == 0x2B /* Spectate : PacketPlayInSpectate */ && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_15.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_15.java +deleted file mode 100644 +index c2cf810f..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_15.java ++++ /dev/null +@@ -1,187 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_15 extends EntityMap +-{ +- +- static final EntityMap_1_15 INSTANCE = new EntityMap_1_15(); +- +- EntityMap_1_15() +- { +- addRewrite( 0x00, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object : PacketPlayOutSpawnEntity +- addRewrite( 0x01, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb : PacketPlayOutSpawnEntityExperienceOrb +- addRewrite( 0x03, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob : PacketPlayOutSpawnEntityLiving +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting : PacketPlayOutSpawnEntityPainting +- addRewrite( 0x05, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player : PacketPlayOutNamedEntitySpawn +- addRewrite( 0x06, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation : PacketPlayOutAnimation +- addRewrite( 0x09, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation : PacketPlayOutBlockBreakAnimation +- addRewrite( 0x1C, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status : PacketPlayOutEntityStatus +- addRewrite( 0x29, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Relative Move : PacketPlayOutRelEntityMove +- addRewrite( 0x2A, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look and Relative Move : PacketPlayOutRelEntityMoveLook +- addRewrite( 0x2B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look : PacketPlayOutEntityLook +- addRewrite( 0x2C, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity : PacketPlayOutEntity +- addRewrite( 0x39, ProtocolConstants.Direction.TO_CLIENT, true ); // Remove Entity Effect : PacketPlayOutRemoveEntityEffect +- addRewrite( 0x3C, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Head Look : PacketPlayOutEntityHeadRotation +- addRewrite( 0x3F, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera : PacketPlayOutCamera +- addRewrite( 0x44, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Metadata : PacketPlayOutEntityMetadata +- addRewrite( 0x45, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity : PacketPlayOutAttachEntity +- addRewrite( 0x46, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Velocity : PacketPlayOutEntityVelocity +- addRewrite( 0x47, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment : PacketPlayOutEntityEquipment +- addRewrite( 0x4B, ProtocolConstants.Direction.TO_CLIENT, true ); // Set Passengers : PacketPlayOutMount +- addRewrite( 0x56, ProtocolConstants.Direction.TO_CLIENT, true ); // Collect Item : PacketPlayOutCollect +- addRewrite( 0x57, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Teleport : PacketPlayOutEntityTeleport +- addRewrite( 0x59, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Properties : PacketPlayOutUpdateAttributes +- addRewrite( 0x5A, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Effect : PacketPlayOutEntityEffect +- +- addRewrite( 0x0E, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity : PacketPlayInUseEntity +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action : PacketPlayInEntityAction +- } +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId, int protocolVersion) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- int jumpIndex = packet.readerIndex(); +- switch ( packetId ) +- { +- case 0x45 /* Attach Entity : PacketPlayOutAttachEntity */: +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- break; +- case 0x56 /* Collect Item : PacketPlayOutCollect */: +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- case 0x4B /* Set Passengers : PacketPlayOutMount */: +- DefinedPacket.readVarInt( packet ); +- jumpIndex = packet.readerIndex(); +- // Fall through on purpose to int array of IDs +- case 0x38 /* Destroy Entities : PacketPlayOutEntityDestroy */: +- int count = DefinedPacket.readVarInt( packet ); +- int[] ids = new int[ count ]; +- for ( int i = 0; i < count; i++ ) +- { +- ids[i] = DefinedPacket.readVarInt( packet ); +- } +- packet.readerIndex( jumpIndex ); +- packet.writerIndex( jumpIndex ); +- DefinedPacket.writeVarInt( count, packet ); +- for ( int id : ids ) +- { +- if ( id == oldId ) +- { +- id = newId; +- } else if ( id == newId ) +- { +- id = oldId; +- } +- DefinedPacket.writeVarInt( id, packet ); +- } +- break; +- case 0x00 /* Spawn Object : PacketPlayOutSpawnEntity */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readUUID( packet ); +- int type = DefinedPacket.readVarInt( packet ); +- +- if ( type == 2 || type == 102 || type == 72 ) // arrow, fishing_bobber or spectral_arrow +- { +- if ( type == 2 || type == 72 ) // arrow or spectral_arrow +- { +- oldId = oldId + 1; +- newId = newId + 1; +- } +- +- packet.skipBytes( 26 ); // double, double, double, byte, byte +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- if ( readId == oldId ) +- { +- packet.setInt( position, newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( position, oldId ); +- } +- } +- break; +- case 0x05 /* Spawn Player : PacketPlayOutNamedEntitySpawn */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- break; +- case 0x33 /* Combat Event : PacketPlayOutCombatEvent */: +- int event = packet.readUnsignedByte(); +- if ( event == 1 /* End Combat*/ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( event == 2 /* Entity Dead */ ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- packet.readerIndex( position ); +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } +- break; +- case 0x44 /* EntityMetadata : PacketPlayOutEntityMetadata */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- rewriteMetaVarInt( packet, oldId + 1, newId + 1, 7, protocolVersion ); // fishing hook +- rewriteMetaVarInt( packet, oldId, newId, 8, protocolVersion ); // fireworks (et al) +- rewriteMetaVarInt( packet, oldId, newId, 16, protocolVersion ); // guardian beam +- break; +- case 0x51 /* Entity Sound Effect : PacketPlayOutEntitySound */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteServerbound( packet, oldId, newId ); +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == 0x2B /* Spectate : PacketPlayInSpectate */ && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_16.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_16.java +deleted file mode 100644 +index c8b06707..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_16.java ++++ /dev/null +@@ -1,187 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_16 extends EntityMap +-{ +- +- static final EntityMap_1_16 INSTANCE = new EntityMap_1_16(); +- +- EntityMap_1_16() +- { +- addRewrite( 0x00, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object : PacketPlayOutSpawnEntity +- addRewrite( 0x01, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb : PacketPlayOutSpawnEntityExperienceOrb +- addRewrite( 0x02, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob : PacketPlayOutSpawnEntityLiving +- addRewrite( 0x03, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting : PacketPlayOutSpawnEntityPainting +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player : PacketPlayOutNamedEntitySpawn +- addRewrite( 0x05, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation : PacketPlayOutAnimation +- addRewrite( 0x08, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation : PacketPlayOutBlockBreakAnimation +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status : PacketPlayOutEntityStatus +- addRewrite( 0x28, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Relative Move : PacketPlayOutRelEntityMove +- addRewrite( 0x29, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look and Relative Move : PacketPlayOutRelEntityMoveLook +- addRewrite( 0x2A, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look : PacketPlayOutEntityLook +- addRewrite( 0x2B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity : PacketPlayOutEntity +- addRewrite( 0x38, ProtocolConstants.Direction.TO_CLIENT, true ); // Remove Entity Effect : PacketPlayOutRemoveEntityEffect +- addRewrite( 0x3B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Head Look : PacketPlayOutEntityHeadRotation +- addRewrite( 0x3E, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera : PacketPlayOutCamera +- addRewrite( 0x44, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Metadata : PacketPlayOutEntityMetadata +- addRewrite( 0x45, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity : PacketPlayOutAttachEntity +- addRewrite( 0x46, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Velocity : PacketPlayOutEntityVelocity +- addRewrite( 0x47, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment : PacketPlayOutEntityEquipment +- addRewrite( 0x4B, ProtocolConstants.Direction.TO_CLIENT, true ); // Set Passengers : PacketPlayOutMount +- addRewrite( 0x55, ProtocolConstants.Direction.TO_CLIENT, true ); // Collect Item : PacketPlayOutCollect +- addRewrite( 0x56, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Teleport : PacketPlayOutEntityTeleport +- addRewrite( 0x58, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Properties : PacketPlayOutUpdateAttributes +- addRewrite( 0x59, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Effect : PacketPlayOutEntityEffect +- +- addRewrite( 0x0E, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity : PacketPlayInUseEntity +- addRewrite( 0x1C, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action : PacketPlayInEntityAction +- } +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId, int protocolVersion) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- int jumpIndex = packet.readerIndex(); +- switch ( packetId ) +- { +- case 0x45 /* Attach Entity : PacketPlayOutAttachEntity */: +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- break; +- case 0x55 /* Collect Item : PacketPlayOutCollect */: +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- case 0x4B /* Set Passengers : PacketPlayOutMount */: +- DefinedPacket.readVarInt( packet ); +- jumpIndex = packet.readerIndex(); +- // Fall through on purpose to int array of IDs +- case 0x37 /* Destroy Entities : PacketPlayOutEntityDestroy */: +- int count = DefinedPacket.readVarInt( packet ); +- int[] ids = new int[ count ]; +- for ( int i = 0; i < count; i++ ) +- { +- ids[i] = DefinedPacket.readVarInt( packet ); +- } +- packet.readerIndex( jumpIndex ); +- packet.writerIndex( jumpIndex ); +- DefinedPacket.writeVarInt( count, packet ); +- for ( int id : ids ) +- { +- if ( id == oldId ) +- { +- id = newId; +- } else if ( id == newId ) +- { +- id = oldId; +- } +- DefinedPacket.writeVarInt( id, packet ); +- } +- break; +- case 0x00 /* Spawn Object : PacketPlayOutSpawnEntity */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readUUID( packet ); +- int type = DefinedPacket.readVarInt( packet ); +- +- if ( type == 2 || type == 102 || type == 72 ) // arrow, fishing_bobber or spectral_arrow +- { +- if ( type == 2 || type == 72 ) // arrow or spectral_arrow +- { +- oldId = oldId + 1; +- newId = newId + 1; +- } +- +- packet.skipBytes( 26 ); // double, double, double, byte, byte +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- if ( readId == oldId ) +- { +- packet.setInt( position, newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( position, oldId ); +- } +- } +- break; +- case 0x04 /* Spawn Player : PacketPlayOutNamedEntitySpawn */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- break; +- case 0x32 /* Combat Event : PacketPlayOutCombatEvent */: +- int event = packet.readUnsignedByte(); +- if ( event == 1 /* End Combat*/ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( event == 2 /* Entity Dead */ ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- packet.readerIndex( position ); +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } +- break; +- case 0x44 /* EntityMetadata : PacketPlayOutEntityMetadata */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- rewriteMetaVarInt( packet, oldId + 1, newId + 1, 7, protocolVersion ); // fishing hook +- rewriteMetaVarInt( packet, oldId, newId, 8, protocolVersion ); // fireworks (et al) +- rewriteMetaVarInt( packet, oldId, newId, 16, protocolVersion ); // guardian beam +- break; +- case 0x50 /* Entity Sound Effect : PacketPlayOutEntitySound */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteServerbound( packet, oldId, newId ); +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == 0x2C /* Spectate : PacketPlayInSpectate */ && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_16_2.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_16_2.java +deleted file mode 100644 +index 7241dc56..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_16_2.java ++++ /dev/null +@@ -1,76 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import lombok.AccessLevel; +-import lombok.RequiredArgsConstructor; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +- +-@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +-class EntityMap_1_16_2 extends EntityMap +-{ +- +- static final EntityMap_1_16_2 INSTANCE_1_16_2 = new EntityMap_1_16_2( 0x04, 0x2D ); +- static final EntityMap_1_16_2 INSTANCE_1_17 = new EntityMap_1_16_2( 0x04, 0x2D ); +- static final EntityMap_1_16_2 INSTANCE_1_18 = new EntityMap_1_16_2( 0x04, 0x2D ); +- static final EntityMap_1_16_2 INSTANCE_1_19 = new EntityMap_1_16_2( 0x02, 0x2F ); +- static final EntityMap_1_16_2 INSTANCE_1_19_1 = new EntityMap_1_16_2( 0x02, 0x30 ); +- // +- private final int spawnPlayerId; +- private final int spectateId; +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId, int protocolVersion) +- { +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == spawnPlayerId ) +- { +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == spectateId && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_2.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_2.java +deleted file mode 100644 +index cdc07dc4..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_2.java ++++ /dev/null +@@ -1,102 +0,0 @@ +-// FlameCord start - 1.7.x support +-package net.md_5.bungee.entitymap; +- +-import io.netty.buffer.ByteBuf; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_7_2 extends EntityMap +-{ +- +- static final EntityMap INSTANCE = new EntityMap_1_7_2(); +- +- EntityMap_1_7_2() +- { +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Equipment +- addRewrite( 0x0A, ProtocolConstants.Direction.TO_CLIENT, false ); // Use bed +- addRewrite( 0x0B, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation +- addRewrite( 0x0C, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player +- addRewrite( 0x0D, ProtocolConstants.Direction.TO_CLIENT, false ); // Collect Item +- addRewrite( 0x0E, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object +- addRewrite( 0x0F, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob +- addRewrite( 0x10, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting +- addRewrite( 0x11, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb +- addRewrite( 0x12, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Velocity +- addRewrite( 0x14, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity +- addRewrite( 0x15, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Relative Move +- addRewrite( 0x16, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Look +- addRewrite( 0x17, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Look and Relative Move +- addRewrite( 0x18, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Teleport +- addRewrite( 0x19, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Head Look +- addRewrite( 0x1A, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity +- addRewrite( 0x1C, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Metadata +- addRewrite( 0x1D, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Effect +- addRewrite( 0x1E, ProtocolConstants.Direction.TO_CLIENT, false ); // Remove Entity Effect +- addRewrite( 0x20, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Properties +- addRewrite( 0x25, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation +- addRewrite( 0x2C, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Global Entity +- +- addRewrite( 0x02, ProtocolConstants.Direction.TO_SERVER, false ); // Use Entity +- addRewrite( 0x0A, ProtocolConstants.Direction.TO_SERVER, false ); // Animation +- addRewrite( 0x0B, ProtocolConstants.Direction.TO_SERVER, false ); // Entity Action +- } +- +- @Override +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- //Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- if ( packetId == 0x0D /* Collect Item */ || packetId == 0x1B /* Attach Entity */ ) +- { +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- } else if ( packetId == 0x13 /* Destroy Entities */ ) +- { +- int count = packet.getByte( packetIdLength ); +- for ( int i = 0; i < count; i++ ) +- { +- rewriteInt( packet, oldId, newId, packetIdLength + 1 + i * 4 ); +- } +- } else if ( packetId == 0x0E /* Spawn Object */ ) +- { +- DefinedPacket.readVarInt( packet ); +- int type = packet.readUnsignedByte(); +- +- if ( type == 60 || type == 90 ) +- { +- packet.skipBytes( 14 ); +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- int changedId = -1; +- if ( readId == oldId ) +- { +- packet.setInt( position, newId ); +- changedId = newId; +- } else if ( readId == newId ) +- { +- packet.setInt( position, oldId ); +- changedId = oldId; +- } +- if ( changedId != -1 ) +- { +- if ( changedId == 0 && readId != 0 ) +- { // Trim off the extra data +- packet.readerIndex( readerIndex ); +- packet.writerIndex( packet.readableBytes() - 6 ); +- } else if ( changedId != 0 && readId == 0 ) +- { // Add on the extra data +- packet.readerIndex( readerIndex ); +- packet.capacity( packet.readableBytes() + 6 ); +- packet.writerIndex( packet.readableBytes() + 6 ); +- } +- } +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +-// FlameCord end - 1.7.x support +\ No newline at end of file +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_6.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_6.java +deleted file mode 100644 +index cb9174b3..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_6.java ++++ /dev/null +@@ -1,62 +0,0 @@ +-// FlameCord start - 1.7.x support +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.connection.LoginResult; +-import net.md_5.bungee.protocol.DefinedPacket; +- +-class EntityMap_1_7_6 extends EntityMap_1_7_2 +-{ +- +- static final EntityMap_1_7_6 INSTANCE = new EntityMap_1_7_6(); +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- if ( packetId == 0x0C /* Spawn Player */ ) +- { +- DefinedPacket.readVarInt( packet ); +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- String uuid = DefinedPacket.readString( packet ); +- String username = DefinedPacket.readString( packet ); +- int props = DefinedPacket.readVarInt( packet ); +- if ( props == 0 ) +- { +- UserConnection player = (UserConnection) BungeeCord.getInstance().getPlayer( username ); +- if ( player != null ) +- { +- LoginResult profile = player.getPendingConnection().getLoginProfile(); +- if ( profile != null && profile.getProperties() != null +- && profile.getProperties().length >= 1 ) +- { +- ByteBuf rest = packet.copy(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeString( player.getUniqueId().toString(), packet ); +- DefinedPacket.writeString( username, packet ); +- DefinedPacket.writeVarInt( profile.getProperties().length, packet ); +- for ( net.md_5.bungee.protocol.Property property : profile.getProperties() ) +- { +- DefinedPacket.writeString( property.getName(), packet ); +- DefinedPacket.writeString( property.getValue(), packet ); +- DefinedPacket.writeString( property.getSignature(), packet ); +- } +- packet.writeBytes( rest ); +- rest.release(); +- } +- } +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +-// FlameCord end - 1.7.x support +\ No newline at end of file +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 +deleted file mode 100644 +index 8e2dbe69..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_8.java ++++ /dev/null +@@ -1,176 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_8 extends EntityMap +-{ +- +- static final EntityMap_1_8 INSTANCE = new EntityMap_1_8(); +- +- EntityMap_1_8() +- { +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment +- addRewrite( 0x0A, ProtocolConstants.Direction.TO_CLIENT, true ); // Use bed +- addRewrite( 0x0B, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation +- addRewrite( 0x0C, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player +- addRewrite( 0x0D, ProtocolConstants.Direction.TO_CLIENT, true ); // Collect Item +- addRewrite( 0x0E, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object +- addRewrite( 0x0F, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob +- addRewrite( 0x10, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting +- addRewrite( 0x11, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb +- addRewrite( 0x12, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Velocity +- addRewrite( 0x14, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity +- addRewrite( 0x15, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Relative Move +- addRewrite( 0x16, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look +- addRewrite( 0x17, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look and Relative Move +- addRewrite( 0x18, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Teleport +- addRewrite( 0x19, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Head Look +- addRewrite( 0x1A, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity +- addRewrite( 0x1C, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Metadata +- addRewrite( 0x1D, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Effect +- addRewrite( 0x1E, ProtocolConstants.Direction.TO_CLIENT, true ); // Remove Entity Effect +- addRewrite( 0x20, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Properties +- addRewrite( 0x25, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation +- addRewrite( 0x2C, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Global Entity +- addRewrite( 0x43, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera +- addRewrite( 0x49, ProtocolConstants.Direction.TO_CLIENT, true ); // Update Entity NBT +- +- addRewrite( 0x02, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity +- addRewrite( 0x0B, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action +- } +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- //Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- if ( packetId == 0x0D /* Collect Item */ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( packetId == 0x1B /* Attach Entity */ ) +- { +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- } else if ( packetId == 0x13 /* Destroy Entities */ ) +- { +- int count = DefinedPacket.readVarInt( packet ); +- int[] ids = new int[ count ]; +- for ( int i = 0; i < count; i++ ) +- { +- ids[i] = DefinedPacket.readVarInt( packet ); +- } +- packet.readerIndex( readerIndex + packetIdLength ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeVarInt( count, packet ); +- for ( int id : ids ) +- { +- if ( id == oldId ) +- { +- id = newId; +- } else if ( id == newId ) +- { +- id = oldId; +- } +- DefinedPacket.writeVarInt( id, packet ); +- } +- } else if ( packetId == 0x0E /* Spawn Object */ ) +- { +- +- DefinedPacket.readVarInt( packet ); +- int type = packet.readUnsignedByte(); +- +- if ( type == 60 || type == 90 ) +- { +- packet.skipBytes( 14 ); +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- int changedId = readId; +- +- if ( readId == oldId ) +- { +- packet.setInt( position, changedId = newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( position, changedId = oldId ); +- } +- +- if ( readId > 0 && changedId <= 0 ) +- { +- packet.writerIndex( packet.writerIndex() - 6 ); +- } else if ( changedId > 0 && readId <= 0 ) +- { +- packet.ensureWritable( 6 ); +- packet.writerIndex( packet.writerIndex() + 6 ); +- } +- } +- } else if ( packetId == 0x0C /* Spawn Player */ ) +- { +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- } else if ( packetId == 0x42 /* Combat Event */ ) +- { +- int event = packet.readUnsignedByte(); +- if ( event == 1 /* End Combat*/ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( event == 2 /* Entity Dead */ ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- packet.readerIndex( position ); +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteServerbound( packet, oldId, newId ); +- //Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == 0x18 /* Spectate */ && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +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 +deleted file mode 100644 +index d61dc0cb..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_9.java ++++ /dev/null +@@ -1,182 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_9 extends EntityMap +-{ +- +- static final EntityMap_1_9 INSTANCE = new EntityMap_1_9(); +- +- EntityMap_1_9() +- { +- addRewrite( 0x00, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object : PacketPlayOutSpawnEntity +- addRewrite( 0x01, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb : PacketPlayOutSpawnEntityExperienceOrb +- addRewrite( 0x03, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob : PacketPlayOutSpawnEntityLiving +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting : PacketPlayOutSpawnEntityPainting +- addRewrite( 0x05, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player : PacketPlayOutNamedEntitySpawn +- addRewrite( 0x06, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation : PacketPlayOutAnimation +- addRewrite( 0x08, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation : PacketPlayOutBlockBreakAnimation +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status : PacketPlayOutEntityStatus +- addRewrite( 0x25, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Relative Move : PacketPlayOutRelEntityMove +- addRewrite( 0x26, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look and Relative Move : PacketPlayOutRelEntityMoveLook +- addRewrite( 0x27, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look : PacketPlayOutEntityLook +- addRewrite( 0x28, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity : PacketPlayOutEntity +- addRewrite( 0x2F, ProtocolConstants.Direction.TO_CLIENT, true ); // Use bed : PacketPlayOutBed +- addRewrite( 0x31, ProtocolConstants.Direction.TO_CLIENT, true ); // Remove Entity Effect : PacketPlayOutRemoveEntityEffect +- addRewrite( 0x34, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Head Look : PacketPlayOutEntityHeadRotation +- addRewrite( 0x36, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera : PacketPlayOutCamera +- addRewrite( 0x39, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Metadata : PacketPlayOutEntityMetadata +- addRewrite( 0x3A, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity : PacketPlayOutAttachEntity +- addRewrite( 0x3B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Velocity : PacketPlayOutEntityVelocity +- addRewrite( 0x3C, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment : PacketPlayOutEntityEquipment +- addRewrite( 0x40, ProtocolConstants.Direction.TO_CLIENT, true ); // Attach Entity : PacketPlayOutMount +- addRewrite( 0x49, ProtocolConstants.Direction.TO_CLIENT, true ); // Collect Item : PacketPlayOutCollect +- addRewrite( 0x4A, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Teleport : PacketPlayOutEntityTeleport +- addRewrite( 0x4B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Properties : PacketPlayOutUpdateAttributes +- addRewrite( 0x4C, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Effect : PacketPlayOutEntityEffect +- +- addRewrite( 0x0A, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity : PacketPlayInUseEntity +- addRewrite( 0x14, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action : PacketPlayInEntityAction +- } +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- int jumpIndex = packet.readerIndex(); +- switch ( packetId ) +- { +- case 0x3A /* Attach Entity : PacketPlayOutAttachEntity */: +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- break; +- case 0x49 /* Collect Item : PacketPlayOutCollect */: +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- case 0x40 /* Attach Entity : PacketPlayOutMount */: +- DefinedPacket.readVarInt( packet ); +- jumpIndex = packet.readerIndex(); +- // Fall through on purpose to int array of IDs +- case 0x30 /* Destroy Entities : PacketPlayOutEntityDestroy */: +- int count = DefinedPacket.readVarInt( packet ); +- int[] ids = new int[ count ]; +- for ( int i = 0; i < count; i++ ) +- { +- ids[i] = DefinedPacket.readVarInt( packet ); +- } +- packet.readerIndex( jumpIndex ); +- packet.writerIndex( jumpIndex ); +- DefinedPacket.writeVarInt( count, packet ); +- for ( int id : ids ) +- { +- if ( id == oldId ) +- { +- id = newId; +- } else if ( id == newId ) +- { +- id = oldId; +- } +- DefinedPacket.writeVarInt( id, packet ); +- } +- break; +- case 0x00 /* Spawn Object : PacketPlayOutSpawnEntity */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readUUID( packet ); +- int type = packet.readUnsignedByte(); +- +- if ( type == 60 || type == 90 || type == 91 ) +- { +- if ( type == 60 || type == 91 ) +- { +- oldId = oldId + 1; +- newId = newId + 1; +- } +- +- packet.skipBytes( 26 ); // double, double, double, byte, byte +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- if ( readId == oldId ) +- { +- packet.setInt( position, newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( position, oldId ); +- } +- } +- break; +- case 0x05 /* Spawn Player : PacketPlayOutNamedEntitySpawn */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- break; +- case 0x2C /* Combat Event : PacketPlayOutCombatEvent */: +- int event = packet.readUnsignedByte(); +- if ( event == 1 /* End Combat*/ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( event == 2 /* Entity Dead */ ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- packet.readerIndex( position ); +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } +- break; +- case 0x39 /* EntityMetadata : PacketPlayOutEntityMetadata */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- rewriteMetaVarInt( packet, oldId + 1, newId + 1, 5 ); // fishing hook +- rewriteMetaVarInt( packet, oldId, newId, 12 ); // guardian beam +- break; +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteServerbound( packet, oldId, newId ); +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == 0x1B /* Spectate : PacketPlayInSpectate */ && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +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 +deleted file mode 100644 +index bfbc8432..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_9_4.java ++++ /dev/null +@@ -1,182 +0,0 @@ +-package net.md_5.bungee.entitymap; +- +-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +-import io.netty.buffer.ByteBuf; +-import java.util.UUID; +-import net.md_5.bungee.BungeeCord; +-import net.md_5.bungee.UserConnection; +-import net.md_5.bungee.api.connection.ProxiedPlayer; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.ProtocolConstants; +- +-class EntityMap_1_9_4 extends EntityMap +-{ +- +- static final EntityMap_1_9_4 INSTANCE = new EntityMap_1_9_4(); +- +- EntityMap_1_9_4() +- { +- addRewrite( 0x00, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object : PacketPlayOutSpawnEntity +- addRewrite( 0x01, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb : PacketPlayOutSpawnEntityExperienceOrb +- addRewrite( 0x03, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob : PacketPlayOutSpawnEntityLiving +- addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting : PacketPlayOutSpawnEntityPainting +- addRewrite( 0x05, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player : PacketPlayOutNamedEntitySpawn +- addRewrite( 0x06, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation : PacketPlayOutAnimation +- addRewrite( 0x08, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation : PacketPlayOutBlockBreakAnimation +- addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status : PacketPlayOutEntityStatus +- addRewrite( 0x25, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Relative Move : PacketPlayOutRelEntityMove +- addRewrite( 0x26, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look and Relative Move : PacketPlayOutRelEntityMoveLook +- addRewrite( 0x27, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Look : PacketPlayOutEntityLook +- addRewrite( 0x28, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity : PacketPlayOutEntity +- addRewrite( 0x2F, ProtocolConstants.Direction.TO_CLIENT, true ); // Use bed : PacketPlayOutBed +- addRewrite( 0x31, ProtocolConstants.Direction.TO_CLIENT, true ); // Remove Entity Effect : PacketPlayOutRemoveEntityEffect +- addRewrite( 0x34, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Head Look : PacketPlayOutEntityHeadRotation +- addRewrite( 0x36, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera : PacketPlayOutCamera +- addRewrite( 0x39, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Metadata : PacketPlayOutEntityMetadata +- addRewrite( 0x3A, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity : PacketPlayOutAttachEntity +- addRewrite( 0x3B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Velocity : PacketPlayOutEntityVelocity +- addRewrite( 0x3C, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment : PacketPlayOutEntityEquipment +- addRewrite( 0x40, ProtocolConstants.Direction.TO_CLIENT, true ); // Attach Entity : PacketPlayOutMount +- addRewrite( 0x48, ProtocolConstants.Direction.TO_CLIENT, true ); // Collect Item : PacketPlayOutCollect +- addRewrite( 0x49, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Teleport : PacketPlayOutEntityTeleport +- addRewrite( 0x4A, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Properties : PacketPlayOutUpdateAttributes +- addRewrite( 0x4B, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Effect : PacketPlayOutEntityEffect +- +- addRewrite( 0x0A, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity : PacketPlayInUseEntity +- addRewrite( 0x14, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action : PacketPlayInEntityAction +- } +- +- @Override +- @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteClientbound( packet, oldId, newId ); +- +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- int jumpIndex = packet.readerIndex(); +- switch ( packetId ) +- { +- case 0x3A /* Attach Entity : PacketPlayOutAttachEntity */: +- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); +- break; +- case 0x48 /* Collect Item : PacketPlayOutCollect */: +- DefinedPacket.readVarInt( packet ); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- break; +- case 0x40 /* Attach Entity : PacketPlayOutMount */: +- DefinedPacket.readVarInt( packet ); +- jumpIndex = packet.readerIndex(); +- // Fall through on purpose to int array of IDs +- case 0x30 /* Destroy Entities : PacketPlayOutEntityDestroy */: +- int count = DefinedPacket.readVarInt( packet ); +- int[] ids = new int[ count ]; +- for ( int i = 0; i < count; i++ ) +- { +- ids[i] = DefinedPacket.readVarInt( packet ); +- } +- packet.readerIndex( jumpIndex ); +- packet.writerIndex( jumpIndex ); +- DefinedPacket.writeVarInt( count, packet ); +- for ( int id : ids ) +- { +- if ( id == oldId ) +- { +- id = newId; +- } else if ( id == newId ) +- { +- id = oldId; +- } +- DefinedPacket.writeVarInt( id, packet ); +- } +- break; +- case 0x00 /* Spawn Object : PacketPlayOutSpawnEntity */: +- DefinedPacket.readVarInt( packet ); +- DefinedPacket.readUUID( packet ); +- int type = packet.readUnsignedByte(); +- +- if ( type == 60 || type == 90 || type == 91 ) +- { +- if ( type == 60 || type == 91 ) +- { +- oldId = oldId + 1; +- newId = newId + 1; +- } +- +- packet.skipBytes( 26 ); // double, double, double, byte, byte +- int position = packet.readerIndex(); +- int readId = packet.readInt(); +- if ( readId == oldId ) +- { +- packet.setInt( position, newId ); +- } else if ( readId == newId ) +- { +- packet.setInt( position, oldId ); +- } +- } +- break; +- case 0x05 /* Spawn Player : PacketPlayOutNamedEntitySpawn */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- int idLength = packet.readerIndex() - readerIndex - packetIdLength; +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength + idLength ); +- DefinedPacket.writeUUID( player.getUniqueId(), packet ); +- packet.writerIndex( previous ); +- } +- break; +- case 0x2C /* Combat Event : PacketPlayOutCombatEvent */: +- int event = packet.readUnsignedByte(); +- if ( event == 1 /* End Combat*/ ) +- { +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } else if ( event == 2 /* Entity Dead */ ) +- { +- int position = packet.readerIndex(); +- rewriteVarInt( packet, oldId, newId, packet.readerIndex() ); +- packet.readerIndex( position ); +- DefinedPacket.readVarInt( packet ); +- rewriteInt( packet, oldId, newId, packet.readerIndex() ); +- } +- break; +- case 0x39 /* EntityMetadata : PacketPlayOutEntityMetadata */: +- DefinedPacket.readVarInt( packet ); // Entity ID +- rewriteMetaVarInt( packet, oldId + 1, newId + 1, 5 ); // fishing hook +- rewriteMetaVarInt( packet, oldId, newId, 12 ); // guardian beam +- break; +- } +- packet.readerIndex( readerIndex ); +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) +- { +- super.rewriteServerbound( packet, oldId, newId ); +- // Special cases +- int readerIndex = packet.readerIndex(); +- int packetId = DefinedPacket.readVarInt( packet ); +- int packetIdLength = packet.readerIndex() - readerIndex; +- +- if ( packetId == 0x1B /* Spectate : PacketPlayInSpectate */ && !BungeeCord.getInstance().getConfig().isIpForward() ) +- { +- UUID uuid = DefinedPacket.readUUID( packet ); +- ProxiedPlayer player; +- if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) +- { +- int previous = packet.writerIndex(); +- packet.readerIndex( readerIndex ); +- packet.writerIndex( readerIndex + packetIdLength ); +- DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); +- packet.writerIndex( previous ); +- } +- } +- packet.readerIndex( readerIndex ); +- } +-} +diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_Dummy.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_Dummy.java +deleted file mode 100644 +index cb81d1dd..00000000 +--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_Dummy.java ++++ /dev/null +@@ -1,30 +0,0 @@ +- +-package net.md_5.bungee.entitymap; +- +-import io.netty.buffer.ByteBuf; +-// Waterfall start +- +-public class EntityMap_Dummy extends EntityMap { +- +- public static final EntityMap_Dummy INSTANCE = new EntityMap_Dummy(); +- +- EntityMap_Dummy() { +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId) { +- } +- +- @Override +- public void rewriteServerbound(ByteBuf packet, int oldId, int newId, int protocolVersion) { +- } +- +- @Override +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId) { +- } +- +- @Override +- public void rewriteClientbound(ByteBuf packet, int oldId, int newId, int protocolVersion) { +- } +-} +-// Waterfall end +\ No newline at end of file +diff --git a/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java b/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java +index caed4384..af428090 100644 +--- a/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java +@@ -11,7 +11,6 @@ import lombok.Setter; + import net.md_5.bungee.BungeeCord; + import net.md_5.bungee.UserConnection; + import net.md_5.bungee.protocol.ProtocolConstants; +-import net.md_5.bungee.protocol.packet.EntityRemoveEffect; + import net.md_5.bungee.protocol.packet.PluginMessage; + + /** +@@ -113,10 +112,6 @@ public class ForgeClientHandler + } + + private void resetAllThePotions(UserConnection con) { +- // Just to be sure +- for (Map.Entry entry: con.getPotions().entries()) { +- con.unsafe().sendPacket(new EntityRemoveEffect(entry.getKey(), entry.getValue())); +- } + con.getPotions().clear(); + } + +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0031-Dont-frame-unreadable-connections.patch b/Waterfall-Proxy-Patches/0031-Dont-frame-unreadable-connections.patch new file mode 100644 index 0000000..cb483b3 --- /dev/null +++ b/Waterfall-Proxy-Patches/0031-Dont-frame-unreadable-connections.patch @@ -0,0 +1,22 @@ +From c3a0de08b9d4d728fe488f5d3b83e19e2766f6ae Mon Sep 17 00:00:00 2001 +From: xIsm4 +Date: Thu, 4 Aug 2022 23:27:43 +0200 +Subject: [PATCH] Dont frame unreadable connections + + +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 +index aa538f74..99d203c7 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java +@@ -33,7 +33,7 @@ public class MinecraftDecoder extends MessageToMessageDecoder + { + // See Varint21FrameDecoder for the general reasoning. We add this here as ByteToMessageDecoder#handlerRemoved() + // will fire any cumulated data through the pipeline, so we want to try and stop it here. +- if ( !ctx.channel().isActive() ) ++ if ( !ctx.channel().isActive() || !in.isReadable() ) // FlameCord - Check if connection is readable + { + return; + } +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0032-Avoid-throwing-IOOB-on-login.patch b/Waterfall-Proxy-Patches/0032-Avoid-throwing-IOOB-on-login.patch new file mode 100644 index 0000000..68aa831 --- /dev/null +++ b/Waterfall-Proxy-Patches/0032-Avoid-throwing-IOOB-on-login.patch @@ -0,0 +1,23 @@ +From 8a2c6364c6c2e36eaa918195ca370382a37debee Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Tue, 13 Sep 2022 20:41:48 -0300 +Subject: [PATCH] Avoid throwing IOOB on login + + +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java +index 763d34c2..57e50a8b 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java +@@ -32,7 +32,8 @@ public class LoginRequest extends DefinedPacket + } + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19_1 ) + { +- if ( buf.readBoolean() ) ++ // FlameCord - Avoid throwing IOOB on login ++ if ( buf.isReadable() && buf.readBoolean() ) + { + uuid = readUUID( buf ); + } +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0033-Only-show-connection-fail-reason-when-detailed-logge.patch b/Waterfall-Proxy-Patches/0033-Only-show-connection-fail-reason-when-detailed-logge.patch new file mode 100644 index 0000000..6df0d6c --- /dev/null +++ b/Waterfall-Proxy-Patches/0033-Only-show-connection-fail-reason-when-detailed-logge.patch @@ -0,0 +1,26 @@ +From 981d25c1f3b03e1c2f10427811c7690c64c2e967 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Tue, 15 Nov 2022 18:07:27 +0100 +Subject: [PATCH] Only show connection fail reason when detailed logger is + enabled + + +diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +index f3d60253..021fe839 100644 +--- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java ++++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java +@@ -405,8 +405,10 @@ public final class UserConnection implements ProxiedPlayer + // FlameCord - Allow for toggle the logging of connection failures + if(FlameCord.getInstance().getFlameCordConfiguration().isLoggerDetailedConnection()) { + bungee.getLogger().log(Level.WARNING, "Error occurred processing connection for " + this.name + " " + Util.exception(cause, false)); // Waterfall ++ // FlameCord - Show connection fail reason ++ return groups.contains( "admin" ) ? cause.getMessage() : cause.getClass().getName(); + } +- return ""; // Waterfall ++ return cause.getClass().getName(); // FlameCord + } + + @Override +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0034-Optimize-Decoder-Encoder-Getter.patch b/Waterfall-Proxy-Patches/0034-Optimize-Decoder-Encoder-Getter.patch new file mode 100644 index 0000000..e4bd12b --- /dev/null +++ b/Waterfall-Proxy-Patches/0034-Optimize-Decoder-Encoder-Getter.patch @@ -0,0 +1,60 @@ +From 69f3e68d5a12bcaf0423d2c18da6b51a8d38d998 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Wed, 28 Dec 2022 14:23:54 -0300 +Subject: [PATCH] Optimize Decoder/Encoder Getter + + +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 +index 6f1d8336..b98ee6a7 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +@@ -31,26 +31,42 @@ public class ChannelWrapper + @Getter + private volatile boolean closing; + ++ // FlameCord start - Optimize Decoder/Encoder Getter ++ MinecraftDecoder decoder; ++ MinecraftEncoder encoder; ++ // FlameCord end - Optimize Decoder/Encoder Getter ++ + public ChannelWrapper(ChannelHandlerContext ctx) + { + this.ch = ctx.channel(); + this.remoteAddress = ( this.ch.remoteAddress() == null ) ? this.ch.parent().localAddress() : this.ch.remoteAddress(); ++ ++ // FlameCord start - Optimize Decoder/Encoder Getter ++ this.decoder = (MinecraftDecoder) ch.pipeline().get( PipelineUtils.PACKET_DECODER ); ++ this.encoder = (MinecraftEncoder) ch.pipeline().get( PipelineUtils.PACKET_ENCODER ); ++ // FlameCord end - Optimize Decoder/Encoder Getter + } + + public void setProtocol(Protocol protocol) + { + // FlameCord - Use pipeline to reduce redundancy + final ChannelPipeline pipeline = ch.pipeline(); +- pipeline.get( MinecraftDecoder.class ).setProtocol( protocol ); +- pipeline.get( MinecraftEncoder.class ).setProtocol( protocol ); ++ ++ // FlameCord start - Optimize Decoder/Encoder Getter ++ decoder.setProtocol( protocol ); ++ encoder.setProtocol( protocol ); ++ // FlameCord end - Optimize Decoder/Encoder Getter + } + + public void setVersion(int protocol) + { + // FlameCord - Use pipeline to reduce redundancy + final ChannelPipeline pipeline = ch.pipeline(); +- pipeline.get( MinecraftDecoder.class ).setProtocolVersion( protocol ); +- pipeline.get( MinecraftEncoder.class ).setProtocolVersion( protocol ); ++ ++ // FlameCord start - Optimize Decoder/Encoder Getter ++ decoder.setProtocolVersion( protocol ); ++ encoder.setProtocolVersion( protocol ); ++ // FlameCord end - Optimize Decoder/Encoder Getter + } + + public void write(Object packet) +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0035-Dont-Process-Links-For-Kick-Messages.patch b/Waterfall-Proxy-Patches/0035-Dont-Process-Links-For-Kick-Messages.patch new file mode 100644 index 0000000..ef5d401 --- /dev/null +++ b/Waterfall-Proxy-Patches/0035-Dont-Process-Links-For-Kick-Messages.patch @@ -0,0 +1,134 @@ +From ab1e5d100f2d678f1471fa83226dc9a5af2d370b Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Tue, 10 Jan 2023 16:37:13 -0300 +Subject: [PATCH] Dont Process Links For Kick Messages + + +diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java +index 4371374a..2bb27e67 100644 +--- a/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java ++++ b/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java +@@ -160,6 +160,107 @@ public final class TextComponent extends BaseComponent + return components.toArray( new BaseComponent[ 0 ] ); + } + ++ // FlameCord start - Dont Process Links For Kick Messages ++ public static BaseComponent[] fromLegacyTextFast(String message) ++ { ++ return fromLegacyTextFast( message, ChatColor.WHITE ); ++ } ++ ++ public static BaseComponent[] fromLegacyTextFast(String message, ChatColor defaultColor) ++ { ++ ArrayList components = new ArrayList<>(); ++ StringBuilder builder = new StringBuilder(); ++ TextComponent component = new TextComponent(); ++ ++ for ( int i = 0; i < message.length(); i++ ) ++ { ++ char c = message.charAt( i ); ++ if ( c == ChatColor.COLOR_CHAR ) ++ { ++ if ( ++i >= message.length() ) ++ { ++ break; ++ } ++ c = message.charAt( i ); ++ if ( c >= 'A' && c <= 'Z' ) ++ { ++ c += 32; ++ } ++ ChatColor format; ++ if ( c == 'x' && i + 12 < message.length() ) ++ { ++ StringBuilder hex = new StringBuilder( "#" ); ++ for ( int j = 0; j < 6; j++ ) ++ { ++ hex.append( message.charAt( i + 2 + ( j * 2 ) ) ); ++ } ++ try ++ { ++ format = ChatColor.of( hex.toString() ); ++ } catch ( IllegalArgumentException ex ) ++ { ++ format = null; ++ } ++ ++ i += 12; ++ } else ++ { ++ format = ChatColor.getByChar( c ); ++ } ++ if ( format == null ) ++ { ++ continue; ++ } ++ if ( builder.length() > 0 ) ++ { ++ TextComponent old = component; ++ component = new TextComponent( old ); ++ old.setText( builder.toString() ); ++ builder = new StringBuilder(); ++ components.add( old ); ++ } ++ if ( format == ChatColor.BOLD ) ++ { ++ component.setBold( true ); ++ } else if ( format == ChatColor.ITALIC ) ++ { ++ component.setItalic( true ); ++ } else if ( format == ChatColor.UNDERLINE ) ++ { ++ component.setUnderlined( true ); ++ } else if ( format == ChatColor.STRIKETHROUGH ) ++ { ++ component.setStrikethrough( true ); ++ } else if ( format == ChatColor.MAGIC ) ++ { ++ component.setObfuscated( true ); ++ } else ++ { ++ if ( format == ChatColor.RESET ) ++ { ++ format = defaultColor; ++ } ++ component = new TextComponent(); ++ component.setColor( format ); ++ component.setReset( true ); ++ } ++ continue; ++ } ++ int pos = message.indexOf( ' ', i ); ++ if ( pos == -1 ) ++ { ++ pos = message.length(); ++ } ++ builder.append( c ); ++ } ++ ++ component.setText( builder.toString() ); ++ components.add( component ); ++ ++ return components.toArray( new BaseComponent[ 0 ] ); ++ } ++ // FlameCord end - Dont Process Links For Kick Messages ++ + /** + * The text of the component that will be displayed to the client + */ +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 +index e8c14c55..6d0719a0 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -833,7 +833,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection + { + if ( canSendKickMessage() ) + { +- disconnect( TextComponent.fromLegacyText( reason ) ); ++ disconnect( TextComponent.fromLegacyTextFast( reason ) ); // FlameCord - Dont Process Links For Kick Messages + } else + { + ch.close(); +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0036-Bungee-Plugins-Command.patch b/Waterfall-Proxy-Patches/0036-Bungee-Plugins-Command.patch new file mode 100644 index 0000000..a40ebcc --- /dev/null +++ b/Waterfall-Proxy-Patches/0036-Bungee-Plugins-Command.patch @@ -0,0 +1,97 @@ +From f37bca815bffe22461c5b2760596ef18d56a1070 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Wed, 11 Jan 2023 10:14:12 -0300 +Subject: [PATCH] Bungee Plugins Command + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java +index 6175d8e2d..2ea33a94b 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java +@@ -96,6 +96,11 @@ public class MessagesConfiguration extends FlameConfig { + setIfUnexistant("flamecord_firewall_remove", "&cThe ip {0} was removed from the firewall!", configuration); + // FlameCord end - Antibot System + ++ // FlameCord - Bungee Plugins Command ++ setIfUnexistant("flamecord_bplugins_nopermission", "&cYou don't have permission to do this!", configuration); ++ setIfUnexistant("flamecord_bplugins_separator", ", ", configuration); ++ setIfUnexistant("flamecord_bplugins_header", "&aPlugins ({0}): ", configuration); ++ + for (final String key : configuration.getKeys()) { + final Object value = configuration.get(key); + +diff --git a/proxy/src/main/java/dev/_2lstudios/flamecord/commands/BungeePluginsCommand.java b/proxy/src/main/java/dev/_2lstudios/flamecord/commands/BungeePluginsCommand.java +new file mode 100644 +index 000000000..2e20e9dca +--- /dev/null ++++ b/proxy/src/main/java/dev/_2lstudios/flamecord/commands/BungeePluginsCommand.java +@@ -0,0 +1,45 @@ ++package dev._2lstudios.flamecord.commands; ++ ++import java.util.Collection; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.MessagesConfiguration; ++import net.md_5.bungee.BungeeCord; ++import net.md_5.bungee.api.CommandSender; ++import net.md_5.bungee.api.chat.TextComponent; ++import net.md_5.bungee.api.plugin.Command; ++import net.md_5.bungee.api.plugin.Plugin; ++ ++public class BungeePluginsCommand extends Command { ++ public BungeePluginsCommand() { ++ super("bplugins"); ++ } ++ ++ @Override ++ public void execute(final CommandSender sender, final String[] args) { ++ final FlameCord flameCord = FlameCord.getInstance(); ++ final MessagesConfiguration messagesConfiguration = flameCord.getMessagesConfiguration(); ++ ++ if (sender.hasPermission("flamecord.usage")) { ++ Collection plugins = BungeeCord.getInstance().getPluginManager().getPlugins(); ++ int amount = plugins.size(); ++ String header = FlameCord.getInstance().getMessagesConfiguration().getTranslation("flamecord_bplugins_header", amount); ++ String separator = FlameCord.getInstance().getMessagesConfiguration().getTranslation("flamecord_bplugins_separator"); ++ StringBuilder stringBuilder = new StringBuilder(header); ++ boolean first = true; ++ ++ for (Plugin plugin : plugins) { ++ stringBuilder.append((first ? "" : separator) + plugin.getDescription().getName()); ++ ++ if (first) { ++ first = false; ++ } ++ } ++ ++ sender.sendMessage(TextComponent.fromLegacyText(stringBuilder.toString())); ++ } else { ++ sender.sendMessage(TextComponent ++ .fromLegacyText(messagesConfiguration.getTranslation("flamecord_bplugins_nopermission"))); ++ } ++ } ++} +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +index 62712330e..ec5fd4af2 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +@@ -11,6 +11,7 @@ import com.google.gson.Gson; + import com.google.gson.GsonBuilder; + + import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.commands.BungeePluginsCommand; + import dev._2lstudios.flamecord.commands.FlameCordCommand; + import dev._2lstudios.flamecord.configuration.ModulesConfiguration; + import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +@@ -912,5 +913,8 @@ public class BungeeCord extends ProxyServer + + // Flamecord Command + pluginManager.registerCommand(null, new FlameCordCommand(this)); ++ ++ // FlameCord - Bungee Plugins Command ++ pluginManager.registerCommand(null, new BungeePluginsCommand()); + } + } +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0037-Bungee-IP-Command.patch b/Waterfall-Proxy-Patches/0037-Bungee-IP-Command.patch new file mode 100644 index 0000000..0bc48e2 --- /dev/null +++ b/Waterfall-Proxy-Patches/0037-Bungee-IP-Command.patch @@ -0,0 +1,98 @@ +From 79f33268f3f154170f55362decf8fcc8b99a68f8 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Sun, 15 Jan 2023 10:12:45 -0300 +Subject: [PATCH] Bungee IP Command + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java +index 2ea33a94b..e42bb29d5 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java +@@ -101,6 +101,12 @@ public class MessagesConfiguration extends FlameConfig { + setIfUnexistant("flamecord_bplugins_separator", ", ", configuration); + setIfUnexistant("flamecord_bplugins_header", "&aPlugins ({0}): ", configuration); + ++ // FlameCord - Bungee IP Command ++ setIfUnexistant("flamecord_bip_nopermission", "&cYou don't have permission to do this!", configuration); ++ setIfUnexistant("flamecord_bip_offline", "&cThe player is not online!", configuration); ++ setIfUnexistant("flamecord_bip_usage", "&c/bip ", configuration); ++ setIfUnexistant("flamecord_bip", "&aInformation about {0}&a:\n&aUUID: &b{1}\n&aIP: &b{2}\n&aPing: &b{3}ms\n&aLocale: &b{4}\n&aView Distance: &b{5}\n&aCurrent Server: &b{6}", configuration); ++ + for (final String key : configuration.getKeys()) { + final Object value = configuration.get(key); + +diff --git a/proxy/src/main/java/dev/_2lstudios/flamecord/commands/BungeeIPCommand.java b/proxy/src/main/java/dev/_2lstudios/flamecord/commands/BungeeIPCommand.java +new file mode 100644 +index 000000000..85313491e +--- /dev/null ++++ b/proxy/src/main/java/dev/_2lstudios/flamecord/commands/BungeeIPCommand.java +@@ -0,0 +1,45 @@ ++package dev._2lstudios.flamecord.commands; ++ ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.MessagesConfiguration; ++import net.md_5.bungee.BungeeCord; ++import net.md_5.bungee.api.CommandSender; ++import net.md_5.bungee.api.chat.TextComponent; ++import net.md_5.bungee.api.connection.ProxiedPlayer; ++import net.md_5.bungee.api.plugin.Command; ++ ++public class BungeeIPCommand extends Command { ++ public BungeeIPCommand() { ++ super("bip"); ++ } ++ ++ @Override ++ public void execute(final CommandSender sender, final String[] args) { ++ final FlameCord flameCord = FlameCord.getInstance(); ++ final MessagesConfiguration messagesConfiguration = flameCord.getMessagesConfiguration(); ++ ++ if (sender.hasPermission("flamecord.bip")) { ++ if (args.length > 0) { ++ ProxiedPlayer player = BungeeCord.getInstance().getPlayer(args[0]); ++ ++ if (player != null) { ++ String message = messagesConfiguration.getTranslation("flamecord_bip", player.getDisplayName(), ++ player.getUniqueId(), player.getSocketAddress(), player.getPing(), player.getLocale(), ++ player.getViewDistance(), player.getServer().getInfo().getName()); ++ ++ sender.sendMessage(TextComponent.fromLegacyText(message)); ++ } else { ++ sender.sendMessage( ++ TextComponent ++ .fromLegacyText(messagesConfiguration.getTranslation("flamecord_bip_offline"))); ++ } ++ } else { ++ sender.sendMessage( ++ TextComponent.fromLegacyText(messagesConfiguration.getTranslation("flamecord_bip_usage"))); ++ } ++ } else { ++ sender.sendMessage(TextComponent ++ .fromLegacyText(messagesConfiguration.getTranslation("flamecord_bip_nopermission"))); ++ } ++ } ++} +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +index ec5fd4af2..f5059666e 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +@@ -11,6 +11,7 @@ import com.google.gson.Gson; + import com.google.gson.GsonBuilder; + + import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.commands.BungeeIPCommand; + import dev._2lstudios.flamecord.commands.BungeePluginsCommand; + import dev._2lstudios.flamecord.commands.FlameCordCommand; + import dev._2lstudios.flamecord.configuration.ModulesConfiguration; +@@ -916,5 +917,8 @@ public class BungeeCord extends ProxyServer + + // FlameCord - Bungee Plugins Command + pluginManager.registerCommand(null, new BungeePluginsCommand()); ++ ++ // FlameCord - Bungee IP Command ++ pluginManager.registerCommand(null, new BungeeIPCommand()); + } + } +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0038-List-Command-Format.patch b/Waterfall-Proxy-Patches/0038-List-Command-Format.patch new file mode 100644 index 0000000..5abacdb --- /dev/null +++ b/Waterfall-Proxy-Patches/0038-List-Command-Format.patch @@ -0,0 +1,37 @@ +From daefdd3d718eecc9ac09fbece9e7baff22e1c195 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Sun, 15 Jan 2023 19:03:34 -0300 +Subject: [PATCH] List Command Format + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java +index e42bb29d5..555319a7f 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java +@@ -107,6 +107,9 @@ public class MessagesConfiguration extends FlameConfig { + setIfUnexistant("flamecord_bip_usage", "&c/bip ", configuration); + setIfUnexistant("flamecord_bip", "&aInformation about {0}&a:\n&aUUID: &b{1}\n&aIP: &b{2}\n&aPing: &b{3}ms\n&aLocale: &b{4}\n&aView Distance: &b{5}\n&aCurrent Server: &b{6}", configuration); + ++ // FlameCord - List Command Format ++ setIfUnexistant("command_list_format", "&aServers:&r", configuration); ++ + for (final String key : configuration.getKeys()) { + final Object value = configuration.get(key); + +diff --git a/module/cmd-list/src/main/java/net/md_5/bungee/module/cmd/list/CommandList.java b/module/cmd-list/src/main/java/net/md_5/bungee/module/cmd/list/CommandList.java +index c22271105..0a5899319 100644 +--- a/module/cmd-list/src/main/java/net/md_5/bungee/module/cmd/list/CommandList.java ++++ b/module/cmd-list/src/main/java/net/md_5/bungee/module/cmd/list/CommandList.java +@@ -25,6 +25,9 @@ public class CommandList extends Command + @Override + public void execute(CommandSender sender, String[] args) + { ++ // FlameCord - List Command Format ++ sender.sendMessage( ProxyServer.getInstance().getTranslation( "command_list_format") ); ++ + for ( ServerInfo server : ProxyServer.getInstance().getServers().values() ) + { + if ( !server.canAccess( sender ) ) +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0039-InitialHandler-Processing-State.patch b/Waterfall-Proxy-Patches/0039-InitialHandler-Processing-State.patch new file mode 100644 index 0000000..00c2732 --- /dev/null +++ b/Waterfall-Proxy-Patches/0039-InitialHandler-Processing-State.patch @@ -0,0 +1,124 @@ +From 477817bdc183b430c925b7b3841e5b51ac632852 Mon Sep 17 00:00:00 2001 +From: LinsaFTW <25271111+linsaftw@users.noreply.github.com> +Date: Sun, 22 Jan 2023 09:41:36 -0300 +Subject: [PATCH] InitialHandler Processing State + + +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 +index 6d0719a0..8beed72f 100644 +--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java ++++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +@@ -135,6 +135,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection + HANDSHAKE, STATUS, PING, USERNAME, ENCRYPT, FINISHING; + } + ++ // FlameCord start - InitialHandler Processing State ++ private boolean processing = false; ++ // FlameCord end - InitialHandler Processing State ++ + private boolean canSendKickMessage() + { + return thisState == State.USERNAME || thisState == State.ENCRYPT || thisState == State.FINISHING; +@@ -275,6 +279,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection + public void handle(StatusRequest statusRequest) throws Exception + { + Preconditions.checkState( thisState == State.STATUS, "Not expecting STATUS" ); ++ // FlameCord start - InitialHandler Processing State ++ Preconditions.checkState( !processing, "Cannot request STATUS while processing another packet"); ++ this.processing = true; ++ // FlameCord end - InitialHandler Processing State + + ServerInfo forced = AbstractReconnectHandler.getForcedHost( this ); + final int protocol = ( ProtocolConstants.SUPPORTED_VERSION_IDS.contains( handshake.getProtocolVersion() ) ) ? handshake.getProtocolVersion() : bungee.getProtocolVersion(); +@@ -392,6 +400,9 @@ public class InitialHandler extends PacketHandler implements PendingConnection + } + + thisState = State.PING; ++ // FlameCord start - InitialHandler Processing State ++ processing = false; ++ // FlameCord end - InitialHandler Processing State + } + + private static final boolean ACCEPT_INVALID_PACKETS = Boolean.parseBoolean(System.getProperty("waterfall.acceptInvalidPackets", "false")); +@@ -401,6 +412,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection + { + // FlameCord - Never accept invalid packets + Preconditions.checkState( thisState == State.PING, "Not expecting PING" ); ++ // FlameCord start - InitialHandler Processing State ++ Preconditions.checkState( !processing, "Cannot request PING while processing another packet"); ++ this.processing = true; ++ // FlameCord end - InitialHandler Processing State + + unsafe.sendPacket( ping ); + +@@ -412,7 +427,11 @@ public class InitialHandler extends PacketHandler implements PendingConnection + public void handle(Handshake handshake) throws Exception + { + Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" ); +- ++ // FlameCord start - InitialHandler Processing State ++ Preconditions.checkState( !processing, "Cannot request HANDSHAKE while processing another packet"); ++ this.processing = true; ++ // FlameCord end - InitialHandler Processing State ++ + // FlameCord start - Antibot System + // Close and firewall on invalid protocol + int protocol = handshake.getRequestedProtocol(); +@@ -485,6 +504,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection + } + // FlameCord end - Antibot System + ++ // FlameCord start - InitialHandler Processing State ++ processing = false; ++ // FlameCord end - InitialHandler Processing State ++ + break; + case 2: + // Login +@@ -517,6 +540,11 @@ public class InitialHandler extends PacketHandler implements PendingConnection + } + return; + } ++ ++ // FlameCord start - InitialHandler Processing State ++ processing = false; ++ // FlameCord end - InitialHandler Processing State ++ + break; + default: + throw new QuietException( "Cannot request protocol " + handshake.getRequestedProtocol() ); +@@ -527,6 +555,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection + public void handle(LoginRequest loginRequest) throws Exception + { + Preconditions.checkState( thisState == State.USERNAME, "Not expecting USERNAME" ); ++ // FlameCord start - InitialHandler Processing State ++ Preconditions.checkState( !processing, "Cannot request USERNAME while processing another packet"); ++ this.processing = true; ++ // FlameCord end - InitialHandler Processing State + + if ( !FlameCord.getInstance().getFlameCordConfiguration().isAllowInvalidNames() && !AllowedCharacters.isValidName( loginRequest.getData(), onlineMode ) ) + { +@@ -634,6 +666,9 @@ public class InitialHandler extends PacketHandler implements PendingConnection + thisState = State.FINISHING; + finish(); + } ++ // FlameCord start - InitialHandler Processing State ++ processing = false; ++ // FlameCord end - InitialHandler Processing State + } + }; + +@@ -647,6 +682,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection + Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" ); + Preconditions.checkState( EncryptionUtil.check( loginRequest.getPublicKey(), encryptResponse, request ), "Invalid verification" ); + thisState = State.FINISHING; // Waterfall - move earlier - There is no verification of this later (and this is not API) ++ // FlameCord start - InitialHandler Processing State ++ Preconditions.checkState( !processing, "Cannot request ENCRYPT while processing another packet"); ++ this.processing = true; ++ // FlameCord end - InitialHandler Processing State + + SecretKey sharedKey = EncryptionUtil.getSecret( encryptResponse, request ); + // Waterfall start +-- +2.37.3.windows.1 + diff --git a/Waterfall-Proxy-Patches/0040-Implement-libdeflate.patch b/Waterfall-Proxy-Patches/0040-Implement-libdeflate.patch new file mode 100644 index 0000000..5619559 --- /dev/null +++ b/Waterfall-Proxy-Patches/0040-Implement-libdeflate.patch @@ -0,0 +1,2623 @@ +From c2ca35b27ef725cf2ce710726aee0ea1efb84723 Mon Sep 17 00:00:00 2001 +From: xIsm4 +Date: Fri, 27 Jan 2023 14:12:44 +0100 +Subject: [PATCH] Implement libdeflate + + +diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +index 6f463b9c6..6ec047ed7 100644 +--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java ++++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java +@@ -59,6 +59,8 @@ public class FlameCordConfiguration extends FlameConfig { + @Getter + private int antibotFirewallExpire = 60; + @Getter ++ private int compressionLevel = 6; ++ @Getter + private boolean antibotFirewallLog = true; + @Getter + private boolean antibotFirewallIpset = true; +@@ -324,6 +326,9 @@ public class FlameCordConfiguration extends FlameConfig { + // FlameCord - Allow Invalid Names + this.allowInvalidNames = setIfUnexistant("allow-invalid-names", this.allowInvalidNames, configuration); + ++ // FlameCord - Implement libdeflate ++ this.compressionLevel = setIfUnexistant("compression-level", this.compressionLevel, configuration); ++ + save(configuration, configurationFile); + } + } +diff --git a/native/compile-linux.sh b/native/compile-linux.sh +new file mode 100644 +index 000000000..5442dce9d +--- /dev/null ++++ b/native/compile-linux.sh +@@ -0,0 +1,25 @@ ++#!/bin/bash ++ ++if [ ! "$CC" ]; then ++ # The libdeflate authors recommend that we build using GCC as it produces "slightly faster binaries": ++ # https://github.com/ebiggers/libdeflate#for-unix ++ export CC=gcc ++fi ++ ++if [ ! -d libdeflate ]; then ++ echo "Cloning libdeflate..." ++ git clone https://github.com/ebiggers/libdeflate.git ++fi ++ ++echo "Compiling libdeflate..." ++cd libdeflate || exit ++CFLAGS="-fPIC -O2 -fomit-frame-pointer" make ++cd .. ++ ++CFLAGS="-O2 -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ -fPIC -shared -Wl,-z,noexecstack -Wall -Werror -fomit-frame-pointer" ++ARCH=$(uname -m) ++mkdir -p src/main/resources/linux_$ARCH ++$CC $CFLAGS -Ilibdeflate src/main/c/jni_util.c src/main/c/jni_zlib_deflate.c src/main/c/jni_zlib_inflate.c \ ++ libdeflate/libdeflate.a -o src/main/resources/linux_$ARCH/velocity-compress.so ++$CC $CFLAGS -shared src/main/c/jni_util.c src/main/c/jni_cipher_openssl.c \ ++ -o src/main/resources/linux_$ARCH/velocity-cipher.so -lcrypto +\ No newline at end of file +diff --git a/native/src/main/c/jni_cipher_macos.c b/native/src/main/c/jni_cipher_macos.c +new file mode 100644 +index 000000000..aa7d1aba3 +--- /dev/null ++++ b/native/src/main/c/jni_cipher_macos.c +@@ -0,0 +1,66 @@ ++#include ++#include ++#include ++#include ++#include "jni_util.h" ++ ++typedef unsigned char byte; ++ ++JNIEXPORT jlong JNICALL ++Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_init(JNIEnv *env, ++ jclass clazz, ++ jbyteArray key, ++ jboolean encrypt) ++{ ++ jsize keyLen = (*env)->GetArrayLength(env, key); ++ if (keyLen != 16) { ++ throwException(env, "java/lang/IllegalArgumentException", "cipher not 16 bytes"); ++ return 0; ++ } ++ ++ // Since we know the array size is always bounded, we can just use GetArrayRegion ++ // and save ourselves some error-checking headaches. ++ jbyte keyBytes[16]; ++ (*env)->GetByteArrayRegion(env, key, 0, keyLen, (jbyte*) keyBytes); ++ if ((*env)->ExceptionCheck(env)) { ++ return 0; ++ } ++ ++ CCCryptorRef cryptor = NULL; ++ CCCryptorStatus result = CCCryptorCreateWithMode(encrypt ? kCCEncrypt : kCCDecrypt, ++ kCCModeCFB8, ++ kCCAlgorithmAES128, ++ ccNoPadding, ++ keyBytes, ++ keyBytes, ++ 16, ++ NULL, ++ 0, ++ 0, ++ 0, ++ &cryptor); ++ if (result != kCCSuccess) { ++ throwException(env, "java/security/GeneralSecurityException", "openssl initialize cipher"); ++ return 0; ++ } ++ return (jlong) cryptor; ++} ++ ++JNIEXPORT void JNICALL ++Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_free(JNIEnv *env, ++ jclass clazz, ++ jlong ptr) ++{ ++ CCCryptorRelease((CCCryptorRef) ptr); ++} ++ ++JNIEXPORT void JNICALL ++Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_process(JNIEnv *env, ++ jclass clazz, ++ jlong ptr, ++ jlong source, ++ jint len, ++ jlong dest) ++{ ++ CCCryptorUpdate((CCCryptorRef) ptr, (byte*) source, len, (byte*) dest, len, NULL); ++} +\ No newline at end of file +diff --git a/native/src/main/c/jni_cipher_openssl.c b/native/src/main/c/jni_cipher_openssl.c +new file mode 100644 +index 000000000..83515be52 +--- /dev/null ++++ b/native/src/main/c/jni_cipher_openssl.c +@@ -0,0 +1,62 @@ ++#include ++#include ++#include ++#include ++#include "jni_util.h" ++ ++typedef unsigned char byte; ++ ++JNIEXPORT jlong JNICALL ++Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_init(JNIEnv *env, ++ jclass clazz, ++ jbyteArray key, ++ jboolean encrypt) ++{ ++ jsize keyLen = (*env)->GetArrayLength(env, key); ++ if (keyLen != 16) { ++ throwException(env, "java/lang/IllegalArgumentException", "cipher not 16 bytes"); ++ return 0; ++ } ++ ++ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); ++ if (ctx == NULL) { ++ throwException(env, "java/lang/OutOfMemoryError", "allocate cipher"); ++ return 0; ++ } ++ ++ // Since we know the array size is always bounded, we can just use GetArrayRegion ++ // and save ourselves some error-checking headaches. ++ jbyte keyBytes[16]; ++ (*env)->GetByteArrayRegion(env, key, 0, keyLen, (jbyte*) keyBytes); ++ if ((*env)->ExceptionCheck(env)) { ++ return 0; ++ } ++ ++ int result = EVP_CipherInit(ctx, EVP_aes_128_cfb8(), (byte*) keyBytes, (byte*) keyBytes, ++ encrypt); ++ if (result != 1) { ++ EVP_CIPHER_CTX_free(ctx); ++ throwException(env, "java/security/GeneralSecurityException", "openssl initialize cipher"); ++ return 0; ++ } ++ return (jlong) ctx; ++} ++ ++JNIEXPORT void JNICALL ++Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_free(JNIEnv *env, ++ jclass clazz, ++ jlong ptr) ++{ ++ EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *) ptr); ++} ++ ++JNIEXPORT void JNICALL ++Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_process(JNIEnv *env, ++ jclass clazz, ++ jlong ptr, ++ jlong source, ++ jint len, ++ jlong dest) ++{ ++ EVP_CipherUpdate((EVP_CIPHER_CTX*) ptr, (byte*) dest, &len, (byte*) source, len); ++} +\ No newline at end of file +diff --git a/native/src/main/c/jni_util.c b/native/src/main/c/jni_util.c +new file mode 100644 +index 000000000..1e2b6bd8c +--- /dev/null ++++ b/native/src/main/c/jni_util.c +@@ -0,0 +1,12 @@ ++#include ++#include "jni_util.h" ++ ++void JNICALL ++throwException(JNIEnv *env, const char *type, const char *msg) ++{ ++ jclass klazz = (*env)->FindClass(env, type); ++ ++ if (klazz != 0) { ++ (*env)->ThrowNew(env, klazz, msg); ++ } ++} +\ No newline at end of file +diff --git a/native/src/main/c/jni_util.h b/native/src/main/c/jni_util.h +new file mode 100644 +index 000000000..8938b26c8 +--- /dev/null ++++ b/native/src/main/c/jni_util.h +@@ -0,0 +1,4 @@ ++#include ++ ++JNIEXPORT void JNICALL ++throwException(JNIEnv *env, const char *type, const char *msg); +\ No newline at end of file +diff --git a/native/src/main/c/jni_zlib_deflate.c b/native/src/main/c/jni_zlib_deflate.c +new file mode 100644 +index 000000000..809a7f857 +--- /dev/null ++++ b/native/src/main/c/jni_zlib_deflate.c +@@ -0,0 +1,43 @@ ++#include ++#include ++#include ++#include ++#include ++#include "jni_util.h" ++ ++JNIEXPORT jlong JNICALL ++Java_com_velocitypowered_natives_compression_NativeZlibDeflate_init(JNIEnv *env, ++ jclass clazz, ++ jint level) ++{ ++ struct libdeflate_compressor *compressor = libdeflate_alloc_compressor(level); ++ if (compressor == NULL) { ++ // Out of memory! ++ throwException(env, "java/lang/OutOfMemoryError", "libdeflate allocate compressor"); ++ return 0; ++ } ++ return (jlong) compressor; ++} ++ ++JNIEXPORT void JNICALL ++Java_com_velocitypowered_natives_compression_NativeZlibDeflate_free(JNIEnv *env, ++ jclass clazz, ++ jlong ctx) ++{ ++ libdeflate_free_compressor((struct libdeflate_compressor *) ctx); ++} ++ ++JNIEXPORT jlong JNICALL ++Java_com_velocitypowered_natives_compression_NativeZlibDeflate_process(JNIEnv *env, ++ jclass clazz, ++ jlong ctx, ++ jlong sourceAddress, ++ jint sourceLength, ++ jlong destinationAddress, ++ jint destinationLength) ++{ ++ struct libdeflate_compressor *compressor = (struct libdeflate_compressor *) ctx; ++ size_t produced = libdeflate_zlib_compress(compressor, (void *) sourceAddress, sourceLength, ++ (void *) destinationAddress, destinationLength); ++ return (jlong) produced; ++} +\ No newline at end of file +diff --git a/native/src/main/c/jni_zlib_inflate.c b/native/src/main/c/jni_zlib_inflate.c +new file mode 100644 +index 000000000..d91319089 +--- /dev/null ++++ b/native/src/main/c/jni_zlib_inflate.c +@@ -0,0 +1,61 @@ ++#include ++#include ++#include ++#include ++#include ++#include "jni_util.h" ++ ++JNIEXPORT jlong JNICALL ++Java_com_velocitypowered_natives_compression_NativeZlibInflate_init(JNIEnv *env, ++ jclass clazz) ++{ ++ struct libdeflate_decompressor *decompress = libdeflate_alloc_decompressor(); ++ if (decompress == NULL) { ++ // Out of memory! ++ throwException(env, "java/lang/OutOfMemoryError", "libdeflate allocate decompressor"); ++ return 0; ++ } ++ ++ return (jlong) decompress; ++} ++ ++JNIEXPORT void JNICALL ++Java_com_velocitypowered_natives_compression_NativeZlibInflate_free(JNIEnv *env, ++ jclass clazz, ++ jlong ctx) ++{ ++ libdeflate_free_decompressor((struct libdeflate_decompressor *) ctx); ++} ++ ++JNIEXPORT jboolean JNICALL ++Java_com_velocitypowered_natives_compression_NativeZlibInflate_process(JNIEnv *env, ++ jclass clazz, ++ jlong ctx, ++ jlong sourceAddress, ++ jint sourceLength, ++ jlong destinationAddress, ++ jint destinationLength, ++ jlong maximumSize) ++{ ++ struct libdeflate_decompressor *decompress = (struct libdeflate_decompressor *) ctx; ++ enum libdeflate_result result = libdeflate_zlib_decompress(decompress, (void *) sourceAddress, ++ sourceLength, (void *) destinationAddress, destinationLength, NULL); ++ ++ switch (result) { ++ case LIBDEFLATE_SUCCESS: ++ // We are happy ++ return JNI_TRUE; ++ case LIBDEFLATE_BAD_DATA: ++ throwException(env, "java/util/zip/DataFormatException", "inflate data is bad"); ++ return JNI_FALSE; ++ case LIBDEFLATE_SHORT_OUTPUT: ++ case LIBDEFLATE_INSUFFICIENT_SPACE: ++ // These cases are the same for us. We expect the full uncompressed size to be known. ++ throwException(env, "java/util/zip/DataFormatException", "uncompressed size is inaccurate"); ++ return JNI_FALSE; ++ default: ++ // Unhandled case ++ throwException(env, "java/util/zip/DataFormatException", "unknown libdeflate return code"); ++ return JNI_FALSE; ++ } ++} +\ No newline at end of file +diff --git a/native/src/main/java/com/velocitypowered/natives/compression/NativeZlibDeflate.java b/native/src/main/java/com/velocitypowered/natives/compression/NativeZlibDeflate.java +new file mode 100644 +index 000000000..4ba41fc11 +--- /dev/null ++++ b/native/src/main/java/com/velocitypowered/natives/compression/NativeZlibDeflate.java +@@ -0,0 +1,15 @@ ++package com.velocitypowered.natives.compression; ++ ++/** ++ * Represents a native interface for zlib's deflate functions. ++ */ ++ ++//TODO Need refactor and recompile native! ++public class NativeZlibDeflate { ++ ++ public static native long init(int level); ++ ++ public static native long free(long ctx); ++ ++ public static native int process(long ctx, long sourceAddress, int sourceLength, long destinationAddress, int destinationLength); ++} +diff --git a/native/src/main/java/com/velocitypowered/natives/compression/NativeZlibInflate.java b/native/src/main/java/com/velocitypowered/natives/compression/NativeZlibInflate.java +new file mode 100644 +index 000000000..81d92e75b +--- /dev/null ++++ b/native/src/main/java/com/velocitypowered/natives/compression/NativeZlibInflate.java +@@ -0,0 +1,17 @@ ++package com.velocitypowered.natives.compression; ++ ++import java.util.zip.DataFormatException; ++ ++/** ++ * Represents a native interface for zlib's inflate functions. ++ */ ++ ++//TODO Need refactor and recompile native! ++public class NativeZlibInflate { ++ ++ public static native long init(); ++ ++ public static native long free(long ctx); ++ ++ public static native boolean process(long ctx, long sourceAddress, int sourceLength, long destinationAddress, int destinationLength) throws DataFormatException; ++} +diff --git a/native/src/main/java/dev/_2lstudios/flamecord/natives/MoreByteBufUtils.java b/native/src/main/java/dev/_2lstudios/flamecord/natives/MoreByteBufUtils.java +new file mode 100644 +index 000000000..a16a25844 +--- /dev/null ++++ b/native/src/main/java/dev/_2lstudios/flamecord/natives/MoreByteBufUtils.java +@@ -0,0 +1,28 @@ ++package dev._2lstudios.flamecord.natives; ++ ++import dev._2lstudios.flamecord.natives.compress.Compressor; ++import io.netty.buffer.ByteBuf; ++import io.netty.buffer.ByteBufAllocator; ++ ++public class MoreByteBufUtils { ++ ++ public static ByteBuf ensureCompatible(ByteBufAllocator alloc, Compressor nativeStuff, ByteBuf buf) { ++ if (isCompatible(nativeStuff, buf)) { ++ return buf.retain(); ++ } ++ ++ // It's not, so we must make a direct copy. ++ ByteBuf newBuf = alloc.directBuffer(buf.readableBytes()); ++ newBuf.writeBytes(buf); ++ return newBuf; ++ } ++ ++ private static boolean isCompatible(Compressor nativeStuff, ByteBuf buf) { ++ if (nativeStuff.isNeedDirectBuffer()) { ++ return buf.hasMemoryAddress(); ++ } ++ ++ return true; ++ } ++ ++} +\ No newline at end of file +diff --git a/native/src/main/java/dev/_2lstudios/flamecord/natives/NativeEnvironmentDetector.java b/native/src/main/java/dev/_2lstudios/flamecord/natives/NativeEnvironmentDetector.java +new file mode 100644 +index 000000000..50f042bdf +--- /dev/null ++++ b/native/src/main/java/dev/_2lstudios/flamecord/natives/NativeEnvironmentDetector.java +@@ -0,0 +1,36 @@ ++package dev._2lstudios.flamecord.natives; ++ ++import io.netty.buffer.ByteBuf; ++import io.netty.buffer.Unpooled; ++ ++public final class NativeEnvironmentDetector { ++ ++ private static final boolean IS_AMD64; ++ private static final boolean IS_AARCH64; ++ private static final boolean CAN_GET_MEMORYADDRESS; ++ ++ static { ++ ByteBuf test = Unpooled.directBuffer(); ++ try { ++ CAN_GET_MEMORYADDRESS = test.hasMemoryAddress(); ++ } ++ finally { ++ test.release(); ++ } ++ ++ String osArch = System.getProperty("os.arch", ""); ++ // HotSpot on Intel macOS prefers x86_64, but OpenJ9 on macOS and HotSpot/OpenJ9 elsewhere ++ // give amd64. ++ IS_AMD64 = osArch.equals("amd64") || osArch.equals("x86_64"); ++ IS_AARCH64 = osArch.equals("aarch64") || osArch.equals("arm64"); ++ } ++ ++ public static boolean isLinux_X86_64() { ++ return CAN_GET_MEMORYADDRESS && System.getProperty("os.name", "").equalsIgnoreCase("Linux") && IS_AMD64; ++ } ++ ++ public static boolean isLinux_AARCH64() { ++ return CAN_GET_MEMORYADDRESS && System.getProperty("os.name", "").equalsIgnoreCase("Linux") && IS_AARCH64; ++ } ++ ++} +\ No newline at end of file +diff --git a/native/src/main/java/dev/_2lstudios/flamecord/natives/Natives.java b/native/src/main/java/dev/_2lstudios/flamecord/natives/Natives.java +new file mode 100644 +index 000000000..a6709a869 +--- /dev/null ++++ b/native/src/main/java/dev/_2lstudios/flamecord/natives/Natives.java +@@ -0,0 +1,138 @@ ++package dev._2lstudios.flamecord.natives; ++ ++import dev._2lstudios.flamecord.natives.compress.Compressor; ++import dev._2lstudios.flamecord.natives.compress.CompressorFactory; ++import dev._2lstudios.flamecord.natives.compress.JavaCompressor; ++import dev._2lstudios.flamecord.natives.compress.LibdeflateCompressor; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.nio.file.Files; ++import java.nio.file.Path; ++import java.nio.file.StandardCopyOption; ++import java.util.Arrays; ++import java.util.List; ++ ++public class Natives { ++ ++ private static final CompressorFactory COMPRESSOR_FACTORY = loadAvailableCompressFactory(); ++ ++ public static CompressorFactory getCompressorFactory() { ++ return COMPRESSOR_FACTORY; ++ } ++ ++ public static List getAvailableCompressorFactories() { ++ return Arrays.asList(new CompressorFactory() { ++ @Override ++ public String getName() { ++ return "Libdeflate (linux_x86_64)"; ++ } ++ ++ @Override ++ public boolean isCorrectEnvironment() { ++ return NativeEnvironmentDetector.isLinux_X86_64(); ++ } ++ ++ @Override ++ public String getNativePath() { ++ return "/libdeflate_x86_64.so"; ++ } ++ ++ @Override ++ public Compressor create(int level) { ++ return new LibdeflateCompressor(level); ++ } ++ }, new CompressorFactory() { ++ @Override ++ public String getName() { ++ return "Libdeflate (linux_aarch64)"; ++ } ++ ++ @Override ++ public boolean isCorrectEnvironment() { ++ return NativeEnvironmentDetector.isLinux_AARCH64(); ++ } ++ ++ @Override ++ public String getNativePath() { ++ return "/libdeflate_aarch64.so"; ++ } ++ ++ @Override ++ public Compressor create(int level) { ++ return new LibdeflateCompressor(level); ++ } ++ }, new CompressorFactory() { ++ @Override ++ public String getName() { ++ return "Java"; ++ } ++ ++ @Override ++ public boolean isCorrectEnvironment() { ++ return true; ++ } ++ ++ @Override ++ public String getNativePath() { ++ return null; ++ } ++ ++ @Override ++ public Compressor create(int level) { ++ return new JavaCompressor(level); ++ } ++ }); ++ } ++ ++ public static CompressorFactory loadAvailableCompressFactory() { ++ for (CompressorFactory factory : getAvailableCompressorFactories()) { ++ if (factory.isCorrectEnvironment()) { ++ String nativePath = factory.getNativePath(); ++ if (nativePath != null) { ++ try { ++ copyAndLoadNative(nativePath); ++ } ++ catch (Exception ignored) { ++ continue; ++ } ++ } ++ ++ return factory; ++ } ++ } ++ ++ throw new IllegalStateException("None of the compress factories recognized the environment!"); ++ } ++ ++ //Too good method to rewrite ++ private static void copyAndLoadNative(String path) { ++ try { ++ InputStream nativeLib = Natives.class.getResourceAsStream(path); ++ if (nativeLib == null) { ++ throw new IllegalStateException("Native library " + path + " not found."); ++ } ++ ++ Path tempFile = Files.createTempFile("native-", path.substring(path.lastIndexOf('.'))); ++ Files.copy(nativeLib, tempFile, StandardCopyOption.REPLACE_EXISTING); ++ Runtime.getRuntime().addShutdownHook(new Thread(() -> { ++ try { ++ Files.deleteIfExists(tempFile); ++ } ++ catch (IOException ignored) { ++ // Well, it doesn't matter... ++ } ++ })); ++ ++ try { ++ System.load(tempFile.toAbsolutePath().toString()); ++ } ++ catch (UnsatisfiedLinkError e) { ++ throw new RuntimeException("Unable to load native " + tempFile.toAbsolutePath(), e); ++ } ++ } ++ catch (IOException e) { ++ throw new RuntimeException("Unable to copy natives", e); ++ } ++ } ++} +\ No newline at end of file +diff --git a/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/Compressor.java b/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/Compressor.java +new file mode 100644 +index 000000000..f28ae3145 +--- /dev/null ++++ b/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/Compressor.java +@@ -0,0 +1,16 @@ ++package dev._2lstudios.flamecord.natives.compress; ++ ++import io.netty.buffer.ByteBuf; ++ ++import java.io.Closeable; ++import java.util.zip.DataFormatException; ++ ++public interface Compressor extends Closeable { ++ ++ void inflate(ByteBuf source, ByteBuf destination, int uncompressedSize) throws DataFormatException; ++ ++ void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException; ++ ++ boolean isNeedDirectBuffer(); ++ ++} +\ No newline at end of file +diff --git a/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/CompressorFactory.java b/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/CompressorFactory.java +new file mode 100644 +index 000000000..3aed690ad +--- /dev/null ++++ b/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/CompressorFactory.java +@@ -0,0 +1,13 @@ ++package dev._2lstudios.flamecord.natives.compress; ++ ++public interface CompressorFactory { ++ ++ String getName(); ++ ++ boolean isCorrectEnvironment(); ++ ++ String getNativePath(); ++ ++ Compressor create(int level); ++ ++} +\ No newline at end of file +diff --git a/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/JavaCompressor.java b/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/JavaCompressor.java +new file mode 100644 +index 000000000..7d99eb02b +--- /dev/null ++++ b/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/JavaCompressor.java +@@ -0,0 +1,90 @@ ++package dev._2lstudios.flamecord.natives.compress; ++ ++import io.netty.buffer.ByteBuf; ++ ++import java.util.zip.DataFormatException; ++import java.util.zip.Deflater; ++import java.util.zip.Inflater; ++ ++import static com.google.common.base.Preconditions.checkArgument; ++import static com.google.common.base.Preconditions.checkState; ++ ++public class JavaCompressor implements Compressor { ++ ++ private final byte[] buffer = new byte[8192]; //NullCordX ++ ++ private final Deflater deflater; ++ private final Inflater inflater; ++ private boolean disposed = false; ++ ++ public JavaCompressor(int level) { ++ this.deflater = new Deflater(level); ++ this.inflater = new Inflater(); ++ } ++ ++ @Override ++ public void inflate(ByteBuf source, ByteBuf destination, int uncompressedSize) throws DataFormatException { ++ ensureNotDisposed(); ++ ++ // We (probably) can't nicely deal with >=1 buffer nicely, so let's scream loudly. ++ checkArgument(source.nioBufferCount() == 1, "source has multiple backing buffers"); ++ checkArgument(destination.nioBufferCount() == 1, "destination has multiple backing buffers"); ++ ++ try { ++ byte[] inData = new byte[source.readableBytes()]; ++ source.readBytes(inData); ++ ++ this.inflater.setInput(inData); ++ ++ while (!this.inflater.finished() && this.inflater.getTotalIn() < inData.length) { ++ int count = this.inflater.inflate(this.buffer); ++ destination.writeBytes(this.buffer, 0, count); ++ } ++ ++ } ++ finally { ++ this.inflater.reset(); ++ } ++ } ++ ++ @Override ++ public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException { ++ ensureNotDisposed(); ++ // We (probably) can't nicely deal with >=1 buffer nicely, so let's scream loudly. ++ checkArgument(source.nioBufferCount() == 1, "source has multiple backing buffers"); ++ checkArgument(destination.nioBufferCount() == 1, "destination has multiple backing buffers"); ++ ++ try { ++ byte[] inData = new byte[source.readableBytes()]; ++ source.readBytes(inData); ++ ++ this.deflater.setInput(inData); ++ this.deflater.finish(); ++ ++ while (!this.deflater.finished()) { ++ int count = this.deflater.deflate(this.buffer); ++ destination.writeBytes(this.buffer, 0, count); ++ } ++ } ++ finally { ++ this.deflater.reset(); ++ } ++ } ++ ++ @Override ++ public void close() { ++ this.disposed = true; ++ this.deflater.end(); ++ this.inflater.end(); ++ } ++ ++ private void ensureNotDisposed() { ++ checkState(!this.disposed, "Object already disposed"); ++ } ++ ++ @Override ++ public boolean isNeedDirectBuffer() { ++ return false; ++ } ++ ++} +diff --git a/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/LibdeflateCompressor.java b/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/LibdeflateCompressor.java +new file mode 100644 +index 000000000..fd9f9007d +--- /dev/null ++++ b/native/src/main/java/dev/_2lstudios/flamecord/natives/compress/LibdeflateCompressor.java +@@ -0,0 +1,83 @@ ++package dev._2lstudios.flamecord.natives.compress; ++ ++import com.google.common.base.Preconditions; ++import com.velocitypowered.natives.compression.NativeZlibDeflate; ++import com.velocitypowered.natives.compression.NativeZlibInflate; ++import io.netty.buffer.ByteBuf; ++ ++import java.util.zip.DataFormatException; ++ ++public class LibdeflateCompressor implements Compressor { ++ ++ private final long inflateCtx; ++ private final long deflateCtx; ++ private boolean disposed = false; ++ ++ public LibdeflateCompressor(int level) { ++ int correctedLevel = level == -1 ? 6 : level; ++ if (correctedLevel > 12 || correctedLevel < 1) { ++ throw new IllegalArgumentException("Invalid compression level " + level); ++ } ++ ++ this.inflateCtx = NativeZlibInflate.init(); ++ this.deflateCtx = NativeZlibDeflate.init(correctedLevel); ++ } ++ ++ @Override ++ public void inflate(ByteBuf source, ByteBuf destination, int uncompressedSize) ++ throws DataFormatException { ++ ensureNotDisposed(); ++ ++ destination.ensureWritable(uncompressedSize); ++ ++ long sourceAddress = source.memoryAddress() + source.readerIndex(); ++ long destinationAddress = destination.memoryAddress() + destination.writerIndex(); ++ ++ NativeZlibInflate.process(this.inflateCtx, sourceAddress, source.readableBytes(), destinationAddress, ++ uncompressedSize); ++ destination.writerIndex(destination.writerIndex() + uncompressedSize); ++ } ++ ++ @Override ++ public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException { ++ ensureNotDisposed(); ++ ++ while (true) { ++ long sourceAddress = source.memoryAddress() + source.readerIndex(); ++ long destinationAddress = destination.memoryAddress() + destination.writerIndex(); ++ ++ int produced = NativeZlibDeflate.process(this.deflateCtx, sourceAddress, source.readableBytes(), ++ destinationAddress, destination.writableBytes()); ++ if (produced > 0) { ++ destination.writerIndex(destination.writerIndex() + produced); ++ break; ++ } ++ else if (produced == 0) { ++ // Insufficient room - enlarge the buffer. ++ destination.capacity(destination.capacity() * 2); ++ } ++ else { ++ throw new DataFormatException("libdeflate returned unknown code " + produced); ++ } ++ } ++ } ++ ++ private void ensureNotDisposed() { ++ Preconditions.checkState(!this.disposed, "Object already disposed"); ++ } ++ ++ @Override ++ public void close() { ++ if (!this.disposed) { ++ NativeZlibInflate.free(this.inflateCtx); ++ NativeZlibDeflate.free(this.deflateCtx); ++ } ++ this.disposed = true; ++ } ++ ++ @Override ++ public boolean isNeedDirectBuffer() { ++ return true; ++ } ++ ++} +diff --git a/native/src/main/resources/libdeflate_aarch64.so b/native/src/main/resources/libdeflate_aarch64.so +new file mode 100644 +index 0000000000000000000000000000000000000000..284450dd1c3632601b58628619dabee4ea57fa91 +GIT binary patch +literal 74880 +zcmb@v3w%`7x%a>J-jigK3FN|6Kr)j=l<}@ug4Jecf)El#Nz#8k+H-Of4Ge-2-_w9- +zCKoL@s7&P?d#G=dfG8PDYcHOYoWB2(fTcogtKsx^&Up_r3AZp<5e-W=$fVy{|xmn>eDNw%=iQI@=x!Ysz9ai(uf=XA&MIEJ)RxQ<`Z)GH^l_~I#QEEL?0!f5oOn+z<-L*kdn57p66fAH +znTfMKR*kZ8mgBAX^NY{<@p6f?l$C$?9DeKp`R(kqEUVeF;y+Q#GP5<)iXTSX|iyV0_ +zZOMCbqj^!1Q&V%Caxa@>636;=>zm7-UGw~Z)(k9CZ(jHG-Pd3HwDaTH;RAuI!#w`N +ze}+m`SEYXbv7sibYIR{CtpcfBN_!XooAL$sg+AxSg}Gk9k$Z{znO{&(&KKO*zu^9@ +zUvOXe=e$I_N4a;Ye&v5bJ=c6eJ+r@{{8z2|9m?s5HGWU*q<#uP*~+1ANRQokJ%0ZL +zXSv@SFFzxG|K@o4Q2c&D{QfuM_nq;268f_vUeD|C@`?Ko#P2^6zkgf29#vKK(8H@% +zR;_-l=F!Khs#MjY%Dbu_So-MFhnB5=Z0VzwcNMQ#wQ^}?P3?-Mv1gZlQdQSjQ}y7o +zl{G7ttzD`ftABLWH|}VxTl&aj%T}#ar8R$FQ&qR>;i|u1x?)w`vd7juvg#YO@Ickd +zn#Y#?{nFL)?2$*8u3k;ys>Rm5`&KNgoxk+K6*Z47ty;En*<*ig9S=Ubbm?DP$0Luf +zs)NY?ztt>S`PX+RQO94?okSgp?mWEo;nhnYQ=IA^S)(4VS-}{|)x)d)eyLjZ$kLVS +z(WNyHASX*7eRSojDxNYzjB4GARjZdO#;NYQ)vK=il3IA@B2RJEP1oHxHz8U&wnXDd +z{1^Kz|JOeApOlw7Cb}f{L;k%O_(#m1u1dKl{}TSjh#T(AxEvpg`<2A~-n3Zx_b$NJB=#@x3IcC5Ji{h0f460IkpG55#E +z%gQNXZ^gBzjJaPQr$_F`RXqMQMz0P%;v*9O?D6t)Kh7fSQRbNY3Es+ssblVsrQbQ` +z{#bgh8goBkQ>307#@rw4QVPc0j}M6ySLT@eWBF4u=Kk3J-ZSR@IID)(|Lz@gKV!`O +z!aUtjO&3YFUY@#B3vc5$!QsQT@gV(NX$ysy;zS)VK%%^WNYIQ5+0<~WVt +zB#ul?ZOYQkQT&tj%(_nII(N)<8rPmN*WZa +zI+tr*`?UR8T<4Ctc603+bA27xwXy3EeN!hEj+#pS_+s(yqVg`K`g6}W9Vkj~_Pfp(z0gPbLEiPezi_nH +zdRM>juCx~$m!DbVa>k)XCs1GYV9`8NQ@2>O9$Gl+<2~zl@7%!mqR|(B$Kh1I^6Eit +z-Wi8E`tjqtFN|eXzVVy?pkIkTovHX)*I7dko-tHVSLUc^syX@z?U<_Yx3uw-I>s{0 +zvrnjJ(3ltu854qMDt7OvD=m3z7R4}f=SSum!mf4*i-|? +zq!RwPh2P6GRq3*;CCG%ggx{a(uQN5^^4SAsjE3?{@}whHW^TNU@Z_^~*sVIQk3T=j +z^D`Acg5OE*v(WX?YFpPw@O`UIWqokJ*7ZTF7Vw;X^1z4IcuWeOtJAyAA)6yca!`Zb +zQLoYUC;B)_pFg=;jn1atBgS<1pfM#F)|36hpW=yTLC4yW92bp +zg1cjFeK-1Wib>UfcG&*NC?aX!^nD&#&$a$UFQ)o=^&U@j7Y5%0F +zo~UEu?6VJ6dxmN>-HF{(1KilUG4-2PH74LWMZXCcDJi35uXh?nPoP%;F>*|z4x^kdf6S1*L2tB?Vh&4 +zrj~41hU4e=&FS8&CZ@IP%3JhFv{LH#AV<=Fz3IsVVPkTzSJ(YQ$19&iU!3}QPKA__ +z{y1s-gZqoR&eeIkM!m&dAFZC>^~u$um%p-fZ%e%V*85g>5B@27i_|CeBJ@@3DWPP^+5(;m_dv(v^Slg4OQ_A7EUw +z{* +zoce-mmvX^ve=^OitE&25<#&OGII*|Kkn7>1i@%6;5Och^C0?|s*F{}TOLM1L!f +zN8bodxpTJ2Wz;}s(YJ?qHdt}zY?0v#ehcpvWxt63KiAb9XkU^`87b$X|5YlB7M;P6zv^GXW!vmWFW_sKg;h? +zbn-B=D|8leE#q;f;wN40-?N=MiJpu8e?!^)$G>lTS9E`#(*32)ty^Y4q~5K1K%JW1 +z+`MH3TVdG_?8G$mC}vaAg6DZ(ni`N&Pu9)Tu7Js@#dc +z@J%j05T=|HU)P#;`Au)2yE|R~w&h>63Bj{zT19)BYA~-=l|QkoTk6u)lIj`72XfT3 +zh;fCgG!50?N%>qu>#s^zrykB%mAN)^jSIcsw!FGAWaPT_+f;Uw)9G!_Ltk%3#$}w< +zjQ$*FZp6DtRXTZhcvo^)6Z~3{t11WaJGZC_{*W;v`1_BeFAnco(Djlr!7u%j{!IB( +z%9ngIiw}(O{2cXtWMl-x{)JuV7n-A0hKiIpRpnOvSP8Us=ypFbcRxOC^tQXzPtuL) +z{W6{#4K+H75Z{-g0#$kH)Q*{|a&xZA^37D)Isd5|u7a+RF)3JROp1iiGYz}?0laoP +z|80wuTdl))#!O_PZ-%PW9A@A#cys2H;sZ|KWAi$S)f{!@!#%t7)JQ%wx7>XHpW2+g +zTiR^uT|HG*cEI;$_#vHO|!?*ZA$Xl^p1F_HC)rUC*>ad+E0y=yB!0wZ&zcj{Wl*=QcH}hS^_M +zm7d#64(xF5-?Co2>KUJr(ZBJE{afr;?B3!iDs?nzD)K%2fKR1A(_HNGi%hf_j(%yk +z>2lTZ7SEcJkO6G|Vq%U~!{M)04uAb<(}CyU<8x(KkF4bWVtBl=-aE3m{^}8rvis{e +z3Y8wY3LSVxPb)i*j8>*zepAt;;sZg~{w>n)3N1Y-eHT6nkM@j2D-XkyWO$K~(_vjz +zc0!ZLX$LYY^67$4E;S+OR5puG2_EV2=;r+1Ez0)DTw`5kg)eF3OCobQYDPqTNn~wO +zf9n)gAo5@TakMfq-aF!UJ2B?In;6qejCrE`K4c<{oJ#u_w{4#P#+QsaB?ojFPiT;G +z9_OAdwR-L|rufA8cnYmT<7&n<^rz@t(SPUgs=2$JyXjl*GlS5r7+(*%;-pV?80aSW +z`S$gyQaNt+E5OXJ)W%Jd;yA_F2PQeO*W?8^Ve7AJC$!#m%@zP6wbqEpz}V5?#H +zugo_KQu0-S$7arJNm28p{apI2PgDii{fO9ou_NC_&YO`9>5I=$^AvtCA&)Oy(YwXz +z7@0e0qz7L@mrl@E^gHN`G=0guseZ2Tf!O6+I`2B+flqTjL%w@~2O`r_N5_>TbItH7 +ztDZz1*!(Fn24dqx??f-g>gW#SGGt5*?$W15`WTOdE{k70cD?zr*yKz5*taboTWra) +z8g1RC0-g>PC{cemvOYzPmiRamQs1cdoP9YX@@_qJeT6>J?!1;(6{uU6@}1TU)zzAzb%~w~ABk2Lr>m~w +zbgj$dle|uktY-{vwD>BQHDSF4_uOqF%!lgb046R8towyDPc#DHsJ +z@d3q{iGB+2>+F}`RHD?Tr_xnca#5NU^Zf#Ou1N#ZBUW3D?K_GrY5i9g_e^YD?gH#+BIC5)^#;L0@@!v_UfXplV3AO&G(5byk?KV`{C7Q +z&xe#hcD?%8Zc`aHr8z`qMzHN47?%-CtKhklsl?fa`y<2V9^RGKb*xH_X5kwOP2!t$ +zRarP6e?K0-gxwF%Qd!mXcWAuIx~gpMdFk`!@oLEw;=#hQ+=9X~S3#38#k+_+!?G1~ +zR6{+sr9+=;`P>c1i5rZV?eEjI{;9;XV)Mn;3y-U(J$b-sU?bix>albzZXXKa+4_&7 +zm9Db61?PyVqzyav>)gpjT_O50ysNZpls+A+GDkn4e_{GBdLw$EztA?I4BbBnFS6n7 +zhh^rdM!UtkHH()=h*J-1zH%GK=`X9%;a#a+zdax0Y3OjY^7NOny)QpCFi4##&?e<& +zEQF86yN!Un`vAwj_&dY97InGaBF05-6FRwLC|X(Pgm>t>_}91Ili2T4{&mk5DJQ%T +z|N6UdbWVskY>++=r@?>tek@yM)g`NJXIXW@<}|g$2~Spk;yK`i|AUiNma}Yj!Cc0} +z$yhkcuHwio_zrc|PUD^8xqiHuZR2x74v(*Q`1UC@;3G8ah88 +zv&QV1I;`$i#d^TSS>o2AL!8n3t(*_@uQ58s)b0`+)pw~hX2_=eS=z+Yrs_}LeaW1Qk6;jQ?$MU*>( +zZ}~3^TH>=X`oS$nylo;h~&!hQdxx@C!ae +zp7fKNnJRF`n9xs5>K~+iC*!Z<9}@TivNn+TEp7HfpR}RyBd#&+2tFgvrM+UG7BFv6 +zqct`)nz)~!_b|41KDJBcNA@Ep4LRax56VG +zTEd1cD7 +zce_gRwrzT9+7a-Pz3}`9_(-9$TmE&bLoEsCX@SrLEg-qRtE9lnKW)nFam-iUUd=}Q +zr$xlCifoBIhZ`Rhd?=7hU4xCYy4T<5>dsM<-^6I6->Z#?ov2zAW{wd!1HsoQc~(Bh6h0E>%jt&`H@thUR`slbkn} +zOB?hd3Hn<#)$=!$D+EIbGk#|(O#GO`oru>ynQo54%Yg7ya^f?L!&&$l%XN+5ht!K) +zTCr?`hqU*iZ<3lLyv6sq1@G%1Mogh?cs?NGWaGdVlwSrPjwAmfYMpS0`tSPV7Mb>+W0Hz1^;|YI#=2GY>}{vQoRY28<*#%agCNJ<4#`u62P0 +zs6doD_kpjx0)L|_*}EH@BT>K9y`H+r1G928&AUERRYuU6#Fp;`h}SJwn%|Xp;Eh2(o$3nC!-% +z20}&*R}{=~GV-S*OGl7D6aIu2sva$aTz_qCx1!zZwYl9n8LHvu@G1}9p`&hf?~@0x +zPyROgL%-kM(^%VG2d@p$dB$0Bzn8HN>mzf8CUad{g~(SMXCM97D<|J9JOd|*+m6ZZ +zL)aY;b@XCa_Fz|9!Hp7iN*iPAt@$|mr9S%<{}BDrp;`3QiQFT{bI36q4-sp`>&sfo +z_->~kua;g>P^G}@kWDa(cd5If2Knnl-zaOvw9#TU>Wbr-Kc{~!;*Y>Hwu55|)>Ma$ +zEC$OgBu5f`IEx?DkS}S!V|h)%VMCR*G*)*PtKR$QOIBU}&!@FCI=k!Q^3kqqmdz4B +zF7ht2ex@Sk!)46Zqc1XkhmlVi>ttj=#(NL865X0ot%JdW$1*ODcI)H=nctq)2wvA+ +z>MW2tBa}(l|1%YDbR9=eBqxj692M+FH1u_O;!(Wz(XzvLa=Js6C?!&&T% +zS%H1y9cXqJQJ%4XH=lQMe6|L$ea>R1KMY-CX})drvowWY`C89&l`R9I;MaN%>*M|O +z`|fdtEdwL^IO5`@po<(GEVld+jz4Dj%IA}(+{hvEmc(CbPwQW)L*60VI!EM>|P%<@6_yNcE^?eok=S2 +zpnaTQ7ugZrLS7!i{x^Z2*C3JMrgUE)oeSAtu*K@Ra^!s1y`Bk&TSH6KGwiL(S0+rRwJc3cbvOou8fsf21 +z8ID;~_37l6r(EzMi}4Dzqz2C3ZH`_!^M{LbQ~GXuI@{d2S=RzZFWawwO8>9hJTKc< +zIbY6PmG^Sy_04UsEWU@cdgkk)a&wPEcjK9m)cU$tE=FU9Y +zOv)_U36>qGefpKf)lx6**U_ejw$w{MUVJmZUEKH3p0r&|d)2h-q|KVPt&1~hv;67K +z#Z?lI(5^{4LxT^`%j7J43KQ?tsbt5mG%YY|ZVP^J*347i$_%Kde>ndABi9CUXQ-@` +z%pYNE9KuUY;v@JbbdH0U$?$Y&S#`lx*zUb*oTH9Ic-v>QPp`Gxr{_GTPDu>*JZ;JQ +zC!4i^y?#|eowvH+X~tpPBy6CvTkq7^lBaJ?O`e|9s7{IB-xF;wpQSdH_ks%~tJfbb +zRz7^3qpZ%ZvKJev{c7DS-}(FW$qsdX)dBp8qjx+sI%OV0_Zpj8tXOTI%mX1ubxGuh +zFPc58#u3*bOUIItGn;M60%+O`j)7h*aXI_93}wnZMS4&hUsqh|OQ8AksjsVZCeA2BlBxzHf|>|0g~-lhU!av~Rc;6e|CCNDHg`%(IJ48BC| +zs-Xy7JnC$oCVV~FtOC{GO-GUQ@zfOtYj&wj$JLKqp3S^kwribHQHQP(`$ismeajXj +zzkiFg*U`AL`z|A;|5w-tEz|r~W~Ry>rfsz@sbY4L8Hl85{_vtl3T79T&hy?;-ZNX% +z9WIsXsCDh$B6Tb0zAe&sR$&Bl^NSBusd15ErTatBBJE!J!&qDG+Ia5{Y|AR_8Ts{+ +zAy0Wv)p@PKv+22M*x9VR$+?F-**$hG!_hiX$6wpa_F$i?t^(Jfzm|QY&$Is!tvvaa +z@*WTUv%`};_}!C?>&dUkySgKHvhLnZe6fq%?zEBOmgjb!{~#57>G4OqE!+3_P2J#9 +zJ<|U{?BkJJp#|IN*|cez#Lwm6S4VD@`m!CK`so$mt2ZS>Pc8Huga*OsUZsAKv8$4h +zEik1IqwND{k*#lFzq0A03x3X~ZB+=Spwya>;dDD~YK_>;5IL1#zN_`A{vGF{FQzkI +zscNcU;`69JA<{7$939!No~^P*^a=er#^i`AmAV+isR#aX3$aF4%K$kn?|7X3(>yBO +zeGrU(8~8hMz`UkOsv#9#7bdBB2l4fXp{o{NRl^4tZ5iZad|IzwRmOCwJki+GsCe+5?-&VZ~>6{Ef=~-V;+Qb}gCtsFT?UnOgnn06>*l=G*(y*wP*t#qXR8LQ3dlG8KSx$Y +zUj~TB-c%WWJ2Y0Gt}58BD(BfX&C$mB-zc}6*mXNL>er2{y4#5HdMRI?+qb1zuf!g_ +zwdJtu=UY4~8=LVa`jCV^U=NfHduasoLKIk(VRM;U2TY*G$# +z+TozzspdRvM}VBcFErOtPFdv)$rCKvoQ(S*v=u{J4&&}-4*V$Pg)ZUaHOXqu9plV3 +zhxBwmbM=Au($f81K3l*{HP`&wd4+$qmhNAldGEYZmkQ{mOAD@EyR=()cewf9c`uJH +z>A`K3?Q2|S@y`SAnq%z;^IdOkDYk6L&*7O3o@qhhQPEY!2W0FIS@vptM0i#NE#E{Q +z$Bw@X95;d<35`ES&WGS-Dq|T$Hxqo7v6XpMiRpUPz4P8y^Pxv~XtYZpmMuE95nCqx +z-l!Qt_~1VQ4foO3#w+@^NW7ewd;gxUMqf))J^z}fh>44=dHFLH{nlK87k%GJJ6`nk +zHO?Vo-$aZXBCh=^*pkGe)trSEuUum{nvw5A$L|eXjf}Tt-okniEpU81G=K|~4&NIP +z+w3E+D9yShkl_@1Q!Aq6&b`RA*e`Mk_c1-ge^^iQ+p*W%Z>tTwpO)cI&ANBq_s#k9 +z?0;Hf+3a_lZGi!GonNP~kE~hQeMQO2f@0*Pq~+duZHNzX6Yq(hNE=&e<9f#N2z-@sl`$P0^LvEff5N8z1Lrdp +z|I%d}A%=O`c6SU5P{DtG+6+kUGDb5=v*ItoK$?jW|7EWk=wN)$QNH`!=JIpQnUgb<7hM)){<-|?)Y-jv +z+oQ)GB8LJm?`SatA@Um;uWDm*f8Qf511{BZT5y*Te&cOyRtOAzWAk^mXuPj?_y$hQ +zRikIAv*1H8$DER`81)eY={_l5KWZ-}>Z%*J$e*@vMqu62i$w@|Q +zny+7TP^Ef*3IBbJRbKyt-B<6K<=1SzYm)I_RbU<_vPT@@g(;QJ{u-XU{8gIrhL{`j +zO*RW;{=Oqk`8B77>ttz8)zd-VEjGFXzuT^-M|Oi(>{2O_7S7ei#Qq$c?bPfn-q$Z< +z-rsC+AuBQ_wsdo+ZY$c^mR`NMj>E&@;;^NIRf9z{Hi4+Dqrhj(%QGd-c+C|_*dGyt%JNjK#e+_Rh_sGZa<;uNaBq}+W+qt~101UNZ1H8eXEdfWYz$RxA +zXJ(Brttog0T%`~Ht@2r~W2CYEK>2@=cAdY=sR9o8g&oOqz^^jxvi?vfwhEqwI&D2W +z?P`ti@cZd%%`UJ7_4Q=8@Ul=%h^&B)YT_H>RIt2kZz`CS@J{6IFXf-tWiHvvnm4bO +zR>m1DT6aH9?7te>snyaP=H+I0r#`{|0+{s`zLRSda9`H?D&3ze{R>nIq9!=|1L)RpHB;)=x<8I}- +zjC&p9{xsumU6U8o6&3Nk$hs~8+0tdm>bOWBGz^28;#+Ae&1$RNNcC>uK5?WQENx=Dp}hN$0e5|M +z!7%)3B_Bg>0-~!<+A7qNz4hps&?S75^-b0EyOn;6KKRgQi^t^aCeNfV%hJJY>z8$J +zSiY>gN=xDVCfRPAjWX^a&75SYNU^OP^VeMbE!Ku(jhULO5MLf +zFpe?0HCscsple5(s(coj!p1na%wvnZ$#_1flK1bW{9bf%FSO{r##S{Wmu +zy*fcV>({b))*9zwHRJUh<0XDw18bGBsI$h)i1RSnJ3A~kCfO}~SxNp1wt0bf2jQJ7 +zB;%#5@shkCVGG_m7`12?J_+5%rRcpOM(=rI5v9iOKhC)oS^BR_mAxlc_7r8Bp??`= +z5;*r~*Z%FZVhxL*l)Dr^yFL)wwEiJvL*z^7`w!}^VGKmpT=3KdPbDARLp&+AOUeyU +z=5suMBt}<1Ok3-OAVAndaMIzUt&#mZq3wD2Nn(m*E4HN8oJ~pgfH>~*&Cw6oO +z^oqUGc;DHJd|2a`8u4K#t-2Y%w|VvqJetp$ykP%ZoM&SX@8K-6QZgE?{2%;2Q}NHN +zi?lHOpov`-KR>ym*7EUxLVgBMY%Xt69gj+WroaG913Rli%Xw +zZLG71&HdkUz4-gVl+4+z5%aDuW_^#ER_5FB4+9?<)7;hR9j9Gtl3$P98&G%N>i67m +zO8~siUzD!-b@h6=-ln?6pZB3}A;T8beS(wt%1h2hUzEA5`@o#J9;o)#Sa}X8gIpbGEK@wRNnN`kN`^1TWI3<4?h1a+GRFp4dJu +z=Qgcj$1SRIE^$JLI6;}&0afx~K^5x)Qmp9NePmykX&SL%ct+ur{#SqX+7Ujp@UHSzlM6R?MhS1zMJGZLZ|>fhzE# +zBHHYjSsc)Yz?(ZJ2PQFA(}rdTrq^8;Q13r+U`E?F2a+WxXWYWA_AWJaWni+A?AKbe +z1L4+W=9i`hFMHsd12WI{M)oZMS!exqTgU0Y;W)(l$P>izA=XA@&Hv`WA$*wR@=JI| +zK4xLhcOc(YYEndzKlwscjT@LhXzi_PJcR#S&{oy$%60nHy;CZbQaAO1t6V{x3{QKu +zqvi)0=yh-~KneLtL-S`NZlo{Jq3hj!e_ypR?HIpSA9Z +z16L|L>t2~p;n{+EnZGl8s|hpi=IiYu#ol&RhpN1^U6ASQK%V_POyRm?L`uI +zv93&FP~^4z+h?LLo?r}Q4s9d3ekT5=bZXYeQPg<7q+vmw+{LS!s(9DKyDoAl +z7w;w8hY&CxT9y|5?jELHz|-zdnp_( +z%O32m;n<73=ud3@j_Zle?-XN;XtUq7sTDj!^B?SpVZ6o@UEis-8UEUI-S2sS!GU7V +zP4<+?Zq^brUJkJvVl%{cXxI%|7bv#FRK2$|cfMHJzOu^1hE!ogYS8CoY)A)t0FK4& +zNGe#9*pdC4RtVk~=Wm)rFrZ#+ih^e~U?vKi;#^c+K>Q)NGI{`SDO24Tw-?wKiBrGG +zzBJ3cX57Y%eSfUI>Er!F_{OSC)v$rwr7B-F>>#GhnW!4J!Z&=+maX#Kp?jsB(3S05 +zSkvjRwdwvl+P*fgi)TmKvrx=+lvu_NkH*?n;S2LVkrrYo;gw}yFYu{wRl?RL_;fLk +zgg?9ClgQ0heo{zP^(m%2N3H$3o_N1;>aDG{r +zSBL$riN7~i?li`Yxu7W`o6_$=*4vn>>3~Nya(vo<)`(20ratjm_wrlt`!A9Y5B)YP +zU3V+yGh%Xb`vp01(OxQY@{0XaIZ2Dli8C-pPAoY{Mh?K`1HuR0UyZEH692<9bJ2q_ +zd4Z0YycnO67tzDRWB9TCfka*+P +z)3LbW3^);M7aIoSWycGj; +zvYDrBs%0R5Q`?qklj^Dld#T38Rx6#u;IMHdaU}1oE?_-cvib==jl7Y$3)-HAjonrs +zi?5R3$}4EcMy-br#H)9+PA4GkY1sK(=G0^k&BUIo%-WSIv*tREm8=(9j4h4`ebvNR +z)t23!HM5Y|>MeYilYMRqye<}7)mX8Wo%^Eysd5(m=Pdf)$-9cRLiEjAqax$oe9=9j +z`wzskGQQ#XGuihc>shW-*iWadPqx+xu@+Se3a-UMT7MrHR>%Nr2LF*bxR5yadg9!^ +zz04P>94ii%IM>3k?!Oqrip7w+YQ^8`e!htD?(dR|0>@>q1bEiJw@d^}niTwq`p+BV +zgCTIJUa+qCUXk*@fu@s!YY{8=>P3MNYhisxQh)fw=7IBIT|rQa$FwbSYwZXOliDlb(PrcK;MPiNDunUY~d5E~mWFCA3T)<>b+??RM{oudqqlYu> +zU5BQmc15mE>soy(`r^Pev*&O|N!Or}?GCY~RWL8v-xo5Hf~-A!kzB`pC}&ZZ`J`46 +zxw^C~#QgMO)(!lT*h=JJYm!3{wR3F?iPu))!ksUvGrUMcjJ$$ptBPh(j?~Q +zxy$yY>+#$8DT&KFS9*xcv2#{jZc@gI$t4#5Tks#`#f7}Mh{dlhVGjQ*s^M56a+0kY +z?3{OHf#cKXonRP3kB>bSVdmb2H^LV$cKTqF?NrzO#4BnNIWpJ>*ckIdl?rU{7;$eh +z_+H@z=EaEdLz!mxHt@)8V2>+qZ8~84y2{=+YmWbIaQoRsrpzBrzn=1KoWtN3&w>Bt +zRhxl{tgm$OI|Q!yJow*6aGdKoXCNECTr+D~O1l3A?o}^z`ft#Dqv17qjhSGDGG}H7 +zr#VP&wiA5egfgvpG&N6EcI;o-{r%n72`*fk^L6tyR;03)z7{IV{vEW^)(~SSZLsfp +zVt7rcQJ&q!8Vusd2F3a1{Waar0`pr68oPG8{Oy{}+lOxEg7e56S``@2qK8yvJ{Zp+ +zm_)l`tzQ+_U_66h5^`-;tI9T|?{6VyY9se;Blo5M6|&#(5ZEd4m3I)o>!KYO?G&vE +z$LQdmMF;0$a!P(L5#05VO7&k)`_F0GyBpuB>V6rzHgPX=psG7ZPxtnU{v%(B`ugJa +z{SrCbmZYq;N*BK)KH-P-^?mqPsAqazTt~rLHr%BuH_)#W9EDnXr06oP>DLJkxi+s; +zm9w?<{Tu1qY_0d-XJ>IR=e!Xdze=ThccXtH`k2G-W%uzL*(cs|UrgJ3l-r}!oJ2pR +zkJ7i_Vv9mbmF?1W?+(hyc=s`mId-#Q5%Lyht;|uw9?a#F9Zu$p`_f!~Wy@juQ+MYQ +z!z(-Mza*w7PR}Kl*PVCtXhtIEP3m!Szo;S>>lan19%W!xz`5YdD>A(voKJXWKSwTQ@n84un~%;S|6ye4Mr6L(u=lf0=~U`m +zY@{}+KZW@t?3a5na@K^7cW}QB9e*&c-y6`WA>`wG==b-~?`7!sBk1>===VN7)!$~C +zfz|8ME&a}XGTqYeYvTI-*przR&0w{n)2UBpRXh(?+h*x?s^7IXud!}nzJCJuMYm-R +z;NUpy6ms8=UXKGaegFNcZn;-&BE$PvbboL6HGV@kN}JH@BJ9}8xL%K2pehfEUbE)s +zyK%kld;CyLpKgom6Xzcw>-p$gTU@Vm^bAX{^UK2p{T{W&;z4*Pn=zPyoZ2E?`= +zVIDzzlGxX)eiK{AdpmU|#+yhIXQ(wf4loAI9!V$9zLRwtJCNCp$hV5m%MGbX;QcAV6Yygn_F)I| +zJP{wT5?L1=ka}4I@4kD6+1-nLi(OX8_jPM-Zank1D*NOU_UW0(^Xez<({EVgY8;O| +zpJo5WYGk-w(<2>>hv-l<*n*6`*yF!+{PP%F<{2e#ANb#lx%j~vbWVKWBi_yt@qc2g +zj+(RlBN9_!v)2DRYoD=8!`OwYS*%;h!^g80ql0=9K0bwKf28kXGfw;>TKNO6H-ML) +z;1HXxE|1%EWZBQNg}f_%Gi=!zY|4MJ2Mpe`&h2$s8_~?T69Z%?#(sm-%CncWG5(6V +zC+5|&+K_``!^Zj(Th^Ot`IamQk7W)jj4X(ab|DLQg0TxOH6B@DJy6zoWWk4>dJb8T +z>*tV#eB@v}HasRPNj)D*Cf5CI$cOE} +zqI1L!i479lBl6F9Ejb9C;ZRjUD`S5Q8T@Pfwv4;wuPy&J9{-o>-Y}8*GUR2Dw#B!V +z>;qrnIrHvr@o$!IQ<}GiyhwarSmpxJchS{O_JfIB$og8LH=lXm+)K>+<}>e`%e=2k +z$-HlDowV5Nv9|iHz2E_ipXip2ev2(^Ll@f6g%u?a6#SI?AvL~iKe@HVbINXG>^z(w +z=9npKGc3BtyO}>?4&k|JA!yCv{s!hQPH>2>hKT7bU&Gu*2hTR~ZW4BEL&=JfQyhg_ +zR-_0yJi+?DO!fvAA``jf5`{*(H}%I+#xUI-s9#oa0^YsPnYoUD761K6%A{KJ0ocu~ +zXQ_L(mbL$zoKNESR$wbcW_;LJi+*f{%$e=FT=q?VYHLoMk4_OgkkEfcnN%&kOmLS? +z;H3Y~H974jE2qVdh+Ppm7T$sVS$iQge9CFcTXFl_$TK=CHcYPhHce#<<*MTRTswvb +zD@s<4%v`hzI%h_jiO)`cjt3duMeLP5feb%czoOtIyvSU(qCotF=uQpujlw6H6Or|O +z^8JI8uhVyUFMY3uuU6mTz046?F+Q}UK*L6D=6=4ZGUXTKR3eLSK%c}4gTgm*0w;de +zDi7a2f=9xO-&0-%A4@nDo8bZZVf!L!y6 +zqm|vzun}5iA48kU*uR6b_$T?k&Y6lI^Zlb3R+7L@#I7XP`v`X8ar&%p<;XgqLa>vo +z`jdhnNQvJmoIx6-?zc>lQv@uhJ-UuOr~w +zQyAC%Eh->ugJd4m!c@Rcj+2{US4NeVZQ&=!{u}(Hh&k4a@slLp6P(}!u$nvZ;ew;c +z{1kS1r^Kz+o`M8^vMr_SH0zXxn8P~sLR!}ea2wZq%#S=oyv|(6Y35Y~KlwGdieM>% +zFAXx@Q(2D@0e3jOZDCjF39VxI1+bLwsGbvGE}`T7>0)b +z+jz>cCy1A^RS7)hYGRt`?c^zssmdrgik)*)98d9rrwBbtOXu4o*%$DXjo>M}z*AEJ8N?ic*-#NLkDZ7wi35&B{q5X%S{LN&$`+FOZIV;f~6SbUSOir +zZ?NJO&0WXuTy%H>SV|{(m&x%u`PReWCiie&$N4yVepkvk|6;I%<;0V(lW!F-bo+bP +z_IHGF0m$iD8iZ%qZ +z39SvqXyBej19tSdkz(!NdJlTH>1qBOX#4x{<6+{IR%mKaY5p6*QVR8P-VW?RqOM-> +zA?XkMSSnkNEBg?{3a-L+b_DWT`oVaTzeX7#NMT$IJ6L;;T +zPjc;iP*s*{%^yzolq)uHcYtm9I3`NGX^8v;f3}s)P_1%(rAD}~jNi62lK8vAf_-)C}G-5J* +zTO31~6d49HNMI<1#NxGFE6NInvJbl*L^lLOsS@0T>v6oNB92!cCXANs%GeCxOET1w#@1ZtJkW^(_qSb +z@vG9t2b^Ur!`!Rhvv94*ZOKW#j|Gp`gPDl!YBEy1smQqCgr~upPg5pBE|`1Gy%lU!auCTgM3__-4tKIgt-;da4DVf6;>*8d_6Qal +zT04ZS^Zx%6)0=9}e&pIfuj-&_lcl?k3%Vm&FWZbayHaX&|=NcI|EWJ&R_?C;_ +zChreiq_;P+uclC+?yaS~)Gz#&Hkkix&yJZw|!2ZG;MqrVB= +zt>>QoUGfcW+yxvop(in0K%Xws-?8tA{{92~+yT#a>2^zho5-C~;oXDuZ3o9e+1Fge +zHGSK`A=e80XRoqb`a5>b*iJ2(zJ02{i{Qcg=6i2`p})tF_t#k? +zU8tvc4Qx^#n2es?d!JKJDPygDnaNzAthc|D{TE6bZ^`EHV(fu7vHuO$P>O7dT=s&M +ziCiv-%Vih+_<{3NA0zT7vL|wR3Yw7Zx#OnW4)$q%!(6jfe3XWnAwt)?YiTUpW8|`%AB!iuZomv2PBG(JRPG-5- +zQ!oADIu{znj_dGGu65{jeLcm^KAWDrRJF!EPOT|SWgipvINgD*X8-Z4$N>B1tu^2h +z(_;I;P?CZ{e5PP1uf{QyyO1N$dB+oHosuFi{W!ZW~}jt-!lHW@H8O<(tq)H$~vna +zzD+u%8r$8BUYX!{_l&{u-Y{?R4;#tgc#|Ua;0NLxODsQ?;q~<0zt-c7+2{A+_bKH2 +zXFRiF2Kpd#^fDfQ?t1mH{E?V*EOt@_$d*K-GwaWIzEk`#+DcR8^Hd)hD|8c9hUu- +z{kvj=XJ4->z1ZKK_po-Fn90SwpBMYP^PSkWf4|d7&j;8Ck0dg +z|MHU?u)*RZgVGOVyoi2@j|@sbxbD44{AAkxXD;%SrJ6xMrh9X5{31UI{qzyK8?-Y~ +z>}F-& +z84T=ZjcpRLza5!QLgvl*9s!d*0w(e=vTkBqoEO;^g>8}8kbQ(}^5V8d}Q}=S(`y=BZI&tSFd>d^n2iw}ontRD-I?xl#Hi2)w%`@?32_JSh +zcI&IuA-SW(*!Jzw%Ga75=^x +z8z03OTH`~1{()zu;H4h={A|1*Ua;GD!ET$tuU+JYC!rU>9gJ~YydN3fW$((~%M5oz +zy%+4(;r`_^Z-MNAlz6-O9qEVT0(R@84f#%Ji24(H@lPL#j!xP?kv@9CwpUXo0H!D3 +zS6k2dhny33ll24vkVH#l?-iPv7@U6H?Qq20nHxc?v2`z-Gt;C(CK;oh6pb8I@l +zWlrrj9*2knwZW6z6$tOsR0#OI8U`y9rj4*H70R$t}0wChBUtaf?! +zGVNX(Z~mpVhFAJ0`jA)h(8$ixhj<@9jqFN1UTTeBZ2wRpdf)Y^>{&{2U&~s(qs+ks +zqX01RKe(6UQ&ShPqium_A@%1T?e!=tC+e~gZd{ihgz +zkwe+DdL!Q^lpNc5Dq6V`ny@=r*85=lA5u=f`F@Rxt@n%VQ&rYlzZib41zkC2Pa0VD +z*tLGHUy!3)F=v{ESzlQ12c9f`4&B*F4lhSQ){`}3+eDvc5{JCYcZ_aLVeQ`CkNwDr +zFTW2Q`?l7Jfnl(l&?+_RZ2fe--`AYJ@>7qebcMjeod)lCWA%qv@7JoO_8$V@_OZUN +ziI_q5JSNurZI?rQPw3r2@Jnkm;!o)D^R50so +zx0m%kCx5{Dz#o@%If%y&@$HQq)*>Hg&5x`Ll<)t^et=zINVD-Z+x5x)tQ&Oi66{E> +z$@T>MI~UpwUm1D5=~rX_|QodpZ- +z1v{#5xqi(<;JIimp$1MM9?!OOibPq=pd3Xu7pim`wuSNF04~{AK +zdf9Kd8~nEq{C9U8J92UFA^632RK0iddmDScUMR6J#ly@&_=uZX=P)Ys2t$-*Zp*?H +z4625g^&bP-;%0Vmv#7FL +zIa?07TMPNskfDQn+b`s73A&!eUe}PbjqR88NpNqW`@M*a`y}3#zGu57Z&QyCT79|} +zxh3{#2(fP9E$G+5A3b1Qd*H`G&2HhFVR%#szA1dHp)XtM3v+gnK4R?}`m&Y2$aM$T +z>_@TYCC2V?WN!+6A%_RIlerIBD{SE)7QQKSBuQ4@)(0IfvSb}w +zWIZE3xPxcbzOdg#Zwak}N0w|l+!9yHnjdtyT%B*4w~2CMR}=EEh&6z((T0q9BBzpZ +zep=ch2HHUkByDvh+9E!2^Xxh5lXz%BN&UzVOY12UTNiQ+8}}>XvV+f^mNd?Puk$MOS_mnqb}S7q&&eb3JkYslQar317l>m_RF +z|5o}h`Vm6@t!MPVfM=JEBY$PZk=t~=UvypiE#D)rp>JF1o796Jh}A>iX7Q{g-nSZJ +z&K<;@h4jA}-^JhDaf4T6RjF9tqMUa_e;EC#udgBI)ZAN_$M$OEu+}j=D1D3Vft8qZ +zg49i){>a&djdF07ah6yK89O!19DibdpFg2AAbTTD#IhlEm!+gi2n3(EHj#{6YmCyOrv=o0)Nz(y|p^ma=q-&31&Q_mF3Dzxv+TI(`rH^PzdNcdaN; +zgzdWnn4i3p%55d17VwZI6 +z0N-Wv=WDE!RSxfQ;>E6Hm1 +z&Btc*CHVt#&3CUWUC?2(>P+*>dW{`cozGmT(@&WYF$);rnnR4y5HXYDcSud?SM&9X +zsGbtp#u&Bh<0GxifrOExKK@2Y60|3C$lnT+Jm@5AbBg$z3wdDj*uJ0(T;9bz4&MWG +zyO@_LGBkf)d>$qb9CB<7*{qxM&asCy#P`rxpA-!9J(=xO)h%cF9U_hsImw;HI)B+? +zuO?aFb=i8wr}5>YZ03WJ#f`eMV$61#tA-aMdm?j9Tt9n4Y)rZpXRaKMeWSh)%va}I +z%@TWRe5YB(ztc=4^0Cf$n&mqiMcCh#l+xLu`|;;WceJOZ%@+C9(&w{R4qL)K_F(qp +z@C`WG-!A1vw$CE_Lbr>xY_V7pT#ontym&I(iYJ>~<|(G52C4(TEu-+6ares_A<>r| +z#2^R3;z#gNzx|efn&?gm_hg**Fy9tpoFsQWL~btfUp+rIW+HnsZr{Rg()TRG(92|= +zcoFuymGxuaEv+RNo*AhDE7bGByvE$?BaYYSfA*eB?mh6?dy><`zbx@>$R>4GTXlvR +zGnw;@+5f3A-=?N|Yth3&WYeI0j-h)ufTe$zeWX&hb$RT&hX={KW4XTa)=^$$UgTde +z);cwF|JXGr>AXj{=$-DJd%MS$;b)2H@o2W-(IS2Cc +z2YmSv_U9j@?BTiX(+)C+%X;W(+m*)O>>3}*BkmPFC{)Q_JL?e-g2{)mYsV<_T<$Et +zV0$I~SNTORiiqP{i1A$a)PnKb+=lCp9+7pC^Ew;+cO~_-61U0kR^qliv%&hEXWrN& +zdLcSt>BG_JoIYZ<6O834Eu{`@#|%xT!9 +z6l~oQ?4rnHFgeZtYI2HS)|ZOUJd2E<`f*B^*hCNImRR;H)q9L_3MT8=#0}GG$rYr& +zAhvKXHnE!XjeBza8E`eSTw92)7SKdS0C>3&G_YPCvt5$vMFVy#1 +z+#Z^=c@{pO`Z4R~TX0c|nFQ?HL&S`Ato2@Rst#XEO`6FX+uF4$k +z1P>Csb&7tRWA0b%KpuMl#6}%MC&R16Cug{ajd8(euvvn`9FjFMjGx#eDK9qZDEBUH +zms0;CyVOK|VwZ-Xk$p<;bI>8*TbFss%;veT@rH~0)NpMKSdlg+@I3rN;P+$WBrfV&StyX +znU}@it;l4ZA-Q}p-xFxP?MC(ysGSlMNi0+azM~I)Hir3RkZbrd+`kaoOlV`>JpJkX +z*J$N`7pp49IoztbeJ +z?_-+1U)`><#je&+PZ--0;#*fAf!U>_$G_Io%VeLP*wpJwSBz|C&39De+rHS;^n8^y +z_8ziQnLhus_cXcpz-RA~%T=bg#cZmx^s$lWOCB3xEl@-Qvp*YOL))%Rwrpy_Yna1eW#%DwBCR~KxokLd^BbgC44$lNM)av$B1HPa`xiTh77S9_AV+LO%H +zhO~6YhujyQ4#s)96`smIDv6i>Qokl~y7;vvo5Zi#f&rD~7n`)6ob8~ltVMJbA#xekos3Ky+8f?vUTF5gk@- +z>|;fb#|zCD=^=j$FethxdKe;J7G3NZivA?rx~NOWxR$@;lBTqN@|Us@e!+#kF-x5N +z@-0lkPs7v)W)8*}(ctL3PA4Y{o+ckSXXPc?);{c6=)>2HDgI&X%UGRrCYd{j(Pg1uzANeDJ0y|| +zTuSE(pHN+ePw=;+veanRHz}l5H!heY?m!l(N$5He!F_t$oh9XOH$lt5|Bm+$O2I3mlIGjcIa`IKf +z+u&W=olg%GY1a338pg@mDw}WM^&`<2t&)=%3fmOaLIITf*THQ(lJ +zzmD|^%&{oGD^WvkH5iS)kADd$C$V3A4}UG+@=W2In@K(`P(5CK+yZ?vREd25gjYkj?ZgNRt9wnYW1 +z`G&ZOeU!hYCjE5sT?Wao`Y7wc +z+_NUP`SzMw&9{#&_ZZ*x?L%JfH+}P#`S+Do@IXi**_+prCZN7sZEXx?>(P*X8-&% +zz9}KRc}Lkk{|q}Z_Srk?o~tj^xi;~P90WQ)^Gx>dSo}V}@N>^r@Ju_;zB%N-h8XW8 +z`M&5t@0sXNMEHaS<9=V!&<4P)hm3LpxRE;}sP>nwdMPIzanB=cA`2IBXn*2>C`MYQz@LmdhlD)S3_@1-B@d@qwdUzNGE5svOEZ_(Af4&H0dR^4Ok`YB~s97a~`tgGbj>d5#F +zF_)9848N4GqWx5Ivg+CXf!@qlPxt=o?@xC;d*rldUGI0y^QH3reZMPT1)TWH#QSF| +ze%kea+Pn7nsH!`E?!5Azgd{*9kqi*z=@1?YDwfGi0zn`|Km}iu$;>1fm`8SI0s#?Y +zq&})`gLwmuL8WW0-DS7<=&oI(;x65Gw^Tr}t88d#rE0sVZG~3Iet+klGnq^h=|{VN +z?frc|-}#;UJHPY0=lss^o^$TpnR_l3V*bfxAN>gJosM^!(f@Pse){aCxS#5SYys+Z +zsgRFFvp!k(y4oiNrN@>Q;D6T`7f&1dZhJbbdF^D(LFLK&!_*h&@%xnb7Op-vW5?5b +zlP^{so5qq?8jWRX2X+|NJ&kv^8x`Y9ywiLozW<*!u!My%X0N}H&n|SQ;=R3yCwm_I +z*{`rK(rG+bqJI7Z)+c||o1XdX48OK!_3PNxw_tCr6FOQOV=utzG*6Fz#)7(@R9M>- +z^zG!$4KGek?z@fVfYb2H2JGKu!#yAQlw#~cKL0Py1?r0<@GbGzd0dHooss`)!toDr +zEcYX$-#|Q`hkZ!lBL@GgdmX*RuLs)iNUb;R$1=Eg3+)w!@2xxaa}i@$zdVex`ky|s +zmflH&@7C6zM-2C)9iEBoXY|fhdN!kx# +zrKFPcGEQXv3|v!s^fT@GM&Bj(VJ$t&+_E<9`Q|s?$Nmp9FFn4Nbd6Y(yv))*IB@il +zJ=2--kprLEj~y_uL(^!lGUQLe?~k#6&+aRaIu{q=Kd<-=aT?w|i1)dh((J%g{Kgc& +zkHIm$2i=f{-osY +zXBU^I`M6MjyyH} +zy}}=wZ!b{Oj~2dUzRjYhHy8f1d1H~9zOnG1%o}E?>6wK;FyA^yO(z$=XuhSm?}mX% +zFPKkFdfxmL*2BL*8%$VxJdXZQz<0Ggv3thU53LrdaL;|C1iS0u&}$+1pYKNgRaW +zkZ)-+6*%b|@Yx4sTnm +z9&cQt@(s4W8z_$SB8Dh_D5fZe8Va7QzSfS6i25K +zM-2P+ytnIx3j40-@jkf&NI!@4A*7$G_~ouAk^X*#0sZbqycyB=CiHz1`reMb1G~O$ +zHc$*8-+{a*kS;|&-c4u5|AhINLNQXc>nZd7hzp7jiWQ1GO1oPSdkOKP;D1kljd+oL +z`eoyzS&UhqtotGM)?>41&Q|9e-80YQcSLW#zx+ZUo~2yMVHa#Vw(wP?U%|~j1MvNB +z3#+_(x0}u0?PNPX=JO}!L^o^Nb16ri=cg{F@1t^``DEQ|Pt$*Ln6B_mJTPga=Y3eC +z9eNEm+>2@7Of`?@9*WgZ);)_o(bRXM;amrq&A^}CeTw=id>z3q)Sja6md-DtdY{LB +zc4HyO`^(;-`G10?>5VCP-@KyFeFXO~v+AKszYPjt{(er!(q6_px}L=~duXSvmGY%- +zjnMlL^FuGLJ33}~E>MW~x;4>viO{_XGMe+{nu^vmpR7C1=ftP%*z*SKQhI0XF04`9 +zl|_#vVUbW+hkHiYJB>BB-G=wrGW`dAIJOyV-zl`Qj}(j(uORON{PJA3VdZ_e_k0ev +z=)ZZB>vtTZdMbG4LeFO=;QT*=-lfb*=TE!w-J}EmpT_;c_c7m|#=YJX=r@Z&!MwWT +zV?8UJUzOD~aVgt|^dylEA$^5N`#)B&?~MgIxc8lJ+rfTtDan4Gk3CcMCoW>2)Qfty +z`y%#2eHq6jM}IKCuiE3S@w=j4i_7m1xhdfe1?t1zNF)?yY?Et?Yp&lFtev~2A-bk! +zwKot7Zz~IjDQ~p$9&fw_RnHexPvUV!T^3)&Qtk5a3~s+6>hsUt?yH|$iri(PaKIHU +zYjk_-qrMO(1WpY>Y18YmM10%5WW*PAx!nz6)R{E|HwQyogO<4VguT&*a1gEWc!d$L +ztTEXvIVr`IYD_bv>oas_C37${mcm^HCIZYu0KTbXcw)lvOBnnRmtoP2cU%Ff0EP*c +z4rBnBUKrWR0>%K@0MSre3T!E`qk|0{%ITr2XA^K1jIL}Ra!UXg&UPR03N#gbm3W_$ +zj2EQ>`V^)Z0299VN(S&(BP9#SOJ|A&D9&Jt4XBbp1)5L?KnLgn17HM9KoXD)qyVWv +z8jucT0A?T)$O6Uyc=W(>fLvfKFb)_Goz#9dJFc1h@hC3a}Kg0VRMP +zZ~&!18L$jk4wM5cfE$68z$#!hPyws~DuJ7TwZJ-HJ#aH{3ves20oVxK2HXzZ0o)1P +z1vr5!zy(wTZomV0ff}F|r~`b!CSWt*2LeD42m$rL-vD7C0z`oZU<-2JlUw1=tNd2DAcgKs(R@ +zbOK#KH?RlT3+w~F1@r)q1HHh0;0fSK;3?n$@NM94fv17*0M7v51)c@I2mBrI_rO8m +zAAo-Zz7HG%o&%l-UI1PMegOOv@Xx?Yzz>0c0bU0BfLDN5f!Bb;!0W)j0{;da0e%Gh +zJMd%RC%}II{|WpQI12m>cmp^F90yJSZvt-tCxN$tp9B8|yaT)ooC5wE=m$;%zW{y- +zoB@6X{2F)s~eYway-%1f?WFlU*8z +zfE$Pa_XAG?uK~XVC^F)S4t0PYkP%8fPLWCHrE^XICIj<;62JxA2fP9(3etcABMOY* +zeiySNfbdq*fh-^oumHt?4X9#qMA5)Qg@T2`MWqx$sR@*pKU5X6Jzr-u{iPbV=+I!;7igKUy{^bPEJlrGI64DWK^Po<}ze5 +zjk8=iyLiDh*Is{vt;A8btbE0dD_5;vQ(1Y_+I8!1zWJ70H*DCr@wVGgs%;O`A4t-n`il3V?#aU&Dyf +zxC;$-duwXze4Eglfgt)2y@!6I4x^r;2Hkx(+H%i5XvV$wq6Mfv>P{6$tx;vFF4dH3 +zNj2oPqnhzb@e1*}@LIHR+Rf=PPOU1nacWnoLr|xnE +z1(?AmVrI$1Y&MRKWx1HyvN79aVWu-<=1IqzC>66%GS*5atZEFHp|F1lvn~C~4NI=S +z?%KtRuDN>Qg8B1`=UT6tGkey|8Pl(vRy1|WWJ}?sD+=-_PRJWSZftH&_L!_pb4Gev +zYD#jF$!O5)l)3fckb7>#>#v#XYG~ZTlzjd5`_R7mXkanEs?4B7>5`!&jemx5C0&f9d41V~%fq^dYqu>X@d(WaC;CV0z(6ke +zLj3UF173+QuNuJ*gFgk{5B?&!;T-%;!3(0eB~rHI=TnW7l>Bu3TLZ^fQm|+({6d)^ +zlFb2HAX`ELP!V7QUV^L=oRwTvVzzwC(4)7v>MBmXd&~?8#8kEpI0HRYmlN6A0rbbp +znk}t{Hho!-t~~Ylu~TN|NV9XU>eH(3SxVN4^Ou;jT6H~2S?Vb|O&Xo!Cx8X{@*E`V +z2MQpQt3u+x2WCLF7@P?^rEzw05tHR{b}Xb1St(=(sf>tTV4`d};T%J!2g`*}5&%%Kv)j?VrDO^2D3e0-ZBaLO$z- +z&3$;_UYW*i_9?A-ZQ~uCxm`Jq?(98dmbZ`V80*-ZwJ)>0$E=^N7|S!(n97$YTGH)6 +z#!gdt2<%Q%c_1-g?W1&DLrq%|8#oma`OgAbJ!I)z0$+kJgscuS>O<+j71Rx&T!6li +zZ;Z1;aGC; +z5y{R#_B3Q|T%yX(LpGxkI0qSC_(kpNOR=|iC>`xeX=|6#)~S?swkch$ +zilgT-MYrq}b=$Gyyv?Oh$Foe9j2~1i1gElUKT+>2>&bR><#guio;mrJ1AWeWTPRRI +z^<_`?O()-CHWZ~Vt3ShdbCeujVm_#}j%yq1=*;dKQ{JB2k<*>ECv(|e^S+Fpbp6Y^ +z;oZn>9EXjE9>&;!u>>|+l-BIFF^=}Ej!Z{)`ku7Xy{Y?B$~(q#}1r|6dsv17vDMoZEx +zC|U(O&+b8gkWcOACf(Ll$|~ib5G?_AL1*7yT-S6nG{kpn$T<|}6yXT)Q~Jc(+EC;$ +z%6C1Bv3DFVe^hCm*fzn@p4Tznu{V3)n9}atJvrr_wu3MY6|mG&r(cHOcQrSYs!rj8Kpg@byNfDoaG&9?dguLl+IMgo}}*Nsy9!RoP4X~=Wol? +z@dEAX9ci7ZT`Aqkdy;xgC8=+ouydu7v~sCW*B;G#OYi~|sy-3PqL9sjEPkAR2(lu` +zC?1IJ0QNyv3E4umo|UbsZ7KF1{f)Ao3Q$@F7Sw~WP@_JO@ZK^`2smn<9ZBOn< +zqI9=`Do2vHOVUb2ijPyD(K)dpy&T)$!OLDZEI*g7^Lzmv( +zqmI;a+*aqxa+EoQGJM>@oYJFnz?W)!J;-|zc_+yph{m->$aZ1=r?HMLGuw>i$;K5) +z4&#j`BYros%3xfrH&*C$52cz`n95BRrd6iZrW;Kwse5iRGl*8uAs1JWPmjRf5&W=_ +z)=xw;fOjCHwMCQ!CFbH*Q=8GzZs?#%wvwDkLyWyiGA&Z4fVj-JWOyQge?n<7p)!`#PpNQE@Ir}RvQ9a>A&!Pa@$szRC~ +zw#RS__YWOvq$YTG!JMV&^(}dK=Xj+0nG!N +z2Q&|89?(3Xc|h}k<^jzEng=uwXdci!pm{*^faU?s1DXdk4`?3HJfL|%^MK|7%>$YT +zG!JMV&^(}dK=Xj+0nG!N2Q&|89?(3Xc|h}k<^jzEng=uwXdci!pm{*^faU?s1DXdk +z4`?3HJfL|%^MK|7%>$YTG!JMV&^++}*8|vmic_C>U-r3+^fFAmFPnej-Lc|*oAMo^ +z{Qa@WqgTKZ?b@w!+T(+$UzDf)MTqcPOimL8KP&PJ#Iel(!=-@`iOz`=;}x!)=zZlx +zc$F!qVo@JaU3RlL7R})A;v_|SO)=5g0o9;TV-{gwzCRhS5amRBq7c!0LW!hYCS*O6 +zMR|ISE)l)^ljzcbTK|4g(E)KR)EQn$&fE8QA|Lz9@nhLe)yiM*^StnLqQxCddLl7r +zdn$90w&%PVf4X_+_QT5O$XnO{7Ae(}P23oJ9%Vhg!)S5#zN +zRXh_#sDJ1^u@tz=ONx{QF6<>Bf}~XNk#uA{FBa__Nk_(bi-`4+bSlL;@A%XG7%~tak)I>ao*= +zWq-V;n$}|vioPdCl4r%n$M{^@Lr{*3L*>~_j>kjgIc%isTaULszbMALq55NE+D$64 +ze^vSL{-PR=e%=%&uN`g{AJUGzUZkA%#v0vD8k5(%)Srz6)jv^A`$LetNlsXzfQ|Fv +z{p%3=ay}O7Y$Fm%T))W6)y5Tu$B&om50^g*In_tb`@A?lY&s#$;_bmsfZ)_${X)+7-heC%0YGvL9h~_78A#dqQ3h*y +zj3;6rv!PFW1WGyW14mRLe2nu~0=flUPZhqcDx#}UfqZh!`gII{6{20QfjHO7bnQC +z5b{bPm))~S$m@k%xSQQ1PiJB8e`N0m3KjCD$VA&0X#eOJo& +zsmGN<|9K%lC*&p^6WRD@ChJXz!yhC6^RA175AuSs4{2w^cVZ%*c=s}P&OJ0#U)njE +zVCT1&vD5$XP&=POpW;W#Pm1g$j0Ys|`X&*E +zS4)JQZwvXs0#(58p^$uD$oqs`&I?DmeE7QSO`%^T=Eo{w|5G7v5q5CMDHk`sRG)KS +zRgde%by^HLt#hQ`q(1FEqhN=(#P#q-VQ0OtFQ?C~Lf$9hHdmatL&z%=>hpq-7mEQ# +z>c1uANAFV&SVV2bZU@gQ7k- +zJ2-i0jJv^3VI`~$itjY +zPsmy84^_MTPERxxjySP1BXfrW^?q;E>#-J(tl_Nj1$|CeIPBWy^ai8hZLB8j3V5BK +zhCpB&io_i`p&1>bx(P)aqCUUX%_zCu@2hrtyfuDT)Eh7I1*J%2LWwFC_C_Mmt8Vc5 +zJzV7RI-{;?zZZ?#FzFY;#5$ +zs-w_j=pt{>@@<(Eg3%E9WoplX0H33)9NsWl*)c6}B +zb*h1wOrFqL<3e0S27MH&kNN_>?Ovge&~i6W=Qsntpfli#y6eb&!|nK8+qaD@oj+V$ +zi-vhdP!D=tVJEMF%O5KxyC$*MVhY~IXxQbHYSljUoAj%ig_5;Vcwl&r=-(kZLObFQ +z4SFR`=B#!_27N0Gkn*S*j9f>xN1Ta}bczsiha#x0xCmsQAcnXk?%3t=d&3LnBS)m1 +zwcemN>>E4|dPBijCLQ3~j$+GI5A*mU^}OFi(U_JG*Ew_7H#lp&u4n^-8|EW!L=WxH +zYITQ#5i(GZT4OLl4AM~IbS_(4vbxM!R#ED7#)rD}xU96ZI@A#K#A{I0#~-`=zS^MI +zgP@{|gz-@&K3@zS7_j217_Y!DqrqVz6pqhKtWSKNE1X~y17RfOtaAn7=6FSEWbh>M +zxkEZ>czcHzc6uA*%hSjb@CHKRZBFh27lz>Ye0A`U4o%2Y10r5MA&TO&q^o%sI7>HH +zl&oIih*wA`n@~};B7;sCT9sNfLsk-lDI$G~*N;HKBM4_%xrmqY(zOt+U+oZ)<6ORK +zjlE=*bIr14>&n(U*O%B=l?{ofAsMoLkyAE&MicgWKFK+q?nW0~Dd^GdLn@-WCBAB* +zdT%iP7_~>l#Y?`VTX-~mNgH&Lf6?xdPL$n)g-v9LPn^!$00J@+bz#1AVwK_@lKlm% +zE>FvwANIOD@h;_^H%ixe!{K1ai2&q_j`&i{irE~?81QIV<#jR@3}Izd8*H%hHP2N( +z593F5u1FoTdbR~oQsvRGnzO|lj!+zNHF4lXUfAn*Q2~*v$HK_U-EWOznq*d5)gdqB +zaYbFs>a7#&xH=EY$|JRex}Z}_$TXFrQ@{YPjw^u4$y$p!ls>HdY^vpITuGQ+0s*WY +z)k^Y~SzXa+*jL?v)kysE`U|J&IXZ4Ra4)5jd`>5Mk&sI!@2BLk6^TFMS+guJpXW(# +z6N>mchm&02i$mo7^l-xpvK~#S_x2!+Oh&%x0$?~!1lGG`n +zWKm&xepz0&KL{NvZzpAte10jpC9eEf%I8{xzbk0OF{$9TnUlPqmR#QVifn!^nI=wA +z7++pK50$*1l;TL5ZPnMV8Q=J#( +z`-K0b-(PtV!@*g2V=TB^>EHB3|sW03qtTXl}WQd`L +zt2BxFKP@yYvVbs=GSC^USvZ*&caa;%l2&+j&9pQI)Nh{|PC? +ZQ9^b2<4NheI5jq>K-J7jD3EZ-{uco)TXz5e + +literal 0 +HcmV?d00001 + +diff --git a/native/src/main/resources/libdeflate_x86_64.so b/native/src/main/resources/libdeflate_x86_64.so +new file mode 100644 +index 0000000000000000000000000000000000000000..cda376fa2c627f3457d3fb5ef79b2791f47118d7 +GIT binary patch +literal 61176 +zcmeFadwdnux$wUy*+8Pvi4rxnD8X(|iMJ9DShj%8;NCFX?f@z%tyr~c6zhdT0@zYb +zoSlT#aWwVXi>LLRdfxihp4XnEKSWW;=8^#30#rb)8mLN!2m)3JDCGS&|M^V +z)nlS;$_F(!$bCyoP~phyAHx1}*W7oP@}0o>pZ&|B{j#vXFT=~Ou)o|jeAMGBp|gDc +zpEzk+XJ>EW^-=$X98WyT8yQ#GQOXOMhojXO +zxQOte+e7_^^ClSUx3eCr7VOhV8V6?c~aD2+Qlj@?1NSYp%KJwmWXWW=_p@ +zvum!oCUQ+`YSlG2+&KHjn`X|bxpDT?s*7*EY2A+ +zck9d_-59C4dG;OilJl>>aaPUDJ8q9$a^2kPuDSk>+pd{=AJS-+1Hyw2fJ_ +z@3Kdp#@nu+b!UXjoEvK*w_SHDb0kl<-7)vZ2-9%= +zd2{YK?}Et0bZY#?*PMS|#mL^<^UoUvRlzX&|9t)p?hT|w-+#R3&*0rqUgzZ1LqB^2 +zF_IrC56fy#omnXDjFcw$%gOZ|{f-FfwltL6x$<3MgQGk2XOUca?z!@a@>%Bx^`;+D +zzU%)4j>_oTx4_ZZ+J&=C*@i%hc8C-E0=eF`}-RP{)U0SVc>5V_!|cP +zhJpY8W59MkDzM!F@4Oy~*bOZ;c|Gm6v$9}C@4$LSZRJ(ZneX$jIkb{HdA?cR`yD;} +zo%xnLi}~TV>*w|IEGCfOqMz5wvzQwGANBJ+JXbgKtp99p+?y2G-T?W|j(69b1UM7q +z9&ieKI?SPUQgKCirT+ic^+)}aT*?pG*+coZ+GS@uzhxV(_PRqgB{XnS*g!!~N3}UL +z(f57bxS70))P7@{?Tp(iPQ9%@s424Dac5E9c{Lz*-poI(11z46t8@M4uKtgCS$&>4 +zG$T+&ny1<7jmvHIS%AkuJyS*q++VTX!M1wOR;{*s$t=OBt+rovbSMir?01~tzJv3HU5QNPer;IL`|&~Vz2AE$eI@9B_doRO +z|I>rH{@cIUe*sP9zSr&d!cZK*kgQnK={ypNINe6gd20D$p`M2IHK#cT^5&ldR*}B4- +zW=)-zP^*$^wT7_%qn`sq+PfiEoOb^ZYnKQ2q1a}w-}cg(Xj+AI)J6%wIW18;o~_-j +z)(h=)iNjgmO85~5t|~t@M@~%>^IEJ_-G_zdDfwnmySiNyqZiw+M61iUeVH= +zaO1Xni*gSL2brzd_&r+-R`Di#dK-JUu?2(Ny$UYI(?y=AxpD+{C?85?-ZIZK(xGaMNZa +z6h(IYKy3#!Iwj>zO-2%9rp5})CWltcdoui+aBq#3ES5%-4Li(+m*vIKhy}}lzofe$ +z^LetNr{-hp#QG*+~AK1?F +zJoC9r4pBMj4!PGdK1-+}jurp3w#8B(3E9M3=fpIBCe&72y=ytGM$&lS&Th?v7zTIl +zJF75i2LS0u~ZOyQ*w!UXwv!ajxEN_C5Qmt+}Q*6asOy_edHms=u$W@C) +zTvn-WOTDiAD)pDNTE3xUUpxF?B-25~h;Cl_%cSOpjsR5wA0(LKqNLiDP#;?5w3bZ{ +z^EZ5G#rK;HcY@H|2$Q@}+pX-Df~2uJnICnPZ*36rC`cIVgx@T+#@IbUMcw%7d7mWI +zZra=Mq1p{4CE~uB8A8X23V*50g89-KXZO(Z*TPEPBvAUQRrsmpd~BEvKcJ?}uQ$m^ +zxC`zrPN~=Q{Q8&iq7NTxJLrUbVnxOdK_|pnpi%nU{XIPb*I_dbbrm(NF*A!0hiP@d +z?}69*$T^_oBT3jFtp$Q#%{NKm0bz2>OZ8}E3Nci)B;xDLh9ni71+fxsSg!f-q;@{)mCf6Rx5sB-uui-LcLz-3l3B^yQTP8xnb%PMtj|> +zEvje3Yd>^EJT!C15#VlZqDqg3kq*I^c>}S6<>pjkH&C +za!S2!U6E3UtZ7y14Qpyj{dL;q=%t%Q;S9IWJ)2>q>bvD)n-j9ObKonss)^Y)(2bBf +zr73UxHeR)+DVb97cEt`G)R##oT4$wIXS%$@cDe_djbA^O#@uL3y=iagunRk-Lb2NH +zygWMFnNQ)qcBf-d%2;W;Gh)T|x-RE0`Oar~X66y(V8h#H<~1#@ +zh8+oS>^Jl`wXk>HXQ!Kuf8d6;Q*GLqN~uq!iH_8U9kf!uBDJrbW^C1-9@d&JZ>MKt +zA6mz}f0`cDIeJh4wlO`d6I&@|Y_d0epx%Uz8)Za(e!6f+m3qh7GALF4YL)Sk(=*6y +zs^D9v2d%S9Blm)|7yXv4&bN&N@~>BPVb^he{A#=B+p5BLM_!mVmfLD%JZ)rC@z>{V +z(}vNdMo9Q)UdXJWgr8~_J$Ab@=bQ( +z&Xn=Ft@haJQ+wEQ%Po1tT(nkgD&GLM82aAUO0(ffa0DZ(`2@hyV;01Qo0&K0f*!1e +z-%6p5!j9%WYP&VDGWBNNXWyJV%r~HrX3Jbu5=%KNjbNsoY(d@rZ<?L<~g7xXXVZPq`9_BTnwk>!>K;pR+ntH-ANr>JGesJmvnO7#dR0gZm!*_ +z_@;TU_?tzn1bpcD`C`wNF(=NJq1YC)AGXU^!vei*lYf_Ena|xC%hN`{zBj6^G3Uer +zxhh`{H=o;)F7LMEtIYd12_h6TzFI%pP#*TDw!OGX$I3dV{Z;HY?o!6by=m@ZRnbn^*z`3hTYP8%Ke*$I&ZgKX!;*yBtt +z{8tYKZTB(#m{#3V12K`0^jS(9z4|!Y3Hd_Q(1ks07xN{w3Bl9yh8@C2Z^J#d+6QtC +z7|d1&g+DTX0J^;^%glz?m}r%?1NI6U%lZZOhHdt~P4=*Tw$XiLgLf0&)Ss^AQ)yYZ +z>VH?u#E8Aj2>;AQ$J^%9ujl*@bqpYs>A9474Ck=ZFVTXXp(3fQ)`zRMtfJ0)-UFwJVBkMd@UZ~^yI>#f$8}2+7G7+wd3*8*pGp}ysxlOtZq@m9Qj?)U3HJ8s^HwDQyCTWn`P2>%oPrrfbw +zLv2%U2GY_lJeXFmgG0OgbuE^BXUpA|d+&XE+#7b3znt}po&BcSG)jd1MnU|-q6_F@ +z-mj57qi>0%jlG~r1b#xj)r0!9o;SggwHR&@%y~b`3P04+R9*sydXe_(4jPJe+p3R1Qj3xh4K5iBR$L7Yn1cP9`H#;A@1e^VM +zXB&jMBhTr|yLe&I$qDZ=BfRQoY=`hM>MS21w~XUtujC^CH7&9#BhH6+t97(6$mtrC +zHgA`Cmh&>;uhMXR-an8!r%YJug*X?aZ7-gxJ +zYFily4}_)&6eYdtU)ynMc=g5erw@3%|$80_U55P?#{CoPBxyvOPhBfMbzL2;JCWE +z?z1z^Mn$K+LeO# +zhd++$h_~1NHRU}bYiC-{v_=ZW&$JPQlsA_4mgk|w!us~&g7A+oE2)uEx+3+P5I)RqFF@hII&I5kKE>+Tw;o#(>#Cn|BSzhZ9sbv4 +zz_4~`vYg?cL0f?x0ux6=y6w<(yGrQxt0CPo0OtU7JBbEzbZaXbvN3Us{eKqikMRxx +z1C0L|V9`G;m +z?mZ!(mj(9~2%SUikhze%KLj4q(D@^UX2ndzCJ>sO)k`dzKyuV^I7MB?=lUc0;vzwr +z9%KvBu%acf2On-RTKoyX*nEV)V=WaWW=%_}gGabLrC;juRB97S`!xMs9y{mq6w{)f +z@!@F<^PJ1G1W}Exr1q^Gzh&Q$m<**eKULVJ-bLNp0+2MsF+#*<$DlO&eKY{2RZChG#b8QNbtvpI%S|^#4h#2HMpkb9WZ$at +zmvMXW!xz2_hCMB_q_M$*&oPaeGdjmV +zeIzjV`e6D2xG0={yU_2Qd|Rzf4{Nd9L_ +zt`>Jli@6_&-FGzqrr(q|J3Fn1MTSq%I{Q`7to@Xb*7NUL@wOU*L-&*{ +zv+z}B<9E)&@0=}ur`ed)0<(w!W+kKjuYQl;8qr2ioya+JQH1`>?4>!k37@IeR^x$U +zJP?eh_whlmuLHdB*kJgSR-q+3!#^$ +z%MYN~&HHb{rNfkZ!*U;bSmcV@q&6aPFkJSwB8)x+l`FS)e)?+phLu}i-nVAu)~Fu*1~y)%p`IVqEKB}g=%fDtbzxjACU!n;cv*4c8M$oatd*v&D!$Tl<`;p +zF${*o&{YY`Y_5`T-*I+|yO^KKb{=Q7HUw^G$56}ZFwwpx+SRl?JF21xwVbPo!GpVo +zpCbh)(3iC^i8>pfnR=>a{lK_q5nBi37=tmX&3YsoX`Ou=A2NG8B;_~Pd76r_)Xwd +zSF9)Gn-6CE`v9*WMAglX_@g($)}cQts}YC$qls~mUWYWe$0L;+aY=&$eA3_sr!=_1 +zD-CXBfVzPK?U%|uB`48jKhN~sv|88enf?d}MC+lN3tyF>G3ry!ChtNv+w78)V0`b +zOik#crX87~ooV&)kv^(?SKPUP@4kzV+HV>fzJ<-^J2V5oi@!RbTkWr|%lWJ6<0AbX +zRvr`HC3vjC4K6D;mfD>w2%J_aTbMoxOkU*ws}|o0DCsc2)&H;ft+&xevqTV!yUyru +z9~WsmT=eT2lq-{hex3Jq(XE6xgXp2z^aS2jpnCBj``nXeF~f`f6iw44@M-_62|bTg +z5iNn^wG-c~*!l2VE4Qxxl(oitj}PqVv{&H2ib>nAHSi~tF%rtyrQXFN5-km7d^jjo +zz8l*|Tl&};f!V~g<9^x0e4O4~ZFEtuDEe^F!o2Xi!OTzq-_iXqEr0O88Up`od*1*L +z_rD&FK#~0bu5Lcu4{H!V?C|$r;*ULCNzIhfAJDHjS)pIo-yhQgL;GWRVk636ZY>tt +zqA~fvAArmU$-*!lptt+eRkkQ%ZGx4r5KXf~{FD07KhlQP*SnJa06fCJ((}Jc<{wY& +zPcr{GPwWZW$j!g_I>qYUlk6yZ9&EHdF=5iu8U +ztW=Lhc!|jz8|yro+10*K8mqHRY{ycKSm9pMUB+kNyZnHc1coJpZ| +zDR~MyAEvY~UxW7r%Z8sDiF8Uru#GJU^e4U`B^7(YV`tZ&Wei!&c>pe0;eS;$N^iPF +zGx;)q=wHxt=JLaybvA;x;G2VbPWo3~RP;#urtK{#9c1r&+aC6wttMj0b@%3n5Qjh3 +zcJJ=)IrC2HH?Qc2Yi-}DaWg5m)u``sSxRHt^S-R3{hwr#1b+O5rg^PjTY +zPqhdxZ#C2==k>h3**r6GpnePW3u@0Jb70`yYxUZV`A@35{&h_7y5pp+x^0G;=vL!O +z=phFets5-j4P?DN}`iY$m&PQz!K{<*Y8)yP}Npt|y!8B|E3?DmAX{ +zDZs^lkzTilRl6286T~#uUp~o;K5Dt8@r`p%?@DXFSM5?3e|gS0uLM<45Z^OzELjRr +zwO7=HpX3(CH_rWY!W(a3u~D@t>6TjYm*=(-wWw0r;N#gVEL9rhnN+b(CHL0UgnP}D +zv^RNp!o6wgw97B{&KZ(yc;C!40;l*9ys3D*Z-NtU<#4wkX+FKeGM|1Wp_b#(mak2g +zFSp{WEpz;O4E3n7Ast^``$lr++pB2=PqmQxf381demxSIcUt|y9#+%mowPVorlZ8= +zotV_iQ3l6Z#)|CDB0W_J^XU&0#$OWgx96M-6n*`>#ci}n=F$}L38K=`N9vBL6)kNO +zr%1UhTlJ?AyZ$4Q*%d7*wg34t?$TZ&w$QFWBpLT<)keT}ncT9ld&q9sYi7{zI!}Wj +zGC`&mqcu^qP%>TS;peAg3sdBQ*xrk=h0^HWu0dfwjPV+FbL}1-xpNLpo_8kvZPkhU +z5FOzhFz21*9Ei>>bq*BHJIm=F@B$`Z#GJUC#0ddzh0`5eWn4Nrk{ +z$ecIKITW3HnscadUQs`I0Hn9a{y_E5=eUB%R!Xg|XpwdFbz~V`;+;c2RwQz{@-fuF +zcb$gsFDws%+{(SHqBR5CWEhd8_Z>tBDt^HzP@IvXNFrVeg_;wHVzxxp8YgfDRHCsHMG>OZ;c)UarV{SB4zM{$-S!by~S9!O8Zms*M +z#_okrZB_4L?R8#{J7hT@8MQkCe%x%i$2(nc+s7$S6zaQfu_EwyvQC}o97Sz{TD6;* +zV!?;%%&n*PCr<1c?S?g#K@hfgzs)6_=YYxuDQ%T8L6i-pZ@0Y{>>sKK;Z^V7rq(LR +zS}#60A9mYq_skNilA3MzR8mR)%JOYKtwM6{&no@OuMfnFU7lf|0 +z>bFXHGQMZ_47JB;&6E1MLSxSaH|D0iOAV`jg)oLy|9%fKuVmrY397_R$BC+#nN5J1 +zQtJ}l?PT@b8Y`Nkj?w{Zi;~$z9?-Yt?Z^j{?%&ax^c}B>70oVId+0a-CK2B{_q_To +zx+i0+YD*LnnJci|lk2xiS)%YQD8auDU&JkWCio<%Z5itmYG+X0x8!~PneqksVIWKk +z0WJU9C=*Xh5gw_JEs^dW)>^OczVr01StyEnrv-n#*x8Q!`xrJVcB1up +z4L$Rxx+P0gdoaRm=P~38@vr2?kz_@N)iH^}wW?%EBED+g3#zlLFyLGA&%7x;!1FUE +zfL6%_{HW`RK6%cYii(!L=b*|^)^iO~@G2v!4H@i?T~fbIBYR=dw;02A;=WO5hfxb3 +zSW!X2wX(>6Edzp$rp9izjn}#gHGN?~M)L#7u|u6@$2ZpQkkqkKj2Um-P;wTn#Ylfy +zbO_{;*rVTcr)I9j#*tOSgyP;@w48Mfao5SFI3?RrP}q* +zzpB^IQ7xoSH3&fdh@dcNf12&x7ZGlk1%O>xLnNJiw)A-FEfbSn%&*RckenD-w&#@* +z+OfR{MTDfi2Dx0dR2Ed1mGRHj{`aVPm_bCoQ^={>&~iSS?luWK>ntYkr;^(R^V#+L +zZ~9#{vRM8xC$3H`Ovfg(WY=^&Vw&8Ob$ap7Hue)G{tWECT+lZ^ZFPx%xt_iwb;zay +z$r0Uwm@c!u9}l2Za;3sf(vPE>E} +z{61B85~|K@%A*Qub}1Kh{lwOa(BE^Pbwmmp`^=|zF(RXU&p1g8ox81J{bGUHwA%?O +zHvJ4H$!^Pw6EhpPhQ=$+2guA>ghfndHo+rfm$`^#eVIXWd9qt6*#iMw3B4SX%pe#a +zqXFA>pc3Lw;9(E_Q1nNZ#0^@1=oW!E{m`K&n(JeRC>GS9@A +zcqTTJFEBe9=W%%^qFD2H?)WH)k)4Go4<=bso7;3MaKIjXEpf-U97mYL5a(~WmhPR8 +z5cqR$joC)+v2_b#lTj~OV60gGd^;B{7wK;Bd=}PJE7Vb$*=r`U5QrvEA&~k55uvI# +z1Db59Sie*}p{~KiKNG+Wjz|$iRua6dUoK>CE_z6}msxKnHX7|TsxpTWXsZzD^Yj=I +z#S{@aTVgJHcJR+xH`M*_Hg@T+4yV#hkPI%O+3*w=210RqKjpW0Dy_jTqib1|7H#I= +zjXYdj$X$S?Lu9H9rbr|};~~Mxp+d7swz6aNE@gJKDDZdbFzK&qC~%(|i|K^PVtY%( +z8cTb>6|<_3pWNqO`GJbKX3Bi}6a1D}Q)*ADd@TlXs(cq*OdvC5PQVOpi>8b(Lvz_C>!U!%*4TEu^RTg|;_xjUC@-Hp#qH8T2|bl_I&J@l{ajjeX#7 +zi6QjsYI)(6TxJ{~l~7&!xrmPKeWD}1Ku|KO;YZ-v1ke5DM#2rIM9UAaGHUXVX(iC3OxMZoy?Sn#B`;IgC=eE#J?{v(*M~yzQ5k(N+#>(H +zOyzb^oFB-*Y-iq)AaC+_5Gca`(F^9HA^bN9)7sv|GTXa*xK?JcdbiA?-PW{A{f{UH#V-@seTALUa#HRhmJai +z0Xm*X-*gQQ{eu4Uspm07#=7lC;9_Gqhpr&=xL*6_h93dgj-13-L%`Qd6VW5!EyCF{CLMMD +z&IM-TDOVEMAkBND-T{*{g3|6?rJ|W+K5W|^D3P=g@=)MTD3B;ysfvK=1Cd&M^c4>y|u<)QYq@Pn;5oh}mW~v{Mi~59Jwxe}sa?av#Y1 +zx(g$o-BUYQtyZplWn*5C6Tw;gLEVz)q&3@nN(Ax?WxC}jrDg8ShHp^V=EyT)6KXxP +zh}zG~z3@{VDT0^wm<>azr5==8wzJbv>zo5d?O(zs#|0pMh+yRNZW1W*-d_?5ww9pn +zDO@7QXxKzoy$7GrccMwO8Anb{!gY1K<@*!)0qkv#G$MakcVqcL{+w(&p#5}U2((x9 +zq|`ojKpi4*a#BRpeu5rV`}hmf10uD@sEytgh816jBToFPygP#-_e~hca=P=)rdzo~ +z1D-){ShoDoWCB{YSM$ecGnPiwav~T5P~NWEU1}41K5Bm<>9@o|r;3Th?V*kEkr@R +zQ4h?HL@xHyspAPMiQFDL5S^MY9T*49rwIlDl31w5I%1B4Yu^<%scv=`JXj`bR7gim +zJM-y%UbNfTjRtR)0bv(vyt~gncrAL*T=e4L0{RcC9`Z)+HFja=I|pXfzGE(0GM`r! +zd!3d+73&FCN9NM^>y@PUbsd-EfAn_IiPGVnR7IgYsEwD`2nO!LhNk>^4JJ0hZpv00 +zsKNuM)vh51AT1uMfu{pI3_*4b^^X;7h+-0>N9+oNtkMC}9RIf)`&bEg_Mcwco^Xf6 +zUcdzC!N{p((~t!7ixcHY)j3eiw`Ro!x5c%l2=e>%5Y6@piQi +z-5h`2bZ*tCNW>;ePM5aLyk~^N6Yf(&9AqI|-nR|AaJk;U6+GtKh3)Qx!n^g`wV}^0 +z7Vp~_swDBZxtc*CfuQbjb|E@pIT5DMgMt(I*j)7BE0aQGW)ou*33ey(GFBS{40i5R>YO(`<1 +zY@cwlYvc7tpgAnmb3Q6gqrd>m5-UXhJW+S#uZo}G?4FT;Ur<) +zX!v2i{>oW^7pc9ff?s-}!L9%q%ID{N3AXP*!wx*3w=;+qi#I8x;Tee(HFbX$S +zF1*OuKVWV_>jVRtF(HqAc-0A2W|Q(jVg)Ic>h&t)!)i4pFRiwAR+E!k(NnSJ5^rQ# +zI{t~7sep=7>d%XNU1{I98cjA6*joZR7AOxmP3uv#SpfsS_`* +zKo!OOCiH_lHin6hLiCJ;96O|Of_m&hk&ojS-aDAA_XyiXja2;s?HT`z%_g@}P#=w% +znI2fdCZ(c45-Lm8y5^*NBQ5_0^k%zGA-2_XcSH?|S^HLg&YhW&^kipy6jL+h-TFwh +ztCWP0Dz%?oZmIJ95N;~Ilg%282M#!y|G#IXFppr$?;QgEhfWq?+0(=Qy{GH@A-@)8 +z)%TXM7Fm!q*1&403`ea=8mm*XDeEozNEfcBsVjOWxJ8e^R5EBCtnUB*@7V+$bG% +zAT$Iz-3J|DLztQKK)sBINLW^&1UP|1k04#xuB)+gtvIN51%#Rf1^MF{bs*`e9pJ$- +z1YUpzNId9Zk!ZxfqJ$LoMg!_=5Xb_swGtElto3}IJ +z8@48ltVXSb%m8eH`_QlJ&?&*2R3YB#bIIuVv!gg*r_JRPDo4KxH3x&xXryD2hCqNg +zL`-3{%W#B_nQCKW%2`$5uaz}n!Rg!n)wm3y{0Ok@e+%?7e8WF6xXJSixgomXHbwvj +zp|_#KTy$SV!`K1=s6jaytj$rp212n$8|zwWGcD|+$QyK7+}AS0R-@s`w3=V6<`*2{ +z4MUb-&p(rBBm63Am4%i8k%>jczZQWsb?sY_nkF8?lYl29$~IgNm$1qk~pjJ0X< +zLWp&Kg9cM-XV>qw>=T<6@wbpqLc@Hqn9qR)UM7SZ*y&_Vk=x)u#XtQbu=pPYP1$R@ +z&-NZ|IVC^RKN=gXm(<@qL?VpU<{bmR1@2|qOBYLtIbq@uiPO%fq{Q}`TEr6JUdQpx +zaxwlY&GO22{_o&_!VT@UokZkXPKgpEljxH-%DY8G?fdc>rmRz@)xf**c&!f8axZ#NUG;~w<^F&VqaC*eu5{fh-qdHr*qHJ!kk +zQ2vtc6{VsXDidnGEcz|o!t9!N9QVi%AZ9lf1HO?0pDh?2F5}mUAyx5rX8*k~==6lz +z*!dl_2zxW_v&kdNeNdWa6|sCXQ>F*aK^}OZdZ;9McG}t#WZN(X1*jZ}rXdEz3FTh-b}AhT&GncbWJ7j{2ax>uc4`XQFlm-pm96NxxOBj##+MX6A10(&n<3l(E?w +zQ(U_NTopTh6rPDX`-hs1`|;sY?)+%VokmD~liBbKdZ3_)f+8u%oJtL-8B9g&iUUOO +zUR;mfG)iJjUP&Bt$0=kGM*(7w3`qmg$z5MO5d25WkjV*kLu|xX)<||wnX{!M>88gW +zwAANLHU@OFEYkhD{!ZxSC`~IGlvRv+uADnyo6Fj5>wM`Gjzn-}h__1-4e7eJO5^NH}$7$&tKpG4wHR9m3SWc!< +zgbu-I7{qckn;Q7*Y1Un*n2qlh(6QH8c|Pm#abY3nRZ6V-rNYjlMCOiy@@11(B{>>R +z%+_66{RLd2pE>?!LF`)fX{Ys0PY+8g4WrOB!npn{Sq&3Gy5epVXz{8AgNX)++)OCKoI0445n)47mS=u5lr6Q=lLrjgI@W6 +z6#A4!1o*_v{E-{P0lUcGw8q41{{&qWJ0C{@dY01zuRK;P+<#}(?iB}gLjk8Dh2r3B +z)>4rJ){-2rh%~T_4T&)`gnEjAym227vQ<;B#vj%vn^2j +z!LcTlXg`Lg{6zVh#F!zm*=Lw9O^rp(moVqwJ$qz~qh_2vLy5yg|0oXl{TrEoP2I9% +zKU8j)0*_EPn>f?nZF!Kk1dw~_`m2Kur|UU9{}1Ro@BbxT@5|BkMsO=U1GU$b1vGth +zf*!2bM$=ee?ZSCK&>G?576pu6Cx<2=i{_``w`s=JulA9 +zeL&Cc3D#Pn=O{w<3lnbMw}atndVZgq{`4#dhJ2Bp+mi0oZ~Z&;{0mwUdj46^?2+_* +zHOs4kJ`;Lg&`ZxZ4oJthnGF~7RY1=-9w!BvUqayNifu=s=k{KDew^`YdTu|;@FY7x +z;4!!+Q~-UiKGT!546z-e=vi(>j--^2vz9j0o{T+-eKM7!mvcIGBitd8SbUtVTtP?Du}O1t+DU407bfK&`J8 +zfwVwbXFEFyE2M~s#cTecTz#y1jw2Bi7z+rm!2$dS`Yic=*$)scgM9% +zm6tflz|vLQyRIglCE2%!RwU`l%Eo+zXTH +z^JGpbv7bWB(D${9BDLGfesluIJJj4hE>cH)`!@nEYGQdUz6|2Enp^0@PW?mvaVXeDbWlLK&Za5cg2Vrym$!;49|FSFv6@ +z4ND_+vatovp{bRv#5i|RrZ(cAD0?A#?IX<}+nDN*KmIKA1%D6#$T7z{0-U}2T5BF@ +z$gkJyaYuoFGw_xM^D|xGm*?rs&vb!bKLG!9fnPrW|8(u^_QEfxRuxAz{AN=GaG^7p +zwPKBbt@Nq!q$P%iV~v2KbhD#{onV}_|4K3j;ixTZx{03ZUgIRtV!G9d8X=b^_8+FQbK@Bo! +zk08aLr$NpLHAO_kA=vyK#7V?|K(v=&s+4F`WhfJ*2}ZX!g_&$lO0LJFv>e#)zorIe +zHEh`9KSvXs2Z%ex92R$-rso;!^b&XMsgvCqr*Yubdn||Dc#>9C8I2!QRWKfiu{nr#`$Gx5X?T@izRrK1Xk4ggu??{i +zkqpIMLj*rb^#I^%(SHU{eon?V-Ch7b(1V8G@eZ>*x*2Xs|@HI +z22@Is#il1|E5JW*V|qM^3vZC|>;(`kaf@A^@B6-ns!ryqPY;E(AE=^ndS(Mv6e^;o +zM}SC{ibs`QA=6%Te4unjqIBSlfE|e1_ft8CMCBm-zo>O~SplVs(iucjyZIa*Rob%) +zs+B#kTFocwqcx7+4&xlkHyfW}-B$RZR2D_ND-Nd1x9dH!pI~x&S9~rW8zFGfNGWfD +zXd*%}h>`&tzQg*$wav0@pjuE!Des&~qL2viqLUueI*I(Te-3rh6O(lOA))?33UFG~ +zD?up4BEh&%Be8Q$6cTAzX68yD2$a$;Fgkh(v16c%X0HE(0uq42wA`U_vW5 +zCH{&XLXU(3hQO~wG{5RC$r1{XAC2*MeJuQ=4wY~8jX-24WZpIX2}6A~puayse`ic~ +zV_(W&mGOq=4gXgR2y8?-mJ-%%xJ;s(gUCTdUki`=S7O5UF}Z)5 +zJoZ{D?9{sg=}V{#VLz=z`x5s8SOB7yX6+xz`#Xuggzt}|Weu}_y$pnfuTRpS|0;aF +zSihbnv+{-Pb@1Ik)>0HJm(xF^{{4&vUlU1pGcGEKUI<124$7W3M7O>UvlUt2VxemKMMVTEs;mu76`0kIZP}`35f+pR|&9=B~x&@QF-BllMwr7%Nw=9im!+5PUj&6 +z_B6p}!|&?;*vW9qu*3D51F}$)_MX<^a`wTo +zxn(o2B@wnxTipX5AkeMXRTfJL8G`M4)y7uO#uiIWb#H97T#Y^93Qw?}GnEw)Sqd5$ +zfwX7nWt{>lXJU_2nIrJNY+;AAXBKjj6+NAAzRYNlGV%dd`gB=>sq+3UE&Fdop;J<$Ts&{?#V0Ks3TGUoI)*Q_ReD67s4)AP%V6_?kTMo_sYb +z>0A%ogX!#oVJ(UCMTx0c?!|^!?PU7FJ{PmXgU6UbFz2OF)fRuk9@EVc0RM7>GMyDTb1@*)En$TgG9<65x;0u)S^N$v(vng +z63MCr7dI1!H)trItCdH_e8VtsufG$hXSok*P9+=6Ig@T#psoC8He&?qC4(ucKOjpb +zPj4yZ6RKVgz>P;rnf6!_P;q9#7hRLUU}JexiQFa3qCZz$J%1XV>MvZ!VHCvW}aI +zudMY2KoW2RnV_YFf1sYwd4eS{RwNGj{NW=e#@egf!62}mW77%Oqx{JNr-wZ{#$&1*mU_fh&doE*8lSWf}cy}aH8yr +z<^KN)t5)(G5kX2C0ml{^oH^EJq2y;v&kmCK<77{g)4W~KmXmRpFqoqMe!&BpJ+ +zUoJU)N}pd`kZ`|Un(&%MT;}osvaonCR(VXMQRSAh=@ttbHJ9P)+t+Wj +z^Vxkx%281sIbUN3B}VVBnNBK2p#RvAJ9_`@Cuuj(ENJRlWX=lm{SIq)xwO0B$rl7U +z&&xmbf3k>!%4$_5s$jS;IdCl25>nWbD9`GQgauGgQng*;7F{?A6FM!wPY~DD<`-6} +z?!(H51?49kSzZ{Ff91$>y*}?%4(mD{}ZB2SL_%0xQ5gnj@x1>sl#;S_iUKyz8UsU2jaF2IWK|a +z(SYeGx=lQUUe4ZL-3(t#eC9-CbfsB-nW#f~zKnSEW!wZ0@bzUKxd-_AGWdEee66EB +zPz1b%S|=rTLOZ*=PLYvK3AwH50pySsU&#QbC(MZ*{yjX~6o +z5Idnh)TDy?#th?$2=A|47{>gerU1do5hI`N5FiDpP@l-xdm-JiPeH(9qhLK$TA)g +z1uyvdRwLx9|aSI^XfU9-{~-9;X`ce&Pn7gi*}n& +zP(@Zop4BdtPJi0hS@DXz%xJ&^AA*T%(cM+jpMa&BSF;;WjFOKnw^4x0HEpRz*~!=W +z*wUbp$N4U3L`W&X40rQ`a}(pZDV!=D1S!Pt>$l4g;fEQDiWQJK*(d7Ch&bvoAb&RS +z@>mwK05tCZRzghT)5ww!W3OP#e>ib1dZK0K{NF*$s@vbg+%aLy7Bk4S$&vEfnFFNx +zGB>$dl~Wvl4rC;MeGggyX{3Y(_LJR~g{Z*Q46!d);U}GaYixdKe>`_FKQHh)o0M5` +zZ?lchSU2Px33x3xC#+1#oUk$#;*OM*&BAcSolj?br-xccOwR*r4h0p3469XHcf;>C%eRbc#ypli^!0D3C%6u*i=CieTKErvGp|;RR +zb6Xz&Vn;3~!K=M?H;0YOIT)O5&X%S&O##9CZQZH1q}=k;6pgvVEo|9Z$;IHF>wC{@Y*lCQ|zfQUO68h$fcF4OT%@&hf94 +z@q0}{ekYzq%9{vaeFLG +zVT>N9rH4z5%t`XmxSJt8R?4UgPTYs|2J`T~8VnX(uIoL;-13*ONv?+R5KjLzWG` +zm`1slv=clkhBXts=`_lBk^xwszUEW79E&zKF3EIA4CcG;UzMT5=~{u=r5*QY8jx@EekA1bD5-D+5|C3uKYWw2ma5cTNap3 +z2_;tjGT}y@Zh3)7?Xx8pIZU@avyVx1x+OnXhzDV*zjm&BCdjvZQ=QM2)A^X7A3=G3G}2v(X2vrwIu_3K9KF +z2;_&N@zSCz%d={=WYgJZ({yH$%?~V2Uc?=9%>qnx3=_Dhr$+;g=5WaUVaYm!bX1A0P`SBg;j~&^0%u~9 +z6y{mpcx>z018r}1LEHEOz^1#)Nh4h+PQm?$cqSG3kP^#wKz1w??xl~-BLkJsE4#iY +z`i%&_ULR#Y#T^p3U~%-Jc_Q9O*!iuNb`#YT0n;UeEb@eV)_)}6(wxC{Td+3zYd!N! +zdPbm&gjRKbzgq0?UcXvDV1IuhUq}1Zdfvl)o%M&UkIINx*V% +zufm$s?F9J%*OT9X4B5s2H38{0u;*BGZHDY;NQy&@o-6iZdcA-|=vgNJh&duU*j&W1 +zIsR72kNJ>}_k#2#xGC!*G9q|Vn`AxE +zcE^s9vr{I^0b|}Jq>8MW`@X0>Ee$5{DLX(2(H0etE*eaVxwPe7W^@hKWx^L1rU!!# +zIog3E8(9bZ&@bkm?)`(%g7reIx8vXRvmnF$LE#V4@pEU^)U@}z7LFv*2Zzq@XFQ$} +z7ve%n9%ejh3$LH?SPDE4DI-cj3`@+Oq&-+hyV^zIn&Dw0Q0f5m +zVZDnr!jF&YJvhi<{=}6o^MAvg=!t~)Qwg@r{I($mlQ1Gts;{yUMX!V}RXfRZUxa81 +z)?F<1fVRYv@zr;Yt}>TBv`Au3q{O!q?91qfG$z5AQ_ai@I;c*@*WOjk>-D+P0jNiF +z(O~szsB|AG%W2b6(~;PeEJ2l-&KS8xG!zH82$rHAmqPUx87V=)&uq9zvo%8fNO|{P +zjW?t@ZFyy?KG1Xgu9V=!--fmUA?G+dqsX)o&LIpNNf%0M!-h?In1l_VA)_e3u(`NM +z3Nmj2WmQG@(jpM>NtO8`KSI%&pD``4S%OD-1 +z>eL?Z_MS;zba2`mH)LcaG7&|03PYwRv*`!YpLbgHd{m7r=vaY@yVyy9OIYP%C-aC_ +zZ#2VZ>{Akj#ia3|zzDCgEW;U3Y6|*mNDtTdT@_&SNs~j|0?rFMV7O +z8HRY~bFUCtpW_4QSfa12eP=7p@=GP2BhQx-&$(2zo_;`|Tq^M#{XjhD(p~BJmR|f? +z^?sy|AbwSkRi5QwvSV>wM=m8d=#hZ7)I&>!s`xQWeL!fnf`}uLnWKe3vmcWSw7T{+ +z0;>2n1$g5qAjg*9fmjhJjeF!216hsK){p2L5%G-Pu2LQ1fyX}+O@oN*9I-@uHUmmW +zfztSA0KD4F6fwbp&bbcx*Ni*QEIcv-pEj2bENDN)&oEQy51DC^f&K}>Gck;FIFcc1 +z-uE>c0*q_bMp6JK?1O@?^)CXpK<f%J6die`d*YzOvnh`R1T}6JxE*O`3)xRA~^wt-sHGYz!v$PVs +zc!=f`QQp#kRwf(3wXP>AH0KcCyFsF^2|UhE({A)E+db#(RJ_w{_+NaaUL#Z5ioZVR +zFS;_sc>_O*YBv6gHb4Vm%5(C@k3NUBB1MUa&zlqM`6sKG{qI#Qeo-Smrf2-rRB3VMO93eHzYTGO7yw2;#prb_A|SgKdzTUFNe||5!aAPuPgWS_qRX-OMV^VL +z6@R&QyJ${%>witA!o5YKPLryv#_HBI8QnzNxvU@PsH>EGymzYl6VQFsC#N;Z$s$Qe +z2)t1*Nn&f`H~BWHHiMv|{E5(p*&vAkNyMX&(1@8atE>OvjVFzLyW~mJKH?yqHn%$S~ +zZ?^6KH~X6pibeJtwhbqbopNO +zs_d96>)Y0R5n&`9brY?Reqgr?6^5IQ5Ap&xZn9T&6C0Z+{%1Be7fTH8O0h59%P~Cu +zl-t*QSo*H)YkosAc>6XrZ#~QZ0ZQQe}}uAKQ1AZGWD@6!QFgh`0ZA`jroXuj6o=X +zVAg#xG5_MVw}2D&}kHfD19 +zW!sm;-x`0-aEZThHqlYWC)_OH8~`N@NQd8UM4kooEpav2VfcS0uvVH2wrFwBnwi;n +zP$GYQfi;|iE=u|b)<%VawZr!@2V8OZKIS_g58`T1(}J+6UxyOO&4(tYZ$SM)#PLk- +z;G`IFs*pgwbU*WIk=ewnm`Jrbp?5Ktu?c$wTV}w5a}dQVKNZXt=CVkcSnl!@U`wF( +zh&~#Qs$lpQYG^!(d|>|*n`baX2Q;2VaUXxLWi~B$aw$Vjup=z(T}7(O-gSQ8?oRo= +z?_4;Ytu~Fa>s!ni!s$1`%q*oL$i1S8gw#nF&?glv*4DPA*tskx6%fu5hQ3=E+H%LR +zxBh55#AJh*>Xsa~G~8e?@bHocuvHoNz-^=Q36ITnP~hwHHV+pv9= +zYqOXC^86Irpd0=VT~NSY{>zV*g3J{n7%J9&Sy+8Dqc{rXXDQYIEaoPZKk)VNGN$j= +zuMpX|feq4!?@PVFp{KQeu1wo_Xj=kvvgd2IDGx-AOOagPq{y4 +zNErGhd_)05`Y}T~XlBlk8f;HKdR#r=>wf_cUm90`3z8IY)^j~9t{%qU!npF3fX|`U +z|8`tCB?oNCa(k~fS}J~bppu$yBitj)XE02}|E*?O?4uz5cNKmLGR&00%l->(b$=6k +zv)OXBW|v6T&645Zo%U7?`LtbJW#my={bzw2E&BrS3%yZG&R`Bn;1Aqr;YGxe1d9n= +zIhjX;Nb!2-Ac5lLWYIh<@bKak9ViY0%1U>sI%ShhK4y&h939C98{3;zkS>3#T1}67 +zS04%;ZZDayH`GW>!<)t~*UjQzgU&k!-2ZpNrHR2tqI_2x4w%7tn|=E`bK&B9ZxYUh +zlYbv_@*gzQ_U-Q^T>Me*dRTvTY-MFV?2G$5i~IJI0EUEw|6B3ma6Z3ef2WQY%l^*( +z@#3_>?`_r)-(oqQNPi*raX?q7dSV_H~Kws8*Aqx5+5LU2PK)j37gH5r4*w+T~pi +zTJIR}4OoHw!zT-U$w7`_a!S`Vnyd8mLJ*qsy6E#O=Z5JS15EW{8ljvV!8cZdjI4kcgR<@9rk1T +z3%5_ob`$(hs&aG;XU4FXDOf8gxsdIXW6XvhQli%i4&Uz;?4L|rI91jQs-%vr7ktio +zK_w5NKGG@x8OORBMB2yS$VCm_EcGTibXI)3dCwZgYrX_+n=cWD+!zw7oHwx(I&}68 +zEkEFu{F`%tb0^-XK`mPS-^xe**BSft!GBMFwD-<$g+#7mxpl@1m^R{UY(AdMWXt}3 +zSpbdY_T`P_H{tt_5B)%J;40Qh&<}UOj9NeF;J_)8H^kO7Q?BkU?8N%7}UUz^UG3ch(gQL)KW>$J&psUJdrKo^!U| +z##%>z{HO=9971Cs^{1ifjMxlaqu%;3Uc`D(X?uf{z$L5Pas4gd~ta +z5y}sYP_ga4RN1R$>dCBa4pHY|H6%#w@kpf +zLs36%^R_SM*L+hJqqEeacL#47;s&gn`9&KVHpJTbIu;I1FTq^LD>RmD@@cZCp +zNhD$CSjSRT9OsQQy0292GOfzw?X*k%Mq0`kAQ`3EI@yGHLmMxXH`= +z#?4+PIh^9jX3UTyto`GWo4EPbs*j_2+kF`~=T~K&`&1pS3YGtDm*W^4mAm +zrYygaZsE@MPvuN6yh*0Nprr2a_nTdrK2&&#Ol +zkGK-n(z0m&F8BtpZ~FmH|f@IU>#ulHFMVUHp9kO>=(7Fm-uEixb1J5 +zXRSE{&65D{`Rzlkl0i~VW4|{};ul}_^8FV2oxLA~9cm3n-)d`E=i=?W>GZ}{sf|0H +zR^N``W!hg#XUVD$pAtILw|u~J;uqsS-$Kp>PfC&8S!-sZ%YM?8mc)MQ#v8bP)aN7Q +zTQo)Nuam6o{dF(b@GEciZHdnDd6OjV5?+VA#iCxsI +zdLt)+BTb;xz0U;hVJstXpb2C*j>WarPWgr%c=%ndwoBlj?7+X9KzS77F)4py2d*`N +z+!$Eg+u3Ss?ZE4KCAhrAH_|tlC|EK|;2U3@U?;&hp5oB<4ScY3{o~*E58j7Eww*q* +zacvuS*0%d!b(a)S+&^K-#f*QO(&ALBZ%RuXB}O;KjOOito6?4O<*dQo{tS`qc}{4J +zS`D-HBslcQN5UKC$FbWlQM!?E8Y!H$NzAv}Luf#6*~XshK*}$zOC5Zc)KLdda%k59 +z-t*t=AC#Xg9p$&}mzDX~eKXX`yc#RKS>vusOsHQ%2HEw8;}(?5?(W2ijq)LFj(Fzr +zVHQ6uC0Kr(sV27J!D=!oOT|gmmob5kTl&|UK{?E6>o<7KT7`UaGhOO3kLUNYki2}r +z&^!3gEcElYJNuI)8PVL4i2i!+pvjFxNwYZ%t+}7`JjT{DDQ2xS60d`nUrLVKQnjJs +zz0^C{KA#oCEA9lwH6|Y1W|+v-p%DhW$9JF+HWXa^r3Y)Ck(W{DSs}F*H7{%V +zupGDiaDdJG4WGUw&A0n^3=O{JgA=x}z4^A>rF%p5d@wyad-y+c8*bu70aUf4b{W6x +z{~5EOPx~kIW#tFz=GIYVD*QzG>Z|(P>+Tn1tf4d%ppNY9mmN9c)5@_6`;2Hd;qv{z +zu>PQi>J(D(ROvMJr84=Q#dEdT%{TuR^m(rKS~-eYSjCgN0YkGJXAkA=vKuq_ +z&KVob!@Fe_RIaOI>=0GxEZ6$oPvKDso4G*A=+1{9bB4D$s+-1;9!P&7xU)Z~hNu&{ +ze7UmrJ-+@^`v5gDp|yRHa!TUwiQ9d<3@)O#zB$%WR=Gd$&A?4I$z#Lm4d3EXjY9@y +zu|~E;-gA_CaAQKk(tR9p{my3PWB9)Mwo|i*znnAf9lqj?yeK`P;=G1|wAc%>Z&Oas +zypz+SgfE-rxs+(RglezSbf36H)d$c;VUmiV}|gi-Y$>&Hkc*e1TLifvuXntLkP<1G#C?nxO3mOjW +zVNbuDyh@~_n~D-DxAKDmrMq2%zv6|_lF@E5`jGXgHaMlY`NOQn&}CRxd0+%D=tg*; +zVKZl6MEMS34^eyxH@9cF+Sh%Sz^4M!OJ3g3a6=e3e~8%TT(U(;c<*2X-&5lEpIgL( +z$9)jG)E@WhH?kf(A?$NCqd#lh*L>fvp?R&;@S#u9pcV4?d$Z$QHg8@tXI)#OZ?jcjxy*)U +zWHo#uAE=o|cBitb4p+))*q$qU{cy3%vxl$adx8l|Zbsg6Q^k1;871Lo-VOJ?P=^n- +zeEVfHl9L~_Lk9jCp0#OxGk)fK&UP=mJUI<-w7mJ{-j;RZUo9kgt>s9?0XRPd68BQd +zj#TW@-t{Hk<>MQx;#{g<5nJu0foHe1v>g3c%ie?ATWSvO+psQp^n;f7kM3;Qo4>tf +zZ*gnOYe&9nIgsDla-eu~%cV!ww~RirrDfZZcJZZWTUx3P#cuFjdZ1;)y4(Zb(4RK= +zMjv=}!@7*4hg-g_*h;wxIJ$qs&YQ-4?Aowy#(~dpsFv4`9%|WtbSwVU+_LTH7cJjb +ze#lSTLzktJW}C#_>uUMi(GOeNk8W%E_U2uLe}hYHvw-prD|p3fruj^7-Wz3(LKZe5-Fz<&IadzbHRBa8JrJNzz8rBi~<*c +z(O?X?5R3)mz(wF*ommBftg?yxEfpoW`jB4THpuyAOPlq0#FEopa{$Z#h?US2j+uPPzK6D1*inq +zgDOxBLZAjL01H7aSOgY>8^Dd=CU7&j1=N9C!EK-(ECEZwGH^S%12lj;!E&$yG=jUp +z-Jl8F1AYwd1^0oUfS-cOeRz&qewupRsjya)acc7XT62jCx|4SWbb +z0z1LS;1lpE*adcjcJLYa9P9ym!5835un+79UxBZ|H{bv`2o8b6;9Kxd@Sosc;0QPh +z!gbav3vz>K5CdXCFAxWM14a>x5rnbGWhH>)7?=8k!QdotDyh!^=YUb*Vl<_JX`ld9 +zqqQD94Az64=#)nZ2ArbRIR+lf4#pqi^#%Qa2PA+*a2z-ubf+ZVwe|KF;cz_og15ZA +z37!V`gT-JDSjOAS`hsD^iPX?rQCwWFUa^W|zKf#2pCa|g#t!nHbxz8tG2<@2G}V`u +zF(E5^;-sA1DS3HUOr17;#*8bknmKdUtgElNX7=nkbFTIK^Yilqfw^-F3JMAfgTY`? +zQPI44#l^)X&~?{cH-G;8Qm70nFE6ivDuu3xs)VYALP9k{3luFBs#Uberp1bGu<1sd +zZqjtKO}A*OGjyw=+YHrr(2@>X+Cj@Sl_e&6ho_7lcS-8_jLhswIg_Q}QfvxMnXje9 +zs=SoeE~^w&6;qedE}t%(T{0<_DU_6`qd2N4svxQqstk4kZ0l{)wcWPS+F)bu?V9e; +z)S&53L(2`VFx1#VcXiU;9n>_(>rEM#nvtE8H+9BUv#yzaEd?$J7R@UzxsKK>E2j-< +zJ=#qgOj=3`bi)ml<))h`#x1u{0?fyDF&wKgS_O8lrOKi$( +zO<7&JFYb=4&BGom-kr2^=Jv5`Y7a**N#8K^k;0D$+&BI03!V;r(|1MA)^ndI-81BY +z**h+I;l_VO-8TN^(|rb&f7Kk +z{;S@-@GlDwCEPvbjq{(Z`0~_;{QtQ8rCYj*>>m|sS@qoZSDWc2C<#c8q! +zw%)z_L{NML_5B{|_dVqKft2tA$)bji@85p_G!Po3X|SfG2s$BxPW&D^>3e9%52TZS +zAc?2A@PL?DT3%W^K3*DB8ciBTDmPKnahi_T)L)ZSos*;*on)%ia8tF0N7Qh)(})`H +zcKSUnaN_s1Kxb#sM=n=Xlv{TS=@6z``pL8+f_j>nNDq{mkwll7#|hvxFar2M0Ne`J +z0hfzo;EDoFv*aTPE~Z%AuyJ<=n7+7@fET2IRFH3VBNr3}xgnWl$TUPEefP}Go-v7? +zGE)*;`-8#YBycJ?1Dpd!fr~*Jm<9?!HK+#t98RQW +zlE`h4HP9MB&c|axB9?flxxUQh`cR8;%r)hUaM6?`O3eeiXtz?W%dN|Jt@9;Vcab&D +zO0mwjlCATsbFuntYq&Mmy3iV9jkYeJ1S4sIVb+hVv#c|%p_J@&S|!q+$owPii?qje +zlKbK(DDwrFmCp{7%^a_W%TwvPyl^;l>Qcs`r!cEFohqdFIVZfyyNZ-h6!9u9AX=dnmRgpN}A3HHEO +z^X`q+@Mic1cq@E6Jdqb@?1OuE^RTNA_Q7YvE8+58$d<-7zq7M_a +zzHF^e7L4C^=u_oK){5*-QKsyOn%Kt^mk~dU|C#Z##N-@;(up?~AMcHiSmQm3cSmJ- +zlJ0Vk_jntl$9smYi1B$+mc?dz#@6?m=xKGiuZ`;C8H>#48AhD(o+Q$Y_ry(-CRqvY +zL*Lt_hyiu1xrNi5uhM +z=n5pW;65aC;CY-+)aUMx#JUOHtn#_?zuyS-QOs6Zbyi(oGd{ +zNwMUos!hmbyti;mye?bXSbS?3VG^H9j)W!7CqVpA#-k^YOVbO9Y&x>X9J)nTjO-DI +zO!B)C*(zjbb8P!pvFZg9e~;^mby#))8piZ@63bEYRw>QCM@wTkd<5>DH$h@x$mxqTjQeRR}1RfD*x +z-ABg~cX~xQ{EEcQ^sID6J%}n_{8aVdu7}ygu`0vi1ri5J1(nE3k&Va4C6BwJCVG+@ +z#kW^P(;t?_WP4KTV{<&Kd%F`|o|H7tFtYFHBDUVA(NW9T{}3vKqz`UJ7R|VxNf~Xw +z%<7>JX4`$R&E>8VPwL(W$4hMu1xYk$G~@nMj%{C>EWR{dTq5a8PvRshLh?-|-X+94 +zO1;?mVr;j*ulj1Hr^)4RjqWjDdY4^}CiLyOiR(#6-^USk-|rktI`#dn&v)xE#?IW{->t6|eK{|(%HPo*T(+EJXO?5^TGj?Eyj-te;5z-1 +zJT6n^RpXr++at!}OsT)9HTont?jxyDxwcKuUC0dCDag-h^t>V6M>miJo#67 +zX8JsNY8-t4y{+gq*K-*&S0mej>?|>Wp@W9VkufQsQ<+2UBkVN^o7!<5j<{|rUrP6+ +zM$NR_lP#64MI(-UJmyqrbwp_?>yR55Zo|M)vHlxwKBAO)MxcV?9 +z%Z-mpgRS4VmBM+6wuUqt_%3Au$M&^@DlNC?NIB@0Hrzp+#GRe>CsF^8{&&?tik<-M +zlLm#PiwT`7z5>Z*LXi%aX_-*%lQx%OUnubdo6CKy5M4#liJB+raG56vnQ%T}Q6cB~ +zIzKZ;$n{@{;a1Tq9WKMTP^vzc>za`C9U-|qD{8Zuj;8vrNMxK5VyIK3=3*Re*8(G# +zabC#SsWyCaj>{`D`LQiR*}qE1XTym)H}$wrpV#Vee%ztLO*yr?|7Xmr+aWT;F}i&x +zXnv*UMVgOovr|-#j7({t@B4$zGt<&8@(xQ6&MgU)d&ee^N=`X%%y9dl=Y$Ta8}A!y +z8mx;vs>44VV%pkm8NcW%kG72ebd|?grX9MkaUK5BRW1XfQ(_;x +zd9vc|Q66tq_9*Xb)%7Uv*U|5sxwvKRZM9Qk@=dUsn|qQcT9q&NBZhBeUKb$e`V>i#8thS!ygDm4;&QK(AtuN*@z%_`;HrzRGRV|oDEujOU{RnG*F$)}!O;#Ys<1nO@yYowNY#Q;d|QG~LM +zT*t3dQg&2m`6?sV<9ZEpWzSGMqPpMbuu;d4%=c*>-;6&ETK@(We~^{hW(&-C{WkKR +z>QnZyx_APb7ZG1xCoA%-2)R5@>`4!AsOwRFHge@xGGT)%ZDw7h<=)Y@K-~j#I7iEQ +zXsSrsPe^t}O8qzMdd{=O)-^hQtB$`w%WJeePaG9e_rLJlw0zZACAJ>2ne{6zPaS6q +z)O{_7FCdqEQ=iijsTU!6W37~{=@R>Vs?DrVwS1qJtNT>Kj%YpJzt|CD^@aI(xYGZU +zEk9Z7IakZ;UbF>juFqkrmN%`n<)`TQS0h*clVJ>o;iX#Mw9J;P`%Dh6H1Y4YrYL#$>+B5VGX58~*@j@AqU*Jue65F5 +zMR#d=%5Us*bx+OV<4RBW>)l^;eD#AO;tjTwS=+SS()F*0Z|fT)k0{qb3Mlo^rsWA* +z&uA@A{FN_z{6a0aerpTP((+rhyjkl%PRs8{?&@JaAoD6^&trB( +zqkpZIr)WJXTF(wGuhZ>l>iLM4@6+*hcePIGB?-IFb4DN+f6H&viZ#1b$-B=(rXd$Q +zQ*?RNJw9nlwY*a2iy4Xz;lQ#dG;+Rovx)zx9gk*G^fTngOS|X_GxgA>BkI{!n0UX^=uj#uL4Pn$l255SzYb6ScoISYGX~ +z3|5uZgaWcPJ9*^DaVe~F+M$vWk=P(k+G?w!sHi#^GI<$kX-Nnzk~?t_p43rb4xH}L9nV?wQYx@ZV@|mDD`Znx2={G((r!WN(B|wj{JhPp{jtG +zJ1OiaZ))61Avrhe_>*T0MDYNkD| +zm<%Gq?1plxi)dwMkf}*k!~XPHlYP0_Y0d}{X(J4^4e2aLS5xLu3}YlCIo*0eu+*ts +zU4XhZmfxQ_d4@kDOZ!U}U!(Q=r{|_=&+%vGOd0RX@lTmBVOqv?|8(E@oQy91)TQ>F +z+1cZ(pQ_2We?FwfVJ_RjU}|b+PWJdT|H$N#$)l}MaaF~_&Yvv#v!~~BF*36UDoExI +z)7AAB=fQ`%CjWtDQy%gIbx3>v(8@7&?Dx+r!>71haar?Ql(?XBQD=!qTwvMF-6@hf +z{2vy;w2Z0*vMQ@s5YafQHF|6dzrUb1AVU;=ZE-{@E13&2QzUq(%I3x9sJtQ+OrBR> +zldR@b=am#%>Z~|WU2G*6E-ELb%|lgoh+LHAw`?k!KJgP)6)X)%0)14;q$^pqL^6lT +z^SGWShq&}v$ue~$Qic3{%}Ndy>&aqqAxg}doz$K++9}L2IRwf|3dp`9B$_c!>6uH1 +zNTyGfF&#DY-yg_jU58s0?s;r7&*=>xs^x~6`!;i)%mHr%sxayE^?~83IwFrz6q)(I +zK4o8_y?4^2U!@NWUnvg~Ajww_P5LAeLa9J>oAhRV!0=QPKpY{h&OYIP7Lc_a6V0q6 +z7=F5rY3w)g44*-`tQDDav)*91x&Mu{U#qnWIgl`u-mFU)Zq}zv{*mP`*XhS=g=W3W +z@O&+o^xPeHt3P@EC3Rx%e>?7b6A?n@d4+vu+iH%5R|98yuTE!po-;DXI=F-19Fg9v +zn;34&Yt)!?!*7pBU#BxL{NOK5N}HSfjkGZ$y;=V>oZHSWByHuSdpVKvoA&EiuUT!! +zYL3XzYl}Pn|AHfD`sQXk$Z**c>!is1e@mbqLpK +z+c6^Jn{&hEd5t`aGU?5FQqsSf{#DY7@0s*w-N~!dH|z47a+`cjwcbWriDJ^5^{KWu +z5JFz{zw@6-Z}{5;In!HhI&Q1YWqfjyNoV-`1Ul1?ZL?#K)#-;h6LW0R8SzdnGxq1V +z*&+ElePn!74<`L?4kZ5+BhcKWH|z2ySmFvfd57=XLF#{<&cC*=EtK{C8er@(NzDWkm|OHp0wH4w7_34-tboV_ +zfhGflCajj0e$|dGpVvP02FJEk5yGQ9u6h~4qM*R|Xqf=k^ebM`(w@8a*=vWx$o{@L +z|FeH<@3r6O?6YUzPffkAn|iH@IZO`@Axz!XS2)c%UlmcQjN%Gn3pd&L1%pk$4r_h6 +zh}ZW$pI+}FTZCRu5L~L!_Xsqp8-!imAr(x@q(q`NqP87St(>wwEYkQRbPBI1hAB{B +za?7j+hg(f^rmYL*#;Z4H#_s)B)b@>?pS}Crx~V65o<#id=0@Q!8h;kWHrw|M>$Evs +zozJ)--aIWc3vn*~CgP8WeEb#QuMmGz;34zO=v|wFdme*FUWPs9lIZc#zm78xu519O +zDIJE)CCV1aiYitfhyTFqe!Ndp{-j@f^y?aag-}!p^skMAWJ{x$`^G?>%YI{KEQ!Ul +zl6iv12w|Nj!QB-CJk6Vc!Ae{$5%GmW*edKL?uU^d-Ygz|N4&pL#J#mb8SfvSp)*ea +z+5T#(kj)|Z86nI~oH+&fRh-aQP`IBU?=TE=F&z(nD!7+;slZjHc;=ukl?Bh1HN9J;JWCly +zQHwij1Q>_uc-ThiZPc=Mf!P;SA@_6vCSaV0sl+?@ka_VZ$80Vcf;5)Pqj+-0PRR;U +ziytFLVOemINOz4Y#E9#27EM-RX2;1tn_ +z;|=_VfhQPvG!@KyO;!r}yP~}5y&}~}lr%#In}JhT>(+b&r?J<0iGfq+=)BUvX;SLk +zA-I?Ef-O1{7ndQ!WCM2_c)o$pH*ns2GI$I;OG4Ck16TDqPsR>|fmcd~H3lx%CAW1N +zc(zQ8+HK%+*txmaz++{4J_mkcFpQH1RKJ1CY0b?q8n|3foc9|zJpnmp`wTo&C*m@I +zT%UuywK)dE2}6cS1|BqUTo%LOeFGmqOc@Iq_!t8Z-{NH~*+%*Z7=6!Es|HF(m`UZXLbK_ixE?ba4w}9u#{PAKgtE);d|F(c>;tG>8Hn1u` +zOEJYcR!egRrac3DZZHVf19L;(?yY&nOo_onlu~z%^y3k^~=2X-dz(rfGw}{I{k# +zeaEgIa2~j*Y1-A_hqIvJu0ACJF5cx)UWS=P0n^u?!1bazN+HZDeny!D$BGXq?Xacf +z)2zpFJbdR5@)hjr8NCc+uAYjeToP(eln#W?z}C{dQ7?sf5d*Fs%O-fObixQ5CI~Nr +zOQnmmpTik5?7*@eW9VQn7vbRR5VVz*#yp80&=AUj=gKli+>Q3|dN@?pnlXSg8cY5i +zmxOPfz%*C!7E8{7>hcQZCFm&6Oq+X2(|qR}e1B{3X|Ch7xs|-@!pmS!d1-VVE?!qp +zCW8y*4=UYIG1Xo9F!sZifz_X2mABRA#^G(MtEcANSjK977!UvWf~M7S%K;lU9DNEL +zgg2(nu#LgYqOD({=X&Mn;NF{>)*HSLGhyxafVJY$Ag?aW0)6`5@S)M+&Ay*Ag!`!~}u82-?!yER}=>NsG +zVyAS$|JlE-L_vM!x)FEd(>uHxPF7YGoKNM}l=o1?$S2eWv1z540t=dPUeg*Ee}*6l +zCRgPsMKHH2XVOp4@fIg+Nx;dA^I(Bq&5C5K_?*SLFW%Qv`xveYOB?L2%2UR}!K(bR +zmiMtEo1D!&*RHQMt-<%PK7wC{e*>oKdrUXphkL8Dt5)Me#>%WjF;*soajeW)bkex^ +zQ`ffDxwhB%X2IqAywP_VYw#gFU!9>Gg8kLDJR7F+9p(k~;5!G6r!aSeaszw45`S(L +z(Kt}1=P571r0MmsefUuE-JhEuJ_75eS2SLDkGE-mF5kH7QzHB(5`BgYUb-O%xb7^D +zYSfo9TFIkdePfVU!p-U96r3e9HZHvflQiHwQyYDM8ch3_riFt@L`C1wSDd94Ya%L| +zjurLrieAG^tO?|DtrI#4@MySckJG|i8yqP~h6LcPi32Q)1mN5+9#f*Lxc +zX?&z?hc)eos2_!+BMFPVL4`elXaZH1i1GIqFzTWIjLz=qVPTX +zwx+E|?MLlJEjg`eAyfzIb<_b=w*~EIuprbR_VW$YAsozF+)@N_vh<>M;w-$5>cOSr +zz;~wlS550j^`O2r5?_=-ya^%bz&FHA)FD(AcUzvTnwF2+i#h}TIDe8-0w2wvWDR3a +zsn{_x@K@nN{DmN^DJ^CG>zXF_8?{Kg@pnHoHq}*ph?a>MW8aEbzi(O?kFNFC=rzOdg$C3p?B!@U*)+Fc{y|NnZxVga+m4pa$jT`-ug!X7{orgKld>Nz?PS5{0o#L? +zbXe9rR+#}?nl~xY5VPPu<+sqYpg3kK+E3tUyo)ir>E=;5w%}e+o1?7!<7^y`glr10 +zP#dyzNLg5?r+5`(E*u+~Vm^0;tQM9me9)4MBh(Iu7S6NqF=SwExzF+z-UVSU_ +zj?o`$bJ_L-#!JroM_Sj7` +zn|N39iKb;C<$>o=aWXUnUTU2jKU;nw$S(ryQa$jm5FUm%ZZqMtlsl|))7SW&F_p=reglOqn+ +zt>{9hPE)G4#3m9rNZq6!(hky2(q2+OX+P-zX^=ETIz-B{m>yJ8n@}$+A>bf&lX^%y +zNIOY;N&Te#qywZu(h%tosr&{L)nKe{)~sq$m3Zk=T7Mpn4Bc~u3jcF +z^C{?&v0G}!BPe;Gb&K-$C9$Af_6CEQjT!#lHDFAi%;fhjaZ$b*_we!@?e+|*On&d) +z&T)g%Yb4^{%vWUJow$|R?#SWykC%HR`iOreoAB&K&Ar_dp9GxC1u3m|yixGHLYAPA +z;a;78qsiliQzNkDyaf(&MD=IMI#-$JVUfOs+~pDXFTI^l +z)45k=4l;Ns13zR_aFIBc2rxjrg*c=14&r=M!_(U$3f@RO6jAU_$=lEYUm6~A^h;@2 +zEWiNq0C7LhfWe_(89PGUL+NFUFUs^0{Bz>$JHi2fw}_=_5-k`YgIshfaa#np5%)hN +z9C?JWQppL(3b}}fh|BcNCN@%Z^+Ry@$zp3j_>m*JLC=PBH2|?|ij_X7UQ=WmS^DEcq+7W!3;+xe* +zDW#aj*{;n2_pB{4F(<5AyU4`m!0ENc^|>OE+~K@NX**^L-cLS)(npa1@)Y$lad!pG +zURRXSd$%yl(_lMxAU{?fD1e>o7G*>p3jdtU?&%Qw)VF@nw+sbXxWtvPS+#(@vZf24+9)GsW1a +zvBgDY<#v1EL+=Y_=!?yO?oD;DI#vz5x#?|_G6cK7Uz`}idB%g(b~uMr?k=U?0Bct< +zba$!BB53L=j`7o;)en7L_LvaGLhug8pAGsBZuAmR)X=g2w!IUy_<5GRVzR6EyjXW94&X#yr^Rv&VSIsRvH` +z>?%Fby|{wK!&n$f<6Com=fPmc9W_--_8bK+-mQ939Dr_@fs- +ziW&p$+p=PMso+k?>Pm$dk;n(oMmW9AE@SFONZLL#dZS2P2=8F_N4Mum;DX)T?J?aw +V!n_vFZMVlN^j;mUKt|X2{|D$~Sdst$ + +delta 7376 +zcmZvh3v^V~xySdJ$%G`tWWp;CLNmbx0w_tukOaZZkVpmyG87O*9@86C9wCHY^;&8( +z69~#cvsU1vf(RN5_ex#1H=0$86mdW*c)eU>X?@^>v2bO=qlDYl3W}cl-)Db29R~MW +zXa0NtzWsikefG5f-nM_Itvx$qps<`@Qk4!*{c$^ukYSV`|Kt5yrh@tO3Og<*%Ilhy~J2V +zFC=7=J0Z)MZuy$v+a`U+ZtM3!>(@u`C)65uTQN!;m4R$z49C4I*4u>`Aku`hTJdBh +zqQR!Pn=AG+*#@|5=1!Lt6bqGzAETROK9R`^(B)=FI6}o=E5TrbPQ5Nw>ZQt3(KyDdqRmY +zIiubx1s-GmJKaO06}ZH_9xC(i;Z{E{&W$MQWcpvesgw~8~0>Vkiu_(g;*$6RoQ +zcq)nKxqt|d@luX{ow=`Afli3k#s(fs-`&cvgA2&u0y=q46n1%%erluclze(FIgwu8 +zn%_!lZj7dpX83LHvfk6uX;Gp=RS(zyxuKaB!=^g-Qv-pm|%HijRsjh_0tgly1K(UP@*e +zy=}LI5c0N_Jt-x<=6Gp1`Zs#XQ`rARL>3YDwmJ6DDNkOa8xf+-WP0j{AHrPd%fRt6 +zLdf8KDJp}UFQ|2LY4UdD0D~x?Lz9OkE`~k2i@un=BqxHoijw~_TM)LuO-bMKXL|D-dt0V3hxka+oWqlg{TQ39R1yArdcB`hg@hl@>IB&KC7AN8i&NHt=X8( +zQ;nlNuUt3H_UK9^LhX)H*zl?Q1|MWxYs~yi#`jlLwlx~~y`k;kaa?!1yV7O9IwJ9>xO4p|NbB<+M{c%u= +z>W>r{(I2DfUu(;3bB58D>a3x4191%jI^{(g;{BmfK+C;r +z6BlD&j&7qKZ{?`3he+$-uOMpV(`&lXv{{IOf)<`K%^CHV0PdjaQ*SlyrPWh!9eLnm +z*{A6n(lL244}NM@v+PdP?cVx=ebd`&R$&!5TIuzvg*fA#o?0}t@FR5O{hkFf)ze>_ +zW?kqn)(CzTeV2w$n{3Pam>!xoymA|sC@QlB5?dL8%Aqp@-89Hs +zD7P-xz$~e2dFWpdr4IAVIV1Fkp3HAeGujKHD*A@C;v50gL{&5k74^%CrUFehj(O5Y +zEfV;7(AhHvlMJ(@0=r+d@y{rB1o8sNlVB0KX53M6c>uhH($@NSUBiF;3Bmuori15X +z@IzGCny+fgpQKGQhI;!UU3y*+H4=YI|H0Rr9Vrh9u_65E=-K<_#*qHY(s1aO|9cQ@ +z(nb6pb9{+k%kqox18=40)ySdRFsZxi(u_=-v9HVFOR?Jq-%S~_3yc@L#?St*RPsAr +zulo1)3&GR-6mj~nWOMh=^1 +z6V!FYG+Uv5=niNQ`ZBZ^8itmpD@h^ +zsOyiWc~FL-SvXO&pF}~>PV`Y2+KmxR#R;JkGh{y0kEwSM>c;v=!W*aef6)-A`%BZ@ +z2JM9IgZl6mINA>@tQ($>WP}T**$ZugCeS;}MjCFqvTS7bRrE~kfdaF^IzabQ>GI6M +z%YJW~`uKou7aQ>`rA5nY%Pzt)0wHlvn)6B9!Zdk4lW8wXcF;5nrLwDoCZJcd=;P%x +zM=g8PG}W6_9c7lmvJGq;$CkBu0xV3kRty~c->~4#XgO>GLXJ`q{ba>ZqmF*DV!e?? +z*(;|T2Wj`p;^aGFKaSo%i%_{E^$(`mNRt{yZf(d-mR0bf|8JwphT7!$2z4N|kajoB +zb6kfnNY@%>JLX^{vS`+->5jedMd^iAwT@o+4$`GnwaF7P9=`WYvy-l@Dxe=VmODB? +zcG2#}TE|8BN~ouCWndvjr|EO7XOtmkra9N!>eG^@yk(u*9*1o&Y)hpLPB!T0U;-~3 +z>s!E^=Cf}yQqqz<{be`ogRK>>vd7?;PIj;y{a!Hr(IUBw`zLVk8FPr4FA1E4QNX2V +zFos>ToJ-QC>65-b*`qHlw--GiU8)k57JhwR%B@xgY`>OsHq(OUJfoS`HjmAB%Atot +zQI`9NGn20=>ALBiW*5#B=bOi7xH(5mM9T$fRG=6$@%})bEz+OX2A)Eu9@~&WiL5=G +zo7KnaXKi8)vbM8!uy(SBS-V*yti7z(RRzj%W?01um(qYJVc=%7=Bj)&65_MC67lDyv<7 +zS_WfdYK~>w@_js8xW1RuYwO1a;`&H_|Jra{;*@JT#>v$yxn82$R1S|J{#k-vx>GEE +zQs&U-&+v_~6eD-dSh#k@l%fOA{F +zLcGP?&+&D^pX>N>{4#U#LzRJi+o*MiceZSCm<{b*fRni^j=Pw5%u*5k9KTd^p8~o< +zUgikHjcIJUqUN3jt%IojY}54 +zwc_5qF&we@%^T0V;PRe4e#iTq^KrK5oRyxpc;ALJrqB8yUb{AdcdfGzDGwI3yp6@F +zWO8V%EmA^14lTAtCea^5#Z!6D(I-23Q7Gfa-dD;@x|zH1{Vkoo4?V}+T}ab5jm?R_ +z*DXf}%Iw0~v~|(v>O^yP5ah(=z{ndHjv_1?Ej|6;Yqr +z5-?~3I4y*h{5&Jz=6l0S%|Tzm|Fo!4MTivgv}Fz-fm53tf%v~n+{{Hz*V?v_EqQ!}s2_XBxbc-;sqOz^#|?Hr +zIzdxtRZ4o-_-zN|CDWCsi!=1;Mr#F?ZZFQzhe@s7v<6h4b+j&^SGE@?yZN~4rk?HP +z$w3a`NV%gtSsygrw0uWJvR)fbdI>fTxtw$!Aw3QQX!J9EA?E;E@l3gMplZK9-&E2o +z&lIQVrJq|(=aGW<6yc-MJIkHC`Riq!OKWx(575UKt=ALj)t$x3%_?_iBc0z_u0vUk +zH2T@TkgJhaJX@a1r<2HPIZ&R2ffRl=KRKw9izYgc1bV+%L!)=~g`8_>#jYx24ZXCh +dDrt?1pG8*?VgAolXcm?3F1P)vl~(K?@&8}0C|Cdh + +diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java b/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java +index f33ce913a..01b208ed2 100644 +--- a/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java ++++ b/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java +@@ -30,6 +30,22 @@ public abstract class DefinedPacket + writeString( s, buf, Short.MAX_VALUE ); + } + ++ //Velocity start ++ private static final int[] VARINT_EXACT_BYTE_LENGTHS = new int[33]; ++ ++ static { ++ for (int i = 0; i <= 32; ++i) { ++ VARINT_EXACT_BYTE_LENGTHS[i] = (int) Math.ceil((31d - (i - 1)) / 7d); ++ } ++ VARINT_EXACT_BYTE_LENGTHS[32] = 1; // Special case for the number 0. ++ } ++ ++ public static int varIntBytes(int value) { ++ return VARINT_EXACT_BYTE_LENGTHS[Integer.numberOfLeadingZeros(value)]; ++ } ++ //Velocity end ++ ++ + public static void writeString(String s, ByteBuf buf, int maxLength) + { + if ( s.length() > maxLength ) +@@ -198,6 +214,12 @@ public abstract class DefinedPacket + } + } + ++ public static void write21BitVarInt(ByteBuf buf, int value) { ++ // See https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/ ++ int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14); ++ buf.writeMedium(w); ++ } ++ + public static int readVarShort(ByteBuf buf) + { + int low = buf.readUnsignedShort(); +diff --git a/proxy/pom.xml b/proxy/pom.xml +index 10fe411d8..f6daaea4d 100644 +--- a/proxy/pom.xml ++++ b/proxy/pom.xml +@@ -165,6 +165,12 @@ + ${project.version} + compile + ++ ++ io.github.waterfallmc ++ waterfall-native ++ ${project.version} ++ compile ++ + + io.github.waterfallmc + waterfall-module-cmd-alert +diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +index f5059666e..dba21bcc7 100644 +--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java ++++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +@@ -17,6 +17,7 @@ import dev._2lstudios.flamecord.commands.FlameCordCommand; + import dev._2lstudios.flamecord.configuration.ModulesConfiguration; + import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import io.github.waterfallmc.waterfall.conf.WaterfallConfiguration; ++import dev._2lstudios.flamecord.natives.Natives; + import io.github.waterfallmc.waterfall.event.ProxyExceptionEvent; + import io.github.waterfallmc.waterfall.exception.ProxyPluginEnableDisableException; + import io.netty.bootstrap.ServerBootstrap; +@@ -256,23 +257,7 @@ public class BungeeCord extends ProxyServer + + pluginManager = new PluginManager( this ); + +- if ( !Boolean.getBoolean( "net.md_5.bungee.native.disable" ) ) +- { +- if ( EncryptionUtil.nativeFactory.load() ) +- { +- logger.info( "Using mbed TLS based native cipher." ); +- } else +- { +- logger.info( "Using standard Java JCE cipher." ); +- } +- if ( CompressFactory.zlib.load() ) +- { +- logger.info( "Using zlib based native compressor." ); +- } else +- { +- logger.info( "Using standard Java compressor." ); +- } +- } ++ logger.log(Level.INFO, "FlameCord is using " + Natives.getCompressorFactory().getName() + " compression"); + } + + /** +diff --git a/proxy/src/main/java/net/md_5/bungee/compress/PacketCompressor.java b/proxy/src/main/java/net/md_5/bungee/compress/PacketCompressor.java +index d07cf4627..10cb0430c 100644 +--- a/proxy/src/main/java/net/md_5/bungee/compress/PacketCompressor.java ++++ b/proxy/src/main/java/net/md_5/bungee/compress/PacketCompressor.java +@@ -1,45 +1,74 @@ + package net.md_5.bungee.compress; + ++import java.util.zip.DataFormatException; ++ ++import dev._2lstudios.flamecord.natives.MoreByteBufUtils; ++import dev._2lstudios.flamecord.natives.compress.Compressor; + import io.netty.buffer.ByteBuf; ++import io.netty.buffer.ByteBufAllocator; + import io.netty.channel.ChannelHandlerContext; + import io.netty.handler.codec.MessageToByteEncoder; +-import java.util.zip.Deflater; + import lombok.Setter; +-import net.md_5.bungee.jni.zlib.BungeeZlib; + import net.md_5.bungee.protocol.DefinedPacket; + +-public class PacketCompressor extends MessageToByteEncoder +-{ ++public class PacketCompressor extends MessageToByteEncoder { ++ ++ private final Compressor compressor; + +- private final BungeeZlib zlib = CompressFactory.zlib.newInstance(); + @Setter + private int threshold = 256; + +- @Override +- public void handlerAdded(ChannelHandlerContext ctx) throws Exception +- { +- zlib.init( true, Deflater.DEFAULT_COMPRESSION ); ++ public PacketCompressor(Compressor compressor) { ++ this.compressor = compressor; + } + + @Override +- public void handlerRemoved(ChannelHandlerContext ctx) throws Exception +- { +- zlib.free(); ++ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { ++ compressor.close(); + } + + @Override +- protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception +- { ++ protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception { + int origSize = msg.readableBytes(); +- if ( origSize < threshold ) +- { +- DefinedPacket.writeVarInt( 0, out ); +- out.writeBytes( msg ); +- } else +- { +- DefinedPacket.writeVarInt( origSize, out ); +- +- zlib.process( msg, out ); ++ if (origSize < threshold) { ++ // Under the threshold, there is nothing to do. ++ DefinedPacket.writeVarInt(0, out); ++ out.writeBytes(msg); ++ return; ++ } ++ ++ int uncompressed = msg.readableBytes(); ++ ++ DefinedPacket.writeVarInt(uncompressed, out); ++ ByteBuf compatibleIn = MoreByteBufUtils.ensureCompatible(ctx.alloc(), compressor, msg); ++ ++ int startCompressed = out.writerIndex(); ++ try { ++ compressor.deflate(compatibleIn, out); ++ } finally { ++ compatibleIn.release(); ++ } ++ int compressedLength = out.writerIndex() - startCompressed; ++ if (compressedLength >= 1 << 21) { ++ throw new DataFormatException("The server sent a very large (over 2MiB compressed) packet."); + } + } ++ ++ @Override ++ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception { ++ return allocateByteBufForCompression(ctx.alloc(), msg, threshold); ++ } ++ ++ public static ByteBuf allocateByteBufForCompression(ByteBufAllocator allocator, ByteBuf msg, int threshold) { ++ int uncompressed = msg.readableBytes(); ++ if (uncompressed < threshold) { ++ int finalBufferSize = uncompressed + 1; ++ finalBufferSize += DefinedPacket.varIntBytes(finalBufferSize); ++ return allocator.directBuffer(finalBufferSize); ++ } ++ ++ // (maximum data length after compression) + packet length varint + uncompressed data varint ++ int initialBufferSize = (uncompressed - 1) + 3 + DefinedPacket.varIntBytes(uncompressed); ++ return allocator.directBuffer(initialBufferSize); ++ } + } +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 +index eaedf4bc4..066bdafc8 100644 +--- a/proxy/src/main/java/net/md_5/bungee/compress/PacketDecompressor.java ++++ b/proxy/src/main/java/net/md_5/bungee/compress/PacketDecompressor.java +@@ -1,5 +1,6 @@ + package net.md_5.bungee.compress; + ++import dev._2lstudios.flamecord.natives.compress.Compressor; + import lombok.*; + + import com.google.common.base.Preconditions; +@@ -8,53 +9,75 @@ import io.netty.channel.ChannelHandlerContext; + import io.netty.handler.codec.MessageToMessageDecoder; + import java.util.List; + import net.md_5.bungee.jni.zlib.BungeeZlib; ++import net.md_5.bungee.protocol.BadPacketException; + import net.md_5.bungee.protocol.DefinedPacket; + ++import static dev._2lstudios.flamecord.natives.MoreByteBufUtils.ensureCompatible; ++ ++ + @RequiredArgsConstructor +-public class PacketDecompressor extends MessageToMessageDecoder +-{ ++public class PacketDecompressor extends MessageToMessageDecoder { ++ private static final int VANILLA_MAXIMUM_UNCOMPRESSED_SIZE = 8 * 1024 * 1024; // 8MiB ++ private static final int MAXIMUM_UNCOMPRESSED_SIZE_WHILE_CHECKING = ((100 * 4) + Short.MAX_VALUE) + 5 + 5; //((100 chars channel tag) + max data size) + string varint + packet id varint + +- private final int compressionThreshold; +- private final BungeeZlib zlib = CompressFactory.zlib.newInstance(); ++ @Setter ++ private boolean checking = false; + +- @Override +- public void handlerAdded(ChannelHandlerContext ctx) throws Exception +- { +- zlib.init( false, 0 ); +- } ++ private final Compressor compressor; ++ private final int threshold; + + @Override +- public void handlerRemoved(ChannelHandlerContext ctx) throws Exception +- { +- zlib.free(); ++ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { ++ compressor.close(); + } + + @Override +- protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception +- { +- int size = DefinedPacket.readVarInt( in ); +- if ( size == 0 ) +- { +- out.add( in.retain() ); +- } else +- { +- Preconditions.checkArgument( size >= compressionThreshold, "Decompressed size %s less than compression threshold %s", size, compressionThreshold); +- ByteBuf decompressed = ctx.alloc().directBuffer(); +- +- try +- { +- zlib.process( in, decompressed ); +- Preconditions.checkArgument( decompressed.readableBytes() == size, "Decompressed size %s is not equal to actual decompressed bytes", size, decompressed.readableBytes()); +- +- out.add( decompressed ); +- decompressed = null; +- } finally +- { +- if ( decompressed != null ) +- { +- decompressed.release(); +- } +- } ++ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { ++ int size = DefinedPacket.readVarInt(in); ++ if (size == 0) { ++ out.add(in.retain()); ++ return; ++ } ++ ++ //Velocity start ++ if (threshold != -1 && size < threshold) { ++ throw new BadPacketException( ++ "Uncompressed size " + size + " is less than threshold " + threshold ++ ); ++ } ++ ++ if (size > VANILLA_MAXIMUM_UNCOMPRESSED_SIZE) { ++ throw new BadPacketException("" + ++ "Uncompressed size " + size + " exceeds threshold of " + VANILLA_MAXIMUM_UNCOMPRESSED_SIZE ++ ); ++ } ++ ++ if (checking && size > MAXIMUM_UNCOMPRESSED_SIZE_WHILE_CHECKING) { ++ throw new BadPacketException( ++ "Uncompressed size " + size + " exceeds threshold of " + MAXIMUM_UNCOMPRESSED_SIZE_WHILE_CHECKING + " (While checking)" ++ ); ++ } ++ //Velocity end ++ ++ ByteBuf compatibleIn = ensureCompatible(ctx.alloc(), compressor, in); ++ ByteBuf uncompressed; ++ if (checking) { ++ uncompressed = ctx.alloc().directBuffer(size, size); ++ } ++ else { ++ uncompressed = ctx.alloc().directBuffer(size); ++ } ++ ++ try { ++ compressor.inflate(compatibleIn, uncompressed, size); ++ out.add(uncompressed); ++ } ++ catch (Exception e) { ++ uncompressed.release(); ++ throw e; ++ } ++ finally { ++ compatibleIn.release(); + } + } +-} ++} +\ No newline at end of file +diff --git a/proxy/src/main/java/net/md_5/bungee/forge/ForgeUtils.java b/proxy/src/main/java/net/md_5/bungee/forge/ForgeUtils.java +index 8d4439ab8..95ed34416 100644 +--- a/proxy/src/main/java/net/md_5/bungee/forge/ForgeUtils.java ++++ b/proxy/src/main/java/net/md_5/bungee/forge/ForgeUtils.java +@@ -44,8 +44,8 @@ public class ForgeUtils + if ( discriminator == 2 ) // ModList + { + ByteBuf buffer = payload.slice(); +- int modCount = DefinedPacket.readVarInt( buffer, 2 ); +- for ( int i = 0; i < modCount; i++ ) ++ int modCount = DefinedPacket.readVarInt( buffer ); // FlameCord - Remove length check for compression ++ for ( int i = 0; i < modCount; i++ ) + { + modTags.put( DefinedPacket.readString( buffer ), DefinedPacket.readString( buffer ) ); + } +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 +index b98ee6a75..ffe038168 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +@@ -1,6 +1,10 @@ + package net.md_5.bungee.netty; + + import com.google.common.base.Preconditions; ++import dev._2lstudios.flamecord.FlameCord; ++import dev._2lstudios.flamecord.configuration.FlameCordConfiguration; ++import dev._2lstudios.flamecord.natives.Natives; ++import dev._2lstudios.flamecord.natives.compress.Compressor; + import io.netty.channel.Channel; + import io.netty.channel.ChannelFutureListener; + import io.netty.channel.ChannelHandler; +@@ -145,15 +149,16 @@ public class ChannelWrapper + return ch; + } + +- public void setCompressionThreshold(int compressionThreshold) +- { ++ public void setCompressionThreshold(int compressionThreshold) { ++ Compressor compressor = Natives.getCompressorFactory().create( FlameCord.getInstance().getFlameCordConfiguration().getCompressionLevel() ); ++ + // FlameCord - Use pipeline to reduce redundancy + final ChannelPipeline pipeline = ch.pipeline(); + if ( pipeline.get( PacketCompressor.class ) == null && compressionThreshold != -1 ) + { +- addBefore( PipelineUtils.PACKET_ENCODER, "compress", new PacketCompressor() ); ++ addBefore( PipelineUtils.PACKET_ENCODER, "compress", new PacketCompressor( compressor ) ); + } +- if ( compressionThreshold != -1 ) ++ if ( compressionThreshold != -1 ) + { + pipeline.get( PacketCompressor.class ).setThreshold( compressionThreshold ); + } else +@@ -163,7 +168,7 @@ public class ChannelWrapper + + if ( pipeline.get( PacketDecompressor.class ) == null && compressionThreshold != -1 ) + { +- addBefore( PipelineUtils.PACKET_DECODER, "decompress", new PacketDecompressor(compressionThreshold) ); ++ addBefore( PipelineUtils.PACKET_DECODER, "decompress", new PacketDecompressor( compressor, compressionThreshold ) ); // FlameCord - Implement Libdeflate + } + if ( compressionThreshold == -1 ) + { +@@ -171,6 +176,7 @@ public class ChannelWrapper + } + } + ++ + // FlameCord start - Antibot System + // Make the channel accessible + public Channel getChannel() { +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 +index f04fdd9ce..9aebc9e1a 100644 +--- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java ++++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java +@@ -126,7 +126,8 @@ public class PipelineUtils + }; + public static final Base BASE = new Base(); + private static final KickStringWriter legacyKicker = new KickStringWriter(); +- private static final Varint21LengthFieldPrepender framePrepender = new Varint21LengthFieldPrepender(); ++ // FlameCord - Implement Libdeflate ++ public static final Varint21LengthFieldPrepender framePrepender = new Varint21LengthFieldPrepender(); + public static final String TIMEOUT_HANDLER = "timeout"; + public static final String PACKET_DECODER = "packet-decoder"; + public static final String PACKET_ENCODER = "packet-encoder"; +-- +2.37.3.windows.1 + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a1167dd --- /dev/null +++ b/pom.xml @@ -0,0 +1,25 @@ + + 4.0.0 + + io.github.waterfallmc + waterfall-super + dev-SNAPSHOT + pom + + Waterfall-Super + Super project for FlameCord. + https://github.com/2lstudios-mc/FlameCord + + + FlameCord-Proxy + + + + clean install + + + + UTF-8 + +