From e62fc6c2916a991e00177c580986d8b1a22fdb41 Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Wed, 23 Jul 2025 19:13:03 +1000 Subject: [PATCH] #3864: Add ServerLinks API --- .../java/net/md_5/bungee/api/ServerLink.java | 77 +++++++++++++++++++ .../bungee/api/connection/ProxiedPlayer.java | 14 ++++ .../bungee/protocol/packet/ServerLinks.java | 25 ++---- .../java/net/md_5/bungee/UserConnection.java | 15 ++++ 4 files changed, 111 insertions(+), 20 deletions(-) create mode 100644 api/src/main/java/net/md_5/bungee/api/ServerLink.java diff --git a/api/src/main/java/net/md_5/bungee/api/ServerLink.java b/api/src/main/java/net/md_5/bungee/api/ServerLink.java new file mode 100644 index 00000000..c8cae1a5 --- /dev/null +++ b/api/src/main/java/net/md_5/bungee/api/ServerLink.java @@ -0,0 +1,77 @@ +package net.md_5.bungee.api; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.api.chat.BaseComponent; + +/** + * Represents a server link which may be sent to the client. + */ +@Data +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class ServerLink +{ + + /** + * The links type. + * + * Note: This value is nullable, if null, label is non-null. + */ + private final LinkType type; + + /** + * The label for the link. + * + * Note: This value is nullable, if null, type is non-null. + */ + private final BaseComponent label; + + /** + * The URL that is displayed. + */ + @NonNull + private final String url; + + /** + * Creates a link with a specified type and URL. + * + * @param type the type of the link + * @param url the URL to be displayed + */ + public ServerLink(@NonNull LinkType type, @NonNull String url) + { + this.type = type; + this.label = null; + this.url = url; + } + + /** + * Creates a link with a label and URL. + * + * @param label the label to be displayed + * @param url the URL to be displayed + */ + public ServerLink(@NonNull BaseComponent label, @NonNull String url) + { + this.type = null; + this.label = label; + this.url = url; + } + + public enum LinkType + { + + REPORT_BUG, + COMMUNITY_GUIDELINES, + SUPPORT, + STATUS, + FEEDBACK, + COMMUNITY, + WEBSITE, + FORUMS, + NEWS, + ANNOUNCEMENTS; + } +} diff --git a/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java b/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java index 35956272..d4831f7f 100644 --- a/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java +++ b/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java @@ -1,5 +1,6 @@ package net.md_5.bungee.api.connection; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.UUID; @@ -8,6 +9,7 @@ import net.md_5.bungee.api.Callback; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ServerConnectRequest; +import net.md_5.bungee.api.ServerLink; import net.md_5.bungee.api.SkinConfiguration; import net.md_5.bungee.api.Title; import net.md_5.bungee.api.chat.BaseComponent; @@ -411,4 +413,16 @@ public interface ProxiedPlayer extends Connection, CommandSender */ @ApiStatus.Experimental void showDialog(Dialog dialog); + + /** + * Sends server links to the player. + * + * Note: The links already sent to the player will be overwritten. Also, the + * backend server is able to override links sent by the proxy. + * + * @param serverLinks the server links to send + * @throws IllegalStateException if the player's version is not at least + * 1.21 + */ + void sendServerLinks(List serverLinks); } 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 9e4b1533..a7a1404b 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 @@ -27,10 +27,10 @@ public class ServerLinks extends DefinedPacket links = new Link[ len ]; for ( int i = 0; i < len; i++ ) { - Either type; + Either type; if ( buf.readBoolean() ) { - type = Either.left( LinkType.values()[readVarInt( buf )] ); + type = Either.left( readVarInt( buf ) ); } else { type = Either.right( readBaseComponent( buf, protocolVersion ) ); @@ -47,11 +47,11 @@ public class ServerLinks extends DefinedPacket writeVarInt( links.length, buf ); for ( Link link : links ) { - Either type = link.getType(); + Either type = link.getType(); if ( type.isLeft() ) { buf.writeBoolean( true ); - writeVarInt( type.getLeft().ordinal(), buf ); + writeVarInt( type.getLeft(), buf ); } else { buf.writeBoolean( false ); @@ -67,26 +67,11 @@ public class ServerLinks extends DefinedPacket handler.handle( this ); } - public enum LinkType - { - - REPORT_BUG, - COMMUNITY_GUIDELINES, - SUPPORT, - STATUS, - FEEDBACK, - COMMUNITY, - WEBSITE, - FORUMS, - NEWS, - ANNOUNCEMENTS; - } - @Data public static class Link { - private final Either type; + private final Either type; private final String url; } } 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 0e01e4c8..fad6835d 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -14,6 +14,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -29,6 +30,7 @@ import net.md_5.bungee.api.Callback; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ServerConnectRequest; +import net.md_5.bungee.api.ServerLink; import net.md_5.bungee.api.SkinConfiguration; import net.md_5.bungee.api.Title; import net.md_5.bungee.api.chat.BaseComponent; @@ -59,6 +61,7 @@ import net.md_5.bungee.protocol.packet.ClientSettings; import net.md_5.bungee.protocol.packet.Kick; import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; import net.md_5.bungee.protocol.packet.PluginMessage; +import net.md_5.bungee.protocol.packet.ServerLinks; import net.md_5.bungee.protocol.packet.SetCompression; import net.md_5.bungee.protocol.packet.ShowDialog; import net.md_5.bungee.protocol.packet.ShowDialogDirect; @@ -860,4 +863,16 @@ public final class UserConnection implements ProxiedPlayer unsafe.sendPacket( new ShowDialog( Either.right( dialog ) ) ); } + + @Override + public void sendServerLinks(List serverLinks) + { + Preconditions.checkState( getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_21, "Server links are only supported in 1.21 and above" ); + + ServerLinks.Link[] links = serverLinks.stream() + .map( link -> new ServerLinks.Link( link.getType() != null ? Either.left( link.getType().ordinal() ) : Either.right( link.getLabel() ), link.getUrl() ) ) + .toArray( ServerLinks.Link[]::new ); + + unsafe.sendPacket( new ServerLinks( links ) ); + } }