Compare commits

...
96 Commits
Author SHA1 Message Date
retrooper 69cbe1e405 Revert "Refactor TaskWrapper into interface and implementation classes" 2025-07-08 19:39:19 +02:00
retrooper 2b0ec87da6 Merge pull request #1250 from ShortyDev/dev
Refactor TaskWrapper into interface and implementation classes
2025-07-08 14:46:55 +02:00
retrooper 08f95b8036 Merge branch '2.0' of https://github.com/retrooper/packetevents into 2.0 2025-07-08 14:34:42 +02:00
retrooper a15ad3a8d7 patch: typo, put actual code in comment; also removed previous sponsor of computational servers; 2025-07-08 14:34:35 +02:00
retrooper fef7f983b0 Merge pull request #1267 from iiAhmedYT/patch-1
Allow getting/setting json in ChatMessage
2025-07-08 14:27:57 +02:00
retrooper 71eae683fb Merge remote-tracking branch 'origin/2.0' into 2.0 2025-07-08 14:21:22 +02:00
retrooper f692f41025 patch: fix the plugin disabling on unsupported version check 2025-07-08 14:21:09 +02:00
retrooper 3b4acde6f8 Merge pull request #1273 from ineanto/2.0
feat: add RemoteChatSession getter
2025-07-08 14:13:17 +02:00
retrooper 6b14595582 patch: import optimization in spigot reflection util 2025-07-08 13:40:58 +02:00
retrooper 50c2a69449 patch: packetevents disables itself when running on an unsupported Minecraft version;
changing "packetevents 2.0 failed to inject" message;
2025-07-07 19:32:23 +02:00
booky 1b5479f04f Merge pull request #1269 from HSGamer/2.0
correctly parse payload in WrapperCommonClientCustomClickAction
2025-06-29 17:20:13 +02:00
booky10 ac0041ff52 Read nbt end tags as null for custom click action packet 2025-06-29 17:13:25 +02:00
ineanto 21226e6ac9 feat: add remote chat session getter via reflection 2025-06-28 21:41:46 +02:00
hsgamer b14f5f5031 correctly parse payload in WrapperCommonClientCustomClickAction
the payload is either an NBT or NBTEnd, but no boolean byte. So we don't have to check optional
close #1268
2025-06-27 13:22:11 +07:00
Ahmed Waleed 6ff9f8c401 Allow getting/setting json in ChatMessage 2025-06-27 02:04:00 +03:00
retrooper 77abb65bab Merge pull request #1264 from booky10/feat/1.21.7-support
1.21.7 support
2025-06-26 08:50:49 +02:00
booky10andSteaf23 001f6b9fd7 Fix InputControl encoding
Co-authored-by: Steaf23 <steafmobile@gmail.com>
2025-06-26 03:44:27 +02:00
booky10 0395378726 Update registries for 1.21.7 2025-06-26 03:09:57 +02:00
booky10 31fe7a89d9 Add 1.21.7 protocol constants 2025-06-26 03:03:38 +02:00
booky d8b47495e4 Merge pull request #1258 from dawon/bugfix/fix-waypoint-info-types
Fix WaypointInfo types
2025-06-20 14:36:29 +02:00
Jakub Zacek cba4603ad8 Fix WaypointInfo types 2025-06-20 13:55:48 +02:00
booky10 f37c74d0d9 Convert PacketEventsEncoder to simple ChannelOutboundHandlerAdapter
This allows cancelling packets without throwing exceptions around

Fixes https://github.com/retrooper/packetevents/issues/1256
2025-06-19 19:34:27 +02:00
booky10 2085dfb84b Skip generating useless CancelPacketException stacktrace 2025-06-19 19:33:40 +02:00
booky10 cdad13d5c5 Fix WrapperPlayClientEntityAction actions for 1.21.6+
Fixes https://github.com/retrooper/packetevents/issues/1254
2025-06-19 19:12:35 +02:00
retrooper 95b50a44e9 Merge pull request #1255 from Cyramek/direction-fix 2025-06-19 17:30:31 +02:00
cyramek 78f630b5c1 fix direction vector being different from vanilla 2025-06-19 16:58:27 +02:00
retrooper 08d000ea82 Merge pull request #1234 from huanmeng-qwq/impl/advancements
Implement WrapperPlayServerUpdateAdvancements
2025-06-19 12:19:42 +02:00
booky10 96c81a69ae Fix compatibility with netty v4.0 (e.g. used by minecraft 1.8) 2025-06-19 02:58:21 +02:00
booky10 2667431ec9 Don't resolve entity id getter method by name 2025-06-18 23:19:23 +02:00
booky10 21ce0f4d34 Small cleanup to other advancement classes 2025-06-18 23:00:40 +02:00
booky10 21c1a7dfa8 Clean up Advancement class
- Removes version parameter from read method
- Convert comments to javadoc
- Add constructor for constructing Advancement without criteria list parameter
- Reorder getters/setters
2025-06-18 23:00:25 +02:00
booky10 518a995e8e Add abstraction class for criterion value in AdvancementProgress 2025-06-18 22:58:49 +02:00
booky10 e20e0854ab Remove unused ClientAsset 2025-06-18 22:58:28 +02:00
booky10 7c3cde51f0 Reformat WrapperPlayServerUpdateAdvancements
- Convert comments to javadoc
- Reorder getter/setter
- Don't make "showAdvancements" field nullable
2025-06-18 22:33:28 +02:00
booky10 cc18a7ad08 Don't register debug listener 2025-06-18 22:28:49 +02:00
booky10 35fa135e9e Fix formatting of Advancement/AdvancementDisplay 2025-06-18 22:26:52 +02:00
booky10 c68fe76156 Merge branch '2.0' into impl/advancements 2025-06-18 22:23:57 +02:00
retrooper 80846cfea2 Merge pull request #1247 from booky10/feat/1.21.6-support
1.21.6 support
2025-06-18 22:03:28 +02:00
retrooper 1a1b03c1f5 i'm not religious, but god bless aboookyrica 2025-06-18 22:01:26 +02:00
booky10 c411a1925b Also support void promises for sponge 2025-06-18 21:46:08 +02:00
booky10 d8b4f5b1b6 Account for void promises while writing on spigot platform 2025-06-18 21:37:16 +02:00
booky10 486c9a493a Add hydration block state property 2025-06-18 21:02:04 +02:00
booky10 b1ed98f53d Fix fabric connection error 2025-06-18 21:01:53 +02:00
ShortyDev ca8ca782fc refactor TaskWrapper into interface and implementation classes 2025-06-18 20:10:49 +02:00
booky10 c75f039c99 Update adventure to v4.23.0 2025-06-18 19:08:38 +02:00
booky10 c1324fe7fe Update adventure to v4.22.0 2025-06-17 18:06:52 +02:00
booky10 781d4a7c82 Add new ui sound category 2025-06-17 18:06:52 +02:00
retrooper 590479adda patch: ensure consistent naming of #getGameMode and #setGameMode 2025-06-17 16:48:37 +02:00
retrooper df2c50ccfc patch: ensure code can compile 2025-06-17 13:50:49 +02:00
retrooper f5b60ea3f2 feat: changed WrapperPlayServerUpdateAdvancements design; updated it to 1.21.5; relocated advancement classes to advancements folder; (requires some testing still) 2025-06-17 13:45:56 +02:00
booky10 c95194f147 Fix test errors 2025-06-16 05:16:03 +02:00
booky10 56a643f8d0 Implement item attribute display for 1.21.6 2025-06-16 04:54:42 +02:00
booky10 7daed688e0 Update equippable component to 1.21.6 2025-06-16 04:28:11 +02:00
booky10 f3782dffe1 Finish implement dialog bodies 2025-06-16 04:21:09 +02:00
booky10 b06063ebf7 Finish implement dialog inputs 2025-06-16 03:53:28 +02:00
booky10 6cb3d301d8 Finish implementing dialog types 2025-06-16 03:16:39 +02:00
booky10 1aab738dd5 Implement dialog actions 2025-06-16 02:44:07 +02:00
booky10 0aafad8b58 Some more dialog progress 2025-06-13 17:04:21 +02:00
huanmeng-qwq 910d77252e fix: Advancement#read & write 2025-06-13 21:59:52 +08:00
retrooper 524e6ca16d patch: added copyright notice to update advancements wrapper 2025-06-13 15:15:58 +02:00
retrooper 1aa88d05bf feat: more progress to WrapperPlayServerUpdateAdvancements; updating to 1.21.5 (incomplete) 2025-06-13 15:11:40 +02:00
huanmeng-qwq 70271f3694 update 2025-06-13 11:52:41 +08:00
booky10 62201ae3fa Start implementing dialogs 2025-06-11 17:55:36 +02:00
booky10 8ce56fc346 Quick fabric module update 2025-06-11 16:45:44 +02:00
booky10 8029fd1b4e Update block and item tags for 1.21.6 2025-06-11 16:39:27 +02:00
booky10 de19712dbb Add missing WrapperConfigServerPing 2025-06-11 16:33:12 +02:00
booky10 1d41037bdd Implement WrapperPlayClientChangeGameMode 2025-06-11 16:26:53 +02:00
booky10 a98b8c5604 Implement WrapperPlayServerWaypoint 2025-06-11 16:24:47 +02:00
booky10 7d5d2682b7 Implement waypoint api 2025-06-11 16:21:36 +02:00
booky10 2b5f8027f0 Implement new clientbound show dialog packet wrappers partially 2025-06-11 15:45:09 +02:00
booky10 1e37351b48 Implement new clientbound clear dialog packet wrappers 2025-06-11 15:40:49 +02:00
booky10 70c8aad74e Implement new serverbound custom click action packet wrappers 2025-06-11 15:38:03 +02:00
booky10 5527546f05 Update existing packet wrappers to 1.21.6 2025-06-11 15:18:56 +02:00
booky10 56671341ae Update packet type mappings for 1.21.6 2025-06-11 15:03:11 +02:00
booky10 41ea688ff3 Update registries for 1.21.6 2025-06-11 14:54:16 +02:00
booky10 004195144d Implement new cloud height for dimension types 2025-06-11 14:22:55 +02:00
booky10 962aefba17 Add new 1.21.6 version constants 2025-06-11 14:13:15 +02:00
huanmeng-qwq 88ec7a583d remove final modifier 2025-05-28 10:36:09 +08:00
huanmeng-qwq 2303d362d7 add setter 2025-05-28 10:32:34 +08:00
retrooper da0e95fa03 Merge pull request #1238 from booky10/fix/enchantment-refs
Fix issues with enchantment loading
2025-05-25 11:56:07 +02:00
booky10 c98bfad8b8 Merge branch '2.0' into fix/enchantment-refs 2025-05-25 04:59:33 +02:00
booky10 3dfb8389dc Bump gradle wrapper validation action to v4
I hope this fixes these random errors while building
2025-05-25 04:57:03 +02:00
booky10 7c74dd2e05 Allocate new buffer when re-encoding clientbound buffers with a limited capacity
Bungee now limits the maximum capacity of bytebufs, presumably because of https://github.com/SpigotMC/BungeeCord/commit/1279cca971b2d32b6bd75103d3a555fbbd629392

This now allocates a completely new bytebuf to ensure we don't run into the maximum bytebuf capacity

Fixes https://github.com/retrooper/packetevents/issues/1227
2025-05-25 04:56:21 +02:00
booky10 05eabbf667 Decode enchantment type exclusive set as mapped entity ref set on registry synchronization 2025-05-25 04:22:12 +02:00
booky10 d1cafe2bcc Resolve self-reference registry after adding entries to registry
Only relevant to enchantments for now
2025-05-25 03:57:49 +02:00
booky10 26078fcb59 Implement MappedEntityRefSet 2025-05-25 03:44:03 +02:00
booky10 3d602b9f96 Fix item reading/writing for 1.13/1.13.1
Just for future reference, so I don't have to remap + decompile these jars again:
* 1.12.2 logic: https://haste.pinofett.de/degt0ejhbr.java
* 1.13/1.13.1 logic: https://haste.pinofett.de/omr31l9mv9.java
* 1.13.2 logic: https://haste.pinofett.de/iul6y7vvny.java
2025-05-25 02:57:11 +02:00
huanmeng-qwq df4f9e425e update 2025-05-24 14:44:41 +08:00
huanmeng-qwq efb9766388 convert time to Optional 2025-05-23 18:17:27 +08:00
huanmeng-qwq ab0ad68d43 update 2025-05-23 18:12:38 +08:00
huanmeng-qwq 1be8db5ff4 update 2025-05-23 18:09:36 +08:00
huanmeng-qwq 1f68ee1f46 convert showAdvances to Optional 2025-05-23 18:00:13 +08:00
huanmeng-qwq e7c6ae4726 Implement WrapperPlayServerStatistics 2025-05-23 17:48:09 +08:00
huanmeng-qwq 5db1671895 Implement WrapperPlayServerUpdateAdvancements 2025-05-23 17:47:49 +08:00
retrooper 023a0da619 packetevents description change 2025-05-16 15:46:40 +02:00
retrooper 51863fa435 bump version to 2.8.1 snapshot 2025-05-15 15:20:19 +02:00
191 changed files with 216620 additions and 443 deletions

No files matched your search

+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v4
- name: Validate gradle wrapper
uses: gradle/actions/wrapper-validation@v3
uses: gradle/actions/wrapper-validation@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
+1 -3
View File
@@ -14,8 +14,8 @@
- [Getting Started](https://docs.packetevents.com/getting-started)
- [JavaDocs](https://javadocs.packetevents.com)
- [Releases](https://github.com/retrooper/packetevents/releases/)
- [GitHub](https://github.com/retrooper/packetevents/releases/)
- [Modrinth](https://modrinth.com/plugin/packetevents)
- [GitHub](https://github.com/retrooper/packetevents/releases/)
- [SpigotMC](https://www.spigotmc.org/resources/packetevents-api.80279/)
- [Development Builds](https://ci.codemc.io/job/retrooper/job/packetevents)
- [Statistics](https://bstats.org/plugin/bukkit/packetevents/11327)
@@ -28,8 +28,6 @@
<a href="https://www.ej-technologies.com"><img src="https://www.ej-technologies.com/images/product_banners/jprofiler_small.png"><br>
<a href="https://github.com/radstevee"><img src="https://github.com/radstevee.png" width="50px" alt="radstevee"/></a><br>
<h3>Credits</h3>
<i>Here are some projects that we are heavily inspired by, thus we have integrated</i>\
@@ -2,4 +2,9 @@ package com.github.retrooper.packetevents.exception;
public class CancelPacketException extends RuntimeException {
public static final CancelPacketException INSTANCE = new CancelPacketException();
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
@@ -86,12 +86,11 @@ public class InternalPacketListener extends PacketListenerAbstract {
WrapperConfigServerRegistryData packet = new WrapperConfigServerRegistryData(event);
if (packet.getElements() != null) { // 1.20.2 to 1.20.5
SynchronizedRegistriesHandler.handleRegistry(user, packet.getServerVersion().toClientVersion(),
SynchronizedRegistriesHandler.handleRegistry(user, packet,
packet.getRegistryKey(), packet.getElements());
}
if (packet.getRegistryData() != null) { // since 1.20.5
SynchronizedRegistriesHandler.handleLegacyRegistries(user, packet.getServerVersion()
.toClientVersion(), packet.getRegistryData());
SynchronizedRegistriesHandler.handleLegacyRegistries(user, packet, packet.getRegistryData());
}
}
@@ -101,7 +100,7 @@ public class InternalPacketListener extends PacketListenerAbstract {
user.setEntityId(joinGame.getEntityId());
if (joinGame.getDimensionCodec() != null) { // 1.16 to 1.20.1
SynchronizedRegistriesHandler.handleLegacyRegistries(user, joinGame.getServerVersion().toClientVersion(),
SynchronizedRegistriesHandler.handleLegacyRegistries(user, joinGame,
joinGame.getDimensionCodec());
}
@@ -50,7 +50,7 @@ public enum ServerVersion {
//1.20 and 1.20.1 have the same protocol version. 1.20.3 and 1.20.4 have the same protocol version. 1.20.5 and 1.20.6 have the same protocol version
V_1_20(763), V_1_20_1(763), V_1_20_2(764), V_1_20_3(765), V_1_20_4(765), V_1_20_5(766), V_1_20_6(766),
//1.21 and 1.21.1 have the same protocol version. 1.21.2 and 1.21.3 have the same protocol version
V_1_21(767), V_1_21_1(767), V_1_21_2(768), V_1_21_3(768), V_1_21_4(769), V_1_21_5(770),
V_1_21(767), V_1_21_1(767), V_1_21_2(768), V_1_21_3(768), V_1_21_4(769), V_1_21_5(770), V_1_21_6(771), V_1_21_7(772),
//TODO UPDATE Add server version constant
ERROR(-1, true);
@@ -0,0 +1,144 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.advancements;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
public class Advancement {
private @Nullable ResourceLocation parent;
private @Nullable AdvancementDisplay display;
/**
* Removed with 1.20.2
*/
private List<String> criteria;
private List<List<String>> requirements;
/**
* Added with 1.20
*/
private boolean sendsTelemetryData;
public Advancement(
@Nullable ResourceLocation parent,
@Nullable AdvancementDisplay display,
List<List<String>> requirements,
boolean sendsTelemetryData
) {
this(parent, display, Collections.emptyList(), requirements, sendsTelemetryData);
}
@ApiStatus.Obsolete
public Advancement(
@Nullable ResourceLocation parent,
@Nullable AdvancementDisplay display,
List<String> criteria,
List<List<String>> requirements,
boolean sendsTelemetryData
) {
this.parent = parent;
this.display = display;
this.criteria = criteria;
this.requirements = requirements;
this.sendsTelemetryData = sendsTelemetryData;
}
public static Advancement read(PacketWrapper<?> wrapper) {
ResourceLocation parentId = wrapper.readOptional(ResourceLocation::read);
AdvancementDisplay display = wrapper.readOptional(AdvancementDisplay::read);
List<String> criteria = wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_20_2)
? wrapper.readList(PacketWrapper::readString) : null;
List<List<String>> requirements = wrapper.readList(
ew -> wrapper.readList(PacketWrapper::readString));
boolean sendsTelemetryData = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_20)
&& wrapper.readBoolean();
return new Advancement(parentId, display, criteria, requirements, sendsTelemetryData);
}
public static void write(PacketWrapper<?> wrapper, Advancement advancement) {
wrapper.writeOptional(advancement.parent, ResourceLocation::write);
wrapper.writeOptional(advancement.display, AdvancementDisplay::write);
if (wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_20_2)) {
wrapper.writeList(advancement.criteria, PacketWrapper::writeString);
}
wrapper.writeList(advancement.getRequirements(), (ew, anyList) ->
ew.writeList(anyList, PacketWrapper::writeString));
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_20)) {
wrapper.writeBoolean(advancement.sendsTelemetryData);
}
}
public @Nullable ResourceLocation getParent() {
return this.parent;
}
public void setParent(@Nullable ResourceLocation parent) {
this.parent = parent;
}
public @Nullable AdvancementDisplay getDisplay() {
return this.display;
}
public void setDisplay(@Nullable AdvancementDisplay display) {
this.display = display;
}
/**
* Removed with 1.20.2
*/
public List<String> getCriteria() {
return this.criteria;
}
/**
* Removed with 1.20.2
*/
public void setCriteria(List<String> criteria) {
this.criteria = criteria;
}
public List<List<String>> getRequirements() {
return this.requirements;
}
public void setRequirements(List<List<String>> requirements) {
this.requirements = requirements;
}
/**
* Added with 1.20
*/
public boolean isSendsTelemetryData() {
return this.sendsTelemetryData;
}
/**
* Added with 1.20
*/
public void setSendsTelemetryData(boolean sendsTelemetryData) {
this.sendsTelemetryData = sendsTelemetryData;
}
}
@@ -0,0 +1,168 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.advancements;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
public final class AdvancementDisplay {
public static final int FLAG_HAS_BACKGROUND = 0b001;
public static final int FLAG_SHOW_TOAST = 0b010;
public static final int FLAG_HIDDEN = 0b100;
private Component title;
private Component description;
private ItemStack icon;
private AdvancementType type;
private boolean showToast;
private boolean hidden;
private @Nullable ResourceLocation background;
private float x;
private float y;
public AdvancementDisplay(Component title, Component description, ItemStack icon, AdvancementType type, @Nullable ResourceLocation background, boolean showToast,
boolean hidden, float x, float y) {
this.title = title;
this.description = description;
this.icon = icon;
this.type = type;
this.showToast = showToast;
this.hidden = hidden;
this.background = background;
this.x = x;
this.y = y;
}
public static AdvancementDisplay read(PacketWrapper<?> wrapper) {
Component title = wrapper.readComponent();
Component description = wrapper.readComponent();
ItemStack icon = wrapper.readItemStack();
AdvancementType type = wrapper.readEnum(AdvancementType.class);
int flags = wrapper.readInt();
ResourceLocation background = (flags & FLAG_HAS_BACKGROUND) != 0 ? ResourceLocation.read(wrapper) : null;
boolean showToast = (flags & FLAG_SHOW_TOAST) != 0;
boolean hidden = (flags & FLAG_HIDDEN) != 0;
float x = wrapper.readFloat();
float y = wrapper.readFloat();
return new AdvancementDisplay(title, description, icon, type, background, showToast, hidden, x, y);
}
public static void write(PacketWrapper<?> wrapper, AdvancementDisplay display) {
wrapper.writeComponent(display.title);
wrapper.writeComponent(display.description);
wrapper.writeItemStack(display.icon);
wrapper.writeEnum(display.type);
wrapper.writeInt(display.packFlags());
if (display.background != null) {
ResourceLocation.write(wrapper, display.background);
}
wrapper.writeFloat(display.x);
wrapper.writeFloat(display.y);
}
public int packFlags() {
int flags = 0;
if (this.background != null) {
flags |= FLAG_HAS_BACKGROUND;
}
if (this.showToast) {
flags |= FLAG_SHOW_TOAST;
}
if (this.hidden) {
flags |= FLAG_HIDDEN;
}
return flags;
}
public Component getTitle() {
return this.title;
}
public void setTitle(Component title) {
this.title = title;
}
public Component getDescription() {
return this.description;
}
public void setDescription(Component description) {
this.description = description;
}
public ItemStack getIcon() {
return this.icon;
}
public void setIcon(ItemStack icon) {
this.icon = icon;
}
public AdvancementType getType() {
return this.type;
}
public void setType(AdvancementType type) {
this.type = type;
}
public boolean isShowToast() {
return this.showToast;
}
public void setShowToast(boolean showToast) {
this.showToast = showToast;
}
public boolean isHidden() {
return this.hidden;
}
public void setHidden(boolean hidden) {
this.hidden = hidden;
}
public @Nullable ResourceLocation getBackground() {
return this.background;
}
public void setBackground(@Nullable ResourceLocation background) {
this.background = background;
}
public float getX() {
return this.x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return this.y;
}
public void setY(float y) {
this.y = y;
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.advancements;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public final class AdvancementHolder {
private ResourceLocation identifier;
private Advancement advancement;
public AdvancementHolder(ResourceLocation identifier, Advancement advancement) {
this.identifier = identifier;
this.advancement = advancement;
}
public static AdvancementHolder read(PacketWrapper<?> wrapper) {
ResourceLocation identifier = wrapper.readIdentifier();
Advancement advancement = Advancement.read(wrapper);
return new AdvancementHolder(identifier, advancement);
}
public static void write(PacketWrapper<?> wrapper, AdvancementHolder holder) {
wrapper.writeIdentifier(holder.identifier);
Advancement.write(wrapper, holder.advancement);
}
public ResourceLocation getIdentifier() {
return this.identifier;
}
public void setIdentifier(ResourceLocation identifier) {
this.identifier = identifier;
}
public Advancement getAdvancement() {
return this.advancement;
}
public void setAdvancement(Advancement advancement) {
this.advancement = advancement;
}
}
@@ -0,0 +1,75 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.advancements;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.Nullable;
import java.util.Map;
public final class AdvancementProgress {
private Map<String, CriterionProgress> criteria;
public AdvancementProgress(Map<String, CriterionProgress> criteria) {
this.criteria = criteria;
}
public static AdvancementProgress read(PacketWrapper<?> wrapper) {
Map<String, CriterionProgress> criteria = wrapper.readMap(PacketWrapper::readString, CriterionProgress::read);
return new AdvancementProgress(criteria);
}
public static void write(PacketWrapper<?> wrapper, AdvancementProgress progress) {
wrapper.writeMap(progress.getCriteria(), PacketWrapper::writeString, CriterionProgress::write);
}
public Map<String, CriterionProgress> getCriteria() {
return this.criteria;
}
public void setCriteria(Map<String, CriterionProgress> criteria) {
this.criteria = criteria;
}
public static final class CriterionProgress {
private @Nullable Long obtainedTimestamp;
public CriterionProgress(@Nullable Long obtainedTimestamp) {
this.obtainedTimestamp = obtainedTimestamp;
}
public static CriterionProgress read(PacketWrapper<?> wrapper) {
return new CriterionProgress(wrapper.readOptional(PacketWrapper::readLong));
}
public static void write(PacketWrapper<?> wrapper, CriterionProgress progress) {
wrapper.writeOptional(progress.obtainedTimestamp, PacketWrapper::writeLong);
}
public @Nullable Long getObtainedTimestamp() {
return this.obtainedTimestamp;
}
public void setObtainedTimestamp(@Nullable Long obtainedTimestamp) {
this.obtainedTimestamp = obtainedTimestamp;
}
}
}
@@ -0,0 +1,27 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.advancements;
public enum AdvancementType {
TASK,
CHALLENGE,
GOAL,
;
}
@@ -0,0 +1,39 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.attribute;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface AttributeDisplay {
static AttributeDisplay read(PacketWrapper<?> wrapper) {
AttributeDisplayType<?> type = wrapper.readMappedEntity(AttributeDisplayTypes.getRegistry());
return type.read(wrapper);
}
@SuppressWarnings("unchecked") // not unchecked
static void write(PacketWrapper<?> wrapper, AttributeDisplay display) {
wrapper.writeMappedEntity(display.getType());
((AttributeDisplayType<? super AttributeDisplay>) display.getType()).write(wrapper, display);
}
AttributeDisplayType<?> getType();
}
@@ -0,0 +1,31 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.attribute;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface AttributeDisplayType<T extends AttributeDisplay> extends MappedEntity {
T read(PacketWrapper<?> wrapper);
void write(PacketWrapper<?> wrapper, T display);
}
@@ -0,0 +1,56 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.attribute;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@NullMarked
public final class AttributeDisplayTypes {
private static final VersionedRegistry<AttributeDisplayType<?>> REGISTRY = new VersionedRegistry<>("attribute_display_type");
private AttributeDisplayTypes() {
}
@ApiStatus.Internal
public static <T extends AttributeDisplay> AttributeDisplayType<T> define(
String name, PacketWrapper.Reader<T> reader, PacketWrapper.Writer<T> writer
) {
return REGISTRY.define(name, data ->
new StaticAttributeDisplayType<>(data, reader, writer));
}
public static VersionedRegistry<AttributeDisplayType<?>> getRegistry() {
return REGISTRY;
}
public static final AttributeDisplayType<DefaultAttributeDisplay> DEFAULT = define("default",
DefaultAttributeDisplay::read, DefaultAttributeDisplay::write);
public static final AttributeDisplayType<HiddenAttributeDisplay> HIDDEN = define("hidden",
HiddenAttributeDisplay::read, HiddenAttributeDisplay::write);
public static final AttributeDisplayType<OverrideAttributeDisplay> OVERRIDE = define("override",
OverrideAttributeDisplay::read, OverrideAttributeDisplay::write);
static {
REGISTRY.unloadMappings();
}
}
@@ -187,6 +187,22 @@ public final class Attributes {
public static final Attribute TEMPT_RANGE = define("tempt_range",
null, 10d, 0d, 2048d);
/**
* Added with 1.21.6
*/
public static final Attribute CAMERA_DISTANCE = define("camera_distance",
null, 4d, 0d, 32d);
/**
* Added with 1.21.6
*/
public static final Attribute WAYPOINT_TRANSMIT_RANGE = define("waypoint_transmit_range",
null, 0d, 0d, 60_000_000d);
/**
* Added with 1.21.6
*/
public static final Attribute WAYPOINT_RECEIVE_RANGE = define("waypoint_receive_range",
null, 0d, 0d, 60_000_000d);
/**
* This attribute has been renamed in 1.20.5 to {@link #JUMP_STRENGTH}
*/
@@ -0,0 +1,44 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.attribute;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class DefaultAttributeDisplay implements AttributeDisplay {
public static final DefaultAttributeDisplay INSTANCE = new DefaultAttributeDisplay();
private DefaultAttributeDisplay() {
}
public static DefaultAttributeDisplay read(PacketWrapper<?> wrapper) {
return INSTANCE;
}
public static void write(PacketWrapper<?> wrapper, DefaultAttributeDisplay display) {
// NO-OP
}
@Override
public AttributeDisplayType<?> getType() {
return AttributeDisplayTypes.DEFAULT;
}
}
@@ -0,0 +1,44 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.attribute;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class HiddenAttributeDisplay implements AttributeDisplay {
public static final HiddenAttributeDisplay INSTANCE = new HiddenAttributeDisplay();
private HiddenAttributeDisplay() {
}
public static HiddenAttributeDisplay read(PacketWrapper<?> wrapper) {
return INSTANCE;
}
public static void write(PacketWrapper<?> wrapper, HiddenAttributeDisplay display) {
// NO-OP
}
@Override
public AttributeDisplayType<?> getType() {
return AttributeDisplayTypes.HIDDEN;
}
}
@@ -0,0 +1,64 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.attribute;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jspecify.annotations.NullMarked;
import java.util.Objects;
@NullMarked
public class OverrideAttributeDisplay implements AttributeDisplay {
private final Component component;
public OverrideAttributeDisplay(Component component) {
this.component = component;
}
public static OverrideAttributeDisplay read(PacketWrapper<?> wrapper) {
return new OverrideAttributeDisplay(wrapper.readComponent());
}
public static void write(PacketWrapper<?> wrapper, OverrideAttributeDisplay display) {
wrapper.writeComponent(display.component);
}
@Override
public AttributeDisplayType<?> getType() {
return AttributeDisplayTypes.OVERRIDE;
}
public Component getComponent() {
return this.component;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof OverrideAttributeDisplay)) return false;
OverrideAttributeDisplay that = (OverrideAttributeDisplay) obj;
return this.component.equals(that.component);
}
@Override
public int hashCode() {
return Objects.hashCode(this.component);
}
}
@@ -0,0 +1,52 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.attribute;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Reader;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Writer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class StaticAttributeDisplayType<T extends AttributeDisplay> extends AbstractMappedEntity implements AttributeDisplayType<T> {
private final Reader<T> reader;
private final Writer<T> writer;
@ApiStatus.Internal
public StaticAttributeDisplayType(@Nullable TypesBuilderData data, Reader<T> reader, Writer<T> writer) {
super(data);
this.reader = reader;
this.writer = writer;
}
@Override
public T read(PacketWrapper<?> wrapper) {
return this.reader.apply(wrapper);
}
@Override
public void write(PacketWrapper<?> wrapper, T display) {
this.writer.accept(wrapper, display);
}
}
@@ -72,7 +72,12 @@ public class ChatTypeDecoration {
wrapper.writeStyle(decoration.style);
}
@Deprecated
public static ChatTypeDecoration decode(NBT nbt, ClientVersion version) {
return decode(nbt, PacketWrapper.createDummyWrapper(version));
}
public static ChatTypeDecoration decode(NBT nbt, PacketWrapper<?> wrapper) {
NBTCompound compound = (NBTCompound) nbt;
String translationKey = compound.getStringTagValueOrThrow("translation_key");
NBT paramsTag = compound.getTagOrThrow("parameters");
@@ -90,11 +95,16 @@ public class ChatTypeDecoration {
}
NBTCompound styleTag = compound.getCompoundTagOrNull("style");
Style style = styleTag == null ? empty() :
AdventureSerializer.serializer(version).nbt().deserializeStyle(styleTag);
wrapper.getSerializers().nbt().deserializeStyle(styleTag, wrapper);
return new ChatTypeDecoration(translationKey, params, style);
}
@Deprecated
public static NBT encode(ChatTypeDecoration decoration, ClientVersion version) {
return encode(decoration, PacketWrapper.createDummyWrapper(version));
}
public static NBT encode(ChatTypeDecoration decoration, PacketWrapper<?> wrapper) {
NBTList<NBTString> paramsTag = NBTList.createStringList();
for (Parameter param : decoration.parameters) {
paramsTag.addTag(new NBTString(param.getId()));
@@ -104,8 +114,8 @@ public class ChatTypeDecoration {
compound.setTag("translation_key", new NBTString(decoration.translationKey));
compound.setTag("parameters", paramsTag);
if (!decoration.style.isEmpty()) {
compound.setTag("style", AdventureSerializer.serializer(version)
.nbt().serializeStyle(decoration.style));
compound.setTag("style", wrapper.getSerializers()
.nbt().serializeStyle(decoration.style, wrapper));
}
return compound;
}
@@ -28,6 +28,20 @@ import java.util.Optional;
public class Node {
public static final byte TYPE_MASK = 0b11;
public static final byte TYPE_ROOT = 0b00;
public static final byte TYPE_LITERAL = 0b01;
public static final byte TYPE_ARGUMENT = 0b10;
public static final byte FLAG_MASK = ~TYPE_MASK;
public static final byte FLAG_EXECUTABLE = 0b000100;
public static final byte FLAG_REDIRECT = 0b001000;
public static final byte FLAG_CUSTOM_SUGGESTIONS = 0b010000;
/**
* Added with 1.21.6
*/
public static final byte FLAG_RESTRICTED = 0b100000;
private byte flags;
private List<Integer> children;
private int redirectNodeIndex;
@@ -27,6 +27,7 @@ import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.util.ArrayList;
import java.util.Arrays;
@@ -36,9 +37,10 @@ import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
@NullMarked
public final class Parsers {
private static final VersionedRegistry<Parser> REGISTRY = new VersionedRegistry<>("argument_type");
private static final VersionedRegistry<Parser> REGISTRY = new VersionedRegistry<>("command_argument_type");
private Parsers() {
}
@@ -218,6 +220,15 @@ public final class Parsers {
(wrapper, value) -> wrapper.writeIdentifier((ResourceLocation) value.get(0))
);
/**
* Added with 1.21.6
*/
public static final Parser HEX_COLOR = define("hex_color", null, null);
/**
* Added with 1.21.6
*/
public static final Parser DIALOG = define("dialog", null, null);
static {
REGISTRY.unloadMappings();
}
@@ -0,0 +1,71 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class ChangePageClickEvent implements ClickEvent {
private final int page;
@ApiStatus.Obsolete
public ChangePageClickEvent(String page) {
this(Integer.parseInt(page));
}
public ChangePageClickEvent(int page) {
this.page = page;
}
public static ChangePageClickEvent decode(NBTCompound compound, PacketWrapper<?> wrapper) {
int page = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5)
? compound.getNumberTagValueOrThrow("page").intValue()
: Integer.parseInt(compound.getStringTagValueOrThrow("value"));
return new ChangePageClickEvent(page);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, ChangePageClickEvent clickEvent) {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5)) {
compound.setTag("page", new NBTInt(clickEvent.page));
} else {
compound.setTag("value", new NBTString(Integer.toString(clickEvent.page)));
}
}
@Override
public ClickEventAction<?> getAction() {
return ClickEventActions.CHANGE_PAGE;
}
@Override
public net.kyori.adventure.text.event.ClickEvent asAdventure() {
return net.kyori.adventure.text.event.ClickEvent.changePage(this.page);
}
public int getPage() {
return this.page;
}
}
@@ -0,0 +1,76 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.protocol.dialog.Dialog;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTEnd;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.adventure.NbtTagHolder;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.event.ClickEvent.Payload;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface ClickEvent {
static ClickEvent decode(NBTCompound compound, PacketWrapper<?> wrapper) {
String actionName = compound.getStringTagValueOrThrow("action");
ClickEventAction<?> action = ClickEventActions.getRegistry().getByNameOrThrow(actionName);
return action.decode(compound, wrapper);
}
@SuppressWarnings("unchecked") // not unchecked
static void encode(NBTCompound compound, PacketWrapper<?> wrapper, ClickEvent clickEvent) {
compound.set("action", clickEvent.getAction().getName(), ResourceLocation::encode, wrapper);
((ClickEventAction<? super ClickEvent>) clickEvent.getAction()).encode(compound, wrapper, clickEvent);
}
ClickEventAction<?> getAction();
static ClickEvent fromAdventure(net.kyori.adventure.text.event.ClickEvent clickEvent) {
switch (clickEvent.action()) {
case OPEN_URL:
return new OpenUrlClickEvent(clickEvent.value());
case OPEN_FILE:
return new OpenFileClickEvent(clickEvent.value());
case RUN_COMMAND:
return new RunCommandClickEvent(clickEvent.value());
case SUGGEST_COMMAND:
return new SuggestCommandClickEvent(clickEvent.value());
case CHANGE_PAGE:
return new ChangePageClickEvent(clickEvent.value());
case COPY_TO_CLIPBOARD:
return new CopyToClipboardClickEvent(clickEvent.value());
case SHOW_DIALOG:
return new ShowDialogClickEvent((Dialog) ((Payload.Dialog) clickEvent.payload()).dialog());
case CUSTOM:
Payload.Custom payload = (Payload.Custom) clickEvent.payload();
NbtTagHolder nbtTag = (NbtTagHolder) payload.nbt();
return new CustomClickEvent(
new ResourceLocation(payload.key()),
nbtTag.getTag() instanceof NBTEnd ? null : nbtTag.getTag()
);
default:
throw new UnsupportedOperationException("Unsupported clickevent: " + clickEvent);
}
}
net.kyori.adventure.text.event.ClickEvent asAdventure();
}
@@ -0,0 +1,34 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface ClickEventAction<T extends ClickEvent> extends MappedEntity {
boolean isAllowFromServer();
T decode(NBTCompound compound, PacketWrapper<?> wrapper);
void encode(NBTCompound compound, PacketWrapper<?> wrapper, T clickEvent);
}
@@ -0,0 +1,89 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.protocol.util.NbtMapDecoder;
import com.github.retrooper.packetevents.protocol.util.NbtMapEncoder;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@NullMarked
public final class ClickEventActions {
private static final VersionedRegistry<ClickEventAction<?>> REGISTRY = new VersionedRegistry<>("click_event_action");
private ClickEventActions() {
}
@ApiStatus.Internal
public static <T extends ClickEvent> ClickEventAction<T> define(
String name, boolean allowFromServer,
NbtMapDecoder<T> decoder, NbtMapEncoder<T> encoder
) {
return REGISTRY.define(name, data -> new StaticClickEventAction<>(
data, allowFromServer, decoder, encoder));
}
public static VersionedRegistry<ClickEventAction<?>> getRegistry() {
return REGISTRY;
}
public static final ClickEventAction<OpenUrlClickEvent> OPEN_URL = define("open_url", true,
OpenUrlClickEvent::decode, OpenUrlClickEvent::encode);
public static final ClickEventAction<OpenFileClickEvent> OPEN_FILE = define("open_file", false,
OpenFileClickEvent::decode, OpenFileClickEvent::encode);
public static final ClickEventAction<RunCommandClickEvent> RUN_COMMAND = define("run_command", true,
RunCommandClickEvent::decode, RunCommandClickEvent::encode);
/**
* Removed in 1.9
*/
@ApiStatus.Obsolete
public static final ClickEventAction<TwitchUserInfoClickEvent> TWITCH_USER_INFO = define("twitch_user_info", false,
TwitchUserInfoClickEvent::decode, TwitchUserInfoClickEvent::encode);
public static final ClickEventAction<SuggestCommandClickEvent> SUGGEST_COMMAND = define("suggest_command", true,
SuggestCommandClickEvent::decode, SuggestCommandClickEvent::encode);
/**
* Added with 1.8
*/
public static final ClickEventAction<ChangePageClickEvent> CHANGE_PAGE = define("change_page", true,
ChangePageClickEvent::decode, ChangePageClickEvent::encode);
/**
* Added with 1.15
*/
public static final ClickEventAction<CopyToClipboardClickEvent> COPY_TO_CLIPBOARD = define("copy_to_clipboard", true,
CopyToClipboardClickEvent::decode, CopyToClipboardClickEvent::encode);
/**
* Added with 1.21.6
*/
public static final ClickEventAction<ShowDialogClickEvent> SHOW_DIALOG = define("show_dialog", true,
ShowDialogClickEvent::decode, ShowDialogClickEvent::encode);
/**
* Added with 1.21.6
*/
public static final ClickEventAction<CustomClickEvent> CUSTOM = define("custom", true,
CustomClickEvent::decode, CustomClickEvent::encode);
static {
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,57 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class CopyToClipboardClickEvent implements ClickEvent {
private final String value;
public CopyToClipboardClickEvent(String value) {
this.value = value;
}
public static CopyToClipboardClickEvent decode(NBTCompound compound, PacketWrapper<?> wrapper) {
String value = compound.getStringTagValueOrThrow("value");
return new CopyToClipboardClickEvent(value);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, CopyToClipboardClickEvent clickEvent) {
compound.setTag("value", new NBTString(clickEvent.value));
}
@Override
public ClickEventAction<?> getAction() {
return ClickEventActions.COPY_TO_CLIPBOARD;
}
@Override
public net.kyori.adventure.text.event.ClickEvent asAdventure() {
return net.kyori.adventure.text.event.ClickEvent.copyToClipboard(this.value);
}
public String getValue() {
return this.value;
}
}
@@ -0,0 +1,72 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTEnd;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.adventure.NbtTagHolder;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class CustomClickEvent implements ClickEvent {
private final ResourceLocation id;
private final @Nullable NBT payload;
public CustomClickEvent(ResourceLocation id, @Nullable NBT payload) {
this.id = id;
this.payload = payload;
}
public static CustomClickEvent decode(NBTCompound compound, PacketWrapper<?> wrapper) {
ResourceLocation id = compound.getOrThrow("id", ResourceLocation::decode, wrapper);
NBT payload = compound.getTagOrNull("payload");
return new CustomClickEvent(id, payload);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, CustomClickEvent clickEvent) {
compound.set("id", clickEvent.id, ResourceLocation::encode, wrapper);
if (clickEvent.payload != null) {
compound.setTag("payload", clickEvent.payload);
}
}
@Override
public ClickEventAction<?> getAction() {
return ClickEventActions.CUSTOM;
}
@Override
public net.kyori.adventure.text.event.ClickEvent asAdventure() {
return net.kyori.adventure.text.event.ClickEvent.custom(
this.id.key(), new NbtTagHolder(this.payload != null ? this.payload : NBTEnd.INSTANCE));
}
public ResourceLocation getId() {
return this.id;
}
public @Nullable NBT getPayload() {
return this.payload;
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class OpenFileClickEvent implements ClickEvent {
private final String path;
public OpenFileClickEvent(String path) {
this.path = path;
}
public static OpenFileClickEvent decode(NBTCompound compound, PacketWrapper<?> wrapper) {
boolean v1215 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5);
String path = compound.getStringTagValueOrThrow(v1215 ? "path" : "value");
return new OpenFileClickEvent(path);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, OpenFileClickEvent clickEvent) {
boolean v1215 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5);
compound.setTag(v1215 ? "path" : "value", new NBTString(clickEvent.path));
}
@Override
public ClickEventAction<?> getAction() {
return ClickEventActions.OPEN_FILE;
}
@Override
public net.kyori.adventure.text.event.ClickEvent asAdventure() {
return net.kyori.adventure.text.event.ClickEvent.openFile(this.path);
}
public String getPath() {
return this.path;
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class OpenUrlClickEvent implements ClickEvent {
private final String url;
public OpenUrlClickEvent(String url) {
this.url = url;
}
public static OpenUrlClickEvent decode(NBTCompound compound, PacketWrapper<?> wrapper) {
boolean v1215 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5);
String url = compound.getStringTagValueOrThrow(v1215 ? "url" : "value");
return new OpenUrlClickEvent(url);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, OpenUrlClickEvent clickEvent) {
boolean v1215 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5);
compound.setTag(v1215 ? "url" : "value", new NBTString(clickEvent.url));
}
@Override
public ClickEventAction<?> getAction() {
return ClickEventActions.OPEN_URL;
}
@Override
public net.kyori.adventure.text.event.ClickEvent asAdventure() {
return net.kyori.adventure.text.event.ClickEvent.openUrl(this.url);
}
public String getUrl() {
return this.url;
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class RunCommandClickEvent implements ClickEvent {
private final String command;
public RunCommandClickEvent(String command) {
this.command = command;
}
public static RunCommandClickEvent decode(NBTCompound compound, PacketWrapper<?> wrapper) {
boolean v1215 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5);
String command = compound.getStringTagValueOrThrow(v1215 ? "command" : "value");
return new RunCommandClickEvent(command);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, RunCommandClickEvent clickEvent) {
boolean v1215 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5);
compound.setTag(v1215 ? "command" : "value", new NBTString(clickEvent.command));
}
@Override
public ClickEventAction<?> getAction() {
return ClickEventActions.RUN_COMMAND;
}
@Override
public net.kyori.adventure.text.event.ClickEvent asAdventure() {
return net.kyori.adventure.text.event.ClickEvent.runCommand(this.command);
}
public String getCommand() {
return this.command;
}
}
@@ -0,0 +1,57 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.protocol.dialog.Dialog;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class ShowDialogClickEvent implements ClickEvent {
private final Dialog dialog;
public ShowDialogClickEvent(Dialog dialog) {
this.dialog = dialog;
}
public static ShowDialogClickEvent decode(NBTCompound compound, PacketWrapper<?> wrapper) {
Dialog dialog = compound.getOrThrow("dialog", Dialog::decode, wrapper);
return new ShowDialogClickEvent(dialog);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, ShowDialogClickEvent clickEvent) {
compound.set("dialog", clickEvent.dialog, Dialog::encode, wrapper);
}
@Override
public ClickEventAction<?> getAction() {
return ClickEventActions.SHOW_DIALOG;
}
@Override
public net.kyori.adventure.text.event.ClickEvent asAdventure() {
return net.kyori.adventure.text.event.ClickEvent.showDialog(this.dialog);
}
public Dialog getDialog() {
return this.dialog;
}
}
@@ -0,0 +1,63 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.util.NbtMapDecoder;
import com.github.retrooper.packetevents.protocol.util.NbtMapEncoder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class StaticClickEventAction<T extends ClickEvent> extends AbstractMappedEntity implements ClickEventAction<T> {
private final boolean allowFromServer;
private final NbtMapDecoder<T> decoder;
private final NbtMapEncoder<T> encoder;
@ApiStatus.Internal
public StaticClickEventAction(
@Nullable TypesBuilderData data, boolean allowFromServer,
NbtMapDecoder<T> decoder, NbtMapEncoder<T> encoder
) {
super(data);
this.allowFromServer = allowFromServer;
this.decoder = decoder;
this.encoder = encoder;
}
@Override
public boolean isAllowFromServer() {
return this.allowFromServer;
}
@Override
public T decode(NBTCompound compound, PacketWrapper<?> wrapper) {
return this.decoder.decode(compound, wrapper);
}
@Override
public void encode(NBTCompound compound, PacketWrapper<?> wrapper, T clickEvent) {
this.encoder.encode(compound, wrapper, clickEvent);
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class SuggestCommandClickEvent implements ClickEvent {
private final String command;
public SuggestCommandClickEvent(String command) {
this.command = command;
}
public static SuggestCommandClickEvent decode(NBTCompound compound, PacketWrapper<?> wrapper) {
boolean v1215 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5);
String command = compound.getStringTagValueOrThrow(v1215 ? "command" : "value");
return new SuggestCommandClickEvent(command);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, SuggestCommandClickEvent clickEvent) {
boolean v1215 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5);
compound.setTag(v1215 ? "command" : "value", new NBTString(clickEvent.command));
}
@Override
public ClickEventAction<?> getAction() {
return ClickEventActions.SUGGEST_COMMAND;
}
@Override
public net.kyori.adventure.text.event.ClickEvent asAdventure() {
return net.kyori.adventure.text.event.ClickEvent.suggestCommand(this.command);
}
public String getCommand() {
return this.command;
}
}
@@ -0,0 +1,62 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat.clickevent;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Removed in 1.9
*/
@NullMarked
@ApiStatus.Obsolete
public class TwitchUserInfoClickEvent implements ClickEvent {
private final String value;
public TwitchUserInfoClickEvent(String value) {
this.value = value;
}
public static TwitchUserInfoClickEvent decode(NBTCompound compound, PacketWrapper<?> wrapper) {
String value = compound.getStringTagValueOrThrow("value");
return new TwitchUserInfoClickEvent(value);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, TwitchUserInfoClickEvent clickEvent) {
compound.setTag("value", new NBTString(clickEvent.value));
}
@Override
public ClickEventAction<?> getAction() {
return ClickEventActions.TWITCH_USER_INFO;
}
@Override
public net.kyori.adventure.text.event.ClickEvent asAdventure() {
throw new UnsupportedOperationException();
}
public String getValue() {
return this.value;
}
}
@@ -20,11 +20,14 @@ package com.github.retrooper.packetevents.protocol.chat.message;
import com.github.retrooper.packetevents.protocol.chat.ChatType;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
public class ChatMessage {
private Component chatContent;
private ChatType type;
private final GsonComponentSerializer gson = GsonComponentSerializer.gson();
protected ChatMessage(Component chatContent, ChatType type) {
this.chatContent = chatContent;
this.type = type;
@@ -34,14 +37,22 @@ public class ChatMessage {
return chatContent;
}
public ChatType getType() {
return type;
public String getChatContentJson() {
return gson.serialize(chatContent);
}
public void setChatContent(Component chatContent) {
this.chatContent = chatContent;
}
public void setChatContentJson(String jsonContent) {
this.chatContent = gson.deserialize(jsonContent);
}
public ChatType getType() {
return type;
}
public void setType(ChatType type) {
this.type = type;
}
@@ -65,6 +65,16 @@ public class Color implements RGBLike {
wrapper.writeInt(color.asRGB());
}
public static Color readShort(PacketWrapper<?> wrapper) {
return new Color(wrapper.readUnsignedByte(), wrapper.readUnsignedByte(), wrapper.readUnsignedByte());
}
public static void writeShort(PacketWrapper<?> wrapper, Color color) {
wrapper.writeByte(color.red);
wrapper.writeByte(color.green);
wrapper.writeByte(color.blue);
}
public static Color decode(NBT nbt, ClientVersion version) {
if (nbt instanceof NBTNumber) {
return new Color(((NBTNumber) nbt).getAsInt());
@@ -22,14 +22,22 @@ import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.IRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.util.Map;
import java.util.Optional;
@NullMarked
public interface IComponentMap {
static StaticComponentMap decode(NBT nbt, PacketWrapper<?> wrapper, IRegistry<? extends ComponentType<?>> registry) {
return decode(nbt, wrapper.getServerVersion().toClientVersion(), registry);
}
@Deprecated
@SuppressWarnings("unchecked") // safe in this case
static StaticComponentMap decode(NBT nbt, ClientVersion version, IRegistry<? extends ComponentType<?>> registry) {
NBTCompound compound = (NBTCompound) nbt;
@@ -45,6 +53,11 @@ public interface IComponentMap {
return components.build();
}
static NBT encode(PacketWrapper<?> wrapper, StaticComponentMap components) {
return encode(components, wrapper.getServerVersion().toClientVersion());
}
@Deprecated
@SuppressWarnings("unchecked") // safe in this case
static NBT encode(StaticComponentMap components, ClientVersion version) {
NBTCompound compound = new NBTCompound();
@@ -20,8 +20,10 @@ package com.github.retrooper.packetevents.protocol.component.builtin.item;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.attribute.Attribute;
import com.github.retrooper.packetevents.protocol.attribute.AttributeDisplay;
import com.github.retrooper.packetevents.protocol.attribute.AttributeOperation;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.attribute.DefaultAttributeDisplay;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes.PropertyModifier;
@@ -133,24 +135,39 @@ public class ItemAttributeModifiers {
private Attribute attribute;
private Modifier modifier;
private EquipmentSlotGroup slotGroup;
private AttributeDisplay display;
@ApiStatus.Obsolete
public ModifierEntry(Attribute attribute, Modifier modifier, EquipmentSlotGroup slotGroup) {
this(attribute, modifier, slotGroup, DefaultAttributeDisplay.INSTANCE);
}
public ModifierEntry(
Attribute attribute, Modifier modifier,
EquipmentSlotGroup slotGroup, AttributeDisplay display
) {
this.attribute = attribute;
this.modifier = modifier;
this.slotGroup = slotGroup;
this.display = display;
}
public static ModifierEntry read(PacketWrapper<?> wrapper) {
Attribute attribute = wrapper.readMappedEntity(Attributes::getById);
Modifier modifier = Modifier.read(wrapper);
EquipmentSlotGroup slot = wrapper.readEnum(EquipmentSlotGroup.values());
return new ModifierEntry(attribute, modifier, slot);
AttributeDisplay display = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_6)
? AttributeDisplay.read(wrapper) : DefaultAttributeDisplay.INSTANCE;
return new ModifierEntry(attribute, modifier, slot, display);
}
public static void write(PacketWrapper<?> wrapper, ModifierEntry entry) {
wrapper.writeMappedEntity(entry.attribute);
Modifier.write(wrapper, entry.modifier);
wrapper.writeEnum(entry.slotGroup);
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_6)) {
AttributeDisplay.write(wrapper, entry.display);
}
}
public Attribute getAttribute() {
@@ -24,6 +24,7 @@ import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
import com.github.retrooper.packetevents.protocol.sound.Sound;
import com.github.retrooper.packetevents.protocol.sound.Sounds;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
@@ -41,7 +42,18 @@ public class ItemEquippable {
private boolean dispensable;
private boolean swappable;
private boolean damageOnHurt;
/**
* Added with 1.21.5
*/
private boolean equipOnInteract;
/**
* Added with 1.21.6
*/
private boolean canBeSheared;
/**
* Added with 1.21.6
*/
private Sound shearingSound;
@ApiStatus.Obsolete
public ItemEquippable(
@@ -58,6 +70,7 @@ public class ItemEquippable {
dispensable, swappable, damageOnHurt, false);
}
@ApiStatus.Obsolete
public ItemEquippable(
EquipmentSlot slot,
Sound equipSound,
@@ -68,6 +81,24 @@ public class ItemEquippable {
boolean swappable,
boolean damageOnHurt,
boolean equipOnInteract
) {
this(slot, equipSound, assetId, cameraOverlay, allowedEntities,
dispensable, swappable, damageOnHurt, equipOnInteract,
false, Sounds.ITEM_SHEARS_SNIP);
}
public ItemEquippable(
EquipmentSlot slot,
Sound equipSound,
@Nullable ResourceLocation assetId,
@Nullable ResourceLocation cameraOverlay,
@Nullable MappedEntitySet<EntityType> allowedEntities,
boolean dispensable,
boolean swappable,
boolean damageOnHurt,
boolean equipOnInteract,
boolean canBeSheared,
Sound shearingSound
) {
this.slot = slot;
this.equipSound = equipSound;
@@ -78,6 +109,8 @@ public class ItemEquippable {
this.swappable = swappable;
this.damageOnHurt = damageOnHurt;
this.equipOnInteract = equipOnInteract;
this.canBeSheared = canBeSheared;
this.shearingSound = shearingSound;
}
public static ItemEquippable read(PacketWrapper<?> wrapper) {
@@ -90,9 +123,19 @@ public class ItemEquippable {
boolean dispensable = wrapper.readBoolean();
boolean swappable = wrapper.readBoolean();
boolean damageOnHurt = wrapper.readBoolean();
boolean equipOnInteract = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5) && wrapper.readBoolean();
boolean equipOnInteract = false;
boolean canBeSheared = false;
Sound shearingSound = Sounds.ITEM_SHEARS_SNIP;
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5)) {
equipOnInteract = wrapper.readBoolean();
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_6)) {
canBeSheared = wrapper.readBoolean();
shearingSound = Sound.read(wrapper);
}
}
return new ItemEquippable(slot, equipSound, assetId,
cameraOverlay, allowedEntities, dispensable, swappable, damageOnHurt, equipOnInteract);
cameraOverlay, allowedEntities, dispensable, swappable,
damageOnHurt, equipOnInteract, canBeSheared, shearingSound);
}
public static void write(PacketWrapper<?> wrapper, ItemEquippable equippable) {
@@ -106,6 +149,10 @@ public class ItemEquippable {
wrapper.writeBoolean(equippable.damageOnHurt);
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5)) {
wrapper.writeBoolean(equippable.equipOnInteract);
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_6)) {
wrapper.writeBoolean(equippable.canBeSheared);
Sound.write(wrapper, equippable.shearingSound);
}
}
}
@@ -187,6 +234,34 @@ public class ItemEquippable {
this.equipOnInteract = equipOnInteract;
}
/**
* Added with 1.21.6
*/
public boolean isCanBeSheared() {
return this.canBeSheared;
}
/**
* Added with 1.21.6
*/
public void setCanBeSheared(boolean canBeSheared) {
this.canBeSheared = canBeSheared;
}
/**
* Added with 1.21.6
*/
public Sound getShearingSound() {
return this.shearingSound;
}
/**
* Added with 1.21.6
*/
public void setShearingSound(Sound shearingSound) {
this.shearingSound = shearingSound;
}
@Deprecated
public @Nullable ResourceLocation getModel() {
return this.assetId;
@@ -199,27 +274,28 @@ public class ItemEquippable {
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof ItemEquippable)) return false;
ItemEquippable that = (ItemEquippable) obj;
if (this.dispensable != that.dispensable) return false;
if (this.swappable != that.swappable) return false;
if (this.damageOnHurt != that.damageOnHurt) return false;
if (this.equipOnInteract != that.equipOnInteract) return false;
if (this.canBeSheared != that.canBeSheared) return false;
if (this.slot != that.slot) return false;
if (!Objects.equals(this.equipSound, that.equipSound)) return false;
if (!this.equipSound.equals(that.equipSound)) return false;
if (!Objects.equals(this.assetId, that.assetId)) return false;
if (!Objects.equals(this.cameraOverlay, that.cameraOverlay)) return false;
if (!Objects.equals(this.allowedEntities, that.allowedEntities)) return false;
return Objects.equals(this.equipOnInteract, that.equipOnInteract);
return this.shearingSound.equals(that.shearingSound);
}
@Override
public int hashCode() {
return Objects.hash(this.slot, this.equipSound, this.assetId, this.cameraOverlay, this.allowedEntities, this.dispensable, this.swappable, this.damageOnHurt, this.equipOnInteract);
return Objects.hash(this.slot, this.equipSound, this.assetId, this.cameraOverlay, this.allowedEntities, this.dispensable, this.swappable, this.damageOnHurt, this.equipOnInteract, this.canBeSheared, this.shearingSound);
}
@Override
public String toString() {
return "ItemEquippable{slot=" + this.slot + ", equipSound=" + this.equipSound + ", assetId=" + this.assetId + ", cameraOverlay=" + this.cameraOverlay + ", allowedEntities=" + this.allowedEntities + ", dispensable=" + this.dispensable + ", swappable=" + this.swappable + ", damageOnHurt=" + this.damageOnHurt + ", equipOnInteract=" + this.equipOnInteract + '}';
return "ItemEquippable{slot=" + this.slot + ", equipSound=" + this.equipSound + ", assetId=" + this.assetId + ", cameraOverlay=" + this.cameraOverlay + ", allowedEntities=" + this.allowedEntities + ", dispensable=" + this.dispensable + ", swappable=" + this.swappable + ", damageOnHurt=" + this.damageOnHurt + ", equipOnInteract=" + this.equipOnInteract + ", canBeSheared=" + this.canBeSheared + ", shearingSound=" + this.shearingSound + '}';
}
}
@@ -0,0 +1,121 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.dialog.body.DialogBody;
import com.github.retrooper.packetevents.protocol.dialog.input.Input;
import com.github.retrooper.packetevents.protocol.nbt.NBTByte;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.List;
@NullMarked
public final class CommonDialogData {
private final Component title;
private final @Nullable Component externalTitle;
private final boolean canCloseWithEscape;
private final boolean pause;
private final DialogAction afterAction;
private final List<DialogBody> body;
private final List<Input> inputs;
public CommonDialogData(
Component title, @Nullable Component externalTitle, boolean canCloseWithEscape, boolean pause,
DialogAction afterAction, List<DialogBody> body, List<Input> inputs
) {
if (pause && !afterAction.isWillUnpause()) {
throw new IllegalArgumentException("Dialogs that pause the game must use after_action "
+ "values that unpause it after user action!");
}
this.title = title;
this.externalTitle = externalTitle;
this.canCloseWithEscape = canCloseWithEscape;
this.pause = pause;
this.afterAction = afterAction;
this.body = body;
this.inputs = inputs;
}
public static CommonDialogData decode(NBTCompound compound, PacketWrapper<?> wrapper) {
Component title = compound.getOrThrow("title", AdventureSerializer.serializer(wrapper), wrapper);
Component externalTitle = compound.getOrNull("external_title", AdventureSerializer.serializer(wrapper), wrapper);
boolean canCloseWithEscape = compound.getBooleanOr("can_close_with_escape", true);
boolean pause = compound.getBooleanOr("pause", true);
DialogAction afterAction = compound.getOr("after_action", DialogAction::decode, DialogAction.CLOSE, wrapper);
List<DialogBody> body = compound.getListOrEmpty("body", DialogBody::decode, wrapper);
List<Input> inputs = compound.getListOrEmpty("inputs", Input::decode, wrapper);
return new CommonDialogData(title, externalTitle, canCloseWithEscape, pause, afterAction, body, inputs);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, CommonDialogData data) {
compound.set("title", data.title, AdventureSerializer.serializer(wrapper), wrapper);
if (data.externalTitle != null) {
compound.set("external_title", data.externalTitle, AdventureSerializer.serializer(wrapper), wrapper);
}
if (!data.canCloseWithEscape) {
compound.setTag("can_close_with_escape", new NBTByte(false));
}
if (!data.pause) {
compound.setTag("pause", new NBTByte(false));
}
if (data.afterAction != DialogAction.CLOSE) {
compound.set("after_action", data.afterAction, DialogAction::encode, wrapper);
}
if (!data.body.isEmpty()) {
compound.setCompactList("body", data.body, DialogBody::encode, wrapper);
}
if (!data.inputs.isEmpty()) {
compound.setList("inputs", data.inputs, Input::encode, wrapper);
}
}
public Component getTitle() {
return this.title;
}
public @Nullable Component getExternalTitle() {
return this.externalTitle;
}
public boolean isCanCloseWithEscape() {
return this.canCloseWithEscape;
}
public boolean isPause() {
return this.pause;
}
public DialogAction getAfterAction() {
return this.afterAction;
}
public List<DialogBody> getBody() {
return this.body;
}
public List<Input> getInputs() {
return this.inputs;
}
}
@@ -0,0 +1,103 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.dialog.button.ActionButton;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.util.Objects;
@NullMarked
public class ConfirmationDialog extends AbstractMappedEntity implements Dialog {
private final CommonDialogData common;
private final ActionButton yesButton;
private final ActionButton noButton;
public ConfirmationDialog(CommonDialogData common, ActionButton yesButton, ActionButton noButton) {
this(null, common, yesButton, noButton);
}
@ApiStatus.Internal
public ConfirmationDialog(
@Nullable TypesBuilderData data, CommonDialogData common,
ActionButton yesButton, ActionButton noButton
) {
super(data);
this.common = common;
this.yesButton = yesButton;
this.noButton = noButton;
}
public static ConfirmationDialog decode(NBTCompound compound, PacketWrapper<?> wrapper) {
CommonDialogData common = CommonDialogData.decode(compound, wrapper);
ActionButton yesButton = compound.getOrThrow("yes", ActionButton::decode, wrapper);
ActionButton noButton = compound.getOrThrow("no", ActionButton::decode, wrapper);
return new ConfirmationDialog(null, common, yesButton, noButton);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, ConfirmationDialog dialog) {
CommonDialogData.encode(compound, wrapper, dialog.common);
compound.set("yes", dialog.yesButton, ActionButton::encode, wrapper);
compound.set("no", dialog.noButton, ActionButton::encode, wrapper);
}
@Override
public Dialog copy(@Nullable TypesBuilderData newData) {
return new ConfirmationDialog(newData, this.common, this.yesButton, this.noButton);
}
public CommonDialogData getCommon() {
return this.common;
}
public ActionButton getYesButton() {
return this.yesButton;
}
public ActionButton getNoButton() {
return this.noButton;
}
@Override
public DialogType<?> getType() {
return DialogTypes.CONFIRMATION;
}
@Override
public boolean deepEquals(Object obj) {
if (!(obj instanceof ConfirmationDialog)) return false;
if (!super.equals(obj)) return false;
ConfirmationDialog that = (ConfirmationDialog) obj;
if (!this.common.equals(that.common)) return false;
if (!this.yesButton.equals(that.yesButton)) return false;
return this.noButton.equals(that.noButton);
}
@Override
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.common, this.yesButton, this.noButton);
}
}
@@ -0,0 +1,86 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.mapper.CopyableEntity;
import com.github.retrooper.packetevents.protocol.mapper.DeepComparableEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.dialog.DialogLike;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface Dialog extends MappedEntity, DeepComparableEntity, CopyableEntity<Dialog>, DialogLike {
static Dialog read(PacketWrapper<?> wrapper) {
return wrapper.readMappedEntityOrDirect(Dialogs.getRegistry(), Dialog::readDirect);
}
static void write(PacketWrapper<?> wrapper, Dialog dialog) {
wrapper.writeMappedEntityOrDirect(dialog, Dialog::writeDirect);
}
static Dialog readDirect(PacketWrapper<?> wrapper) {
return decodeDirect(wrapper.readNBTRaw(), wrapper, null);
}
static void writeDirect(PacketWrapper<?> wrapper, Dialog dialog) {
wrapper.writeNBTRaw(encodeDirect(dialog, wrapper));
}
static Dialog decode(NBT nbt, PacketWrapper<?> wrapper) {
if (nbt instanceof NBTString) {
return wrapper.replaceRegistry(Dialogs.getRegistry()).getByNameOrThrow(((NBTString) nbt).getValue());
}
return decodeDirect(nbt, wrapper, null);
}
static NBT encode(PacketWrapper<?> wrapper, Dialog dialog) {
if (dialog.isRegistered()) {
return new NBTString(dialog.getName().toString());
}
return encodeDirect(dialog, wrapper);
}
@ApiStatus.Internal
static Dialog decodeDirect(NBT nbt, PacketWrapper<?> wrapper, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
String dialogTypeName = compound.getStringTagValueOrThrow("type");
DialogType<?> dialogType = DialogTypes.getRegistry().getByNameOrThrow(dialogTypeName);
// TODO pass down data instead of copying
return dialogType.decode(compound, wrapper).copy(data);
}
@ApiStatus.Internal
@SuppressWarnings("unchecked") // not unchecked
static NBT encodeDirect(Dialog dialog, PacketWrapper<?> wrapper) {
NBTCompound compound = new NBTCompound();
compound.setTag("type", new NBTString(dialog.getType().getName().toString()));
((DialogType<? super Dialog>) dialog.getType()).encode(compound, wrapper, dialog);
return compound;
}
DialogType<?> getType();
}
@@ -0,0 +1,61 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.util.adventure.AdventureIndexUtil;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.util.Index;
import org.jspecify.annotations.NullMarked;
@NullMarked
public enum DialogAction {
CLOSE("close", true),
NONE("none", false),
WAIT_FOR_RESPONSE("wait_for_response", true);
public static final Index<String, DialogAction> NAME_INDEX = Index.create(
DialogAction.class, DialogAction::getName);
private final String name;
private final boolean willUnpause;
DialogAction(String name, boolean willUnpause) {
this.name = name;
this.willUnpause = willUnpause;
}
public static DialogAction decode(NBT nbt, PacketWrapper<?> wrapper) {
return AdventureIndexUtil.indexValueOrThrow(NAME_INDEX, ((NBTString) nbt).getValue());
}
public static NBT encode(PacketWrapper<?> wrapper, DialogAction action) {
return new NBTString(action.name);
}
public String getName() {
return this.name;
}
public boolean isWillUnpause() {
return this.willUnpause;
}
}
@@ -0,0 +1,138 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.dialog.button.ActionButton;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntityRefSet;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.util.Objects;
@NullMarked
public class DialogListDialog extends AbstractMappedEntity implements Dialog {
private final CommonDialogData common;
private final MappedEntityRefSet<Dialog> dialogs;
private final @Nullable ActionButton exitAction;
private final int columns;
private final int buttonWidth;
public DialogListDialog(
CommonDialogData common,
MappedEntityRefSet<Dialog> dialogs,
@Nullable ActionButton exitAction,
int columns, int buttonWidth
) {
this(null, common, dialogs, exitAction, columns, buttonWidth);
}
@ApiStatus.Internal
public DialogListDialog(
@Nullable TypesBuilderData data,
CommonDialogData common,
MappedEntityRefSet<Dialog> dialogs,
@Nullable ActionButton exitAction,
int columns, int buttonWidth
) {
super(data);
this.common = common;
this.dialogs = dialogs;
this.exitAction = exitAction;
this.columns = columns;
this.buttonWidth = buttonWidth;
}
public static DialogListDialog decode(NBTCompound compound, PacketWrapper<?> wrapper) {
CommonDialogData common = CommonDialogData.decode(compound, wrapper);
MappedEntityRefSet<Dialog> dialogs = compound.getOrThrow("dialogs", MappedEntitySet::decodeRefSet, wrapper);
ActionButton action = compound.getOrNull("exit_action", ActionButton::decode, wrapper);
int columns = compound.getNumberTagValueOrDefault("columns", 2).intValue();
int buttonWidth = compound.getNumberTagValueOrDefault("button_width", 150).intValue();
return new DialogListDialog(null, common, dialogs, action, columns, buttonWidth);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, DialogListDialog dialog) {
CommonDialogData.encode(compound, wrapper, dialog.common);
compound.set("dialogs", dialog.dialogs, MappedEntitySet::encodeRefSet, wrapper);
if (dialog.exitAction != null) {
compound.set("exit_action", dialog.exitAction, ActionButton::encode, wrapper);
}
if (dialog.columns != 2) {
compound.setTag("columns", new NBTInt(dialog.columns));
}
if (dialog.buttonWidth != 150) {
compound.setTag("button_width", new NBTInt(dialog.buttonWidth));
}
}
@Override
public Dialog copy(@Nullable TypesBuilderData newData) {
return new DialogListDialog(newData, this.common, this.dialogs, this.exitAction, this.columns, this.buttonWidth);
}
public CommonDialogData getCommon() {
return this.common;
}
public MappedEntityRefSet<Dialog> getDialogs() {
return this.dialogs;
}
public @Nullable ActionButton getExitAction() {
return this.exitAction;
}
public int getColumns() {
return this.columns;
}
public int getButtonWidth() {
return this.buttonWidth;
}
@Override
public DialogType<?> getType() {
return DialogTypes.DIALOG_LIST;
}
@Override
public boolean deepEquals(Object obj) {
if (!(obj instanceof DialogListDialog)) return false;
if (!super.equals(obj)) return false;
DialogListDialog that = (DialogListDialog) obj;
if (this.columns != that.columns) return false;
if (this.buttonWidth != that.buttonWidth) return false;
if (!this.common.equals(that.common)) return false;
if (!this.dialogs.equals(that.dialogs)) return false;
return Objects.equals(this.exitAction, that.exitAction);
}
@Override
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.common, this.dialogs, this.exitAction, this.columns, this.buttonWidth);
}
}
@@ -0,0 +1,32 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface DialogType<T extends Dialog> extends MappedEntity {
T decode(NBTCompound compound, PacketWrapper<?> wrapper);
void encode(NBTCompound compound, PacketWrapper<?> wrapper, T dialog);
}
@@ -0,0 +1,58 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.util.NbtMapDecoder;
import com.github.retrooper.packetevents.protocol.util.NbtMapEncoder;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@NullMarked
public final class DialogTypes {
private static final VersionedRegistry<DialogType<?>> REGISTRY = new VersionedRegistry<>("dialog_type");
private DialogTypes() {
}
public static VersionedRegistry<DialogType<?>> getRegistry() {
return REGISTRY;
}
@ApiStatus.Internal
public static <T extends Dialog> DialogType<T> define(String name, NbtMapDecoder<T> decoder, NbtMapEncoder<T> encoder) {
return REGISTRY.define(name, data -> new StaticDialogType<>(data, decoder, encoder));
}
public static final DialogType<NoticeDialog> NOTICE = define("notice",
NoticeDialog::decode, NoticeDialog::encode);
public static final DialogType<ServerLinksDialog> SERVER_LINKS = define("server_links",
ServerLinksDialog::decode, ServerLinksDialog::encode);
public static final DialogType<DialogListDialog> DIALOG_LIST = define("dialog_list",
DialogListDialog::decode, DialogListDialog::encode);
public static final DialogType<MultiActionDialog> MULTI_ACTION = define("multi_action",
MultiActionDialog::decode, MultiActionDialog::encode);
public static final DialogType<ConfirmationDialog> CONFIRMATION = define("confirmation",
ConfirmationDialog::decode, ConfirmationDialog::encode);
static {
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,101 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.dialog.button.ActionButton;
import com.github.retrooper.packetevents.protocol.dialog.button.CommonButtonData;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import java.util.Collections;
@NullMarked
public final class Dialogs {
private static final ActionButton DEFAULT_BACK_BUTTON = new ActionButton(
new CommonButtonData(Component.translatable("gui.back"), null, 200),
null);
private static final VersionedRegistry<Dialog> REGISTRY = new VersionedRegistry<>("dialog");
private Dialogs() {
}
@ApiStatus.Internal
public static Dialog define(String name, Dialog dialog) {
return REGISTRY.define(name, dialog::copy);
}
public static VersionedRegistry<Dialog> getRegistry() {
return REGISTRY;
}
public static final Dialog SERVER_LINKS = define("server_links", new ServerLinksDialog(
new CommonDialogData(
Component.translatable("menu.server_links.title"),
Component.translatable("menu.server_links"),
true,
true,
DialogAction.CLOSE,
Collections.emptyList(),
Collections.emptyList()
),
DEFAULT_BACK_BUTTON,
1,
310
));
public static final Dialog CUSTOM_OPTIONS = define("custom_options", new DialogListDialog(
new CommonDialogData(
Component.translatable("menu.custom_options.title"),
Component.translatable("menu.custom_options"),
true,
true,
DialogAction.CLOSE,
Collections.emptyList(),
Collections.emptyList()
),
new MappedEntitySet<>(new ResourceLocation("pause_screen_additions")),
DEFAULT_BACK_BUTTON,
1,
310
));
public static final Dialog QUICK_ACTIONS = define("quick_actions", new DialogListDialog(
new CommonDialogData(
Component.translatable("menu.quick_actions.title"),
Component.translatable("menu.quick_actions"),
true,
true,
DialogAction.CLOSE,
Collections.emptyList(),
Collections.emptyList()
),
new MappedEntitySet<>(new ResourceLocation("quick_actions")),
DEFAULT_BACK_BUTTON,
1,
310
));
static {
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,127 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.dialog.button.ActionButton;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.util.List;
import java.util.Objects;
@NullMarked
public class MultiActionDialog extends AbstractMappedEntity implements Dialog {
private final CommonDialogData common;
private final List<ActionButton> actions;
private final @Nullable ActionButton exitAction;
private final int columns;
public MultiActionDialog(
CommonDialogData common,
List<ActionButton> actions,
@Nullable ActionButton exitAction,
int columns
) {
this(null, common, actions, exitAction, columns);
}
@ApiStatus.Internal
public MultiActionDialog(
@Nullable TypesBuilderData data,
CommonDialogData common,
List<ActionButton> actions,
@Nullable ActionButton exitAction,
int columns
) {
super(data);
this.common = common;
this.actions = actions;
this.exitAction = exitAction;
this.columns = columns;
}
public static MultiActionDialog decode(NBTCompound compound, PacketWrapper<?> wrapper) {
CommonDialogData common = CommonDialogData.decode(compound, wrapper);
List<ActionButton> actions = compound.getListOrThrow("actions", ActionButton::decode, wrapper);
ActionButton action = compound.getOrNull("exit_action", ActionButton::decode, wrapper);
int columns = compound.getNumberTagValueOrDefault("columns", 2).intValue();
int buttonWidth = compound.getNumberTagValueOrDefault("button_width", 150).intValue();
return new MultiActionDialog(null, common, actions, action, columns);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, MultiActionDialog dialog) {
CommonDialogData.encode(compound, wrapper, dialog.common);
compound.setList("actions", dialog.actions, ActionButton::encode, wrapper);
if (dialog.exitAction != null) {
compound.set("exit_action", dialog.exitAction, ActionButton::encode, wrapper);
}
if (dialog.columns != 2) {
compound.setTag("columns", new NBTInt(dialog.columns));
}
}
@Override
public Dialog copy(@Nullable TypesBuilderData newData) {
return new MultiActionDialog(newData, this.common, this.actions, this.exitAction, this.columns);
}
public CommonDialogData getCommon() {
return this.common;
}
public List<ActionButton> getActions() {
return this.actions;
}
public @Nullable ActionButton getExitAction() {
return this.exitAction;
}
public int getColumns() {
return this.columns;
}
@Override
public DialogType<?> getType() {
return DialogTypes.MULTI_ACTION;
}
@Override
public boolean deepEquals(Object obj) {
if (!(obj instanceof MultiActionDialog)) return false;
if (!super.equals(obj)) return false;
MultiActionDialog that = (MultiActionDialog) obj;
if (this.columns != that.columns) return false;
if (!this.common.equals(that.common)) return false;
if (!this.actions.equals(that.actions)) return false;
return Objects.equals(this.exitAction, that.exitAction);
}
@Override
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.common, this.actions, this.exitAction, this.columns);
}
}
@@ -0,0 +1,100 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.dialog.button.ActionButton;
import com.github.retrooper.packetevents.protocol.dialog.button.CommonButtonData;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.util.Objects;
@NullMarked
public class NoticeDialog extends AbstractMappedEntity implements Dialog {
public static final ActionButton DEFAULT_ACTION = new ActionButton(
new CommonButtonData(Component.translatable("gui.ok"), null, 150),
null
);
private final CommonDialogData common;
private final ActionButton action;
public NoticeDialog(CommonDialogData common, ActionButton action) {
this(null, common, action);
}
@ApiStatus.Internal
public NoticeDialog(@Nullable TypesBuilderData data, CommonDialogData common, ActionButton action) {
super(data);
this.common = common;
this.action = action;
}
public static NoticeDialog decode(NBTCompound compound, PacketWrapper<?> wrapper) {
CommonDialogData common = CommonDialogData.decode(compound, wrapper);
ActionButton action = compound.getOr("action", ActionButton::decode, DEFAULT_ACTION, wrapper);
return new NoticeDialog(null, common, action);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, NoticeDialog dialog) {
CommonDialogData.encode(compound, wrapper, dialog.common);
if (dialog.action != DEFAULT_ACTION) {
compound.set("action", dialog.action, ActionButton::encode, wrapper);
}
}
@Override
public Dialog copy(@Nullable TypesBuilderData newData) {
return new NoticeDialog(newData, this.common, this.action);
}
public CommonDialogData getCommon() {
return this.common;
}
public ActionButton getAction() {
return this.action;
}
@Override
public DialogType<?> getType() {
return DialogTypes.NOTICE;
}
@Override
public boolean deepEquals(Object obj) {
if (!(obj instanceof NoticeDialog)) return false;
if (!super.equals(obj)) return false;
NoticeDialog that = (NoticeDialog) obj;
if (!this.common.equals(that.common)) return false;
return this.action.equals(that.action);
}
@Override
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.common, this.action);
}
}
@@ -0,0 +1,125 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.dialog.button.ActionButton;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.util.Objects;
@NullMarked
public class ServerLinksDialog extends AbstractMappedEntity implements Dialog {
private final CommonDialogData common;
private final @Nullable ActionButton exitAction;
private final int columns;
private final int buttonWidth;
public ServerLinksDialog(
CommonDialogData common,
@Nullable ActionButton exitAction,
int columns, int buttonWidth
) {
this(null, common, exitAction, columns, buttonWidth);
}
@ApiStatus.Internal
public ServerLinksDialog(
@Nullable TypesBuilderData data,
CommonDialogData common,
@Nullable ActionButton exitAction,
int columns, int buttonWidth
) {
super(data);
this.common = common;
this.exitAction = exitAction;
this.columns = columns;
this.buttonWidth = buttonWidth;
}
public static ServerLinksDialog decode(NBTCompound compound, PacketWrapper<?> wrapper) {
CommonDialogData common = CommonDialogData.decode(compound, wrapper);
ActionButton action = compound.getOrNull("exit_action", ActionButton::decode, wrapper);
int columns = compound.getNumberTagValueOrDefault("columns", 2).intValue();
int buttonWidth = compound.getNumberTagValueOrDefault("button_width", 150).intValue();
return new ServerLinksDialog(null, common, action, columns, buttonWidth);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, ServerLinksDialog dialog) {
CommonDialogData.encode(compound, wrapper, dialog.common);
if (dialog.exitAction != null) {
compound.set("exit_action", dialog.exitAction, ActionButton::encode, wrapper);
}
if (dialog.columns != 2) {
compound.setTag("columns", new NBTInt(dialog.columns));
}
if (dialog.buttonWidth != 150) {
compound.setTag("button_width", new NBTInt(dialog.buttonWidth));
}
}
@Override
public Dialog copy(@Nullable TypesBuilderData newData) {
return new ServerLinksDialog(newData, this.common, this.exitAction, this.columns, this.buttonWidth);
}
public CommonDialogData getCommon() {
return this.common;
}
public @Nullable ActionButton getExitAction() {
return this.exitAction;
}
public int getColumns() {
return this.columns;
}
public int getButtonWidth() {
return this.buttonWidth;
}
@Override
public DialogType<?> getType() {
return DialogTypes.SERVER_LINKS;
}
@Override
public boolean deepEquals(Object obj) {
if (!(obj instanceof ServerLinksDialog)) return false;
if (!super.equals(obj)) return false;
ServerLinksDialog that = (ServerLinksDialog) obj;
if (this.columns != that.columns) return false;
if (this.buttonWidth != that.buttonWidth) return false;
if (!this.common.equals(that.common)) return false;
return Objects.equals(this.exitAction, that.exitAction);
}
@Override
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.common, this.exitAction, this.columns, this.buttonWidth);
}
}
@@ -0,0 +1,53 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.util.NbtMapDecoder;
import com.github.retrooper.packetevents.protocol.util.NbtMapEncoder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class StaticDialogType<T extends Dialog> extends AbstractMappedEntity implements DialogType<T> {
private final NbtMapDecoder<T> decoder;
private final NbtMapEncoder<T> encoder;
@ApiStatus.Internal
public StaticDialogType(@Nullable TypesBuilderData data, NbtMapDecoder<T> decoder, NbtMapEncoder<T> encoder) {
super(data);
this.decoder = decoder;
this.encoder = encoder;
}
@Override
public T decode(NBTCompound compound, PacketWrapper<?> wrapper) {
return this.decoder.decode(compound, wrapper);
}
@Override
public void encode(NBTCompound compound, PacketWrapper<?> wrapper, T dialog) {
this.encoder.encode(compound, wrapper, dialog);
}
}
@@ -0,0 +1,46 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.action;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface Action {
static Action decode(NBT nbt, PacketWrapper<?> wrapper) {
NBTCompound compound = (NBTCompound) nbt;
String typeName = compound.getStringTagValueOrThrow("type");
ActionType<?> action = ActionTypes.getRegistry().getByNameOrThrow(typeName);
return action.decode(compound, wrapper);
}
@SuppressWarnings("unchecked") // not unchecked
static NBT encode(PacketWrapper<?> wrapper, Action action) {
NBTCompound compound = new NBTCompound();
compound.set("type", action.getType().getName(), ResourceLocation::encode, wrapper);
((ActionType<? super Action>) action.getType()).encode(compound, wrapper, action);
return compound;
}
ActionType<?> getType();
}
@@ -0,0 +1,32 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.action;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface ActionType<T extends Action> extends MappedEntity {
T decode(NBTCompound compound, PacketWrapper<?> wrapper);
void encode(NBTCompound compound, PacketWrapper<?> wrapper, T action);
}
@@ -0,0 +1,66 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.action;
import com.github.retrooper.packetevents.protocol.util.NbtMapDecoder;
import com.github.retrooper.packetevents.protocol.util.NbtMapEncoder;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@NullMarked
public final class ActionTypes {
private static final VersionedRegistry<ActionType<?>> REGISTRY = new VersionedRegistry<>("dialog_action_type");
private ActionTypes() {
}
@ApiStatus.Internal
public static <T extends Action> ActionType<T> define(String name, NbtMapDecoder<T> decoder, NbtMapEncoder<T> encoder) {
return REGISTRY.define(name, data -> new StaticActionType<>(data, decoder, encoder));
}
public static final ActionType<StaticAction> OPEN_URL = define("open_url",
StaticAction::decode, StaticAction::encode);
public static final ActionType<StaticAction> RUN_COMMAND = define("run_command",
StaticAction::decode, StaticAction::encode);
public static final ActionType<StaticAction> SUGGEST_COMMAND = define("suggest_command",
StaticAction::decode, StaticAction::encode);
public static final ActionType<StaticAction> SHOW_DIALOG = define("show_dialog",
StaticAction::decode, StaticAction::encode);
public static final ActionType<StaticAction> CHANGE_PAGE = define("change_page",
StaticAction::decode, StaticAction::encode);
public static final ActionType<StaticAction> COPY_TO_CLIPBOARD = define("copy_to_clipboard",
StaticAction::decode, StaticAction::encode);
public static final ActionType<StaticAction> CUSTOM = define("custom",
StaticAction::decode, StaticAction::encode);
public static final ActionType<DynamicRunCommandAction> DYNAMIC_RUN_COMMAND = define("dynamic/run_command",
DynamicRunCommandAction::decode, DynamicRunCommandAction::encode);
public static final ActionType<DynamicCustomAction> DYNAMIC_CUSTOM = define("dynamic/custom",
DynamicCustomAction::decode, DynamicCustomAction::encode);
public static VersionedRegistry<ActionType<?>> getRegistry() {
return REGISTRY;
}
static {
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,49 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.action;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
/**
* This uses the same format as command function macros.
*/
@NullMarked
public class DialogTemplate {
private final String raw;
public DialogTemplate(String raw) {
this.raw = raw;
}
public static DialogTemplate decode(NBT nbt, PacketWrapper<?> wrapper) {
return new DialogTemplate(((NBTString) nbt).getValue());
}
public static NBT encode(PacketWrapper<?> wrapper, DialogTemplate template) {
return new NBTString(template.raw);
}
public String getRaw() {
return this.raw;
}
}
@@ -0,0 +1,63 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.action;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class DynamicCustomAction implements Action {
private final ResourceLocation id;
private final @Nullable NBTCompound additions;
public DynamicCustomAction(ResourceLocation id, @Nullable NBTCompound additions) {
this.id = id;
this.additions = additions;
}
public static DynamicCustomAction decode(NBTCompound compound, PacketWrapper<?> wrapper) {
ResourceLocation id = compound.getOrThrow("id", ResourceLocation::decode, wrapper);
NBTCompound additions = compound.getCompoundTagOrNull("additions");
return new DynamicCustomAction(id, additions);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, DynamicCustomAction action) {
compound.set("id", action.id, ResourceLocation::encode, wrapper);
if (action.additions != null) {
compound.setTag("additions", action.additions);
}
}
@Override
public ActionType<?> getType() {
return ActionTypes.DYNAMIC_CUSTOM;
}
public ResourceLocation getId() {
return this.id;
}
public @Nullable NBTCompound getAdditions() {
return this.additions;
}
}
@@ -0,0 +1,51 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.action;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class DynamicRunCommandAction implements Action {
private final DialogTemplate template;
public DynamicRunCommandAction(DialogTemplate template) {
this.template = template;
}
public static DynamicRunCommandAction decode(NBTCompound compound, PacketWrapper<?> wrapper) {
DialogTemplate template = compound.getOrThrow("template", DialogTemplate::decode, wrapper);
return new DynamicRunCommandAction(template);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, DynamicRunCommandAction action) {
compound.set("template", action.template, DialogTemplate::encode, wrapper);
}
public DialogTemplate getTemplate() {
return this.template;
}
@Override
public ActionType<?> getType() {
return ActionTypes.DYNAMIC_RUN_COMMAND;
}
}
@@ -0,0 +1,63 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.action;
import com.github.retrooper.packetevents.protocol.chat.clickevent.ClickEvent;
import com.github.retrooper.packetevents.protocol.chat.clickevent.ClickEventAction;
import com.github.retrooper.packetevents.protocol.chat.clickevent.ClickEventActions;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class StaticAction implements Action {
private final ActionType<?> actionType;
private final ClickEvent clickEvent;
public StaticAction(ClickEvent clickEvent) {
if (!clickEvent.getAction().isAllowFromServer()) {
throw new IllegalArgumentException("Can't create action for unreadable "
+ "click event with action " + clickEvent.getAction());
}
this.actionType = ActionTypes.getRegistry().getByNameOrThrow(clickEvent.getAction().getName());
this.clickEvent = clickEvent;
}
public static StaticAction decode(NBTCompound compound, PacketWrapper<?> wrapper) {
String actionName = compound.getStringTagValueOrThrow("type");
ClickEventAction<?> action = ClickEventActions.getRegistry().getByNameOrThrow(actionName);
ClickEvent clickEvent = action.decode(compound, wrapper);
return new StaticAction(clickEvent);
}
@SuppressWarnings("unchecked")
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, StaticAction action) {
((ClickEventAction<? super ClickEvent>) action.clickEvent.getAction()).encode(compound, wrapper, action.clickEvent);
}
@Override
public ActionType<?> getType() {
return this.actionType;
}
public ClickEvent getClickEvent() {
return this.clickEvent;
}
}
@@ -0,0 +1,53 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.action;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.util.NbtMapDecoder;
import com.github.retrooper.packetevents.protocol.util.NbtMapEncoder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class StaticActionType<T extends Action> extends AbstractMappedEntity implements ActionType<T> {
private final NbtMapDecoder<T> decoder;
private final NbtMapEncoder<T> encoder;
@ApiStatus.Internal
public StaticActionType(@Nullable TypesBuilderData data, NbtMapDecoder<T> decoder, NbtMapEncoder<T> encoder) {
super(data);
this.decoder = decoder;
this.encoder = encoder;
}
@Override
public T decode(NBTCompound compound, PacketWrapper<?> wrapper) {
return this.decoder.decode(compound, wrapper);
}
@Override
public void encode(NBTCompound compound, PacketWrapper<?> wrapper, T action) {
this.encoder.encode(compound, wrapper, action);
}
}
@@ -0,0 +1,46 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.body;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface DialogBody {
static DialogBody decode(NBT nbt, PacketWrapper<?> wrapper) {
NBTCompound compound = (NBTCompound) nbt;
String typeName = compound.getStringTagValueOrThrow("type");
DialogBodyType<?> type = DialogBodyTypes.getRegistry().getByNameOrThrow(typeName);
return type.decode(compound, wrapper);
}
@SuppressWarnings("unchecked") // not unchecked
static NBT encode(PacketWrapper<?> wrapper, DialogBody body) {
NBTCompound compound = new NBTCompound();
compound.set("type", body.getType().getName(), ResourceLocation::encode, wrapper);
((DialogBodyType<? super DialogBody>) body.getType()).encode(compound, wrapper, body);
return compound;
}
DialogBodyType<?> getType();
}
@@ -0,0 +1,32 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.body;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface DialogBodyType<T extends DialogBody> extends MappedEntity {
T decode(NBTCompound compound, PacketWrapper<?> wrapper);
void encode(NBTCompound compound, PacketWrapper<?> wrapper, T dialogBody);
}
@@ -0,0 +1,52 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.body;
import com.github.retrooper.packetevents.protocol.util.NbtMapDecoder;
import com.github.retrooper.packetevents.protocol.util.NbtMapEncoder;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@NullMarked
public final class DialogBodyTypes {
private static final VersionedRegistry<DialogBodyType<?>> REGISTRY = new VersionedRegistry<>("dialog_body_type");
private DialogBodyTypes() {
}
@ApiStatus.Internal
public static <T extends DialogBody> DialogBodyType<T> define(String name, NbtMapDecoder<T> decoder, NbtMapEncoder<T> encoder) {
return REGISTRY.define(name, data -> new StaticDialogBodyType<>(data, decoder, encoder));
}
public static VersionedRegistry<DialogBodyType<?>> getRegistry() {
return REGISTRY;
}
public static final DialogBodyType<ItemDialogBody> ITEM = define("item",
ItemDialogBody::decode, ItemDialogBody::encode);
public static final DialogBodyType<PlainMessageDialogBody> PLAIN_MESSAGE = define("plain_message",
PlainMessageDialogBody::decode, PlainMessageDialogBody::encode);
static {
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,109 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.body;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.nbt.NBTByte;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class ItemDialogBody implements DialogBody {
private final ItemStack item;
private final @Nullable PlainMessage description;
private final boolean showDecorations;
private final boolean showTooltip;
private final int width;
private final int height;
public ItemDialogBody(
ItemStack item, @Nullable PlainMessage description,
boolean showDecorations, boolean showTooltip,
int width, int height
) {
this.item = item;
this.description = description;
this.showDecorations = showDecorations;
this.showTooltip = showTooltip;
this.width = width;
this.height = height;
}
public static ItemDialogBody decode(NBTCompound compound, PacketWrapper<?> wrapper) {
ItemStack item = compound.getOrThrow("item", ItemStack::decode, wrapper);
PlainMessage description = compound.getOrNull("description", PlainMessage::decode, wrapper);
boolean showDecorations = compound.getBooleanOr("show_decorations", true);
boolean showTooltip = compound.getBooleanOr("show_tooltip", true);
int width = compound.getNumberTagValueOrDefault("width", 16).intValue();
int height = compound.getNumberTagValueOrDefault("height", 16).intValue();
return new ItemDialogBody(item, description, showDecorations, showTooltip, width, height);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, ItemDialogBody body) {
compound.set("item", body.item, ItemStack::encode, wrapper);
if (body.description != null) {
compound.set("description", body.description, PlainMessage::encode, wrapper);
}
if (!body.showDecorations) {
compound.setTag("show_decorations", new NBTByte(false));
}
if (!body.showTooltip) {
compound.setTag("show_tooltip", new NBTByte(false));
}
if (body.width != 16) {
compound.setTag("width", new NBTInt(body.width));
}
if (body.height != 16) {
compound.setTag("height", new NBTInt(body.height));
}
}
@Override
public DialogBodyType<?> getType() {
return DialogBodyTypes.ITEM;
}
public ItemStack getItem() {
return this.item;
}
public @Nullable PlainMessage getDescription() {
return this.description;
}
public boolean isShowDecorations() {
return this.showDecorations;
}
public boolean isShowTooltip() {
return this.showTooltip;
}
public int getWidth() {
return this.width;
}
public int getHeight() {
return this.height;
}
}
@@ -0,0 +1,70 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.body;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class PlainMessage {
private final Component contents;
private final int width;
public PlainMessage(Component contents, int width) {
this.contents = contents;
this.width = width;
}
public static PlainMessage decode(NBT nbt, PacketWrapper<?> wrapper) {
return decode((NBTCompound) nbt, wrapper);
}
public static PlainMessage decode(NBTCompound compound, PacketWrapper<?> wrapper) {
Component contents = compound.getOrThrow("contents", AdventureSerializer.serializer(wrapper), wrapper);
int width = compound.getNumberTagValueOrDefault("width", 200).intValue();
return new PlainMessage(contents, width);
}
public static NBT encode(PacketWrapper<?> wrapper, PlainMessage message) {
NBTCompound compound = new NBTCompound();
encode(compound, wrapper, message);
return compound;
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, PlainMessage message) {
compound.set("contents", message.contents, AdventureSerializer.serializer(wrapper), wrapper);
if (message.width != 200) {
compound.setTag("width", new NBTInt(message.width));
}
}
public Component getContents() {
return this.contents;
}
public int getWidth() {
return this.width;
}
}
@@ -0,0 +1,50 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.body;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class PlainMessageDialogBody implements DialogBody {
private final PlainMessage message;
public PlainMessageDialogBody(PlainMessage message) {
this.message = message;
}
public static PlainMessageDialogBody decode(NBTCompound compound, PacketWrapper<?> wrapper) {
return new PlainMessageDialogBody(PlainMessage.decode(compound, wrapper));
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, PlainMessageDialogBody body) {
PlainMessage.encode(compound, wrapper, body.message);
}
@Override
public DialogBodyType<?> getType() {
return DialogBodyTypes.PLAIN_MESSAGE;
}
public PlainMessage getMessage() {
return this.message;
}
}
@@ -0,0 +1,53 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.body;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.util.NbtMapDecoder;
import com.github.retrooper.packetevents.protocol.util.NbtMapEncoder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class StaticDialogBodyType<T extends DialogBody> extends AbstractMappedEntity implements DialogBodyType<T> {
private final NbtMapDecoder<T> decoder;
private final NbtMapEncoder<T> encoder;
@ApiStatus.Internal
public StaticDialogBodyType(@Nullable TypesBuilderData data, NbtMapDecoder<T> decoder, NbtMapEncoder<T> encoder) {
super(data);
this.decoder = decoder;
this.encoder = encoder;
}
@Override
public T decode(NBTCompound compound, PacketWrapper<?> wrapper) {
return this.decoder.decode(compound, wrapper);
}
@Override
public void encode(NBTCompound compound, PacketWrapper<?> wrapper, T dialogBody) {
this.encoder.encode(compound, wrapper, dialogBody);
}
}
@@ -0,0 +1,62 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.button;
import com.github.retrooper.packetevents.protocol.dialog.action.Action;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class ActionButton {
private final CommonButtonData button;
private final @Nullable Action action;
public ActionButton(CommonButtonData button, @Nullable Action action) {
this.button = button;
this.action = action;
}
public static ActionButton decode(NBT nbt, PacketWrapper<?> wrapper) {
NBTCompound compound = (NBTCompound) nbt;
CommonButtonData button = CommonButtonData.decode(compound, wrapper);
Action action = compound.getOrNull("action", Action::decode, wrapper);
return new ActionButton(button, action);
}
public static NBT encode(PacketWrapper<?> wrapper, ActionButton button) {
NBTCompound compound = new NBTCompound();
CommonButtonData.encode(compound, wrapper, button.button);
if (button.action != null) {
compound.set("action", button.action, Action::encode, wrapper);
}
return compound;
}
public CommonButtonData getButton() {
return this.button;
}
public @Nullable Action getAction() {
return this.action;
}
}
@@ -0,0 +1,72 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.button;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class CommonButtonData {
private final Component label;
private final @Nullable Component tooltip;
private final int width;
public CommonButtonData(Component label, @Nullable Component tooltip, int width) {
this.label = label;
this.tooltip = tooltip;
this.width = width;
}
public static CommonButtonData decode(NBTCompound compound, PacketWrapper<?> wrapper) {
AdventureSerializer serializer = AdventureSerializer.serializer(wrapper);
Component label = compound.getOrThrow("label", serializer, wrapper);
Component tooltip = compound.getOrNull("tooltip", serializer, wrapper);
int width = compound.getNumberTagValueOrDefault("width", 150).intValue();
return new CommonButtonData(label, tooltip, width);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, CommonButtonData button) {
AdventureSerializer serializer = AdventureSerializer.serializer(wrapper);
compound.set("label", button.label, serializer, wrapper);
if (button.tooltip != null) {
compound.set("tooltip", button.tooltip, serializer, wrapper);
}
if (button.width != 150) {
compound.setTag("width", new NBTInt(button.width));
}
}
public Component getLabel() {
return this.label;
}
public @Nullable Component getTooltip() {
return this.tooltip;
}
public int getWidth() {
return this.width;
}
}
@@ -0,0 +1,85 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.input;
import com.github.retrooper.packetevents.protocol.nbt.NBTByte;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class BooleanInputControl implements InputControl {
private final Component label;
private final boolean initial;
private final String onTrue;
private final String onFalse;
public BooleanInputControl(Component label, boolean initial, String onTrue, String onFalse) {
this.label = label;
this.initial = initial;
this.onTrue = onTrue;
this.onFalse = onFalse;
}
public static BooleanInputControl decode(NBTCompound compound, PacketWrapper<?> wrapper) {
Component label = compound.getOrThrow("label", AdventureSerializer.serializer(wrapper), wrapper);
boolean initial = compound.getBoolean("initial");
String onTrue = compound.getStringTagValueOrDefault("on_true", "true");
String onFalse = compound.getStringTagValueOrDefault("on_false", "false");
return new BooleanInputControl(label, initial, onTrue, onFalse);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, BooleanInputControl control) {
compound.set("label", control.label, AdventureSerializer.serializer(wrapper), wrapper);
if (control.initial) {
compound.setTag("initial", new NBTByte(true));
}
if (!"true".equals(control.onTrue)) {
compound.setTag("on_true", new NBTString(control.onTrue));
}
if (!"false".equals(control.onFalse)) {
compound.setTag("on_false", new NBTString(control.onFalse));
}
}
@Override
public InputControlType<?> getType() {
return InputControlTypes.BOOLEAN;
}
public Component getLabel() {
return this.label;
}
public boolean isInitial() {
return this.initial;
}
public String getOnTrue() {
return this.onTrue;
}
public String getOnFalse() {
return this.onFalse;
}
}
@@ -0,0 +1,59 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.input;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class Input {
private final String key;
private final InputControl control;
public Input(String key, InputControl control) {
this.key = key;
this.control = control;
}
public static Input decode(NBT nbt, PacketWrapper<?> wrapper) {
NBTCompound compound = (NBTCompound) nbt;
String key = compound.getStringTagValueOrThrow("key");
InputControl control = InputControl.decode(compound, wrapper);
return new Input(key, control);
}
public static NBT encode(PacketWrapper<?> wrapper, Input input) {
NBTCompound compound = new NBTCompound();
compound.setTag("key", new NBTString(input.key));
InputControl.encode(compound, wrapper, input.control);
return compound;
}
public String getKey() {
return this.key;
}
public InputControl getControl() {
return this.control;
}
}
@@ -0,0 +1,42 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.input;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface InputControl {
static InputControl decode(NBTCompound compound, PacketWrapper<?> wrapper) {
String typeName = compound.getStringTagValueOrThrow("type");
InputControlType<?> type = InputControlTypes.getRegistry().getByNameOrThrow(typeName);
return type.decode(compound, wrapper);
}
@SuppressWarnings("unchecked") // not unchecked
static void encode(NBTCompound compound, PacketWrapper<?> wrapper, InputControl control) {
compound.set("type", control.getType().getName(), ResourceLocation::encode, wrapper);
((InputControlType<? super InputControl>) control.getType()).encode(compound, wrapper, control);
}
InputControlType<?> getType();
}
@@ -0,0 +1,32 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.input;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface InputControlType<T extends InputControl> extends MappedEntity {
T decode(NBTCompound compound, PacketWrapper<?> wrapper);
void encode(NBTCompound compound, PacketWrapper<?> wrapper, T control);
}
@@ -0,0 +1,59 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.input;
import com.github.retrooper.packetevents.protocol.util.NbtMapDecoder;
import com.github.retrooper.packetevents.protocol.util.NbtMapEncoder;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@NullMarked
public final class InputControlTypes {
private static final VersionedRegistry<InputControlType<?>> REGISTRY = new VersionedRegistry<>("input_control_type");
private InputControlTypes() {
}
public static VersionedRegistry<InputControlType<?>> getRegistry() {
return REGISTRY;
}
@ApiStatus.Internal
public static <T extends InputControl> InputControlType<T> define(
String name, NbtMapDecoder<T> decoder, NbtMapEncoder<T> encoder
) {
return REGISTRY.define(name, data ->
new StaticInputControlType<>(data, decoder, encoder));
}
public static final InputControlType<BooleanInputControl> BOOLEAN = define("boolean",
BooleanInputControl::decode, BooleanInputControl::encode);
public static final InputControlType<NumberRangeInputControl> NUMBER_RANGE = define("number_range",
NumberRangeInputControl::decode, NumberRangeInputControl::encode);
public static final InputControlType<SingleOptionInputControl> SINGLE_OPTION = define("single_option",
SingleOptionInputControl::decode, SingleOptionInputControl::encode);
public static final InputControlType<TextInputControl> TEXT = define("text",
TextInputControl::decode, TextInputControl::encode);
static {
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,146 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.input;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTNumber;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class NumberRangeInputControl implements InputControl {
private final int width;
private final Component label;
private final String labelFormat;
private final RangeInfo rangeInfo;
public NumberRangeInputControl(int width, Component label, String labelFormat, RangeInfo rangeInfo) {
this.width = width;
this.label = label;
this.labelFormat = labelFormat;
this.rangeInfo = rangeInfo;
}
public static NumberRangeInputControl decode(NBTCompound compound, PacketWrapper<?> wrapper) {
int width = compound.getNumberTagValueOrDefault("width", 200).intValue();
Component label = compound.getOrThrow("label", AdventureSerializer.serializer(wrapper), wrapper);
String labelFormat = compound.getStringTagValueOrDefault("label_format", "options.generic_value");
RangeInfo rangeInfo = RangeInfo.decode(compound, wrapper);
return new NumberRangeInputControl(width, label, labelFormat, rangeInfo);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, NumberRangeInputControl control) {
if (control.width != 200) {
compound.setTag("width", new NBTInt(control.width));
}
compound.set("label", control.label, AdventureSerializer.serializer(wrapper), wrapper);
if (!"options.generic_value".equals(control.labelFormat)) {
compound.setTag("label_format", new NBTString(control.labelFormat));
}
RangeInfo.encode(compound, wrapper, control.rangeInfo);
}
@Override
public InputControlType<?> getType() {
return InputControlTypes.NUMBER_RANGE;
}
public int getWidth() {
return this.width;
}
public Component getLabel() {
return this.label;
}
public String getLabelFormat() {
return this.labelFormat;
}
public RangeInfo getRangeInfo() {
return this.rangeInfo;
}
public static final class RangeInfo {
private final float start;
private final float end;
private final @Nullable Float initial;
private final @Nullable Float step;
public RangeInfo(float start, float end, @Nullable Float initial, @Nullable Float step) {
if (initial != null) {
float min = Math.min(start, end);
float max = Math.max(start, end);
if (initial < min || initial > max) {
throw new IllegalArgumentException("Initial value " + initial
+ " is outside of range [" + min + ", " + max + "]");
}
}
this.start = start;
this.end = end;
this.initial = initial;
this.step = step;
}
public static RangeInfo decode(NBTCompound compound, PacketWrapper<?> wrapper) {
float start = compound.getNumberTagValueOrThrow("start").floatValue();
float end = compound.getNumberTagValueOrThrow("end").floatValue();
NBTNumber initialTag = compound.getNumberTagOrNull("initial");
Float initial = initialTag != null ? initialTag.getAsFloat() : null;
NBTNumber stepTag = compound.getNumberTagOrNull("step");
Float step = stepTag != null ? stepTag.getAsFloat() : null;
return new RangeInfo(start, end, initial, step);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, RangeInfo rangeInfo) {
compound.setTag("start", new NBTFloat(rangeInfo.start));
compound.setTag("end", new NBTFloat(rangeInfo.end));
if (rangeInfo.initial != null) {
compound.setTag("initial", new NBTFloat(rangeInfo.initial));
}
if (rangeInfo.step != null) {
compound.setTag("step", new NBTFloat(rangeInfo.step));
}
}
public float getStart() {
return this.start;
}
public float getEnd() {
return this.end;
}
public @Nullable Float getInitial() {
return this.initial;
}
public @Nullable Float getStep() {
return this.step;
}
}
}
@@ -0,0 +1,148 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.input;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTByte;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.List;
@NullMarked
public class SingleOptionInputControl implements InputControl {
private final int width;
private final List<Entry> options;
private final Component label;
private final boolean labelVisible;
public SingleOptionInputControl(int width, List<Entry> options, Component label, boolean labelVisible) {
boolean initial = false;
for (Entry entry : options) {
if (entry.initial) {
if (initial) {
throw new IllegalArgumentException("Multiple initial values");
}
initial = true;
}
}
this.width = width;
this.options = options;
this.label = label;
this.labelVisible = labelVisible;
}
public static SingleOptionInputControl decode(NBTCompound compound, PacketWrapper<?> wrapper) {
int width = compound.getNumberTagValueOrDefault("width", 200).intValue();
List<Entry> options = compound.getListOrThrow("options", Entry::decode, wrapper);
Component label = compound.getOrThrow("label", AdventureSerializer.serializer(wrapper), wrapper);
boolean labelVisible = compound.getBooleanOr("label_visible", true);
return new SingleOptionInputControl(width, options, label, labelVisible);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, SingleOptionInputControl control) {
if (control.width != 200) {
compound.setTag("width", new NBTInt(control.width));
}
compound.setList("options", control.options, Entry::encode, wrapper);
compound.set("label", control.label, AdventureSerializer.serializer(wrapper), wrapper);
if (!control.labelVisible) {
compound.setTag("label_visible", new NBTByte(false));
}
}
@Override
public InputControlType<?> getType() {
return InputControlTypes.SINGLE_OPTION;
}
public int getWidth() {
return this.width;
}
public List<Entry> getOptions() {
return this.options;
}
public Component getLabel() {
return this.label;
}
public boolean isLabelVisible() {
return this.labelVisible;
}
public static final class Entry {
private final String id;
private final @Nullable Component display;
private final boolean initial;
public Entry(String id, @Nullable Component display, boolean initial) {
this.id = id;
this.display = display;
this.initial = initial;
}
public static Entry decode(NBT nbt, PacketWrapper<?> wrapper) {
if (nbt instanceof NBTString) {
return new Entry(((NBTString) nbt).getValue(), null, false);
}
NBTCompound compound = (NBTCompound) nbt;
String id = compound.getStringTagValueOrThrow("id");
Component display = compound.getOrNull("display", AdventureSerializer.serializer(wrapper), wrapper);
boolean initial = compound.getBooleanOr("initial", false);
return new Entry(id, display, initial);
}
public static NBT encode(PacketWrapper<?> wrapper, Entry entry) {
if (entry.display == null && !entry.initial) {
return new NBTString(entry.id);
}
NBTCompound compound = new NBTCompound();
compound.setTag("id", new NBTString(entry.id));
if (entry.display != null) {
compound.set("display", entry.display, AdventureSerializer.serializer(wrapper), wrapper);
}
if (entry.initial) {
compound.setTag("initial", new NBTByte(true));
}
return compound;
}
public String getId() {
return this.id;
}
public @Nullable Component getDisplay() {
return this.display;
}
public boolean isInitial() {
return this.initial;
}
}
}
@@ -0,0 +1,57 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.input;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.util.NbtMapDecoder;
import com.github.retrooper.packetevents.protocol.util.NbtMapEncoder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class StaticInputControlType<T extends InputControl> extends AbstractMappedEntity implements InputControlType<T> {
private final NbtMapDecoder<T> decoder;
private final NbtMapEncoder<T> encoder;
@ApiStatus.Internal
public StaticInputControlType(
@Nullable TypesBuilderData data,
NbtMapDecoder<T> decoder,
NbtMapEncoder<T> encoder
) {
super(data);
this.decoder = decoder;
this.encoder = encoder;
}
@Override
public T decode(NBTCompound compound, PacketWrapper<?> wrapper) {
return this.decoder.decode(compound, wrapper);
}
@Override
public void encode(NBTCompound compound, PacketWrapper<?> wrapper, T control) {
this.encoder.encode(compound, wrapper, control);
}
}
@@ -0,0 +1,151 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.dialog.input;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTByte;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class TextInputControl implements InputControl {
private final int width;
private final Component label;
private final boolean labelVisible;
private final String initial;
private final int maxLength;
private final @Nullable MultilineOptions multiline;
public TextInputControl(int width, Component label, boolean labelVisible, String initial, int maxLength, @Nullable MultilineOptions multiline) {
if (initial.length() > maxLength) {
throw new IllegalArgumentException("Default text length exceeds allowed size");
}
this.width = width;
this.label = label;
this.labelVisible = labelVisible;
this.initial = initial;
this.maxLength = maxLength;
this.multiline = multiline;
}
public static TextInputControl decode(NBTCompound compound, PacketWrapper<?> wrapper) {
int width = compound.getNumberTagValueOrDefault("width", 200).intValue();
Component label = compound.getOrThrow("label", AdventureSerializer.serializer(wrapper), wrapper);
boolean labelVisible = compound.getBooleanOr("label_visible", true);
String initial = compound.getStringTagValueOrDefault("initial", "");
int maxLength = compound.getNumberTagValueOrDefault("max_length", 32).intValue();
MultilineOptions multiline = compound.getOrNull("multiline", MultilineOptions::decode, wrapper);
return new TextInputControl(width, label, labelVisible, initial, maxLength, multiline);
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, TextInputControl control) {
if (control.width != 200) {
compound.setTag("width", new NBTInt(control.width));
}
compound.set("label", control.label, AdventureSerializer.serializer(wrapper), wrapper);
if (!control.labelVisible) {
compound.setTag("label_visible", new NBTByte(false));
}
if (!control.initial.isEmpty()) {
compound.setTag("initial", new NBTString(control.initial));
}
if (control.maxLength != 32) {
compound.setTag("max_length", new NBTInt(control.maxLength));
}
if (control.multiline != null) {
compound.set("multiline", control.multiline, MultilineOptions::encode, wrapper);
}
}
@Override
public InputControlType<?> getType() {
return InputControlTypes.TEXT;
}
public int getWidth() {
return this.width;
}
public Component getLabel() {
return this.label;
}
public boolean isLabelVisible() {
return this.labelVisible;
}
public String getInitial() {
return this.initial;
}
public int getMaxLength() {
return this.maxLength;
}
public @Nullable MultilineOptions getMultiline() {
return this.multiline;
}
public static final class MultilineOptions {
private final @Nullable Integer maxLines;
private final @Nullable Integer height;
public MultilineOptions(@Nullable Integer maxLines, @Nullable Integer height) {
this.maxLines = maxLines;
this.height = height;
}
public static MultilineOptions decode(NBT nbt, PacketWrapper<?> wrapper) {
NBTCompound compound = (NBTCompound) nbt;
Number maxLines = compound.getNumberTagValueOrNull("max_lines");
Number height = compound.getNumberTagValueOrNull("height");
return new MultilineOptions(
maxLines != null ? maxLines.intValue() : null,
height != null ? height.intValue() : null
);
}
public static NBT encode(PacketWrapper<?> wrapper, MultilineOptions options) {
NBTCompound compound = new NBTCompound();
if (options.maxLines != null) {
compound.setTag("max_lines", new NBTInt(options.maxLines));
}
if (options.height != null) {
compound.setTag("height", new NBTInt(options.height));
}
return compound;
}
public @Nullable Integer getMaxLines() {
return this.maxLines;
}
public @Nullable Integer getHeight() {
return this.height;
}
}
}
@@ -296,6 +296,11 @@ public final class EntityTypes {
*/
public static final EntityType LINGERING_POTION = define("lingering_potion", POTION);
/**
* Added with 1.21.6
*/
public static final EntityType HAPPY_GHAST = define("happy_ghast", ABSTRACT_ANIMAL);
/**
* Returns an immutable view of the entity types.
*
@@ -87,14 +87,19 @@ public interface WolfVariant extends MappedEntity, CopyableEntity<WolfVariant>,
MappedEntitySet.write(wrapper, variant.getBiomes());
}
@Deprecated
static WolfVariant decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
return decode(nbt, PacketWrapper.createDummyWrapper(version), data);
}
static WolfVariant decode(NBT nbt, PacketWrapper<?> wrapper, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
if (version.isOlderThan(ClientVersion.V_1_21_5)) {
if (wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_21_5)) {
ResourceLocation wildTexture = new ResourceLocation(compound.getStringTagValueOrThrow("wild_texture"));
ResourceLocation tameTexture = new ResourceLocation(compound.getStringTagValueOrThrow("tame_texture"));
ResourceLocation angryTexture = new ResourceLocation(compound.getStringTagValueOrThrow("angry_texture"));
MappedEntitySet<Biome> biomes = MappedEntitySet.decode(
compound.getTagOrThrow("biomes"), version, Biomes.getRegistry());
MappedEntitySet<Biome> biomes = compound.getOrThrow("biomes", (tag, ew) ->
MappedEntitySet.decode(tag, ew, Biomes.getRegistry()), wrapper);
return new StaticWolfVariant(data, wildTexture, tameTexture, angryTexture, biomes);
}
NBTCompound assets = compound.getCompoundTagOrThrow("assets");
@@ -105,13 +110,18 @@ public interface WolfVariant extends MappedEntity, CopyableEntity<WolfVariant>,
angryTexture, MappedEntitySet.createEmpty());
}
@Deprecated
static NBT encode(WolfVariant variant, ClientVersion version) {
return encode(PacketWrapper.createDummyWrapper(version), variant);
}
static NBT encode(PacketWrapper<?> wrapper, WolfVariant variant) {
NBTCompound compound = new NBTCompound();
if (version.isOlderThan(ClientVersion.V_1_21_5)) {
if (wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_21_5)) {
compound.setTag("wild_texture", new NBTString(variant.getWildTexture().toString()));
compound.setTag("tame_texture", new NBTString(variant.getTameTexture().toString()));
compound.setTag("angry_texture", new NBTString(variant.getAngryTexture().toString()));
compound.setTag("biomes", MappedEntitySet.encode(variant.getBiomes(), version));
compound.set("biomes", variant.getBiomes(), MappedEntitySet::encode, wrapper);
} else {
NBTCompound assets = new NBTCompound();
assets.setTag("wild", new NBTString(variant.getWildTexture().toString()));
@@ -107,6 +107,11 @@ public class ItemStack {
this.registryHolder = registryHolder;
}
public static ItemStack decode(NBT nbt, PacketWrapper<?> wrapper) {
return decode(nbt, wrapper.getServerVersion().toClientVersion());
}
@Deprecated
public static ItemStack decode(NBT nbt, ClientVersion version) {
if (nbt instanceof NBTString) {
ResourceLocation itemName = new ResourceLocation(((NBTString) nbt).getValue());
@@ -131,6 +136,11 @@ public class ItemStack {
return builder.build();
}
public static NBT encode(PacketWrapper<?> wrapper, ItemStack itemStack) {
return encodeForParticle(itemStack, wrapper.getServerVersion().toClientVersion());
}
@Deprecated
public static NBT encodeForParticle(ItemStack itemStack, ClientVersion version) {
if (version.isNewerThanOrEquals(ClientVersion.V_1_20_5)) {
boolean simple = itemStack.isEmpty()
@@ -69,7 +69,7 @@ public final class ItemStackSerialization {
ClientVersion version = wrapper.getServerVersion().toClientVersion();
ItemType type = ItemTypes.getRegistry().getByIdOrThrow(version, typeId);
int amount = wrapper.readByte();
int legacyData = v1_13_2 ? -1 : wrapper.readShort();
int legacyData = version.isOlderThan(ClientVersion.V_1_13) ? wrapper.readShort() : -1;
NBTCompound nbt = wrapper.readNBT();
return ItemStack.builder().type(type).amount(amount)
.nbt(nbt).legacyData(legacyData)
@@ -80,12 +80,14 @@ public final class ItemStackSerialization {
* Removed with 1.20.5
*/
private static void writeLegacy(PacketWrapper<?> wrapper, ItemStack stack) {
if (wrapper.getServerVersion().isOlderThanOrEquals(ServerVersion.V_1_12_2)) {
if (wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_13_2)) {
int typeId = stack.isEmpty() ? -1 : stack.getType().getId(wrapper.getServerVersion().toClientVersion());
wrapper.writeShort(typeId);
if (typeId != -1) {
wrapper.writeByte(stack.getAmount());
wrapper.writeShort(stack.getLegacyData());
if (wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_13)) {
wrapper.writeShort(stack.getLegacyData());
}
wrapper.writeNBT(stack.getNBT());
}
} else if (stack.isEmpty()) {
@@ -22,6 +22,7 @@ import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import java.util.Objects;
@@ -35,14 +36,24 @@ public final class EnchantmentCost {
this.perLevelAboveFirst = perLevelAboveFirst;
}
@Deprecated
public static EnchantmentCost decode(NBT nbt, ClientVersion version) {
return decode(nbt, PacketWrapper.createDummyWrapper(version));
}
public static EnchantmentCost decode(NBT nbt, PacketWrapper<?> wrapper) {
NBTCompound compound = (NBTCompound) nbt;
int base = compound.getNumberTagOrThrow("base").getAsInt();
int perLevelAboveFirst = compound.getNumberTagOrThrow("per_level_above_first").getAsInt();
return new EnchantmentCost(base, perLevelAboveFirst);
}
@Deprecated
public static NBT encode(EnchantmentCost cost, ClientVersion version) {
return encode(PacketWrapper.createDummyWrapper(version), cost);
}
public static NBT encode(PacketWrapper<?> wrapper, EnchantmentCost cost) {
NBTCompound compound = new NBTCompound();
compound.setTag("base", new NBTInt(cost.base));
compound.setTag("per_level_above_first", new NBTInt(cost.perLevelAboveFirst));
@@ -28,6 +28,8 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTList;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
import java.util.ArrayList;
import java.util.Collections;
@@ -37,6 +39,7 @@ import java.util.Optional;
import static com.github.retrooper.packetevents.util.adventure.AdventureIndexUtil.indexValueOrThrow;
@NullMarked
public final class EnchantmentDefinition {
private final MappedEntitySet<ItemType> supportedItems;
@@ -65,16 +68,21 @@ public final class EnchantmentDefinition {
this.slots = slots;
}
@Deprecated
public static EnchantmentDefinition decode(NBT nbt, ClientVersion version) {
return decode(nbt, PacketWrapper.createDummyWrapper(version));
}
public static EnchantmentDefinition decode(NBT nbt, PacketWrapper<?> wrapper) {
NBTCompound compound = (NBTCompound) nbt;
MappedEntitySet<ItemType> supportedItems = MappedEntitySet.decode(
compound.getTagOrThrow("supported_items"), version, ItemTypes.getRegistry());
Optional<MappedEntitySet<ItemType>> primaryItems = Optional.ofNullable(compound.getTagOrNull("primary_items"))
.map(items -> MappedEntitySet.decode(items, version, ItemTypes.getRegistry()));
MappedEntitySet<ItemType> supportedItems = compound.getOrThrow("supported_items", (tag, ew) ->
MappedEntitySet.decode(tag, ew, ItemTypes.getRegistry()), wrapper);
Optional<MappedEntitySet<ItemType>> primaryItems = Optional.ofNullable(compound.getOrNull("primary_items",
(tag, ew) -> MappedEntitySet.decode(tag, ew, ItemTypes.getRegistry()), wrapper));
int weight = compound.getNumberTagOrThrow("weight").getAsInt();
int maxLevel = compound.getNumberTagOrThrow("max_level").getAsInt();
EnchantmentCost minCost = EnchantmentCost.decode(compound.getTagOrThrow("min_cost"), version);
EnchantmentCost maxCost = EnchantmentCost.decode(compound.getTagOrThrow("max_cost"), version);
EnchantmentCost minCost = compound.getOrThrow("min_cost", EnchantmentCost::decode, wrapper);
EnchantmentCost maxCost = compound.getOrThrow("max_cost", EnchantmentCost::decode, wrapper);
int anvilCost = compound.getNumberTagOrThrow("anvil_cost").getAsInt();
NBT slotsTag = compound.getTagOrThrow("slots");
@@ -96,24 +104,32 @@ public final class EnchantmentDefinition {
maxLevel, minCost, maxCost, anvilCost, slots);
}
@Deprecated
public static NBT encode(EnchantmentDefinition definition, ClientVersion version) {
return encode(PacketWrapper.createDummyWrapper(version), definition);
}
public static NBT encode(PacketWrapper<?> wrapper, EnchantmentDefinition definition) {
NBTCompound compound = new NBTCompound();
encode(compound, wrapper, definition);
return compound;
}
public static void encode(NBTCompound compound, PacketWrapper<?> wrapper, EnchantmentDefinition definition) {
NBTList<NBTString> slotsTag = NBTList.createStringList();
for (EquipmentSlotGroup slot : definition.slots) {
slotsTag.addTag(new NBTString(slot.getId()));
}
NBTCompound compound = new NBTCompound();
compound.setTag("supported_items", MappedEntitySet.encode(
definition.supportedItems, version));
definition.primaryItems.ifPresent(set -> compound.setTag(
"primary_items", MappedEntitySet.encode(set, version)));
compound.set("supported_items", definition.supportedItems, MappedEntitySet::encode, wrapper);
definition.primaryItems.ifPresent(set -> compound.set(
"primary_items", set, MappedEntitySet::encode, wrapper));
compound.setTag("weight", new NBTInt(definition.weight));
compound.setTag("max_level", new NBTInt(definition.maxLevel));
compound.setTag("min_cost", EnchantmentCost.encode(definition.minCost, version));
compound.setTag("max_cost", EnchantmentCost.encode(definition.maxCost, version));
compound.set("min_cost", definition.minCost, EnchantmentCost::encode, wrapper);
compound.set("max_cost", definition.maxCost, EnchantmentCost::encode, wrapper);
compound.setTag("anvil_cost", new NBTInt(definition.anvilCost));
compound.setTag("slots", slotsTag);
return compound;
}
public MappedEntitySet<ItemType> getSupportedItems() {
@@ -25,17 +25,20 @@ import com.github.retrooper.packetevents.protocol.item.enchantment.EnchantmentDe
import com.github.retrooper.packetevents.protocol.mapper.CopyableEntity;
import com.github.retrooper.packetevents.protocol.mapper.DeepComparableEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntityRefSet;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.util.Optional;
@NullMarked
public interface EnchantmentType extends MappedEntity, CopyableEntity<EnchantmentType>, DeepComparableEntity {
Component getDescription();
@@ -44,30 +47,43 @@ public interface EnchantmentType extends MappedEntity, CopyableEntity<Enchantmen
MappedEntitySet<EnchantmentType> getExclusiveSet();
MappedEntityRefSet<EnchantmentType> getExclusiveRefSet();
StaticComponentMap getEffects();
@Deprecated
static EnchantmentType decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
return decode(nbt, PacketWrapper.createDummyWrapper(version), data);
}
static EnchantmentType decode(NBT nbt, PacketWrapper<?> wrapper, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
Component description = AdventureSerializer.serializer(version).fromNbtTag(compound.getTagOrThrow("description"));
EnchantmentDefinition definition = EnchantmentDefinition.decode(compound, version);
MappedEntitySet<EnchantmentType> exclusiveSet = Optional.ofNullable(compound.getTagOrNull("exclusive_set"))
.map(tag -> MappedEntitySet.decode(tag, version, EnchantmentTypes.getRegistry()))
Component description = compound.getOrThrow("description", wrapper.getSerializers(), wrapper);
EnchantmentDefinition definition = EnchantmentDefinition.decode(compound, wrapper);
MappedEntityRefSet<EnchantmentType> exclusiveSet = Optional.ofNullable(compound.getTagOrNull("exclusive_set"))
.map(tag -> MappedEntitySet.<EnchantmentType>decodeRefSet(tag, wrapper))
.orElseGet(MappedEntitySet::createEmpty);
StaticComponentMap effects = Optional.ofNullable(compound.getTagOrNull("effects"))
.map(tag -> IComponentMap.decode(tag, version,
.map(tag -> IComponentMap.decode(tag, wrapper,
EnchantEffectComponentTypes.getRegistry()))
.orElse(StaticComponentMap.EMPTY);
return new StaticEnchantmentType(data, description, definition, exclusiveSet, effects);
}
@Deprecated
static NBT encode(EnchantmentType type, ClientVersion version) {
NBTCompound compound = (NBTCompound) EnchantmentDefinition.encode(type.getDefinition(), version);
compound.setTag("description", AdventureSerializer.serializer(version).asNbtTag(type.getDescription()));
if (!type.getExclusiveSet().isEmpty()) {
compound.setTag("exclusive_set", MappedEntitySet.encode(type.getExclusiveSet(), version));
return encode(type, PacketWrapper.createDummyWrapper(version));
}
static NBT encode(EnchantmentType type, PacketWrapper<?> wrapper) {
NBTCompound compound = new NBTCompound();
EnchantmentDefinition.encode(compound, wrapper, type.getDefinition());
compound.set("description", type.getDescription(), wrapper.getSerializers(), wrapper);
if (!type.getExclusiveRefSet().isEmpty()) {
compound.set("exclusive_set", type.getExclusiveRefSet(), MappedEntitySet::encodeRefSet, wrapper);
}
if (!type.getEffects().isEmpty()) {
compound.setTag("effects", IComponentMap.encode(type.getEffects(), version));
compound.set("effects", type.getEffects(), IComponentMap::encode, wrapper);
}
return compound;
}
@@ -25,6 +25,7 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
@@ -64,12 +65,13 @@ public final class EnchantmentTypes {
@ApiStatus.Internal
public static EnchantmentType define(String key) {
PacketWrapper<?> wrapper = PacketWrapper.createDummyWrapper(ClientVersion.getLatest());
return REGISTRY.define(key, data -> {
NBTCompound dataTag = ENCHANTMENT_DATA.get(data.getName());
if (dataTag == null) {
throw new IllegalArgumentException("Can't define enchantment " + data.getName() + ", no data found");
}
return EnchantmentType.decode(dataTag, ClientVersion.getLatest(), data);
return EnchantmentType.decode(dataTag, wrapper, data);
});
}
@@ -18,22 +18,30 @@
package com.github.retrooper.packetevents.protocol.item.enchantment.type;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.component.StaticComponentMap;
import com.github.retrooper.packetevents.protocol.item.enchantment.EnchantmentDefinition;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntityRefSet;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
import com.github.retrooper.packetevents.protocol.mapper.ResolvableEntity;
import com.github.retrooper.packetevents.util.mappings.GlobalRegistryHolder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.util.Objects;
public class StaticEnchantmentType extends AbstractMappedEntity implements EnchantmentType {
@NullMarked
public class StaticEnchantmentType extends AbstractMappedEntity implements EnchantmentType, ResolvableEntity {
private final Component description;
private final EnchantmentDefinition definition;
private final MappedEntitySet<EnchantmentType> exclusiveSet;
private final MappedEntityRefSet<EnchantmentType> exclusiveSetRef;
private @Nullable MappedEntitySet<EnchantmentType> exclusiveSet;
private final StaticComponentMap effects;
public StaticEnchantmentType(
@@ -50,20 +58,27 @@ public class StaticEnchantmentType extends AbstractMappedEntity implements Encha
@Nullable TypesBuilderData data,
Component description,
EnchantmentDefinition definition,
MappedEntitySet<EnchantmentType> exclusiveSet,
MappedEntityRefSet<EnchantmentType> exclusiveSet,
StaticComponentMap effects
) {
super(data);
this.description = description;
this.definition = definition;
this.exclusiveSet = exclusiveSet;
this.exclusiveSetRef = exclusiveSet;
this.effects = effects;
}
@Override
public void doResolve(PacketWrapper<?> wrapper) {
this.exclusiveSet = this.exclusiveSetRef.resolve(wrapper, EnchantmentTypes.getRegistry());
}
@Override
public EnchantmentType copy(@Nullable TypesBuilderData newData) {
return new StaticEnchantmentType(newData, this.description,
this.definition, this.exclusiveSet, this.effects);
StaticEnchantmentType type = new StaticEnchantmentType(newData, this.description,
this.definition, this.exclusiveSetRef, this.effects);
type.exclusiveSet = this.exclusiveSet;
return type;
}
@Override
@@ -78,9 +93,22 @@ public class StaticEnchantmentType extends AbstractMappedEntity implements Encha
@Override
public MappedEntitySet<EnchantmentType> getExclusiveSet() {
if (this.exclusiveSet == null) {
// this shouldn't need to be called normally
this.exclusiveSet = this.exclusiveSetRef.resolve(
PacketEvents.getAPI().getServerManager().getVersion().toClientVersion(),
GlobalRegistryHolder.INSTANCE,
EnchantmentTypes.getRegistry()
);
}
return this.exclusiveSet;
}
@Override
public MappedEntityRefSet<EnchantmentType> getExclusiveRefSet() {
return this.exclusiveSetRef;
}
@Override
public StaticComponentMap getEffects() {
return this.effects;
@@ -94,17 +122,17 @@ public class StaticEnchantmentType extends AbstractMappedEntity implements Encha
StaticEnchantmentType that = (StaticEnchantmentType) obj;
if (!this.description.equals(that.description)) return false;
if (!this.definition.equals(that.definition)) return false;
if (!this.exclusiveSet.equals(that.exclusiveSet)) return false;
if (!this.exclusiveSetRef.equals(that.exclusiveSetRef)) return false;
return this.effects.equals(that.effects);
}
@Override
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.description, this.definition, this.exclusiveSet, this.effects);
return Objects.hash(super.hashCode(), this.description, this.definition, this.exclusiveSetRef, this.effects);
}
@Override
public String toString() {
return "StaticEnchantmentType{description=" + this.description + ", definition=" + this.definition + ", exclusiveSet=" + this.exclusiveSet + ", effects=" + this.effects + "}";
return "StaticEnchantmentType{description=" + this.description + ", definition=" + this.definition + ", exclusiveSetRef=" + this.exclusiveSetRef + ", effects=" + this.effects + "}";
}
}
@@ -28,7 +28,6 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.sound.Sound;
import com.github.retrooper.packetevents.util.MathUtil;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
@@ -82,21 +81,31 @@ public interface Instrument extends MappedEntity, CopyableEntity<Instrument>, De
}
}
@Deprecated
static Instrument decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
return decode(nbt, PacketWrapper.createDummyWrapper(version), data);
}
static Instrument decode(NBT nbt, PacketWrapper<?> wrapper, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
Sound sound = Sound.decode(compound.getTagOrThrow("sound_event"), version);
Sound sound = compound.getOrThrow("sound_event", Sound::decode, wrapper);
float useSeconds = compound.getNumberTagOrThrow("use_duration").getAsFloat();
float range = compound.getNumberTagOrThrow("range").getAsFloat();
Component description = AdventureSerializer.serializer(version).fromNbtTag(compound.getTagOrThrow("description"));
Component description = compound.getOrThrow("description", wrapper.getSerializers(), wrapper);
return new StaticInstrument(data, sound, useSeconds, range, description);
}
@Deprecated
static NBT encode(Instrument instrument, ClientVersion version) {
return encode(PacketWrapper.createDummyWrapper(version), instrument);
}
static NBT encode(PacketWrapper<?> wrapper, Instrument instrument) {
NBTCompound compound = new NBTCompound();
compound.setTag("sound_event", Sound.encode(instrument.getSound(), version));
compound.set("sound_event", instrument.getSound(), Sound::encode, wrapper);
compound.setTag("use_duration", new NBTFloat(instrument.getUseSeconds()));
compound.setTag("range", new NBTFloat(instrument.getRange()));
compound.setTag("description", AdventureSerializer.serializer(version).asNbtTag(instrument.getDescription()));
compound.set("description", instrument.getDescription(), wrapper.getSerializers(), wrapper);
return compound;
}
}
@@ -27,7 +27,6 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.sound.Sound;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
@@ -43,23 +42,31 @@ public interface IJukeboxSong extends MappedEntity, CopyableEntity<IJukeboxSong>
int getComparatorOutput();
@Deprecated
static IJukeboxSong decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
return decode(nbt, PacketWrapper.createDummyWrapper(version), data);
}
static IJukeboxSong decode(NBT nbt, PacketWrapper<?> wrapper, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
Sound sound = Sound.decode(compound.getTagOrThrow("sound_event"), version);
Component description = AdventureSerializer.serializer(version).fromNbtTag(compound.getTagOrThrow("description"));
Sound sound = compound.getOrThrow("sound_event", Sound::decode, wrapper);
Component description = compound.getOrThrow("description", wrapper.getSerializers(), wrapper);
float length = compound.getNumberTagOrThrow("length_in_seconds").getAsFloat();
int comparator_output = compound.getNumberTagOrThrow("comparator_output").getAsInt();
return new JukeboxSong(data, sound, description, length, comparator_output);
}
@Deprecated
static NBT encode(IJukeboxSong jukeboxSong, ClientVersion version) {
return encode(PacketWrapper.createDummyWrapper(version), jukeboxSong);
}
static NBT encode(PacketWrapper<?> wrapper, IJukeboxSong song) {
NBTCompound compound = new NBTCompound();
compound.setTag("sound_event", Sound.encode(jukeboxSong.getSound(), version));
compound.setTag("description", AdventureSerializer.serializer(version).asNbtTag(jukeboxSong.getDescription()));
compound.setTag("length_in_seconds", new NBTFloat(jukeboxSong.getLengthInSeconds()));
compound.setTag("comparator_output", new NBTInt(jukeboxSong.getComparatorOutput()));
compound.set("sound_event", song.getSound(), Sound::encode, wrapper);
compound.set("description", song.getDescription(), wrapper.getSerializers(), wrapper);
compound.setTag("length_in_seconds", new NBTFloat(song.getLengthInSeconds()));
compound.setTag("comparator_output", new NBTInt(song.getComparatorOutput()));
return compound;
}
@@ -81,6 +81,18 @@ public final class JukeboxSongs {
public static final IJukeboxSong CREATOR_MUSIC_BOX = define("creator_music_box", Sounds.MUSIC_DISC_CREATOR_MUSIC_BOX,
73, 11);
/**
* Added with 1.21.6
*/
public static final IJukeboxSong TEARS = define("tears", Sounds.MUSIC_DISC_TEARS,
175, 10);
/**
* Added with 1.21.7
*/
public static final IJukeboxSong LAVA_CHICKEN = define("lava_chicken", Sounds.MUSIC_DISC_LAVA_CHICKEN,
134, 9);
/**
* Returns an immutable view of the jukebox songs.
*
@@ -31,7 +31,6 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
@@ -102,12 +101,17 @@ public interface TrimMaterial extends MappedEntity, CopyableEntity<TrimMaterial>
wrapper.writeComponent(material.getDescription());
}
@Deprecated
static TrimMaterial decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
return decode(nbt, PacketWrapper.createDummyWrapper(version), data);
}
static TrimMaterial decode(NBT nbt, PacketWrapper<?> wrapper, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
String assetName = compound.getStringTagValueOrThrow("asset_name");
ItemType ingredient = version.isNewerThanOrEquals(ClientVersion.V_1_21_5)
ItemType ingredient = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5)
? null : ItemTypes.getByName(compound.getStringTagValueOrThrow("ingredient"));
float itemModelIndex = version.isNewerThanOrEquals(ClientVersion.V_1_21_4)
float itemModelIndex = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_4)
? FALLBACK_ITEM_MODEL_INDEX : compound.getNumberTagOrThrow("item_model_index").getAsFloat();
NBTCompound overrideArmorMaterialsTag = compound.getCompoundTagOrNull("override_armor_materials");
Map<ArmorMaterial, String> overrideArmorMaterials;
@@ -121,11 +125,16 @@ public interface TrimMaterial extends MappedEntity, CopyableEntity<TrimMaterial>
} else {
overrideArmorMaterials = Collections.emptyMap();
}
Component description = AdventureSerializer.serializer(version).fromNbtTag(((NBTCompound) nbt).getTagOrThrow("description"));
Component description = ((NBTCompound) nbt).getOrThrow("description", wrapper.getSerializers(), wrapper);
return new StaticTrimMaterial(data, assetName, ingredient, itemModelIndex, overrideArmorMaterials, description);
}
@Deprecated
static NBT encode(TrimMaterial material, ClientVersion version) {
return encode(PacketWrapper.createDummyWrapper(version), material);
}
static NBT encode(PacketWrapper<?> wrapper, TrimMaterial material) {
NBTCompound overrideArmorMaterialsTag;
if (!material.getOverrideArmorMaterials().isEmpty()) {
overrideArmorMaterialsTag = new NBTCompound();
@@ -140,16 +149,16 @@ public interface TrimMaterial extends MappedEntity, CopyableEntity<TrimMaterial>
NBTCompound compound = new NBTCompound();
compound.setTag("asset_name", new NBTString(material.getAssetName()));
if (version.isOlderThan(ClientVersion.V_1_21_5)) {
if (wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_21_5)) {
compound.setTag("ingredient", new NBTString(material.getIngredient().getName().toString()));
}
if (version.isOlderThan(ClientVersion.V_1_21_4)) {
if (wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_21_4)) {
compound.setTag("item_model_index", new NBTFloat(material.getItemModelIndex()));
}
if (overrideArmorMaterialsTag != null) {
compound.setTag("override_armor_materials", overrideArmorMaterialsTag);
}
compound.setTag("description", AdventureSerializer.serializer(version).asNbtTag(material.getDescription()));
compound.set("description", material.getDescription(), wrapper.getSerializers(), wrapper);
return compound;
}
}
@@ -30,7 +30,6 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
@@ -77,24 +76,34 @@ public interface TrimPattern extends MappedEntity, CopyableEntity<TrimPattern>,
wrapper.writeBoolean(pattern.isDecal());
}
@Deprecated
static TrimPattern decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
return decode(nbt, PacketWrapper.createDummyWrapper(version), data);
}
static TrimPattern decode(NBT nbt, PacketWrapper<?> wrapper, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
ResourceLocation assetId = new ResourceLocation(compound.getStringTagValueOrThrow("asset_id"));
ItemType templateItem = version.isNewerThanOrEquals(ClientVersion.V_1_21_5) ? null :
ItemType templateItem = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5) ? null :
ItemTypes.getByName(compound.getStringTagValueOrThrow("template_item"));
Component description = AdventureSerializer.serializer(version).fromNbtTag(compound.getTagOrThrow("description"));
boolean decal = version.isNewerThanOrEquals(ClientVersion.V_1_20_2) && compound.getBoolean("decal");
Component description = compound.getOrThrow("description", wrapper.getSerializers(), wrapper);
boolean decal = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_20_2) && compound.getBoolean("decal");
return new StaticTrimPattern(data, assetId, templateItem, description, decal);
}
@Deprecated
static NBT encode(TrimPattern pattern, ClientVersion version) {
return encode(PacketWrapper.createDummyWrapper(version), pattern);
}
static NBT encode(PacketWrapper<?> wrapper, TrimPattern pattern) {
NBTCompound compound = new NBTCompound();
compound.setTag("asset_id", new NBTString(pattern.getAssetId().toString()));
if (version.isOlderThan(ClientVersion.V_1_21_5)) {
if (wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_21_5)) {
compound.setTag("template_item", new NBTString(pattern.getTemplateItem().getName().toString()));
}
compound.setTag("description", AdventureSerializer.serializer(version).asNbtTag(pattern.getDescription()));
if (version.isNewerThanOrEquals(ClientVersion.V_1_20_2)) {
compound.set("description", pattern.getDescription(), wrapper.getSerializers(), wrapper);
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_20_2)) {
compound.setTag("decal", new NBTByte(pattern.isDecal()));
}
return compound;
@@ -1476,6 +1476,88 @@ public final class ItemTypes {
public static final ItemType BLUE_EGG = builder("blue_egg").setMaxAmount(16).build();
public static final ItemType BROWN_EGG = builder("brown_egg").setMaxAmount(16).build();
/**
* Added with 1.21.6
*/
public static final ItemType DRIED_GHAST = builder("dried_ghast").setMaxAmount(64).setPlacedType(StateTypes.DRIED_GHAST).build();
/**
* Added with 1.21.6
*/
public static final ItemType WHITE_HARNESS = builder("white_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType ORANGE_HARNESS = builder("orange_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType MAGENTA_HARNESS = builder("magenta_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType LIGHT_BLUE_HARNESS = builder("light_blue_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType YELLOW_HARNESS = builder("yellow_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType LIME_HARNESS = builder("lime_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType PINK_HARNESS = builder("pink_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType GRAY_HARNESS = builder("gray_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType LIGHT_GRAY_HARNESS = builder("light_gray_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType CYAN_HARNESS = builder("cyan_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType PURPLE_HARNESS = builder("purple_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType BLUE_HARNESS = builder("blue_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType BROWN_HARNESS = builder("brown_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType GREEN_HARNESS = builder("green_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType RED_HARNESS = builder("red_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType BLACK_HARNESS = builder("black_harness").setMaxAmount(1).build();
/**
* Added with 1.21.6
*/
public static final ItemType HAPPY_GHAST_SPAWN_EGG = builder("happy_ghast_spawn_egg").setMaxAmount(64).build();
/**
* Added with 1.21.6
*/
public static final ItemType MUSIC_DISC_TEARS = builder("music_disc_tears").setMaxAmount(1).setAttributes(ItemAttribute.MUSIC_DISC).build();
/**
* Added with 1.21.7
*/
public static final ItemType MUSIC_DISC_LAVA_CHICKEN = builder("music_disc_lava_chicken").setMaxAmount(1).setAttributes(ItemAttribute.MUSIC_DISC).build();
/**
* @deprecated Burning furnace shows up as a missing texture, removed in 1.9
*/
@@ -1603,7 +1685,8 @@ public final class ItemTypes {
// all versions where base components were changed TODO UPDATE
ClientVersion[] versions = new ClientVersion[]{
ClientVersion.V_1_20_5, ClientVersion.V_1_21, ClientVersion.V_1_21_2,
ClientVersion.V_1_21_4, ClientVersion.V_1_21_5,
ClientVersion.V_1_21_4, ClientVersion.V_1_21_5, ClientVersion.V_1_21_6,
ClientVersion.V_1_21_7,
};
for (ClientVersion version : versions) {
parseAllComponents(version);
@@ -0,0 +1,44 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.mapper;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.IRegistry;
import com.github.retrooper.packetevents.util.mappings.IRegistryHolder;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface MappedEntityRefSet<T extends MappedEntity> {
default MappedEntitySet<T> resolve(PacketWrapper<?> wrapper, IRegistry<T> registry) {
ClientVersion version = wrapper.getServerVersion().toClientVersion();
IRegistry<T> replacedRegistry = wrapper.getRegistryHolder().getRegistryOr(registry, version);
return this.resolve(version, replacedRegistry);
}
default MappedEntitySet<T> resolve(ClientVersion version, IRegistryHolder registryHolder, IRegistry<T> registry) {
IRegistry<T> replacedRegistry = registryHolder.getRegistryOr(registry, version);
return this.resolve(version, replacedRegistry);
}
MappedEntitySet<T> resolve(ClientVersion version, IRegistry<T> registry);
boolean isEmpty();
}
@@ -24,10 +24,14 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.IRegistry;
import com.github.retrooper.packetevents.util.mappings.IRegistryHolder;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
@@ -35,7 +39,8 @@ import java.util.function.BiFunction;
/**
* Either a key to a specific tag or a list of possible entities.
*/
public class MappedEntitySet<T> {
@NullMarked
public class MappedEntitySet<T extends MappedEntity> implements MappedEntityRefSet<T> {
private final @Nullable ResourceLocation tagKey;
private final @Nullable List<T> entities;
@@ -63,6 +68,27 @@ public class MappedEntitySet<T> {
return new MappedEntitySet<>(new ArrayList<>(0));
}
public static <Z extends MappedEntity> MappedEntityRefSet<Z> readRefSet(PacketWrapper<?> wrapper) {
int count = wrapper.readVarInt() - 1;
if (count == -1) {
return new MappedEntitySet<>(wrapper.readIdentifier());
}
int[] entries = wrapper.readVarIntArrayOfSize(Math.min(count, 65536));
return new IdRefSetImpl<>(entries);
}
public static void writeRefSet(PacketWrapper<?> wrapper, MappedEntityRefSet<?> refSet) {
if (refSet instanceof IdRefSetImpl<?>) {
IdRefSetImpl<?> idRefSet = (IdRefSetImpl<?>) refSet;
wrapper.writeVarInt(idRefSet.entries.length + 1);
wrapper.writeVarIntArrayOfSize(idRefSet.entries);
} else if (refSet instanceof MappedEntitySet<?>) {
write(wrapper, (MappedEntitySet<?>) refSet);
} else {
throw new UnsupportedOperationException("Unsupported mapped entity reference set implementation: " + refSet);
}
}
public static <Z extends MappedEntity> MappedEntitySet<Z> read(
PacketWrapper<?> wrapper, BiFunction<ClientVersion, Integer, Z> getter) {
int count = wrapper.readVarInt() - 1;
@@ -90,8 +116,14 @@ public class MappedEntitySet<T> {
}
}
@Deprecated
public static <Z extends MappedEntity> MappedEntitySet<Z> decode(
NBT nbt, ClientVersion version, IRegistry<Z> registry) {
return decode(nbt, PacketWrapper.createDummyWrapper(version), registry);
}
public static <Z extends MappedEntity> MappedEntitySet<Z> decode(
NBT nbt, PacketWrapper<?> wrapper, IRegistry<Z> registry) {
List<Z> list;
if (nbt instanceof NBTString) {
String singleEntry = ((NBTString) nbt).getValue();
@@ -117,7 +149,12 @@ public class MappedEntitySet<T> {
return new MappedEntitySet<>(list);
}
@Deprecated
public static <Z extends MappedEntity> NBT encode(MappedEntitySet<Z> set, ClientVersion version) {
return encodeRefSet(PacketWrapper.createDummyWrapper(version), set);
}
public static <Z extends MappedEntity> NBT encode(PacketWrapper<?> wrapper, MappedEntitySet<Z> set) {
if (set.tagKey != null) {
return new NBTString("#" + set.tagKey);
}
@@ -130,6 +167,69 @@ public class MappedEntitySet<T> {
return listTag;
}
public static <Z extends MappedEntity> MappedEntityRefSet<Z> decodeRefSet(NBT nbt, PacketWrapper<?> wrapper) {
return decodeRefSet(nbt, wrapper.getServerVersion().toClientVersion());
}
@Deprecated
public static <Z extends MappedEntity> MappedEntityRefSet<Z> decodeRefSet(NBT nbt, ClientVersion version) {
List<String> list;
if (nbt instanceof NBTString) {
String singleEntry = ((NBTString) nbt).getValue();
// check whether this is a tag key or a single-entry list
if (!singleEntry.isEmpty() && singleEntry.charAt(0) == '#') {
String tagName = singleEntry.substring(1);
ResourceLocation tagKey = new ResourceLocation(tagName);
return new MappedEntitySet<>(tagKey);
}
// single entry list
list = Collections.singletonList(singleEntry);
} else {
// assume it's a list
NBTList<?> listTag = (NBTList<?>) nbt;
list = new ArrayList<>(listTag.size());
for (NBT tag : listTag.getTags()) {
list.add(((NBTString) tag).getValue());
}
}
return new NameRefSetImpl<>(list);
}
public static <Z extends MappedEntity> NBT encodeRefSet(PacketWrapper<?> wrapper, MappedEntityRefSet<Z> refSet) {
return encodeRefSet(refSet, wrapper.getServerVersion().toClientVersion());
}
@Deprecated
public static <Z extends MappedEntity> NBT encodeRefSet(MappedEntityRefSet<Z> refSet, ClientVersion version) {
if (refSet instanceof NameRefSetImpl<?>) {
NameRefSetImpl<?> nameRefSet = (NameRefSetImpl<?>) refSet;
NBTList<NBTString> listTag = NBTList.createStringList();
for (String entityName : nameRefSet.entries) {
listTag.addTag(new NBTString(entityName));
}
return listTag;
} else if (refSet instanceof MappedEntitySet<?>) {
return encode((MappedEntitySet<?>) refSet, version);
} else {
throw new UnsupportedOperationException("Unsupported mapped entity reference set implementation: " + refSet);
}
}
@Override
public MappedEntitySet<T> resolve(PacketWrapper<?> wrapper, IRegistry<T> registry) {
return this;
}
@Override
public MappedEntitySet<T> resolve(ClientVersion version, IRegistryHolder registryHolder, IRegistry<T> registry) {
return this;
}
@Override
public MappedEntitySet<T> resolve(ClientVersion version, IRegistry<T> registry) {
return this;
}
public boolean isEmpty() {
return this.entities != null && this.entities.isEmpty();
}
@@ -160,4 +260,84 @@ public class MappedEntitySet<T> {
public String toString() {
return "MappedEntitySet{tagKey=" + this.tagKey + ", entities=" + this.entities + '}';
}
private static final class IdRefSetImpl<T extends MappedEntity> implements MappedEntityRefSet<T> {
private final int[] entries;
public IdRefSetImpl(int[] entries) {
this.entries = entries;
}
@Override
public MappedEntitySet<T> resolve(ClientVersion version, IRegistry<T> registry) {
List<T> entities = new ArrayList<>(this.entries.length);
for (int entityId : this.entries) {
entities.add(registry.getByIdOrThrow(version, entityId));
}
return new MappedEntitySet<>(entities);
}
@Override
public boolean isEmpty() {
return this.entries.length == 0;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof IdRefSetImpl)) return false;
IdRefSetImpl<?> idRefSet = (IdRefSetImpl<?>) obj;
return Arrays.equals(this.entries, idRefSet.entries);
}
@Override
public int hashCode() {
return Arrays.hashCode(this.entries);
}
@Override
public String toString() {
return "IdRefSetImpl{entries=" + Arrays.toString(this.entries) + '}';
}
}
private static final class NameRefSetImpl<T extends MappedEntity> implements MappedEntityRefSet<T> {
private final List<String> entries;
public NameRefSetImpl(List<String> entries) {
this.entries = entries;
}
@Override
public MappedEntitySet<T> resolve(ClientVersion version, IRegistry<T> registry) {
List<T> entities = new ArrayList<>(this.entries.size());
for (String entityName : this.entries) {
entities.add(registry.getByNameOrThrow(entityName));
}
return new MappedEntitySet<>(entities);
}
@Override
public boolean isEmpty() {
return this.entries.isEmpty();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof NameRefSetImpl)) return false;
NameRefSetImpl<?> that = (NameRefSetImpl<?>) obj;
return this.entries.equals(that.entries);
}
@Override
public int hashCode() {
return Objects.hashCode(this.entries);
}
@Override
public String toString() {
return "NameRefSetImpl{entries=" + this.entries + '}';
}
}
}
@@ -0,0 +1,36 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.mapper;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.IRegistryHolder;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Used internally for loading additional data
* after registry entries have already been added.
*/
@ApiStatus.Internal
@NullMarked
public interface ResolvableEntity {
void doResolve(PacketWrapper<?> wrapper);
}
@@ -18,11 +18,17 @@
package com.github.retrooper.packetevents.protocol.nbt;
import com.github.retrooper.packetevents.protocol.util.NbtDecoder;
import com.github.retrooper.packetevents.protocol.util.NbtEncoder;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -108,6 +114,20 @@ public class NBTCompound extends NBT {
return getTagOfTypeOrNull(key, NBTCompound.class);
}
public Number getNumberTagValueOrThrow(String key) {
return this.getNumberTagOrThrow(key).getAsNumber();
}
public @Nullable Number getNumberTagValueOrNull(String key) {
return this.getNumberTagValueOrDefault(key, null);
}
@Contract("_, !null -> !null")
public @Nullable Number getNumberTagValueOrDefault(String key, @Nullable Number number) {
NBTNumber tag = this.getNumberTagOrNull(key);
return tag != null ? tag.getAsNumber() : number;
}
public NBTNumber getNumberTagOrThrow(String key) {
return getTagOfTypeOrThrow(key, NBTNumber.class);
}
@@ -207,9 +227,92 @@ public class NBTCompound extends NBT {
}
public boolean getBoolean(String string) {
return this.getBooleanOr(string, false);
}
public boolean getBooleanOr(String string, boolean defaultValue) {
NBTNumber nbtByte = this.getTagOfTypeOrNull(string, NBTNumber.class);
// Empty byte tags are considered 0
return nbtByte != null && nbtByte.getAsByte() != 0;
return nbtByte != null ? nbtByte.getAsByte() != 0 : defaultValue;
}
@Contract("_, _, !null, _ -> !null")
public <T> @Nullable T getOr(String key, NbtDecoder<T> decoder, @Nullable T def, PacketWrapper<?> wrapper) {
NBT tag = this.getTagOrNull(key);
return tag != null ? decoder.decode(tag, wrapper) : def;
}
public <T> @Nullable T getOrNull(String key, NbtDecoder<T> decoder, PacketWrapper<?> wrapper) {
return this.getOr(key, decoder, null, wrapper);
}
public <T> T getOrThrow(String key, NbtDecoder<T> decoder, PacketWrapper<?> wrapper) {
return decoder.decode(this.getTagOrThrow(key), wrapper);
}
@Contract("_, _, !null, _ -> !null")
public <T> @Nullable List<T> getListOr(String key, NbtDecoder<T> decoder, @Nullable List<T> def, PacketWrapper<?> wrapper) {
NBT tag = this.getTagOrNull(key);
if (tag instanceof NBTList) {
// entries in list format
List<? extends NBT> tags = ((NBTList<?>) tag).getTags();
List<T> list = new ArrayList<>(tags.size());
for (NBT element : tags) {
list.add(decoder.decode(element, wrapper));
}
return list;
} else if (tag != null) {
// single entry
List<T> list = new ArrayList<>(1);
list.add(decoder.decode(tag, wrapper));
return list;
} else {
// null, return default
return def;
}
}
public <T> @Nullable List<T> getListOrNull(String key, NbtDecoder<T> decoder, PacketWrapper<?> wrapper) {
return this.getListOr(key, decoder, null, wrapper);
}
public <T> List<T> getListOrEmpty(String key, NbtDecoder<T> decoder, PacketWrapper<?> wrapper) {
return this.getListOr(key, decoder, Collections.emptyList(), wrapper);
}
public <T> List<T> getListOrThrow(String key, NbtDecoder<T> decoder, PacketWrapper<?> wrapper) {
List<T> list = this.getListOrNull(key, decoder, wrapper);
if (list == null) {
throw new IllegalStateException(MessageFormat.format("NBT {0} does not exist", key));
}
return null;
}
public <T> void set(String key, T value, NbtEncoder<T> encoder, PacketWrapper<?> wrapper) {
this.setTag(key, encoder.encode(wrapper, value));
}
public <T> void setList(String key, List<T> value, NbtEncoder<T> encoder, PacketWrapper<?> wrapper) {
if (value.isEmpty()) {
this.setTag(key, new NBTList<>(NBTType.END, 0));
} else {
// determine list type using first value in list
NBT firstVal = encoder.encode(wrapper, value.get(0));
NBTList<?> list = new NBTList<>(firstVal.getType(), value.size());
list.addTagUnsafe(firstVal);
// add remaining list entries
for (int i = 1; i < value.size(); i++) {
list.addTagUnsafe(encoder.encode(wrapper, value.get(i)));
}
this.setTag(key, list);
}
}
public <T> void setCompactList(String key, List<T> value, NbtEncoder<T> encoder, PacketWrapper<?> wrapper) {
if (value.size() == 1) {
this.set(key, value.get(0), encoder, wrapper);
} else {
this.setList(key, value, encoder, wrapper);
}
}
@Override
@@ -41,6 +41,7 @@ import com.github.retrooper.packetevents.protocol.packettype.clientbound.Clientb
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_21;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_21_2;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_21_5;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_21_6;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_7_10;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_8;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_9;
@@ -49,8 +50,10 @@ import com.github.retrooper.packetevents.protocol.packettype.config.clientbound.
import com.github.retrooper.packetevents.protocol.packettype.config.clientbound.ClientboundConfigPacketType_1_20_3;
import com.github.retrooper.packetevents.protocol.packettype.config.clientbound.ClientboundConfigPacketType_1_20_5;
import com.github.retrooper.packetevents.protocol.packettype.config.clientbound.ClientboundConfigPacketType_1_21;
import com.github.retrooper.packetevents.protocol.packettype.config.clientbound.ClientboundConfigPacketType_1_21_6;
import com.github.retrooper.packetevents.protocol.packettype.config.serverbound.ServerboundConfigPacketType_1_20_2;
import com.github.retrooper.packetevents.protocol.packettype.config.serverbound.ServerboundConfigPacketType_1_20_5;
import com.github.retrooper.packetevents.protocol.packettype.config.serverbound.ServerboundConfigPacketType_1_21_6;
import com.github.retrooper.packetevents.protocol.packettype.serverbound.ServerboundPacketType_1_12;
import com.github.retrooper.packetevents.protocol.packettype.serverbound.ServerboundPacketType_1_12_1;
import com.github.retrooper.packetevents.protocol.packettype.serverbound.ServerboundPacketType_1_13;
@@ -69,6 +72,7 @@ import com.github.retrooper.packetevents.protocol.packettype.serverbound.Serverb
import com.github.retrooper.packetevents.protocol.packettype.serverbound.ServerboundPacketType_1_21_2;
import com.github.retrooper.packetevents.protocol.packettype.serverbound.ServerboundPacketType_1_21_4;
import com.github.retrooper.packetevents.protocol.packettype.serverbound.ServerboundPacketType_1_21_5;
import com.github.retrooper.packetevents.protocol.packettype.serverbound.ServerboundPacketType_1_21_6;
import com.github.retrooper.packetevents.protocol.packettype.serverbound.ServerboundPacketType_1_7_10;
import com.github.retrooper.packetevents.protocol.packettype.serverbound.ServerboundPacketType_1_8;
import com.github.retrooper.packetevents.protocol.packettype.serverbound.ServerboundPacketType_1_9;
@@ -77,17 +81,20 @@ import com.github.retrooper.packetevents.util.VersionMapper;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.configuration.client.WrapperConfigClientConfigurationEndAck;
import com.github.retrooper.packetevents.wrapper.configuration.client.WrapperConfigClientCookieResponse;
import com.github.retrooper.packetevents.wrapper.configuration.client.WrapperConfigClientCustomClickAction;
import com.github.retrooper.packetevents.wrapper.configuration.client.WrapperConfigClientKeepAlive;
import com.github.retrooper.packetevents.wrapper.configuration.client.WrapperConfigClientPluginMessage;
import com.github.retrooper.packetevents.wrapper.configuration.client.WrapperConfigClientPong;
import com.github.retrooper.packetevents.wrapper.configuration.client.WrapperConfigClientResourcePackStatus;
import com.github.retrooper.packetevents.wrapper.configuration.client.WrapperConfigClientSelectKnownPacks;
import com.github.retrooper.packetevents.wrapper.configuration.client.WrapperConfigClientSettings;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerClearDialog;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerConfigurationEnd;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerCookieRequest;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerCustomReportDetails;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerDisconnect;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerKeepAlive;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerPing;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerPluginMessage;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerRegistryData;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerResetChat;
@@ -95,6 +102,7 @@ import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperCon
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerResourcePackSend;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerSelectKnownPacks;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerServerLinks;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerShowDialog;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerStoreCookie;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerTransfer;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerUpdateEnabledFeatures;
@@ -112,6 +120,7 @@ import com.github.retrooper.packetevents.wrapper.login.server.WrapperLoginServer
import com.github.retrooper.packetevents.wrapper.login.server.WrapperLoginServerSetCompression;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientAdvancementTab;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientAnimation;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientChangeGameMode;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientChatAck;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientChatCommand;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientChatCommandUnsigned;
@@ -128,6 +137,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCo
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCookieResponse;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCraftRecipeRequest;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCreativeInventoryAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCustomClickAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientDebugPing;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientDebugSampleSubscription;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEditBook;
@@ -220,7 +230,8 @@ public final class PacketType {
ClientVersion.V_1_20_5,
ClientVersion.V_1_21,
ClientVersion.V_1_21_2,
ClientVersion.V_1_21_5);
ClientVersion.V_1_21_5,
ClientVersion.V_1_21_6);
//TODO UPDATE Update packet type mappings (serverbound pt. 1)
private static final VersionMapper SERVERBOUND_PLAY_VERSION_MAPPER = new VersionMapper(
@@ -244,18 +255,21 @@ public final class PacketType {
ClientVersion.V_1_20_5,
ClientVersion.V_1_21_2,
ClientVersion.V_1_21_4,
ClientVersion.V_1_21_5);
ClientVersion.V_1_21_5,
ClientVersion.V_1_21_6);
// TODO UPDATE Update packet type mappings (config clientbound pt. 1)
private static final VersionMapper CLIENTBOUND_CONFIG_VERSION_MAPPER = new VersionMapper(
ClientVersion.V_1_20_2,
ClientVersion.V_1_20_3,
ClientVersion.V_1_20_5,
ClientVersion.V_1_21);
ClientVersion.V_1_21,
ClientVersion.V_1_21_6);
// TODO UPDATE Update packet type mappings (config serverbound pt. 1)
private static final VersionMapper SERVERBOUND_CONFIG_VERSION_MAPPER = new VersionMapper(
ClientVersion.V_1_20_2,
ClientVersion.V_1_20_5);
ClientVersion.V_1_20_5,
ClientVersion.V_1_21_6);
public static void prepare() {
PacketType.Play.Client.load();
@@ -587,7 +601,9 @@ public final class PacketType {
}
}
// Added in 1.20.2
/**
* Added with 1.20.2
*/
public static class Configuration {
public enum Client implements PacketTypeCommon, ServerBoundPacket {
@@ -599,9 +615,19 @@ public final class PacketType {
PONG(WrapperConfigClientPong.class),
RESOURCE_PACK_STATUS(WrapperConfigClientResourcePackStatus.class),
// Added in 1.20.5
/**
* Added with 1.20.5
*/
COOKIE_RESPONSE(WrapperConfigClientCookieResponse.class),
/**
* Added with 1.20.5
*/
SELECT_KNOWN_PACKS(WrapperConfigClientSelectKnownPacks.class),
/**
* Added with 1.21.6
*/
CUSTOM_CLICK_ACTION(WrapperConfigClientCustomClickAction.class),
;
private static int INDEX = 0;
@@ -624,6 +650,7 @@ public final class PacketType {
INDEX = 0;
loadPacketIds(ServerboundConfigPacketType_1_20_2.values());
loadPacketIds(ServerboundConfigPacketType_1_20_5.values());
loadPacketIds(ServerboundConfigPacketType_1_21_6.values());
// TODO UPDATE Update packet type mappings (config serverbound pt. 2)
}
@@ -678,25 +705,55 @@ public final class PacketType {
DISCONNECT(WrapperConfigServerDisconnect.class),
CONFIGURATION_END(WrapperConfigServerConfigurationEnd.class),
KEEP_ALIVE(WrapperConfigServerKeepAlive.class),
PING(null),
PING(WrapperConfigServerPing.class),
REGISTRY_DATA(WrapperConfigServerRegistryData.class),
RESOURCE_PACK_SEND(WrapperConfigServerResourcePackSend.class),
UPDATE_ENABLED_FEATURES(WrapperConfigServerUpdateEnabledFeatures.class),
UPDATE_TAGS(null),
// Added in 1.20.3
/**
* Added with 1.20.3
*/
RESOURCE_PACK_REMOVE(WrapperConfigServerResourcePackRemove.class),
// Added in 1.20.5
/**
* Added with 1.20.5
*/
COOKIE_REQUEST(WrapperConfigServerCookieRequest.class),
/**
* Added with 1.20.5
*/
RESET_CHAT(WrapperConfigServerResetChat.class),
/**
* Added with 1.20.5
*/
STORE_COOKIE(WrapperConfigServerStoreCookie.class),
/**
* Added with 1.20.5
*/
TRANSFER(WrapperConfigServerTransfer.class),
/**
* Added with 1.20.5
*/
SELECT_KNOWN_PACKS(WrapperConfigServerSelectKnownPacks.class),
// added in 1.21
/**
* Added with 1.21
*/
CUSTOM_REPORT_DETAILS(WrapperConfigServerCustomReportDetails.class),
/**
* Added with 1.21
*/
SERVER_LINKS(WrapperConfigServerServerLinks.class),
/**
* Added with 1.21.6
*/
CLEAR_DIALOG(WrapperConfigServerClearDialog.class),
/**
* Added with 1.21.6
*/
SHOW_DIALOG(WrapperConfigServerShowDialog.class),
;
private static int INDEX = 0;
@@ -721,6 +778,7 @@ public final class PacketType {
loadPacketIds(ClientboundConfigPacketType_1_20_3.values());
loadPacketIds(ClientboundConfigPacketType_1_20_5.values());
loadPacketIds(ClientboundConfigPacketType_1_21.values());
loadPacketIds(ClientboundConfigPacketType_1_21_6.values());
// TODO UPDATE Update packet type mappings (config clientbound pt. 2)
}
@@ -916,6 +974,15 @@ public final class PacketType {
* Added with 1.21.5
*/
TEST_INSTANCE_BLOCK_ACTION(WrapperPlayClientTestInstanceBlockAction.class),
/**
* Added with 1.21.6
*/
CHANGE_GAME_MODE(WrapperPlayClientChangeGameMode.class),
/**
* Added with 1.21.6
*/
CUSTOM_CLICK_ACTION(WrapperPlayClientCustomClickAction.class),
;
private static int INDEX = 0;
@@ -980,6 +1047,7 @@ public final class PacketType {
loadPacketIds(ServerboundPacketType_1_21_2.values());
loadPacketIds(ServerboundPacketType_1_21_4.values());
loadPacketIds(ServerboundPacketType_1_21_5.values());
loadPacketIds(ServerboundPacketType_1_21_6.values());
//TODO UPDATE Update packet type mappings (serverbound pt. 2)
}
@@ -1031,7 +1099,7 @@ public final class PacketType {
@ApiStatus.Obsolete
SPAWN_EXPERIENCE_ORB(WrapperPlayServerSpawnExperienceOrb.class),
ENTITY_ANIMATION(WrapperPlayServerEntityAnimation.class),
STATISTICS(null),
STATISTICS(WrapperPlayServerStatistics.class),
BLOCK_BREAK_ANIMATION(WrapperPlayServerBlockBreakAnimation.class),
BLOCK_ENTITY_DATA(WrapperPlayServerBlockEntityData.class),
BLOCK_ACTION(WrapperPlayServerBlockAction.class),
@@ -1123,70 +1191,179 @@ public final class PacketType {
NBT_QUERY_RESPONSE(WrapperPlayServerNBTQueryResponse.class),
COLLECT_ITEM(WrapperPlayServerCollectItem.class),
ENTITY_TELEPORT(WrapperPlayServerEntityTeleport.class),
UPDATE_ADVANCEMENTS(null),
UPDATE_ADVANCEMENTS(WrapperPlayServerUpdateAdvancements.class),
UPDATE_ATTRIBUTES(WrapperPlayServerUpdateAttributes.class),
ENTITY_EFFECT(WrapperPlayServerEntityEffect.class),
DECLARE_RECIPES(WrapperPlayServerDeclareRecipes.class),
TAGS(WrapperPlayServerTags.class),
CHAT_MESSAGE(WrapperPlayServerChatMessage.class),
// Added in 1.19
/**
* Added with 1.19
*/
ACKNOWLEDGE_BLOCK_CHANGES(WrapperPlayServerAcknowledgeBlockChanges.class),
/**
* Added with 1.19
*/
SERVER_DATA(WrapperPlayServerServerData.class),
/**
* Added with 1.19
*/
SYSTEM_CHAT_MESSAGE(WrapperPlayServerSystemChatMessage.class),
// Added in 1.19.1
/**
* Added with 1.19.1
*/
DELETE_CHAT(WrapperPlayServerDeleteChat.class),
/**
* Added with 1.19.1
*/
CUSTOM_CHAT_COMPLETIONS(WrapperPlayServerCustomChatCompletions.class),
// Added in 1.19.3
/**
* Added with 1.19.3
*/
DISGUISED_CHAT(WrapperPlayServerDisguisedChat.class),
/**
* Added with 1.19.3
*/
PLAYER_INFO_REMOVE(WrapperPlayServerPlayerInfoRemove.class),
/**
* Added with 1.19.3
*/
PLAYER_INFO_UPDATE(WrapperPlayServerPlayerInfoUpdate.class),
// Added in 1.19.4
/**
* Added with 1.19.4
*/
DAMAGE_EVENT(WrapperPlayServerDamageEvent.class),
/**
* Added with 1.19.4
*/
HURT_ANIMATION(WrapperPlayServerHurtAnimation.class),
/**
* Added with 1.19.4
*/
BUNDLE(WrapperPlayServerBundle.class),
/**
* Added with 1.19.4
*/
CHUNK_BIOMES(null),
// Added in 1.20.2
/**
* Added with 1.20.2
*/
CHUNK_BATCH_END(WrapperPlayServerChunkBatchEnd.class),
/**
* Added with 1.20.2
*/
CHUNK_BATCH_BEGIN(WrapperPlayServerChunkBatchBegin.class),
/**
* Added with 1.20.2
*/
DEBUG_PONG(WrapperPlayServerDebugPong.class),
/**
* Added with 1.20.2
*/
CONFIGURATION_START(WrapperPlayServerConfigurationStart.class),
// Added in 1.20.3
/**
* Added with 1.20.3
*/
RESET_SCORE(WrapperPlayServerResetScore.class),
/**
* Added with 1.20.3
*/
RESOURCE_PACK_REMOVE(WrapperPlayServerResourcePackRemove.class),
/**
* Added with 1.20.3
*/
TICKING_STATE(WrapperPlayServerTickingState.class),
/**
* Added with 1.20.3
*/
TICKING_STEP(WrapperPlayServerTickingStep.class),
// Added in 1.20.5
/**
* Added with 1.20.5
*/
COOKIE_REQUEST(WrapperPlayServerCookieRequest.class),
/**
* Added with 1.20.5
*/
DEBUG_SAMPLE(WrapperPlayServerDebugSample.class),
/**
* Added with 1.20.5
*/
STORE_COOKIE(WrapperPlayServerStoreCookie.class),
/**
* Added with 1.20.5
*/
TRANSFER(WrapperPlayServerTransfer.class),
/**
* Added with 1.20.5
*/
PROJECTILE_POWER(WrapperPlayServerProjectilePower.class),
// added in 1.21
/**
* Added with 1.21
*/
CUSTOM_REPORT_DETAILS(WrapperPlayServerCustomReportDetails.class),
/**
* Added with 1.21
*/
SERVER_LINKS(WrapperPlayServerServerLinks.class),
// added in 1.21.2
/**
* Added with 1.21.2
*/
MOVE_MINECART(WrapperPlayServerMoveMinecart.class),
/**
* Added with 1.21.2
*/
SET_CURSOR_ITEM(WrapperPlayServerSetCursorItem.class),
/**
* Added with 1.21.2
*/
SET_PLAYER_INVENTORY(WrapperPlayServerSetPlayerInventory.class),
/**
* Added with 1.21.2
*/
ENTITY_POSITION_SYNC(WrapperPlayServerEntityPositionSync.class),
/**
* Added with 1.21.2
*/
PLAYER_ROTATION(WrapperPlayServerPlayerRotation.class),
/**
* Added with 1.21.2
*/
RECIPE_BOOK_ADD(WrapperPlayServerRecipeBookAdd.class),
/**
* Added with 1.21.2
*/
RECIPE_BOOK_REMOVE(WrapperPlayServerRecipeBookRemove.class),
/**
* Added with 1.21.2
*/
RECIPE_BOOK_SETTINGS(WrapperPlayServerRecipeBookSettings.class),
/**
* Added with 1.21.5
*/
TEST_INSTANCE_BLOCK_STATUS(WrapperPlayServerTestInstanceBlockStatus.class),
/**
* Added with 1.21.6
*/
WAYPOINT(WrapperPlayServerWaypoint.class),
/**
* Added with 1.21.6
*/
CLEAR_DIALOG(WrapperPlayServerClearDialog.class),
/**
* Added with 1.21.6
*/
SHOW_DIALOG(WrapperPlayServerShowDialog.class),
;
private static int INDEX = 0;
@@ -1267,6 +1444,7 @@ public final class PacketType {
loadPacketIds(ClientboundPacketType_1_21.values());
loadPacketIds(ClientboundPacketType_1_21_2.values());
loadPacketIds(ClientboundPacketType_1_21_5.values());
loadPacketIds(ClientboundPacketType_1_21_6.values());
//TODO UPDATE Update packet type mappings (clientbound pt. 2)
}
}
@@ -0,0 +1,166 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.packettype.clientbound;
public enum ClientboundPacketType_1_21_6 {
BUNDLE,
SPAWN_ENTITY,
ENTITY_ANIMATION,
STATISTICS,
ACKNOWLEDGE_BLOCK_CHANGES,
BLOCK_BREAK_ANIMATION,
BLOCK_ENTITY_DATA,
BLOCK_ACTION,
BLOCK_CHANGE,
BOSS_BAR,
SERVER_DIFFICULTY,
CHUNK_BATCH_END,
CHUNK_BATCH_BEGIN,
CHUNK_BIOMES,
CLEAR_TITLES,
TAB_COMPLETE,
DECLARE_COMMANDS,
CLOSE_WINDOW,
WINDOW_ITEMS,
WINDOW_PROPERTY,
SET_SLOT,
COOKIE_REQUEST,
SET_COOLDOWN,
CUSTOM_CHAT_COMPLETIONS,
PLUGIN_MESSAGE,
DAMAGE_EVENT,
DEBUG_SAMPLE,
DELETE_CHAT,
DISCONNECT,
DISGUISED_CHAT,
ENTITY_STATUS,
ENTITY_POSITION_SYNC,
EXPLOSION,
UNLOAD_CHUNK,
CHANGE_GAME_STATE,
OPEN_HORSE_WINDOW,
HURT_ANIMATION,
INITIALIZE_WORLD_BORDER,
KEEP_ALIVE,
CHUNK_DATA,
EFFECT,
PARTICLE,
UPDATE_LIGHT,
JOIN_GAME,
MAP_DATA,
MERCHANT_OFFERS,
ENTITY_RELATIVE_MOVE,
ENTITY_RELATIVE_MOVE_AND_ROTATION,
MOVE_MINECART,
ENTITY_ROTATION,
VEHICLE_MOVE,
OPEN_BOOK,
OPEN_WINDOW,
OPEN_SIGN_EDITOR,
PING,
DEBUG_PONG,
CRAFT_RECIPE_RESPONSE,
PLAYER_ABILITIES,
CHAT_MESSAGE,
END_COMBAT_EVENT,
ENTER_COMBAT_EVENT,
DEATH_COMBAT_EVENT,
PLAYER_INFO_REMOVE,
PLAYER_INFO_UPDATE,
FACE_PLAYER,
PLAYER_POSITION_AND_LOOK,
PLAYER_ROTATION,
RECIPE_BOOK_ADD,
RECIPE_BOOK_REMOVE,
RECIPE_BOOK_SETTINGS,
DESTROY_ENTITIES,
REMOVE_ENTITY_EFFECT,
RESET_SCORE,
RESOURCE_PACK_REMOVE,
RESOURCE_PACK_SEND,
RESPAWN,
ENTITY_HEAD_LOOK,
MULTI_BLOCK_CHANGE,
SELECT_ADVANCEMENTS_TAB,
SERVER_DATA,
ACTION_BAR,
WORLD_BORDER_CENTER,
WORLD_BORDER_LERP_SIZE,
WORLD_BORDER_SIZE,
WORLD_BORDER_WARNING_DELAY,
WORLD_BORDER_WARNING_REACH,
CAMERA,
UPDATE_VIEW_POSITION,
UPDATE_VIEW_DISTANCE,
SET_CURSOR_ITEM,
SPAWN_POSITION,
DISPLAY_SCOREBOARD,
ENTITY_METADATA,
ATTACH_ENTITY,
ENTITY_VELOCITY,
ENTITY_EQUIPMENT,
SET_EXPERIENCE,
UPDATE_HEALTH,
HELD_ITEM_CHANGE,
SCOREBOARD_OBJECTIVE,
SET_PASSENGERS,
SET_PLAYER_INVENTORY,
TEAMS,
UPDATE_SCORE,
UPDATE_SIMULATION_DISTANCE,
SET_TITLE_SUBTITLE,
TIME_UPDATE,
SET_TITLE_TEXT,
SET_TITLE_TIMES,
ENTITY_SOUND_EFFECT,
SOUND_EFFECT,
CONFIGURATION_START,
STOP_SOUND,
STORE_COOKIE,
SYSTEM_CHAT_MESSAGE,
PLAYER_LIST_HEADER_AND_FOOTER,
NBT_QUERY_RESPONSE,
COLLECT_ITEM,
ENTITY_TELEPORT,
TEST_INSTANCE_BLOCK_STATUS,
TICKING_STATE,
TICKING_STEP,
TRANSFER,
UPDATE_ADVANCEMENTS,
UPDATE_ATTRIBUTES,
ENTITY_EFFECT,
DECLARE_RECIPES,
TAGS,
PROJECTILE_POWER,
CUSTOM_REPORT_DETAILS,
SERVER_LINKS,
/**
* New packet
*/
WAYPOINT,
/**
* New packet
*/
CLEAR_DIALOG,
/**
* New packet
*/
SHOW_DIALOG,
}
@@ -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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.packettype.config.clientbound;
public enum ClientboundConfigPacketType_1_21_6 {
COOKIE_REQUEST,
PLUGIN_MESSAGE,
DISCONNECT,
CONFIGURATION_END,
KEEP_ALIVE,
PING,
RESET_CHAT,
REGISTRY_DATA,
RESOURCE_PACK_REMOVE,
RESOURCE_PACK_SEND,
STORE_COOKIE,
TRANSFER,
UPDATE_ENABLED_FEATURES,
UPDATE_TAGS,
SELECT_KNOWN_PACKS,
CUSTOM_REPORT_DETAILS,
SERVER_LINKS,
/**
* New packet
*/
CLEAR_DIALOG,
/**
* New packet
*/
SHOW_DIALOG,
}
@@ -0,0 +1,35 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.packettype.config.serverbound;
public enum ServerboundConfigPacketType_1_21_6 {
CLIENT_SETTINGS,
COOKIE_RESPONSE,
PLUGIN_MESSAGE,
CONFIGURATION_END_ACK,
KEEP_ALIVE,
PONG,
RESOURCE_PACK_STATUS,
SELECT_KNOWN_PACKS,
/**
* New packet
*/
CUSTOM_CLICK_ACTION,
}
Loaded 100 of 191 files, more files were not shown because too many files have changed in this diff. Show more