From f802b0592af106fb57346f193beea5788b5905c8 Mon Sep 17 00:00:00 2001 From: realstresser Date: Sun, 8 Jun 2025 00:28:14 +0300 Subject: [PATCH 1/6] Experimental Upstream update --- .../src/main/java/net/md_5/bungee/connection/InitialHandler.java | 1 + 1 file changed, 1 insertion(+) 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 4c29fa78..cddd988c 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 @@ -2,6 +2,7 @@ import com.google.common.base.Preconditions; import com.google.gson.Gson; +import io.netty.buffer.Unpooled; import io.netty.channel.EventLoop; import ir.xenoncommunity.XenonCore; import lombok.*; From 049e982498e79ef0a240816241c0223d8aae0a1e Mon Sep 17 00:00:00 2001 From: realstresser Date: Tue, 10 Jun 2025 12:12:34 +0300 Subject: [PATCH 2/6] Experimental Upstream update --- .../md_5/bungee/connection/LoginResult.java | 3 +- .../net/md_5/bungee/api/chat/ClickEvent.java | 4 - .../net/md_5/bungee/api/chat/HoverEvent.java | 23 +- .../bungee/api/chat/hover/content/Entity.java | 16 -- .../chat/hover/content/EntitySerializer.java | 28 +- .../bungee/chat/BaseComponentSerializer.java | 100 +++++-- .../net/md_5/bungee/chat/ChatVersion.java | 9 + .../md_5/bungee/chat/ComponentSerializer.java | 84 +----- .../chat/KeybindComponentSerializer.java | 6 + .../bungee/chat/ScoreComponentSerializer.java | 5 +- .../chat/SelectorComponentSerializer.java | 9 +- .../bungee/chat/TextComponentSerializer.java | 5 +- .../chat/TranslatableComponentSerializer.java | 5 +- .../chat/VersionedComponentSerializer.java | 269 ++++++++++++++++++ .../md_5/bungee/protocol/ChatSerializer.java | 19 ++ .../md_5/bungee/protocol/DefinedPacket.java | 13 +- .../net/md_5/bungee/protocol/packet/Kick.java | 9 +- .../main/java/net/md_5/bungee/BungeeCord.java | 10 - .../java/net/md_5/bungee/UserConnection.java | 6 +- .../bungee/connection/DownstreamBridge.java | 14 +- .../bungee/connection/InitialHandler.java | 4 +- .../md_5/bungee/connection/PingHandler.java | 10 +- .../bungee/connection/ServerConnector.java | 4 +- .../bungee/util/ChatComponentTransformer.java | 9 - 24 files changed, 465 insertions(+), 199 deletions(-) create mode 100644 chat/src/main/java/net/md_5/bungee/chat/ChatVersion.java create mode 100644 chat/src/main/java/net/md_5/bungee/chat/VersionedComponentSerializer.java create mode 100644 protocol/src/main/java/net/md_5/bungee/protocol/ChatSerializer.java diff --git a/api/src/main/java/net/md_5/bungee/connection/LoginResult.java b/api/src/main/java/net/md_5/bungee/connection/LoginResult.java index 885848be..fb07a90d 100644 --- a/api/src/main/java/net/md_5/bungee/connection/LoginResult.java +++ b/api/src/main/java/net/md_5/bungee/connection/LoginResult.java @@ -1,5 +1,6 @@ package net.md_5.bungee.connection; +import com.google.gson.Gson; import lombok.AllArgsConstructor; import lombok.Data; import net.md_5.bungee.protocol.Property; @@ -7,7 +8,7 @@ @Data @AllArgsConstructor public class LoginResult { - + public static final Gson GSON = new Gson(); private String id; private String name; private Property[] properties; 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 862b61ed..c640e5a0 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 @@ -20,10 +20,6 @@ public final class ClickEvent { */ private final String value; - @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 8e69cccf..b3b05912 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 @@ -7,6 +7,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 net.md_5.bungee.chat.VersionedComponentSerializer; import org.jetbrains.annotations.ApiStatus; import java.util.ArrayList; @@ -34,12 +35,6 @@ public final class HoverEvent { @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; - /** * Creates event with an action and a list of contents. * @@ -95,20 +90,6 @@ public static Class getClass(HoverEvent.Action action, boolean array) { } } - /** - * 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); - } - } - } @Deprecated public BaseComponent[] getValue() { @@ -117,7 +98,7 @@ public BaseComponent[] getValue() { return (BaseComponent[]) ((Text) content).getValue(); } - TextComponent component = new TextComponent(ComponentSerializer.toString(content)); + TextComponent component = new TextComponent(VersionedComponentSerializer.getDefault().toString(content)); return new BaseComponent[] { component 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 6bbf7911..60ec0593 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 @@ -31,22 +31,6 @@ public class Entity extends Content { * This is optional and will be hidden if null. */ private BaseComponent name; - /** - * True if this entity is for 1.21.5 or later - */ - @ApiStatus.Internal - private boolean v1_21_5; - - /** - * 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); - } @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 12615c1b..382d3812 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 @@ -2,12 +2,20 @@ import com.google.gson.*; import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.chat.BaseComponentSerializer; import java.lang.reflect.Type; import java.util.UUID; -public class EntitySerializer implements JsonSerializer, JsonDeserializer { +import net.md_5.bungee.chat.ChatVersion; +import net.md_5.bungee.chat.VersionedComponentSerializer; +public class EntitySerializer extends BaseComponentSerializer implements JsonSerializer, JsonDeserializer { + + public EntitySerializer(VersionedComponentSerializer serializer) + { + super( serializer ); + } private static UUID parseUUID(int[] array) { return new UUID((long) array[0] << 32 | (long) array[1] & 0XFFFFFFFFL, (long) array[2] << 32 | (long) array[3] & 0XFFFFFFFFL); } @@ -26,16 +34,26 @@ public Entity deserialize(JsonElement element, Type type, JsonDeserializationCon idString = uuid.getAsString(); } return new Entity((value.has(newEntity ? "id" : "type")) ? value.get(newEntity ? "id" : "type").getAsString() : null, - idString, (value.has("name")) ? context.deserialize(value.get("name"), BaseComponent.class) : null, - newEntity + idString, ( value.has( "name" ) ) ? context.deserialize( value.get( "name" ), BaseComponent.class ) : null ); } @Override public JsonElement serialize(Entity content, Type type, JsonSerializationContext context) { JsonObject object = new JsonObject(); - 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()); + switch ( serializer.getVersion() ) + { + case V1_21_5: + object.addProperty( "id", ( content.getType() != null ) ? content.getType() : "minecraft:pig" ); + object.addProperty( "uuid", content.getId() ); + break; + case V1_16: + object.addProperty( "type", ( content.getType() != null ) ? content.getType() : "minecraft:pig" ); + object.addProperty( "id", content.getId() ); + break; + default: + throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() ); + } 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 78bf5198..8cf9529d 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 @@ -5,6 +5,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonSerializationContext; +import lombok.RequiredArgsConstructor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ComponentStyle; @@ -13,8 +14,11 @@ import java.util.*; +@RequiredArgsConstructor public class BaseComponentSerializer { + protected final VersionedComponentSerializer serializer; + protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context) { component.applyStyle(context.deserialize(object, ComponentStyle.class)); @@ -49,7 +53,6 @@ protected void deserialize(JsonObject object, BaseComponent component, JsonDeser 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() : "")); } @@ -83,7 +86,6 @@ 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"); @@ -116,13 +118,13 @@ protected void deserialize(JsonObject object, BaseComponent component, JsonDeser protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context) { boolean first = false; - if (ComponentSerializer.serializedComponents.get() == null) { + if (VersionedComponentSerializer.serializedComponents.get() == null) { first = true; - ComponentSerializer.serializedComponents.set(Collections.newSetFromMap(new IdentityHashMap())); + VersionedComponentSerializer.serializedComponents.set(Collections.newSetFromMap(new IdentityHashMap())); } try { - Preconditions.checkArgument(!ComponentSerializer.serializedComponents.get().contains(component), "Component loop"); - ComponentSerializer.serializedComponents.get().add(component); + Preconditions.checkArgument(!VersionedComponentSerializer.serializedComponents.get().contains(component), "Component loop"); + VersionedComponentSerializer.serializedComponents.get().add(component); ComponentStyleSerializer.serializeTo(component.getStyle(), object); @@ -135,7 +137,36 @@ protected void serialize(JsonObject object, BaseComponent component, JsonSeriali JsonObject clickEvent = new JsonObject(); String actionName = component.getClickEvent().getAction().toString().toLowerCase(Locale.ROOT); clickEvent.addProperty("action", actionName.toLowerCase(Locale.ROOT)); - if (component.getClickEvent().isV1_21_5()) { + switch ( serializer.getVersion() ) + + { + case V1_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 ); + break; + case V1_16: + clickEvent.addProperty( "value", component.getClickEvent().getValue() ); + object.add( "clickEvent", clickEvent ); + break; + default: + throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() ); + /*if (component.getClickEvent().isV1_21_5()) { ClickEvent.Action action = ClickEvent.Action.valueOf(actionName.toUpperCase(Locale.ROOT)); switch (action) { case OPEN_URL: @@ -152,45 +183,56 @@ protected void serialize(JsonObject object, BaseComponent component, JsonSeriali clickEvent.addProperty("value", component.getClickEvent().getValue()); break; } - object.add("click_event", clickEvent); - } else { - clickEvent.addProperty("value", component.getClickEvent().getValue()); - object.add("clickEvent", clickEvent); + object.add("click_event", 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 { - 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())); + switch (serializer.getVersion()) { + case V1_21_5: + 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() ) ); + } + break; + case V1_16: + hoverEvent.add( "contents", context.serialize( ( component.getHoverEvent().getContents().size() == 1 ) + ? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ) ); + break; + default: + throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() ); } - } - object.add(newFormat ? "hover_event" : "hoverEvent", hoverEvent); + switch ( serializer.getVersion() ) + { + case V1_21_5: + object.add( "hover_event", hoverEvent ); + break; + case V1_16: + object.add( "hoverEvent", hoverEvent ); + break; + default: + throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() ); + } } if (component.getExtra() != null) { object.add("extra", context.serialize(component.getExtra())); } } finally { - ComponentSerializer.serializedComponents.get().remove(component); + VersionedComponentSerializer.serializedComponents.get().remove(component); if (first) { - ComponentSerializer.serializedComponents.set(null); + VersionedComponentSerializer.serializedComponents.set(null); } } } diff --git a/chat/src/main/java/net/md_5/bungee/chat/ChatVersion.java b/chat/src/main/java/net/md_5/bungee/chat/ChatVersion.java new file mode 100644 index 00000000..c0128538 --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/chat/ChatVersion.java @@ -0,0 +1,9 @@ +package net.md_5.bungee.chat; + +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public enum ChatVersion{ + V1_16, + V1_21_5; +} \ No newline at end of file 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 5297c0e9..acc315b1 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 @@ -9,21 +9,6 @@ public class ComponentSerializer implements JsonDeserializer { - public static final ThreadLocal> serializedComponents = new ThreadLocal>(); - private static final Gson gson = new GsonBuilder(). - registerTypeAdapter(BaseComponent.class, new ComponentSerializer()). - registerTypeAdapter(TextComponent.class, new TextComponentSerializer()). - registerTypeAdapter(TranslatableComponent.class, new TranslatableComponentSerializer()). - registerTypeAdapter(KeybindComponent.class, new KeybindComponentSerializer()). - registerTypeAdapter(ScoreComponent.class, new ScoreComponentSerializer()). - registerTypeAdapter(SelectorComponent.class, new SelectorComponentSerializer()). - registerTypeAdapter(ComponentStyle.class, new ComponentStyleSerializer()). - registerTypeAdapter(Entity.class, new EntitySerializer()). - registerTypeAdapter(Text.class, new TextSerializer()). - registerTypeAdapter(Item.class, new ItemSerializer()). - registerTypeAdapter(ItemTag.class, new ItemTag.Serializer()). - create(); - /** * Parse a JSON-compliant String as an array of base components. The input * can be one of either an array of components, or a single component @@ -42,16 +27,7 @@ public class ComponentSerializer implements JsonDeserializer { * @return an array of all parsed components */ public static BaseComponent[] parse(String json) { - JsonElement jsonElement = JsonParser.parseString(json); - - if (jsonElement.isJsonArray()) { - return gson.fromJson(jsonElement, BaseComponent[].class); - } else { - return new BaseComponent[] - { - gson.fromJson(jsonElement, BaseComponent.class) - }; - } + return VersionedComponentSerializer.getDefault().parse( json ); } /** @@ -63,9 +39,7 @@ public static BaseComponent[] parse(String json) { * component string is passed as input */ public static BaseComponent deserialize(String json) { - JsonElement jsonElement = JsonParser.parseString(json); - - return deserialize(jsonElement); + return VersionedComponentSerializer.getDefault().deserialize( json ); } /** @@ -77,17 +51,7 @@ public static BaseComponent deserialize(String json) { * component is passed as input */ public static BaseComponent deserialize(JsonElement jsonElement) { - if (jsonElement instanceof JsonPrimitive) { - JsonPrimitive primitive = (JsonPrimitive) jsonElement; - if (primitive.isString()) { - return new TextComponent(primitive.getAsString()); - } - } else if (jsonElement instanceof JsonArray) { - BaseComponent[] array = gson.fromJson(jsonElement, BaseComponent[].class); - return TextComponent.fromArray(array); - } - - return gson.fromJson(jsonElement, BaseComponent.class); + return VersionedComponentSerializer.getDefault().deserialize( jsonElement ); } /** @@ -99,9 +63,7 @@ public static BaseComponent deserialize(JsonElement jsonElement) { * component style string is passed as input */ public static ComponentStyle deserializeStyle(String json) { - JsonElement jsonElement = JsonParser.parseString(json); - - return deserializeStyle(jsonElement); + return VersionedComponentSerializer.getDefault().deserializeStyle( json ); } /** @@ -113,15 +75,15 @@ public static ComponentStyle deserializeStyle(String json) { * component style is passed as input */ public static ComponentStyle deserializeStyle(JsonElement jsonElement) { - return gson.fromJson(jsonElement, ComponentStyle.class); + return VersionedComponentSerializer.getDefault().deserializeStyle( jsonElement ); } public static JsonElement toJson(BaseComponent component) { - return gson.toJsonTree(component); + return VersionedComponentSerializer.getDefault().toJson( component ); } public static JsonElement toJson(ComponentStyle style) { - return gson.toJsonTree(style); + return VersionedComponentSerializer.getDefault().toJson( style ); } /** @@ -131,7 +93,7 @@ public static JsonElement toJson(ComponentStyle style) { */ @Deprecated public static String toString(Object object) { - return gson.toJson(object); + return VersionedComponentSerializer.getDefault().toString( object ); } /** @@ -141,43 +103,23 @@ public static String toString(Object object) { */ @Deprecated public static String toString(Content content) { - return gson.toJson(content); + return VersionedComponentSerializer.getDefault().toString( content ); } public static String toString(BaseComponent component) { - return gson.toJson(component); + return VersionedComponentSerializer.getDefault().toString( component ); } public static String toString(BaseComponent... components) { - if (components.length == 1) { - return gson.toJson(components[0]); - } else { - return gson.toJson(new TextComponent(components)); - } + return VersionedComponentSerializer.getDefault().toString( components ); } public static String toString(ComponentStyle style) { - return gson.toJson(style); + return VersionedComponentSerializer.getDefault().toString( style ); } @Override public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - if (json.isJsonPrimitive()) { - return new TextComponent(json.getAsString()); - } - JsonObject object = json.getAsJsonObject(); - if (object.has("translate")) { - return context.deserialize(json, TranslatableComponent.class); - } - if (object.has("keybind")) { - return context.deserialize(json, KeybindComponent.class); - } - if (object.has("score")) { - return context.deserialize(json, ScoreComponent.class); - } - if (object.has("selector")) { - return context.deserialize(json, SelectorComponent.class); - } - return context.deserialize(json, TextComponent.class); + return VersionedComponentSerializer.getDefault().deserialize( json, typeOfT, context ); } } diff --git a/chat/src/main/java/net/md_5/bungee/chat/KeybindComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/KeybindComponentSerializer.java index 627b821c..3a052c1a 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/KeybindComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/KeybindComponentSerializer.java @@ -7,6 +7,12 @@ public class KeybindComponentSerializer extends BaseComponentSerializer implements JsonSerializer, JsonDeserializer { + public KeybindComponentSerializer(VersionedComponentSerializer serializer) + { + super( serializer ); + } + + @Override public KeybindComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject object = json.getAsJsonObject(); diff --git a/chat/src/main/java/net/md_5/bungee/chat/ScoreComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/ScoreComponentSerializer.java index 0ae3475a..78800827 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/ScoreComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/ScoreComponentSerializer.java @@ -6,7 +6,10 @@ import java.lang.reflect.Type; public class ScoreComponentSerializer extends BaseComponentSerializer implements JsonSerializer, JsonDeserializer { - + public ScoreComponentSerializer(VersionedComponentSerializer serializer) + { + super( serializer ); + } @Override public ScoreComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException { JsonObject json = element.getAsJsonObject(); diff --git a/chat/src/main/java/net/md_5/bungee/chat/SelectorComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/SelectorComponentSerializer.java index 0278d101..e2347a07 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/SelectorComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/SelectorComponentSerializer.java @@ -6,7 +6,10 @@ import java.lang.reflect.Type; public class SelectorComponentSerializer extends BaseComponentSerializer implements JsonSerializer, JsonDeserializer { - + public SelectorComponentSerializer(VersionedComponentSerializer serializer) + { + super( serializer ); + } @Override public SelectorComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException { JsonObject object = element.getAsJsonObject(); @@ -18,7 +21,7 @@ public SelectorComponent deserialize(JsonElement element, Type type, JsonDeseria JsonElement separator = object.get("separator"); if (separator != null) { - component.setSeparator(ComponentSerializer.deserialize(separator.getAsString())); + component.setSeparator(serializer.deserialize(separator.getAsString())); } deserialize(object, component, context); @@ -32,7 +35,7 @@ public JsonElement serialize(SelectorComponent component, Type type, JsonSeriali object.addProperty("selector", component.getSelector()); if (component.getSeparator() != null) { - object.addProperty("separator", ComponentSerializer.toString(component.getSeparator())); + object.addProperty("separator", serializer.toString(component.getSeparator())); } return object; } diff --git a/chat/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java index 2ebd0b77..1424aa6d 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java @@ -6,7 +6,10 @@ import java.lang.reflect.Type; public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer, JsonDeserializer { - + public TextComponentSerializer(VersionedComponentSerializer serializer) + { + super( serializer ); + } @Override public TextComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { TextComponent component = new TextComponent(); diff --git a/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java index baca8d86..4285e052 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java @@ -8,7 +8,10 @@ import java.util.Arrays; public class TranslatableComponentSerializer extends BaseComponentSerializer implements JsonSerializer, JsonDeserializer { - + public TranslatableComponentSerializer(VersionedComponentSerializer serializer) + { + super( serializer ); + } @Override public TranslatableComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { TranslatableComponent component = new TranslatableComponent(); diff --git a/chat/src/main/java/net/md_5/bungee/chat/VersionedComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/VersionedComponentSerializer.java new file mode 100644 index 00000000..bb8dc9d0 --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/chat/VersionedComponentSerializer.java @@ -0,0 +1,269 @@ +package net.md_5.bungee.chat; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; +import java.lang.reflect.Type; +import java.util.Set; +import lombok.Getter; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ComponentStyle; +import net.md_5.bungee.api.chat.ItemTag; +import net.md_5.bungee.api.chat.KeybindComponent; +import net.md_5.bungee.api.chat.ScoreComponent; +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; +import net.md_5.bungee.api.chat.hover.content.ItemSerializer; +import net.md_5.bungee.api.chat.hover.content.Text; +import net.md_5.bungee.api.chat.hover.content.TextSerializer; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public class VersionedComponentSerializer implements JsonDeserializer +{ + + @Getter + @ApiStatus.Internal + private final Gson gson; + @Getter + @ApiStatus.Internal + private final ChatVersion version; + + public VersionedComponentSerializer(ChatVersion version) + { + this.version = version; + this.gson = new GsonBuilder(). + registerTypeAdapter( BaseComponent.class, this ). + registerTypeAdapter( TextComponent.class, new TextComponentSerializer( this ) ). + registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer( this ) ). + registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer( this ) ). + registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer( this ) ). + registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer( this ) ). + registerTypeAdapter( ComponentStyle.class, new ComponentStyleSerializer() ). + registerTypeAdapter( Entity.class, new EntitySerializer( this ) ). + registerTypeAdapter( Text.class, new TextSerializer() ). + registerTypeAdapter( Item.class, new ItemSerializer() ). + registerTypeAdapter( ItemTag.class, new ItemTag.Serializer() ). + create(); + } + + private static final VersionedComponentSerializer v1_16 = new VersionedComponentSerializer( ChatVersion.V1_16 ); + private static final VersionedComponentSerializer v1_21_5 = new VersionedComponentSerializer( ChatVersion.V1_21_5 ); + + public static VersionedComponentSerializer forVersion(ChatVersion version) + { + switch ( version ) + { + case V1_16: + return v1_16; + case V1_21_5: + return v1_21_5; + default: + throw new IllegalArgumentException( "Unknown version " + version ); + } + } + + @Deprecated + @ApiStatus.Internal + public static VersionedComponentSerializer getDefault() + { + return v1_16; + } + + @ApiStatus.Internal + public static final ThreadLocal> serializedComponents = new ThreadLocal>(); + + /** + * Parse a JSON-compliant String as an array of base components. The input + * can be one of either an array of components, or a single component + * object. If the input is an array, each component will be parsed + * individually and returned in the order that they were parsed. If the + * input is a single component object, a single-valued array with the + * component will be returned. + *

+ * NOTE: {@link #deserialize(String)} is preferred as it + * will parse only one component as opposed to an array of components which + * is non- standard behavior. This method is still appropriate for parsing + * multiple components at once, although such use case is rarely (if at all) + * exhibited in vanilla Minecraft. + * + * @param json the component json to parse + * @return an array of all parsed components + */ + public BaseComponent[] parse(String json) + { + JsonElement jsonElement = JsonParser.parseString( json ); + + if ( jsonElement.isJsonArray() ) + { + return gson.fromJson( jsonElement, BaseComponent[].class ); + } else + { + return new BaseComponent[] + { + gson.fromJson( jsonElement, BaseComponent.class ) + }; + } + } + + /** + * Deserialize a JSON-compliant String as a single component. + * + * @param json the component json to parse + * @return the deserialized component + * @throws IllegalArgumentException if anything other than a valid JSON + * component string is passed as input + */ + public BaseComponent deserialize(String json) + { + JsonElement jsonElement = JsonParser.parseString( json ); + + return deserialize( jsonElement ); + } + + /** + * Deserialize a JSON element as a single component. + * + * @param jsonElement the component json to parse + * @return the deserialized component + * @throws IllegalArgumentException if anything other than a valid JSON + * component is passed as input + */ + public BaseComponent deserialize(JsonElement jsonElement) + { + if ( jsonElement instanceof JsonPrimitive ) + { + JsonPrimitive primitive = (JsonPrimitive) jsonElement; + if ( primitive.isString() ) + { + return new TextComponent( primitive.getAsString() ); + } + } else if ( jsonElement instanceof JsonArray ) + { + BaseComponent[] array = gson.fromJson( jsonElement, BaseComponent[].class ); + return TextComponent.fromArray( array ); + } + + return gson.fromJson( jsonElement, BaseComponent.class ); + } + + /** + * Deserialize a JSON-compliant String as a component style. + * + * @param json the component style json to parse + * @return the deserialized component style + * @throws IllegalArgumentException if anything other than a valid JSON + * component style string is passed as input + */ + public ComponentStyle deserializeStyle(String json) + { + JsonElement jsonElement = JsonParser.parseString( json ); + + return deserializeStyle( jsonElement ); + } + + /** + * Deserialize a JSON element as a component style. + * + * @param jsonElement the component style json to parse + * @return the deserialized component style + * @throws IllegalArgumentException if anything other than a valid JSON + * component style is passed as input + */ + public ComponentStyle deserializeStyle(JsonElement jsonElement) + { + return gson.fromJson( jsonElement, ComponentStyle.class ); + } + + public JsonElement toJson(BaseComponent component) + { + return gson.toJsonTree( component ); + } + + public 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 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 String toString(Content content) + { + return gson.toJson( content ); + } + + public String toString(BaseComponent component) + { + return gson.toJson( component ); + } + + public String toString(BaseComponent... components) + { + if ( components.length == 1 ) + { + return gson.toJson( components[0] ); + } else + { + return gson.toJson( new TextComponent( components ) ); + } + } + + public String toString(ComponentStyle style) + { + return gson.toJson( style ); + } + + @Override + public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException + { + if ( json.isJsonPrimitive() ) + { + return new TextComponent( json.getAsString() ); + } + JsonObject object = json.getAsJsonObject(); + if ( object.has( "translate" ) ) + { + return context.deserialize( json, TranslatableComponent.class ); + } + if ( object.has( "keybind" ) ) + { + return context.deserialize( json, KeybindComponent.class ); + } + if ( object.has( "score" ) ) + { + return context.deserialize( json, ScoreComponent.class ); + } + if ( object.has( "selector" ) ) + { + return context.deserialize( json, SelectorComponent.class ); + } + return context.deserialize( json, TextComponent.class ); + } +} \ No newline at end of file diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/ChatSerializer.java b/protocol/src/main/java/net/md_5/bungee/protocol/ChatSerializer.java new file mode 100644 index 00000000..71e92edf --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/ChatSerializer.java @@ -0,0 +1,19 @@ +package net.md_5.bungee.protocol; + +import net.md_5.bungee.chat.ChatVersion; +import net.md_5.bungee.chat.VersionedComponentSerializer; + +public class ChatSerializer +{ + + public static VersionedComponentSerializer forVersion(int protocolVersion) + { + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 ) + { + return VersionedComponentSerializer.forVersion( ChatVersion.V1_21_5 ); + } else + { + return VersionedComponentSerializer.forVersion( ChatVersion.V1_16 ); + } + } +} \ No newline at end of file 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 a8dd52ba..b2814a4a 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 @@ -9,7 +9,6 @@ import lombok.RequiredArgsConstructor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentStyle; -import net.md_5.bungee.chat.ComponentSerializer; import se.llbit.nbt.ErrorTag; import se.llbit.nbt.NamedTag; import se.llbit.nbt.SpecificTag; @@ -97,11 +96,11 @@ public static BaseComponent readBaseComponent(ByteBuf buf, int maxStringLength, SpecificTag nbt = (SpecificTag) readTag(buf, protocolVersion); JsonElement json = TagUtil.toJson(nbt); - return ComponentSerializer.deserialize(json); + return ChatSerializer.forVersion( protocolVersion ).deserialize(json); } else { String string = readString(buf, maxStringLength); - return ComponentSerializer.deserialize(string); + return ChatSerializer.forVersion( protocolVersion ).deserialize(string); } } @@ -109,7 +108,7 @@ public static ComponentStyle readComponentStyle(ByteBuf buf, int protocolVersion SpecificTag nbt = (SpecificTag) readTag(buf, protocolVersion); JsonElement json = TagUtil.toJson(nbt); - return ComponentSerializer.deserializeStyle(json); + return ChatSerializer.forVersion( protocolVersion ).deserializeStyle(json); } public static void writeEitherBaseComponent(Either message, ByteBuf buf, int protocolVersion) { @@ -122,19 +121,19 @@ public static void writeEitherBaseComponent(Either messag public static void writeBaseComponent(BaseComponent message, ByteBuf buf, int protocolVersion) { if (protocolVersion >= ProtocolConstants.MINECRAFT_1_20_3) { - JsonElement json = ComponentSerializer.toJson(message); + JsonElement json = ChatSerializer.forVersion( protocolVersion ).toJson(message); SpecificTag nbt = TagUtil.fromJson(json); writeTag(nbt, buf, protocolVersion); } else { - String string = ComponentSerializer.toString(message); + String string = ChatSerializer.forVersion( protocolVersion ).toString(message); writeString(string, buf); } } public static void writeComponentStyle(ComponentStyle style, ByteBuf buf, int protocolVersion) { - JsonElement json = ComponentSerializer.toJson(style); + JsonElement json = ChatSerializer.forVersion( protocolVersion ).toJson(style); SpecificTag nbt = TagUtil.fromJson(json); writeTag(nbt, buf, protocolVersion); diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Kick.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Kick.java index 7c2ea810..6e8207bb 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Kick.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Kick.java @@ -7,10 +7,7 @@ import lombok.NoArgsConstructor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.chat.ComponentSerializer; -import net.md_5.bungee.protocol.AbstractPacketHandler; -import net.md_5.bungee.protocol.DefinedPacket; -import net.md_5.bungee.protocol.Protocol; -import net.md_5.bungee.protocol.ProtocolConstants; +import net.md_5.bungee.protocol.*; @Data @NoArgsConstructor @@ -23,7 +20,7 @@ public class Kick extends DefinedPacket { @Override public void read(ByteBuf buf, Protocol protocol, ProtocolConstants.Direction direction, int protocolVersion) { if (protocol == Protocol.LOGIN) { - message = ComponentSerializer.deserialize(readString(buf)); + message = ChatSerializer.forVersion( protocolVersion ).deserialize(readString(buf)); } else { message = readBaseComponent(buf, protocolVersion); } @@ -32,7 +29,7 @@ public void read(ByteBuf buf, Protocol protocol, ProtocolConstants.Direction dir @Override public void write(ByteBuf buf, Protocol protocol, ProtocolConstants.Direction direction, int protocolVersion) { if (protocol == Protocol.LOGIN) { - writeString(ComponentSerializer.toString(message), buf); + writeString(ChatSerializer.forVersion( protocolVersion ).toString(message), buf); } else { writeBaseComponent(message, buf, protocolVersion); } diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index 064d5941..ff213bfb 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -77,16 +77,6 @@ public class BungeeCord extends ProxyServer { */ @Getter public final PluginManager pluginManager; - public final Gson gson = new GsonBuilder() - .registerTypeAdapter(BaseComponent.class, new ComponentSerializer()) - .registerTypeAdapter(TextComponent.class, new TextComponentSerializer()) - .registerTypeAdapter(TranslatableComponent.class, new TranslatableComponentSerializer()) - .registerTypeAdapter(KeybindComponent.class, new KeybindComponentSerializer()) - .registerTypeAdapter(ScoreComponent.class, new ScoreComponentSerializer()) - .registerTypeAdapter(SelectorComponent.class, new SelectorComponentSerializer()) - .registerTypeAdapter(ComponentStyle.class, new ComponentStyleSerializer()) - .registerTypeAdapter(ServerPing.PlayerInfo.class, new PlayerInfoSerializer()) - .registerTypeAdapter(Favicon.class, Favicon.getFaviconTypeAdapter()).create(); /** * locations.yml save thread. */ 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 84f3c0c1..d2ef7efb 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -25,6 +25,7 @@ import net.md_5.bungee.api.event.ServerConnectEvent; import net.md_5.bungee.api.score.Scoreboard; import net.md_5.bungee.chat.ComponentSerializer; +import net.md_5.bungee.chat.VersionedComponentSerializer; import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.connection.ServerConnector; import net.md_5.bungee.entitymap.EntityMap; @@ -70,6 +71,8 @@ public final class UserConnection implements ProxiedPlayer { private final Scoreboard serverSentScoreboard = new Scoreboard(); @Getter private final Collection sentBossBars = new HashSet<>(); + @Getter + private VersionedComponentSerializer chatSerializer; // Waterfall start @Getter private final Multimap potions = HashMultimap.create(); @@ -145,6 +148,7 @@ public void sendPacketQueued(DefinedPacket packet) { public boolean init() { this.entityRewrite = EntityMap.getEntityMap(getPendingConnection().getVersion()); + this.chatSerializer = ChatSerializer.forVersion( getPendingConnection().getVersion() ); this.displayName = name; tabListHandler = new ServerUnique(this); @@ -462,7 +466,7 @@ private void sendMessage(ChatMessageType position, UUID sender, BaseComponent me position = ChatMessageType.SYSTEM; sendPacketQueued(new SystemChat(message, position.ordinal())); } else { - sendPacketQueued(new Chat(ComponentSerializer.toString(message), (byte) position.ordinal(), sender)); + sendPacketQueued( new Chat( chatSerializer.toString( message ), (byte) position.ordinal(), sender ) ); } } 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 d293bac9..cae54449 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 @@ -173,7 +173,7 @@ public void handle(ScoreboardObjective objective) throws Exception { final Scoreboard serverScoreboard = con.getServerSentScoreboard(); switch (objective.getAction()) { case 0: - serverScoreboard.addObjective(new Objective(objective.getName(), (objective.getValue().isLeft()) ? objective.getValue().getLeft() : ComponentSerializer.toString(objective.getValue().getRight()), objective.getType().toString())); + serverScoreboard.addObjective(new Objective(objective.getName(), (objective.getValue().isLeft()) ? objective.getValue().getLeft() : con.getChatSerializer().toString(objective.getValue().getRight()), objective.getType().toString())); break; case 1: serverScoreboard.removeObjective(objective.getName()); @@ -181,7 +181,7 @@ public void handle(ScoreboardObjective objective) throws Exception { case 2: Objective oldObjective = serverScoreboard.getObjective(objective.getName()); if (oldObjective != null) { - oldObjective.setValue((objective.getValue().isLeft()) ? objective.getValue().getLeft() : ComponentSerializer.toString(objective.getValue().getRight())); + oldObjective.setValue((objective.getValue().isLeft()) ? objective.getValue().getLeft() : con.getChatSerializer().toString(objective.getValue().getRight())); oldObjective.setType(objective.getType().toString()); } break; @@ -242,9 +242,9 @@ public void handle(net.md_5.bungee.protocol.packet.Team team) throws Exception { if (t == null) return; if (team.getMode() == 0 || team.getMode() == 2) { - t.setDisplayName(team.getDisplayName().getLeftOrCompute(ComponentSerializer::toString)); - t.setPrefix(team.getPrefix().getLeftOrCompute(ComponentSerializer::toString)); - t.setSuffix(team.getSuffix().getLeftOrCompute(ComponentSerializer::toString)); + t.setDisplayName(team.getDisplayName().getLeftOrCompute((component) -> con.getChatSerializer().toString( component ))); + t.setPrefix(team.getPrefix().getLeftOrCompute((component) -> con.getChatSerializer().toString( component ))); + t.setSuffix(team.getSuffix().getLeftOrCompute((component) -> con.getChatSerializer().toString( component ))); t.setFriendlyFire(team.getFriendlyFire()); t.setNameTagVisibility(team.getNameTagVisibility().getKey()); if (team.getCollisionRule() != null) @@ -481,7 +481,7 @@ public void handle(PluginMessage pluginMessage) throws Exception { } case "MessageRaw": { final String target = in.readUTF(); - final BaseComponent[] message = ComponentSerializer.parse(in.readUTF()); + final BaseComponent[] message = con.getChatSerializer().parse(in.readUTF()); if (target.equals("ALL")) { bungee.getPlayers().forEach(player -> player.sendMessage(message)); } else { @@ -529,7 +529,7 @@ public void handle(PluginMessage pluginMessage) throws Exception { case "KickPlayerRaw": { final ProxiedPlayer player = bungee.getPlayer(in.readUTF()); if (player != null) { - player.disconnect(ComponentSerializer.parse(in.readUTF())); + player.disconnect(con.getChatSerializer().parse(in.readUTF())); } break; } 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 cddd988c..3ecfff38 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 @@ -175,7 +175,7 @@ public void done(ServerPing result, Throwable error) { } Callback callback = (pingResult, error1) -> { - Gson gson = BungeeCord.getInstance().gson; + Gson gson = PingHandler.gson; unsafe.sendPacket(new StatusResponse(gson.toJson(pingResult.getResponse()))); thisState = State.PING; }; @@ -403,7 +403,7 @@ public void handle(EncryptionResponse encryptResponse) throws Exception { @Override public void done(String result, Throwable error) { if (error == null) { - LoginResult obj = BungeeCord.getInstance().gson.fromJson(result, LoginResult.class); + LoginResult obj = LoginResult.GSON.fromJson(result, LoginResult.class); if (obj != null && obj.getId() != null) { loginProfile = obj; name = obj.getName(); diff --git a/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java index c1412e0e..19dd931c 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java @@ -1,13 +1,16 @@ package net.md_5.bungee.connection; +import com.google.gson.Gson; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import lombok.RequiredArgsConstructor; import net.md_5.bungee.BungeeCord; import net.md_5.bungee.BungeeServerInfo; import net.md_5.bungee.api.Callback; +import net.md_5.bungee.api.Favicon; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.chat.VersionedComponentSerializer; import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.PacketHandler; import net.md_5.bungee.netty.PipelineUtils; @@ -19,11 +22,14 @@ import net.md_5.bungee.protocol.packet.StatusRequest; import net.md_5.bungee.protocol.packet.StatusResponse; import net.md_5.bungee.util.BufUtil; +import net.md_5.bungee.util.PlayerInfoSerializer; import net.md_5.bungee.util.QuietException; @RequiredArgsConstructor public class PingHandler extends PacketHandler { - + static final Gson gson = VersionedComponentSerializer.getDefault().getGson().newBuilder() + .registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer() ) + .registerTypeAdapter( Favicon.class, Favicon.getFaviconTypeAdapter() ).create(); private final ServerInfo target; private final Callback callback; private final int protocol; @@ -58,7 +64,7 @@ public void handle(PacketWrapper packet) throws Exception { @Override @SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR") public void handle(StatusResponse statusResponse) throws Exception { - ServerPing serverPing = BungeeCord.getInstance().gson.fromJson(statusResponse.getResponse(), ServerPing.class); + ServerPing serverPing = gson.fromJson(statusResponse.getResponse(), ServerPing.class); ((BungeeServerInfo) target).cachePing(serverPing); callback.done(serverPing, null); channel.close(); diff --git a/proxy/src/main/java/net/md_5/bungee/connection/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/connection/ServerConnector.java index 0cf7878d..9f175b4d 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/ServerConnector.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/ServerConnector.java @@ -132,7 +132,7 @@ public static void handleLogin(ProxyServer bungee, ChannelWrapper ch, UserConnec serverScoreboard.getObjectives().forEach(objective -> { user.unsafe().sendPacket(new ScoreboardObjective( objective.getName(), - (user.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_13) ? Either.right(ComponentSerializer.deserialize(objective.getValue())) : Either.left(objective.getValue()), + (user.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_13) ? Either.right(user.getChatSerializer().deserialize(objective.getValue())) : Either.left(objective.getValue()), ScoreboardObjective.HealthDisplay.fromString(objective.getType()), (byte) 1, null) ); @@ -253,7 +253,7 @@ public void connected(ChannelWrapper channel) throws Exception { // If we touched any properties, then append them if (properties.length > 0) { - newHost += "\00" + BungeeCord.getInstance().gson.toJson(properties); + newHost += "\00" + LoginResult.GSON.toJson(properties); } copiedHandshake.setHost(newHost); 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 e34472e7..208376ce 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,15 +50,6 @@ 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); - } } From d8639cba01bcae81171845360110b369bddfec8a Mon Sep 17 00:00:00 2001 From: realstresser Date: Tue, 10 Jun 2025 12:19:55 +0300 Subject: [PATCH 3/6] Experimental Upstream update --- .../md_5/bungee/chat/VersionedComponentSerializer.java | 10 ++++++++++ .../java/net/md_5/bungee/api/chat/ComponentsTest.java | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/chat/src/main/java/net/md_5/bungee/chat/VersionedComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/VersionedComponentSerializer.java index bb8dc9d0..9f81be26 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/VersionedComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/VersionedComponentSerializer.java @@ -247,6 +247,16 @@ public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializa { return new TextComponent( json.getAsString() ); } + if ( json.isJsonArray() ) + { + JsonArray arr = json.getAsJsonArray(); + BaseComponent[] components = new BaseComponent[arr.size()]; + for ( int i = 0; i < arr.size(); i++ ) + { + components[i] = deserialize( arr.get( i ), BaseComponent.class, context ); + } + return TextComponent.fromArray( components ); + } JsonObject object = json.getAsJsonObject(); if ( object.has( "translate" ) ) { diff --git a/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java b/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java index 170aacab..aa7a16e4 100644 --- a/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java +++ b/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java @@ -816,4 +816,12 @@ public void testStyleIsEmpty() { .build(); assertFalse(style.isEmpty()); } + @Test + + + public void testArrayParsing() + { + assertEquals( "Outfluencer is very cool bdfg28dhzcathisisacoolcomponent", + ComponentSerializer.deserialize( "[Outfluencer,[\" \",is,[\" very\",\" cool \",[b,dfg28dhz,[c,[a,thisisacoolcomponent]]]]]]" ).toPlainText() ); + } } From 2cab086c35422d91252e19894e89cfad452c3204 Mon Sep 17 00:00:00 2001 From: realstresser Date: Tue, 10 Jun 2025 12:38:39 +0300 Subject: [PATCH 4/6] Entity Mapping & chat Fixes --- .../net/md_5/bungee/protocol/packet/ClientChat.java | 10 ++++++++++ .../md_5/bungee/protocol/packet/ClientCommand.java | 11 +++++++++++ .../java/net/md_5/bungee/entitymap/EntityMap.java | 13 +++++++++++++ .../net/md_5/bungee/entitymap/EntityMap_1_16_2.java | 6 ++++++ 4 files changed, 40 insertions(+) diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientChat.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientChat.java index a47c4883..01d84430 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientChat.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientChat.java @@ -20,6 +20,7 @@ public class ClientChat extends DefinedPacket { private boolean signedPreview; private ChatChain chain; private SeenMessages seenMessages; + private byte checksum; @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -45,6 +46,10 @@ public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protoco chain = new ChatChain(); chain.read(buf, direction, protocolVersion); } + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 ) + { + checksum = buf.readByte(); + } } @Override @@ -68,6 +73,11 @@ public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protoc } else if (protocolVersion >= ProtocolConstants.MINECRAFT_1_19_1) { chain.write(buf, direction, protocolVersion); } + + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 ) + { + buf.writeByte( checksum ); + } } @Override 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 ee2056d6..cee7aebf 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 @@ -24,6 +24,7 @@ public class ClientCommand extends DefinedPacket { private boolean signedPreview; private ChatChain chain; private SeenMessages seenMessages; + private byte checksum; @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -57,8 +58,13 @@ public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protoco chain = new ChatChain(); chain.read(buf, direction, protocolVersion); } + + if (protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5) { + checksum = buf.readByte(); + } } + @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { writeString(command, buf); @@ -83,6 +89,11 @@ public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protoc } else if (protocolVersion >= ProtocolConstants.MINECRAFT_1_19_1) { chain.write(buf, direction, protocolVersion); } + + if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 ) + { + buf.writeByte( checksum ); + } } @Override 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 20050d6f..d1a3e211 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 @@ -78,6 +78,19 @@ public static EntityMap getEntityMap(int version) { case ProtocolConstants.MINECRAFT_1_19_4: case ProtocolConstants.MINECRAFT_1_20: return EntityMap_1_16_2.INSTANCE_1_19_4; + case ProtocolConstants.MINECRAFT_1_20_2: + return EntityMap_1_16_2.INSTANCE_1_20_2; + 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; + 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; + case ProtocolConstants.MINECRAFT_1_21_5: + return EntityMap_1_16_2.INSTANCE_1_21_5; default: return null; 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 4d1a42f7..9a63004e 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 @@ -15,6 +15,12 @@ final class EntityMap_1_16_2 extends EntityMap { static final EntityMap_1_16_2 INSTANCE_1_19 = new EntityMap_1_16_2(0x02, 0x2F); static final EntityMap_1_16_2 INSTANCE_1_19_1 = new EntityMap_1_16_2(0x02, 0x30); static final EntityMap_1_16_2 INSTANCE_1_19_4 = new EntityMap_1_16_2(0x03, 0x30); + 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 ); + 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 8fb6b42de7c2be3551ee9d0eca0a21c8735b797f Mon Sep 17 00:00:00 2001 From: realstresser Date: Wed, 11 Jun 2025 13:43:56 +0300 Subject: [PATCH 5/6] Remove Unnecessary Entity Maps --- .../java/net/md_5/bungee/entitymap/EntityMap.java | 13 ------------- .../net/md_5/bungee/entitymap/EntityMap_1_16_2.java | 6 ------ 2 files changed, 19 deletions(-) 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 d1a3e211..20050d6f 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 @@ -78,19 +78,6 @@ public static EntityMap getEntityMap(int version) { case ProtocolConstants.MINECRAFT_1_19_4: case ProtocolConstants.MINECRAFT_1_20: return EntityMap_1_16_2.INSTANCE_1_19_4; - case ProtocolConstants.MINECRAFT_1_20_2: - return EntityMap_1_16_2.INSTANCE_1_20_2; - 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; - 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; - case ProtocolConstants.MINECRAFT_1_21_5: - return EntityMap_1_16_2.INSTANCE_1_21_5; default: return null; 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 9a63004e..4d1a42f7 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 @@ -15,12 +15,6 @@ final class EntityMap_1_16_2 extends EntityMap { static final EntityMap_1_16_2 INSTANCE_1_19 = new EntityMap_1_16_2(0x02, 0x2F); static final EntityMap_1_16_2 INSTANCE_1_19_1 = new EntityMap_1_16_2(0x02, 0x30); static final EntityMap_1_16_2 INSTANCE_1_19_4 = new EntityMap_1_16_2(0x03, 0x30); - 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 ); - 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 04ca402b9e14e0d7c4e762c906917fd3f0fd4e44 Mon Sep 17 00:00:00 2001 From: realstresser Date: Sat, 14 Jun 2025 14:34:32 +0300 Subject: [PATCH 6/6] Socket Backend Removal --- .../java/ir/xenoncommunity/XenonCore.java | 43 ------------------- .../xenoncommunity/utils/Configuration.java | 4 +- proxy/src/main/resources/XenonCord.yml | 3 -- 3 files changed, 1 insertion(+), 49 deletions(-) diff --git a/proxy/src/main/java/ir/xenoncommunity/XenonCore.java b/proxy/src/main/java/ir/xenoncommunity/XenonCore.java index 528d307d..8cce5f03 100644 --- a/proxy/src/main/java/ir/xenoncommunity/XenonCore.java +++ b/proxy/src/main/java/ir/xenoncommunity/XenonCore.java @@ -77,10 +77,6 @@ public XenonCore(boolean isDev) { public void init(long startTime) { ClassHelper.registerModules(); getLogger().info("Successfully booted! Loading the proxy server with plugins took: {}ms", System.currentTimeMillis() - startTime); - - if (configData.isSocket_backend()) { - XenonCore.instance.getTaskManager().async(this::initBackend); - } } /** @@ -114,43 +110,4 @@ public void logdebugerror(String msg) { if (configData.isDebug()) logger.error(msg); } - - /** - * Initializes command execution backend support - */ - @SneakyThrows - private void initBackend() { - if (!getConfiguration().getSocketBackendSecretFile().exists()) - getConfiguration().getSocketBackendSecretFile().createNewFile(); - - if (getConfiguration().getSocketBackendSecretFile().length() == 0) { - getLogger().info(Colorize.console("&c[NOTICE] &rsocket-backend-secret.txt is empty")); - getLogger().info(Colorize.console("&c[NOTICE] &rXenonCord &fwill generate a secret inside this file")); - getLogger().info(Colorize.console("&c[NOTICE] &rplease configure your plugins/XenonBanBackend with this secret, to avoid issues.")); - @Cleanup final BufferedWriter writer = new BufferedWriter(new FileWriter(getConfiguration().getSocketBackendSecretFile())); - writer.write(new String(Util.randomAlphanumericSequence(12), StandardCharsets.UTF_8)); - } - @Cleanup final BufferedReader reader = new BufferedReader(new FileReader(getConfiguration().getSocketBackendSecretFile())); - final String secret = reader.readLine(); - @Cleanup final ServerSocket serverSocket = new ServerSocket(20019, 50, InetAddress.getByName("127.0.0.1")); - - while (true) { - @Cleanup final Socket socket = serverSocket.accept(); - @Cleanup final BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); - - String req; - while ((req = br.readLine()) != null) { - final String reqWithoutSecret = req.replaceAll(secret, "").startsWith(" ") ? req.replaceAll(secret, "").substring(1) : req.replaceAll(secret, ""); - getLogger().info("Received a request from socket backend, request: " + reqWithoutSecret); - if (!req.contains(secret)) { - getLogger().info(Colorize.console("&c[NOTICE] &rBlocked a request without secret via command exec backend. please be careful with what's happening.")); - break; - } - // remove secret & space from request - XenonCore.instance.getBungeeInstance().getPluginManager().dispatchCommand( - XenonCore.instance.getBungeeInstance().getConsole(), reqWithoutSecret - ); - } - } - } } diff --git a/proxy/src/main/java/ir/xenoncommunity/utils/Configuration.java b/proxy/src/main/java/ir/xenoncommunity/utils/Configuration.java index 4e9b321e..9a0dbdb8 100644 --- a/proxy/src/main/java/ir/xenoncommunity/utils/Configuration.java +++ b/proxy/src/main/java/ir/xenoncommunity/utils/Configuration.java @@ -16,13 +16,11 @@ public class Configuration { private final File configFile; private final File bstatsFile; - private final File socketBackendSecretFile; private final Logger logger; public Configuration() { this.bstatsFile = new File("bstats", "bstats.txt"); this.configFile = new File("XenonCord.yml"); - this.socketBackendSecretFile = new File("socket-backend-secret.txt"); this.logger = XenonCore.instance.getLogger(); } @@ -63,7 +61,7 @@ public ConfigData init() { public static class ConfigData { private String cannot_execute_as_console_message, unknown_option_message, xenoncord_permission, reload_permission, reload_message, reload_complete_message; - private boolean debug, socket_backend; + private boolean debug; private Modules modules; } diff --git a/proxy/src/main/resources/XenonCord.yml b/proxy/src/main/resources/XenonCord.yml index 4aecf9f1..ef6041c8 100644 --- a/proxy/src/main/resources/XenonCord.yml +++ b/proxy/src/main/resources/XenonCord.yml @@ -6,9 +6,6 @@ cannot_execute_as_console_message: "&b&lXenonCord &cCannot execute this command unknown_option_message: "&b&lXenonCord &cUnknown option, available: OPTIONS" # XenonCord command perm xenoncord_permission: "xenoncord.xenoncord" -# Backend support. -# this is required by: XenonBanBackend, or any plugin with the need of command execution without plugin message -socket_backend: true # "xenoncord reload" Command Permission reload_permission: "xenoncord.xenoncord.reload" # Reload Command Message