From 8e8a6353610aec0d91ba082afe44663be2202aec Mon Sep 17 00:00:00 2001 From: md_5 Date: Mon, 27 May 2024 06:48:08 +1000 Subject: [PATCH 01/58] Minecraft 24w21b support --- .../protocol/AbstractPacketHandler.java | 10 +++ .../net/md_5/bungee/protocol/Protocol.java | 22 +++++ .../bungee/protocol/ProtocolConstants.java | 5 +- .../packet/DisconnectReportDetails.java | 55 +++++++++++++ .../bungee/protocol/packet/ServerLinks.java | 82 +++++++++++++++++++ .../net/md_5/bungee/entitymap/EntityMap.java | 1 + 6 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 protocol/src/main/java/net/md_5/bungee/protocol/packet/DisconnectReportDetails.java create mode 100644 protocol/src/main/java/net/md_5/bungee/protocol/packet/ServerLinks.java 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 422cba541e..8d91211b47 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,6 +10,7 @@ import net.md_5.bungee.protocol.packet.Commands; import net.md_5.bungee.protocol.packet.CookieRequest; import net.md_5.bungee.protocol.packet.CookieResponse; +import net.md_5.bungee.protocol.packet.DisconnectReportDetails; import net.md_5.bungee.protocol.packet.EncryptionRequest; import net.md_5.bungee.protocol.packet.EncryptionResponse; import net.md_5.bungee.protocol.packet.EntityStatus; @@ -38,6 +39,7 @@ import net.md_5.bungee.protocol.packet.ScoreboardScore; import net.md_5.bungee.protocol.packet.ScoreboardScoreReset; import net.md_5.bungee.protocol.packet.ServerData; +import net.md_5.bungee.protocol.packet.ServerLinks; import net.md_5.bungee.protocol.packet.SetCompression; import net.md_5.bungee.protocol.packet.StartConfiguration; import net.md_5.bungee.protocol.packet.StatusRequest; @@ -268,4 +270,12 @@ public void handle(CookieRequest cookieRequest) throws Exception public void handle(CookieResponse cookieResponse) throws Exception { } + + public void handle(DisconnectReportDetails disconnectReportDetails) throws Exception + { + } + + public void handle(ServerLinks serverLinks) throws Exception + { + } } 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 2271a2e51f..317fb2d456 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 @@ -18,6 +18,7 @@ import net.md_5.bungee.protocol.packet.Commands; import net.md_5.bungee.protocol.packet.CookieRequest; import net.md_5.bungee.protocol.packet.CookieResponse; +import net.md_5.bungee.protocol.packet.DisconnectReportDetails; import net.md_5.bungee.protocol.packet.EncryptionRequest; import net.md_5.bungee.protocol.packet.EncryptionResponse; import net.md_5.bungee.protocol.packet.EntityStatus; @@ -44,6 +45,7 @@ import net.md_5.bungee.protocol.packet.ScoreboardScore; import net.md_5.bungee.protocol.packet.ScoreboardScoreReset; import net.md_5.bungee.protocol.packet.ServerData; +import net.md_5.bungee.protocol.packet.ServerLinks; import net.md_5.bungee.protocol.packet.SetCompression; import net.md_5.bungee.protocol.packet.StartConfiguration; import net.md_5.bungee.protocol.packet.StatusRequest; @@ -496,6 +498,16 @@ public enum Protocol Transfer::new, map( ProtocolConstants.MINECRAFT_1_20_5, 0x73 ) ); + TO_CLIENT.registerPacket( + DisconnectReportDetails.class, + DisconnectReportDetails::new, + map( ProtocolConstants.MINECRAFT_1_21, 0x7A ) + ); + TO_CLIENT.registerPacket( + ServerLinks.class, + ServerLinks::new, + map( ProtocolConstants.MINECRAFT_1_21, 0x7B ) + ); TO_SERVER.registerPacket( KeepAlive.class, @@ -742,6 +754,16 @@ public enum Protocol Transfer::new, map( ProtocolConstants.MINECRAFT_1_20_5, 0x0B ) ); + TO_CLIENT.registerPacket( + DisconnectReportDetails.class, + DisconnectReportDetails::new, + map( ProtocolConstants.MINECRAFT_1_21, 0x0F ) + ); + TO_CLIENT.registerPacket( + ServerLinks.class, + ServerLinks::new, + map( ProtocolConstants.MINECRAFT_1_21, 0x10 ) + ); TO_SERVER.registerPacket( ClientSettings.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 40045b7704..2b1487e05d 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 @@ -46,6 +46,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_2 = 764; public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; + public static final int MINECRAFT_1_21 = 1073742022; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; @@ -110,8 +111,8 @@ public class ProtocolConstants if ( SNAPSHOT_SUPPORT ) { - // supportedVersions.add( "1.20.x" ); - // supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_20_5 ); + supportedVersions.add( "1.21.x" ); + supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21 ); } SUPPORTED_VERSIONS = supportedVersions.build(); diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/DisconnectReportDetails.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/DisconnectReportDetails.java new file mode 100644 index 0000000000..a84144610b --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/DisconnectReportDetails.java @@ -0,0 +1,55 @@ +package net.md_5.bungee.protocol.packet; + +import com.google.common.base.Preconditions; +import io.netty.buffer.ByteBuf; +import java.util.HashMap; +import java.util.Map; +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 DisconnectReportDetails extends DefinedPacket +{ + + private Map details; + + @Override + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + int len = readVarInt( buf ); + Preconditions.checkArgument( len <= 32, "Too many details" ); + + details = new HashMap<>(); + for ( int i = 0; i < len; i++ ) + { + details.put( readString( buf, 128 ), readString( buf, 4096 ) ); + } + } + + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + Preconditions.checkArgument( details.size() <= 32, "Too many details" ); + writeVarInt( details.size(), buf ); + + for ( Map.Entry detail : details.entrySet() ) + { + writeString( detail.getKey(), buf, 128 ); + writeString( detail.getValue(), buf, 4096 ); + } + } + + @Override + public void handle(AbstractPacketHandler handler) throws Exception + { + handler.handle( this ); + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ServerLinks.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ServerLinks.java new file mode 100644 index 0000000000..24225a81bf --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ServerLinks.java @@ -0,0 +1,82 @@ +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.api.chat.BaseComponent; +import net.md_5.bungee.protocol.AbstractPacketHandler; +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.Either; +import net.md_5.bungee.protocol.ProtocolConstants; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class ServerLinks extends DefinedPacket +{ + + private Link[] links; + + @Override + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + int len = readVarInt( buf ); + links = new Link[ len ]; + for ( int i = 0; i < len; i++ ) + { + Either type; + if ( buf.readBoolean() ) + { + type = Either.left( LinkType.values()[readVarInt( buf )] ); + } else + { + type = Either.right( readBaseComponent( buf, protocolVersion ) ); + } + String url = readString( buf ); + + links[i] = new Link( type, url ); + } + } + + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + writeVarInt( links.length, buf ); + for ( Link link : links ) + { + Either type = link.getType(); + if ( type.isLeft() ) + { + buf.writeBoolean( true ); + writeVarInt( type.getLeft().ordinal(), buf ); + } else + { + buf.writeBoolean( false ); + writeBaseComponent( type.getRight(), buf, protocolVersion ); + } + writeString( link.getUrl(), buf ); + } + } + + @Override + public void handle(AbstractPacketHandler handler) throws Exception + { + handler.handle( this ); + } + + public enum LinkType + { + REPORT_BUG; + } + + @Data + public static class Link + { + + private final Either type; + private final String url; + } +} 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 2bf92a03a3..70b58fab29 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 @@ -87,6 +87,7 @@ public static EntityMap getEntityMap(int version) case ProtocolConstants.MINECRAFT_1_20_3: return EntityMap_1_16_2.INSTANCE_1_20_3; case ProtocolConstants.MINECRAFT_1_20_5: + case ProtocolConstants.MINECRAFT_1_21: return EntityMap_1_16_2.INSTANCE_1_20_5; } throw new RuntimeException( "Version " + version + " has no entity map" ); From 52ab21b1ffcde905bad2a65e98d82424b72826c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 06:59:21 +1000 Subject: [PATCH 02/58] #3682: Bump io.netty:netty-bom from 4.1.109.Final to 4.1.110.Final Bumps [io.netty:netty-bom](https://github.com/netty/netty) from 4.1.109.Final to 4.1.110.Final. - [Commits](https://github.com/netty/netty/compare/netty-4.1.109.Final...netty-4.1.110.Final) --- updated-dependencies: - dependency-name: io.netty:netty-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 463012be4c..03c222fb62 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ io.netty netty-bom - 4.1.109.Final + 4.1.110.Final pom import From e7e0b97cfff751934fab7ae7a8b58656243cb7e1 Mon Sep 17 00:00:00 2001 From: md_5 Date: Sun, 2 Jun 2024 09:41:02 +1000 Subject: [PATCH 03/58] Minecraft 1.21-pre2 support --- .../net/md_5/bungee/protocol/ProtocolConstants.java | 2 +- .../net/md_5/bungee/protocol/packet/ServerLinks.java | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) 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 2b1487e05d..080ba0b8d8 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 @@ -46,7 +46,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_2 = 764; public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; - public static final int MINECRAFT_1_21 = 1073742022; + public static final int MINECRAFT_1_21 = 1073742024; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ServerLinks.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ServerLinks.java index 24225a81bf..5df25c24e6 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ServerLinks.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ServerLinks.java @@ -69,7 +69,17 @@ public void handle(AbstractPacketHandler handler) throws Exception public enum LinkType { - REPORT_BUG; + + REPORT_BUG, + COMMUNITY_GUIDELINES, + SUPPORT, + STATUS, + FEEDBACK, + COMMUNITY, + WEBSITE, + FORUMS, + NEWS, + ANNOUNCEMENTS; } @Data From b8b373a53ed80d0188425d3a7093f315b7fcc76a Mon Sep 17 00:00:00 2001 From: md_5 Date: Sat, 8 Jun 2024 10:31:45 +1000 Subject: [PATCH 04/58] Minecraft 1.21-pre4 support --- .../main/java/net/md_5/bungee/protocol/ProtocolConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 080ba0b8d8..8185378d08 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 @@ -46,7 +46,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_2 = 764; public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; - public static final int MINECRAFT_1_21 = 1073742024; + public static final int MINECRAFT_1_21 = 1073742026; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; From 07df657f3cda067315dd6a81d87589757a551f8e Mon Sep 17 00:00:00 2001 From: md_5 Date: Tue, 11 Jun 2024 07:00:30 +1000 Subject: [PATCH 05/58] Minecraft 1.21-rc1 support --- .../main/java/net/md_5/bungee/protocol/ProtocolConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8185378d08..8f76a4adb9 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 @@ -46,7 +46,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_2 = 764; public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; - public static final int MINECRAFT_1_21 = 1073742026; + public static final int MINECRAFT_1_21 = 1073742027; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; From 006a14a75ce684067474299990a4f54eaea64752 Mon Sep 17 00:00:00 2001 From: Outfluencer <48880402+Outfluencer@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:07:08 +0200 Subject: [PATCH 06/58] #3689, #3690: Don't immediately close HAProxy health check --- proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 6caf30cdca..d82173b1ea 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 @@ -30,6 +30,7 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter private ChannelWrapper channel; private PacketHandler handler; + private boolean healthCheck; public void setHandler(PacketHandler handler) { @@ -96,7 +97,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception channel.setRemoteAddress( newAddress ); } else { - channel.close(); + healthCheck = true; } } finally { @@ -146,7 +147,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E { if ( ctx.channel().isActive() ) { - boolean logExceptions = !( handler instanceof PingHandler ); + boolean logExceptions = !( handler instanceof PingHandler ) && !healthCheck; if ( logExceptions ) { From 8a88ce464e0b107b15523109afd7810096e635ca Mon Sep 17 00:00:00 2001 From: md_5 Date: Fri, 14 Jun 2024 01:05:00 +1000 Subject: [PATCH 07/58] Minecraft 1.21 support --- api/pom.xml | 4 ++-- bootstrap/pom.xml | 4 ++-- chat/pom.xml | 4 ++-- config/pom.xml | 4 ++-- event/pom.xml | 4 ++-- log/pom.xml | 4 ++-- module/cmd-alert/pom.xml | 4 ++-- module/cmd-find/pom.xml | 4 ++-- module/cmd-kick/pom.xml | 4 ++-- module/cmd-list/pom.xml | 4 ++-- module/cmd-send/pom.xml | 4 ++-- module/cmd-server/pom.xml | 4 ++-- module/pom.xml | 4 ++-- module/reconnect-yaml/pom.xml | 4 ++-- native/pom.xml | 4 ++-- pom.xml | 2 +- protocol/pom.xml | 4 ++-- .../net/md_5/bungee/protocol/ProtocolConstants.java | 12 +++++++----- proxy/pom.xml | 6 +++--- query/pom.xml | 4 ++-- slf4j/pom.xml | 4 ++-- 21 files changed, 47 insertions(+), 45 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 4f520c3134..57c96a4213 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-api - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar BungeeCord-API diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml index 9106170013..ff27b54eb3 100644 --- a/bootstrap/pom.xml +++ b/bootstrap/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-bootstrap - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar BungeeCord-Bootstrap diff --git a/chat/pom.xml b/chat/pom.xml index 4239d5a198..71c4ce2797 100644 --- a/chat/pom.xml +++ b/chat/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-chat - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar BungeeCord-Chat diff --git a/config/pom.xml b/config/pom.xml index e7a474c6be..4b94f11267 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-config - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar BungeeCord-Config diff --git a/event/pom.xml b/event/pom.xml index 1f36ab9094..84711f0ffb 100644 --- a/event/pom.xml +++ b/event/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-event - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar BungeeCord-Event diff --git a/log/pom.xml b/log/pom.xml index 1e2f2e96fe..0091af1368 100644 --- a/log/pom.xml +++ b/log/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-log - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar BungeeCord-Log diff --git a/module/cmd-alert/pom.xml b/module/cmd-alert/pom.xml index 62e1f13762..fd6bb27ec8 100644 --- a/module/cmd-alert/pom.xml +++ b/module/cmd-alert/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-module - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-module-cmd-alert - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar cmd_alert diff --git a/module/cmd-find/pom.xml b/module/cmd-find/pom.xml index 5ca1b4c3bf..a679a54a34 100644 --- a/module/cmd-find/pom.xml +++ b/module/cmd-find/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-module - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-module-cmd-find - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar cmd_find diff --git a/module/cmd-kick/pom.xml b/module/cmd-kick/pom.xml index 2a498c05f5..8f79be48ab 100644 --- a/module/cmd-kick/pom.xml +++ b/module/cmd-kick/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-module - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-module-cmd-kick - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar cmd_kick diff --git a/module/cmd-list/pom.xml b/module/cmd-list/pom.xml index 99090af717..39e7b99845 100644 --- a/module/cmd-list/pom.xml +++ b/module/cmd-list/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-module - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-module-cmd-list - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar cmd_list diff --git a/module/cmd-send/pom.xml b/module/cmd-send/pom.xml index 5aee81737a..fe51d004fd 100644 --- a/module/cmd-send/pom.xml +++ b/module/cmd-send/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-module - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-module-cmd-send - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar cmd_send diff --git a/module/cmd-server/pom.xml b/module/cmd-server/pom.xml index d33200e2c3..bc1409aa08 100644 --- a/module/cmd-server/pom.xml +++ b/module/cmd-server/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-module - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-module-cmd-server - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar cmd_server diff --git a/module/pom.xml b/module/pom.xml index 21ba8f2bf4..cea355bdba 100644 --- a/module/pom.xml +++ b/module/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-module - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT pom BungeeCord Modules diff --git a/module/reconnect-yaml/pom.xml b/module/reconnect-yaml/pom.xml index e3a077f3db..f1fb430aff 100644 --- a/module/reconnect-yaml/pom.xml +++ b/module/reconnect-yaml/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-module - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-module-reconnect-yaml - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar reconnect_yaml diff --git a/native/pom.xml b/native/pom.xml index 56d505ab57..bd3a8ab8ba 100644 --- a/native/pom.xml +++ b/native/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-native - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar BungeeCord-Native diff --git a/pom.xml b/pom.xml index 03c222fb62..7335e8b798 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT pom BungeeCord-Parent diff --git a/protocol/pom.xml b/protocol/pom.xml index 6f5acbc2ee..d697cea8f8 100644 --- a/protocol/pom.xml +++ b/protocol/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-protocol - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar BungeeCord-Protocol 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 8f76a4adb9..82ef4c5d41 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 @@ -46,7 +46,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_2 = 764; public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; - public static final int MINECRAFT_1_21 = 1073742027; + public static final int MINECRAFT_1_21 = 767; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; @@ -65,7 +65,8 @@ public class ProtocolConstants "1.17.x", "1.18.x", "1.19.x", - "1.20.x" + "1.20.x", + "1.21.x" ); ImmutableList.Builder supportedVersionIds = ImmutableList.builder().add( ProtocolConstants.MINECRAFT_1_8, @@ -106,13 +107,14 @@ public class ProtocolConstants ProtocolConstants.MINECRAFT_1_20, ProtocolConstants.MINECRAFT_1_20_2, ProtocolConstants.MINECRAFT_1_20_3, - ProtocolConstants.MINECRAFT_1_20_5 + ProtocolConstants.MINECRAFT_1_20_5, + ProtocolConstants.MINECRAFT_1_21 ); if ( SNAPSHOT_SUPPORT ) { - supportedVersions.add( "1.21.x" ); - supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21 ); + // supportedVersions.add( "1.21.x" ); + // supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21 ); } SUPPORTED_VERSIONS = supportedVersions.build(); diff --git a/proxy/pom.xml b/proxy/pom.xml index 3729633c44..764ae27d4c 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-proxy - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar BungeeCord-Proxy @@ -108,7 +108,7 @@ com.mysql mysql-connector-j - 8.3.0 + 8.4.0 runtime diff --git a/query/pom.xml b/query/pom.xml index e07828dd67..84c5f2072f 100644 --- a/query/pom.xml +++ b/query/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-query - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar BungeeCord-Query diff --git a/slf4j/pom.xml b/slf4j/pom.xml index 3b944b8520..5c611d8ce9 100644 --- a/slf4j/pom.xml +++ b/slf4j/pom.xml @@ -6,13 +6,13 @@ net.md-5 bungeecord-parent - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT ../pom.xml net.md-5 bungeecord-slf4j - 1.20-R0.3-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar BungeeCord-SLF4J From df413f62dbb89b79154d59c563df811ed5409445 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 23 Jun 2024 08:22:54 +1000 Subject: [PATCH 08/58] #3677: Bump com.mysql:mysql-connector-j from 8.3.0 to 8.4.0 Bumps [com.mysql:mysql-connector-j](https://github.com/mysql/mysql-connector-j) from 8.3.0 to 8.4.0. - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.x/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.3.0...8.4.0) --- updated-dependencies: - dependency-name: com.mysql:mysql-connector-j dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From cda4537fbac3fb91a261ee4c812cf32fe880343e Mon Sep 17 00:00:00 2001 From: Outfluencer <48880402+Outfluencer@users.noreply.github.com> Date: Sun, 23 Jun 2024 00:47:05 +0200 Subject: [PATCH 09/58] #3695, #3696: Connect player to fallback if backend disconnects silently --- .../bungee/connection/DownstreamBridge.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) 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 4684bfd8d4..d518d05ffc 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 @@ -103,7 +103,7 @@ public void exception(Throwable t) throws Exception { server.setObsolete( true ); con.connectNow( def, ServerConnectEvent.Reason.SERVER_DOWN_REDIRECT ); - con.sendMessage( bungee.getTranslation( "server_went_down" ) ); + con.sendMessage( bungee.getTranslation( "server_went_down", def.getName() ) ); } else { con.disconnect( Util.exception( t ) ); @@ -120,13 +120,25 @@ public void disconnected(ChannelWrapper channel) throws Exception bungee.getReconnectHandler().setServer( con ); } - if ( !server.isObsolete() ) + ServerDisconnectEvent serverDisconnectEvent = new ServerDisconnectEvent( con, server.getInfo() ); + bungee.getPluginManager().callEvent( serverDisconnectEvent ); + + if ( server.isObsolete() ) { - con.disconnect( bungee.getTranslation( "lost_connection" ) ); + // do not perform any actions if the user has already moved + return; } - ServerDisconnectEvent serverDisconnectEvent = new ServerDisconnectEvent( con, server.getInfo() ); - bungee.getPluginManager().callEvent( serverDisconnectEvent ); + ServerInfo def = con.updateAndGetNextServer( server.getInfo() ); + if ( def != null ) + { + server.setObsolete( true ); + con.connectNow( def, ServerConnectEvent.Reason.SERVER_DOWN_REDIRECT ); + con.sendMessage( bungee.getTranslation( "server_went_down", def.getName() ) ); + } else + { + con.disconnect( bungee.getTranslation( "lost_connection" ) ); + } } @Override From 8b195d1d2124f18f795d9d6fa8a44bb28c930361 Mon Sep 17 00:00:00 2001 From: lax1dude Date: Tue, 25 Jun 2024 07:01:10 +1000 Subject: [PATCH 10/58] #3693: Compile natives as C instead of C++, check malloc/calloc return values for null --- native/compile-native.sh | 19 ++++++++-- ...ativeCipherImpl.cpp => NativeCipherImpl.c} | 25 ++++++------- ...eCompressImpl.cpp => NativeCompressImpl.c} | 34 +++++++++++------- native/src/main/c/shared.c | 15 ++++++++ native/src/main/c/shared.h | 10 ++++++ .../md_5/bungee/jni/NativeCodeException.java | 2 +- .../bungee/jni/zlib/NativeCompressImpl.java | 7 ++++ .../net/md_5/bungee/jni/zlib/NativeZlib.java | 10 +++++- native/src/main/resources/native-cipher.so | Bin 39784 -> 40016 bytes native/src/main/resources/native-compress.so | Bin 117056 -> 117320 bytes .../java/net/md_5/bungee/NativeZlibTest.java | 29 +++++++++++++++ 11 files changed, 121 insertions(+), 30 deletions(-) rename native/src/main/c/{NativeCipherImpl.cpp => NativeCipherImpl.c} (70%) rename native/src/main/c/{NativeCompressImpl.cpp => NativeCompressImpl.c} (69%) create mode 100644 native/src/main/c/shared.c create mode 100644 native/src/main/c/shared.h diff --git a/native/compile-native.sh b/native/compile-native.sh index c342e78243..5b6e54c4da 100755 --- a/native/compile-native.sh +++ b/native/compile-native.sh @@ -8,7 +8,20 @@ echo "Compiling mbedtls" echo "Compiling zlib" (cd zlib && CFLAGS=-fPIC ./configure --static && make) -CXX="g++ -shared -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/" +CC="gcc" +CFLAGS="-c -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/" +LDFLAGS="-shared" -$CXX -Imbedtls/include src/main/c/NativeCipherImpl.cpp -o src/main/resources/native-cipher.so mbedtls/library/libmbedcrypto.a -$CXX -Izlib src/main/c/NativeCompressImpl.cpp -o src/main/resources/native-compress.so zlib/libz.a +echo "Compiling bungee" +$CC $CFLAGS -o shared.o src/main/c/shared.c +$CC $CFLAGS -Imbedtls/include -o NativeCipherImpl.o src/main/c/NativeCipherImpl.c +$CC $CFLAGS -Izlib -o NativeCompressImpl.o src/main/c/NativeCompressImpl.c + +echo "Linking native-cipher.so" +$CC $LDFLAGS -o src/main/resources/native-cipher.so shared.o NativeCipherImpl.o mbedtls/library/libmbedcrypto.a + +echo "Linking native-compress.so" +$CC $LDFLAGS -o src/main/resources/native-compress.so shared.o NativeCompressImpl.o zlib/libz.a + +echo "Cleaning up" +rm shared.o NativeCipherImpl.o NativeCompressImpl.o diff --git a/native/src/main/c/NativeCipherImpl.cpp b/native/src/main/c/NativeCipherImpl.c similarity index 70% rename from native/src/main/c/NativeCipherImpl.cpp rename to native/src/main/c/NativeCipherImpl.c index cfe5f08994..727a2f1fea 100644 --- a/native/src/main/c/NativeCipherImpl.cpp +++ b/native/src/main/c/NativeCipherImpl.c @@ -2,31 +2,33 @@ #include #include +#include "shared.h" #include "net_md_5_bungee_jni_cipher_NativeCipherImpl.h" typedef unsigned char byte; -struct crypto_context { +typedef struct crypto_context { int mode; mbedtls_aes_context cipher; - byte *key; -}; + byte key[]; +} crypto_context; jlong JNICALL Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_init(JNIEnv* env, jobject obj, jboolean forEncryption, jbyteArray key) { - jsize keyLen = env->GetArrayLength(key); - jbyte *keyBytes = env->GetByteArrayElements(key, NULL); + jsize keyLen = (*env)->GetArrayLength(env, key); - crypto_context *crypto = (crypto_context*) malloc(sizeof (crypto_context)); - mbedtls_aes_init(&crypto->cipher); + crypto_context *crypto = (crypto_context*) malloc(sizeof (crypto_context) + (size_t) keyLen); + if (!crypto) { + throwOutOfMemoryError(env, "Failed to malloc new crypto_context"); + return 0; + } - mbedtls_aes_setkey_enc(&crypto->cipher, (byte*) keyBytes, keyLen * 8); + (*env)->GetByteArrayRegion(env, key, 0, keyLen, (jbyte*) &crypto->key); - crypto->key = (byte*) malloc(keyLen); - memcpy(crypto->key, keyBytes, keyLen); + mbedtls_aes_init(&crypto->cipher); + mbedtls_aes_setkey_enc(&crypto->cipher, (byte*) &crypto->key, keyLen * 8); crypto->mode = (forEncryption) ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT; - env->ReleaseByteArrayElements(key, keyBytes, JNI_ABORT); return (jlong) crypto; } @@ -34,7 +36,6 @@ void Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_free(JNIEnv* env, jobject crypto_context *crypto = (crypto_context*) ctx; mbedtls_aes_free(&crypto->cipher); - free(crypto->key); free(crypto); } diff --git a/native/src/main/c/NativeCompressImpl.cpp b/native/src/main/c/NativeCompressImpl.c similarity index 69% rename from native/src/main/c/NativeCompressImpl.cpp rename to native/src/main/c/NativeCompressImpl.c index 4b93a56a4e..7fb8e6b985 100644 --- a/native/src/main/c/NativeCompressImpl.cpp +++ b/native/src/main/c/NativeCompressImpl.c @@ -2,28 +2,28 @@ #include #include +#include "shared.h" #include "net_md_5_bungee_jni_zlib_NativeCompressImpl.h" typedef unsigned char byte; +static jclass classID; static jfieldID consumedID; static jfieldID finishedID; +static jmethodID makeExceptionID; void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_initFields(JNIEnv* env, jclass clazz) { - // We trust that these fields will be there - consumedID = env->GetFieldID(clazz, "consumed", "I"); - finishedID = env->GetFieldID(clazz, "finished", "Z"); + classID = clazz; + // We trust that these will be there + consumedID = (*env)->GetFieldID(env, clazz, "consumed", "I"); + finishedID = (*env)->GetFieldID(env, clazz, "finished", "Z"); + makeExceptionID = (*env)->GetMethodID(env, clazz, "makeException", "(Ljava/lang/String;I)Lnet/md_5/bungee/jni/NativeCodeException;"); } jint throwException(JNIEnv *env, const char* message, int err) { - // These can't be static for some unknown reason - jclass exceptionClass = env->FindClass("net/md_5/bungee/jni/NativeCodeException"); - jmethodID exceptionInitID = env->GetMethodID(exceptionClass, "", "(Ljava/lang/String;I)V"); - - jstring jMessage = env->NewStringUTF(message); - - jthrowable throwable = (jthrowable) env->NewObject(exceptionClass, exceptionInitID, jMessage, err); - return env->Throw(throwable); + jstring jMessage = (*env)->NewStringUTF(env, message); + jthrowable throwable = (jthrowable) (*env)->CallStaticObjectMethod(env, classID, makeExceptionID, jMessage, err); + return (*env)->Throw(env, throwable); } void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_reset(JNIEnv* env, jobject obj, jlong ctx, jboolean compress) { @@ -48,10 +48,17 @@ void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_end(JNIEnv* env, jlong JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_init(JNIEnv* env, jobject obj, jboolean compress, jint level) { z_stream* stream = (z_stream*) calloc(1, sizeof (z_stream)); + if (!stream) { + throwOutOfMemoryError(env, "Failed to calloc new z_stream"); + return 0; + } + int ret = (compress) ? deflateInit(stream, level) : inflateInit(stream); if (ret != Z_OK) { + free(stream); throwException(env, "Could not init z_stream", ret); + return 0; } return (jlong) stream; @@ -70,15 +77,16 @@ jint JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_process(JNIEnv* e switch (ret) { case Z_STREAM_END: - env->SetBooleanField(obj, finishedID, true); + (*env)->SetBooleanField(env, obj, finishedID, JNI_TRUE); break; case Z_OK: break; default: throwException(env, "Unknown z_stream return code", ret); + return -1; } - env->SetIntField(obj, consumedID, inLength - stream->avail_in); + (*env)->SetIntField(env, obj, consumedID, inLength - stream->avail_in); return outLength - stream->avail_out; } diff --git a/native/src/main/c/shared.c b/native/src/main/c/shared.c new file mode 100644 index 0000000000..8b35bc6b6f --- /dev/null +++ b/native/src/main/c/shared.c @@ -0,0 +1,15 @@ +#include "shared.h" +#include +#include + +void throwOutOfMemoryError(JNIEnv* env, const char* msg) { + jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); + if (!exceptionClass) { + // If the proxy ran out of memory, loading this class may fail + fprintf(stderr, "OUT OF MEMORY: %s\n", msg); + fprintf(stderr, "Could not load class java.lang.OutOfMemoryError!\n"); + exit(-1); + return; + } + (*env)->ThrowNew(env, exceptionClass, msg); +} diff --git a/native/src/main/c/shared.h b/native/src/main/c/shared.h new file mode 100644 index 0000000000..e3ad9c8624 --- /dev/null +++ b/native/src/main/c/shared.h @@ -0,0 +1,10 @@ +// This header contains functions to be shared between both native libraries + +#include + +#ifndef _INCLUDE_SHARED_H +#define _INCLUDE_SHARED_H + +void throwOutOfMemoryError(JNIEnv* env, const char* msg); + +#endif diff --git a/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java b/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java index 0020c54b08..1ac8f59012 100644 --- a/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java +++ b/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java @@ -1,6 +1,6 @@ package net.md_5.bungee.jni; -public class NativeCodeException extends Exception +public class NativeCodeException extends RuntimeException { public NativeCodeException(String message, int reason) diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java index 3bcc6ddbac..f78caf188d 100644 --- a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java @@ -1,5 +1,7 @@ package net.md_5.bungee.jni.zlib; +import net.md_5.bungee.jni.NativeCodeException; + public class NativeCompressImpl { @@ -20,4 +22,9 @@ public class NativeCompressImpl native long init(boolean compress, int compressionLevel); native int process(long ctx, long in, int inLength, long out, int outLength, boolean compress); + + NativeCodeException makeException(String message, int err) + { + return new NativeCodeException( message, err ); + } } diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java index f1f3b1e3ca..e9fe82b88b 100644 --- a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java @@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf; import java.util.zip.DataFormatException; import lombok.Getter; +import net.md_5.bungee.jni.NativeCodeException; public class NativeZlib implements BungeeZlib { @@ -48,7 +49,14 @@ public void process(ByteBuf in, ByteBuf out) throws DataFormatException { out.ensureWritable( 8192 ); - int processed = nativeCompress.process( ctx, in.memoryAddress() + in.readerIndex(), in.readableBytes(), out.memoryAddress() + out.writerIndex(), out.writableBytes(), compress ); + int processed; + try + { + processed = nativeCompress.process( ctx, in.memoryAddress() + in.readerIndex(), in.readableBytes(), out.memoryAddress() + out.writerIndex(), out.writableBytes(), compress ); + } catch ( NativeCodeException exception ) + { + throw (DataFormatException) new DataFormatException( "Failed to decompress via Zlib!" ).initCause( exception ); + } in.readerIndex( in.readerIndex() + nativeCompress.consumed ); out.writerIndex( out.writerIndex() + processed ); diff --git a/native/src/main/resources/native-cipher.so b/native/src/main/resources/native-cipher.so index 843f7f7a2ddcb36b92aee155b564f1e8b219fe3e..87cc1756fed8437a69adc88fae7f1d9da625760d 100755 GIT binary patch delta 8882 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"cwLXN^4d*36Xb-E=;&N4Xm%&j^aNk}q3N{uG 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 aZZ9J(YgFv!4)4<&aFE9wq4iFYX8#9~+S!`` delta 15840 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 ZVeCGcp%1@`48GYFGTDsrYe!hi{{tq2LU#ZF diff --git a/native/src/test/java/net/md_5/bungee/NativeZlibTest.java b/native/src/test/java/net/md_5/bungee/NativeZlibTest.java index 030fae28f5..70b2b475d5 100644 --- a/native/src/test/java/net/md_5/bungee/NativeZlibTest.java +++ b/native/src/test/java/net/md_5/bungee/NativeZlibTest.java @@ -28,6 +28,17 @@ public void doTest() throws DataFormatException test( new JavaZlib() ); } + @Test + public void testException() throws DataFormatException + { + if ( NativeCode.isSupported() ) + { + assertTrue( factory.load(), "Native code failed to load!" ); + testExceptionImpl( factory.newInstance() ); + } + testExceptionImpl( new JavaZlib() ); + } + private void test(BungeeZlib zlib) throws DataFormatException { System.out.println( "Testing: " + zlib ); @@ -66,4 +77,22 @@ private void test(BungeeZlib zlib) throws DataFormatException assertTrue( Arrays.equals( dataBuf, check ), "Results do not match" ); } + + private void testExceptionImpl(BungeeZlib zlib) throws DataFormatException + { + System.out.println( "Testing Exception: " + zlib ); + long start = System.currentTimeMillis(); + + byte[] dataBuf = new byte[ 1 << 12 ]; // 4096 random bytes + new Random().nextBytes( dataBuf ); + + zlib.init( false, 0 ); + + ByteBuf originalBuf = Unpooled.directBuffer(); + originalBuf.writeBytes( dataBuf ); + + ByteBuf decompressed = Unpooled.directBuffer(); + + assertThrows( DataFormatException.class, () -> zlib.process( originalBuf, decompressed ), "Decompressing random bytes did not result in a DataFormatException!" ); + } } From a57adcce001c36d1502aef99067b47ff1cc97caf Mon Sep 17 00:00:00 2001 From: Raraph84 Date: Sun, 28 Jul 2024 13:02:31 +0200 Subject: [PATCH 11/58] #3711, #3712: Don't try to reconnect player when it disconnects manually * Set server obsolete when disconnected by the proxy --- proxy/src/main/java/net/md_5/bungee/ServerConnection.java | 1 + proxy/src/main/java/net/md_5/bungee/ServerConnector.java | 1 - proxy/src/main/java/net/md_5/bungee/UserConnection.java | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java index 5d54045027..b4cc85b23b 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java @@ -81,6 +81,7 @@ public void disconnect(BaseComponent... reason) { Preconditions.checkArgument( reason.length == 0, "Server cannot have disconnect reason" ); + isObsolete = true; ch.close(); } 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 d735989127..95f6b1566e 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java @@ -373,7 +373,6 @@ private void cutThrough(ServerConnection server) // Remove from old servers if ( user.getServer() != null ) { - user.getServer().setObsolete( true ); user.getServer().disconnect( "Quitting" ); } 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 41670a1629..54842ca0ee 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -439,7 +439,6 @@ public void disconnect0(final BaseComponent reason) if ( server != null ) { - server.setObsolete( true ); server.disconnect( "Quitting" ); } } From 45d2f4400335189e1d4a478d006cf1f8c71ebd60 Mon Sep 17 00:00:00 2001 From: Raraph84 Date: Sun, 28 Jul 2024 13:04:41 +0200 Subject: [PATCH 12/58] #3713: Add default admin permissions for /find and /send --- proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 de85f0b28a..d659a138fb 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 @@ -95,7 +95,7 @@ public void load() } ) ); set( "permissions.admin", Arrays.asList( new String[] { - "bungeecord.command.alert", "bungeecord.command.end", "bungeecord.command.ip", "bungeecord.command.reload", "bungeecord.command.kick" + "bungeecord.command.alert", "bungeecord.command.end", "bungeecord.command.ip", "bungeecord.command.reload", "bungeecord.command.kick", "bungeecord.command.send", "bungeecord.command.find" } ) ); } From b64615e298ec18aa7377894c31937b72bd8f9b44 Mon Sep 17 00:00:00 2001 From: Nick Date: Sun, 28 Jul 2024 21:07:49 +1000 Subject: [PATCH 13/58] #3715: Fix maximum length for command packets --- .../java/net/md_5/bungee/protocol/packet/ClientCommand.java | 2 +- .../net/md_5/bungee/protocol/packet/UnsignedClientCommand.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientCommand.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientCommand.java index 887ff29f22..33c838f99a 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientCommand.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientCommand.java @@ -32,7 +32,7 @@ public class ClientCommand extends DefinedPacket @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - command = readString( buf, 256 ); + command = readString( buf, ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 ) ? 32767 : 256 ); timestamp = buf.readLong(); salt = buf.readLong(); diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/UnsignedClientCommand.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/UnsignedClientCommand.java index 97a2bb702f..5ee93eaf27 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/UnsignedClientCommand.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/UnsignedClientCommand.java @@ -21,7 +21,7 @@ public class UnsignedClientCommand extends DefinedPacket @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - command = readString( buf, 256 ); + command = readString( buf, ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 ) ? 32767 : 256 ); } @Override From c310e3339ff4a0ced4925c087c8954d02f0cebb2 Mon Sep 17 00:00:00 2001 From: Outfluencer <48880402+Outfluencer@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:57:09 +0200 Subject: [PATCH 14/58] #3720: Replace some println calls with proxy logger --- .../net/md_5/bungee/module/JenkinsModuleSource.java | 7 ++++--- .../java/net/md_5/bungee/module/ModuleManager.java | 10 +++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/proxy/src/main/java/net/md_5/bungee/module/JenkinsModuleSource.java b/proxy/src/main/java/net/md_5/bungee/module/JenkinsModuleSource.java index 2536435cde..064639f37d 100644 --- a/proxy/src/main/java/net/md_5/bungee/module/JenkinsModuleSource.java +++ b/proxy/src/main/java/net/md_5/bungee/module/JenkinsModuleSource.java @@ -7,6 +7,7 @@ import java.net.URLConnection; import lombok.Data; import net.md_5.bungee.Util; +import net.md_5.bungee.api.ProxyServer; @Data public class JenkinsModuleSource implements ModuleSource @@ -15,7 +16,7 @@ public class JenkinsModuleSource implements ModuleSource @Override public void retrieve(ModuleSpec module, ModuleVersion version) { - System.out.println( "Attempting to Jenkins download module " + module.getName() + " v" + version.getBuild() ); + ProxyServer.getInstance().getLogger().info( "Attempting to Jenkins download module " + module.getName() + " v" + version.getBuild() ); try { URL website = new URL( "https://ci.md-5.net/job/BungeeCord/" + version.getBuild() + "/artifact/module/" + module.getName().replace( '_', '-' ) + "/target/" + module.getName() + ".jar" ); @@ -25,10 +26,10 @@ public void retrieve(ModuleSpec module, ModuleVersion version) con.setReadTimeout( 15000 ); Files.write( ByteStreams.toByteArray( con.getInputStream() ), module.getFile() ); - System.out.println( "Download complete" ); + ProxyServer.getInstance().getLogger().info( "Download complete" ); } catch ( IOException ex ) { - System.out.println( "Failed to download: " + Util.exception( ex ) ); + ProxyServer.getInstance().getLogger().warning( "Failed to download: " + Util.exception( ex ) ); } } } diff --git a/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java b/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java index 9ec4920e93..d3c50b0f63 100644 --- a/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java +++ b/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java @@ -44,7 +44,7 @@ public void load(ProxyServer proxy, File moduleDirectory) throws Exception ModuleVersion bungeeVersion = ModuleVersion.parse( proxy.getVersion() ); if ( bungeeVersion == null ) { - System.out.println( "Couldn't detect bungee version. Custom build?" ); + proxy.getLogger().warning( "Couldn't detect bungee version. Custom build?" ); return; } @@ -105,19 +105,19 @@ public void load(ProxyServer proxy, File moduleDirectory) throws Exception ModuleSource source = knownSources.get( uri.getScheme() ); if ( source == null ) { - System.out.println( "Unknown module source: " + s ); + proxy.getLogger().warning( "Unknown module source: " + s ); continue; } String name = uri.getAuthority(); if ( name == null ) { - System.out.println( "Unknown module host: " + s ); + proxy.getLogger().warning( "Unknown module host: " + s ); continue; } ModuleSpec spec = new ModuleSpec( name, new File( moduleDirectory, name + ".jar" ), source ); modules.add( spec ); - System.out.println( "Discovered module: " + spec ); + proxy.getLogger().info( "Discovered module: " + spec ); } for ( ModuleSpec module : modules ) @@ -126,7 +126,7 @@ public void load(ProxyServer proxy, File moduleDirectory) throws Exception if ( !bungeeVersion.equals( moduleVersion ) ) { - System.out.println( "Attempting to update plugin from " + moduleVersion + " to " + bungeeVersion ); + proxy.getLogger().info( "Attempting to update plugin from " + moduleVersion + " to " + bungeeVersion ); module.getProvider().retrieve( module, bungeeVersion ); } } From e49759025fcc7a422d4b96f6cecf12ab445d4bc3 Mon Sep 17 00:00:00 2001 From: lax1dude Date: Thu, 8 Aug 2024 18:19:20 +1000 Subject: [PATCH 15/58] #3716, #3707: Fix native-cipher segfault when using musl libc --- native/compile-native.sh | 4 ++- native/src/main/c/NativeCipherImpl.c | 6 ++++ native/src/main/c/mbedtls_custom_config.h | 31 +++++++++++++++++++++ native/src/main/resources/native-cipher.so | Bin 40016 -> 48648 bytes 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 native/src/main/c/mbedtls_custom_config.h diff --git a/native/compile-native.sh b/native/compile-native.sh index 5b6e54c4da..b5c011d8a7 100755 --- a/native/compile-native.sh +++ b/native/compile-native.sh @@ -2,8 +2,10 @@ set -eu +CWD=$(pwd) + echo "Compiling mbedtls" -(cd mbedtls && make no_test) +(cd mbedtls && CFLAGS="-fPIC -I$CWD/src/main/c -DMBEDTLS_USER_CONFIG_FILE=''" make no_test) echo "Compiling zlib" (cd zlib && CFLAGS=-fPIC ./configure --static && make) diff --git a/native/src/main/c/NativeCipherImpl.c b/native/src/main/c/NativeCipherImpl.c index 727a2f1fea..020aaa1d26 100644 --- a/native/src/main/c/NativeCipherImpl.c +++ b/native/src/main/c/NativeCipherImpl.c @@ -5,11 +5,15 @@ #include "shared.h" #include "net_md_5_bungee_jni_cipher_NativeCipherImpl.h" +// Hack to keep the compiler from optimizing the memset away +static void *(*const volatile memset_func)(void *, int, size_t) = memset; + typedef unsigned char byte; typedef struct crypto_context { int mode; mbedtls_aes_context cipher; + int keyLen; byte key[]; } crypto_context; @@ -22,6 +26,7 @@ jlong JNICALL Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_init(JNIEnv* env return 0; } + crypto->keyLen = (int) keyLen; (*env)->GetByteArrayRegion(env, key, 0, keyLen, (jbyte*) &crypto->key); mbedtls_aes_init(&crypto->cipher); @@ -36,6 +41,7 @@ void Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_free(JNIEnv* env, jobject crypto_context *crypto = (crypto_context*) ctx; mbedtls_aes_free(&crypto->cipher); + memset_func(crypto->key, 0, (size_t) crypto->keyLen); free(crypto); } diff --git a/native/src/main/c/mbedtls_custom_config.h b/native/src/main/c/mbedtls_custom_config.h new file mode 100644 index 0000000000..a32df88719 --- /dev/null +++ b/native/src/main/c/mbedtls_custom_config.h @@ -0,0 +1,31 @@ + +// This is a hack to deal with a glitch that happens when mbedtls is compiled against glibc +// but then run on a linux distro that uses musl libc. This implementation of the zeroize +// is compatible with both glibc and musl without requiring the library to be recompiled. + +// I checked with a disassembler and for BungeeCord's usage of the library, implementing +// this function as a static function only resulted in 2 different subroutines referencing +// different versions of memset_func, so we might as well keep things simple and use a +// static function here instead of requiring the mbedtls makefile to be modified to add +// additional source files. + +#ifndef _INCLUDE_MBEDTLS_CUSTOM_CONFIG_H +#define _INCLUDE_MBEDTLS_CUSTOM_CONFIG_H + +#include + +#define MBEDTLS_PLATFORM_ZEROIZE_ALT + +#define mbedtls_platform_zeroize mbedtls_platform_zeroize_impl + +// hack to prevent compilers from optimizing the memset away +static void *(*const volatile memset_func)(void *, int, size_t) = memset; + +static void mbedtls_platform_zeroize_impl(void *buf, size_t len) { + if (len > 0) { + memset_func(buf, 0, len); + } +} + +#endif // _INCLUDE_MBEDTLS_CUSTOM_CONFIG_H + diff --git a/native/src/main/resources/native-cipher.so b/native/src/main/resources/native-cipher.so index 87cc1756fed8437a69adc88fae7f1d9da625760d..45afa5f87ab684fdbcf4d75acc5d60862d7df837 100755 GIT binary patch literal 48648 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 Zqy6$MJA3G<{hEiB8uXK&+7Oo&{tw~_u0#L; From 6b612302e14af1348ff758d2256d79ff51a04ceb Mon Sep 17 00:00:00 2001 From: lax1dude Date: Thu, 8 Aug 2024 18:19:20 +1000 Subject: [PATCH 16/58] #3718, #3717: Add check for SSE 4.2 and PCLMUL support to native zlib --- native/src/main/c/NativeCompressImpl.c | 5 ++++ native/src/main/c/cpuid_helper.h | 22 ++++++++++++++++++ ..._md_5_bungee_jni_zlib_NativeCompressImpl.h | 8 +++++++ .../java/net/md_5/bungee/jni/NativeCode.java | 21 ++++++++++++++++- .../md_5/bungee/jni/NativeCodeException.java | 6 +++++ .../bungee/jni/zlib/NativeCompressImpl.java | 2 ++ .../net/md_5/bungee/jni/zlib/NativeZlib.java | 8 +++++++ native/src/main/resources/native-compress.so | Bin 117320 -> 117408 bytes .../java/net/md_5/bungee/NativeZlibTest.java | 2 +- .../md_5/bungee/compress/CompressFactory.java | 2 +- 10 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 native/src/main/c/cpuid_helper.h diff --git a/native/src/main/c/NativeCompressImpl.c b/native/src/main/c/NativeCompressImpl.c index 7fb8e6b985..b6b27682b0 100644 --- a/native/src/main/c/NativeCompressImpl.c +++ b/native/src/main/c/NativeCompressImpl.c @@ -3,6 +3,7 @@ #include #include "shared.h" +#include "cpuid_helper.h" #include "net_md_5_bungee_jni_zlib_NativeCompressImpl.h" typedef unsigned char byte; @@ -26,6 +27,10 @@ jint throwException(JNIEnv *env, const char* message, int err) { return (*env)->Throw(env, throwable); } +JNIEXPORT jboolean JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_checkSupported(JNIEnv* env, jobject obj) { + return (jboolean) checkCompressionNativesSupport(); +} + void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_reset(JNIEnv* env, jobject obj, jlong ctx, jboolean compress) { z_stream* stream = (z_stream*) ctx; int ret = (compress) ? deflateReset(stream) : inflateReset(stream); diff --git a/native/src/main/c/cpuid_helper.h b/native/src/main/c/cpuid_helper.h new file mode 100644 index 0000000000..2394ecd6cf --- /dev/null +++ b/native/src/main/c/cpuid_helper.h @@ -0,0 +1,22 @@ +// Header to check for SSE 4.2 support in compression natives +// GCC only! + +#ifndef _INCLUDE_CPUID_HELPER_H +#define _INCLUDE_CPUID_HELPER_H + +#include +#include + +static inline bool checkCompressionNativesSupport() { + unsigned int eax; + unsigned int ebx; + unsigned int ecx; + unsigned int edx; + if(__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { + return (ecx & bit_PCLMUL) != 0 && (ecx & bit_SSE4_2) != 0; + }else { + return false; + } +} + +#endif // _INCLUDE_CPUID_HELPER_H diff --git a/native/src/main/c/net_md_5_bungee_jni_zlib_NativeCompressImpl.h b/native/src/main/c/net_md_5_bungee_jni_zlib_NativeCompressImpl.h index befb7dcc30..5c73e1310d 100644 --- a/native/src/main/c/net_md_5_bungee_jni_zlib_NativeCompressImpl.h +++ b/native/src/main/c/net_md_5_bungee_jni_zlib_NativeCompressImpl.h @@ -15,6 +15,14 @@ extern "C" { JNIEXPORT void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_initFields (JNIEnv *, jclass); +/* + * Class: net_md_5_bungee_jni_zlib_NativeCompressImpl + * Method: checkSupported + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_checkSupported + (JNIEnv *, jobject); + /* * Class: net_md_5_bungee_jni_zlib_NativeCompressImpl * Method: end diff --git a/native/src/main/java/net/md_5/bungee/jni/NativeCode.java b/native/src/main/java/net/md_5/bungee/jni/NativeCode.java index 62bdaa0e8c..be4d5b0b82 100644 --- a/native/src/main/java/net/md_5/bungee/jni/NativeCode.java +++ b/native/src/main/java/net/md_5/bungee/jni/NativeCode.java @@ -15,14 +15,23 @@ public final class NativeCode private final String name; private final Supplier javaImpl; private final Supplier nativeImpl; + private final boolean enableNativeFlag; + private final boolean extendedSupportCheck; // private boolean loaded; public NativeCode(String name, Supplier javaImpl, Supplier nativeImpl) + { + this( name, javaImpl, nativeImpl, false ); + } + + public NativeCode(String name, Supplier javaImpl, Supplier nativeImpl, boolean extendedSupportCheck) { this.name = name; this.javaImpl = javaImpl; this.nativeImpl = nativeImpl; + this.enableNativeFlag = Boolean.parseBoolean( System.getProperty( "net.md_5.bungee.jni." + name + ".enable", "true" ) ); + this.extendedSupportCheck = extendedSupportCheck; } public T newInstance() @@ -32,7 +41,7 @@ public T newInstance() public boolean load() { - if ( !loaded && isSupported() ) + if ( enableNativeFlag && !loaded && isSupported() ) { String fullName = "bungeecord-" + name; @@ -59,6 +68,13 @@ public boolean load() } System.load( temp.getPath() ); + + if ( extendedSupportCheck ) + { + // Should throw NativeCodeException if incompatible + nativeImpl.get(); + } + loaded = true; } catch ( IOException ex ) { @@ -66,6 +82,9 @@ public boolean load() } catch ( UnsatisfiedLinkError ex ) { System.out.println( "Could not load native library: " + ex.getMessage() ); + } catch ( NativeCodeException ex ) + { + System.out.println( "Native library " + name + " is incompatible: " + ex.getMessage() ); } } } diff --git a/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java b/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java index 1ac8f59012..d264675c92 100644 --- a/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java +++ b/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java @@ -7,4 +7,10 @@ public NativeCodeException(String message, int reason) { super( message + " : " + reason ); } + + public NativeCodeException(String message) + { + super( message ); + } + } diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java index f78caf188d..d91f9b58b4 100644 --- a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java @@ -15,6 +15,8 @@ public class NativeCompressImpl static native void initFields(); + native boolean checkSupported(); + native void end(long ctx, boolean compress); native void reset(long ctx, boolean compress); diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java index e9fe82b88b..96e3077713 100644 --- a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java @@ -15,6 +15,14 @@ public class NativeZlib implements BungeeZlib private boolean compress; private long ctx; + public NativeZlib() + { + if ( !nativeCompress.checkSupported() ) + { + throw new NativeCodeException( "This CPU does not support the required SSE 4.2 and/or PCLMUL extensions!" ); + } + } + @Override public void init(boolean compress, int level) { diff --git a/native/src/main/resources/native-compress.so b/native/src/main/resources/native-compress.so index 2ebbfed6137a110f9e9923e101c85d2ea73f9493..181396f1d8460a48af05e7a3605811b5633e5f60 100755 GIT binary patch delta 10528 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 o{BhZbnd;n>;)wjTjCBMhxQKYea>em$ZziIJP factory = new NativeCode<>( "native-compress", JavaZlib::new, NativeZlib::new ); + private final NativeCode factory = new NativeCode<>( "native-compress", JavaZlib::new, NativeZlib::new, true ); @Test public void doTest() throws DataFormatException diff --git a/proxy/src/main/java/net/md_5/bungee/compress/CompressFactory.java b/proxy/src/main/java/net/md_5/bungee/compress/CompressFactory.java index 2e0c63d1f0..e4cbbeb765 100644 --- a/proxy/src/main/java/net/md_5/bungee/compress/CompressFactory.java +++ b/proxy/src/main/java/net/md_5/bungee/compress/CompressFactory.java @@ -8,5 +8,5 @@ public class CompressFactory { - public static final NativeCode zlib = new NativeCode<>( "native-compress", JavaZlib::new, NativeZlib::new ); + public static final NativeCode zlib = new NativeCode<>( "native-compress", JavaZlib::new, NativeZlib::new, true ); } From cd56fb32c207b39b9470d66d7c61f68d9f0c7e78 Mon Sep 17 00:00:00 2001 From: lax1dude Date: Fri, 9 Aug 2024 19:07:38 +1000 Subject: [PATCH 17/58] #3722: Disable GZIP in native compress library (no longer requires PCLMUL) --- native/compile-native.sh | 2 +- native/src/main/c/cpuid_helper.h | 9 +++------ native/src/main/resources/native-compress.so | Bin 117408 -> 113312 bytes 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/native/compile-native.sh b/native/compile-native.sh index b5c011d8a7..a47bc812ef 100755 --- a/native/compile-native.sh +++ b/native/compile-native.sh @@ -8,7 +8,7 @@ echo "Compiling mbedtls" (cd mbedtls && CFLAGS="-fPIC -I$CWD/src/main/c -DMBEDTLS_USER_CONFIG_FILE=''" make no_test) echo "Compiling zlib" -(cd zlib && CFLAGS=-fPIC ./configure --static && make) +(cd zlib && CFLAGS="-fPIC -DNO_GZIP" ./configure --static && make) CC="gcc" CFLAGS="-c -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/" diff --git a/native/src/main/c/cpuid_helper.h b/native/src/main/c/cpuid_helper.h index 2394ecd6cf..f44c8c729d 100644 --- a/native/src/main/c/cpuid_helper.h +++ b/native/src/main/c/cpuid_helper.h @@ -1,4 +1,4 @@ -// Header to check for SSE 4.2 support in compression natives +// Header to check for SSE 2, SSSE 3, and SSE 4.2 support in compression natives // GCC only! #ifndef _INCLUDE_CPUID_HELPER_H @@ -8,12 +8,9 @@ #include static inline bool checkCompressionNativesSupport() { - unsigned int eax; - unsigned int ebx; - unsigned int ecx; - unsigned int edx; + unsigned int eax, ebx, ecx, edx; if(__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { - return (ecx & bit_PCLMUL) != 0 && (ecx & bit_SSE4_2) != 0; + return (edx & bit_SSE2) != 0 && (ecx & bit_SSSE3) != 0 && (ecx & bit_SSE4_2) != 0; }else { return false; } diff --git a/native/src/main/resources/native-compress.so b/native/src/main/resources/native-compress.so index 181396f1d8460a48af05e7a3605811b5633e5f60..968bb06d6f0a62f97b5938dffe12f19b30e27b73 100755 GIT binary patch delta 19594 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 From 534148763fa65c2a3334d6bed9d32ece3e78148d Mon Sep 17 00:00:00 2001 From: Outfluencer <48880402+Outfluencer@users.noreply.github.com> Date: Thu, 22 Aug 2024 11:25:45 +0200 Subject: [PATCH 18/58] #3721: Improve same uuid and name checks We didn't return so the login event was fired for a disconnected player --- .../bungee/connection/InitialHandler.java | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) 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 1d9a9ee5de..413c81237e 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 @@ -580,34 +580,24 @@ private void finish() } } + ProxiedPlayer oldName = bungee.getPlayer( getName() ); + if ( oldName != null ) + { + // TODO See #1218 + disconnect( bungee.getTranslation( "already_connected_proxy" ) ); + return; + } + if ( isOnlineMode() ) { - // Check for multiple connections - // We have to check for the old name first - ProxiedPlayer oldName = bungee.getPlayer( getName() ); - if ( oldName != null ) - { - // TODO See #1218 - disconnect( bungee.getTranslation( "already_connected_proxy" ) ); - } // And then also for their old UUID ProxiedPlayer oldID = bungee.getPlayer( getUniqueId() ); if ( oldID != null ) - { - // TODO See #1218 - disconnect( bungee.getTranslation( "already_connected_proxy" ) ); - } - } else - { - // In offline mode the existing user stays and we kick the new one - ProxiedPlayer oldName = bungee.getPlayer( getName() ); - if ( oldName != null ) { // TODO See #1218 disconnect( bungee.getTranslation( "already_connected_proxy" ) ); return; } - } Callback complete = new Callback() From e1d4b6adc7d00bb6d907d682ceeb03969866ee9d Mon Sep 17 00:00:00 2001 From: Outfluencer <48880402+Outfluencer@users.noreply.github.com> Date: Sat, 24 Aug 2024 12:27:05 +0200 Subject: [PATCH 19/58] #3731: Update cookie handling with vanilla limits and don't allow unrequested cookies --- .../java/net/md_5/bungee/protocol/packet/CookieResponse.java | 2 +- .../main/java/net/md_5/bungee/connection/InitialHandler.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/CookieResponse.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/CookieResponse.java index 77a541b622..24b1fa05d8 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/CookieResponse.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/CookieResponse.java @@ -23,7 +23,7 @@ public class CookieResponse extends DefinedPacket public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { cookie = readString( buf ); - data = readNullable( DefinedPacket::readArray, buf ); + data = readNullable( read -> DefinedPacket.readArray( read, 5120 ), buf ); } @Override 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 413c81237e..3bbf688b54 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 @@ -716,6 +716,10 @@ public void handle(CookieResponse cookieResponse) throw CancelSendSignal.INSTANCE; } + + // if there is no userCon we can't have a connection to a backend server that could have requested this cookie + // which means that this cookie is invalid as the proxy also has not requested it + Preconditions.checkState( userCon != null, "not requested cookie received" ); } @Override From d32eedd3333c1b080a4b9c2b5d7a77f7d354786a Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Sun, 25 Aug 2024 09:15:03 +1000 Subject: [PATCH 20/58] #3727: 24w34a snapshot support --- .../net/md_5/bungee/protocol/Protocol.java | 85 +++++++++++++------ .../bungee/protocol/ProtocolConstants.java | 3 +- .../protocol/packet/ClientSettings.java | 16 ++++ .../md_5/bungee/protocol/packet/Login.java | 11 ++- .../protocol/packet/PlayerListItem.java | 3 + .../protocol/packet/PlayerListItemUpdate.java | 9 +- .../md_5/bungee/protocol/packet/Respawn.java | 9 ++ .../java/net/md_5/bungee/ServerConnector.java | 8 +- .../net/md_5/bungee/entitymap/EntityMap.java | 2 + .../bungee/entitymap/EntityMap_1_16_2.java | 5 ++ 10 files changed, 115 insertions(+), 36 deletions(-) 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 317fb2d456..35712217b3 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 @@ -146,7 +146,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x41 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x43 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x45 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x47 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x47 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x48 ) ); TO_CLIENT.registerPacket( BossBar.class, @@ -206,7 +207,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x58 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x5A ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x5C ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x5E ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x5E ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x60 ) ); TO_CLIENT.registerPacket( ScoreboardScore.class, @@ -224,13 +226,15 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x5B ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x5D ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x5F ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x61 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x61 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x64 ) ); TO_CLIENT.registerPacket( ScoreboardScoreReset.class, ScoreboardScoreReset::new, map( ProtocolConstants.MINECRAFT_1_20_3, 0x42 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x44 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x44 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x45 ) ); TO_CLIENT.registerPacket( ScoreboardDisplay.class, @@ -248,7 +252,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x51 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x53 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x55 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x57 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x57 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x58 ) ); TO_CLIENT.registerPacket( Team.class, @@ -266,7 +271,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x5A ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x5C ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x5E ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x60 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x60 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x63 ) ); TO_CLIENT.registerPacket( PluginMessage.class, @@ -321,7 +327,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x5F ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x61 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x63 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x65 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x65 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x68 ) ); TO_CLIENT.registerPacket( ClearTitles.class, @@ -342,7 +349,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x5D ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x5F ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x61 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x63 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x63 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x66 ) ); TO_CLIENT.registerPacket( TitleTimes.class, @@ -354,7 +362,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x60 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x62 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x64 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x66 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x66 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x69 ) ); TO_CLIENT.registerPacket( SystemChat.class, @@ -365,7 +374,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x64 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x67 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x69 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x6C ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x6C ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x6F ) ); TO_CLIENT.registerPacket( PlayerListHeaderFooter.class, @@ -387,7 +397,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x65 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x68 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x6A ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x6D ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x6D ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x70 ) ); TO_CLIENT.registerPacket( EntityStatus.class, @@ -458,7 +469,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x45 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x47 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x49 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x4B ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x4B ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x4C ) ); TO_CLIENT.registerPacket( PlayerListItemRemove.class, @@ -466,7 +478,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_3, 0x35 ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x39 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x3B ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x3D ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x3D ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x3E ) ); TO_CLIENT.registerPacket( PlayerListItemUpdate.class, @@ -474,14 +487,16 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_3, 0x36 ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x3A ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x3C ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x3E ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x3E ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x3F ) ); TO_CLIENT.registerPacket( StartConfiguration.class, StartConfiguration::new, map( ProtocolConstants.MINECRAFT_1_20_2, 0x65 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x67 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x69 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x69 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x6C ) ); TO_CLIENT.registerPacket( CookieRequest.class, @@ -491,22 +506,26 @@ public enum Protocol TO_CLIENT.registerPacket( StoreCookie.class, StoreCookie::new, - map( ProtocolConstants.MINECRAFT_1_20_5, 0x6B ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x6B ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x6E ) ); TO_CLIENT.registerPacket( Transfer.class, Transfer::new, - map( ProtocolConstants.MINECRAFT_1_20_5, 0x73 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x73 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x76 ) ); TO_CLIENT.registerPacket( DisconnectReportDetails.class, DisconnectReportDetails::new, - map( ProtocolConstants.MINECRAFT_1_21, 0x7A ) + map( ProtocolConstants.MINECRAFT_1_21, 0x7A ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x7D ) ); TO_CLIENT.registerPacket( ServerLinks.class, ServerLinks::new, - map( ProtocolConstants.MINECRAFT_1_21, 0x7B ) + map( ProtocolConstants.MINECRAFT_1_21, 0x7B ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x7E ) ); TO_SERVER.registerPacket( @@ -526,7 +545,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x12 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x14 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x15 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x18 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x18 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x1A ) ); TO_SERVER.registerPacket( Chat.class, Chat::new, @@ -542,19 +562,23 @@ public enum Protocol ClientCommand::new, map( ProtocolConstants.MINECRAFT_1_19, 0x03 ), map( ProtocolConstants.MINECRAFT_1_19_1, 0x04 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x05 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x05 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x06 ) ); TO_SERVER.registerPacket( UnsignedClientCommand.class, UnsignedClientCommand::new, - map( ProtocolConstants.MINECRAFT_1_20_5, 0x04 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x04 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x05 ) + ); TO_SERVER.registerPacket( ClientChat.class, ClientChat::new, map( ProtocolConstants.MINECRAFT_1_19, 0x04 ), map( ProtocolConstants.MINECRAFT_1_19_1, 0x05 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x06 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x06 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x07 ) ); TO_SERVER.registerPacket( TabCompleteRequest.class, @@ -570,7 +594,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_3, 0x08 ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x09 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x0A ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x0B ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x0B ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x0D ) ); TO_SERVER.registerPacket( ClientSettings.class, @@ -585,7 +610,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_3, 0x07 ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x08 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x09 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x0A ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x0A ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x0C ) ); TO_SERVER.registerPacket( PluginMessage.class, @@ -603,18 +629,21 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x0D ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x0F ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x10 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x12 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x12 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x14 ) ); TO_SERVER.registerPacket( StartConfiguration.class, StartConfiguration::new, map( ProtocolConstants.MINECRAFT_1_20_2, 0x0B ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x0C ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x0C ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x0E ) ); TO_SERVER.registerPacket( CookieResponse.class, CookieResponse::new, - map( ProtocolConstants.MINECRAFT_1_20_5, 0x11 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x11 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x13 ) ); } }, 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 82ef4c5d41..7798c85eb6 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 @@ -47,6 +47,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; + public static final int MINECRAFT_1_21_2 = 1073742030; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; @@ -114,7 +115,7 @@ public class ProtocolConstants if ( SNAPSHOT_SUPPORT ) { // supportedVersions.add( "1.21.x" ); - // supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21 ); + supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_2 ); } SUPPORTED_VERSIONS = supportedVersions.build(); 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 d7d4e6ab4f..a796b0e238 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 @@ -25,6 +25,7 @@ public class ClientSettings extends DefinedPacket private int mainHand; private boolean disableTextFiltering; private boolean allowServerListing; + private ParticleStatus particleStatus; @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) @@ -46,6 +47,10 @@ public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protoco { allowServerListing = buf.readBoolean(); } + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 ) + { + particleStatus = ParticleStatus.values()[readVarInt( buf )]; + } } @Override @@ -74,6 +79,10 @@ public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protoc { buf.writeBoolean( allowServerListing ); } + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 ) + { + writeVarInt( particleStatus.ordinal(), buf ); + } } @Override @@ -81,4 +90,11 @@ public void handle(AbstractPacketHandler handler) throws Exception { handler.handle( this ); } + + public enum ParticleStatus + { + ALL, + DECREASED, + MINIMAL; + } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Login.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Login.java index a4e804e14c..8314cf4b9b 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Login.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Login.java @@ -41,6 +41,7 @@ public class Login extends DefinedPacket private boolean flat; private Location deathLocation; private int portalCooldown; + private int seaLevel; private boolean secureProfile; @Override @@ -161,7 +162,10 @@ public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protoco { portalCooldown = readVarInt( buf ); } - + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 ) + { + seaLevel = readVarInt( buf ); + } if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 ) { secureProfile = buf.readBoolean(); @@ -293,7 +297,10 @@ public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protoc { writeVarInt( portalCooldown, buf ); } - + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 ) + { + writeVarInt( seaLevel, buf ); + } if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 ) { buf.writeBoolean( secureProfile ); 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 34a12a80ed..60f7305b0d 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 @@ -145,5 +145,8 @@ public static class Item // ADD_PLAYER & UPDATE_DISPLAY_NAME BaseComponent displayName; + // UPDATE_LIST_ORDER 1.21.2 + Integer listOrder; + } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItemUpdate.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItemUpdate.java index f6708b062f..a45b001e87 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItemUpdate.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItemUpdate.java @@ -61,6 +61,9 @@ public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protoco item.displayName = DefinedPacket.readBaseComponent( buf, protocolVersion ); } break; + case UPDATE_LIST_ORDER: + item.listOrder = DefinedPacket.readVarInt( buf ); + break; } } } @@ -109,6 +112,9 @@ public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protoc DefinedPacket.writeBaseComponent( item.displayName, buf, protocolVersion ); } break; + case UPDATE_LIST_ORDER: + DefinedPacket.writeVarInt( item.listOrder, buf ); + break; } } } @@ -128,6 +134,7 @@ public static enum Action UPDATE_GAMEMODE, UPDATE_LISTED, UPDATE_LATENCY, - UPDATE_DISPLAY_NAME; + UPDATE_DISPLAY_NAME, + UPDATE_LIST_ORDER; } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Respawn.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Respawn.java index 46859e633e..e57db1c23f 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Respawn.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Respawn.java @@ -30,6 +30,7 @@ public class Respawn extends DefinedPacket private byte copyMeta; private Location deathLocation; private int portalCooldown; + private int seaLevel; @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) @@ -84,6 +85,10 @@ public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protoco { portalCooldown = readVarInt( buf ); } + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 ) + { + seaLevel = readVarInt( buf ); + } if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_2 ) { copyMeta = buf.readByte(); @@ -148,6 +153,10 @@ public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protoc { writeVarInt( portalCooldown, buf ); } + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 ) + { + writeVarInt( seaLevel, buf ); + } if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_2 ) { buf.writeByte( copyMeta ); 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 95f6b1566e..8283286563 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java @@ -252,7 +252,7 @@ public static void handleLogin(ProxyServer bungee, ChannelWrapper ch, UserConnec // Set tab list size, TODO: what shall we do about packet mutability 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.isLimitedCrafting(), login.isDebug(), login.isFlat(), login.getDeathLocation(), - login.getPortalCooldown(), login.isSecureProfile() ); + login.getPortalCooldown(), login.getSeaLevel(), login.isSecureProfile() ); user.unsafe().sendPacket( modLogin ); @@ -270,7 +270,7 @@ public static void handleLogin(ProxyServer bungee, ChannelWrapper ch, UserConnec user.getSentBossBars().clear(); user.unsafe().sendPacket( new Respawn( login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), (byte) 0, login.getDeathLocation(), - login.getPortalCooldown() ) ); + login.getPortalCooldown(), login.getSeaLevel() ) ); } else { user.unsafe().sendPacket( BungeeCord.getInstance().registerChannels( user.getPendingConnection().getVersion() ) ); @@ -332,12 +332,12 @@ public static void handleLogin(ProxyServer bungee, ChannelWrapper ch, UserConnec if ( login.getDimension() == user.getDimension() ) { 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(), - (byte) 0, login.getDeathLocation(), login.getPortalCooldown() ) ); + (byte) 0, login.getDeathLocation(), login.getPortalCooldown(), login.getSeaLevel() ) ); } user.setServerEntityId( login.getEntityId() ); user.unsafe().sendPacket( new Respawn( login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), - (byte) 0, login.getDeathLocation(), login.getPortalCooldown() ) ); + (byte) 0, login.getDeathLocation(), login.getPortalCooldown(), login.getSeaLevel() ) ); if ( user.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_14 ) { user.unsafe().sendPacket( new ViewDistance( login.getViewDistance() ) ); 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 70b58fab29..755b8fd6ab 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 @@ -89,6 +89,8 @@ public static EntityMap getEntityMap(int version) case ProtocolConstants.MINECRAFT_1_20_5: case ProtocolConstants.MINECRAFT_1_21: return EntityMap_1_16_2.INSTANCE_1_20_5; + case ProtocolConstants.MINECRAFT_1_21_2: + return EntityMap_1_16_2.INSTANCE_1_21_2; } throw new RuntimeException( "Version " + version + " has no entity map" ); } 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 index 0ed7ad6423..a71932a74a 100644 --- 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 @@ -23,6 +23,7 @@ class EntityMap_1_16_2 extends EntityMap static final EntityMap_1_16_2 INSTANCE_1_20_2 = new EntityMap_1_16_2( -1, 0x33 ); static final EntityMap_1_16_2 INSTANCE_1_20_3 = new EntityMap_1_16_2( -1, 0x34 ); static final EntityMap_1_16_2 INSTANCE_1_20_5 = new EntityMap_1_16_2( -1, 0x37 ); + static final EntityMap_1_16_2 INSTANCE_1_21_2 = new EntityMap_1_16_2( -1, 0x39 ); // private final int spawnPlayerId; private final int spectateId; @@ -31,6 +32,10 @@ class EntityMap_1_16_2 extends EntityMap @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") public void rewriteClientbound(ByteBuf packet, int oldId, int newId, int protocolVersion) { + if ( spawnPlayerId == -1 ) + { + return; + } // Special cases int readerIndex = packet.readerIndex(); int packetId = DefinedPacket.readVarInt( packet ); From 79f85a2ce2b523dc7861594da249b80545ea2534 Mon Sep 17 00:00:00 2001 From: Janmm14 Date: Sun, 25 Aug 2024 09:27:40 +1000 Subject: [PATCH 21/58] #3662: Add deprecation warning to ComponentSerializer.toString(Object) It taking all objects is error-prone, deprecate it and create overloads for used acceptable types. --- .../md_5/bungee/chat/ComponentSerializer.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java index 1164804f7c..15ffc1ee24 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java @@ -20,6 +20,7 @@ import net.md_5.bungee.api.chat.SelectorComponent; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TranslatableComponent; +import net.md_5.bungee.api.chat.hover.content.Content; import net.md_5.bungee.api.chat.hover.content.Entity; import net.md_5.bungee.api.chat.hover.content.EntitySerializer; import net.md_5.bungee.api.chat.hover.content.Item; @@ -158,11 +159,28 @@ public static JsonElement toJson(ComponentStyle style) return gson.toJsonTree( style ); } + /** + * @param object the object to serialize + * @return the JSON string representation of the object + * @deprecated Error-prone, be careful which object you input here + */ + @Deprecated public static String toString(Object object) { return gson.toJson( object ); } + /** + * @param content the content to serialize + * @return the JSON string representation of the object + * @deprecated for legacy internal use only + */ + @Deprecated + public static String toString(Content content) + { + return gson.toJson( content ); + } + public static String toString(BaseComponent component) { return gson.toJson( component ); From 5fbcc6b1195addc96673c8d9e70521562e12120b Mon Sep 17 00:00:00 2001 From: Outfluencer <48880402+Outfluencer@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:06:34 +0200 Subject: [PATCH 22/58] #3732: Fix protocol state issue --- .../java/net/md_5/bungee/connection/InitialHandler.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 3bbf688b54..0d745b91b5 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 @@ -66,6 +66,7 @@ import net.md_5.bungee.protocol.packet.Kick; import net.md_5.bungee.protocol.packet.LegacyHandshake; import net.md_5.bungee.protocol.packet.LegacyPing; +import net.md_5.bungee.protocol.packet.LoginAcknowledged; import net.md_5.bungee.protocol.packet.LoginPayloadResponse; import net.md_5.bungee.protocol.packet.LoginRequest; import net.md_5.bungee.protocol.packet.LoginSuccess; @@ -690,6 +691,13 @@ public void handle(LoginPayloadResponse response) throws Exception disconnect( "Unexpected custom LoginPayloadResponse" ); } + @Override + public void handle(LoginAcknowledged loginAcknowledged) throws Exception + { + // this packet should only be sent after the login success (it should be handled in the UpstreamBridge) + disconnect( "Unexpected LoginAcknowledged" ); + } + @Override public void handle(CookieResponse cookieResponse) { From 84ac7ab944dc911c333b4ef7d16e185c4c2625b7 Mon Sep 17 00:00:00 2001 From: md_5 Date: Mon, 2 Sep 2024 21:06:40 +1000 Subject: [PATCH 23/58] Minecraft 24w35a support --- .../main/java/net/md_5/bungee/protocol/ProtocolConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7798c85eb6..768eb789d7 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 @@ -47,7 +47,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; - public static final int MINECRAFT_1_21_2 = 1073742030; + public static final int MINECRAFT_1_21_2 = 1073742031; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; From 8f8c270f3b825c75105d56d5d6b14348a6962ef3 Mon Sep 17 00:00:00 2001 From: md_5 Date: Sat, 7 Sep 2024 09:13:23 +1000 Subject: [PATCH 24/58] Minecraft 24w36a support --- .../main/java/net/md_5/bungee/protocol/ProtocolConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 768eb789d7..b53b1e50ff 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 @@ -47,7 +47,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; - public static final int MINECRAFT_1_21_2 = 1073742031; + public static final int MINECRAFT_1_21_2 = 1073742032; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; From eca6090f1ef00a7fc82a9a9f7d537160b5dde4e7 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Sun, 8 Sep 2024 09:11:05 +1000 Subject: [PATCH 25/58] #3739: Support aarch64 natives --- native/compile-native-arm.sh | 29 ++++++++++++++++++ native/src/main/c/NativeCompressImpl.c | 8 ++++- .../java/net/md_5/bungee/jni/NativeCode.java | 13 +++++++- .../src/main/resources/native-cipher-arm.so | Bin 0 -> 50104 bytes .../src/main/resources/native-compress-arm.so | Bin 0 -> 88584 bytes 5 files changed, 48 insertions(+), 2 deletions(-) create mode 100755 native/compile-native-arm.sh create mode 100755 native/src/main/resources/native-cipher-arm.so create mode 100755 native/src/main/resources/native-compress-arm.so diff --git a/native/compile-native-arm.sh b/native/compile-native-arm.sh new file mode 100755 index 0000000000..f3e2fd5943 --- /dev/null +++ b/native/compile-native-arm.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +set -eu + +CWD=$(pwd) + +echo "Compiling mbedtls" +(cd mbedtls && CFLAGS="-fPIC -I$CWD/src/main/c -DMBEDTLS_USER_CONFIG_FILE=''" make CC=aarch64-linux-gnu-gcc AR=aarch64-linux-gnu-ar no_test) + +echo "Compiling zlib" +(cd zlib && CFLAGS="-fPIC -DNO_GZIP" CC=aarch64-linux-gnu-gcc CHOST=arm64 ./configure --target="aarch64" --static && make CFLAGS="-fPIC -march=armv8-a+crc" CC=aarch64-linux-gnu-gcc AR=aarch64-linux-gnu-ar) + +CC="aarch64-linux-gnu-gcc" +CFLAGS="-c -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/" +LDFLAGS="-shared" + +echo "Compiling bungee" +$CC $CFLAGS -o shared.o src/main/c/shared.c +$CC $CFLAGS -Imbedtls/include -o NativeCipherImpl.o src/main/c/NativeCipherImpl.c +$CC $CFLAGS -Izlib -o NativeCompressImpl.o src/main/c/NativeCompressImpl.c + +echo "Linking native-cipher-arm.so" +$CC $LDFLAGS -o src/main/resources/native-cipher-arm.so shared.o NativeCipherImpl.o mbedtls/library/libmbedcrypto.a + +echo "Linking native-compress-arm.so" +$CC $LDFLAGS -o src/main/resources/native-compress-arm.so shared.o NativeCompressImpl.o zlib/libz.a + +echo "Cleaning up" +rm shared.o NativeCipherImpl.o NativeCompressImpl.o diff --git a/native/src/main/c/NativeCompressImpl.c b/native/src/main/c/NativeCompressImpl.c index b6b27682b0..dee50c6a40 100644 --- a/native/src/main/c/NativeCompressImpl.c +++ b/native/src/main/c/NativeCompressImpl.c @@ -3,7 +3,9 @@ #include #include "shared.h" +#if !defined(__aarch64__) #include "cpuid_helper.h" +#endif #include "net_md_5_bungee_jni_zlib_NativeCompressImpl.h" typedef unsigned char byte; @@ -28,7 +30,11 @@ jint throwException(JNIEnv *env, const char* message, int err) { } JNIEXPORT jboolean JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_checkSupported(JNIEnv* env, jobject obj) { - return (jboolean) checkCompressionNativesSupport(); + #if !defined(__aarch64__) + return (jboolean) checkCompressionNativesSupport(); + #else + return JNI_TRUE; + #endif } void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_reset(JNIEnv* env, jobject obj, jlong ctx, jboolean compress) { diff --git a/native/src/main/java/net/md_5/bungee/jni/NativeCode.java b/native/src/main/java/net/md_5/bungee/jni/NativeCode.java index be4d5b0b82..4426823c84 100644 --- a/native/src/main/java/net/md_5/bungee/jni/NativeCode.java +++ b/native/src/main/java/net/md_5/bungee/jni/NativeCode.java @@ -43,6 +43,7 @@ public boolean load() { if ( enableNativeFlag && !loaded && isSupported() ) { + String name = this.name + ( isAarch64() ? "-arm" : "" ); String fullName = "bungeecord-" + name; try @@ -94,6 +95,16 @@ public boolean load() public static boolean isSupported() { - return "Linux".equals( System.getProperty( "os.name" ) ) && "amd64".equals( System.getProperty( "os.arch" ) ); + return "Linux".equals( System.getProperty( "os.name" ) ) && ( isAmd64() || isAarch64() ); + } + + private static boolean isAmd64() + { + return "amd64".equals( System.getProperty( "os.arch" ) ); + } + + private static boolean isAarch64() + { + return "aarch64".equals( System.getProperty( "os.arch" ) ); } } diff --git a/native/src/main/resources/native-cipher-arm.so b/native/src/main/resources/native-cipher-arm.so new file mode 100755 index 0000000000000000000000000000000000000000..512c82b4ab08dbc469315a4323712c8429943406 GIT binary patch literal 50104 zcmeHw33!x6wsv)QfM8e>7K4bU6Ht&P1Yrgcm~;{#Y!ZgV0Y{xqr_)&^8{HjQ!j%SG zP*iB~4*r8c1RVhvaJi!yXEdO5MRAGXC_0MW0Ug|fxWxhce@`vl)n7UZv)$+Z&+o_6 zRMk1BPMtb+>eTvuSvhUabhF9C)MH^!vPgoHnD|% z0a788+^LMN_#N0H@CUYtQdPLYBH8M8_jBYUInVY-m|rJ#_~;%QobbolJO%sNjIx3Y}PG4=G$pwzU{WI zGX~tSn#K86&5G;F%xPKOIeVf%4?lX&$8QjRR{ZEm!;hM17=FX?qh}<3qwveX?^66; z%2@N?bye3+zyAG4?%1>BFF$W;{N}rRH;yX3IsdWk zOOEEV>ZAFa)->HWu<)VRo_Ts|#d(9PzxmxuNAst-|MJ6^`|Ixd=#~dshGstU;ZtcJ zKU}=V^Yx2wmrcK8`_cUF-LfA{iQnA=MRxdDSa&vn6k&KQtUt@RS7}rr_d=n440Mz{ zf_8`nzZtk#aB5#Ge#U1y0Hj0Xvj~6~BC;pjk>@$k$13+$R3{dEM>}w;y%j&>;|C&^ zJpb8_JomN(cY|>(d4{wjkFOnivN0=u#^*Qfl)JMX_&2;A6Ic(g;H2E#OGx%%3G9sv z1f1-fo~I@K+KD24wY0V}39lF--~kb^FA)+ScCG-}B)putkJsV$+U?9fv*2=jvCHi$E%$g`?t;s6Dypko1&*Q$m%zow*quuq_LA}{ zM@9K^7xR|6tC!@}dh<#ycU4xqmrZlKtKH1w1z$I_+e>QP+qIe=gQ%UnUysa_VOxZ zt8`RUR6AK^k*nBS;jueh9tqa~9+!8KYnk0u<@^UqDRH}8QS3S3@0V88B8rpKy{yJ- zca{`QV3n>)ND1|zDV&Q)Irh?8hr2imQsOADh+?qR8>6-aGz#r>*~=UryQj9MrW!5n zc6n+mpu32U3WvtZYb<{4s&S7k# zQfH;Tk{Y(6yvUjEsZReXt0*2-QC?NMbdvw5gX?-5rom~RubvDIo}qvUCundrRukH$!PQ!U;4?IMcLK;~o(2yu@qk;X!3QW} zgoPUX91UKk!PWYM&@~!dZ1`V#;)!iC=j7DFt!Q~W@ zV|QxsG>v|*2JfxGpVHu$Xz)f2uKFgS4`^^X1>(7yG`K#mKcd0ql#J5{G`K#0KcT@> zIHmZnMS~C3;NNNR!5W;&enIx%SA)lEaH|GS)Zha(_y7$qr=}ch)!=f9#o@y=_-Kt! ziq-PxGj-4II%8q}9gN*|rf%QPMvMK;7QmVq+mQ4_Zfjz5rr&B#d6Q_>54gQaW?O5b znQ<x zkMwFS{R^bGYw2GiyFrv2 zInw*J^z}$LYw6WUo8uzo--L9UmcA9~Y%P5U(nVVOPNY|B>0cwgU8Eafd+c~_tBtWI z9u9?)$tIQUQ3(8AR@lYA4`C|8OvXL|tN`f^sYh~K9mv;WG3{osTgq#sPi5?2+Ub>t zvwEyrmwGyDBl115A9=Gs&225>dClRxRNk4oXLuhumB3mLqs+phX%lTG_7m{kz~0Sm z^&t;0<8E8)%us045&W#oH@~o`%fwR&w$?Ls`+2`ob$AzbQgNNXnb}%T<(qbW1=&wo z*lx(#l0CrInriLpx3SDOQRWj)v#m7a>LfoH6ne;weA`TF!nc&6?TyMo|Fe&JiE zW!J(%tdElI>#NMWzDD`q0H6I(Hrgoj&95xj7F%K4$QtAPmmt5-!k!@cllR8?>$$w= z?mliw-VK?Cn;}ydYfh_oxTzsv>A$-+jrr@rgW~5RZxD18U+umie?HPF7Pgq`8SJ)g zpu@}>sJ>rcmA&g*XAbK9Jl8AHlde(&-TW6J59)E~8%@X5el{!fQ+=onPg%^nouGS; z+VZhDf6?$&4JOFtyvNpB$Yp_EWxM?Zd;*}~j{1FIi9ytiUHlfbYct?fMxeJSL$tfFi!*gkbNeD5$g_c+$OSFhc_^bi zhYBg*Sk@32YinTM3-d|WLGVG_tqY`}%oJNIW+x4S(V!U(n)t?F2F4>k9`QJq#N&p% z(APSS2W@aLcpz^Oc>{wL;TpK&?j*w0C(zeqhRxtoVlep_oc zGkrp`+d%)+4`fq+;%yaL6aV^|I>zg(^6YK|ZV!NSVAlcUtbTWT) zu5DsknRP+{VKCX&kOrINdT_@16`UKV3l$VveDlrV1DlwS{qj`C3Jms`y@#UvO1-dL4XZw*1khz8VdisNq%e*x8DU#=G%5Ar- zA#CyGXF|eOd|mvG9=`d|Kb6zOeBJ#ix0xn#eL%JaVST_)mK9Eb9~d4;;q{s=bb;{^ za9$_K6_|~>&DP|j_=;}%kfjgF104+Ka)B<$>1`;B&Eon0x8{Rfq;rxJZP}*~`cg7C zqCcCUL$VoVFUP1}Xj3z2fp0QFr-kT0+-735d(W>$yT1)x>-==Ro^^YZUNw0Gd66`` zdO$wu$|h7gnr4)NMx>)@ zri5u?`Px^GhYD!CfgF77Ywn%ZW(*m6hQ<)vE{q@kMvMJTecUw<@f}iQYZB<9$5t_S zqp_97Ug-K{J;ntS@=!VvV-xA7qdYCALz{?}`QrSaA>I)!@ej}#3Hn)(Mdiyy+*nR1 zhrJ%?#xQpK<|8ia^?NZFNXC4kFKca1VGT+ze4HFD<^&k?F&@V)E;OSo8DATYo5eWI z;g-e7Ux4*H#!_2rmWhuq4cIT>=~Xg~{SuzuC({;6TYPm-Ly8f`0<{y~f@Vd3+LI)VL zu*HzKfWkSKA{@pR`yg9N2Erb!;C7NHlQmpJ@`!m-1M0>5Vsj?=E-iY>3O^JCE;WPs zNsiPN@%gFH!7+84r^E(s~vI-?Erbs zx*b4EedMg$AxwXk?V#v8YX_s=63;?!)Lwc!)M)zkY5JA+5N-$H!tJoZKxfm+1ujfi zZ=jo}(E%5x+pE!0yHLBL-FUke!k}% z(EeTR8m2$Xc2)G9wX09lhp~SF7uMSb1D#dVFK}VHdIMdCMh9G&Zm)sPCiKDm6mY5J zr#kadpc6WbZ{O#rIWg5OOy7Y|(|uH!K3eB7{Zyl&KJ&DC0~fA$lYy>KqXRBX7ckJ( zXmr4Z=~^^8vPZIK*ekbZpR{MdQwFfb-9Hv{B8IV*)_L_b)?#1a*l1fT?Zr5m`4jA= z@bz`zd(1O1KAfrhZ@$JpQ@69t975%XEUm$3b~bKfoD3ju zGsZ$1OHU9Fst3tV^Me7bAb6FH&j)BM;AI=e_*2Nk$8K$mkD@b-@jkH!!RHP^l$~lL zyTcexvcvxPdIV$l?cn zYedeQk4NqIImrw8F7Rg~AI(9aS26DMyzTeDNZwK5yx}$fwdm*##%qHpg#_Ut*6{wsRyfSpCvZ-E>;$R6N>x$gBf9qlEfy#iwq!UvPj zQZ^Y_2fL#EW%Oa%bLQjXIve(T;cp0se6(i3UNrWj4SCnc$h%(VB^>3oA}@_iu!E>_ zH^#`jQRXEa<;_4|norW)Lzj6|jJ%sabIWZtNiv`vt4Wk=-WcI`9B$J-&IKS5*A-;NmaQTb9nk;l-!yzGd4g2vFk zyzEZoHO{8I!k? zRr8rK>6EOzorGVCNvC8r_)ab>Wa&^=&3DG+tz_jg2>%(APRXkI(3o^eR`R3hn?YKC zjmO*+=MS_#!@Uezi-|iLz|nj<06!U=q|r<=(9ql%zABih(PSEEXbud25zNtOax|L3 zX1^)UcPQsue#b`bOEvL*sReDF`$voS&jQWhA~voMD}$!AH&>O&`b%J#pUi~3>A3{@)_pFg2u2P#p@86n+O_% ze7p{kxrv}L$k)jj8dE;Q93-ZEhA}jze7p{w$k)l*BBp$XIY>m%zzL;GsuXN1PkzM7AZ&=}gcleI-m`859?;cJjj8#5y`2Kh9<9-%SFXBaE_ z9th==bN86?@p?z*?lId}8z&=W8|344=%jrO<0F@k@^vI1uXiW%X=7u%^6@%!BHur? zw&)r5Rk8eHJ?&F=;ui&tL9YhCC}<4r-iconc?|Ly{9;V`41O`Ddw&5;XJPyb-{TT zKd1ODWaak&ktSaIKtt~((E5_jF?It_XJk6;&w!n&Q}x*^bc}Zn=zd%?>U9iv6pusa zAK)yK%dOsBSjn(P^&L{@d|0zK;C`Q|GsRJUp{(=U@>~wElMaI6Jn;<)V?Atl5}atQ z(9f#`$9WAu=aGC?C9{HG5nQy}uE5Q`c7^K>8r<1yeE{{)&jd}#6F}YhSrdhHUPXMd z&SqxZ4aHg-clBDW;2RtZIM&LDuM5!H7;9qK1I~(|2Rci{dYte!!sh~?8^P0g67s!- zyZEiJlL((Y(9K3#@(ASdbvCyj;>BpaPHS};$GV@}?)JZh3i=pi!hMj5K`8^th&kKD zW3sG5r0Fc3_BTv;uYmK56TFEhr8Chd*5V!`*>2$Gxx1R72U{H5#q~*jg!&Y8#3+t- z=4W0u++9WcQ=Hl-75D1UcKx_c^8rU&1QsA1f$%CObZUoAol>U@0C!5AmV&M{qBE}3 zs|23wbP@22BKWXQtD)0;jgJR()krIydNiG)EUr_OPvufPXgBColZoq8$`ID6EUS%9 zi67|{yoo3A*LBKm5&AeYf&(;C$flE*`77Am5C;$7t`_&4~zyH4bESw}tvnilhe}r$;`( zcLxRoPIfjLVG2T&&+q?^*4mx)NquQ7Xa;LEVm(hQs-d|b^-JGd=LEOQ} z=^cK5iR9|k5B^oW*Hka>JMg}9dg@_*)(jnxf8+9#FKfp?enaQUpiP;~PL@eOcoHzW zhrz7%hsf5-B^)nZ^&?&I`w-4vetwUlFWuMh%`c%ghJC1eTE6qd9SVH+r5~NQQ$JRB zuGt{rcWrddP9Z1gd>gf;k#4 zA2{?`j`R3{2h9?M9)xwET|!~w6zd>sRbVC3810{lANdmSCZ5Ef+BfW<(YDRdQxLynxF^$$ zdotu#zG#VVTWT9>=Td6h1n_|m=K6&X=6Z#%;^Q2AZ)lO!s~vQUq+a1$$(M57-3;Bp zr-gAI;5-ty4*Izn`dN=~9m0*ET~DFZ>n5bhk1D=PkarXCn?b)B;S%B_^$HrUSI9&3 zT(6}Dy=pRYy=roCy~?uM=#}`9UcoyEp2T0*tFpuWc&~x`E;ZJ{7P(C1kB7oH#Ie_L z|BBnU<3sr0(Zbe!sgZu-Al>iig}ds|J=rk%o&?V4b(&v_7;bzym1bLD{C;kbY3M z=^o(sfG72pC($lDG~DCBJuY!iqivo9&9exfM)(|PpQTW?=?h42*781yye|NM5cCHT zK1qCJo1$#4KgdIJ@HTzG(59M5WIunN&Iyk(pVD9-yHrTw!fK8Hcxi_G-M_HKZ}s`{Tvhi z<^|~Wpw#QLfFG24eGPQ4K^D^MYtYSe8tyP~hb8VX^z#~M-a>d7;a@=e7KKu;?<4)R zmiH~>eINK^pg)H2E#f2fin6(0ArHyH_4%?{(w7x18S5_bcIpf*8y3epu_V^RWg?#u}_IYyA*@ zs-ET=`Es0Ba|(VwfgjB)$S>xRtW;*cC?^$pXf6nU=?_9Klrs_Qfd-7D4JN#MOnIqH8~PyOu)o~b zigRzgr`wl&8qbHiWXW|4&2@+#H0y%kk*d}+fRUcV^C;LW_tDS`>U+}heW(E6km*o% zt3mcvak#&(l}GKa*M-`hWW@KJ{KrT}ZGA=c3PL8pc)cL+M3TEB8B;N5pthiV@cXSH z=&{Wl9(s-3@0W9P)UloUC$}lcNqF#->+n42pXPF~3Dy{UWAb?}`phNJ14EzTeFS}h%R_y{pud}_ zJZ=8Hir%Hx^uzrN@(^FL^N#dIG+~>9FChEj`heV2{ywrPvPIC5O@;M>KCQmRaHejL zSmTi|8I1b2=S!&HqWyWjWB7_2q4UsK*6KybXgr0@aeo3i?p3}5@}){&F_rWneF52@ zqZjl5{Lj!2Y5ysRwvv7QAn^(gMtUsQ$w9(pVh*$m_MyW$9dt=_Y2+hFkBFxtPQHrx z6(Zg|3H&k{_Hgi{a&pn%X3Oy?6Y1GHH;91WKbT)xDO#kevWG~*GD zMK}qx<0+KmQ6|!pw7d(DHxu|A(B~jrKz!tQgtBQ2qPhS7^tvL1b`3&bq!Y3|nzQmW zbWi%u!TWV&1LRX^9nI~N)~*}|9ilzMHU|HErtWpIUS5-=ub1gvponi8ZRWlyG!*uA zthe8cyCo*rNd0H952wk0Cxt1(*VxckcwWQ4Xb?1HLu%bh?+QoJX*i+-u7L4#zBK=D zk(c}|PqV@P?MP#8-cSJ;=7hK>YU&F;G{BDVO%;~JiU*2!DhS6|AJ`oA3+_W3Vdva; zp$zhcoM-)DeEUW6ZyfBWJ>y^|&)dQRgZ;m!`Z*K*X7m}VcM+wfPo{nogic5g^sYiC z;Yd$oeGR5hR~!~~i1%BmtSlUxUt7Wy(CJYrvcK*Xcbs*AV6awd zpfWxYHp}HNM1C9gmmJ7TZG6-JL>v2t__Ih>%!5@MfAlHRgJJ8k%|D>DW&=S=w_^j( z5YA`=?3{3$8g0N#WgF=mBlMlwz)_SpQL}-4bA=7Sr}pDAVje+uD)w|%$UU8(?GouM znVu}u*UPkN*D=(W+Yqe}U_<&lKcpA<_=aPuuF{rLXSZ)l57Stu+0s+UYqX`S+uBkS zgDu@pY(Y`NeVzwdv zt3j8g*?}n&Z6R$mNbOHP>G*O{zO9!iAMIg{vptMH`mi-F3!&*@E}yL@f6oTrBPGAv zWTJEoS|d-Z^mE5S%k2lY6SSDP{d{PN+Z|;?@xq4s==CGNJQ1=YznSpxIqAR~?F5e4 z=ySN8oKL)rc2eKgP7Z$2)=s7(FWJe8{|P&>UF08v^0=-W`?PN-wxpPLa?2-e?W6^; zv$2yyptEUql9k?pov?l#+X?F%Z6}oO%uc|wBRf&wZ&PEI33lMp>joPM4vV&t`++yw z$jo+Z}Hu5y`l8vl1+6eJ8%r)w%&&mEL=NfH%2i88jxee4`wP4u3-l$r0t!>61Fy%_D~kJP5o z^BC?^x{?l!_R`qaUe?91m(i5>SQmePgk(2$|7yEo7yJA0G9vTx-&6g`j?`FVyFm0| zj4#U9N84Gd=#L{9)~eV;(Cop4`8*$sPzSTsH~$5LZXG>g<56W_%A{FBD~A zE}Tqu0hvfY#QR+X?|rm4rSnE!%xS{*H^a!=W)1VE_Ez&3;vXJ&MqsZ*p2aLRl((u| zTRT|~8l#`xG_|#pgqF5r&;67aa^8%P)>-(DFy9{;AVYj7n5Quq^N{Wq zq~JU>QHC^5Q<~|F)1w}AD-AuD6I;=4{K_n}@Y5AAEfk4+>UC>Z3JsNxLYwo>1K4dwKFJrsWRDf)f;`mmS4f}D3V^0Tl!oCRdPcaRupBw0g{Q}xw;Cl*)V;{jf zV;JDX4{6$iz}^EZQ!u%ofU-D0?3D$u7ZSyf_BODG5u`l~>|aFj8v%YJV)F~H^+`_5 zD+AaQh~k?Mo(u5H2j2w-zIouA7n?7a6Z=rGnP`5w;58e+T=1K1;3xM|V%0lJPHj&o ztWP>?z?njj&J=K-5LJJ9o)wEPms9Q2= zZQ!*7zir@$bFL_Uo562$2mG)X7r@?F6u*t&wF$qC;J3-ZZ$0>}?|>ieJ7WJTir>xP zwGO|V!Ec>`-x}~+(*Zy1fzcjf6u&y~T8UpB_^mYXTLOMdI^c&rwgC1aqxe;Wmj}OU z@beh>Edswq9q_}rig6*DpA)=F@pFP-sm>2{w6~~y!BFJId?iYjtB}u*-&M$KM_#%Y z(G4koj!G9oFUl{RjsMd5=7Dcs zY`$mhzjS^%;FlAdUzD6@HL)cYLDO9_%EHG z@?Y)oI~)I{^Hct-J$`57zjS`ef3?T&Z2XtbPx-I*_??aa()lU>)gHgI@n1SW<-gkF zcQ*bj%+KHp{z3nBrcR!n!k6gZQKEA=eI64?M&H3)iQf^YIg|d48=9ZU`E4q##rd2{ z%wcKX5=d^G@;kam1RgX`j)lLTz70!dV9ts)CC?K$AL;WOrwG3+&V(4|mf^dHYs|0n zxhUqk`2JG^WNx5+b&M$u>Rx&f{4ht=z6%e2R5w1Cl>GUeQt;<{?N~o@9`wBi@VkIy zruBxr>mEdY;z|4WsesY<#hOtLopIA$s367>E1i=K2LHj*-;hsDK{|!tG|tE~E^Qs7 zmq~G256!`M*U!3a*dt^TWs@xpMR`L}9v>4$d9-E;tuf273wM^K-k8MatSv;NJtzesRb|U5v^j$pIFt;1(<23gMjGtHawedB>8Y&+&WJlXzA3@j< zrIF_#JDt4|^6#d6*7(NX(AlBlL3Eri`8LTH-*|dm!EvVCP>;Kx{|7(P0nOjAmZE(l zI%{lTxOZ$0T#P$(k-H*%Pn+AYt-*#nBUro7((_^M*ud9KcrTuK5RSep1$!0Wrj_Y1 zomHblJccpOm}pLZ$O*iJIyBp@~ofYGs1B5$;Nm^ zV>!8s;v^e*hs(sAEnFtz;WCL1=`h_qjSg`|H=6pg56^(Eym&(vw}-)Mb=+Q3GBTec1U zPO^-fPx@%Tg2OE*O@KEe9+Z7xFmy-!$tTmiEP!w5sdr;S7Wih&m4g=aY0Q_yzEAdb z#!9yGK4Jlmk1YtLKj1jX7r>Yl=xaL@Otzh2fncuy`G?+^dt0RcdJfg zul@Mh;w)9$dDx|DH4nR074fipRW=V3s?vDav&ziF-c`*#C|^?5ejfI%+RnrNRjYZJ zTvf!w^Qy9Wcz#tH4=<=P^Dw2#LVG({_cZ^Nnbka(Y$-_N2-;#L z>0XU1NEcC>#yyO?fzb$Q{2L4U(Glj6vJymJix7cL?Is5HCSIFzA7~_YJy#ZV}?jUYgf)KOObH#N-b`n^q0FZ|;eq zS(Rt%4vBjpmdM|Iq52YE8~8fFYc*(hfG>qy7x)&wsgTW4Wx)Tn21CLoLz%b_mYF5S zTi77$?dubm1lS}At3y5N8YBHr%|Vs`zJ%&LqdMS0+yfe#ub?bi19KfxJPvWv7scZd zhfaf}Tj1ReER^rrw|fl}bn zEsM#fuc_bC-vHx%fP4hWLHz{dZh+>H-1d6&krzL)&_xwXGb zF8l9?zgST>BQCgV{fC#-p10$~Z41W4^}V&r>0fpKtv?yu!dmemgj zcX;-G`%bUo`3DkDjqq()_Ce8iA0L`OauJRU#Zx%tlf?cnEq-Q%=aH5h|6|Fjl%I_3 zHpvnH)~LG^cXaRf{#VRow#0Sm+AY5OISD;__UgT8-!skWwr#)uXv+0To{h=F9~pJ) zf(tDV-Lv({>W8N7x#Q;<2L?^warN<}5B|3Ayn*M;|Kgi%L#Dl-w>kOY5m|K~R*X-a zl=*i0lcOHCjvY7iyAzZDHtWD|r;V?Fd{^cx$6k4O+n^8DU0j<|y5r!Zwnu%poxJ(J zuikH*-Q_1Mt{K$p*W&;%~CNrLV4BxhnamslOgL zq9*x*Da+F@y!C=VZ(jT8fe(9scH#HWzu0KscF(RgdG*0?<_la z_VusaaMRt_OemZf`mEoiVUHfTG-Hop(JMaBj)6R>RH@DXAxY0YX z;nnY~V~VC+@#yiz|F7$N$u0A4Ki9wB^7Z8T@BC@*k&8Cjx?FgV{nKx9rhdCKt?;v# zGv>I4eRJgtOH1xge0$%UkNnE0@6-F&-9E>;W<~cC$!uR@oay%8{O04vZ?3Gq;-1RO z_gs1ZmzCSB@e5w6_}M)V&7b}DLoW_D>O1d~cS~)D5-vNi{@NqM^Y-4p_`%%zn@&`H zc52I@tA4-f`FPXC{VpgyRd*tP{V&)3)pq=bN6O}Z*C(;`<=Nlff87;JPHnw@NY|yw z-v{2xdGpnjOY{BC>-^7Hel^d!^PwuojhD36zYD9*P_~WG#43LGm&SrgSH3gjZT#6A zqywh9p-Wa~&Pu+ih0^)uRiza!YZ3l}`yy+lr&P-uJ8I|;iAjC>_UnIc@__ROokoV`$inCT>8^L z|K{qCA32qnf6-&zuxyMxiHKV98xjF^7)vrdX@)2C!Bat5aW|Y%!qI zI%E#y)PM5vulQ!`AIl#5bn~{4c6|KaZ|{61=g`;xdG}{G-e%tK8vX2@>CYDo_|>)N zZmgO();8gxFV?L4+konj*Z1)A!#1w|^v%O}wd}cZ?4m1|pB}g&A^ounUz~a9&iCiU zU7wJepL)~%n+B#Wzr`s3Sb8*|F>SSGP0Oe8&2^cz)&patbk4MNl|{!F)s|OyN0nDu z%PVUtT$QdWuftnjUBzkXuhr9oKTgj>o^RB&oa|8-jjgo~yU26NWmZCX7M0i3xQbZ~ z#!&=J(B)+380aK?dN%%|yp9s^yy-^1lD=U6C}hye6Yzxv`386iAD_kF0=GaeqK@6;)=;P(@yQ6EK)3=S*6{3Qgr6dO7hxP0=oR>*rXvwf zzAY3gM7RRs4G8Z>cn`vN5&i+;f!jl&cM&Gu6$+6M6A)SvRv@$?yc=O5!Z#555S~DI zH^PxyLZPP+#@!nV(cg>TiLeFX+U=oGA~rM1c7#G12%p~*3e7`k{X-~JgYXH28xY>` z$53c5!Ww+sz6s&l{m73n{$ME74=+OWcqJ4Xhp_n7P^bW5!E2$A7vWQ{heCHD>=%Ii z2p1xJ7vTYfrxC9GAQT#aiy64b5*mjv?i1ukhzBj zgeMTjorE56k$w)s0SFsUg+fy)#2-~HMo9jE9@BCx^Os&|I=@E>hYbL%crEJ8v4U+iJ<)Xr6oYy~DU}f_mXsjNcK^Kpo*{2x7QH$(jJ) zKv0=CXQTNp)8*$N`y0JjP6D@)`+%N+kLn)~bha(=Tf60KGT&{Qy0PnBUH&uf*A^!- zymjQyZ&IeL1S{x{;P(dd*br*GXX|pE2C4-#gNQ~x6z>PS`wH->1e0>*9et12hw$To zA3*jg@bk8uv$gv&iMIk)0Q_;_XF&hRUmusXW$M-(%X-s$N5edpf+pS{3UP-n%b1~+ zaX0XZz>gDUaJ@qKaQ>%&9|AnJiIQ=qhCc#)2Jn+L{N)l)ZSo!PbAYEf@+aniGt~`$ z2b|3Sd}=(07jEgXHNn!`%ySYx1NaJT@KPT^KA&Yv#@5m1uQ;A~%>#Z2`VzGn@U~LP zw+!&TfCmJe^Sw@!7k}@L)og7azZ3Y3ZK2TBxV!|udre!;-P+&M-$E=%t^>eVJlH<| z1n@hbL|+r-XKd-ZwTn|LKOXfN^c4E8hIecDVZgr({HL3$k$I)y zL1jDyeA5e|&_cb8f(ZU);NxF}-vKT1bGuwF&|F6(`w`%a1^*4^Eooa*&6_v{^~WA@ z(80^lAz4{guRwg(mfK8Q*O@GLS)`Q0M#h0|-aGC0j{@Mmz}E;lxb6N@%0Yg`3;grI zHzGtiO9)_({+qAJ=iE#`k65cLHw{`l&aW=Mr1)gH8bV zGWk-GH*u=8O9mfH@FxUZ*wrGzl*+UMU-)4tGz7X+Wtv}z;BCMce-sK;i2U5Hu9PxS ze<%cgFYv_z?=vmv#pPKF_-Vjr2zbpr$V0wh1K=a!>%F3l>djk*Y#p54Wky<;nW^Ug zbM(EgGrG>CN|9_dUU=dEnUKu~KV#dPY5pTM7V)BSq~MFNJv5oN4BUDicn#<>GkIx* z6)1mve$>E^8u(EIKWgAd4g9EqA2sl!27c7Qj~e(<1OGk^Y@o#v9-9nl$wm*~S3s!J zxZlJdTK3a3AW4My_AP(Bt3-I7Odr@Ha-J{KwA`i#?@IBfN#be!M32IM{X?jl(k-%J zES>mMk3BMaXe)=FBeX8Vqs}Gq-5~zZVFW#VeTh(t%}#6+3}rU9K_XKTue+pI<^2#6@+V6AkW8y`u^q_kcb^orRi>4klHI>7I3o4@FJmYf zWytpbF%$~vR1fz`gE2Hj$dJy>$+^rr?24k=DsQcI%$W3X=^3NO)biw*>o2`DJ!9M@ z5^TsABWXeJr9FJ(V`1iS|I%?5^cK~xqTz8&^}lF%7pCku8s3#D`;UfqW2zrT!{eFi zZ_)7XVLuZMKZhxON5d1?-o83S#4W4`Q~pWET3AoEc9jlM^u5^gZBCrXm-J@y@C7pC zW5G_n@^?DUf@^Tf&qc%guvq=l!uqnE(oaRx_Y3>4Xn22?5yP%5>|C}mCR};92p2Ms zICj!#hSDFm)hD?vGmF5L{}g_)ZF>oLTm6&2Y`@&@Y?$;fN>atgXJ{`v7;c|W0&6nZ z!*D6T5k6AF`KF*M6k#Thx7BmDq*vpn%CZmEigAAM(^n_}v!@2FgtFAwW)lfE)sz`$WJVknrQv1mG?SZ;B+3CMOyve1iT63i-KoJL{P%#NqPnUQo?-(xWz2^>@~pqNq9iQ3nl+y5^h@~ z0ErSlNy6tzxJ|<6O85o|Z=q@n0?BjS_B?^mj@)yHNldC49T0 zmvE(r$0fYQ0Do1&tv3k<8zi5j628{}|6IatHw!{J9cLj4pC{pkl20Ng;M87w4e&Gx zPh2Yq75z8~uaR&i|7;1bm-B}>$$x=_-)De3C48p=UM=Ad8Q^sizSjW1S;C(%z=eJG zW`(y&L1eHI^d0%VdCLDtdgWIYT=+Y+ZcAWC%A@%oN0R!Zf=`v`%_x`m(`yBwQ7UMU zgwNrtBixIS@aH5v{yIUg)>%ywZk6ySB!4p&Y{dT!3Fqg9z`X~0YTu`%e$=}0xa3p3 zTren>d_I@(0}?(%!doRgQQq|A=Yzm?gS=Gk4gVntK}V1B52{=NRq)=-w_ZU+n1Z1F zcInLuaI;>22B2IM=A}kB(Zu3&F_*LLd~77=({`RiAK)-yerTMpW&_Sx+j;U8l85zPk^V{AqBr|Jo-q?1YzXW{nJ(V`kE$zVX;qU}D;7XCD82z7~$0U5kRU*yL+7Ug_ z4xb}{TLo#<|6c)~76lQBUR_Ae1lGeV()_F((TgQ~7)=!Mw1|LB2HY^^<(RpWzU5*; zsP<)C625k#fb;tph_2>v0n`7dkHC^VO}`LO8b|4QM8e;=K%|F>fIZs|pVuY*0a-7b z6)zgp5ymi)^k+;*-2sQtEkB|JmY%WAOSN%)LhK~Hy8 z=y{pLMO^>?sNy5%=LaORKl(Mb?;z>tYb1P}gjb9ZfPjSONjRo<{IN;+O@LcvTo39w zy$%ua$J&Aa3GgJW3uef1O`0wHkki9&`$T@#AJ0hmY1uEv2{dC)x3@gKnhjaXoQ{eL z`x2L9k-emfz*PVv@OW!WO46OI*yVPWmV3M|x7}N5cUDwaxjbx0F|*r?tL>!~)kTg9 zd$G6L?Xf#*!K|9@o_bxy=^3#p>?P$@<#vbL?O0~V4ORCtR^oP4y6nZZm6gkoMN8R1 z>5U@wlsVjB?PPOtv-LVxPI*n4%RLh%0#xa$^tinCl3Ltwb+|kPDRLEiD?Ij^3Wv9( z+Ffa1?s8X`FL&ALery=)^tw@y)6Fl;;@uDISh?J6dVUd`UXX#`82m29Z!DV+*nGg| z12!M9`GC1WUrcX|c)X4(uZKz&Vi!YCJXPwdvU?px70{Xzh=|;a?A0YDkT?pu)a$Y1 zs%~|$%f869Ot7nTEGu%^-7ZH(d1+Ocy0iorX)9|h>`U$7%}esSJYKuYSwtM70d(t^ z3gr~eB4;!O0ir2Niol#C&~gb_`E_kTHQqKLrv|DnDT?Mq0G$n`bsm(~8Bha7ivmDZ zb(PZlXJr2j;V zkJI8~wfLo4e2f;)paPvPm@N;fLEOt~yl7m!c_3kE*JzmCUG23msdg80X+5wc8o*tkjP^ z)%G$+RWS_BKK07ES(neu0W^2+74~T}WQG}2=L1x5d5+4OJ0~wYYmPl{`t^aly)JdH+%WS!GW@hKuFHIjao*zuikmV5QQ z_L&8j!@8Brf;{A~3aI}ARm+qn%l-4@j z#X5(WN|M$eLm+CFnp&?X77J1@^CHDMI)tj-j51*KVQ`cHrEu3I}D7u^Pyi&efjI-!@IBi4kzsG_tUCSDBm& zl@%keN{Jj|PQ-W)6$fKSWw{fjR)a78OXmd$WkawUd8rDYuZ+%M%zuL+zju|O zS0U9|e~j|q3M9#|#8l_6D!fc7OfoSf*`0>`>YP@Ew-IygF_vEsoL;^<&sAZ(Y8c7G zSpIIn>E6Gx4|VQ)V2j9Lq*rMb{tjflwfyRQxalnO??+w|QI)Uu<&MbwO|t%~-l|;H zjt7yKNK}5cAGdQGfOu^D|HywTzY3dx((|i(H78_#I=9gul~09l8}h63>x_Fvu3>s+ z#8o~8eqX|r{A!?=nGq*ntR(SwFSycpaS-$1zISNz7QN3n=}n==p$_!q@h*P zf(jP3K}HzmuPLBHebtc}n;Aw^Kn3vuWkhuR&)l1$f}m26k!Ugh?{A&GZ%%HKrlob9 z|K~I5XLp~y_u6Z(z1G@mul+dZ-f-SU=cl5`g})N_4cDD=F6#@*7If!k-I;DMCF9(& z20JKJ?jt_w?Qy*jJC9wthY@xhAD8bqK4rB-{6uMt>@^>UdV;cgC-{0N_>dm8`=-l7G;T~|lZ?8YT z%=Ybt*muYJHX0l%o^{D(u5$j5CoFtA{mp1l?wor*-TL&2A9&9f)Wll}BMIWm5JHrh zROXt~lbriR_9S=2@H?aQijlXLWhV`HtG5g<+cf*a)3%?xseMlBcsFlI)dRP<){)t_ zmOir4rB6$_hg_*}sa^dYMHoXkns6-P&4jlQGKBGjw-Jsb(3qP-IG&)tlL#jhP9dB| zcn9GO!kL6=gtH0nB4i2rJC|^tJvn!QkLM6yM7Wf2#BbjE^edMP``QiDZu-e*o~|i< z{?&OunzZ)s-u}he&pddh3?vA50r@u}~A zaQ$}|?wWgbOY4sM%l>fU`*)3>ddwH*e*Cf%QlBo{TsHJ~bHD$MALsXs`om@Ky?MjU z8!O)b+L}c>cbxUD^UnC8`=@KSHU8~?JayDH&+KWq@2TV0e5K~Rubh6{()T}l_046U zU-6mG{Og`yUig1s-u31+=brv|zx=|B4Rvoi{@#0jylchRelzZ)_YB^8(cjHG;is`i)AE__DFRi%Z;_T>9jA9EH-8Q6$N> z`0vAH^p!9DO&0@`KA#}j7ym6MV?=Q#X^s27@P7gF`pRG1PyTQFppKO8t zj`sOmJUt8JZ1+x2|Gi&}Gj0a2{%U;wj(>{Nq20Ip{HwOd`K#l|Z7_W}+)X;lcgW-U zlc)0mPk-9OFZc93>*?9x^M4!QJ%iI8-JPBfqpyzvPxJZT^6)>1p+ZoaFOA?D5>bF3yB}+TT^{}$KEFy66n~Gzk^9d;r3MW^47WON#@+4tGx6W! z{9zvcBc9H^no#+>-REEE^S3lxwi|as91r#F{+mxv^ziTX?LOEP!v*<%$+!E=uj2e+ zapV?xJn5gt8KoXiqlYi`_UdL2|A2>o!V9p>_v`aMfA;zq@Cu*b;OSrTT%3QV$MYvo z=jicq#<@QKvmU;EPn;3-ydM0L*Q7BXv5zhTOnUN!92EYx#F4w)(|^0y+rS^$SHVB) z`LNK#Z=~H3lq~ap=Ukuf7A;!3vVPT~wT;ycjf)n!MHei*c+ryDhT5fdYa44D7G6Aa zMg6MUh1H8!)W&6fmn^DTSH0-!x>eOH>aMSKjmsM9ueJ8wkSyo%Ke8IJ=SJyW*)-HJi2%xIA(JiUHdPQ|(?R-nrtx6;gynD5)mIQp~ z@#wMX@xXD3RRO~VJTD5E z74}D{31~J;kl8U0KZBIkmN`T}?v` zrHdLHOcfG1X4h7uZ6RYWoUB__Xk%_e-AdG%T34^QcI~o7i|ZN-{g_+bP`$E{T6pcM z+JIz6{k3o&N@`X&S!I-c=_-3#+gRUFyJXQ~`n5cO&!qp!Hmq#XirQ668<)YHB@nQL zF%~~26~#K1%s3_R)^pI8`Ce$~)-2axTD0`q>V|@vENZkt>wyi} zfOu99ru!;ACE+uH^sEg~O{;1Gngu-1t!He94C8?ZylfQ$Sy=}^ea5@t-x7vONMBst zusl2`v`1oUL}i+stiZc#8`f%k>sh9tZbj|+E2@{Sb=Sv6W@W5gUT+p%A2bMrD;KNr zP~y|4pXR_+bK=_i6Hj+5mYlGnZq>ExPN=SKs9ARUY05p>&ARA<88a81a^lHaA##Va!7&^<(}X)(KFPAa#7*#<9x5;Kv;B%i;oDE~~K-&~ZR_4zj!<DJa8gnJGe^Yvvy1Y#`-R%vqWrl> zdVUt=KkDU1s`x73x8#ad_RP(Y~A`=yALo|(w^OC8J4Ci2S?cxET^&rRgdP2~F} zkd_PezqGQ_Es6Z3 z{@j_!zbJvHHIaXMBLBWb{yP)-+Y|YI31_7{68T3Y;M)`VM<((gP2``G$bTY{@0ZqA z`b;8Ub58inCGx}CQ)SO3@@EBL;+GQn;}ZF=CGuw^@}0L2(*I);`DKax;}ZG96Zxkk z^2a9fCnoYM(j`BBef_uYf4#)De46Fz>+5&k*Iu&d>6ggE)NLQ}=UJV@JEpg!Qx#7O zHU!F^7?H|$4o^8Nv-Dx4$0yQ5NzX{6k08A`kse8UQzHE)(sw7)$B=#~k$wy5jzoGq z=~UF+{@X~8PoyW4o{>nONP2N1eG2JKiS+5D?@pxON&2Bg`dy?u66qPFQ>pIu&nG=T zk-m`hj70hp(u))6`J^``((fjHcOv~aq#sJ8-%Gk9kzPzXRnp!5)uhKK($|olkw~v1 zy*QC>AiXJ(ejn+(6Y1+oKa@y+fOJP9y@_} ze8#!Q$M3!2smfs+H)r-%eqtx^Y)x&S@#3t`#a1^}sH^r~U;j-T1Fw|3&QY{kHU9i* z*@z<#(fR#Zoz2v-HnwCt=XG`6@eCpDn&(%IKWy47<=M{H*FR+ACiLOw^eH?i@uyt2 z^OXhB{lACyS4!MA`tj0?vDwZ{Hr0}K)1Riz2WnE;PWG?1b(DlN=l4wXxzS=@(eW4pO@C|n$T(Rg}AQNy`o7JJ&q& zDB{=G|LgtmsAuF3TR)qIPDaOIw0_<<#UHQ__hz&c3^MHLieDS&kc=XTaR$?r}F++M~SBo7kFBb zkMS-wS#*k*Gwzy!4o!c0H|bjFk-m(F)@MsbPtH7=?G*oJ49j+Y?$OH5R^Y_f#lR)` zI^#6)HP(k9Gs(8Sq+2Ho?V}%RI|uv?<6MJu@)v*Zx`X%~Nj*3X9yfP&oz_v}CR|I21g;_c%&;UWiT>^XE6TA7K8-^Jr^# zH0>T`x}$c*&rI`SE#m3l`?jMansmG|E`C@l`iE7guGAE zuJELeXr7;G9^CR=qRj^aO;^r9Ph*+o;lK2u1o#=iKahm~bJuB4kVe0aM&Zr04sKaX z9{Rb<`j^0^ei)1!-0~F<^DN`*_4N<-;NR~{4zoV|S4rt)_2UmUCHMaUU7qP&=ecFB z?Yv>G^Zc}H>q2hyYoTPg5ji)3lk<@$nj?2wMX6iEnbX31(Glehnt50Yx-~gpG6tC+ zwe6;RD?9Hl%bH#r4COULvu&X~H8|V(aH?d^O%GHWOx5^o+pF}YIpyZ4Uw^2X36CBz zy;l1gztdZjVtxU!b1abHIUekAL5-RPdo4R*2<=yJBxxolh4v92vwK{ytA zDza_S6+4f~L66&zZA&{hlsYZa>7aMHBdI^tHRXSEXEHD|DZHk;@M;n+$zTH0X z84chOY;IAuDdak_R7_^;qv|Y`Wx*J_^@x~1NBSo zMrXT`f=l($FY!S1Wn8K}1Dy4bI$kC5H&$&_AHs-qJH+FU{47R9O|HmM=<6K(@ z^4mSZwf$;Bw(Xa+IS5@Ujhg50CZq`Y?oj)Pt6kQU@PO|)*r2IiolqCHwe z*}_)l7&h-D>i{H+@(G!eYQLK8c)Xy(iic%#LW@kSsz&%m^D8D zCL8(=jLW)tg2_#ZasCF|V9yiXnp^dJD*0)*CZlJyGY%Xn{|vqrdKQh)Qy!kF%lX&B z9P&~AobWu{t$9&>6drKy)blK!u}R{qc=%wTm2&ktjhu}>_*ViyjjxP@)UP(pmu;zH z&dVJKpN{L6MHP6q2@iG9ksXzM5V!RF**|g~QA9%|8#e8OchX)#Gt+5_;I_$90>5#@2S-JE{yef;+TYY)u zf>_p$@#mgnT;Y$Z?~xy;&+qNJLpt+j>5Sw^eZ%AF$;kdIe;xf+Z28e-x2|FVJ6Pq> zFfV^MrcLE}#zXW#EL+*AXa~=XBlK{j)qlxNQ~MvM-PhND+vdtJ-{l4;`o?pxA+740 z(%x3s*npp`;YVBkIB3SU$%cs+SCw@eoW^e` zpNOqXrdLodeunb9C{LyzpV6`~~f6 zHVO~^ociJWIr5Y0CooR^oIpP(@O*q0zQ(+*D06F?MUU(i_GGJY2*&rdkiM64+6eZh zS$KV&jOwcm^s58^A`cDkMn*Qy$d5E;z*!+%A>52f`62LjIX+a}Hn=n58|5XMBL&Bp zl3vS~zz5nE!HXR0oMXXl`0k4FRh;Ybk3)|V_|pzMPJPkC^X42p3+3hyiDvp~bAz6d zx!FXX%}9crd9`Be=KWSgEKVh!T zlb<6mBcFl1$kSggZ}MW{HLxv*k2!=%-c#{9{SSKXK-Y& zeNi^1@4b}8d%0nbR`@phy=@E4rXq^$fUIZwJA z9c`YU)_BKPMSrC;W?xpu^fSNECa}>K@KHVwUS zp9_lk^ZtZd8w%`$g# zGxmNfyqEm9{xLBoD61B2j04u?u^wv=*zP|VENF|XWQ6|yzCLS1z)`-+B@?pJtK$BmbcfnxV`7cIy2g}n2sXnJ3MX8 zlCNYp*0rn+GL~N=-GYvSJ;M)>U-G3P#Pmt^Z{KOQ~%t!&**O4Zdu%JX`=7qLk0WS*5+*BS#xd` z|3w39Dy3UVBlAwto<@e8kBycbqrDdu$@P+_x@G*H_}SNy9)x|Xu9<%CX)t;8}N!Y$&9&$Dv!CA0KlaefIpV)dDWRHuWzL&iS)ekI$^6>jQA zf}za#WN8=c0d)%b(hK=7Dwhr=;e&q!T|R$~!KI;B^^A7IANx4oFH)aGYhHa2oOtcq z2|Cd2SNQdV#IbGX3|gP%id?>XQP94+|Dz=Cph_%*qi*6o<3myHNS=tPWgPm8h=#> zzMC%doYdufiF`3k?8MFpEMf;SZL-U4%7IZQ<__N1g14o)K5X!uLu7Xa{CHh3C?RfxOz~RSF zHE^LH&3(aPDtu-*x96MBOV6yzYOa-Wgv!;n`s;4ShgJw2F3#eZn zZ75xk!|u3c$1Y&3&QTftq<%EVZNR^jF9OZMzNlTLzkDz z4*pyI+1)niGvp`tE!OrS1DgZcyWBXuTbIIo-h8CbW6WyL;{3or(xGj^PG@}=X+0&r z%O-lg$jKj2p5R%}0k?dH_Yr#N3S|?ub^tcAwv6v{q&xYNQuD7|G41sCgMmMuzCv5l zIkoF)OKa`vY4iSBr#q8A0hjUZrV{q4G%;TYCJRgk|2^_^hU((iBRA;vIvfd~Z$mz% zLk{ZijsMlfevHhqU#jjB&y~_Qs$G zlC7VEnvdv~t?ePJZ<2En@2dyWi&OWZ7nJqT3(}0I9(r+oalg@vF6|3@diF)zM#iz( zhBI8-xS-qg(fj14BL&}1a;9&z&m5VZW_!i75he7d0Ly$DVDo2muTye7SKupbMJJ5D z{>I8d^5h-GJn`)o%f7n2r|d(zAbZX%DwDGJenmDZd+mF_QQ5N(-mgudtcUDL7t8)n zuNLV%vTshxejl_IWY0c*57}!yq5d$Q3-TeCGUr?>Q`T)SgDp>Muf)q)dm_c-yiRi>{7)zN-xu6$*#qE8`|uSC z{7#IYf?r4-&2IIe_ngLq67P`{K(t3h;^sd zln(#lLahx1BijiKe6lre#93!p>)L)RetTFCWaJOb%(kTjLtNOCO~5b*&6$q{&C*dlgLk8zp(&hgTfX`eV4%(HWkTMePEy`K6g|SpdNiYb zBFTp50DC$*mlllj@kd+HhZ6Jzz1#(!UB;K+qgN_df4$FSXP@GEBmHFFDL(uBx#}cj zlK1_o&{-n+b{48w=YI6zokhC4)59nG!u{@;PWEG}D3?Clxv|>u=WJSYYtG!_2uLojKN072GAMhJRW37v`Amz2ENh=x~N}vgf(k zEche(RA2o6n)q+$Jn)n=jTCo+b*3ynfB2PHKkVL=_zK-U?v*9ZHiWMVeE-MG6C2IE z1HNBv+NFCH*uT!T!bhA3SERjL;(*)kWKkx6WCr$!zO%1T+I^2p_;io!Ko3Q0W&GM5n(@QqI76Df*vEMnceCn+I~`HLle|Bsa@`*@xRMr&?;RAU zb8m~&+kJZb9Fvf z?s*TL;T}(X7Q-Hv^{KKF2zSM|3dsi9(G3vxS{aW#T`V#ouJUbdz*AzYFg&ggYx5bLPLIf8lP9 z`6r}{^#Xsw?q)rp{!-qbpT=E+CL6=Tsq^Q3!G<$-!E$cBPuOtQE?Ca8_X!)$+y$#M zcVhFO6zl$!V%?Wg?ER=}`WM|7=h?j}$|BvH63m@GUhTM_t9ORt`!=sE*m3`>S4Q{$ zL1)7LUbUUSekqG@N*&H(ZT#v?mc2fWol_+2F%aRiR-#{>Rjsa zsJ`H22ga7po6eeHi1dZLC{dPXJwf@1FhArjkLybp*)xGoa9jkgE93lP`dnFQ%R8X) zx)d;Tn%mqk_hePdZGMv@t+t@sa6A}~ryE5-ezMz;T2Scoy}(==mv0LFvpmOIpZ+ay zF|J9_bVj!AH1Wi@)l6IL!?f|;pXK4~g)wrQ=B|z4YKE>Z${hZ7p1Qd!ZU10suy^@W zfhnYEr?~5W8J)Vq-(jIP?ZwR zytAbEEbPr0imlD>VN*2TY@ThFYFv$O@G+}Vya&;iTh!df z_tG}&9@^UR;3wRWI_ibp+zC{C0d&2>J0Y#9QIBg(Wz)^`N4sdw*N2uqnVXYs<6Aq+ zKk9Wr{ft^&+wY}2@LpxtXpLGV!vp16o$yP(MLgTNr)#0s*5a3RO|j{RTY zXYti^hO$?$x9B+ucmTNdJjb zRxf@BB#e=V4~GWIY(5pNbdLT|f3xnl7i>B6-UWN&`7CNx`-!u7_$nOMPBLF>GUcBe zWIf=W2MTr3Rf`O1w1tepi~h#CSHL`5eK@Kj6HevkRRaD}Lcy zOV1b|(xG#5Q-GU5Jhl0p{1nCQ=cJFXT4it=*T+$Q9Qo)!bJ;nDTmE^LyCXW!M?a9^ z2#tyA50orWS~Al8(^sI;XpoI4rorp9(QpQBh=#ME;S9wI8W;T7w7C=6{(&`g=5_1(v5Ph*&ow>naaF=`La3YJ1Ac?Fu$rTXq}ehF2YHYT~V3QKwgXH zPTf~fx#Z2BkkJxa8VvO-MwhPP`(bgNgsdt%de)2X^mjZpmXQH^ z_rj*3z8<`lZ3{S6uKutdG+sFB=BLJIQFZa2Tc-n$)gd3)$8%GFor2vhE8Qdi4m-Ka z^3+esD2Cgd2S)bru^5A{dht|r!lV~|zG@*~`Vs4kjn8VHu{-mh7Vkn` zV4ThwNJkn&J|AA&J9TzHp1k0TVfT>_Ya8g|43#nA>7Z@}xN?C`;X+UH@Iv&L`FmWl z10OI9sCpP}9DB0NTg* zL3`$R@%`p2py3CIrL*LkAjQm-^!O}XRh|eD!fYv{-C)*dFv*3|kt|C4$$!{+! z(J4LpL3lUl5bMX~1JM0gKXhw6Nf*<^-D};VaV?z)w%Cu;FNS*qKL61^#_8w6S%6>| zTdkAufw@az{8JwD-`i!|g&*CN4-<6zx7Z=`X#~%EeX8$#+YzwJV~pDvOzQA+(1A^} zHonGrQotA1S#FDO_f5WC(z>(68f0GX4(@e%S|a&Vdd5!^9oWh_tdp9QXYUiJZ120{ zb3UGD?&aM(KdQ)p)Z-hd?#57Wf7-dj-z!iXntP$IgFI~5_UIYD zf5H9x^s=fmQ>CRNH13sl?cA^C9Y5KA$9k7~BTAsHukt+a?X&mPq&`&aEwOpHgJ;A4 zQa672Ms`2O`Z{=l_?2>{Bl?0Xw6mRed;=ZB`=BFHZgdQX4)Fw@n_S%GZH%YoVNW>h z?V8U4oa7wz!`_QhUGl8%cJG&lvFLU2f{+Hzd>O0b;yUOYc_WMl`Q&P&NM|dPI_qiZ zg$LZy+(SQ1})R9PwVhQ zgr^^!-e0)+2#OwAjjt=o0_n84`9o`SE4jOyUaXsIcG8xGZXuaX*nU}-4r_b+ej=MP3(-+O1 z#vC`*+Xrt~`kUk4!JI8yS+HH+J{9%|wMU?FaC?kT`eE~!*Xgw9bJ$CC-mWdy8b;`*E?IditPm-KfBya{L1qzkmDO#6)J?FHj}yS^XFpN@^b z`Pc+Kf`>26KYX~GIZO9!JdVOVd{1nXY%GgED!;vZ9v07>=DW-NoG^gx%3q$Ck9q!! z&Byon`B-gu`Z~yC9^7(B=VR~}=3|}>e_!*l^|i-*yd^4vE7<77eCrOye9Rt@?zotJINc})qaiYu-_Wb^>@VQzhSSoJ*=$~u*v#fPev&2 z07uaA5B@f`$)>}8KR)cgC-pkg_<<&9C@;{hxu1P4*}3dd-iJKIc5caw`<_#t_U9wX zIVH~>T-iBnjrkAm&X~q3f49Qc{hqI;o8DJ7U*QXf)(3z$(@gdQjms~1UxB!whlko% zkREz@;UAe@JlHj3@!I^g7xpc$Yl6JcryhDFd0-34n-`B2XjNXqSEzhE_7%SF=h%$q z0Do50%h%}x--UdSOFqVI9~=Il8`^uM&OUYHv->B@{gwIt!sS2fb%XdXsvGn_jQzis z54LOo{rQ>B42tyPujPZCGXSk$?T1#`rfwhXk)H1(C*CEreTK4bAFRc{8yWKZ^1*%; zY@Ylr>~izTv0hjBvsJ4r|74^e|BOSywU6kn|5`&~AD_hLZ?<=j^{%n@-R0BiS?}lS zy?LK+bBg(6(eg^|Lk<9wkv$#&Cek@X(YQ3a*atSc^se&&I5ui*4**lGd$|L^RBGRI z0GN#S;s$`h#tjIQm)|`Ao}Jn+8USXi_JDxVdEj*R-0tDde4c03gMWo{PQ61De1fn) zRM;axm#Diu>{|yvCcuS#q#=6uMPn16x8T3BkCaB=|9{ypYUh2}L+YOv?H3vTBEPe* z{i43$3by}H`=@>%GRU>h{UZ5)Ubd{E{&x>1x!;!9$L;066#F@0|Mtnh%pT00&+ps+ z8mcp`qWN>{_hY~6io_nwf5-X4$^mFCobx1nz`xd9KVtw|9|&g-MYQ%k*Y|U#z&yOs zp8F{4C`KN)h|ChU%u00et*XDejVrO z?u2|JfBv2LE@8KQkPUdM=o@fZKkkhdo!N5+Z{LH{S^Q|xrs8oG;fm=tPt5kt@E! z!&Ds=-zT0O=SQ=%R?fS>KK5zd3#VQN-bBkLwCUN}S}~zbGVSYZRy5yI#3y^liToaX zI@{Bj_3^pHzLw?jHrU+>X#r(AbEgeQJi8yr9G!L!7j zaQfQ8I(9eT)DrKEmM|t*Kc)|BwlT3$eV9O>6W=p7zN&5L*BEAej65giRs3y3%DB^E z<@uNgojdW_3*R$VU9We64_lLP;d{ow**g)!$GKsu{-*Bl_-akb8Y$3F1r7GT9k5zE zq32c}-+l40;uU3Kyx6w~fWI9c=?-AncMI<)82^3S!fEf+khXVE&Pd)N_qxb94|nZ% zS8zv$J3D#aYm_{e_mD$R-RCfK7~-HkJR{^DppK z<5}=;4LGv?n|RRT?~mF(aYeIR<89nXepAKtoiV*Doo@e>W1r8~o1+*5E*hE>ovM#4 z=$k$3?pBs>2g)vGvA`Q>psrYYEl@a-(w>`LwcWZdYMNOpyE2EIk>Xs=Lq zT8r5r(pzP7HaNR?-u*4Cgl=keF$QJN==(9_iLLJ? zrP~VMPnKSJ9SXQ4mo%_V;+=4u%~+AVG?o%LGvblz%eE17=RS^Q<9t1I%*Ox2qK~{1 zY(yv%e+iH4ewy*w*HJ$A8O$cU_m}8#0(S&0N=LLY*V7mBbheh(9TD_Ck1hjeZA%yQ zY~w+70}a5dtqGE`$~`UM*sS`6yI#-yh-P z?pWNHM4yfgeG+Z7WqKx>1FZ5%OD5B?tCE${f%j3iXL*M&xBMhN_@}Yd{m#fY;_t@$ zeZY3TTb$$FV)+-doaVt@8qYuE&XM}O)!S9wF(e&-cTZy&c%}1dS803^!B>$+r$%PV z(Wx-j{hed^Cb?h5zR5<_n@)e9^Yg!8^*)mBKI@r#b#^aT&$Pv!V4N=;I>)50>?*SE zv}eY0PIqy2@Az^n=l(IjE1-81)K2Ux>26VZ#X7gCMhnJZ<>A{}SmOBSGh+q4bM3)Yshk(Ly~tohgW>skEc-2tq3~Pm6fZL3e@~lb=G* zI%#{e(W`sHd@sl7odQwbLMSE`*3&!9_^X&TNTsnneRPWdNAp@UG8e8TJ$d6$BXK6hrJV9 zWqjP~{k2~Xfe)(3w=tk)Ppg-w($Fx{zA0bmkF);dG=4;fz7xcFWIW#%Xc-T0(lc(} z-@3~L$eV%RZx%Emvt}^&i&`Hu;GVMcJ z^gTM!mcb8}Z}~5xRqf*MRHVJ1gI*bYdT1~BuLgLvC;gJVbbc-#csx$>yENuWFL_+b zm`5IZZ$q-ReRS~xxp803#&fu*U^KM$rr~3v0oV-kF#Ah=^M8r?CQ=VsFD;T+EOYdR zKI+{xquE_&@7!p7wlW4&vOmOm@TK8Vknw7@8}xhXA=YnrXY-Q!LLa>Tv`G*5M=lqL zHtM2doMVqNoicwX$kLVflI7Xmbxcp4?3rYU{s&s5vl)JdI&uk`+!({(DxFoEHZI>{ zc`oSjevYSqdA8?R47?WJoqjxxmOMP)(-RL7-!~-eN+07?wk0Z!$BOYZ^x50I9(`7x z_|{*aji+;Cn^GQZ%oTC^$euQ46FU9AeefjT2Tz)hJw!Z7+FH*OTeCoa4}GAYT9c^H zk-m)=>z&rU-Fh4NbqI9ilzs3cVw~C=%aJ0>P>?6eXzdybW^JDs|Ebq4AL(U;)>@k-tHy@H` zduNk;aIbNCwd5!O-pfbT z9HIB@uu0{GcBm)%hRA;Tw()Omj3K zj&Df^d)VH|JwN26_OMqY@_MGfqET%-of#*1OgnqB3En#PtdtjQxzXr($a}QL*M|a6 zBn$0vsT`V1XpeWkJ76T6eE4S3h9RT8?#F(W*0ho-u<~cMHb|~j))w(xun}|>{8!R>&$mZuKiGxb z&khlff}aEr+}84j`V7!S;ql;*RrSF2K&v4%`wffjFYq{ zd9^bs`_b{3-74P!esk}VIe_|lo(>N+pXzxk`TiR#(#7Bh*qp0Ga zaJ=|HosGVZ-N`_{wDD3V*L$34F0atLc-E)h_zn|3txNYIhon7e-g$_4pBO9M>v#PA zv$M8G5$uQcD?6;$dh8i}mtJx^+slo4H91Bqi+I}l&$0ehhkSUbc{%ByHEW*)-=Hra z&hi;gnrFpPU{{Y>|2bCcqdJ5MmIsH>C<`L`Z+o=MhupH4Z+?#04#ec ztdC;Y<^bErehIkTP4sVF>6-h3kAAb#-R~osuC({om5V*EDsBZoc(!^SI7Q>o^Q?W_ zA>vumrg+_Sw?P^_N57vI(T4iwnqwWk=TBV=hkHAt_EYkADIZV!#wXu?n)a2?{L`aN z-q&q1eAKO&%{LGHzG9@aQQ(<-ZO*W>QKfZeL7dmTye{T*RFyK=6u)JyvXWe6v8x)^ zqTh>tmUYE$e)D!Q->P@i`_CNqQ2VS4VmCXsz!)aq#^S z?aUwXH)1`{N~AA={{8HeY*$A9UtvD$E%VPObXxO&U;Sq7*<|ZDwaa|Xe(eae)k^m< zr!EfsIhVcgV*Y&O5b*~Zk~(!D{OQt~RJ3CwdiJHaEtsmlcwJ53bckpVzt8#xWLuzp zEVLh=l*6oUJ2tM!jxh&fqioM2h0V9Ii>=R+r}_7J@9UZ!%k|cg;9nJw6Ro$$dmG!| zSn+!U^Rzc0ecT!Bp0}@k(R0tw<2Aa@hSL7dOx3mUUN+~X^O}0;b=48QY}vgf-ewJ8 zUn|3zuz1NxX~U-Po@ zGkmj#vJ~UFm7c2OrM+%h?q%uOq)!F#Tu!af|N3<6l*j zmacib{}#xhSRd*8P_j{DL_hH%Id2p{(VuXxxwXdkNoQc(Cpy~qy@F@sy}lEKO*oWu zTkB%l#CFK8zR`2Y!raC?l+ydG_TPAR!^YX3kmetK2g2U@Q=iq3rP(Iy2mN_g^X_-E z)9|Aw=N`^Z)0j`r+0D$^9hAF?4;{n&ZNKSW_$EP^zbQ9c%lwlDCTVw*b9ws8gYWN; z!Z+bUo3hhk{tkJ?^Y@CHwmfBg&&6z<?O-+(u{Dpvc7SI1+wGQp4N%%X9;p*L-2TMxiTtvdJMd6^u?{f=GH{#P2i z*>euQkN;!Fu8qe&##q(xUi0#&w6;l(^{RNR7mvB>gpTz$=F+DgJP-RFb5#d0=G2EU zz7Elt+t}TI^W&=cjX#^W@ds@D>g-2tLZ|k2WH0X4yLB3;qMf`RaM^CS`@me~n@J*xX=p{*hEVb#9ROSWbA%_vKq9n+DOy}`x+vzd9h)AsyCr|zW*4jiJl z7|!rxdqSJf2fAgC=zn=wOR2xIVY<&2_MY_JM$zYC``iCw{ngDY?O%y5tJCxBj(xDs zwBEzC??FN{&mG*+%IJQR&cC4F{0_;5XZa4*fA(iz;8VI%-=q+JFWaYM`mL;0-?0!c z)VB7(b$<%{mE-*R8gv@|K%>X;oxlUmFx5ty_d%2Mk~;>x2k8^pK7!xx?4g7D)-3*` zofG0yG6!vr(=qRRP}u`vLL!OBMUg`aY&;r+=|dadx6~vzK>?&Oqd|Cgj^e znt99aOuqH!U3Uas7zp;gz-rB%)?F~s)bo7B?#d}YNn^H%u8eeFe$pE_BheVJ^J(gf zjtuRyM^iX&f`?CJvLkxg&5Mw8a(_7AZ6AzQlYP8iuzNvDw-%kHwfeJU)1!mArx*6V zi};ia;BzpHO_5k9&Rl7yUjIYcA8Nvtzxk z_UnI_Mu*X3c(fedj%@>LDCgJUvlDr($Xjr?$RF0ak?#>^Js$g=U#;UjK6K3g=2*!# zjmK?#qX-__S&rza^!ybco9MT#0ckgXxt}w1Z?p)XeYcoA)_9%F-`!)qjB%uV-4&oO zZ^Q<)CTu{qXsop+ZGhU*{Sa>pG?tQfV8Xw5-C^&d&_}Za1HfMi{61_!e8*loJ}j17 z6})%+_M^tHLpQ_vCFR$>i;z#XV21Yt!@6HE?S4(8dRd=m_0rw-*e{LOed+^en&P=+ zxKVZKzn+`*>~&$Mp8dYFa5ek!qCENcw7Et1n|IG*j^Q40jZ3}28e&%#*g^0?zxVk1 z`p0^GV^y;KDBluMdsV=u!E5_dPM?|Y*c(+pVe~(JQlsg z@@M2r_&f64yC?1Q)83C^ZO`v$Gq%9d@H2R)a^S@004p58+g<+xuA(-CM|YE|g@gV> z+n!d*QS_)?@*?M$E0g!u{cmBz_q_VAd-?QpVp0F6CHp@C9!-Eo?;|+Lh_v==+Btul zj!p*IPEoz0vP$d>x>C4P{ZY}&_q01I=rcUX9hdFA+2{Fo?6-`;`-1Rh(HYrz`epF& zWwX^)|Dk=8ewQQmYfL`v;sG=y?UcSHuROaq!QI1{C*SU+ulaKN3g2>k6GXBO{j%R# z^*UD2S@T2UJLVcQY3Lm6`Iaw-Ke|6n%=wXT&%fLFFuxbC_XcPS{WCdn_sY&*^c}X0 za7?AI)Ky<~FUxde3i`*H`ZC%Q{pJT$@clg(E#Im>Q||P=$0@Gu@IG*a{^)zA;>SjC zs19w+Q605u^`RlpGkmbQWs}FP^ZpAsR}+8o)RS(hPS?ZXx9GcnXT6m(zQX$u$eR5X zdmrK>MSNvGisz4D3&dmeO0?$HuEyXMq{qemZidGjREK=&UzMNh((+x%tM+q~mFv9* zUtebj8Qvd=^O1|sk95~UxUi936I>gH*>I;+cLIIXxgq&8@S`;*`A&Ar_+{xctv)bz zr&aaCyKK-J@mszDr@?vpXKOzOSUay%`zNJZgs+2k*7M$?%1X!+e29y^;BzykjRxe$cT2!8`Zcf1e)9bFpF%%zo^P5!ujS=stK`>% zBYzxt$Hw@=9-Ko*rTamjc4I4w=bu{PwfU#_cvgGA;adtp_tW%IKAq7PbdI*AOKIlP z>DaO!dWRlX!vpFS=Ivf{i66@v>$`kg#y8rg-95hk$n|O$7@JeXAD&CnS&JRIG|;<^ z%c~K8O|sRVwrJoi2Y$YAk7!Kfr^s*Kd82%X%__f7 zekeEuFTCTauQ)@x()r&@qTJ5dlW^?6^$Dl5nk3xpe&A*UhwQ}%^AkmLf^VkJ%9HPb zZWi84qs~pC-_jku4=BABjRk+>k=}F5n0S0#P^6DqLloF0+(A!Ybclwd5;X8V{oB&LY3Ow96SJoK zgnMxHQ|7m*J=t$R=C%fUw12^P55EH?m}b9riF_ZT)itrr0hYFGZ^--9yPQjH?xyn} z;&=4$4f2e)4%V!@89&eB_xyq}+nSuC>YdF|UF=DiJn)fq|0G|NYMH()y-q%})7+{( z3!Zgv;d%CFbf4Phj5!`2eSs#;*>(GN1Wmo!|elb9&orV$X%K zv3#cH416T>545h*yTq(xZ4V6JJc%1UUSm8`zRs``cS*KFt9Ycfs@7@p>xI8geCOHT z^&=LJ4C7Vn6m06WG;+@kj;3{e*fp8{FfXNsv$ur&#xvgS&ZPVR=~jPb&~N(eebJZM z4>Y`->svoA?cEQvOUf6|fwQ~(p5_~i`vRQ$vRHlLS$#45H9kgWCZihg>9^v@H+d%H zV*ZuN9rsLB&a-%N4LBWR@Y~wM>_Z3a{-lRxpHpjatJm!LDu1oY<9GlZUDriTMq8jM z=-KX6O9uT2bakM2S<+gc&V&c(RJrs9f8Tx^H4nV$O)*Vp#&_1Fm&j+d>6QAK&`b5p z$tL%qv+^~vL1$@0Ix8P&0&Q24p8;0~`7@Vg*6F^p=^r*gIxoG=0LMJg)So`zDF0DB z@^?nV^K+ud^R89A(=)tF!_%?ep0H=z30~Ik`kg8Ca=mMlUryWGUDX*a%8#GVY!Pfa z9m8lm$NAF|Fz+;&E^u@$C%`Y$9gT0V%ek4fr?GM+<%=A;(L6GD9p&UnpEbAgyG3oB z0mb9(2A)0L8`TH$E1np==DE-x`n)sbXNQD$?&qKAb&I)UWNs0-h@p3mVvW6moIIap zTO2Ymon0?|i$S%ffU$8mGuC2?0yc2kmI7|lnfDuiy%_ei?K-%n_(b-Si0e_FW z>>-4@qD^~dj^F8)y^)=g#TnbTM*m9Dt*&YxCNJx zbz!_FY=P!8tBX!g$A&)k0^c1}A8&~1&tDhkJMzU>wO1`2*0^9yXw2yTLEx*#Oj3Rt zJ3ZuvZ9R(q=aHrG6=aj>Q-8Mj2ePl!GoA@=!k#Db<9LRW`A^HcGq|t*gZ%d#0vnMu}Qzt36!gvUD!XuSu7~`TXl5fvEF5iJ?*}||c zv+o}4uJ64r>-uq6m;KOuVf&_2(Y{Bt#s7xGq|4hnSbt<$qtoX1(+c!bBFS^?zDV#3p<63H%tFBv|;r7vPkco3w{#vw>*9a%tiC! zyl&jom7dJWLl?H>Q&tX5(kJ@T(q6LYX+0}W{)XtEv`!cP;rwo^V*PF_@m9T~&|2CO zeJM^y`W;u5>vvp3d3sPww8NL{cU@J^@46PsTPc4ePFHyTRCs=1t8N!P;deO0H?+Dc zVm~WC82(f=x4Fc(-I#})SS!V23OGGiG?uRUvV7a(dWr8@+WqKW^<3ZecrQPU3EqY2 z-5&R=)m{$zWP78VhcxNA;%4Me9NmnLD84C9SBynQN{>Y^6!SYaNtv)8^TK>r9 zw#@eO7Pqc?hkX|p*_tmn#kJkccou)%y$jp&Q(}AfJ%3M&aW+C_8TmfGzLS3>n}=RV z#~Albg6Wvj+&05t7Vii!rqh0&lzqL2GqoIT9BRCk8Uy{{)ff;xqScjVTcYjrrxnxN z;bF2K#sMRG;Z2Wy#oSc%=MDC~MtgJzQ9q-34myS7jr65X`wZ02BPYo+C0$VZ$dJzL z5D(EE_F}vBLoi9*`0{Vj)kM9HsoYswm)T)D==VaqW=2ou1~V6yU^h#sFB`6XFge($TPMx=`k%8>oV(ljcL}a zzwu)%x^JP4n?7}#Jq+=dIk8DN9k4dvvUjmGv@<3#mX4(#$9kRcb*PWF_-x=S9HFh!|`K;6auGK|e zsyhdNBBbf_oG@nd$HhEAX6&){riJ__gCmq-{%{g^g-|aNgsTlZsoT(gT7P-ee`-ST>2368OVz{W)&sH}2B^l5yJZoMdKGpXn7nZ~lSF&4|TQvFZLkY~wl=O4O#U2Ko}Cazo>av*LAKb&Ah8KG}|Yc4j=Hx;r)}cw~P&2=dl)O(z&u@ z9J({vN1X&oY;RefpI z7?~B*AiT*vh&=hW2SL8}AhgyfKg`{Zugv>OJ@J~K%w9xcyv+1;L5KWZ>6GO9QT8M% z@S6f}I?iBxVy}_o3*t`}{kZ5UeRuM^eowdioGY*MLi_-1LMOH&e#Q^Av~0M_l;%C< z+*z&-zj>r=x8{$Edq-QmbrwF)eWTlS_BT=d33DViA@IJy7i?FkuQ@6B5s}9gktf{2 zws$c88B05g=go@kBd6J3Z&6!j_i4jyKJ?_(-dWyW!lyGuA2_vU`o6c_2CF`)Ez0TR ze6^Q>mhspDJ&%Vz#S`RX5iiC@%2!IS8*g)PHR<4A@qQoomAx!I4>Ix#k)`UY%hJe+6qHqVmZi<*DorS(tkJTNDo*L|DZ z37n_?1zu?$aquX!uF7~6Jy6d0fw#V$^TY313J+t~XlK5L5A&=)=u{jR=>+shC-xhj z@oex#y23eJJpMJ$dpVge?D-t#G|q6!ZSD^815c2jd{OzHvLjw5Ey4Gkp!r#433!!B z-yC~m;=7eoH{eT_3$N@j?aMZ|q@^peR|$GG4;1_g+L1l)pf9SUxi~F92pEmCyygvX zriHyLb5PZEgDb-u2T4XV^{j-F%jP;WpVmbD6guVnY_dw$jb0Kq|`YPWl&og~6d7OamO2-t?FYib6zBA;cYtkj1O-a|VKlW`y zwZ~lEtauLPUIqnSqs-`|jQgl?5^R5;W#h6<@+a($7Io+AI~8&J@%!dHORp;N zX>>lWJy507`hJ3F@wzNOrce9_(DtdtZ9|Vk_abP|>G|iE8LfJr6kvHT-f4bC2KcXb zM=C=Z_sHy97t+t;if}DX;5vdb>7Ug{hYR&z?5;m3)IUC^r6W=Q z&wuQ?!*s=Xh1|W46!biyBc|sG`4;p%0aMWPgx(eO9J^zBBiqfhd}ry&$``x!{Pp#H z-x~@0cW&++&i>tGjq`i_Z@q`TJGZR6?5^-T@VOJX*8|^yzZP4b)mas`SbagKw?tLE z2QZa6i{Ckxosq11=qCCneTrm5rW*a-egtPoFQRva{!u@L?;|+PQJkSn*1nec>1VnT z!dk~j8YRI>lrck91&u#f)BFH7p&8sSGzOqSp>+vjDuTxU1nF?q#yZJx=CyeB0S zm1(cl(;mvcSC~(W?rLbgD zkKM@C-a%)L>K=&g)R(mfsWfXAyIY4&XWN!s_89alLz=_cVv?ByN(DWZuyeDFfvRA`(E-=@TO{O|YeyB*NTeY8xh`;@!R z!kv`S8aLpwdc^P?eQU{k95Ypivn|HXo*ejgqYE9trSXX^ zlMe5eJt4nBJR>&U6&!ksPHj`($7x6L5t2D=$tF~JK5g~#e+6If_4VJ0&sS!${?VS} zV8(I@a$v8*eoI?>7U4ItdG7JM?}PDw@uefZgT%9~?Za=s???TO-E{|UtE6XJ>W}T| z(oT-DjMt($J|8eU1wYza{cp>w4cQ#~ZF%6b zvO9^hkkF?6wtOGuh2NHkPx>v}=a7Zr=6AxxJK>f+7!uROnepHS`c3%)4BxQ{F!_K# zai+rEEbDWR_7mlW-;`G$^_%jv}k`Hb5r|zwfN`HjPK@kJZ1)8?Srk~(dyVg&&t@BX&>2!y#>a3Q$ zXx4ekEa;mR%Q4411N;`(nl6;*@r{MU)(-H+`V0L3w0=n!u|M-nccqt%_ebOLZofyZ za>-PC5C+>l*5h(N)(bM1tQoKLy|=8BW&bl}{DjM%Q;=a&#*VRSI?z16P>!!M*?8i( zOQ*39ul~o%nQ@`HF^{dA&bna2Gx(B>Nq)wlrC@W|CokklKJsxG3)r{5#~^b&axOR9 z3Y=`sAsK_h&lhr0l|Bk$amyWyKJ>wKKM|a}~YezQ!v@ zzU4KYh2bBw9-$4^5RPwO^6d`!W6asy^P+t1^_*`UYyF$2GbKH%|LnmntolH-;FZBM zUemn~=3Dc*juk)Dw@L6GUsBJJ?w1g!H0LTU`|DxsT#Ip%#vW9%hs0ddWM#UmvUl!N z2Cw@a29Ix(#TE&N?t1t<-9cW#9j8KioLh6aKcgz`qfPq;Gj(T*F5h<2e{D)<=xllq=U-!#t|Z{D~JMi%7A-6G^|ch121M)+y>bEvEPIZ3{~zP^v& zijt18R{5gV176o;Ta{PPDV|NI6ib(USyGqs=&MjF$9zd< za=>w<_`&)X-?}BwnmA9r@zhhCLDwg0EyME?bc%PGCfmL_J|cQOP2-7r^YV-*@lxLs z6CV`99t30WCD;D8*k?tby6{y^hP0=(j9@LDE=o5SrCq8Ae8CTvjvnba17NHl32*Q{ zTIG{GH_P_k@mXFAu*vm$9^2C~3YlQnpnVVh)m~>?dV@PwV`t*`bspGz9iK0Ue&&y> zC=Yc5T#tK6>ZjRE`{pZqc|I*4P&QP0Q|k7>KR+L3r@3+L2T%vzvY&%2RR7PPweQ7h z{p{1X70Qu|+OV|tG)VV=kJB-{+9w_Heul5JZG^sOL|@eZ>ex2#8m2tacDCCyX}nu# z?->GP`a$2dKIlbf5gn=z{FAIx4R?f1-Jia+wlQ~oZtKhGaqTbXqTI`Ae%EF^XLmE~ zcWqLeQ+qGvcWqMJ-1i%w;ysYZ-HRI(Z>Il`8-I)X_fzg&+uZVxn_?b7Pt_FkggujZ zzTc0&(0ArQ`3eK~Td-vZ06XjeVA%)RZyMM~*>Bjf2LLT9)I{?@-4*>S;1Axt9A7zV<^FEApnrGSJ%=7VhJE-v_`@?uTC>*j$*n-?Y zZZaEo-|pC#k-ceuvs?I2VJ^&w%55Bo9ska6B^?Ooq`+2~U{d(* zvwmE}x5a^zKDl&aUeWqO^QH7`oZItU*t>8auzitMofAnm+zB?kQ@|m;;Z8{&8M)az z`E6Iyg25LHxSYGe>bLv)k?u^1UhU7aUScn;$@Y?FvA=z80tbF#H;(ST8Pg;D8^fW! zHR1R$YppY&B*>!c#?+JHY{>eqZy%fy_m@7!cNfJI?xwT*GEZxqyuZ`M zUaj%P>Dx)i`R6q44fcDvzykkx{y4wZnBEQJN=sv0p^W##ptTeIhIi1f=Pb|SrJnWu z9Pvia`rgef4?l_LDgOBiV8m;|=vxDGe7?TNd8vP1r*YG4?`Yzmhkaa~HTTTdxdXha z;Jw*p+B>1owwB>8r~Jr>HJ8d0d+nQz4)V~S5k|)wsFO!tI{Re2e8kQH=6K#sh5o7Z zs}vb?9uGZjdBOR;KIxwFs5i&j7OeEa!W(do%q-jzgu)UWbD z!#?}{*N_=!7d>Ql>7kKXkb~nJonH1$c5WaUc{wC`5C7hfo+fcTBpo@_@|*7EH+^8e zm95B4H~u{?Jl(*iU|cbTE3ZZy$1gZr@#^JmbFw zbYc8{btiU(5U+tYN}sXaYSY?~z0>)f=3L;cJ@nCZeH5@f7hoA92CF?P;iNB|leTm) z#xk-Q+WV61WOou{$bZiho1L(g)c=rt8R!cA-^9CTMjLq*f0G{%4u9oOJ{l?avw-91}YbS8S~xmyl^*X znfCa}uVC$-8$lnXr~9D6V7b43i|#@W0K1+(F%Em_f&oxEv>Dcdd;f3sh3nY*1fNG zX8n@d^VZeWu5PTWUv-vqE321xr)i^`?uOd6wTM70C7QWx)$&#K*R3j4NjKCs zUfZxLT?5@}BE6*c>J`jJT*PDesxnr-O^=^>652Td&dcE9#b{ud7?Nr2e|}+PdqFyh1&X4O*A3O~c5z zRp&x04K?YNb!%5vH`Xk(qJVEPJX@Y_Y+4*u3Fl-Y^^Fb*4L+3 zRSAqo+6x#A$uR;^Mv0M{x;>@gM_0?oLf)0 zlkiQ#BZSGkaJz)Cl<-x;F@v0IILx_!C;SKD5sX|J7Za1_lFm_1gJo9`#**IR zMh#|=m#rf(>V`al!DR+1TSk~fdOI-)%5EmiCjAoi=|CA*DauB>qsn$ue+tiNLD^}P zKT3UeGRu|_CX&97SnxLy&L#aE^@Z;$0$$XRJKgBP3wYi@IGgk{^oK9km8~Ffvt-B) z;$eha2^W!mjruUO4D(+$lJ@VR{Ruo{Y|Boe{1MtO=Xo(SQeVu* zT9$EhcgXFuKa=O{3GXERZ?r#(=W7U)Nk2d=etv{-A?e>!AK{g~oA4%=r7NQbpUU(5 z2&Ys2ecI=wyzFYi+em+jSp5DF;e66BQXip~y_fJ7(s$8*4bLASyo>bDY5!QBR}oGm z{UGrXgpUy}A-&hd@}Ea~1pU9!%YO~&lPP}){Gzv-kRg3HG0IuCkx)sxgL2XH9>US2 zZ}akRB0Y`tE-(K&(r+jI55%JP7QzLjU#7m~c^Tm-H@qwr#qz(F^gAg3o|iwysqC$! zw-Jk9A0(Vd`q$K#{Qs8lX40SX@_#>RZtxEInU_D)Ng1BgkbfqYK7EvMG3h^2U-FmE zjH3S!d-<;=jU0x2$ID+lok05Y#Dc$(Fq8BzsW16oNr2BoKI!GZp7dFypZ4;{GL)g$ zLpXUVlRj-G%pv_>)K`BlCyYtR|2ooVQ2s+N|D~jlBmEU(wfAAdEYiQFzWBL_z_=aq z8881ENKYsIA71|Tq^FYpCb9JC4*Zeq%mm=SKrHwh2{TFmiu#iOm4stS-{IxIp7dFyf8yo8ob(jZ-ykk0Y$nVh z{Rir+KmCpWA9?vN1?D*5ze=q3K1`TJ`ghb9Kg0O{te1Zn|NrUbUk}Vw;J-zDB;n(P zOG$UR;e#SKW>6H38Z^QkJ181HV$dLW^q{Eps6mzP&4Z%s@IfVR?4T$$dQirV8x*BS z4jO{L5tY4Z&_G$ z(_I3NLEsw<<{@A$2iuXfHOh@~$GQqPflbj<-C1s?Ti|NkT6d$n$35(JyA4q0P{iT6q)y#_(?}|BmM0G5kB0f8+S~X8vXQH_)HD znG7k?$$*wae|KP%(wpvp=;>Q`;IYrY&_=)=a0Xlf2UY}WIk*)EMzu{fXgB<06W<3$ z53+RVM;sWw_#R<0VH@EZ!mkO(5I#lt8^X^B?R z_$A>@gijLQO?aAcI^mxP?<4#d;RwRV2$v9kNH~%36~ZdQZwYT9e1`B|!haCnMffJ+ z1B5>lBEm*OC1EEaL)c2FCgchFxK9gV9^pyC$%L;F))4j(h7fKcTtIk?@OHxeggV0W zgrf<6Pk0aE|Fw7i(ODg39=~}LS_%PDNZYif<*mkTJWbO;(-swN2v9;B+7wcaO7|ut zFC^QL#N^F3AXav_tI^^qO4_0VX0@)Y(k&|8qEd^NRo2p#-C`SUshlHPjm_u+5D{{Vjt{t1>_0p0=r0{jJdF?ccfY4FqFd%^dD&w3egpgl_Ft`w02!0p*E_gY3Irs(e3*bs{CHNxvB6tCK0r&v;0QfHOUEs6ev*2~$ zb>O4mqu`z3o#1c5--1)XDd4@}z2Gh2E#UXT?}Jx?SAh?M4})D`7x+u?m*5O=2KX89 zGhhgY;E%x{gV%%CgO7ucgC7Jx2u5H8$_P=7PvV>KMfiGrCB7Rk&#_v38NLl)f^Wpn z#`oh3@bmGd_*Q%|z5!o_Pvi5k@OSum_$Tmh#D5Oog8vC#M%*ITi^pMmoO{9)gN69% z_ym3`UY?^D;m^XKkDrXc6rYDb7k>`^LcIJt4?hWi2|gb`4L=coG5&1)1^6H@kzY_S zZroY8vvK3qouh6-j+>a{3eVssoxx3hjc&?obnb~Tb=tII+cSik-Br$O;tBdozz?0Ngc+W)u*A= zt6@&{jtb^f_o(0*s{f)hRlhv@WiDg$1ew3c+{NZBa%ajMMdl^^!r~s~MFXB+#oU(x z<3+%j5HL;zj0FK>NWi!eFopz-8$qxRKZM_dKa7vN+;k;PNl(&}bR>-=UTjz6oAB%K zd+?I!b$^0^&J?S^t?ezcY~S3oI`FIVvivGWvu*{;@5xm5{e;Cc#;tSh1qL zr9IV|W`SGYZa8DhVOb5&#FTZ^Ro%QoSdRU_PW8xwxJ1mZqx<#n)33wk$YmiJeOtdB zYuz{R;{3OjFbP~vyc0nbRdQXL7e({IR;xMvk{(R#4$eDuK$CFp~N7m{DF@}ejzVA8w0 zB8p1Eat>RV50+gWMXg|cRTSL~4qX#P4}wQ#N6`Ur&O4&$1Xx@{{^^V-Yso*jb_w|h z+ulR|!6Ubme{kS0Ngv!1l0G=TiTs0en#n(p)xqWDAKVMpf%{v?KUmUA`rzFwNFN;9 z7)5)*IfLY1@ILa-jaPp^`3Lua^TACIMA0%Z`6=oh8~`_i+c(o*;7Ra#aNQQ#0W8}} zd-20!*B7E_4!C|B3OAiVlM7zCwL~;~%9yxG}rG zMtQ)--INEceT?!*_?xr`xDDJ6?gRIO#g9`TV9B?lsE8jlrB6gr8Cdmiln0#iJ<0>F z2m8T+C!=TsxMzP9Z3Fi|g&lAhJPelqdla1n8^Iz5qzzytxC?9mcRm|M-QW>$1GtU< zn0gy{5Zng_KO#S1)pOJ@KYe7J5tp}SNl?&#Y2L+!Gcse(?%%%>$=D-K;+5lv;aGul zF3I0&e8DTPM73ZrcXDz|!PfkmjftCIIP|on=Ellau#!0378zH{6{~qc8T+^_`;zdNYo^<{rHmTm8i|f zv%C!EHF$GUhU55F;?PB%E8}kg<7g8ctCg92P=;E`#pGb_#D!XzO2n1K9U!jcojkj| z)JR@x?n~66;4(9JVx3;OXF@AF2hiCY)0w9__vhUzTF*!s=1yD^yY_WEgl;2a)+w)B zy=DB?vuieqaF(HadV);tU&&m%X-!?H*Ue`v8 zWBEFPd@u4wO;`FXT_jWPV#fPZ$oswA=`V}PtK|7m7)8l=`Zr|LZxA`-da2L9YqP|M zv+^|Z#wmb>0EpNS=2F8ASj4Cz;QwN`Z_6N_n zHcBFvK2muMDi-64lvqH6U+fo>1rYi*D6&$-HGf zd`G-re=?gdna>O_<9U6hv%4X0%havs<}a8iF(h9y&pG%Jp7-9a%U3FvFPZ<;e>5{* zx%P^WSI3Z7-ota;=i7}(iFw)d3u(vF_4NOkeeM2k94$wlMn0_aw3bWeRaGBj9^uR7 z?G9z_%6x0{z05aab{k{)mU-9_cHk{z~c2H#Dk_+>k;WEY9iq(qsicN~$iUW$96iv@tcI=qu1S{%E1oZF*v8LP#a{H0f-E(sskgf-cqp0QJ$ zJQl?rkU2e$$B1(k8d0OU=Yt3|T5$9#pCA91=b{Q^ycWk4&AAH{**52#@lEIYu;p`` z>dRwXoK{EhO62pO)cBExE#2c9zDpjYxR*6-_2Ah*-``zVTFQSun}3X@by^{>qpz*p zdPvUn?NaF`sm$Ld`wpK6T#EO*B3Nf}KbDx+Pth9!bL>dV$gM5*#pKf< zvfKE6Al;XoIkTdwqVmd_eJ(iju357xDy!bCv{^bm=5vql%Vr}JF+WIT?$->-MLz%R zJ{`#yWbWsY{J6mGi;+BIs@_i{`LhGNFGup@W!vj0m(SZ-8@J-Zd`1y#pOO5;;IIu? zF+o0Wh-|!z(Uf))2UU%(pnTq36<#zJKP4!;WGucYu<>)G{c{5wH%IbQgMB)lj^w8W zW8MGx%u)w*JRBMSyv#T`l0QGN{yLJsAQ;eoJd(H3Fw06QIUXy@GXBbsr=#mx#_LlT%f7*Q4t=%HKN_m^t$~#VRbCkFFsJv4YXZ4v(x?twdBD16X9F;6pewp&S zly6qP_%bC3i+dlK$9UVO<+XX?T^hgWav$%Q2M&Yxs_%gxF!yV>*ggvC9m z{N`Ccyw*doL-`UuV-V-o`oV81pH4fP&oVtesr*2P54*K$E}vDtYO&|tS~Hi2mEV4g z=iOR7m)0M~&mcm~{WrnWk8cx$<9Eg)b9qwj8~>n+Comn6b{}Sox41eF!TIn~&jI_H zozo6I!}v*_U#IcktaeJ-Dle{susG|V-8ud9TD7xI?fiw09=t>C?BC&uVdbqJhTfq0 zQGOBe$EpWQcbnSR=$ZamWgSfg4Vs@}jn8g>=j2_Q)I*8R(~NIXzESyVjem#on{s%| zf7zQx=D&{ke<=SpKUDKUkJ>k0yIpXX@-|=WSA~x%Z}UWJ-v^X0(RrxM9mNePZ}Zc2 z1)hIMd7GD-{jV!u=3WZ2UhSg?-%)-@d5iDs;ryU1XIwqt;%E7!+Id0wT8(e~G3DEG zcpHEAeoGA~{Eqn2&yAOHTpZi+9lz<@K5XN5vGP?bh^V21MLdp+US&AH_NFLHKv zd`Ee$w`={PZ=*=VRVFu!{>cQfd=fr=R{K%IjxxAz@mL2&_DKW=4ZTopr6+8ORxDYSd2d@1V)99Z&cBV)XCCM(oIUHo5Mv zPQ$Np{KTN>60gywiXTzF;89NuDE~?Lq+ZA25sklLooC%UTP`0{zV@Frwl?Q~!k^3Y z%jSnQDn0`L2EC5MD{80oInNd{ZidNNW5+9r&&L&QyL# z$J;??JqX^ae5rIioQ*SSXMa*)&+)6gIQWpp-z7gNaGB?t@;l2tpVWGI(D9y&|NR$@ zKU?o>RSO-z01y?D*N5}t4FAlC# zKCMKV*5`c3Yor+5uJM~>kq@`mLolfPvK^l2R{rzKA6V-Nd;WgqH1@x(@%PoL9j)i5 zlpoakv3CEV@iO9of_~)>>it!xJZE;f_`Cf!Y4-Secdzm`PbgIW z)9~_@qRlJnm48sPCfyjbj-*4AKa(>gJ1R$ z2Q=M(P~O&0wyT{Nls~Bhw%G|FDZA^#-M_K^V4ng)2L{mN$2Vt(>aT8#ec0jNR=WsdTEfqH^pQ zp`5%EHuv;2uMXKO&o=oLJ{fA1+6n&_h@cz!AN!&K2z|`+C@w-_;qWo7tLgQcmrW^I|f=70tcr zOrW=;>&~F9xwkD`(bw6+7X02+Pg>6T;Xs#Qd0)pJ6u_up)#^~U`%^QT_R=&b@XT`u z7{L5J?X_uYl0OWeWcUPcM4)$^}{xrC==)*+Z-{}8uHCS zKr^jO^`=9AGLC*^Oj-46M9TfCR#)iWfHPcHwD(uc3gq(xxtbaDb@Qo1N^{oLLvJAe zy{kK0dQ;6kEp1|ugN;(X*zQ=~+?%Sn8Y}&2vEhO<$sfVWfWJM>VFw4}^y+nM?0UHu zEbjQYqW$NV%q|(E+AGzf7ndmt|hM)g|3+D<-2FE-8v$KZ7-w}Lzi^Aht_iG zNKd0EbT@wjPxt#cji{4DhPs25sZNPaitbXrx9AO)w}(9`Y0nf5-QLj}`h2!D(|=r! zB@LflCnoJIFZY{-dg*!Ht2IR|x;Cb|(j21{$_FN)JHJS7DLFbR8&O)LuRB=Nmu{DS z-z*0*T|Fy%6{51dy)zYZAGbIGtt4HSC}DWRlDUf)gbQw-ABKUu8|6mr?F!pC;fXF3 z&VTpKa~Cg~2fOg*+rkC4s!%(B2~@-4d8WFs?v|Rlb>S^H+^}>(L)b94rfxwP@aaoS z_v-3}b&G1|g|jMVUPoKEr{m#tTTj=WnWM9;L%vf^fuf@Y;i87cw3CfFq$eXzgS#oU z^P0}WRC<2)OCGx?q)y#oR^+y&zgbF%?KP=*9;klqb56DL6-0j_#h!Q!O=^i8-gQ$d z)g6e6>m@gG_*lrnU?B&C&Af)2O-=?2IT>96d6H-923`w4Y_0*0CIFDz|l# z{3;sE?&bQP_EjlHtPW1Gisx$00{8uqS3^^MUFkAg}DD(?g(4j zc=&M4SUfqs$0-LNIECTLRgBHVW`qp`*N1Y?ej8zJWnXhoYuuccb}1uQeOG&@-0kk* zg#~>1rI%!Zk;Ed7=v;b>Ss`k=`cUIx)y-I+ULpC9r}gT)i0fIJGTmCMObOo`q{8Bf zXm!W(KaYN8+4t3KO~Pm-kBt>~_Hdlo7)Iin`T%_OWpF2dE$!>>=8?rCQ!}sKWsUDI zt`~AUuIx0q@3eC3yi>o1TN83GuIkM^<+Asnnsmf0KT?c2PK5GE@EzyX7o$gRd2498 zZ)}K>T+U-O=G(v<7##w1?fXKw5K{+GdEvDA45t| z!TEJO-@$Z`$2Pm-T*Xwy%FezD_Yk|Xy)|$lnbQOnt*blH^qB7P68mD&MbkittS8mc zEDCzn-H{F|T%WE;^V|$MT!}doAkA{qH7_q;pcl^!>&b3lE8q=PV=hv z7V^`DVg6LO)V$^846_wH{Z=ux)EV$=19{!aPb${PyxXTg4-89{H(=RjgsUL*KkP9w z{o|@{_`HT$_HvG&8zY?jj*_3grf>T>4A)8KaFVXrF#QrCan*R?P2cu$8QQ)rqBy77 zu^`|42Z8LNGJV@0VmPERE&mqJkQ0#Ho-7L|b&<-jTJKocV zFiYR`t^A#;U!w+XUy5OqDhkM!m0bOPIH?nsFPyXeD~6Viid{R{wO}tku5bHX44bTA zs#K(CAg}S_`nDg#aPv>ln(hXK|OC{udEW##=JBuV+`eSk1P#3&g@^N0v3G z;7#B5|LplA^}mg-q-6T`zJH(UA4e8D#*dt}A{14|vG5o9SCW7)Cdq zzOAm qf#sJ>-hE8}H(Bdb`ng1iIZNLt8Sbp?#tBYX;$!}Aoa8!J|Nj8SmpSPG literal 0 HcmV?d00001 From 477ea5983c4c1f7c86a7af6af347ba1179fea378 Mon Sep 17 00:00:00 2001 From: md_5 Date: Sun, 8 Sep 2024 13:15:40 +1000 Subject: [PATCH 26/58] Remove unused field --- proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java b/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java index fb497ca0cd..27c5b0664a 100644 --- a/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java +++ b/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java @@ -23,7 +23,6 @@ import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; -import lombok.Getter; import net.md_5.bungee.jni.NativeCode; import net.md_5.bungee.jni.cipher.BungeeCipher; import net.md_5.bungee.jni.cipher.JavaCipher; @@ -41,8 +40,6 @@ public class EncryptionUtil private static final Random random = new Random(); private static final Base64.Encoder MIME_ENCODER = Base64.getMimeEncoder( 76, "\n".getBytes( StandardCharsets.UTF_8 ) ); public static final KeyPair keys; - @Getter - private static final SecretKey secret = new SecretKeySpec( new byte[ 16 ], "AES" ); public static final NativeCode nativeFactory = new NativeCode<>( "native-cipher", JavaCipher::new, NativeCipher::new ); private static final PublicKey MOJANG_KEY; From b309e4ac50ca44f2cfc06e8d27de33a1f0c77549 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Mon, 9 Sep 2024 21:01:19 +1000 Subject: [PATCH 27/58] #3737: Use composite buffers where possible --- .../md_5/bungee/jni/cipher/BungeeCipher.java | 6 ++++ .../md_5/bungee/jni/cipher/JavaCipher.java | 13 +++++-- .../md_5/bungee/jni/cipher/NativeCipher.java | 6 ++++ .../net/md_5/bungee/jni/zlib/BungeeZlib.java | 8 +++++ .../net/md_5/bungee/jni/zlib/JavaZlib.java | 6 ++++ .../net/md_5/bungee/jni/zlib/NativeZlib.java | 8 ++++- .../Varint21LengthFieldPrepender.java | 29 ++++++++++----- .../bungee/compress/PacketCompressor.java | 28 ++++++++++----- .../bungee/connection/InitialHandler.java | 2 ++ .../net/md_5/bungee/netty/ChannelWrapper.java | 35 +++++++++++++++++++ .../net/md_5/bungee/netty/PipelineUtils.java | 3 +- .../bungee/netty/cipher/CipherEncoder.java | 2 ++ 12 files changed, 123 insertions(+), 23 deletions(-) diff --git a/native/src/main/java/net/md_5/bungee/jni/cipher/BungeeCipher.java b/native/src/main/java/net/md_5/bungee/jni/cipher/BungeeCipher.java index f71cf9c9d4..50178dd6f0 100644 --- a/native/src/main/java/net/md_5/bungee/jni/cipher/BungeeCipher.java +++ b/native/src/main/java/net/md_5/bungee/jni/cipher/BungeeCipher.java @@ -18,4 +18,10 @@ public interface BungeeCipher void cipher(ByteBuf in, ByteBuf out) throws GeneralSecurityException; ByteBuf cipher(ChannelHandlerContext ctx, ByteBuf in) throws GeneralSecurityException; + + /* + * This indicates whether the input ByteBuf is allowed to be a CompositeByteBuf. + * If you need access to a memory address, you should not allow composite buffers. + */ + boolean allowComposite(); } diff --git a/native/src/main/java/net/md_5/bungee/jni/cipher/JavaCipher.java b/native/src/main/java/net/md_5/bungee/jni/cipher/JavaCipher.java index 0e27c2d7e5..94d0269136 100644 --- a/native/src/main/java/net/md_5/bungee/jni/cipher/JavaCipher.java +++ b/native/src/main/java/net/md_5/bungee/jni/cipher/JavaCipher.java @@ -2,6 +2,7 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.util.concurrent.FastThreadLocal; import java.security.GeneralSecurityException; import javax.crypto.Cipher; import javax.crypto.SecretKey; @@ -12,10 +13,10 @@ public class JavaCipher implements BungeeCipher { private final Cipher cipher; - private static final ThreadLocal heapInLocal = new EmptyByteThreadLocal(); - private static final ThreadLocal heapOutLocal = new EmptyByteThreadLocal(); + private static final FastThreadLocal heapInLocal = new EmptyByteThreadLocal(); + private static final FastThreadLocal heapOutLocal = new EmptyByteThreadLocal(); - private static class EmptyByteThreadLocal extends ThreadLocal + private static class EmptyByteThreadLocal extends FastThreadLocal { @Override @@ -88,4 +89,10 @@ private byte[] bufToByte(ByteBuf in) in.readBytes( heapIn, 0, readableBytes ); return heapIn; } + + @Override + public boolean allowComposite() + { + return true; + } } diff --git a/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java b/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java index 7797a46b27..614b8b2992 100644 --- a/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java +++ b/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java @@ -71,4 +71,10 @@ public ByteBuf cipher(ChannelHandlerContext ctx, ByteBuf in) throws GeneralSecur return heapOut; } + + @Override + public boolean allowComposite() + { + return false; + } } diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/BungeeZlib.java b/native/src/main/java/net/md_5/bungee/jni/zlib/BungeeZlib.java index 0c98ad4073..50701454d3 100644 --- a/native/src/main/java/net/md_5/bungee/jni/zlib/BungeeZlib.java +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/BungeeZlib.java @@ -6,9 +6,17 @@ public interface BungeeZlib { + public static final int OUTPUT_BUFFER_SIZE = 8192; + void init(boolean compress, int level); void free(); void process(ByteBuf in, ByteBuf out) throws DataFormatException; + + /* + * This indicates whether the input ByteBuf is allowed to be a CompositeByteBuf. + * If you need access to a memory address, you should not allow composite buffers. + */ + boolean allowComposite(); } diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/JavaZlib.java b/native/src/main/java/net/md_5/bungee/jni/zlib/JavaZlib.java index 10da5d95c3..0d65522cec 100644 --- a/native/src/main/java/net/md_5/bungee/jni/zlib/JavaZlib.java +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/JavaZlib.java @@ -73,4 +73,10 @@ public void process(ByteBuf in, ByteBuf out) throws DataFormatException inflater.reset(); } } + + @Override + public boolean allowComposite() + { + return true; + } } diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java index 96e3077713..bad84bc444 100644 --- a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java @@ -55,7 +55,7 @@ public void process(ByteBuf in, ByteBuf out) throws DataFormatException while ( !nativeCompress.finished && ( compress || in.isReadable() ) ) { - out.ensureWritable( 8192 ); + out.ensureWritable( OUTPUT_BUFFER_SIZE ); int processed; try @@ -74,4 +74,10 @@ public void process(ByteBuf in, ByteBuf out) throws DataFormatException nativeCompress.consumed = 0; nativeCompress.finished = false; } + + @Override + public boolean allowComposite() + { + return false; + } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java index 1adfbfcd74..3f0a866fc3 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java @@ -1,27 +1,38 @@ package net.md_5.bungee.protocol; import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; +import io.netty.handler.codec.MessageToMessageEncoder; +import java.util.List; +import lombok.Setter; /** * Prepend length of the message as a Varint21 by writing length and data to a * new buffer */ -@ChannelHandler.Sharable -public class Varint21LengthFieldPrepender extends MessageToByteEncoder +public class Varint21LengthFieldPrepender extends MessageToMessageEncoder { + @Setter + private boolean compose = true; + @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List list) throws Exception { int bodyLen = msg.readableBytes(); int headerLen = varintSize( bodyLen ); - out.ensureWritable( headerLen + bodyLen ); - - DefinedPacket.writeVarInt( bodyLen, out ); - out.writeBytes( msg ); + if ( compose ) + { + ByteBuf buf = ctx.alloc().directBuffer( headerLen ); + DefinedPacket.writeVarInt( bodyLen, buf ); + list.add( ctx.alloc().compositeDirectBuffer( 2 ).addComponents( true, buf, msg.retain() ) ); + } else + { + ByteBuf buf = ctx.alloc().directBuffer( headerLen + bodyLen ); + DefinedPacket.writeVarInt( bodyLen, buf ); + buf.writeBytes( msg ); + list.add( buf ); + } } static int varintSize(int paramInt) 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 d07cf46274..314cd00a8d 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 @@ -2,18 +2,23 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; +import io.netty.handler.codec.MessageToMessageEncoder; +import java.util.List; import java.util.zip.Deflater; +import lombok.Getter; 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 MessageToMessageEncoder { + @Getter private final BungeeZlib zlib = CompressFactory.zlib.newInstance(); @Setter private int threshold = 256; + @Setter + private boolean compose = true; @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception @@ -28,18 +33,25 @@ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception } @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception { int origSize = msg.readableBytes(); if ( origSize < threshold ) { - DefinedPacket.writeVarInt( 0, out ); - out.writeBytes( msg ); + if ( compose ) + { + // create a virtual buffer to avoid copying of data + out.add( ctx.alloc().compositeDirectBuffer( 2 ).addComponents( true, ctx.alloc().directBuffer( 1 ).writeByte( 0 ), msg.retain() ) ); + } else + { + out.add( ctx.alloc().directBuffer( origSize + 1 ).writeByte( 0 ).writeBytes( msg ) ); + } } else { - DefinedPacket.writeVarInt( origSize, out ); - - zlib.process( msg, out ); + ByteBuf buf = ctx.alloc().directBuffer( BungeeZlib.OUTPUT_BUFFER_SIZE ); + DefinedPacket.writeVarInt( origSize, buf ); + zlib.process( msg, buf ); + out.add( buf ); } } } 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 0d745b91b5..12396c9a7b 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 @@ -508,6 +508,8 @@ public void handle(final EncryptionResponse encryptResponse) throws Exception ch.addBefore( PipelineUtils.FRAME_DECODER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) ); BungeeCipher encrypt = EncryptionUtil.getCipher( true, sharedKey ); ch.addBefore( PipelineUtils.FRAME_PREPENDER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) ); + // disable use of composite buffers if we use natives + ch.updateComposite(); String encName = URLEncoder.encode( InitialHandler.this.getName(), "UTF-8" ); 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 e8d5ed1969..682bb80734 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 @@ -7,15 +7,19 @@ import io.netty.channel.ChannelHandlerContext; import java.net.SocketAddress; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; import lombok.Getter; import lombok.Setter; +import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.compress.PacketCompressor; import net.md_5.bungee.compress.PacketDecompressor; +import net.md_5.bungee.netty.cipher.CipherEncoder; import net.md_5.bungee.protocol.DefinedPacket; 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.Varint21LengthFieldPrepender; import net.md_5.bungee.protocol.packet.Kick; public class ChannelWrapper @@ -187,5 +191,36 @@ public void setCompressionThreshold(int compressionThreshold) { ch.pipeline().remove( "decompress" ); } + // disable use of composite buffers if we use natives + updateComposite(); + } + + /* + * Should be called on encryption add and on compressor add or remove + */ + public void updateComposite() + { + CipherEncoder cipherEncoder = ch.pipeline().get( CipherEncoder.class ); + PacketCompressor packetCompressor = ch.pipeline().get( PacketCompressor.class ); + Varint21LengthFieldPrepender prepender = ch.pipeline().get( Varint21LengthFieldPrepender.class ); + boolean compressorCompose = cipherEncoder == null || cipherEncoder.getCipher().allowComposite(); + boolean prependerCompose = compressorCompose && ( packetCompressor == null || packetCompressor.getZlib().allowComposite() ); + + if ( prepender != null ) + { + ProxyServer.getInstance().getLogger().log( Level.FINE, "set prepender compose to {0} for {1}", new Object[] + { + prependerCompose, ch + } ); + prepender.setCompose( prependerCompose ); + } + if ( packetCompressor != null ) + { + ProxyServer.getInstance().getLogger().log( Level.FINE, "set packetCompressor compose to {0} for {1}", new Object[] + { + compressorCompose, ch + } ); + packetCompressor.setCompose( compressorCompose ); + } } } 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 25f045bebe..2446f48919 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 @@ -93,7 +93,6 @@ protected void initChannel(Channel ch) throws Exception public static final Base BASE = new Base( false ); public static final Base BASE_SERVERSIDE = new Base( true ); private static final KickStringWriter legacyKicker = new KickStringWriter(); - private static final Varint21LengthFieldPrepender framePrepender = new Varint21LengthFieldPrepender(); private static final Varint21LengthFieldExtraBufPrepender serverFramePrepender = new Varint21LengthFieldExtraBufPrepender(); public static final String TIMEOUT_HANDLER = "timeout"; public static final String PACKET_DECODER = "packet-decoder"; @@ -202,7 +201,7 @@ public void initChannel(Channel ch) throws Exception ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) ); // No encryption bungee -> server, therefore use extra buffer to avoid copying everything for length prepending // Not used bungee -> client as header would need to be encrypted separately through expensive JNI call - ch.pipeline().addLast( FRAME_PREPENDER, ( toServer ) ? serverFramePrepender : framePrepender ); + ch.pipeline().addLast( FRAME_PREPENDER, ( toServer ) ? serverFramePrepender : new Varint21LengthFieldPrepender() ); ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() ); } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherEncoder.java b/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherEncoder.java index fc19ded01e..e89c52d6c2 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherEncoder.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherEncoder.java @@ -3,6 +3,7 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; +import lombok.Getter; import lombok.RequiredArgsConstructor; import net.md_5.bungee.jni.cipher.BungeeCipher; @@ -10,6 +11,7 @@ public class CipherEncoder extends MessageToByteEncoder { + @Getter private final BungeeCipher cipher; @Override From a89cf5f36d7bfeebf0fd199cdbf7e5c6bb860260 Mon Sep 17 00:00:00 2001 From: Outfluencer <48880402+Outfluencer@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:06:48 +1000 Subject: [PATCH 28/58] #3736: Add simple login payload API --- .../api/connection/PendingConnection.java | 14 +++++++++ .../bungee/connection/InitialHandler.java | 31 ++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/net/md_5/bungee/api/connection/PendingConnection.java b/api/src/main/java/net/md_5/bungee/api/connection/PendingConnection.java index acfc627d56..1420c3ddb7 100644 --- a/api/src/main/java/net/md_5/bungee/api/connection/PendingConnection.java +++ b/api/src/main/java/net/md_5/bungee/api/connection/PendingConnection.java @@ -113,4 +113,18 @@ public interface PendingConnection extends Connection */ @ApiStatus.Experimental CompletableFuture retrieveCookie(String cookie); + + /** + * Sends a login payload request to the client. + * + * @param channel the channel to send this data via + * @param data the data to send + * @return a {@link CompletableFuture} that will be completed when the Login + * Payload response is received. If the Vanilla client doesn't know the + * channel, the {@link CompletableFuture} will complete with a null value + * @throws IllegalStateException if the player's version is not at least + * 1.13 + */ + @ApiStatus.Experimental + CompletableFuture sendData(String channel, byte[] data); } 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 12396c9a7b..091ae9a226 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 @@ -10,8 +10,10 @@ import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.time.Instant; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; +import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.UUID; @@ -67,6 +69,7 @@ import net.md_5.bungee.protocol.packet.LegacyHandshake; import net.md_5.bungee.protocol.packet.LegacyPing; import net.md_5.bungee.protocol.packet.LoginAcknowledged; +import net.md_5.bungee.protocol.packet.LoginPayloadRequest; import net.md_5.bungee.protocol.packet.LoginPayloadResponse; import net.md_5.bungee.protocol.packet.LoginRequest; import net.md_5.bungee.protocol.packet.LoginSuccess; @@ -96,6 +99,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection @Getter private final Set registeredChannels = new HashSet<>(); private State thisState = State.HANDSHAKE; + private int loginPayloadId; + private final Map> requestedLoginPayloads = new HashMap<>(); private final Queue requestedCookies = new LinkedList<>(); @Data @@ -690,7 +695,13 @@ public void done(PostLoginEvent result, Throwable error) @Override public void handle(LoginPayloadResponse response) throws Exception { - disconnect( "Unexpected custom LoginPayloadResponse" ); + CompletableFuture future; + synchronized ( requestedLoginPayloads ) + { + future = requestedLoginPayloads.remove( response.getId() ); + } + Preconditions.checkState( future != null, "Unexpected custom LoginPayloadResponse" ); + future.complete( response.getData() ); } @Override @@ -886,4 +897,22 @@ public CompletableFuture retrieveCookie(String cookie) return future; } + + @Override + public CompletableFuture sendData(String channel, byte[] data) + { + Preconditions.checkState( getVersion() >= ProtocolConstants.MINECRAFT_1_13, "LoginPayloads are only supported in 1.13 and above" ); + Preconditions.checkState( loginRequest != null, "Cannot send login data for status or legacy connections" ); + + CompletableFuture future = new CompletableFuture<>(); + final int id; + synchronized ( requestedLoginPayloads ) + { + // thread safe loginPayloadId + id = loginPayloadId++; + requestedLoginPayloads.put( id, future ); + } + unsafe.sendPacket( new LoginPayloadRequest( id, channel, data ) ); + return future; + } } From 9437cedc48aee056253254429bf63c24c2420289 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Sat, 21 Sep 2024 01:02:28 +0200 Subject: [PATCH 29/58] #3748: Minecraft 24w38a support --- .../main/java/net/md_5/bungee/protocol/ProtocolConstants.java | 2 +- .../java/net/md_5/bungee/protocol/packet/LoginSuccess.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 b53b1e50ff..9028dc1303 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 @@ -47,7 +47,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; - public static final int MINECRAFT_1_21_2 = 1073742032; + public static final int MINECRAFT_1_21_2 = 1073742034; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; 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 60ff5700c9..daa6d18306 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 @@ -37,7 +37,7 @@ public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protoco { properties = readProperties( buf ); } - if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 && protocolVersion < ProtocolConstants.MINECRAFT_1_21_2 ) { // Whether the client should disconnect on its own if it receives invalid data from the server buf.readBoolean(); @@ -59,7 +59,7 @@ public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protoc { writeProperties( properties, buf ); } - if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 && protocolVersion < ProtocolConstants.MINECRAFT_1_21_2 ) { // Whether the client should disconnect on its own if it receives invalid data from the server // Vanilla sends true so we also send true From acb85e30faf3aa10d21ab51efa8fe478f13b8361 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Sat, 21 Sep 2024 01:05:50 +0200 Subject: [PATCH 30/58] #3742: Add more checks to InitialHandler --- .../java/net/md_5/bungee/connection/InitialHandler.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 091ae9a226..224f231b63 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 @@ -196,6 +196,7 @@ public void handle(PluginMessage pluginMessage) throws Exception @Override public void handle(LegacyHandshake legacyHandshake) throws Exception { + Preconditions.checkState( !this.legacy, "Not expecting LegacyHandshake" ); this.legacy = true; ch.close( bungee.getTranslation( "outdated_client", bungee.getGameVersion() ) ); } @@ -203,6 +204,7 @@ public void handle(LegacyHandshake legacyHandshake) throws Exception @Override public void handle(LegacyPing ping) throws Exception { + Preconditions.checkState( !this.legacy, "Not expecting LegacyPing" ); this.legacy = true; final boolean v1_5 = ping.isV1_5(); @@ -342,7 +344,7 @@ public void handle(PingPacket ping) throws Exception @Override public void handle(Handshake handshake) throws Exception { - Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" ); + Preconditions.checkState( thisState == State.HANDSHAKE && !this.legacy, "Not expecting HANDSHAKE" ); this.handshake = handshake; ch.setVersion( handshake.getProtocolVersion() ); ch.getHandle().pipeline().remove( PipelineUtils.LEGACY_KICKER ); @@ -420,7 +422,7 @@ public void handle(Handshake handshake) throws Exception @Override public void handle(LoginRequest loginRequest) throws Exception { - Preconditions.checkState( thisState == State.USERNAME, "Not expecting USERNAME" ); + Preconditions.checkState( thisState == State.USERNAME && this.loginRequest == null, "Not expecting USERNAME" ); if ( !AllowedCharacters.isValidName( loginRequest.getData(), onlineMode ) ) { From f0a30c43cd2b03902f7a9e8de1514b1ccd985d4a Mon Sep 17 00:00:00 2001 From: md_5 Date: Sat, 28 Sep 2024 08:51:09 +1000 Subject: [PATCH 31/58] Minecraft 24w39a support --- .../main/java/net/md_5/bungee/protocol/ProtocolConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9028dc1303..172a9a90f1 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 @@ -47,7 +47,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; - public static final int MINECRAFT_1_21_2 = 1073742034; + public static final int MINECRAFT_1_21_2 = 1073742035; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; From 01a5f36012284255ef130d0987eafcb918448e6f Mon Sep 17 00:00:00 2001 From: Valentine <21033866+BoomEaro@users.noreply.github.com> Date: Sun, 29 Sep 2024 12:44:15 +0300 Subject: [PATCH 32/58] #3751: Fix potential overriding of cipher by other libraries --- .../src/main/java/net/md_5/bungee/EncryptionUtil.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java b/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java index 27c5b0664a..a8b7767820 100644 --- a/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java +++ b/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java @@ -11,12 +11,12 @@ import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; -import java.util.Arrays; import java.util.Base64; import java.util.Random; import java.util.UUID; @@ -108,17 +108,17 @@ public static boolean check(PlayerPublicKey publicKey, EncryptionResponse resp, return signature.verify( resp.getEncryptionData().getSignature() ); } else { - Cipher cipher = Cipher.getInstance( "RSA" ); + Cipher cipher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" ); cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() ); byte[] decrypted = cipher.doFinal( resp.getVerifyToken() ); - return Arrays.equals( request.getVerifyToken(), decrypted ); + return MessageDigest.isEqual( request.getVerifyToken(), decrypted ); } } public static SecretKey getSecret(EncryptionResponse resp, EncryptionRequest request) throws GeneralSecurityException { - Cipher cipher = Cipher.getInstance( "RSA" ); + Cipher cipher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" ); cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() ); return new SecretKeySpec( cipher.doFinal( resp.getSharedSecret() ), "AES" ); } @@ -143,7 +143,7 @@ private static PublicKey getPubkey(byte[] b) throws GeneralSecurityException public static byte[] encrypt(Key key, byte[] b) throws GeneralSecurityException { - Cipher hasher = Cipher.getInstance( "RSA" ); + Cipher hasher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" ); hasher.init( Cipher.ENCRYPT_MODE, key ); return hasher.doFinal( b ); } From 9813e46e66b540628479406b39515de3a83c3ccc Mon Sep 17 00:00:00 2001 From: Valentine <21033866+BoomEaro@users.noreply.github.com> Date: Sun, 6 Oct 2024 10:55:12 +0300 Subject: [PATCH 33/58] #3746, #3666: Fix potential race conditions when connecting to multiple servers at the same time --- proxy/src/main/java/net/md_5/bungee/ServerConnector.java | 5 ++++- proxy/src/main/java/net/md_5/bungee/UserConnection.java | 9 +++++---- 2 files changed, 9 insertions(+), 5 deletions(-) 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 8283286563..90c5237cad 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java @@ -361,7 +361,10 @@ private void cutThrough(ServerConnection server) if ( user.getServer() != null ) { // Begin config mode - user.unsafe().sendPacket( new StartConfiguration() ); + if ( user.getCh().getEncodeProtocol() != Protocol.CONFIGURATION ) + { + user.unsafe().sendPacket( new StartConfiguration() ); + } } else { LoginResult loginProfile = user.getPendingConnection().getLoginProfile(); 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 54842ca0ee..2359e86472 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -306,6 +306,11 @@ public void connect(final ServerConnectRequest request) { Preconditions.checkNotNull( request, "request" ); + ch.getHandle().eventLoop().execute( () -> connect0( request ) ); + } + + private void connect0(final ServerConnectRequest request) + { final Callback callback = request.getCallback(); ServerConnectEvent event = new ServerConnectEvent( this, request.getTarget(), request.getReason(), request ); if ( bungee.getPluginManager().callEvent( event ).isCancelled() ) @@ -315,10 +320,6 @@ public void connect(final ServerConnectRequest request) callback.done( ServerConnectRequest.Result.EVENT_CANCEL, null ); } - if ( getServer() == null && !ch.isClosing() ) - { - throw new IllegalStateException( "Cancelled ServerConnectEvent with no server or disconnect." ); - } return; } From 2bacf6572baa8a98e701946a36a6e03651b75ec2 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Sun, 15 Sep 2024 22:33:20 +0200 Subject: [PATCH 34/58] #3743: Fix infinite encrypting screen on miss configured ip-forwarding --- proxy/src/main/java/net/md_5/bungee/ServerConnector.java | 9 +++++++++ 1 file changed, 9 insertions(+) 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 90c5237cad..0f158a6550 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java @@ -137,6 +137,15 @@ public void connected(ChannelWrapper channel) throws Exception public void disconnected(ChannelWrapper channel) throws Exception { user.getPendingConnects().remove( target ); + + if ( !obsolete && user.getPendingConnects().isEmpty() && thisState == State.LOGIN_SUCCESS ) + { + // this is called if we get disconnected but not have received any response after we send the handshake + // in this case probably an exception was thrown because the handshake could not be read correctly + // because of the extra ip forward data, also we skip the disconnect if another server is also in the + // pendingConnects queue because we don't want to lose the player + user.disconnect( "Unexpected disconnect during server login, did you forget to enable BungeeCord / IP forwarding on your server?" ); + } } @Override From 672db9fe47a48f5e1aad049ed29043ee9b8a1bf7 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Sun, 13 Oct 2024 00:38:59 +0200 Subject: [PATCH 35/58] #3753, #3754: Don't disconnect during login if the player is on a server --- proxy/src/main/java/net/md_5/bungee/ServerConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0f158a6550..b58a01c6dd 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java @@ -138,7 +138,7 @@ public void disconnected(ChannelWrapper channel) throws Exception { user.getPendingConnects().remove( target ); - if ( !obsolete && user.getPendingConnects().isEmpty() && thisState == State.LOGIN_SUCCESS ) + if ( user.getServer() == null && !obsolete && user.getPendingConnects().isEmpty() && thisState == State.LOGIN_SUCCESS ) { // this is called if we get disconnected but not have received any response after we send the handshake // in this case probably an exception was thrown because the handshake could not be read correctly From 6ea49962c57b5c69c93d857a66d39fe8248cc5ca Mon Sep 17 00:00:00 2001 From: md_5 Date: Sun, 13 Oct 2024 09:53:59 +1100 Subject: [PATCH 36/58] Minecraft 1.21.2-pre3 support --- .../net/md_5/bungee/protocol/Protocol.java | 50 ++++++++++--------- .../bungee/protocol/ProtocolConstants.java | 2 +- 2 files changed, 28 insertions(+), 24 deletions(-) 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 35712217b3..8aa57ed4fb 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 @@ -98,7 +98,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_3, 0x1F ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x23 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x24 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x26 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x26 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x27 ) ); TO_CLIENT.registerPacket( Login.class, @@ -115,7 +116,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_3, 0x24 ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x28 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x29 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x2B ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x2B ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x2C ) ); TO_CLIENT.registerPacket( Chat.class, Chat::new, @@ -147,7 +149,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x43 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x45 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x47 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x48 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x4C ) ); TO_CLIENT.registerPacket( BossBar.class, @@ -208,7 +210,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x5A ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x5C ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x5E ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x60 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x64 ) ); TO_CLIENT.registerPacket( ScoreboardScore.class, @@ -227,14 +229,14 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x5D ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x5F ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x61 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x64 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x68 ) ); TO_CLIENT.registerPacket( ScoreboardScoreReset.class, ScoreboardScoreReset::new, map( ProtocolConstants.MINECRAFT_1_20_3, 0x42 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x44 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x45 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x49 ) ); TO_CLIENT.registerPacket( ScoreboardDisplay.class, @@ -253,7 +255,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x53 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x55 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x57 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x58 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x5C ) ); TO_CLIENT.registerPacket( Team.class, @@ -272,7 +274,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x5C ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x5E ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x60 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x63 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x67 ) ); TO_CLIENT.registerPacket( PluginMessage.class, @@ -328,7 +330,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x61 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x63 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x65 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x68 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x6C ) ); TO_CLIENT.registerPacket( ClearTitles.class, @@ -350,7 +352,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x5F ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x61 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x63 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x66 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x6A ) ); TO_CLIENT.registerPacket( TitleTimes.class, @@ -363,7 +365,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x62 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x64 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x66 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x69 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x6D ) ); TO_CLIENT.registerPacket( SystemChat.class, @@ -375,7 +377,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x67 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x69 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x6C ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x6F ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x73 ) ); TO_CLIENT.registerPacket( PlayerListHeaderFooter.class, @@ -398,7 +400,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x68 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x6A ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x6D ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x70 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x74 ) ); TO_CLIENT.registerPacket( EntityStatus.class, @@ -443,7 +445,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_3, 0x1C ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x1F ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x20 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x22 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x22 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x23 ) ); TO_CLIENT.registerPacket( ViewDistance.class, @@ -458,7 +461,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x4F ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x51 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x53 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x55 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x55 ), + map( ProtocolConstants.MINECRAFT_1_21_2, 0x59 ) ); TO_CLIENT.registerPacket( ServerData.class, @@ -470,7 +474,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x47 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x49 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x4B ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x4C ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x50 ) ); TO_CLIENT.registerPacket( PlayerListItemRemove.class, @@ -479,7 +483,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x39 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x3B ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x3D ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x3E ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x3F ) ); TO_CLIENT.registerPacket( PlayerListItemUpdate.class, @@ -488,7 +492,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x3A ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x3C ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x3E ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x3F ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x40 ) ); TO_CLIENT.registerPacket( StartConfiguration.class, @@ -496,7 +500,7 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x65 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x67 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x69 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x6C ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x70 ) ); TO_CLIENT.registerPacket( CookieRequest.class, @@ -507,25 +511,25 @@ public enum Protocol StoreCookie.class, StoreCookie::new, map( ProtocolConstants.MINECRAFT_1_20_5, 0x6B ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x6E ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x72 ) ); TO_CLIENT.registerPacket( Transfer.class, Transfer::new, map( ProtocolConstants.MINECRAFT_1_20_5, 0x73 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x76 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x7A ) ); TO_CLIENT.registerPacket( DisconnectReportDetails.class, DisconnectReportDetails::new, map( ProtocolConstants.MINECRAFT_1_21, 0x7A ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x7D ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x81 ) ); TO_CLIENT.registerPacket( ServerLinks.class, ServerLinks::new, map( ProtocolConstants.MINECRAFT_1_21, 0x7B ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x7E ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x82 ) ); TO_SERVER.registerPacket( 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 172a9a90f1..d0bce3318c 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 @@ -47,7 +47,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; - public static final int MINECRAFT_1_21_2 = 1073742035; + public static final int MINECRAFT_1_21_2 = 1073742039; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; From 2593130b3e74860d46c300bff851b91c25ec0966 Mon Sep 17 00:00:00 2001 From: md_5 Date: Sat, 19 Oct 2024 08:56:06 +1100 Subject: [PATCH 37/58] Minecraft 1.21.2-rc1 support --- .../main/java/net/md_5/bungee/protocol/ProtocolConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d0bce3318c..a460d19e17 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 @@ -47,7 +47,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; - public static final int MINECRAFT_1_21_2 = 1073742039; + public static final int MINECRAFT_1_21_2 = 1073742042; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; From 8212e10c7c4d38294ec6ed970d4d2230cc918c2c Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Mon, 21 Oct 2024 12:05:01 +0200 Subject: [PATCH 38/58] #3756, #3757: Queue PlayerListItemRemove packets for disconnecting players --- .../main/java/net/md_5/bungee/connection/UpstreamBridge.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 5c1cf858d9..c9e3697e7f 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,8 @@ public void disconnected(ChannelWrapper channel) throws Exception { if ( player.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_19_3 ) { - player.unsafe().sendPacket( newPacket ); + // need to queue, because players in config state could receive it + ( (UserConnection) player ).sendPacketQueued( newPacket ); } else { player.unsafe().sendPacket( oldPacket ); From 7338d0f444975f10d5891c59dcbf7f16d771a88e Mon Sep 17 00:00:00 2001 From: md_5 Date: Tue, 22 Oct 2024 07:32:49 +1100 Subject: [PATCH 39/58] Minecraft 1.21.2-rc2 support --- .../main/java/net/md_5/bungee/protocol/ProtocolConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a460d19e17..963526a3ff 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 @@ -47,7 +47,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; - public static final int MINECRAFT_1_21_2 = 1073742042; + public static final int MINECRAFT_1_21_2 = 1073742043; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; From 4886c4be017761333dc67b9c300acc7481354a7d Mon Sep 17 00:00:00 2001 From: md_5 Date: Wed, 23 Oct 2024 02:15:00 +1100 Subject: [PATCH 40/58] Minecraft 1.21.2 support --- .../java/net/md_5/bungee/protocol/ProtocolConstants.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 963526a3ff..86412d488d 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 @@ -47,7 +47,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_3 = 765; public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; - public static final int MINECRAFT_1_21_2 = 1073742043; + public static final int MINECRAFT_1_21_2 = 768; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; @@ -109,13 +109,14 @@ public class ProtocolConstants ProtocolConstants.MINECRAFT_1_20_2, ProtocolConstants.MINECRAFT_1_20_3, ProtocolConstants.MINECRAFT_1_20_5, - ProtocolConstants.MINECRAFT_1_21 + ProtocolConstants.MINECRAFT_1_21, + ProtocolConstants.MINECRAFT_1_21_2 ); if ( SNAPSHOT_SUPPORT ) { // supportedVersions.add( "1.21.x" ); - supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_2 ); + // supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_2 ); } SUPPORTED_VERSIONS = supportedVersions.build(); From 7a42f127161884323f25808f504b9476df02deaa Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Sun, 17 Nov 2024 11:43:31 +1100 Subject: [PATCH 41/58] #3760: Fix possible NPE when trying to get encoder/decoder protocol --- .../net/md_5/bungee/ServerConnection.java | 39 ++++++++++++------ .../java/net/md_5/bungee/UserConnection.java | 40 +++++++++++++------ .../net/md_5/bungee/netty/ChannelWrapper.java | 36 +++++++++++++---- 3 files changed, 81 insertions(+), 34 deletions(-) diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java index b4cc85b23b..54254a9bb6 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java @@ -5,7 +5,6 @@ import java.net.SocketAddress; import java.util.ArrayDeque; import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; import lombok.Data; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -32,7 +31,7 @@ public class ServerConnection implements Server private final boolean forgeServer = false; @Getter private final Queue keepAlives = new ArrayDeque<>(); - private final Queue packetQueue = new ConcurrentLinkedQueue<>(); + private final Queue packetQueue = new ArrayDeque<>(); private final Unsafe unsafe = new Unsafe() { @@ -45,23 +44,37 @@ public void sendPacket(DefinedPacket packet) public void sendPacketQueued(DefinedPacket packet) { - Protocol encodeProtocol = ch.getEncodeProtocol(); - if ( !encodeProtocol.TO_SERVER.hasPacket( packet.getClass(), ch.getEncodeVersion() ) ) + ch.scheduleIfNecessary( () -> { - packetQueue.add( packet ); - } else - { - unsafe().sendPacket( packet ); - } + if ( ch.isClosed() ) + { + return; + } + Protocol encodeProtocol = ch.getEncodeProtocol(); + if ( !encodeProtocol.TO_SERVER.hasPacket( packet.getClass(), ch.getEncodeVersion() ) ) + { + packetQueue.add( packet ); + } else + { + unsafe().sendPacket( packet ); + } + } ); } public void sendQueuedPackets() { - DefinedPacket packet; - while ( ( packet = packetQueue.poll() ) != null ) + ch.scheduleIfNecessary( () -> { - unsafe().sendPacket( packet ); - } + if ( ch.isClosed() ) + { + return; + } + DefinedPacket packet; + while ( ( packet = packetQueue.poll() ) != null ) + { + unsafe().sendPacket( packet ); + } + } ); } @Override 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 2359e86472..8f121aa952 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -11,6 +11,7 @@ import io.netty.util.internal.PlatformDependent; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.ArrayDeque; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -21,7 +22,6 @@ import java.util.Queue; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Level; import lombok.Getter; import lombok.NonNull; @@ -146,7 +146,7 @@ public final class UserConnection implements ProxiedPlayer @Setter private ForgeServerHandler forgeServerHandler; /*========================================================================*/ - private final Queue packetQueue = new ConcurrentLinkedQueue<>(); + private final Queue packetQueue = new ArrayDeque<>(); private final Unsafe unsafe = new Unsafe() { @Override @@ -186,23 +186,37 @@ public void sendPacket(PacketWrapper packet) public void sendPacketQueued(DefinedPacket packet) { - Protocol encodeProtocol = ch.getEncodeProtocol(); - if ( !encodeProtocol.TO_CLIENT.hasPacket( packet.getClass(), getPendingConnection().getVersion() ) ) + ch.scheduleIfNecessary( () -> { - packetQueue.add( packet ); - } else - { - unsafe().sendPacket( packet ); - } + if ( ch.isClosed() ) + { + return; + } + Protocol encodeProtocol = ch.getEncodeProtocol(); + if ( !encodeProtocol.TO_CLIENT.hasPacket( packet.getClass(), getPendingConnection().getVersion() ) ) + { + packetQueue.add( packet ); + } else + { + unsafe().sendPacket( packet ); + } + } ); } public void sendQueuedPackets() { - DefinedPacket packet; - while ( ( packet = packetQueue.poll() ) != null ) + ch.scheduleIfNecessary( () -> { - unsafe().sendPacket( packet ); - } + if ( ch.isClosed() ) + { + return; + } + DefinedPacket packet; + while ( ( packet = packetQueue.poll() ) != null ) + { + unsafe().sendPacket( packet ); + } + } ); } @Deprecated 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 682bb80734..a15ba5dfb7 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 @@ -42,23 +42,22 @@ public ChannelWrapper(ChannelHandlerContext ctx) public Protocol getDecodeProtocol() { - return ch.pipeline().get( MinecraftDecoder.class ).getProtocol(); + return getMinecraftDecoder().getProtocol(); } public void setDecodeProtocol(Protocol protocol) { - ch.pipeline().get( MinecraftDecoder.class ).setProtocol( protocol ); + getMinecraftDecoder().setProtocol( protocol ); } public Protocol getEncodeProtocol() { - return ch.pipeline().get( MinecraftEncoder.class ).getProtocol(); - + return getMinecraftEncoder().getProtocol(); } public void setEncodeProtocol(Protocol protocol) { - ch.pipeline().get( MinecraftEncoder.class ).setProtocol( protocol ); + getMinecraftEncoder().setProtocol( protocol ); } public void setProtocol(Protocol protocol) @@ -69,13 +68,23 @@ public void setProtocol(Protocol protocol) public void setVersion(int protocol) { - ch.pipeline().get( MinecraftDecoder.class ).setProtocolVersion( protocol ); - ch.pipeline().get( MinecraftEncoder.class ).setProtocolVersion( protocol ); + getMinecraftDecoder().setProtocolVersion( protocol ); + getMinecraftEncoder().setProtocolVersion( protocol ); + } + + public MinecraftDecoder getMinecraftDecoder() + { + return ch.pipeline().get( MinecraftDecoder.class ); + } + + public MinecraftEncoder getMinecraftEncoder() + { + return ch.pipeline().get( MinecraftEncoder.class ); } public int getEncodeVersion() { - return ch.pipeline().get( MinecraftEncoder.class ).getProtocolVersion(); + return getMinecraftEncoder().getProtocolVersion(); } public void write(Object packet) @@ -223,4 +232,15 @@ public void updateComposite() packetCompressor.setCompose( compressorCompose ); } } + + public void scheduleIfNecessary(Runnable task) + { + if ( ch.eventLoop().inEventLoop() ) + { + task.run(); + return; + } + + ch.eventLoop().execute( task ); + } } From 81b118a8ba1598882d712a9b878dbd2f649a3b28 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Sun, 17 Nov 2024 11:44:17 +1100 Subject: [PATCH 42/58] #3759: Remove unnecessary protocol version check for UnsignedClientCommand --- .../net/md_5/bungee/protocol/packet/UnsignedClientCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/UnsignedClientCommand.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/UnsignedClientCommand.java index 5ee93eaf27..750eb18039 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/UnsignedClientCommand.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/UnsignedClientCommand.java @@ -21,7 +21,7 @@ public class UnsignedClientCommand extends DefinedPacket @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - command = readString( buf, ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 ) ? 32767 : 256 ); + command = readString( buf ); } @Override From f6b40b1186b8bf892158060d1f6cabd0502c1340 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Tue, 19 Nov 2024 10:20:17 +0100 Subject: [PATCH 43/58] #3758: Handle LoginPayloadResponse in UpstreamBridge --- .../java/net/md_5/bungee/connection/InitialHandler.java | 2 +- .../java/net/md_5/bungee/connection/UpstreamBridge.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) 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 224f231b63..2d365c39de 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 @@ -904,7 +904,7 @@ public CompletableFuture retrieveCookie(String cookie) public CompletableFuture sendData(String channel, byte[] data) { Preconditions.checkState( getVersion() >= ProtocolConstants.MINECRAFT_1_13, "LoginPayloads are only supported in 1.13 and above" ); - Preconditions.checkState( loginRequest != null, "Cannot send login data for status or legacy connections" ); + Preconditions.checkState( ch.getEncodeProtocol() == Protocol.LOGIN, "LoginPayloads are only supported in the login phase" ); CompletableFuture future = new CompletableFuture<>(); final int id; 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 c9e3697e7f..49967de9dd 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 @@ -36,6 +36,7 @@ import net.md_5.bungee.protocol.packet.FinishConfiguration; import net.md_5.bungee.protocol.packet.KeepAlive; import net.md_5.bungee.protocol.packet.LoginAcknowledged; +import net.md_5.bungee.protocol.packet.LoginPayloadResponse; import net.md_5.bungee.protocol.packet.PlayerListItem; import net.md_5.bungee.protocol.packet.PlayerListItemRemove; import net.md_5.bungee.protocol.packet.PluginMessage; @@ -378,6 +379,12 @@ public void handle(CookieResponse cookieResponse) throws Exception con.getPendingConnection().handle( cookieResponse ); } + @Override + public void handle(LoginPayloadResponse loginPayloadResponse) throws Exception + { + con.getPendingConnection().handle( loginPayloadResponse ); + } + @Override public String toString() { From 373dab05ad456bdb72b3d70e533ef7c281453c78 Mon Sep 17 00:00:00 2001 From: md_5 Date: Sat, 23 Nov 2024 12:27:38 +1100 Subject: [PATCH 44/58] Minecraft 1.21.4-pre1 support --- .../md_5/bungee/api/chat/BaseComponent.java | 50 +++++++++++++++++++ .../md_5/bungee/api/chat/ComponentStyle.java | 30 ++++++++++- .../api/chat/ComponentStyleBuilder.java | 16 +++++- .../bungee/chat/ComponentStyleSerializer.java | 17 +++++++ .../md_5/bungee/protocol/DefinedPacket.java | 4 +- .../bungee/protocol/ProtocolConstants.java | 3 +- .../protocol/packet/PlayerListItem.java | 2 + .../protocol/packet/PlayerListItemUpdate.java | 9 +++- .../net/md_5/bungee/entitymap/EntityMap.java | 2 + .../bungee/entitymap/EntityMap_1_16_2.java | 1 + 10 files changed, 127 insertions(+), 7 deletions(-) diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index 558343c338..005c7c49f5 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -1,5 +1,6 @@ package net.md_5.bungee.api.chat; +import java.awt.Color; import java.util.ArrayList; import java.util.List; import lombok.AccessLevel; @@ -129,6 +130,10 @@ public void copyFormatting(BaseComponent component, FormatRetention retention, b { setColor( component.getColorRaw() ); } + if ( replace || !style.hasShadowColor() ) + { + setShadowColor( component.getShadowColorRaw() ); + } if ( replace || !style.hasFont() ) { setFont( component.getFontRaw() ); @@ -175,6 +180,7 @@ public void retain(FormatRetention retention) if ( retention == FormatRetention.EVENTS || retention == FormatRetention.NONE ) { setColor( null ); + setShadowColor( null ); setBold( null ); setItalic( null ); setUnderlined( null ); @@ -295,6 +301,46 @@ public ChatColor getColorRaw() return style.getColor(); } + /** + * Set this component's shadow color. + * + * @param color the component shadow color, or null to use the default + */ + public void setShadowColor(Color color) + { + this.style.setShadowColor( color ); + } + + /** + * Returns the shadow color of this component. This uses the parent's shadow color if this + * component doesn't have one. null is returned if no shadow color is found. + * + * @return the shadow color of this component + */ + public Color getShadowColor() + { + if ( !style.hasShadowColor() ) + { + if ( parent == null ) + { + return null; + } + return parent.getShadowColor(); + } + return style.getShadowColor(); + } + + /** + * Returns the shadow color of this component without checking the parents + * shadow color. May return null + * + * @return the shadow color of this component + */ + public Color getShadowColorRaw() + { + return style.getShadowColor(); + } + /** * Set this component's font. * @@ -536,6 +582,10 @@ public void applyStyle(ComponentStyle style) { setColor( style.getColor() ); } + if ( style.hasShadowColor() ) + { + setShadowColor( style.getShadowColor() ); + } if ( style.hasFont() ) { setFont( style.getFont() ); diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyle.java b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyle.java index 15f44ea730..463a55976f 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyle.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyle.java @@ -1,5 +1,6 @@ package net.md_5.bungee.api.chat; +import java.awt.Color; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -23,6 +24,10 @@ public final class ComponentStyle implements Cloneable * {@link ChatColor#color} should not be null). */ private ChatColor color; + /** + * The shadow color of this style. + */ + private Color shadowColor; /** * The font of this style. */ @@ -68,6 +73,26 @@ public boolean hasColor() return ( color != null ); } + /** + * Returns the shadow color of this style. May return null. + * + * @return the shadow color of this style, or null if default color + */ + public Color getShadowColor() + { + return shadowColor; + } + + /** + * Returns whether or not this style has a shadow color set. + * + * @return whether a shadow color is set + */ + public boolean hasShadowColor() + { + return ( shadowColor != null ); + } + /** * Returns the font of this style. May return null. * @@ -195,7 +220,7 @@ public Boolean isObfuscatedRaw() */ public boolean isEmpty() { - return color == null && font == null && bold == null + return color == null && shadowColor == null && font == null && bold == null && italic == null && underlined == null && strikethrough == null && obfuscated == null; } @@ -203,7 +228,7 @@ public boolean isEmpty() @Override public ComponentStyle clone() { - return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated ); + return new ComponentStyle( color, shadowColor, font, bold, italic, underlined, strikethrough, obfuscated ); } /** @@ -227,6 +252,7 @@ public static ComponentStyleBuilder builder(ComponentStyle other) { return new ComponentStyleBuilder() .color( other.color ) + .shadowColor( other.shadowColor ) .font( other.font ) .bold( other.bold ) .italic( other.italic ) diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyleBuilder.java b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyleBuilder.java index 6481ae81ec..e8ca223587 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyleBuilder.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyleBuilder.java @@ -1,5 +1,6 @@ package net.md_5.bungee.api.chat; +import java.awt.Color; import net.md_5.bungee.api.ChatColor; /** @@ -26,6 +27,7 @@ public final class ComponentStyleBuilder { private ChatColor color; + private Color shadowColor; private String font; private Boolean bold, italic, underlined, strikethrough, obfuscated; @@ -41,6 +43,18 @@ public ComponentStyleBuilder color(ChatColor color) return this; } + /** + * Set the style shadow color. + * + * @param shadowColor the shadow color to set, or null to use the default + * @return this ComponentStyleBuilder for chaining + */ + public ComponentStyleBuilder shadowColor(Color shadowColor) + { + this.shadowColor = shadowColor; + return this; + } + /** * Set the style font. * @@ -121,6 +135,6 @@ public ComponentStyleBuilder obfuscated(Boolean obfuscated) */ public ComponentStyle build() { - return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated ); + return new ComponentStyle( color, shadowColor, font, bold, italic, underlined, strikethrough, obfuscated ); } } diff --git a/chat/src/main/java/net/md_5/bungee/chat/ComponentStyleSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/ComponentStyleSerializer.java index 84e63727e0..c3ef577558 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/ComponentStyleSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/ComponentStyleSerializer.java @@ -1,5 +1,6 @@ package net.md_5.bungee.chat; +import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -8,6 +9,7 @@ import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; +import java.awt.Color; import java.lang.reflect.Type; import java.util.Map; import net.md_5.bungee.api.ChatColor; @@ -67,6 +69,10 @@ static void serializeTo(ComponentStyle style, JsonObject object) { object.addProperty( "color", style.getColor().getName() ); } + if ( style.hasShadowColor() ) + { + object.addProperty( "shadow_color", style.getShadowColor().getRGB() ); + } if ( style.hasFont() ) { object.addProperty( "font", style.getFont() ); @@ -102,6 +108,17 @@ public ComponentStyle deserialize(JsonElement json, Type typeOfT, JsonDeserializ case "color": builder.color( ChatColor.of( value.getAsString() ) ); break; + case "shadow_color": + if ( value.isJsonArray() ) + { + JsonArray array = value.getAsJsonArray(); + + builder.shadowColor( new Color( array.get( 0 ).getAsFloat(), array.get( 1 ).getAsFloat(), array.get( 2 ).getAsFloat(), array.get( 3 ).getAsFloat() ) ); + } else if ( value.isJsonPrimitive() ) + { + builder.shadowColor( new Color( value.getAsNumber().intValue(), true ) ); + } + break; case "font": builder.font( value.getAsString() ); break; 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 6cd9e6a425..f3f690c4ef 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 @@ -499,7 +499,7 @@ public static > EnumSet readEnumSet(Class oclass, ByteBu public static BitSet readFixedBitSet(int i, ByteBuf buf) { - byte[] bits = new byte[ ( i + 8 ) >> 3 ]; + byte[] bits = new byte[ ( i + 7 ) >> 3 ]; buf.readBytes( bits ); return BitSet.valueOf( bits ); @@ -511,7 +511,7 @@ public static void writeFixedBitSet(BitSet bits, int size, ByteBuf buf) { throw new OverflowPacketException( "BitSet too large (expected " + size + " got " + bits.size() + ")" ); } - buf.writeBytes( Arrays.copyOf( bits.toByteArray(), ( size + 8 ) >> 3 ) ); + buf.writeBytes( Arrays.copyOf( bits.toByteArray(), ( size + 7 ) >> 3 ) ); } public void read(ByteBuf buf) 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 86412d488d..93963ca39b 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 @@ -48,6 +48,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; public static final int MINECRAFT_1_21_2 = 768; + public static final int MINECRAFT_1_21_4 = 1073742047; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; @@ -116,7 +117,7 @@ public class ProtocolConstants if ( SNAPSHOT_SUPPORT ) { // supportedVersions.add( "1.21.x" ); - // supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_2 ); + supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_4 ); } SUPPORTED_VERSIONS = supportedVersions.build(); 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 60f7305b0d..67a54ecca4 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 @@ -148,5 +148,7 @@ public static class Item // UPDATE_LIST_ORDER 1.21.2 Integer listOrder; + // UPDATE_HAT 1.21.4 + Boolean showHat; } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItemUpdate.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItemUpdate.java index a45b001e87..40492bd669 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItemUpdate.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItemUpdate.java @@ -64,6 +64,9 @@ public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protoco case UPDATE_LIST_ORDER: item.listOrder = DefinedPacket.readVarInt( buf ); break; + case UPDATE_HAT: + item.showHat = buf.readBoolean(); + break; } } } @@ -115,6 +118,9 @@ public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protoc case UPDATE_LIST_ORDER: DefinedPacket.writeVarInt( item.listOrder, buf ); break; + case UPDATE_HAT: + buf.writeBoolean( item.showHat ); + break; } } } @@ -135,6 +141,7 @@ public static enum Action UPDATE_LISTED, UPDATE_LATENCY, UPDATE_DISPLAY_NAME, - UPDATE_LIST_ORDER; + UPDATE_LIST_ORDER, + UPDATE_HAT; } } 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 755b8fd6ab..206bc8d2fa 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 @@ -91,6 +91,8 @@ public static EntityMap getEntityMap(int version) return EntityMap_1_16_2.INSTANCE_1_20_5; case ProtocolConstants.MINECRAFT_1_21_2: return EntityMap_1_16_2.INSTANCE_1_21_2; + case ProtocolConstants.MINECRAFT_1_21_4: + return EntityMap_1_16_2.INSTANCE_1_21_4; } throw new RuntimeException( "Version " + version + " has no entity map" ); } 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 index a71932a74a..66afd2f190 100644 --- 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 @@ -24,6 +24,7 @@ class EntityMap_1_16_2 extends EntityMap static final EntityMap_1_16_2 INSTANCE_1_20_3 = new EntityMap_1_16_2( -1, 0x34 ); static final EntityMap_1_16_2 INSTANCE_1_20_5 = new EntityMap_1_16_2( -1, 0x37 ); static final EntityMap_1_16_2 INSTANCE_1_21_2 = new EntityMap_1_16_2( -1, 0x39 ); + static final EntityMap_1_16_2 INSTANCE_1_21_4 = new EntityMap_1_16_2( -1, 0x3B ); // private final int spawnPlayerId; private final int spectateId; From b376f61578655c58340e6e99b874dbf110649dc2 Mon Sep 17 00:00:00 2001 From: md_5 Date: Tue, 26 Nov 2024 20:38:03 +1100 Subject: [PATCH 45/58] Minecraft 1.21.4-pre2 support --- .../main/java/net/md_5/bungee/protocol/ProtocolConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 93963ca39b..53a6d722b6 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 @@ -48,7 +48,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; public static final int MINECRAFT_1_21_2 = 768; - public static final int MINECRAFT_1_21_4 = 1073742047; + public static final int MINECRAFT_1_21_4 = 1073742048; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; From 20a71b06a9ef09544b8f794418cdae006d47b3d8 Mon Sep 17 00:00:00 2001 From: md_5 Date: Sat, 30 Nov 2024 10:11:33 +1100 Subject: [PATCH 46/58] Minecraft 1.21.4-rc3 support --- .../main/java/net/md_5/bungee/protocol/ProtocolConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 53a6d722b6..50ea3d5556 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 @@ -48,7 +48,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; public static final int MINECRAFT_1_21_2 = 768; - public static final int MINECRAFT_1_21_4 = 1073742048; + public static final int MINECRAFT_1_21_4 = 1073742052; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; From 8a80435e647c358b0c2235549fabbda4627b532a Mon Sep 17 00:00:00 2001 From: md_5 Date: Wed, 4 Dec 2024 03:20:00 +1100 Subject: [PATCH 47/58] Minecraft 1.21.4 support --- chat/pom.xml | 2 +- config/pom.xml | 2 +- pom.xml | 4 ++-- .../java/net/md_5/bungee/protocol/ProtocolConstants.java | 7 ++++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/chat/pom.xml b/chat/pom.xml index 71c4ce2797..9a9bc5069e 100644 --- a/chat/pom.xml +++ b/chat/pom.xml @@ -22,7 +22,7 @@ com.google.code.gson gson - 2.10.1 + 2.11.0 compile diff --git a/config/pom.xml b/config/pom.xml index 4b94f11267..58f0b7cad3 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -22,7 +22,7 @@ com.google.code.gson gson - 2.10.1 + 2.11.0 compile true diff --git a/pom.xml b/pom.xml index 7335e8b798..7a41e46424 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ io.netty netty-bom - 4.1.110.Final + 4.1.115.Final pom import @@ -99,7 +99,7 @@ com.google.guava guava - 32.1.2-jre + 33.3.1-jre compile 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 50ea3d5556..b3d1c2bbaf 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 @@ -48,7 +48,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_20_5 = 766; public static final int MINECRAFT_1_21 = 767; public static final int MINECRAFT_1_21_2 = 768; - public static final int MINECRAFT_1_21_4 = 1073742052; + public static final int MINECRAFT_1_21_4 = 769; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; @@ -111,13 +111,14 @@ public class ProtocolConstants ProtocolConstants.MINECRAFT_1_20_3, ProtocolConstants.MINECRAFT_1_20_5, ProtocolConstants.MINECRAFT_1_21, - ProtocolConstants.MINECRAFT_1_21_2 + ProtocolConstants.MINECRAFT_1_21_2, + ProtocolConstants.MINECRAFT_1_21_4 ); if ( SNAPSHOT_SUPPORT ) { // supportedVersions.add( "1.21.x" ); - supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_4 ); + // supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_4 ); } SUPPORTED_VERSIONS = supportedVersions.build(); From 7340f1a0350bc0c9a186722ac1ffb2dc15cd3143 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Sat, 7 Dec 2024 21:50:03 +0100 Subject: [PATCH 48/58] #3765: Fix forgotten boolean write --- .../net/md_5/bungee/protocol/packet/EncryptionResponse.java | 4 ++++ 1 file changed, 4 insertions(+) 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 63e9d18da2..1b9116996c 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 @@ -45,6 +45,10 @@ public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protoc writeArray( verifyToken, buf ); } else { + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 && protocolVersion <= ProtocolConstants.MINECRAFT_1_19_3 ) + { + buf.writeBoolean( false ); + } buf.writeLong( encryptionData.getSalt() ); writeArray( encryptionData.getSignature(), buf ); } From 15bd33b25b14b59e41799f6429c05e1231170fbc Mon Sep 17 00:00:00 2001 From: Janmm14 Date: Fri, 13 Dec 2024 22:46:35 +0000 Subject: [PATCH 49/58] #3767: Remove explicit direct buffer check --- .../src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java | 3 --- native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java | 3 --- 2 files changed, 6 deletions(-) diff --git a/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java b/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java index 614b8b2992..0b7861c000 100644 --- a/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java +++ b/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java @@ -37,9 +37,6 @@ public void free() @Override public void cipher(ByteBuf in, ByteBuf out) throws GeneralSecurityException { - // Smoke tests - in.memoryAddress(); - out.memoryAddress(); Preconditions.checkState( ctx != 0, "Invalid pointer to AES key!" ); // Store how many bytes we can cipher diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java index bad84bc444..4dc558d53e 100644 --- a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java @@ -48,9 +48,6 @@ public void free() @Override public void process(ByteBuf in, ByteBuf out) throws DataFormatException { - // Smoke tests - in.memoryAddress(); - out.memoryAddress(); Preconditions.checkState( ctx != 0, "Invalid pointer to compress!" ); while ( !nativeCompress.finished && ( compress || in.isReadable() ) ) From d99570214aca18a7eb0f6682a36d8992da65b57a Mon Sep 17 00:00:00 2001 From: md_5 Date: Wed, 1 Jan 2025 10:19:29 +1100 Subject: [PATCH 50/58] Update date in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68e9ffa26e..6ef76cd892 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,4 @@ Binaries -------- Precompiled binaries are available for end users on [Jenkins](https://www.spigotmc.org/go/bungeecord-dl). -(c) 2012-2024 SpigotMC Pty. Ltd. +(c) 2012-2025 SpigotMC Pty. Ltd. From 1265a9927b4593748dd108d7132768bd02506110 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Tue, 7 Jan 2025 20:46:11 +1100 Subject: [PATCH 51/58] #3769: Fix possible NoSuchElementException changing compression --- .../net/md_5/bungee/netty/ChannelWrapper.java | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) 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 a15ba5dfb7..a7627d4016 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 @@ -180,26 +180,32 @@ public Channel getHandle() public void setCompressionThreshold(int compressionThreshold) { - if ( ch.pipeline().get( PacketCompressor.class ) == null && compressionThreshold >= 0 ) - { - addBefore( PipelineUtils.PACKET_ENCODER, "compress", new PacketCompressor() ); - } + PacketCompressor compressor = ch.pipeline().get( PacketCompressor.class ); + PacketDecompressor decompressor = ch.pipeline().get( PacketDecompressor.class ); if ( compressionThreshold >= 0 ) { - ch.pipeline().get( PacketCompressor.class ).setThreshold( compressionThreshold ); + if ( compressor == null ) + { + addBefore( PipelineUtils.PACKET_ENCODER, "compress", compressor = new PacketCompressor() ); + } + compressor.setThreshold( compressionThreshold ); + + if ( decompressor == null ) + { + addBefore( PipelineUtils.PACKET_DECODER, "decompress", decompressor = new PacketDecompressor() ); + } } else { - ch.pipeline().remove( "compress" ); + if ( compressor != null ) + { + ch.pipeline().remove( "compress" ); + } + if ( decompressor != null ) + { + ch.pipeline().remove( "decompress" ); + } } - if ( ch.pipeline().get( PacketDecompressor.class ) == null && compressionThreshold >= 0 ) - { - addBefore( PipelineUtils.PACKET_DECODER, "decompress", new PacketDecompressor() ); - } - if ( compressionThreshold < 0 ) - { - ch.pipeline().remove( "decompress" ); - } // disable use of composite buffers if we use natives updateComposite(); } From 0aa2871b261681dc53c71ae3ed8f177ce97fe6e1 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Wed, 22 Jan 2025 21:11:11 +0100 Subject: [PATCH 52/58] #3761: Whitelist LoginPayloadResponse in UpstreamBridge#shouldHandle Required for #3758 to function correctly. --- .../main/java/net/md_5/bungee/connection/InitialHandler.java | 3 +++ .../main/java/net/md_5/bungee/connection/UpstreamBridge.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) 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 2d365c39de..472714b325 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 @@ -704,6 +704,9 @@ public void handle(LoginPayloadResponse response) throws Exception } Preconditions.checkState( future != null, "Unexpected custom LoginPayloadResponse" ); future.complete( response.getData() ); + + // we should never pass this to the backend + throw CancelSendSignal.INSTANCE; } @Override 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 49967de9dd..e508ad91e4 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 @@ -132,7 +132,7 @@ public void writabilityChanged(ChannelWrapper channel) throws Exception @Override public boolean shouldHandle(PacketWrapper packet) throws Exception { - return con.getServer() != null || packet.packet instanceof PluginMessage || packet.packet instanceof CookieResponse; + return con.getServer() != null || packet.packet instanceof PluginMessage || packet.packet instanceof CookieResponse || packet.packet instanceof LoginPayloadResponse; } @Override From 60a3bf082f9fa0a09a2f4181e3abe9af0d341b5a Mon Sep 17 00:00:00 2001 From: md_5 Date: Mon, 27 Jan 2025 20:08:58 +1100 Subject: [PATCH 53/58] Preallocate compression output buffer to remove unnecessary resizing --- .../main/java/net/md_5/bungee/jni/zlib/NativeZlib.java | 8 +++++++- native/src/test/java/net/md_5/bungee/NativeZlibTest.java | 2 +- .../java/net/md_5/bungee/compress/PacketDecompressor.java | 8 +++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java index 4dc558d53e..6fa9c6fd3b 100644 --- a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java @@ -52,7 +52,13 @@ public void process(ByteBuf in, ByteBuf out) throws DataFormatException while ( !nativeCompress.finished && ( compress || in.isReadable() ) ) { - out.ensureWritable( OUTPUT_BUFFER_SIZE ); + if ( compress ) + { + out.ensureWritable( OUTPUT_BUFFER_SIZE ); + } else + { + Preconditions.checkArgument( out.isWritable(), "Output buffer overrun" ); + } int processed; try diff --git a/native/src/test/java/net/md_5/bungee/NativeZlibTest.java b/native/src/test/java/net/md_5/bungee/NativeZlibTest.java index 112a3b83be..b40484a269 100644 --- a/native/src/test/java/net/md_5/bungee/NativeZlibTest.java +++ b/native/src/test/java/net/md_5/bungee/NativeZlibTest.java @@ -64,7 +64,7 @@ private void test(BungeeZlib zlib) throws DataFormatException zlib.process( originalBuf, compressed ); - ByteBuf uncompressed = Unpooled.directBuffer(); + ByteBuf uncompressed = Unpooled.directBuffer( dataBuf.length, dataBuf.length ); zlib.init( false, 0 ); zlib.process( compressed, uncompressed ); 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 445ee947ad..8e89d4b651 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 @@ -7,10 +7,12 @@ import java.util.List; import net.md_5.bungee.jni.zlib.BungeeZlib; import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.OverflowPacketException; public class PacketDecompressor extends MessageToMessageDecoder { + private static final int MAX_DECOMPRESSED_LEN = 1 << 23; private final BungeeZlib zlib = CompressFactory.zlib.newInstance(); @Override @@ -34,8 +36,12 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) t out.add( in.retain() ); } else { - ByteBuf decompressed = ctx.alloc().directBuffer(); + if ( size > MAX_DECOMPRESSED_LEN ) + { + throw new OverflowPacketException( "Packet may not be larger than " + MAX_DECOMPRESSED_LEN + " bytes" ); + } + ByteBuf decompressed = ctx.alloc().directBuffer( size, size ); try { zlib.process( in, decompressed ); From 6b22690971515f5ab715b40993d083881c1b5c02 Mon Sep 17 00:00:00 2001 From: md_5 Date: Mon, 27 Jan 2025 20:09:01 +1100 Subject: [PATCH 54/58] Minecraft 25w04a protocol support --- .../net/md_5/bungee/protocol/Protocol.java | 85 +++++++++++++------ .../bungee/protocol/ProtocolConstants.java | 3 +- .../md_5/bungee/protocol/packet/Commands.java | 65 +++++++++++++- .../net/md_5/bungee/entitymap/EntityMap.java | 2 + .../bungee/entitymap/EntityMap_1_16_2.java | 1 + 5 files changed, 126 insertions(+), 30 deletions(-) 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 8aa57ed4fb..15bafe8395 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 @@ -99,7 +99,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x23 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x24 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x26 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x27 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x27 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x26 ) ); TO_CLIENT.registerPacket( Login.class, @@ -117,7 +118,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x28 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x29 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x2B ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x2C ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x2C ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x2B ) ); TO_CLIENT.registerPacket( Chat.class, Chat::new, @@ -149,7 +151,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x43 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x45 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x47 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x4C ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x4C ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x4B ) ); TO_CLIENT.registerPacket( BossBar.class, @@ -160,7 +163,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_17, 0x0D ), map( ProtocolConstants.MINECRAFT_1_19, 0x0A ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x0B ), - map( ProtocolConstants.MINECRAFT_1_20_2, 0x0A ) + map( ProtocolConstants.MINECRAFT_1_20_2, 0x0A ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x09 ) ); TO_CLIENT.registerPacket( PlayerListItem.class, // PlayerInfo @@ -191,7 +195,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19, 0x0E ), map( ProtocolConstants.MINECRAFT_1_19_3, 0x0D ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x0F ), - map( ProtocolConstants.MINECRAFT_1_20_2, 0x10 ) + map( ProtocolConstants.MINECRAFT_1_20_2, 0x10 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x0F ) ); TO_CLIENT.registerPacket( ScoreboardObjective.class, @@ -210,7 +215,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x5A ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x5C ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x5E ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x64 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x64 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x63 ) ); TO_CLIENT.registerPacket( ScoreboardScore.class, @@ -229,14 +235,16 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x5D ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x5F ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x61 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x68 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x68 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x67 ) ); TO_CLIENT.registerPacket( ScoreboardScoreReset.class, ScoreboardScoreReset::new, map( ProtocolConstants.MINECRAFT_1_20_3, 0x42 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x44 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x49 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x49 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x48 ) ); TO_CLIENT.registerPacket( ScoreboardDisplay.class, @@ -255,7 +263,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x53 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x55 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x57 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x5C ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x5C ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x5B ) ); TO_CLIENT.registerPacket( Team.class, @@ -274,7 +283,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x5C ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x5E ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x60 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x67 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x67 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x66 ) ); TO_CLIENT.registerPacket( PluginMessage.class, @@ -292,7 +302,9 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_3, 0x15 ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x17 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x18 ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x19 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x19 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x2B ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x18 ) ); TO_CLIENT.registerPacket( Kick.class, @@ -310,7 +322,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_3, 0x17 ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x1A ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x1B ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x1D ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x1D ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x1C ) ); TO_CLIENT.registerPacket( Title.class, @@ -330,7 +343,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x61 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x63 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x65 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x6C ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x6C ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x6B ) ); TO_CLIENT.registerPacket( ClearTitles.class, @@ -339,7 +353,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19, 0x0D ), map( ProtocolConstants.MINECRAFT_1_19_3, 0x0C ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x0E ), - map( ProtocolConstants.MINECRAFT_1_20_2, 0x0F ) + map( ProtocolConstants.MINECRAFT_1_20_2, 0x0F ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x0E ) ); TO_CLIENT.registerPacket( Subtitle.class, @@ -352,7 +367,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x5F ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x61 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x63 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x6A ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x6A ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x69 ) ); TO_CLIENT.registerPacket( TitleTimes.class, @@ -365,7 +381,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x62 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x64 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x66 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x6D ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x6D ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x6C ) ); TO_CLIENT.registerPacket( SystemChat.class, @@ -377,7 +394,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x67 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x69 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x6C ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x73 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x73 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x72 ) ); TO_CLIENT.registerPacket( PlayerListHeaderFooter.class, @@ -400,7 +418,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x68 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x6A ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x6D ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x74 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x74 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x73 ) ); TO_CLIENT.registerPacket( EntityStatus.class, @@ -418,7 +437,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_3, 0x19 ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x1C ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x1D ), - map( ProtocolConstants.MINECRAFT_1_20_5, 0x1F ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x1F ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x1E ) ); TO_CLIENT.registerPacket( Commands.class, @@ -431,7 +451,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19, 0x0F ), map( ProtocolConstants.MINECRAFT_1_19_3, 0x0E ), map( ProtocolConstants.MINECRAFT_1_19_4, 0x10 ), - map( ProtocolConstants.MINECRAFT_1_20_2, 0x11 ) + map( ProtocolConstants.MINECRAFT_1_20_2, 0x11 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x10 ) ); TO_CLIENT.registerPacket( GameState.class, @@ -446,7 +467,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x1F ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x20 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x22 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x23 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x23 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x22 ) ); TO_CLIENT.registerPacket( ViewDistance.class, @@ -462,7 +484,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x51 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x53 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x55 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x59 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x59 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x58 ) ); TO_CLIENT.registerPacket( ServerData.class, @@ -474,7 +497,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x47 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x49 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x4B ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x50 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x50 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x4F ) ); TO_CLIENT.registerPacket( PlayerListItemRemove.class, @@ -483,7 +507,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x39 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x3B ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x3D ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x3F ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x3F ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x3E ) ); TO_CLIENT.registerPacket( PlayerListItemUpdate.class, @@ -492,7 +517,8 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x3A ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x3C ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x3E ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x40 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x40 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x3F ) ); TO_CLIENT.registerPacket( StartConfiguration.class, @@ -500,18 +526,21 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_20_2, 0x65 ), map( ProtocolConstants.MINECRAFT_1_20_3, 0x67 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x69 ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x70 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x70 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x6F ) ); TO_CLIENT.registerPacket( CookieRequest.class, CookieRequest::new, - map( ProtocolConstants.MINECRAFT_1_20_5, 0x16 ) + map( ProtocolConstants.MINECRAFT_1_20_5, 0x16 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x15 ) ); TO_CLIENT.registerPacket( StoreCookie.class, StoreCookie::new, map( ProtocolConstants.MINECRAFT_1_20_5, 0x6B ), - map( ProtocolConstants.MINECRAFT_1_21_2, 0x72 ) + map( ProtocolConstants.MINECRAFT_1_21_2, 0x72 ), + map( ProtocolConstants.MINECRAFT_1_21_5, 0x71 ) ); TO_CLIENT.registerPacket( Transfer.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 b3d1c2bbaf..6e8af8f3cc 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 @@ -49,6 +49,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_21 = 767; public static final int MINECRAFT_1_21_2 = 768; public static final int MINECRAFT_1_21_4 = 769; + public static final int MINECRAFT_1_21_5 = 1073742055; public static final List SUPPORTED_VERSIONS; public static final List SUPPORTED_VERSION_IDS; @@ -118,7 +119,7 @@ public class ProtocolConstants if ( SNAPSHOT_SUPPORT ) { // supportedVersions.add( "1.21.x" ); - // supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_4 ); + supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_5 ); } SUPPORTED_VERSIONS = supportedVersions.build(); diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Commands.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Commands.java index 6523ed30da..70b30aea36 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Commands.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Commands.java @@ -312,6 +312,7 @@ private static class ArgumentRegistry private static final ArgumentSerializer[] IDS_1_19_4; private static final ArgumentSerializer[] IDS_1_20_3; private static final ArgumentSerializer[] IDS_1_20_5; + private static final ArgumentSerializer[] IDS_1_21_5; private static final Map, ProperArgumentSerializer> PROPER_PROVIDERS = new HashMap<>(); // private static final ArgumentSerializer VOID = new ArgumentSerializer() @@ -858,6 +859,65 @@ protected void write(ByteBuf buf, String t) get( "minecraft:loot_predicate", VOID ), get( "minecraft:loot_modifier", VOID ) }; + + IDS_1_21_5 = new ArgumentSerializer[] + { + get( "brigadier:bool", VOID ), + get( "brigadier:float", FLOAT_RANGE ), + get( "brigadier:double", DOUBLE_RANGE ), + get( "brigadier:integer", INTEGER_RANGE ), + get( "brigadier:long", LONG_RANGE ), + get( "brigadier:string", STRING ), + get( "minecraft:entity", BYTE ), + get( "minecraft:game_profile", VOID ), + get( "minecraft:block_pos", VOID ), + get( "minecraft:column_pos", VOID ), + get( "minecraft:vec3", VOID ), + get( "minecraft:vec2", VOID ), + get( "minecraft:block_state", VOID ), + get( "minecraft:block_predicate", VOID ), + get( "minecraft:item_stack", VOID ), + get( "minecraft:item_predicate", VOID ), + get( "minecraft:color", VOID ), + get( "minecraft:component", VOID ), + get( "minecraft:style", VOID ), + get( "minecraft:message", VOID ), + get( "minecraft:nbt_compound_tag", VOID ), + get( "minecraft:nbt_tag", VOID ), + get( "minecraft:nbt_path", VOID ), + get( "minecraft:objective", VOID ), + get( "minecraft:objective_criteria", VOID ), + get( "minecraft:operation", VOID ), + get( "minecraft:particle", VOID ), + get( "minecraft:angle", VOID ), + get( "minecraft:rotation", VOID ), + get( "minecraft:scoreboard_slot", VOID ), + get( "minecraft:score_holder", BYTE ), + get( "minecraft:swizzle", VOID ), + get( "minecraft:team", VOID ), + get( "minecraft:item_slot", VOID ), + get( "minecraft:item_slots", VOID ), + get( "minecraft:resource_location", VOID ), + get( "minecraft:function", VOID ), + get( "minecraft:entity_anchor", VOID ), + get( "minecraft:int_range", VOID ), + get( "minecraft:float_range", VOID ), + get( "minecraft:dimension", VOID ), + get( "minecraft:gamemode", VOID ), + get( "minecraft:time", INTEGER ), + get( "minecraft:resource_or_tag", RAW_STRING ), + get( "minecraft:resource_or_tag_key", RAW_STRING ), + get( "minecraft:resource", RAW_STRING ), + get( "minecraft:resource_key", RAW_STRING ), + get( "minecraft:resource_selector", RAW_STRING ), + get( "minecraft:template_mirror", VOID ), + get( "minecraft:template_rotation", VOID ), + get( "minecraft:uuid", VOID ), + get( "minecraft:heightmap", VOID ), + get( "minecraft:loot_table", VOID ), + get( "minecraft:loot_predicate", VOID ), + get( "minecraft:loot_modifier", VOID ) + }; } private static void register(String name, ArgumentSerializer serializer) @@ -879,7 +939,10 @@ private static ArgumentType read(ByteBuf buf, int protocolVersion) { key = readVarInt( buf ); - if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 ) + { + reader = IDS_1_21_5[(Integer) key]; + } else if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 ) { reader = IDS_1_20_5[(Integer) key]; } else if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_3 ) 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 206bc8d2fa..c8fdb2e91d 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 @@ -93,6 +93,8 @@ public static EntityMap getEntityMap(int version) return EntityMap_1_16_2.INSTANCE_1_21_2; case ProtocolConstants.MINECRAFT_1_21_4: return EntityMap_1_16_2.INSTANCE_1_21_4; + case ProtocolConstants.MINECRAFT_1_21_5: + return EntityMap_1_16_2.INSTANCE_1_21_5; } throw new RuntimeException( "Version " + version + " has no entity map" ); } 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 index 66afd2f190..ce96ac9eb4 100644 --- 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 @@ -25,6 +25,7 @@ class EntityMap_1_16_2 extends EntityMap static final EntityMap_1_16_2 INSTANCE_1_20_5 = new EntityMap_1_16_2( -1, 0x37 ); static final EntityMap_1_16_2 INSTANCE_1_21_2 = new EntityMap_1_16_2( -1, 0x39 ); static final EntityMap_1_16_2 INSTANCE_1_21_4 = new EntityMap_1_16_2( -1, 0x3B ); + static final EntityMap_1_16_2 INSTANCE_1_21_5 = new EntityMap_1_16_2( -1, 0x3C ); // private final int spawnPlayerId; private final int spectateId; From 4fded9828f082263ee68c2ef17f74e56b6fc25b7 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Wed, 29 Jan 2025 07:52:09 +1100 Subject: [PATCH 55/58] #3775: Allow decompressed packets to grow to max capacity Do not use size as max capacity, as its possible that the entity rewriter increases the size afterwards. This would result in a kick (it happens rarely as the entity ids size must differ). --- .../java/net/md_5/bungee/compress/PacketDecompressor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 8e89d4b651..1612ffcf9a 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 @@ -41,7 +41,9 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) t throw new OverflowPacketException( "Packet may not be larger than " + MAX_DECOMPRESSED_LEN + " bytes" ); } - ByteBuf decompressed = ctx.alloc().directBuffer( size, size ); + // Do not use size as max capacity, as its possible that the entity rewriter increases the size afterwards + // This would result in a kick (it happens rarely as the entity ids size must differ) + ByteBuf decompressed = ctx.alloc().directBuffer( size, MAX_DECOMPRESSED_LEN ); try { zlib.process( in, decompressed ); From 80bb23728906fdcf9f782f29c0efacf113b9acbb Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Wed, 29 Jan 2025 20:35:46 +1100 Subject: [PATCH 56/58] #3774: Minecraft 25w04a chat component changes --- .../net/md_5/bungee/api/chat/ClickEvent.java | 9 ++ .../net/md_5/bungee/api/chat/HoverEvent.java | 26 +++ .../bungee/api/chat/hover/content/Entity.java | 19 +++ .../chat/hover/content/EntitySerializer.java | 20 ++- .../bungee/chat/BaseComponentSerializer.java | 148 ++++++++++++++---- .../bungee/util/ChatComponentTransformer.java | 14 ++ 6 files changed, 197 insertions(+), 39 deletions(-) diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java b/chat/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java index 1f6435998d..1389c4b2d8 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java @@ -3,7 +3,9 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.Setter; import lombok.ToString; +import org.jetbrains.annotations.ApiStatus; @Getter @ToString @@ -23,6 +25,13 @@ public final class ClickEvent */ private final String value; + /** + * Returns whether this click event is used for version above 1.21.4 + */ + @Setter + @ApiStatus.Internal + private boolean v1_21_5 = false; + public enum Action { diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java b/chat/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java index 02da5ac52a..9149ee0d2a 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java @@ -14,6 +14,7 @@ import net.md_5.bungee.api.chat.hover.content.Item; import net.md_5.bungee.api.chat.hover.content.Text; import net.md_5.bungee.chat.ComponentSerializer; +import org.jetbrains.annotations.ApiStatus; @Getter @ToString @@ -34,8 +35,33 @@ public final class HoverEvent * Returns whether this hover event is prior to 1.16 */ @Setter + @ApiStatus.Internal private boolean legacy = false; + /** + * Returns whether this hover event is used for version above 1.21.4 + */ + @ApiStatus.Internal + private boolean v1_21_5 = false; + + /** + * Set the compatibility to 1.21.5, also modifies the underlying entities. + * + * @param v1_21_5 the compatibility to set + */ + @ApiStatus.Internal + public void setV1_21_5(boolean v1_21_5) + { + this.v1_21_5 = v1_21_5; + for ( Content content : contents ) + { + if ( content instanceof Entity ) + { + ( (Entity) content ).setV1_21_5( v1_21_5 ); + } + } + } + /** * Creates event with an action and a list of contents. * diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/Entity.java b/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/Entity.java index bbe708e964..0ae5c22802 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/Entity.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/Entity.java @@ -7,6 +7,7 @@ import lombok.ToString; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.HoverEvent; +import org.jetbrains.annotations.ApiStatus; @Data @AllArgsConstructor @@ -15,6 +16,18 @@ public class Entity extends Content { + /** + * Required for backwards compatibility. + * + * @param type the type of the entity, for example 'minecraft:pig' + * @param id for example '6cb1b229-ce5c-4179-af8d-eea185c25963' + * @param name the name of the entity + */ + public Entity(String type, @NonNull String id, BaseComponent name) + { + this( type, id, name, false ); + } + /** * Namespaced entity ID. * @@ -35,6 +48,12 @@ public class Entity extends Content */ private BaseComponent name; + /** + * True if this entity is for 1.21.5 or later + */ + @ApiStatus.Internal + private boolean v1_21_5; + @Override public HoverEvent.Action requiredAction() { diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/EntitySerializer.java b/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/EntitySerializer.java index 654dc3aa50..0bac2d3574 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/EntitySerializer.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/EntitySerializer.java @@ -19,20 +19,23 @@ public Entity deserialize(JsonElement element, Type type, JsonDeserializationCon { JsonObject value = element.getAsJsonObject(); + boolean newEntity = value.has( "uuid" ); + String idString; - JsonElement id = value.get( "id" ); - if ( id.isJsonArray() ) + JsonElement uuid = value.get( newEntity ? "uuid" : "id" ); + if ( uuid.isJsonArray() ) { - idString = parseUUID( context.deserialize( id, int[].class ) ).toString(); + idString = parseUUID( context.deserialize( uuid, int[].class ) ).toString(); } else { - idString = id.getAsString(); + idString = uuid.getAsString(); } return new Entity( - ( value.has( "type" ) ) ? value.get( "type" ).getAsString() : null, + ( value.has( newEntity ? "id" : "type" ) ) ? value.get( newEntity ? "id" : "type" ).getAsString() : null, idString, - ( value.has( "name" ) ) ? context.deserialize( value.get( "name" ), BaseComponent.class ) : null + ( value.has( "name" ) ) ? context.deserialize( value.get( "name" ), BaseComponent.class ) : null, + newEntity ); } @@ -40,8 +43,9 @@ public Entity deserialize(JsonElement element, Type type, JsonDeserializationCon public JsonElement serialize(Entity content, Type type, JsonSerializationContext context) { JsonObject object = new JsonObject(); - object.addProperty( "type", ( content.getType() != null ) ? content.getType() : "minecraft:pig" ); - object.addProperty( "id", content.getId() ); + + object.addProperty( content.isV1_21_5() ? "id" : "type", ( content.getType() != null ) ? content.getType() : "minecraft:pig" ); + object.addProperty( content.isV1_21_5() ? "uuid" : "id", content.getId() ); if ( content.getName() != null ) { object.add( "name", context.serialize( content.getName() ) ); diff --git a/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java index 5ecbc30d1e..9d53b60fc8 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -30,42 +30,65 @@ protected void deserialize(JsonObject object, BaseComponent component, JsonDeser } //Events - JsonObject clickEvent = object.getAsJsonObject( "clickEvent" ); + JsonObject clickEvent; + boolean newClickEvent = ( clickEvent = object.getAsJsonObject( "click_event" ) ) != null; + if ( !newClickEvent ) + { + clickEvent = object.getAsJsonObject( "clickEvent" ); + } if ( clickEvent != null ) { - component.setClickEvent( new ClickEvent( - ClickEvent.Action.valueOf( clickEvent.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) ), - ( clickEvent.has( "value" ) ) ? clickEvent.get( "value" ).getAsString() : "" ) ); + ClickEvent.Action action = ClickEvent.Action.valueOf( clickEvent.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) ); + if ( newClickEvent ) + { + switch ( action ) + { + case OPEN_URL: + component.setClickEvent( new ClickEvent( action, clickEvent.get( "url" ).getAsString() ) ); + break; + case RUN_COMMAND: + case SUGGEST_COMMAND: + component.setClickEvent( new ClickEvent( action, clickEvent.get( "command" ).getAsString() ) ); + break; + case CHANGE_PAGE: + int page = clickEvent.get( "page" ).getAsInt(); + Preconditions.checkArgument( page >= 0, "Page number has to be positive" ); + component.setClickEvent( new ClickEvent( action, Integer.toString( page ) ) ); + break; + default: + component.setClickEvent( new ClickEvent( action, ( clickEvent.has( "value" ) ) ? clickEvent.get( "value" ).getAsString() : "" ) ); + break; + } + component.getClickEvent().setV1_21_5( true ); + } else + { + component.setClickEvent( new ClickEvent( action, ( clickEvent.has( "value" ) ) ? clickEvent.get( "value" ).getAsString() : "" ) ); + } + } + + JsonObject hoverEventJson; + boolean newHoverEvent = ( hoverEventJson = object.getAsJsonObject( "hover_event" ) ) != null; + if ( !newHoverEvent ) + { + hoverEventJson = object.getAsJsonObject( "hoverEvent" ); } - JsonObject hoverEventJson = object.getAsJsonObject( "hoverEvent" ); + if ( hoverEventJson != null ) { HoverEvent hoverEvent = null; HoverEvent.Action action = HoverEvent.Action.valueOf( hoverEventJson.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) ); - JsonElement value = hoverEventJson.get( "value" ); - if ( value != null ) + if ( newHoverEvent || hoverEventJson.has( "contents" ) ) { - - // Plugins previously had support to pass BaseComponent[] into any action. - // If the GSON is possible to be parsed as BaseComponent, attempt to parse as so. - BaseComponent[] components; - if ( value.isJsonArray() ) - { - components = context.deserialize( value, BaseComponent[].class ); - } else + // value is only used for text in >= 1.21.5 (its inlined now) + JsonElement contents = hoverEventJson.get( newHoverEvent ? "value" : "contents" ); + if ( contents != null || ( newHoverEvent && ( action == HoverEvent.Action.SHOW_ITEM || action == HoverEvent.Action.SHOW_ENTITY ) ) ) { - components = new BaseComponent[] + if ( contents == null ) { - context.deserialize( value, BaseComponent.class ) - }; - } - hoverEvent = new HoverEvent( action, components ); - } else - { - JsonElement contents = hoverEventJson.get( "contents" ); - if ( contents != null ) - { + // this is the new inline for SHOW_ITEM and SHOW_ENTITY + contents = hoverEventJson; + } Content[] list; if ( contents.isJsonArray() ) { @@ -78,6 +101,27 @@ protected void deserialize(JsonObject object, BaseComponent component, JsonDeser }; } hoverEvent = new HoverEvent( action, new ArrayList<>( Arrays.asList( list ) ) ); + hoverEvent.setV1_21_5( newHoverEvent ); + } + } else + { + JsonElement value = hoverEventJson.get( "value" ); + if ( value != null ) + { + // Plugins previously had support to pass BaseComponent[] into any action. + // If the GSON is possible to be parsed as BaseComponent, attempt to parse as so. + BaseComponent[] components; + if ( value.isJsonArray() ) + { + components = context.deserialize( value, BaseComponent[].class ); + } else + { + components = new BaseComponent[] + { + context.deserialize( value, BaseComponent.class ) + }; + } + hoverEvent = new HoverEvent( action, components ); } } @@ -118,23 +162,65 @@ protected void serialize(JsonObject object, BaseComponent component, JsonSeriali if ( component.getClickEvent() != null ) { JsonObject clickEvent = new JsonObject(); - clickEvent.addProperty( "action", component.getClickEvent().getAction().toString().toLowerCase( Locale.ROOT ) ); - clickEvent.addProperty( "value", component.getClickEvent().getValue() ); - object.add( "clickEvent", clickEvent ); + String actionName = component.getClickEvent().getAction().toString().toLowerCase( Locale.ROOT ); + clickEvent.addProperty( "action", actionName.toLowerCase( Locale.ROOT ) ); + if ( component.getClickEvent().isV1_21_5() ) + { + ClickEvent.Action action = ClickEvent.Action.valueOf( actionName.toUpperCase( Locale.ROOT ) ); + switch ( action ) + { + case OPEN_URL: + clickEvent.addProperty( "url", component.getClickEvent().getValue() ); + break; + case RUN_COMMAND: + case SUGGEST_COMMAND: + clickEvent.addProperty( "command", component.getClickEvent().getValue() ); + break; + case CHANGE_PAGE: + clickEvent.addProperty( "page", Integer.parseInt( component.getClickEvent().getValue() ) ); + break; + default: + clickEvent.addProperty( "value", component.getClickEvent().getValue() ); + break; + } + object.add( "click_event", clickEvent ); + } else + { + clickEvent.addProperty( "value", component.getClickEvent().getValue() ); + object.add( "clickEvent", clickEvent ); + } + } if ( component.getHoverEvent() != null ) { JsonObject hoverEvent = new JsonObject(); hoverEvent.addProperty( "action", component.getHoverEvent().getAction().toString().toLowerCase( Locale.ROOT ) ); + boolean newFormat = component.getHoverEvent().isV1_21_5(); if ( component.getHoverEvent().isLegacy() ) { hoverEvent.add( "value", context.serialize( component.getHoverEvent().getContents().get( 0 ) ) ); } else { - hoverEvent.add( "contents", context.serialize( ( component.getHoverEvent().getContents().size() == 1 ) - ? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ) ); + if ( newFormat ) + { + if ( component.getHoverEvent().getAction() == HoverEvent.Action.SHOW_ITEM || component.getHoverEvent().getAction() == HoverEvent.Action.SHOW_ENTITY ) + { + JsonObject inlined = context.serialize( ( component.getHoverEvent().getContents().size() == 1 ) + ? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ).getAsJsonObject(); + inlined.entrySet().forEach( entry -> hoverEvent.add( entry.getKey(), entry.getValue() ) ); + } else + { + hoverEvent.add( "value", context.serialize( ( component.getHoverEvent().getContents().size() == 1 ) + ? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ) ); + } + } else + { + hoverEvent.add( "contents", context.serialize( ( component.getHoverEvent().getContents().size() == 1 ) + ? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ) ); + } + } - object.add( "hoverEvent", hoverEvent ); + object.add( newFormat ? "hover_event" : "hoverEvent", hoverEvent ); } if ( component.getExtra() != null ) diff --git a/proxy/src/main/java/net/md_5/bungee/util/ChatComponentTransformer.java b/proxy/src/main/java/net/md_5/bungee/util/ChatComponentTransformer.java index 298e4118d1..e4cbb9fd02 100644 --- a/proxy/src/main/java/net/md_5/bungee/util/ChatComponentTransformer.java +++ b/proxy/src/main/java/net/md_5/bungee/util/ChatComponentTransformer.java @@ -50,6 +50,20 @@ public BaseComponent legacyHoverTransform(ProxiedPlayer player, BaseComponent ne next.getHoverEvent().getContents().clear(); next.getHoverEvent().getContents().add( exception ); } + } else if ( player.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_21_5 ) + { + if ( next.getHoverEvent() != null && !next.getHoverEvent().isV1_21_5() ) + { + next = next.duplicate(); + next.getHoverEvent().setV1_21_5( true ); + } + + if ( next.getClickEvent() != null && !next.getClickEvent().isV1_21_5() ) + { + next = next.duplicate(); + next.getClickEvent().setV1_21_5( true ); + } + } return next; From b5ae0196fcc453bd47664761b1b944ff31caa73e Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Wed, 29 Jan 2025 20:38:20 +1100 Subject: [PATCH 57/58] #3771: Make PluginManager methods thread safe --- .../md_5/bungee/api/plugin/PluginManager.java | 105 ++++++++++++++---- 1 file changed, 84 insertions(+), 21 deletions(-) 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 a8b21559ec..b159456f7f 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 @@ -23,6 +23,10 @@ import java.util.Map; import java.util.Set; import java.util.Stack; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Level; @@ -59,6 +63,9 @@ public final class PluginManager private final Multimap commandsByPlugin = ArrayListMultimap.create(); private final Multimap listenersByPlugin = ArrayListMultimap.create(); + private final ReadWriteLock commandsLock = new ReentrantReadWriteLock(); + private final Lock listenersLock = new ReentrantLock(); + @SuppressWarnings("unchecked") public PluginManager(ProxyServer proxy) { @@ -93,12 +100,19 @@ public PluginManager(ProxyServer proxy) */ public void registerCommand(Plugin plugin, Command command) { - commandMap.put( command.getName().toLowerCase( Locale.ROOT ), command ); - for ( String alias : command.getAliases() ) + commandsLock.writeLock().lock(); + try + { + commandMap.put( command.getName().toLowerCase( Locale.ROOT ), command ); + for ( String alias : command.getAliases() ) + { + commandMap.put( alias.toLowerCase( Locale.ROOT ), command ); + } + commandsByPlugin.put( plugin, command ); + } finally { - commandMap.put( alias.toLowerCase( Locale.ROOT ), command ); + commandsLock.writeLock().unlock(); } - commandsByPlugin.put( plugin, command ); } /** @@ -108,8 +122,15 @@ public void registerCommand(Plugin plugin, Command command) */ public void unregisterCommand(Command command) { - while ( commandMap.values().remove( command ) ); - commandsByPlugin.values().remove( command ); + commandsLock.writeLock().lock(); + try + { + while ( commandMap.values().remove( command ) ); + commandsByPlugin.values().remove( command ); + } finally + { + commandsLock.writeLock().unlock(); + } } /** @@ -119,11 +140,18 @@ public void unregisterCommand(Command command) */ public void unregisterCommands(Plugin plugin) { - for ( Iterator it = commandsByPlugin.get( plugin ).iterator(); it.hasNext(); ) + commandsLock.writeLock().lock(); + try { - Command command = it.next(); - while ( commandMap.values().remove( command ) ); - it.remove(); + for ( Iterator it = commandsByPlugin.get( plugin ).iterator(); it.hasNext(); ) + { + Command command = it.next(); + while ( commandMap.values().remove( command ) ); + it.remove(); + } + } finally + { + commandsLock.writeLock().unlock(); } } @@ -137,7 +165,14 @@ private Command getCommandIfEnabled(String commandName, CommandSender sender) return null; } - return commandMap.get( commandLower ); + commandsLock.readLock().lock(); + try + { + return commandMap.get( commandLower ); + } finally + { + commandsLock.readLock().unlock(); + } } /** @@ -434,13 +469,20 @@ public T callEvent(T event) */ public void registerListener(Plugin plugin, Listener listener) { - for ( Method method : listener.getClass().getDeclaredMethods() ) + listenersLock.lock(); + try { - Preconditions.checkArgument( !method.isAnnotationPresent( Subscribe.class ), + for ( Method method : listener.getClass().getDeclaredMethods() ) + { + Preconditions.checkArgument( !method.isAnnotationPresent( Subscribe.class ), "Listener %s has registered using deprecated subscribe annotation! Please update to @EventHandler.", listener ); + } + eventBus.register( listener ); + listenersByPlugin.put( plugin, listener ); + } finally + { + listenersLock.unlock(); } - eventBus.register( listener ); - listenersByPlugin.put( plugin, listener ); } /** @@ -450,8 +492,15 @@ public void registerListener(Plugin plugin, Listener listener) */ public void unregisterListener(Listener listener) { - eventBus.unregister( listener ); - listenersByPlugin.values().remove( listener ); + listenersLock.lock(); + try + { + eventBus.unregister( listener ); + listenersByPlugin.values().remove( listener ); + } finally + { + listenersLock.unlock(); + } } /** @@ -461,10 +510,17 @@ public void unregisterListener(Listener listener) */ public void unregisterListeners(Plugin plugin) { - for ( Iterator it = listenersByPlugin.get( plugin ).iterator(); it.hasNext(); ) + listenersLock.lock(); + try + { + for ( Iterator it = listenersByPlugin.get( plugin ).iterator(); it.hasNext(); ) + { + eventBus.unregister( it.next() ); + it.remove(); + } + } finally { - eventBus.unregister( it.next() ); - it.remove(); + listenersLock.unlock(); } } @@ -475,7 +531,14 @@ public void unregisterListeners(Plugin plugin) */ public Collection> getCommands() { - return Collections.unmodifiableCollection( commandMap.entrySet() ); + commandsLock.readLock().lock(); + try + { + return Collections.unmodifiableCollection( commandMap.entrySet() ); + } finally + { + commandsLock.readLock().unlock(); + } } boolean isTransitiveDepend(PluginDescription plugin, PluginDescription depend) From 9dd5fb626d3d65eb462c6e83286ec739a67ef162 Mon Sep 17 00:00:00 2001 From: md_5 Date: Thu, 30 Jan 2025 07:43:05 +1100 Subject: [PATCH 58/58] Fix duplicate 25w04a packet mapping --- protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 15bafe8395..40fdb86821 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 @@ -303,7 +303,6 @@ public enum Protocol map( ProtocolConstants.MINECRAFT_1_19_4, 0x17 ), map( ProtocolConstants.MINECRAFT_1_20_2, 0x18 ), map( ProtocolConstants.MINECRAFT_1_20_5, 0x19 ), - map( ProtocolConstants.MINECRAFT_1_21_5, 0x2B ), map( ProtocolConstants.MINECRAFT_1_21_5, 0x18 ) ); TO_CLIENT.registerPacket( @@ -994,6 +993,8 @@ private void registerPacket(Class packetClass, Supplier } ProtocolData data = protocols.get( protocol ); + Preconditions.checkState( data.packetConstructors[mapping.packetID] == null, "Duplicate packet mapping (%s)", mapping.packetID ); + data.packetMap.put( packetClass, mapping.packetID ); data.packetConstructors[mapping.packetID] = constructor; }