From 80bb23728906fdcf9f782f29c0efacf113b9acbb Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Wed, 29 Jan 2025 20:35:46 +1100 Subject: [PATCH] #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 1f643599..1389c4b2 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 @@ package net.md_5.bungee.api.chat; 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 02da5ac5..9149ee0d 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.Entity; 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 bbe708e9..0ae5c228 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.NonNull; 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 @@ import net.md_5.bungee.api.chat.HoverEvent; 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 654dc3aa..0bac2d35 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 class EntitySerializer implements JsonSerializer, JsonDeserialize { 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 class EntitySerializer implements JsonSerializer, JsonDeserialize 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 5ecbc30d..9d53b60f 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 @@ public class BaseComponentSerializer } //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 = object.getAsJsonObject( "hoverEvent" ); + + JsonObject hoverEventJson; + boolean newHoverEvent = ( hoverEventJson = object.getAsJsonObject( "hover_event" ) ) != null; + if ( !newHoverEvent ) + { + 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() ) + // 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 = context.deserialize( value, BaseComponent[].class ); - } else - { - 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 @@ public class BaseComponentSerializer }; } 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 @@ public class BaseComponentSerializer 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 298e4118..e4cbb9fd 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 final class ChatComponentTransformer 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;