#3774: Minecraft 25w04a chat component changes

This commit is contained in:
Outfluencer 2025-01-29 20:35:46 +11:00 committed by md_5
parent 4fded9828f
commit 80bb237289
No known key found for this signature in database
GPG Key ID: E8E901AC7C617C11
6 changed files with 197 additions and 39 deletions

View File

@ -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
{

View File

@ -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.
*

View File

@ -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()
{

View File

@ -19,20 +19,23 @@ public class EntitySerializer implements JsonSerializer<Entity>, 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<Entity>, 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() ) );

View File

@ -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 )

View File

@ -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;