diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/score/BlankScoreFormat.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/BlankScoreFormat.java
new file mode 100644
index 0000000000..3cd073ddd0
--- /dev/null
+++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/BlankScoreFormat.java
@@ -0,0 +1,39 @@
+/*
+ * This file is part of packetevents - https://github.com/retrooper/packetevents
+ * Copyright (C) 2024 retrooper and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.github.retrooper.packetevents.protocol.score;
+
+import net.kyori.adventure.text.Component;
+
+public final class BlankScoreFormat implements ScoreFormat {
+
+ public static final BlankScoreFormat INSTANCE = new BlankScoreFormat();
+
+ private BlankScoreFormat() {
+ }
+
+ @Override
+ public Component format(int score) {
+ return Component.empty();
+ }
+
+ @Override
+ public ScoreFormatType getType() {
+ return ScoreFormatTypes.BLANK;
+ }
+}
diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/score/FixedScoreFormat.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/FixedScoreFormat.java
new file mode 100644
index 0000000000..7c70f8e3e5
--- /dev/null
+++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/FixedScoreFormat.java
@@ -0,0 +1,44 @@
+/*
+ * This file is part of packetevents - https://github.com/retrooper/packetevents
+ * Copyright (C) 2024 retrooper and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.github.retrooper.packetevents.protocol.score;
+
+import net.kyori.adventure.text.Component;
+
+public final class FixedScoreFormat implements ScoreFormat {
+
+ private final Component value;
+
+ public FixedScoreFormat(Component value) {
+ this.value = value;
+ }
+
+ @Override
+ public ScoreFormatType getType() {
+ return ScoreFormatTypes.FIXED;
+ }
+
+ @Override
+ public Component format(int score) {
+ return this.value;
+ }
+
+ public Component getValue() {
+ return this.value;
+ }
+}
diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/score/ScoreFormat.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/ScoreFormat.java
new file mode 100644
index 0000000000..35e0000af0
--- /dev/null
+++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/ScoreFormat.java
@@ -0,0 +1,43 @@
+/*
+ * This file is part of packetevents - https://github.com/retrooper/packetevents
+ * Copyright (C) 2024 retrooper and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.github.retrooper.packetevents.protocol.score;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.Style;
+import org.jetbrains.annotations.ApiStatus;
+
+@ApiStatus.NonExtendable
+public interface ScoreFormat {
+
+ static BlankScoreFormat blankScore() {
+ return BlankScoreFormat.INSTANCE;
+ }
+
+ static StyledScoreFormat styledScore(Style style) {
+ return new StyledScoreFormat(style);
+ }
+
+ static FixedScoreFormat fixedScore(Component value) {
+ return new FixedScoreFormat(value);
+ }
+
+ Component format(int score);
+
+ ScoreFormatType getType();
+}
diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/score/ScoreFormatType.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/ScoreFormatType.java
new file mode 100644
index 0000000000..8e542118ad
--- /dev/null
+++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/ScoreFormatType.java
@@ -0,0 +1,29 @@
+/*
+ * This file is part of packetevents - https://github.com/retrooper/packetevents
+ * Copyright (C) 2024 retrooper and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.github.retrooper.packetevents.protocol.score;
+
+import com.github.retrooper.packetevents.protocol.mapper.StaticMappedEntity;
+import com.github.retrooper.packetevents.wrapper.PacketWrapper;
+
+public interface ScoreFormatType extends StaticMappedEntity {
+
+ ScoreFormat read(PacketWrapper> wrapper);
+
+ void write(PacketWrapper> wrapper, ScoreFormat format);
+}
diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/score/ScoreFormatTypes.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/ScoreFormatTypes.java
new file mode 100644
index 0000000000..3bf6fd9fd5
--- /dev/null
+++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/ScoreFormatTypes.java
@@ -0,0 +1,106 @@
+/*
+ * This file is part of packetevents - https://github.com/retrooper/packetevents
+ * Copyright (C) 2024 retrooper and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.github.retrooper.packetevents.protocol.score;
+
+import com.github.retrooper.packetevents.protocol.player.ClientVersion;
+import com.github.retrooper.packetevents.resources.ResourceLocation;
+import com.github.retrooper.packetevents.wrapper.PacketWrapper;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+public final class ScoreFormatTypes {
+
+ private static final Map SCORE_FORMAT_TYPE_MAP = new HashMap<>();
+ private static final Map SCORE_FORMAT_TYPE_ID_MAP = new HashMap<>();
+
+ public static final ScoreFormatType BLANK = define(0, "blank", BlankScoreFormat.class,
+ wrapper -> ScoreFormat.blankScore(),
+ (wrapper, format) -> { /**/ });
+ public static final ScoreFormatType STYLED = define(1, "styled", StyledScoreFormat.class,
+ wrapper -> ScoreFormat.styledScore(wrapper.readStyle()),
+ (wrapper, format) -> wrapper.writeStyle(format.getStyle()));
+ public static final ScoreFormatType FIXED = define(2, "fixed", FixedScoreFormat.class,
+ wrapper -> ScoreFormat.fixedScore(wrapper.readComponent()),
+ (wrapper, format) -> wrapper.writeComponent(format.getValue()));
+
+ private ScoreFormatTypes() {
+ }
+
+ public static ScoreFormat read(PacketWrapper> wrapper) {
+ int formatTypeId = wrapper.readVarInt();
+ ScoreFormatType formatType = getById(wrapper.getServerVersion().toClientVersion(), formatTypeId);
+ if (formatType == null) {
+ throw new NullPointerException("Can't resolve format type " + formatTypeId);
+ }
+ return formatType.read(wrapper);
+ }
+
+ public static void write(PacketWrapper> wrapper, ScoreFormat format) {
+ int formatTypeId = format.getType().getId(wrapper.getServerVersion().toClientVersion());
+ wrapper.writeVarInt(formatTypeId);
+ format.getType().write(wrapper, format);
+ }
+
+ public static ScoreFormatType define(int id, String name,
+ Class formatClass,
+ Function, T> reader,
+ BiConsumer, T> writer) {
+ ResourceLocation location = new ResourceLocation(name);
+ ScoreFormatType type = new ScoreFormatType() {
+ @Override
+ public ScoreFormat read(PacketWrapper> wrapper) {
+ return reader.apply(wrapper);
+ }
+
+ @Override
+ public void write(PacketWrapper> wrapper, ScoreFormat format) {
+ writer.accept(wrapper, formatClass.cast(format));
+ }
+
+ @Override
+ public ResourceLocation getName() {
+ return location;
+ }
+
+ @Override
+ public int getId() {
+ return id;
+ }
+ };
+ SCORE_FORMAT_TYPE_MAP.put(location.toString(), type);
+ SCORE_FORMAT_TYPE_ID_MAP.put((byte) id, type);
+ return type;
+ }
+
+ public static @Nullable ScoreFormatType getById(ClientVersion version, int id) {
+ return SCORE_FORMAT_TYPE_ID_MAP.get((byte) id);
+ }
+
+ public static @Nullable ScoreFormatType getByName(String name) {
+ return getByName(new ResourceLocation(name));
+ }
+
+ public static @Nullable ScoreFormatType getByName(ResourceLocation name) {
+ return SCORE_FORMAT_TYPE_MAP.get(name.toString());
+ }
+}
diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/score/StyledScoreFormat.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/StyledScoreFormat.java
new file mode 100644
index 0000000000..27746f5e27
--- /dev/null
+++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/score/StyledScoreFormat.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of packetevents - https://github.com/retrooper/packetevents
+ * Copyright (C) 2024 retrooper and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.github.retrooper.packetevents.protocol.score;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.Style;
+
+public final class StyledScoreFormat implements ScoreFormat {
+
+ private final Style style;
+
+ public StyledScoreFormat(Style style) {
+ this.style = style;
+ }
+
+ @Override
+ public Component format(int score) {
+ return Component.text()
+ .content(Integer.toString(score))
+ .style(this.style)
+ .build();
+ }
+
+ @Override
+ public ScoreFormatType getType() {
+ return ScoreFormatTypes.STYLED;
+ }
+
+ public Style getStyle() {
+ return this.style;
+ }
+}
diff --git a/api/src/main/java/com/github/retrooper/packetevents/util/adventure/AdventureNBTSerialization.java b/api/src/main/java/com/github/retrooper/packetevents/util/adventure/AdventureNBTSerialization.java
index d836ef344a..d8684520ea 100644
--- a/api/src/main/java/com/github/retrooper/packetevents/util/adventure/AdventureNBTSerialization.java
+++ b/api/src/main/java/com/github/retrooper/packetevents/util/adventure/AdventureNBTSerialization.java
@@ -1,6 +1,6 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
- * Copyright (C) 2023 retrooper and contributors
+ * Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
package com.github.retrooper.packetevents.util.adventure;
import com.github.retrooper.packetevents.netty.buffer.ByteBufInputStream;
+import com.github.retrooper.packetevents.netty.buffer.ByteBufOutputStream;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.BlockNBTComponent;
@@ -35,8 +36,10 @@
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
+import net.kyori.adventure.text.format.TextDecoration.State;
import net.kyori.adventure.text.serializer.json.JSONComponentConstants;
import java.io.DataInput;
@@ -56,6 +59,7 @@
import static net.kyori.adventure.text.Component.storageNBT;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.Component.translatable;
+import static net.kyori.adventure.text.event.ClickEvent.clickEvent;
import static net.kyori.adventure.text.event.HoverEvent.ShowEntity.showEntity;
import static net.kyori.adventure.text.event.HoverEvent.ShowItem.showItem;
import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.CLICK_EVENT;
@@ -148,6 +152,445 @@ private static void requireState(boolean state) {
}
}
+ public static Style readStyle(Object byteBuf) throws IOException {
+ return readStyle(new ByteBufInputStream(byteBuf));
+ }
+
+ public static Style readStyle(DataInput input) throws IOException {
+ TagType type = resolveNbtType(input.readByte());
+ return readStyle(input, type);
+ }
+
+ private static Style readStyle(DataInput input, TagType rootType) throws IOException {
+ return readStyle(input, rootType, 0);
+ }
+
+ private static Style readStyle(DataInput input, TagType rootType, int depth) throws IOException {
+ if (depth > DEPTH_LIMIT) {
+ throw new RuntimeException("Depth limit reached while decoding style: " + depth + " > " + DEPTH_LIMIT);
+ }
+ if (rootType != TagType.COMPOUND) {
+ throw new RuntimeException("Unsupported nbt tag type for style: " + rootType);
+ }
+
+ Style.Builder style = null;
+
+ // read until end
+ TagType type;
+ while ((type = resolveNbtType(input.readByte())) != TagType.END) {
+ String key = input.readUTF();
+
+ if (style == null) {
+ style = Style.style();
+ }
+ readStyle(style, key, type, input, depth);
+ }
+
+ return style == null ? Style.empty() : style.build();
+ }
+
+ @SuppressWarnings({"PatternValidation", "unchecked"}) // Key and HoverEvent
+ private static void readStyle(Style.Builder style, String key, TagType type,
+ DataInput input, int depth) throws IOException {
+ switch (key) {
+ case FONT:
+ requireType(type, TagType.STRING);
+ style.font(key(input.readUTF()));
+ break;
+ case COLOR:
+ requireType(type, TagType.STRING);
+ style.color(parseColor(input.readUTF()));
+ break;
+ case BOLD:
+ requireType(type, TagType.BYTE);
+ style.decoration(TextDecoration.BOLD, State.byBoolean(input.readBoolean()));
+ break;
+ case ITALIC:
+ requireType(type, TagType.BYTE);
+ style.decoration(TextDecoration.ITALIC, State.byBoolean(input.readBoolean()));
+ break;
+ case UNDERLINED:
+ requireType(type, TagType.BYTE);
+ style.decoration(TextDecoration.UNDERLINED, State.byBoolean(input.readBoolean()));
+ break;
+ case STRIKETHROUGH:
+ requireType(type, TagType.BYTE);
+ style.decoration(TextDecoration.STRIKETHROUGH, State.byBoolean(input.readBoolean()));
+ break;
+ case OBFUSCATED:
+ requireType(type, TagType.BYTE);
+ style.decoration(TextDecoration.OBFUSCATED, State.byBoolean(input.readBoolean()));
+ break;
+ case INSERTION:
+ requireType(type, TagType.STRING);
+ style.insertion(input.readUTF());
+ break;
+ case CLICK_EVENT:
+ requireType(type, TagType.COMPOUND);
+
+ ClickEvent.Action clickEventAction = null;
+ String clickEventValue = null;
+
+ TagType clickType;
+ while ((clickType = resolveNbtType(input.readByte())) != TagType.END) {
+ String clickKey = input.readUTF();
+ switch (clickKey) {
+ case CLICK_EVENT_ACTION:
+ requireType(clickType, TagType.STRING);
+ requireState(clickEventAction == null);
+
+ String actionId = input.readUTF();
+ switch (actionId) {
+ case OPEN_URL:
+ clickEventAction = ClickEvent.Action.OPEN_URL;
+ break;
+ case RUN_COMMAND:
+ clickEventAction = ClickEvent.Action.RUN_COMMAND;
+ break;
+ case SUGGEST_COMMAND:
+ clickEventAction = ClickEvent.Action.SUGGEST_COMMAND;
+ break;
+ case CHANGE_PAGE:
+ clickEventAction = ClickEvent.Action.CHANGE_PAGE;
+ break;
+ case COPY_TO_CLIPBOARD:
+ clickEventAction = ClickEvent.Action.COPY_TO_CLIPBOARD;
+ break;
+ default:
+ throw new IllegalStateException("Illegal click event action read: '" + actionId + "'");
+ }
+ break;
+ case CLICK_EVENT_VALUE:
+ requireType(clickType, TagType.STRING);
+ requireState(clickEventValue == null);
+ clickEventValue = input.readUTF();
+ break;
+ default:
+ throw new IllegalStateException("Illegal click event nbt key read: '" + clickKey + "'");
+ }
+ }
+ requireState(clickEventAction != null && clickEventValue != null);
+ style.clickEvent(clickEvent(clickEventAction, clickEventValue));
+ break;
+ case HOVER_EVENT:
+ requireType(type, TagType.COMPOUND);
+
+ HoverEvent.Action> hoverEventAction = null;
+ Object hoverEventContents = null;
+
+ TagType hoverType;
+ while ((hoverType = resolveNbtType(input.readByte())) != TagType.END) {
+ String hoverKey = input.readUTF();
+ switch (hoverKey) {
+ case HOVER_EVENT_ACTION:
+ requireType(hoverType, TagType.STRING);
+ requireState(hoverEventAction == null);
+
+ String actionId = input.readUTF();
+ switch (actionId) {
+ case SHOW_TEXT:
+ hoverEventAction = HoverEvent.Action.SHOW_TEXT;
+ break;
+ case SHOW_ITEM:
+ hoverEventAction = HoverEvent.Action.SHOW_ITEM;
+ break;
+ case SHOW_ENTITY:
+ hoverEventAction = HoverEvent.Action.SHOW_ENTITY;
+ break;
+ default:
+ throw new IllegalStateException("Illegal hover event action read: '" + actionId + "'");
+ }
+ break;
+ case HOVER_EVENT_CONTENTS:
+ requireState(hoverEventContents == null);
+ requireState(hoverEventAction != null);
+
+ switch (hoverEventAction.toString()) {
+ case SHOW_TEXT:
+ requireComponentType(hoverType);
+ hoverEventContents = readComponent(input, hoverType, depth + 1);
+ break;
+ case SHOW_ITEM:
+ if (hoverType == TagType.STRING) {
+ String itemId = input.readUTF();
+ hoverEventContents = showItem(key(itemId), 1);
+ } else {
+ requireType(hoverType, TagType.COMPOUND);
+
+ String itemId = null;
+ int count = 1;
+ String tag = null;
+
+ TagType itemType;
+ while ((itemType = resolveNbtType(input.readByte())) != TagType.END) {
+ String itemKey = input.readUTF();
+ switch (itemKey) {
+ case ITEM_ID:
+ requireType(itemType, TagType.STRING);
+ requireState(itemId == null);
+ itemId = input.readUTF();
+ break;
+ case ITEM_COUNT:
+ requireType(itemType, TagType.INT);
+ count = input.readInt();
+ break;
+ case ITEM_TAG:
+ requireType(itemType, TagType.STRING);
+ tag = input.readUTF();
+ break;
+ }
+ }
+
+ requireState(itemId != null);
+ hoverEventContents = showItem(key(itemId), count,
+ tag == null ? null : binaryTagHolder(tag));
+ }
+ break;
+ case SHOW_ENTITY:
+ requireType(hoverType, TagType.COMPOUND);
+
+ String entityType = null;
+ UUID entityId = null;
+ Component entityName = null;
+
+ TagType itemType;
+ while ((itemType = resolveNbtType(input.readByte())) != TagType.END) {
+ String itemKey = input.readUTF();
+ switch (itemKey) {
+ case ENTITY_TYPE:
+ requireType(itemType, TagType.STRING);
+ requireState(entityType == null);
+ entityType = input.readUTF();
+ break;
+ case ENTITY_ID:
+ requireType(itemType, TagType.INT_ARRAY);
+ requireState(entityId == null);
+ entityId = readUniqueId(input);
+ break;
+ case ENTITY_NAME:
+ requireComponentType(itemType);
+ requireState(entityName == null);
+ entityName = readComponent(input, itemType, depth + 1);
+ break;
+ }
+ }
+
+ requireState(entityType != null && entityId != null);
+ hoverEventContents = showEntity(key(entityType), entityId, entityName);
+ break;
+ }
+ break;
+ default:
+ throw new IllegalStateException("Illegal hover event nbt key read: '" + hoverKey + "'");
+ }
+ }
+ requireState(hoverEventContents != null);
+
+ // this is not unchecked, as it will 100% never fail - validated while reading
+ HoverEvent.Action super Object> unsafeAction = (HoverEvent.Action super Object>) hoverEventAction;
+ style.hoverEvent(HoverEvent.hoverEvent(unsafeAction, hoverEventContents));
+ break;
+ default:
+ throw new IllegalStateException("Illegal component nbt key read: '" + key + "'");
+ }
+ }
+
+ public static void writeStyle(Object byteBuf, Style style) throws IOException {
+ writeStyle(new ByteBufOutputStream(byteBuf), style);
+ }
+
+ public static void writeStyle(DataOutput output, Style style) throws IOException {
+ TagType tagType = TagType.COMPOUND;
+ output.writeByte(tagType.getId());
+ writeStyle(output, style, tagType);
+ output.writeByte(TagType.END.getId()); // ends style tag
+ }
+
+ private static void writeStyle(DataOutput output, Style style, TagType rootType) throws IOException {
+ if (rootType != TagType.COMPOUND) {
+ throw new UnsupportedEncodingException();
+ }
+ if (style.isEmpty()) {
+ return;
+ }
+
+ // font
+ Key font = style.font();
+ if (font != null) {
+ output.writeByte(TagType.STRING.getId());
+ output.writeUTF(FONT);
+ output.writeUTF(font.asString());
+ }
+
+ // color
+ TextColor color = style.color();
+ if (color != null) {
+ output.writeByte(TagType.STRING.getId());
+ output.writeUTF(COLOR);
+ output.writeUTF(stringifyColor(color));
+ }
+
+ // bold
+ State bold = style.decoration(TextDecoration.BOLD);
+ if (bold != State.NOT_SET) {
+ output.writeByte(TagType.BYTE.getId());
+ output.writeUTF(BOLD);
+ output.writeBoolean(bold == State.TRUE);
+ }
+ // italic
+ State italic = style.decoration(TextDecoration.ITALIC);
+ if (italic != State.NOT_SET) {
+ output.writeByte(TagType.BYTE.getId());
+ output.writeUTF(ITALIC);
+ output.writeBoolean(italic == State.TRUE);
+ }
+ // underlined
+ State underlined = style.decoration(TextDecoration.UNDERLINED);
+ if (underlined != State.NOT_SET) {
+ output.writeByte(TagType.BYTE.getId());
+ output.writeUTF(UNDERLINED);
+ output.writeBoolean(underlined == State.TRUE);
+ }
+ // strikethrough
+ State strikethrough = style.decoration(TextDecoration.STRIKETHROUGH);
+ if (strikethrough != State.NOT_SET) {
+ output.writeByte(TagType.BYTE.getId());
+ output.writeUTF(STRIKETHROUGH);
+ output.writeBoolean(strikethrough == State.TRUE);
+ }
+ // obfuscated
+ State obfuscated = style.decoration(TextDecoration.OBFUSCATED);
+ if (obfuscated != State.NOT_SET) {
+ output.writeByte(TagType.BYTE.getId());
+ output.writeUTF(OBFUSCATED);
+ output.writeBoolean(obfuscated == State.TRUE);
+ }
+
+ // insertion
+ String insertion = style.insertion();
+ if (insertion != null) {
+ output.writeByte(TagType.STRING.getId());
+ output.writeUTF(INSERTION);
+ output.writeUTF(insertion);
+ }
+
+ // click event
+ ClickEvent clickEvent = style.clickEvent();
+ if (clickEvent != null) {
+ // nested compound
+ output.writeByte(TagType.COMPOUND.getId());
+ output.writeUTF(CLICK_EVENT);
+
+ // click event action
+ ClickEvent.Action action = clickEvent.action();
+ output.writeByte(TagType.STRING.getId());
+ output.writeUTF(CLICK_EVENT_ACTION);
+ output.writeUTF(action.toString());
+
+ // click event value
+ String value = clickEvent.value();
+ output.writeByte(TagType.STRING.getId());
+ output.writeUTF(CLICK_EVENT_VALUE);
+ output.writeUTF(value);
+
+ output.writeByte(TagType.END.getId()); // ends compound
+ }
+
+ // hover event
+ HoverEvent> hoverEvent = style.hoverEvent();
+ if (hoverEvent != null) {
+ // nested compound
+ output.writeByte(TagType.COMPOUND.getId());
+ output.writeUTF(HOVER_EVENT);
+
+ // hover event action
+ HoverEvent.Action> action = hoverEvent.action();
+ output.writeByte(TagType.STRING.getId());
+ output.writeUTF(HOVER_EVENT_ACTION);
+ output.writeUTF(action.toString());
+
+ // hover event contents
+ switch (action.toString()) {
+ case SHOW_TEXT:
+ Component text = (Component) hoverEvent.value();
+ TagType textTagType = getComponentTagType(text);
+ output.writeByte(textTagType.getId());
+ output.writeUTF(HOVER_EVENT_CONTENTS);
+ writeComponent(output, text, textTagType);
+ break;
+ case SHOW_ITEM:
+ HoverEvent.ShowItem item = (HoverEvent.ShowItem) hoverEvent.value();
+ Key itemId = item.item();
+ int count = item.count();
+ BinaryTagHolder nbt = item.nbt();
+
+ if (count == 1 && nbt == null) {
+ output.writeByte(TagType.STRING.getId());
+ output.writeUTF(HOVER_EVENT_CONTENTS);
+ output.writeUTF(itemId.asString());
+ } else {
+ // nested compound
+ output.writeByte(TagType.COMPOUND.getId());
+ output.writeUTF(HOVER_EVENT_CONTENTS);
+
+ // item id
+ output.writeByte(TagType.STRING.getId());
+ output.writeUTF(ITEM_ID);
+ output.writeUTF(itemId.asString());
+
+ // item count
+ if (count != 1) {
+ output.writeByte(TagType.INT.getId());
+ output.writeUTF(ITEM_COUNT);
+ output.writeInt(count);
+ }
+
+ // item nbt
+ if (nbt != null) {
+ output.writeByte(TagType.STRING.getId());
+ output.writeUTF(ITEM_TAG);
+ output.writeUTF(nbt.string());
+ }
+
+ output.writeByte(TagType.END.getId()); // ends compound
+ }
+ break;
+ case SHOW_ENTITY:
+ HoverEvent.ShowEntity entity = (HoverEvent.ShowEntity) hoverEvent.value();
+ Key entityType = entity.type();
+ UUID entityId = entity.id();
+ Component entityName = entity.name();
+
+ // nested compound
+ output.writeByte(TagType.COMPOUND.getId());
+ output.writeUTF(HOVER_EVENT_CONTENTS);
+
+ // entity type
+ output.writeByte(TagType.STRING.getId());
+ output.writeUTF(ENTITY_TYPE);
+ output.writeUTF(entityType.asString());
+
+ // entity uuid
+ output.writeByte(TagType.INT_ARRAY.getId());
+ output.writeUTF(ENTITY_ID);
+ writeUniqueId(output, entityId);
+
+ // entity name
+ if (entityName != null) {
+ TagType nameTagType = getComponentTagType(entityName);
+ output.writeByte(nameTagType.getId());
+ output.writeUTF(ENTITY_NAME);
+ writeComponent(output, entityName, nameTagType);
+ }
+
+ output.writeByte(TagType.END.getId()); // ends compound
+ break;
+ }
+
+ output.writeByte(TagType.END.getId()); // ends compound
+ }
+ }
+
public static Component readComponent(Object byteBuf) throws IOException {
return readComponent(new ByteBufInputStream(byteBuf));
}
@@ -161,7 +604,7 @@ private static Component readComponent(DataInput input, TagType rootType) throws
return readComponent(input, rootType, 0);
}
- @SuppressWarnings({"PatternValidation", "unchecked"}) // Key and HoverEvent
+ @SuppressWarnings({"PatternValidation"}) // Key
private static Component readComponent(DataInput input, TagType rootType, int depth) throws IOException {
if (depth > DEPTH_LIMIT) {
throw new RuntimeException("Depth limit reached while decoding component: " + depth + " > " + DEPTH_LIMIT);
@@ -191,19 +634,7 @@ private static Component readComponent(DataInput input, TagType rootType, int de
String nbtStorage = null;
Component separator = null;
- // style
- String font = null;
- String color = null;
- TextDecoration.State obfuscated = TextDecoration.State.NOT_SET;
- TextDecoration.State bold = TextDecoration.State.NOT_SET;
- TextDecoration.State strikethrough = TextDecoration.State.NOT_SET;
- TextDecoration.State underlined = TextDecoration.State.NOT_SET;
- TextDecoration.State italic = TextDecoration.State.NOT_SET;
- String insertion = null;
- ClickEvent.Action clickEventAction = null;
- String clickEventValue = null;
- HoverEvent.Action> hoverEventAction = null;
- Object hoverEventContents = null;
+ Style.Builder style = null;
// read until end
TagType type;
@@ -301,204 +732,12 @@ private static Component readComponent(DataInput input, TagType rootType, int de
requireState(separator == null);
separator = readComponent(input, type, depth + 1);
break;
- case FONT:
- requireType(type, TagType.STRING);
- requireState(font == null);
- font = input.readUTF();
- break;
- case COLOR:
- requireType(type, TagType.STRING);
- requireState(color == null);
- color = input.readUTF();
- break;
- case BOLD:
- requireType(type, TagType.BYTE);
- requireState(bold == TextDecoration.State.NOT_SET);
- bold = TextDecoration.State.byBoolean(input.readBoolean());
- break;
- case ITALIC:
- requireType(type, TagType.BYTE);
- requireState(italic == TextDecoration.State.NOT_SET);
- italic = TextDecoration.State.byBoolean(input.readBoolean());
- break;
- case UNDERLINED:
- requireType(type, TagType.BYTE);
- requireState(underlined == TextDecoration.State.NOT_SET);
- underlined = TextDecoration.State.byBoolean(input.readBoolean());
- break;
- case STRIKETHROUGH:
- requireType(type, TagType.BYTE);
- requireState(strikethrough == TextDecoration.State.NOT_SET);
- strikethrough = TextDecoration.State.byBoolean(input.readBoolean());
- break;
- case OBFUSCATED:
- requireType(type, TagType.BYTE);
- requireState(obfuscated == TextDecoration.State.NOT_SET);
- obfuscated = TextDecoration.State.byBoolean(input.readBoolean());
- break;
- case INSERTION:
- requireType(type, TagType.STRING);
- requireState(insertion == null);
- insertion = input.readUTF();
- break;
- case CLICK_EVENT:
- requireType(type, TagType.COMPOUND);
- requireState(clickEventAction == null && clickEventValue == null);
-
- TagType clickType;
- while ((clickType = resolveNbtType(input.readByte())) != TagType.END) {
- String clickKey = input.readUTF();
- switch (clickKey) {
- case CLICK_EVENT_ACTION:
- requireType(clickType, TagType.STRING);
- requireState(clickEventAction == null);
-
- String actionId = input.readUTF();
- switch (actionId) {
- case OPEN_URL:
- clickEventAction = ClickEvent.Action.OPEN_URL;
- break;
- case RUN_COMMAND:
- clickEventAction = ClickEvent.Action.RUN_COMMAND;
- break;
- case SUGGEST_COMMAND:
- clickEventAction = ClickEvent.Action.SUGGEST_COMMAND;
- break;
- case CHANGE_PAGE:
- clickEventAction = ClickEvent.Action.CHANGE_PAGE;
- break;
- case COPY_TO_CLIPBOARD:
- clickEventAction = ClickEvent.Action.COPY_TO_CLIPBOARD;
- break;
- default:
- throw new IllegalStateException("Illegal click event action read: '" + actionId + "'");
- }
- break;
- case CLICK_EVENT_VALUE:
- requireType(clickType, TagType.STRING);
- requireState(clickEventValue == null);
- clickEventValue = input.readUTF();
- break;
- default:
- throw new IllegalStateException("Illegal click event nbt key read: '" + clickKey + "'");
- }
- }
- requireState(clickEventAction != null && clickEventValue != null);
- break;
- case HOVER_EVENT:
- requireType(type, TagType.COMPOUND);
- requireState(hoverEventAction == null);
-
- TagType hoverType;
- while ((hoverType = resolveNbtType(input.readByte())) != TagType.END) {
- String hoverKey = input.readUTF();
- switch (hoverKey) {
- case HOVER_EVENT_ACTION:
- requireType(hoverType, TagType.STRING);
- requireState(hoverEventAction == null);
-
- String actionId = input.readUTF();
- switch (actionId) {
- case SHOW_TEXT:
- hoverEventAction = HoverEvent.Action.SHOW_TEXT;
- break;
- case SHOW_ITEM:
- hoverEventAction = HoverEvent.Action.SHOW_ITEM;
- break;
- case SHOW_ENTITY:
- hoverEventAction = HoverEvent.Action.SHOW_ENTITY;
- break;
- default:
- throw new IllegalStateException("Illegal hover event action read: '" + actionId + "'");
- }
- break;
- case HOVER_EVENT_CONTENTS:
- requireState(hoverEventContents == null);
- requireState(hoverEventAction != null);
-
- switch (hoverEventAction.toString()) {
- case SHOW_TEXT:
- requireComponentType(hoverType);
- hoverEventContents = readComponent(input, hoverType, depth + 1);
- break;
- case SHOW_ITEM:
- if (hoverType == TagType.STRING) {
- String itemId = input.readUTF();
- hoverEventContents = showItem(key(itemId), 1);
- } else {
- requireType(hoverType, TagType.COMPOUND);
-
- String itemId = null;
- int count = 1;
- String tag = null;
-
- TagType itemType;
- while ((itemType = resolveNbtType(input.readByte())) != TagType.END) {
- String itemKey = input.readUTF();
- switch (itemKey) {
- case ITEM_ID:
- requireType(itemType, TagType.STRING);
- requireState(itemId == null);
- itemId = input.readUTF();
- break;
- case ITEM_COUNT:
- requireType(itemType, TagType.INT);
- count = input.readInt();
- break;
- case ITEM_TAG:
- requireType(itemType, TagType.STRING);
- tag = input.readUTF();
- break;
- }
- }
-
- requireState(itemId != null);
- hoverEventContents = showItem(key(itemId), count,
- tag == null ? null : binaryTagHolder(tag));
- }
- break;
- case SHOW_ENTITY:
- requireType(hoverType, TagType.COMPOUND);
-
- String entityType = null;
- UUID entityId = null;
- Component entityName = null;
-
- TagType itemType;
- while ((itemType = resolveNbtType(input.readByte())) != TagType.END) {
- String itemKey = input.readUTF();
- switch (itemKey) {
- case ENTITY_TYPE:
- requireType(itemType, TagType.STRING);
- requireState(entityType == null);
- entityType = input.readUTF();
- break;
- case ENTITY_ID:
- requireType(itemType, TagType.INT_ARRAY);
- requireState(entityId == null);
- entityId = readUniqueId(input);
- break;
- case ENTITY_NAME:
- requireComponentType(itemType);
- requireState(entityName == null);
- entityName = readComponent(input, itemType, depth + 1);
- break;
- }
- }
-
- requireState(entityType != null && entityId != null);
- hoverEventContents = showEntity(key(entityType), entityId, entityName);
- break;
- }
- break;
- default:
- throw new IllegalStateException("Illegal hover event nbt key read: '" + hoverKey + "'");
- }
+ default:
+ if (style == null) {
+ style = Style.style();
}
- requireState(hoverEventContents != null);
+ readStyle(style, key, type, input, depth);
break;
- default:
- throw new IllegalStateException("Illegal component nbt key read: '" + key + "'");
}
}
@@ -538,37 +777,8 @@ private static Component readComponent(DataInput input, TagType rootType, int de
throw new IllegalStateException("Illegal nbt component, component type could not be determined");
}
- if (font != null) {
- builder.font(key(font));
- }
- if (color != null) {
- builder.color(parseColor(color));
- }
- if (obfuscated != TextDecoration.State.NOT_SET) {
- builder.decoration(TextDecoration.OBFUSCATED, obfuscated);
- }
- if (bold != TextDecoration.State.NOT_SET) {
- builder.decoration(TextDecoration.BOLD, bold);
- }
- if (strikethrough != TextDecoration.State.NOT_SET) {
- builder.decoration(TextDecoration.STRIKETHROUGH, strikethrough);
- }
- if (underlined != TextDecoration.State.NOT_SET) {
- builder.decoration(TextDecoration.UNDERLINED, underlined);
- }
- if (italic != TextDecoration.State.NOT_SET) {
- builder.decoration(TextDecoration.ITALIC, italic);
- }
- if (insertion != null) {
- builder.insertion(insertion);
- }
- if (clickEventAction != null) {
- builder.clickEvent(ClickEvent.clickEvent(clickEventAction, clickEventValue));
- }
- if (hoverEventAction != null) {
- // this is not unchecked, as it will 100% never fail - validated while reading
- HoverEvent.Action super Object> unsafeAction = (HoverEvent.Action super Object>) hoverEventAction;
- builder.hoverEvent(HoverEvent.hoverEvent(unsafeAction, hoverEventContents));
+ if (style != null) {
+ builder.style(style.build());
}
if (extra != null) {
@@ -577,6 +787,10 @@ private static Component readComponent(DataInput input, TagType rootType, int de
return builder.build();
}
+ public static void writeComponent(Object byteBuf, Component component) throws IOException {
+ writeComponent(new ByteBufOutputStream(byteBuf), component);
+ }
+
public static void writeComponent(DataOutput output, Component component) throws IOException {
TagType tagType = getComponentTagType(component);
output.writeByte(tagType.getId());
@@ -706,181 +920,7 @@ private static void writeComponent(DataOutput output, Component component, TagTy
}
if (component.hasStyling()) {
- // font
- Key font = component.font();
- if (font != null) {
- output.writeByte(TagType.STRING.getId());
- output.writeUTF(FONT);
- output.writeUTF(font.asString());
- }
-
- // color
- TextColor color = component.color();
- if (color != null) {
- output.writeByte(TagType.STRING.getId());
- output.writeUTF(COLOR);
- output.writeUTF(stringifyColor(color));
- }
-
- // bold
- TextDecoration.State bold = component.decoration(TextDecoration.BOLD);
- if (bold != TextDecoration.State.NOT_SET) {
- output.writeByte(TagType.BYTE.getId());
- output.writeUTF(BOLD);
- output.writeBoolean(bold == TextDecoration.State.TRUE);
- }
- // italic
- TextDecoration.State italic = component.decoration(TextDecoration.ITALIC);
- if (italic != TextDecoration.State.NOT_SET) {
- output.writeByte(TagType.BYTE.getId());
- output.writeUTF(ITALIC);
- output.writeBoolean(italic == TextDecoration.State.TRUE);
- }
- // underlined
- TextDecoration.State underlined = component.decoration(TextDecoration.UNDERLINED);
- if (underlined != TextDecoration.State.NOT_SET) {
- output.writeByte(TagType.BYTE.getId());
- output.writeUTF(UNDERLINED);
- output.writeBoolean(underlined == TextDecoration.State.TRUE);
- }
- // strikethrough
- TextDecoration.State strikethrough = component.decoration(TextDecoration.STRIKETHROUGH);
- if (strikethrough != TextDecoration.State.NOT_SET) {
- output.writeByte(TagType.BYTE.getId());
- output.writeUTF(STRIKETHROUGH);
- output.writeBoolean(strikethrough == TextDecoration.State.TRUE);
- }
- // obfuscated
- TextDecoration.State obfuscated = component.decoration(TextDecoration.OBFUSCATED);
- if (obfuscated != TextDecoration.State.NOT_SET) {
- output.writeByte(TagType.BYTE.getId());
- output.writeUTF(OBFUSCATED);
- output.writeBoolean(obfuscated == TextDecoration.State.TRUE);
- }
-
- // insertion
- String insertion = component.insertion();
- if (insertion != null) {
- output.writeByte(TagType.STRING.getId());
- output.writeUTF(INSERTION);
- output.writeUTF(insertion);
- }
-
- // click event
- ClickEvent clickEvent = component.clickEvent();
- if (clickEvent != null) {
- // nested compound
- output.writeByte(TagType.COMPOUND.getId());
- output.writeUTF(CLICK_EVENT);
-
- // click event action
- ClickEvent.Action action = clickEvent.action();
- output.writeByte(TagType.STRING.getId());
- output.writeUTF(CLICK_EVENT_ACTION);
- output.writeUTF(action.toString());
-
- // click event value
- String value = clickEvent.value();
- output.writeByte(TagType.STRING.getId());
- output.writeUTF(CLICK_EVENT_VALUE);
- output.writeUTF(value);
-
- output.writeByte(TagType.END.getId()); // ends compound
- }
-
- // hover event
- HoverEvent> hoverEvent = component.hoverEvent();
- if (hoverEvent != null) {
- // nested compound
- output.writeByte(TagType.COMPOUND.getId());
- output.writeUTF(HOVER_EVENT);
-
- // hover event action
- HoverEvent.Action> action = hoverEvent.action();
- output.writeByte(TagType.STRING.getId());
- output.writeUTF(HOVER_EVENT_ACTION);
- output.writeUTF(action.toString());
-
- // hover event contents
- switch (action.toString()) {
- case SHOW_TEXT:
- Component text = (Component) hoverEvent.value();
- TagType textTagType = getComponentTagType(text);
- output.writeByte(textTagType.getId());
- output.writeUTF(HOVER_EVENT_CONTENTS);
- writeComponent(output, text, textTagType);
- break;
- case SHOW_ITEM:
- HoverEvent.ShowItem item = (HoverEvent.ShowItem) hoverEvent.value();
- Key itemId = item.item();
- int count = item.count();
- BinaryTagHolder nbt = item.nbt();
-
- if (count == 1 && nbt == null) {
- output.writeByte(TagType.STRING.getId());
- output.writeUTF(HOVER_EVENT_CONTENTS);
- output.writeUTF(itemId.asString());
- } else {
- // nested compound
- output.writeByte(TagType.COMPOUND.getId());
- output.writeUTF(HOVER_EVENT_CONTENTS);
-
- // item id
- output.writeByte(TagType.STRING.getId());
- output.writeUTF(ITEM_ID);
- output.writeUTF(itemId.asString());
-
- // item count
- if (count != 1) {
- output.writeByte(TagType.INT.getId());
- output.writeUTF(ITEM_COUNT);
- output.writeInt(count);
- }
-
- // item nbt
- if (nbt != null) {
- output.writeByte(TagType.STRING.getId());
- output.writeUTF(ITEM_TAG);
- output.writeUTF(nbt.string());
- }
-
- output.writeByte(TagType.END.getId()); // ends compound
- }
- break;
- case SHOW_ENTITY:
- HoverEvent.ShowEntity entity = (HoverEvent.ShowEntity) hoverEvent.value();
- Key entityType = entity.type();
- UUID entityId = entity.id();
- Component entityName = entity.name();
-
- // nested compound
- output.writeByte(TagType.COMPOUND.getId());
- output.writeUTF(HOVER_EVENT_CONTENTS);
-
- // entity type
- output.writeByte(TagType.STRING.getId());
- output.writeUTF(ENTITY_TYPE);
- output.writeUTF(entityType.asString());
-
- // entity uuid
- output.writeByte(TagType.INT_ARRAY.getId());
- output.writeUTF(ENTITY_ID);
- writeUniqueId(output, entityId);
-
- // entity name
- if (entityName != null) {
- TagType nameTagType = getComponentTagType(entityName);
- output.writeByte(nameTagType.getId());
- output.writeUTF(ENTITY_NAME);
- writeComponent(output, entityName, nameTagType);
- }
-
- output.writeByte(TagType.END.getId()); // ends compound
- break;
- }
-
- output.writeByte(TagType.END.getId()); // ends compound
- }
+ writeStyle(output, component.style(), TagType.COMPOUND);
}
// component children
diff --git a/api/src/main/java/com/github/retrooper/packetevents/wrapper/PacketWrapper.java b/api/src/main/java/com/github/retrooper/packetevents/wrapper/PacketWrapper.java
index fea4b1ae75..61f99e8d10 100644
--- a/api/src/main/java/com/github/retrooper/packetevents/wrapper/PacketWrapper.java
+++ b/api/src/main/java/com/github/retrooper/packetevents/wrapper/PacketWrapper.java
@@ -25,7 +25,6 @@
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.manager.server.VersionComparison;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
-import com.github.retrooper.packetevents.netty.buffer.ByteBufOutputStream;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import com.github.retrooper.packetevents.protocol.PacketSide;
import com.github.retrooper.packetevents.protocol.chat.ChatType;
@@ -68,6 +67,7 @@
import com.github.retrooper.packetevents.util.crypto.SaltSignature;
import com.github.retrooper.packetevents.util.crypto.SignatureData;
import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.Style;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.ApiStatus.Experimental;
import org.jetbrains.annotations.NotNull;
@@ -542,7 +542,7 @@ public void writeComponent(Component component) {
public void writeComponentAsNBT(Component component) {
try {
- AdventureNBTSerialization.writeComponent(new ByteBufOutputStream(this.buffer), component);
+ AdventureNBTSerialization.writeComponent(this.buffer, component);
} catch (IOException exception) {
throw new IllegalStateException(exception);
}
@@ -553,6 +553,22 @@ public void writeComponentAsJSON(Component component) {
this.writeString(jsonString, this.getMaxMessageLength());
}
+ public Style readStyle() {
+ try {
+ return AdventureNBTSerialization.readStyle(this.buffer);
+ } catch (IOException exception) {
+ throw new IllegalStateException(exception);
+ }
+ }
+
+ public void writeStyle(Style style) {
+ try {
+ AdventureNBTSerialization.writeStyle(this.buffer, style);
+ } catch (IOException exception) {
+ throw new IllegalStateException(exception);
+ }
+ }
+
public ResourceLocation readIdentifier(int maxLen) {
return new ResourceLocation(readString(maxLen));
}
diff --git a/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerDisplayScoreboard.java b/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerDisplayScoreboard.java
index ef13aff126..db45857c71 100644
--- a/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerDisplayScoreboard.java
+++ b/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerDisplayScoreboard.java
@@ -44,7 +44,11 @@ public void read() {
} else {
position = readByte();
}
- scoreName = readString(16);
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_18)) {
+ scoreName = readString();
+ } else {
+ scoreName = readString(16);
+ }
}
@Override
@@ -54,7 +58,11 @@ public void write() {
} else {
writeByte(position);
}
- writeString(scoreName, 16);
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_18)) {
+ writeString(scoreName); // length limit removed
+ } else {
+ writeString(scoreName, 16);
+ }
}
@Override
diff --git a/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerScoreboardObjective.java b/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerScoreboardObjective.java
index 60dc955b14..f7d298a102 100644
--- a/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerScoreboardObjective.java
+++ b/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerScoreboardObjective.java
@@ -1,6 +1,6 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
- * Copyright (C) 2022 retrooper and contributors
+ * Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,22 +21,27 @@
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.github.retrooper.packetevents.protocol.score.ScoreFormat;
+import com.github.retrooper.packetevents.protocol.score.ScoreFormatTypes;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
public class WrapperPlayServerScoreboardObjective extends PacketWrapper {
+
private String name;
private ObjectiveMode mode;
private Component displayName;
private @Nullable RenderType renderType;
+ private @Nullable ScoreFormat scoreFormat;
public WrapperPlayServerScoreboardObjective(PacketSendEvent event) {
super(event);
}
- public WrapperPlayServerScoreboardObjective(String name, ObjectiveMode mode, Component displayName, @Nullable RenderType renderType) {
+ public WrapperPlayServerScoreboardObjective(String name, ObjectiveMode mode, Component displayName,
+ @Nullable RenderType renderType) {
super(PacketType.Play.Server.SCOREBOARD_OBJECTIVE);
this.name = name;
this.mode = mode;
@@ -46,11 +51,16 @@ public WrapperPlayServerScoreboardObjective(String name, ObjectiveMode mode, Com
@Override
public void read() {
- name = readString();
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_18)) {
+ name = readString();
+ } else {
+ name = readString(16);
+ }
mode = ObjectiveMode.getById(readByte());
if (mode != ObjectiveMode.CREATE && mode != ObjectiveMode.UPDATE) {
displayName = Component.empty();
renderType = RenderType.INTEGER;
+ scoreFormat = null;
} else {
if (serverVersion.isOlderThan(ServerVersion.V_1_13)) {
displayName = AdventureSerializer.fromLegacyFormat(readString(32));
@@ -58,13 +68,18 @@ public void read() {
} else {
displayName = readComponent();
renderType = RenderType.getById(readVarInt());
+ scoreFormat = readOptional(ScoreFormatTypes::read);
}
}
}
@Override
public void write() {
- writeString(name);
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_18)) {
+ writeString(name);
+ } else {
+ writeString(name, 16);
+ }
writeByte((byte) mode.ordinal());
if (this.mode == ObjectiveMode.CREATE || this.mode == ObjectiveMode.UPDATE) {
if (serverVersion.isOlderThan(ServerVersion.V_1_13)) {
@@ -81,6 +96,7 @@ public void write() {
} else {
writeVarInt(RenderType.INTEGER.ordinal());
}
+ writeOptional(scoreFormat, ScoreFormatTypes::write);
}
}
}
@@ -91,6 +107,7 @@ public void copy(WrapperPlayServerScoreboardObjective wrapper) {
mode = wrapper.mode;
displayName = wrapper.displayName;
renderType = wrapper.renderType;
+ scoreFormat = wrapper.scoreFormat;
}
public String getName() {
@@ -125,6 +142,14 @@ public void setRenderType(@Nullable RenderType renderType) {
this.renderType = renderType;
}
+ public @Nullable ScoreFormat getScoreFormat() {
+ return this.scoreFormat;
+ }
+
+ public void setScoreFormat(@Nullable ScoreFormat scoreFormat) {
+ this.scoreFormat = scoreFormat;
+ }
+
public enum ObjectiveMode {
CREATE,
REMOVE,
@@ -168,4 +193,4 @@ public static RenderType getById(int id) {
return VALUES[id];
}
}
-}
\ No newline at end of file
+}
diff --git a/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerUpdateScore.java b/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerUpdateScore.java
index 47555559a5..bac5ba733c 100644
--- a/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerUpdateScore.java
+++ b/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerUpdateScore.java
@@ -1,6 +1,6 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
- * Copyright (C) 2022 retrooper and contributors
+ * Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,22 +21,22 @@
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.github.retrooper.packetevents.protocol.score.ScoreFormat;
+import com.github.retrooper.packetevents.protocol.score.ScoreFormatTypes;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
+import net.kyori.adventure.text.Component;
+import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class WrapperPlayServerUpdateScore extends PacketWrapper {
+
private String entityName;
private Action action;
private String objectiveName;
private Optional value;
-
- public enum Action {
- CREATE_OR_UPDATE_ITEM,
- REMOVE_ITEM;
-
- public static final Action[] VALUES = values();
- }
+ private @Nullable Component entityDisplayName;
+ private @Nullable ScoreFormat scoreFormat;
public WrapperPlayServerUpdateScore(PacketSendEvent event) {
super(event);
@@ -50,9 +50,26 @@ public WrapperPlayServerUpdateScore(String entityName, Action action, String obj
this.value = value;
}
+ public WrapperPlayServerUpdateScore(String entityName, Action action, String objectiveName, int value,
+ @Nullable Component entityDisplayName, @Nullable ScoreFormat scoreFormat) {
+ super(PacketType.Play.Server.UPDATE_SCORE);
+ this.entityName = entityName;
+ this.action = action;
+ this.objectiveName = objectiveName;
+ this.value = Optional.of(value);
+ this.entityDisplayName = entityDisplayName;
+ this.scoreFormat = scoreFormat;
+ }
+
@Override
public void read() {
- if (serverVersion == ServerVersion.V_1_7_10) {
+ if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_20_3)) {
+ this.entityName = this.readString();
+ this.objectiveName = this.readString();
+ this.value = Optional.of(this.readVarInt());
+ this.entityDisplayName = this.readOptional(PacketWrapper::readComponent);
+ this.scoreFormat = this.readOptional(ScoreFormatTypes::read);
+ } else if (this.serverVersion == ServerVersion.V_1_7_10) {
entityName = readString(16);
action = Action.VALUES[readByte()];
if (action != Action.REMOVE_ITEM) {
@@ -63,9 +80,17 @@ public void read() {
value = Optional.empty();
}
} else {
- entityName = readString(40);
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_18)) {
+ entityName = readString();
+ } else {
+ entityName = readString(40);
+ }
action = Action.VALUES[readByte()];
- objectiveName = readString(16);
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_18)) {
+ objectiveName = readString();
+ } else {
+ objectiveName = readString(16);
+ }
if (action != Action.REMOVE_ITEM) {
value = Optional.of(readVarInt());
} else {
@@ -77,7 +102,13 @@ public void read() {
@Override
public void write() {
- if (serverVersion == ServerVersion.V_1_7_10) {
+ if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_20_3)) {
+ this.writeString(this.entityName);
+ this.writeString(this.objectiveName);
+ this.writeVarInt(this.value.orElse(0));
+ this.writeOptional(this.entityDisplayName, PacketWrapper::writeComponent);
+ this.writeOptional(this.scoreFormat, ScoreFormatTypes::write);
+ } else if (this.serverVersion == ServerVersion.V_1_7_10) {
writeString(entityName, 16);
writeByte(action.ordinal());
if (action != Action.REMOVE_ITEM) {
@@ -88,9 +119,17 @@ public void write() {
value = Optional.empty();
}
} else {
- writeString(entityName, 40);
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_18)) {
+ writeString(entityName);
+ } else {
+ writeString(entityName, 40);
+ }
writeByte(action.ordinal());
- writeString(objectiveName, 16);
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_18)) {
+ writeString(objectiveName);
+ } else {
+ writeString(objectiveName, 16);
+ }
if (action != Action.REMOVE_ITEM) {
writeVarInt(value.orElse(-1));
}
@@ -104,6 +143,8 @@ public void copy(WrapperPlayServerUpdateScore wrapper) {
action = wrapper.action;
objectiveName = wrapper.objectiveName;
value = wrapper.value;
+ entityDisplayName = wrapper.entityDisplayName;
+ scoreFormat = wrapper.scoreFormat;
}
public String getEntityName() {
@@ -130,11 +171,41 @@ public void setObjectiveName(String objectiveName) {
this.objectiveName = objectiveName;
}
+ /**
+ * Always present for >=1.20.3
+ */
public Optional getValue() {
return value;
}
+ /**
+ * Always present for >=1.20.3
+ */
public void setValue(Optional value) {
this.value = value;
}
+
+ public @Nullable Component getEntityDisplayName() {
+ return this.entityDisplayName;
+ }
+
+ public void setEntityDisplayName(@Nullable Component entityDisplayName) {
+ this.entityDisplayName = entityDisplayName;
+ }
+
+ public @Nullable ScoreFormat getScoreFormat() {
+ return this.scoreFormat;
+ }
+
+ public void setScoreFormat(@Nullable ScoreFormat scoreFormat) {
+ this.scoreFormat = scoreFormat;
+ }
+
+ public enum Action {
+
+ CREATE_OR_UPDATE_ITEM,
+ REMOVE_ITEM;
+
+ public static final Action[] VALUES = values();
+ }
}