Skip to content

Commit 144723a

Browse files
committed
Ensure we're always using leniency with components
Fixes #252
1 parent 087913a commit 144723a

File tree

3 files changed

+92
-23
lines changed

3 files changed

+92
-23
lines changed

modules/API/src/main/java/com/comphenix/protocol/wrappers/ComponentConverter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
* Note: The BungeeCord Chat API is not included in CraftBukkit.
2626
* @author dmulloy2
2727
*/
28-
2928
public final class ComponentConverter {
3029

3130
private ComponentConverter() {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
3+
* Copyright (C) 2016 dmulloy2
4+
*
5+
* This program is free software; you can redistribute it and/or modify it under the terms of the
6+
* GNU General Public License as published by the Free Software Foundation; either version 2 of
7+
* the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11+
* See the GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License along with this program;
14+
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
15+
* 02111-1307 USA
16+
*/
17+
package com.comphenix.protocol.wrappers;
18+
19+
import java.io.IOException;
20+
import java.io.StringReader;
21+
import java.lang.reflect.Method;
22+
23+
/**
24+
* Handles component parsing in 1.8
25+
* @author dmulloy2
26+
*/
27+
public class ComponentParser {
28+
29+
private ComponentParser() {
30+
}
31+
32+
public static Object deserialize(Object gson, Class<?> component, StringReader str) {
33+
try {
34+
com.google.gson.stream.JsonReader reader = new com.google.gson.stream.JsonReader(str);
35+
reader.setLenient(true);
36+
return ((com.google.gson.Gson) gson).getAdapter(component).read(reader);
37+
} catch (IOException ex) {
38+
throw new RuntimeException("Failed to read JSON", ex);
39+
} catch (LinkageError er) {
40+
return deserializeLegacy(gson, component, str);
41+
}
42+
}
43+
44+
// Should only be needed on 1.8.
45+
private static Object deserializeLegacy(Object gson, Class<?> component, StringReader str) {
46+
try {
47+
Class<?> readerClass = Class.forName("org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonReader");
48+
Object reader = readerClass.getConstructor(StringReader.class).newInstance(str);
49+
Method setLenient = readerClass.getMethod("setLenienent", boolean.class);
50+
setLenient.invoke(reader, true);
51+
Method getAdapter = gson.getClass().getMethod("getAdapter", Class.class);
52+
Object adapter = getAdapter.invoke(gson, component);
53+
Method read = adapter.getClass().getMethod("read", readerClass);
54+
return read.invoke(adapter, reader);
55+
} catch (ReflectiveOperationException ex) {
56+
throw new RuntimeException("Failed to read JSON", ex);
57+
}
58+
}
59+
}

modules/API/src/main/java/com/comphenix/protocol/wrappers/WrappedChatComponent.java

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.comphenix.protocol.wrappers;
22

3-
import java.lang.reflect.Method;
4-
import java.util.List;
3+
import java.io.StringReader;
54

65
import org.bukkit.ChatColor;
76

7+
import com.comphenix.protocol.reflect.FieldUtils;
88
import com.comphenix.protocol.reflect.FuzzyReflection;
99
import com.comphenix.protocol.reflect.accessors.Accessors;
1010
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
@@ -19,18 +19,35 @@
1919
public class WrappedChatComponent extends AbstractWrapper {
2020
private static final Class<?> SERIALIZER = MinecraftReflection.getChatSerializerClass();
2121
private static final Class<?> COMPONENT = MinecraftReflection.getIChatBaseComponentClass();
22+
private static final Class<?> GSON_CLASS = MinecraftReflection.getMinecraftGsonClass();
23+
24+
private static Object GSON = null;
25+
private static MethodAccessor DESERIALIZE = null;
26+
2227
private static MethodAccessor SERIALIZE_COMPONENT = null;
23-
private static MethodAccessor DESERIALIZE_COMPONENT = null;
2428
private static MethodAccessor CONSTRUCT_COMPONENT = null;
2529
private static ConstructorAccessor CONSTRUCT_TEXT_COMPONENT = null;
26-
30+
2731
static {
28-
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER);
32+
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true);
2933

3034
// Retrieve the correct methods
3135
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("serialize", /* a */
3236
String.class, new Class<?>[] { COMPONENT }));
33-
DESERIALIZE_COMPONENT = findDeserialize(fuzzy);
37+
38+
try {
39+
GSON = FieldUtils.readStaticField(fuzzy.getFieldByType("gson", GSON_CLASS), true);
40+
} catch (IllegalAccessException ex) {
41+
throw new RuntimeException("Failed to obtain GSON field", ex);
42+
}
43+
44+
try {
45+
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getMinecraftClass("ChatDeserializer"), true)
46+
.getMethodByParameters("deserialize", Object.class, new Class<?>[] { GSON_CLASS, String.class, Class.class, boolean.class }));
47+
} catch (IllegalArgumentException ex) {
48+
// We'll handle it in the ComponentParser
49+
DESERIALIZE = null;
50+
}
3451

3552
// Get a component from a standard Minecraft message
3653
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(MinecraftReflection.getCraftChatMessage(), "fromString", String.class);
@@ -39,23 +56,17 @@ public class WrappedChatComponent extends AbstractWrapper {
3956
CONSTRUCT_TEXT_COMPONENT = Accessors.getConstructorAccessor(MinecraftReflection.getChatComponentTextClass(), String.class);
4057
}
4158

42-
private static MethodAccessor findDeserialize(FuzzyReflection fuzzy) {
43-
List<Method> methods = fuzzy.getMethodListByParameters(COMPONENT, new Class<?>[] { String.class });
44-
if (methods.isEmpty()) {
45-
throw new IllegalArgumentException("Unable to find deserialize method in " + fuzzy.getSource().getName());
46-
}
47-
48-
// Try to find b, we want leniency
49-
for (Method method : methods) {
50-
if (method.getName().equals("b")) {
51-
return Accessors.getMethodAccessor(method);
52-
}
59+
private static Object deserialize(String json) {
60+
// Should be non-null on 1.9 and up
61+
if (DESERIALIZE != null) {
62+
return DESERIALIZE.invoke(null, GSON, json, COMPONENT, true);
5363
}
5464

55-
// Oh well
56-
return Accessors.getMethodAccessor(methods.get(0));
65+
// Mock leniency behavior in 1.8
66+
StringReader str = new StringReader(json);
67+
return ComponentParser.deserialize(GSON, COMPONENT, str);
5768
}
58-
69+
5970
private transient String cache;
6071

6172
private WrappedChatComponent(Object handle, String cache) {
@@ -79,7 +90,7 @@ public static WrappedChatComponent fromHandle(Object handle) {
7990
* @return The chat component wrapper.
8091
*/
8192
public static WrappedChatComponent fromJson(String json) {
82-
return new WrappedChatComponent(DESERIALIZE_COMPONENT.invoke(null, json), json);
93+
return new WrappedChatComponent(deserialize(json), json);
8394
}
8495

8596
/**
@@ -127,7 +138,7 @@ public String getJson() {
127138
* @param obj - the JSON that represents the new component.
128139
*/
129140
public void setJson(String obj) {
130-
this.handle = DESERIALIZE_COMPONENT.invoke(null, obj);
141+
this.handle = deserialize(obj);
131142
this.cache = obj;
132143
}
133144

0 commit comments

Comments
 (0)