Merge f822c8523c
into e1255edb32
This commit is contained in:
commit
45ccda0aaa
|
@ -1,18 +1,22 @@
|
|||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final class ChannelProtocolUtil {
|
||||
|
@ -28,7 +32,9 @@ final class ChannelProtocolUtil {
|
|||
.build());
|
||||
|
||||
BiFunction<Channel, PacketType.Sender, Object> baseResolver = null;
|
||||
if (attributeKeys.size() == 1) {
|
||||
if (attributeKeys.isEmpty()) {
|
||||
baseResolver = new Post1_20_4WrappedResolver();
|
||||
} else if (attributeKeys.size() == 1) {
|
||||
// if there is only one attribute key we can assume it's the correct one (1.8 - 1.20.1)
|
||||
Object protocolKey = Accessors.getFieldAccessor(attributeKeys.get(0)).get(null);
|
||||
baseResolver = new Pre1_20_2DirectResolver((AttributeKey<Object>) protocolKey);
|
||||
|
@ -130,4 +136,69 @@ final class ChannelProtocolUtil {
|
|||
return this.protocolAccessor;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Post1_20_4WrappedResolver implements BiFunction<Channel, PacketType.Sender, Object> {
|
||||
|
||||
// lazy initialized when needed
|
||||
private Function<Object, Object> serverProtocolAccessor;
|
||||
private Function<Object, Object> clientProtocolAccessor;
|
||||
|
||||
@Override
|
||||
public Object apply(Channel channel, PacketType.Sender sender) {
|
||||
String key = this.getKeyForSender(sender);
|
||||
Object codecHandler = channel.pipeline().get(key);
|
||||
if (codecHandler == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Function<Object, Object> protocolAccessor = this.getProtocolAccessor(sender, codecHandler.getClass());
|
||||
|
||||
return protocolAccessor.apply(codecHandler);
|
||||
}
|
||||
|
||||
private String getKeyForSender(PacketType.Sender sender) {
|
||||
switch (sender) {
|
||||
case SERVER:
|
||||
return "encoder";
|
||||
case CLIENT:
|
||||
return "decoder";
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal packet sender " + sender.name());
|
||||
}
|
||||
}
|
||||
|
||||
private Function<Object, Object> getProtocolAccessor(PacketType.Sender sender, Class<?> codecHandler) {
|
||||
switch (sender) {
|
||||
case SERVER:
|
||||
if (this.serverProtocolAccessor == null) {
|
||||
this.serverProtocolAccessor = getProtocolAccessor(codecHandler);
|
||||
}
|
||||
return this.serverProtocolAccessor;
|
||||
case CLIENT:
|
||||
if (this.clientProtocolAccessor == null) {
|
||||
this.clientProtocolAccessor = getProtocolAccessor(codecHandler);
|
||||
}
|
||||
return this.clientProtocolAccessor;
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal packet sender " + sender.name());
|
||||
}
|
||||
}
|
||||
|
||||
private Function<Object, Object> getProtocolAccessor(Class<?> codecHandler) {
|
||||
Class<?> protocolInfoClass = MinecraftReflection.getMinecraftClass("network.ProtocolInfo");
|
||||
|
||||
MethodAccessor protocolAccessor = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(protocolInfoClass)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeExact(MinecraftReflection.getEnumProtocolClass())
|
||||
.build()));
|
||||
|
||||
FieldAccessor protocolInfoAccessor = Accessors.getFieldAccessor(codecHandler, protocolInfoClass, true);
|
||||
|
||||
return (handler) -> {
|
||||
Object protocolInfo = protocolInfoAccessor.get(handler);
|
||||
return protocolAccessor.invoke(protocolInfo);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,9 +18,21 @@
|
|||
package com.comphenix.protocol.injector.packet;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
|
@ -279,6 +291,172 @@ public class PacketRegistry {
|
|||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static synchronized Register createNewNewRegister() {
|
||||
Object[] protocols = ENUM_PROTOCOL.getEnumConstants();
|
||||
|
||||
// ID to Packet class maps
|
||||
final Map<Object, Class<?>> packetTypeMap = new HashMap<>();
|
||||
final Map<Object, Map<Class<?>, Integer>> serverMaps = new LinkedHashMap<>();
|
||||
final Map<Object, Map<Class<?>, Integer>> clientMaps = new LinkedHashMap<>();
|
||||
|
||||
String[] protocolClassNames = new String[] {
|
||||
"configuration.ConfigurationProtocols",
|
||||
"game.GameProtocols",
|
||||
"handshake.HandshakeProtocols",
|
||||
"login.LoginProtocols",
|
||||
"status.StatusProtocols"
|
||||
};
|
||||
|
||||
String[] packetTypesClassNames = new String[] {
|
||||
"common.CommonPacketTypes",
|
||||
"configuration.ConfigurationPacketTypes",
|
||||
"cookie.CookiePacketTypes",
|
||||
"game.GamePacketTypes",
|
||||
"handshake.HandshakePacketTypes",
|
||||
"login.LoginPacketTypes",
|
||||
"ping.PingPacketTypes",
|
||||
"status.StatusPacketTypes",
|
||||
};
|
||||
|
||||
Class<?> protocolInfoClass = MinecraftReflection.getMinecraftClass("network.ProtocolInfo");
|
||||
Class<?> protocolInfoUnboundClass = MinecraftReflection.getMinecraftClass("network.ProtocolInfo$a");
|
||||
Class<?> streamCodecClass = MinecraftReflection.getMinecraftClass("network.codec.StreamCodec");
|
||||
Class<?> idDispatchCodecClass = MinecraftReflection.getMinecraftClass("network.codec.IdDispatchCodec");
|
||||
Class<?> packetTypeClass = MinecraftReflection.getMinecraftClass("network.protocol.PacketType");
|
||||
Class<?> protocolDirectionClass = MinecraftReflection.getMinecraftClass("network.protocol.EnumProtocolDirection");
|
||||
|
||||
Function<?, ?> emptyFunction = input -> null;
|
||||
|
||||
Method protocolMethod = FuzzyReflection.fromClass(protocolInfoClass)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeExact(MinecraftReflection.getEnumProtocolClass())
|
||||
.build());
|
||||
protocolMethod.setAccessible(true);
|
||||
|
||||
Method directionMethod = FuzzyReflection.fromClass(protocolInfoClass)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeExact(protocolDirectionClass)
|
||||
.build());
|
||||
directionMethod.setAccessible(true);
|
||||
|
||||
Method codecMethod = FuzzyReflection.fromClass(protocolInfoClass)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeExact(streamCodecClass)
|
||||
.build());
|
||||
codecMethod.setAccessible(true);
|
||||
|
||||
Method bindMethod = FuzzyReflection.fromClass(protocolInfoUnboundClass)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeExact(protocolInfoClass)
|
||||
.build());
|
||||
bindMethod.setAccessible(true);
|
||||
|
||||
Field toIdField = FuzzyReflection.fromClass(idDispatchCodecClass, true)
|
||||
.getField(FuzzyFieldContract.newBuilder()
|
||||
.typeDerivedOf(Map.class)
|
||||
.build());
|
||||
toIdField.setAccessible(true);
|
||||
|
||||
for (String packetTypesClassName : packetTypesClassNames) {
|
||||
Class<?> packetTypesClass = MinecraftReflection.getMinecraftClass("network.protocol." + packetTypesClassName);
|
||||
|
||||
for (Field field : packetTypesClass.getDeclaredFields()) {
|
||||
try {
|
||||
if (!Modifier.isFinal(field.getModifiers()) || !Modifier.isStatic(field.getModifiers())) {
|
||||
System.out.println("failed modifier " + field);
|
||||
continue;
|
||||
}
|
||||
|
||||
Object packetType = field.get(null);
|
||||
if (!packetTypeClass.isInstance(packetType)) {
|
||||
System.out.println("failed packetTypeClass " + field);
|
||||
continue;
|
||||
}
|
||||
|
||||
Type packet = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
|
||||
if (packet instanceof Class<?>) {
|
||||
packetTypeMap.put(packetType, (Class<?>) packet);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (String protocolClassName : protocolClassNames) {
|
||||
Class<?> protocolClass = MinecraftReflection.getMinecraftClass("network.protocol." + protocolClassName);
|
||||
|
||||
for (Field field : protocolClass.getDeclaredFields()) {
|
||||
try {
|
||||
if (!Modifier.isFinal(field.getModifiers()) || !Modifier.isStatic(field.getModifiers())) {
|
||||
System.out.println("failed modifier " + field);
|
||||
continue;
|
||||
}
|
||||
|
||||
Object protocolInfo = field.get(null);
|
||||
if (protocolInfoUnboundClass.isInstance(protocolInfo)) {
|
||||
protocolInfo = bindMethod.invoke(protocolInfo, new Object[] { emptyFunction });
|
||||
}
|
||||
|
||||
if (!protocolInfoClass.isInstance(protocolInfo)) {
|
||||
System.out.println("failed protocolInfoClass " + field);
|
||||
continue;
|
||||
}
|
||||
|
||||
Object codec = codecMethod.invoke(protocolInfo);
|
||||
if (!idDispatchCodecClass.isInstance(codec)) {
|
||||
System.out.println("failed idDispatchCodecClass " + field);
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<Class<?>, Integer> packetMap = new HashMap<>();
|
||||
Map<Object, Integer> packetTypeIdMap = (Map<Object, Integer>) toIdField.get(codec);
|
||||
|
||||
for (Map.Entry<Object, Integer> entry : packetTypeIdMap.entrySet()) {
|
||||
Class<?> packet = packetTypeMap.get(entry.getKey());
|
||||
if (packet == null) {
|
||||
throw new RuntimeException("packetType missing packet " + entry.getKey());
|
||||
}
|
||||
|
||||
packetMap.put(packet, entry.getValue());
|
||||
}
|
||||
|
||||
|
||||
Object protocol = protocolMethod.invoke(protocolInfo);
|
||||
String direction = directionMethod.invoke(protocolInfo).toString();
|
||||
|
||||
if (direction.contains("CLIENTBOUND")) { // Sent by Server
|
||||
serverMaps.put(protocol, packetMap);
|
||||
} else if (direction.contains("SERVERBOUND")) { // Sent by Client
|
||||
clientMaps.put(protocol, packetMap);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Register result = new Register();
|
||||
|
||||
for (Object protocol : protocols) {
|
||||
Enum<?> enumProtocol = (Enum<?>) protocol;
|
||||
PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol);
|
||||
|
||||
// Associate known types
|
||||
if (serverMaps.containsKey(protocol)) {
|
||||
associatePackets(result, reverse(serverMaps.get(protocol)), equivalent, Sender.SERVER);
|
||||
}
|
||||
if (clientMaps.containsKey(protocol)) {
|
||||
associatePackets(result, reverse(clientMaps.get(protocol)), equivalent, Sender.CLIENT);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses a key->value map to value->key
|
||||
* Non-deterministic behavior when multiple keys are mapped to the same value
|
||||
|
@ -330,7 +508,9 @@ public class PacketRegistry {
|
|||
return;
|
||||
}
|
||||
|
||||
if (MinecraftVersion.BEE_UPDATE.atOrAbove()) {
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
REGISTER = createNewNewRegister();
|
||||
} else if (MinecraftVersion.BEE_UPDATE.atOrAbove()) {
|
||||
REGISTER = createNewRegister();
|
||||
} else {
|
||||
REGISTER = createOldRegister();
|
||||
|
|
|
@ -62,7 +62,7 @@ public final class ChatExtensions {
|
|||
// since 1.19 system chat is extracted into a separate packet
|
||||
packet = new PacketContainer(PacketType.Play.Server.SYSTEM_CHAT);
|
||||
|
||||
packet.getStrings().write(0, component.getJson());
|
||||
packet.getChatComponents().write(0, component);
|
||||
packet.getBooleans().write(0, false);
|
||||
} else {
|
||||
packet = new PacketContainer(PacketType.Play.Server.CHAT);
|
||||
|
|
|
@ -1660,7 +1660,7 @@ public final class MinecraftReflection {
|
|||
*/
|
||||
private static void useFallbackServer() {
|
||||
// Get the first constructor that matches CraftServer(MINECRAFT_OBJECT, ANY)
|
||||
Constructor<?> selected = FuzzyReflection.fromClass(getCraftBukkitClass("CraftServer"))
|
||||
Constructor<?> selected = FuzzyReflection.fromClass(getCraftServer())
|
||||
.getConstructor(FuzzyMethodContract.newBuilder()
|
||||
.parameterMatches(getMinecraftObjectMatcher(), 0)
|
||||
.parameterCount(2)
|
||||
|
@ -1716,4 +1716,12 @@ public final class MinecraftReflection {
|
|||
public static Class<?> getHolderClass() {
|
||||
return getMinecraftClass("core.Holder");
|
||||
}
|
||||
|
||||
public static Class<?> getCraftServer() {
|
||||
return getCraftBukkitClass("CraftServer");
|
||||
}
|
||||
|
||||
public static Class<?> getHolderLookupProviderClass() {
|
||||
return getMinecraftClass("core.HolderLookup$a" /* Spigot Mappings */, "core.HolderLookup$Provider" /* Mojang Mappings */);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
|
||||
public class MinecraftRegistryAccess {
|
||||
|
||||
private static MethodAccessor GET_SERVER = null;
|
||||
private static MethodAccessor REGISTRY_ACCESS = null;
|
||||
|
||||
private static Object registryAccess = null;
|
||||
|
||||
static {
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
GET_SERVER = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getCraftServer(), false)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnDerivedOf(MinecraftReflection.getMinecraftServerClass())
|
||||
.build()));
|
||||
|
||||
REGISTRY_ACCESS = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getMinecraftServerClass(), false)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnDerivedOf(MinecraftReflection.getHolderLookupProviderClass())
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
public static Object get() {
|
||||
if (GET_SERVER == null || REGISTRY_ACCESS == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (registryAccess == null) {
|
||||
Object server = GET_SERVER.invoke(Bukkit.getServer());
|
||||
registryAccess = REGISTRY_ACCESS.invoke(server);
|
||||
}
|
||||
|
||||
return registryAccess;
|
||||
}
|
||||
}
|
|
@ -36,6 +36,11 @@ import org.bukkit.Server;
|
|||
* @author Kristian
|
||||
*/
|
||||
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
|
||||
/**
|
||||
* Version 1.20.5 - the cookie and transfer packet update
|
||||
*/
|
||||
public static final MinecraftVersion v1_20_5 = new MinecraftVersion("1.20.5");
|
||||
|
||||
/**
|
||||
* Version 1.20.4 - the decorated pot update
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import java.io.StringReader;
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -11,10 +10,10 @@ import com.comphenix.protocol.reflect.accessors.Accessors;
|
|||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftRegistryAccess;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.minecraft.network.chat.IChatBaseComponent;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* Represents a chat component added in Minecraft 1.7.2
|
||||
|
@ -39,14 +38,22 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
|
|||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true);
|
||||
|
||||
// Retrieve the correct methods
|
||||
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */
|
||||
String.class, new Class<?>[] { COMPONENT }));
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */
|
||||
String.class, new Class<?>[] { COMPONENT, MinecraftReflection.getHolderLookupProviderClass() }));
|
||||
} else {
|
||||
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */
|
||||
String.class, new Class<?>[] { COMPONENT }));
|
||||
}
|
||||
|
||||
GSON = Accessors.getFieldAccessor(fuzzy.getFieldByType("gson", GSON_CLASS)).get(null);
|
||||
|
||||
if (MinecraftVersion.v1_20_4.atOrAbove()) {
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(SERIALIZER, false)
|
||||
.getMethodByReturnTypeAndParameters("fromJsonLenient", MUTABLE_COMPONENT_CLASS.get(), String.class));
|
||||
.getMethodByReturnTypeAndParameters("fromJson", MUTABLE_COMPONENT_CLASS.get(), new Class[] { String.class, MinecraftReflection.getHolderLookupProviderClass() }));
|
||||
} else if (MinecraftVersion.v1_20_4.atOrAbove()) {
|
||||
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(SERIALIZER, false)
|
||||
.getMethodByReturnTypeAndParameters("fromJson", MUTABLE_COMPONENT_CLASS.get(), new Class[] { String.class }));
|
||||
} else {
|
||||
try {
|
||||
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getChatDeserializer(), true)
|
||||
|
@ -68,7 +75,19 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
|
|||
}
|
||||
}
|
||||
|
||||
private static Object serialize(Object handle) {
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
return SERIALIZE_COMPONENT.invoke(null, handle, MinecraftRegistryAccess.get());
|
||||
}
|
||||
|
||||
return SERIALIZE_COMPONENT.invoke(null, handle);
|
||||
}
|
||||
|
||||
private static Object deserialize(String json) {
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
return DESERIALIZE.invoke(null, json, MinecraftRegistryAccess.get());
|
||||
}
|
||||
|
||||
if (MinecraftVersion.v1_20_4.atOrAbove()) {
|
||||
return DESERIALIZE.invoke(null, json);
|
||||
}
|
||||
|
@ -167,7 +186,7 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
|
|||
*/
|
||||
public String getJson() {
|
||||
if (cache == null) {
|
||||
cache = (String) SERIALIZE_COMPONENT.invoke(null, handle);
|
||||
cache = (String) serialize(handle);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue