Skip to content

Commit e34105e

Browse files
committed
Rework the component array modifier to work in 1.9.4 and up
Fixes #215
1 parent a995866 commit e34105e

File tree

2 files changed

+118
-11
lines changed

2 files changed

+118
-11
lines changed

modules/API/src/main/java/com/comphenix/protocol/events/PacketContainer.java

Lines changed: 105 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717

1818
package com.comphenix.protocol.events;
1919

20-
import io.netty.buffer.ByteBuf;
21-
import io.netty.buffer.UnpooledByteBufAllocator;
22-
2320
import java.io.DataInput;
2421
import java.io.DataInputStream;
2522
import java.io.DataOutput;
@@ -105,11 +102,16 @@
105102
import com.comphenix.protocol.wrappers.WrappedStatistic;
106103
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
107104
import com.comphenix.protocol.wrappers.nbt.NbtBase;
105+
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
106+
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
108107
import com.google.common.base.Function;
109108
import com.google.common.base.Preconditions;
110109
import com.google.common.collect.Maps;
111110
import com.google.common.collect.Sets;
112111

112+
import io.netty.buffer.ByteBuf;
113+
import io.netty.buffer.UnpooledByteBufAllocator;
114+
113115
/**
114116
* Represents a Minecraft packet indirectly.
115117
*
@@ -659,15 +661,19 @@ public StructureModifier<WrappedChatComponent> getChatComponents() {
659661
/**
660662
* Retrieves a read/write structure for arrays of chat components.
661663
* <p>
662-
* This modifier will automatically marshall between WrappedChatComponent and the
663-
* internal Minecraft IChatBaseComponent.
664+
* This modifier will automatically marshal between WrappedChatComponent and the
665+
* internal Minecraft IChatBaseComponent (1.9.2 and below) or the internal
666+
* NBTCompound (1.9.4 and up).
667+
* <p>
668+
* Note that in 1.9.4 and up this modifier only works properly with sign
669+
* tile entities.
664670
* @return A modifier for ChatComponent array fields.
665671
*/
666672
public StructureModifier<WrappedChatComponent[]> getChatComponentArrays() {
667673
// Convert to and from the Bukkit wrapper
668674
return structureModifier.<WrappedChatComponent[]>withType(
669-
MinecraftReflection.getIChatBaseComponentArrayClass(),
670-
BukkitConverters.getIgnoreNull(new WrappedChatComponentArrayConverter()));
675+
ComponentArrayConverter.getGenericType(),
676+
BukkitConverters.getIgnoreNull(new ComponentArrayConverter()));
671677
}
672678

673679
/**
@@ -923,7 +929,7 @@ public <T extends Enum<T>> StructureModifier<T> getEnumModifier(Class<T> enumCla
923929
*/
924930
public <T extends Enum<T>> StructureModifier<T> getEnumModifier(Class<T> enumClass, int index) {
925931
return getEnumModifier(enumClass, structureModifier.getField(index).getType());
926-
}
932+
}
927933

928934
/**
929935
* Retrieves the ID of this packet.
@@ -1076,7 +1082,7 @@ private void readObject(ObjectInputStream input) throws ClassNotFoundException,
10761082
* Construct a new packet data serializer.
10771083
* @return The packet data serializer.
10781084
*/
1079-
private ByteBuf createPacketBuffer() {
1085+
public static ByteBuf createPacketBuffer() {
10801086
ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.buffer();
10811087
Class<?> packetSerializer = MinecraftReflection.getPacketDataSerializerClass();
10821088

@@ -1224,11 +1230,11 @@ public Class<ItemStack[]> getSpecificType() {
12241230
* Represents an equivalent converter for ChatComponent arrays.
12251231
* @author dmulloy2
12261232
*/
1227-
private static class WrappedChatComponentArrayConverter implements EquivalentConverter<WrappedChatComponent[]> {
1233+
private static class LegacyComponentConverter implements EquivalentConverter<WrappedChatComponent[]> {
12281234
final EquivalentConverter<WrappedChatComponent> componentConverter = BukkitConverters.getWrappedChatComponentConverter();
12291235

12301236
@Override
1231-
public Object getGeneric(Class<?>genericType, WrappedChatComponent[] specific) {
1237+
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
12321238
Class<?> nmsComponent = MinecraftReflection.getIChatBaseComponentClass();
12331239
Object[] result = (Object[]) Array.newInstance(nmsComponent, specific.length);
12341240

@@ -1257,6 +1263,94 @@ public Class<WrappedChatComponent[]> getSpecificType() {
12571263
}
12581264
}
12591265

1266+
/**
1267+
* Converts from NBT to WrappedChatComponent arrays
1268+
* @author dmulloy2
1269+
*/
1270+
private static class NBTComponentConverter implements EquivalentConverter<WrappedChatComponent[]> {
1271+
private EquivalentConverter<NbtBase<?>> nbtConverter = BukkitConverters.getNbtConverter();
1272+
private final int lines = 4;
1273+
1274+
@Override
1275+
public WrappedChatComponent[] getSpecific(Object generic) {
1276+
NbtBase<?> nbtBase = nbtConverter.getSpecific(generic);
1277+
NbtCompound compound = (NbtCompound) nbtBase;
1278+
1279+
WrappedChatComponent[] components = new WrappedChatComponent[lines];
1280+
for (int i = 0; i < lines; i++) {
1281+
if (compound.containsKey("Text" + (i + 1))) {
1282+
components[i] = WrappedChatComponent.fromJson(compound.getString("Text" + (i + 1)));
1283+
} else {
1284+
components[i] = WrappedChatComponent.fromText("");
1285+
}
1286+
}
1287+
1288+
return components;
1289+
}
1290+
1291+
@Override
1292+
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
1293+
NbtCompound compound = NbtFactory.ofCompound("");
1294+
1295+
for (int i = 0; i < lines; i++) {
1296+
WrappedChatComponent component;
1297+
if (i < specific.length && specific[i] != null) {
1298+
component = specific[i];
1299+
} else {
1300+
component = WrappedChatComponent.fromText("");
1301+
}
1302+
1303+
compound.put("Text" + (i + 1), component.getJson());
1304+
}
1305+
1306+
return nbtConverter.getGeneric(genericType, compound);
1307+
}
1308+
1309+
@Override
1310+
public Class<WrappedChatComponent[]> getSpecificType() {
1311+
return WrappedChatComponent[].class;
1312+
}
1313+
}
1314+
1315+
/**
1316+
* A delegated converter that supports NBT to Component Array and regular Component Array
1317+
* @author dmulloy2
1318+
*/
1319+
private static class ComponentArrayConverter implements EquivalentConverter<WrappedChatComponent[]> {
1320+
private static final EquivalentConverter<WrappedChatComponent[]> DELEGATE;
1321+
static {
1322+
Class<?> packetClass = PacketType.Play.Server.UPDATE_SIGN.getPacketClass();
1323+
if (packetClass.getName().contains("Sign")) {
1324+
DELEGATE = new LegacyComponentConverter();
1325+
} else {
1326+
DELEGATE = new NBTComponentConverter();
1327+
}
1328+
}
1329+
1330+
@Override
1331+
public WrappedChatComponent[] getSpecific(Object generic) {
1332+
return DELEGATE.getSpecific(generic);
1333+
}
1334+
1335+
@Override
1336+
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
1337+
return DELEGATE.getGeneric(genericType, specific);
1338+
}
1339+
1340+
@Override
1341+
public Class<WrappedChatComponent[]> getSpecificType() {
1342+
return DELEGATE.getSpecificType();
1343+
}
1344+
1345+
public static Class<?> getGenericType() {
1346+
if (DELEGATE instanceof NBTComponentConverter) {
1347+
return MinecraftReflection.getNBTCompoundClass();
1348+
} else {
1349+
return MinecraftReflection.getIChatBaseComponentArrayClass();
1350+
}
1351+
}
1352+
}
1353+
12601354
@Override
12611355
public String toString() {
12621356
return "PacketContainer[type=" + type + ", structureModifier=" + structureModifier + "]";

modules/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,19 @@ public static enum Action {
520520
UPDATE_PROPERTIES;
521521
}
522522

523+
@Test
524+
public void testComponentArrays() {
525+
PacketContainer signChange = new PacketContainer(PacketType.Play.Server.TILE_ENTITY_DATA);
526+
WrappedChatComponent[] components = new WrappedChatComponent[] {
527+
WrappedChatComponent.fromText("hello world"), WrappedChatComponent.fromText(""),
528+
WrappedChatComponent.fromText(""), WrappedChatComponent.fromText("")
529+
};
530+
signChange.getChatComponentArrays().write(0, components);
531+
532+
WrappedChatComponent[] back = signChange.getChatComponentArrays().read(0);
533+
assertArrayEquals(components, back);
534+
}
535+
523536
private static final List<PacketType> BLACKLISTED = Util.asList(
524537
PacketType.Play.Client.CUSTOM_PAYLOAD, PacketType.Play.Server.CUSTOM_PAYLOAD,
525538
PacketType.Play.Server.SET_COOLDOWN

0 commit comments

Comments
 (0)