Commits

blablubbabc authored and md_5 committed ff2b9440f54
SPIGOT-6304: Removed the detection of legacy text based on color codes

Apparently there are items and plugins out there that still use legacy color codes within text components, and which thereby break this heuristic. Our remaining approach to differentiate between legacy (plain) and modern (JSON-based) text is to check if a particular text can be parsed as JSON-based text. This approach is not perfect either as there are ambiguous cases that it cannot resolve correctly. However, these cases are hopefully rare enough in practice that this approach remains suitable.
No tags

src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java

Modified
17 17 import net.minecraft.server.ChatModifier;
18 18 import net.minecraft.server.EnumChatFormat;
19 19 import net.minecraft.server.IChatBaseComponent;
20 20 import net.minecraft.server.IChatMutableComponent;
21 21 import org.bukkit.ChatColor;
22 22
23 23 public final class CraftChatMessage {
24 24
25 25 private static final Pattern LINK_PATTERN = Pattern.compile("((?:(?:https?):\\/\\/)?(?:[-\\w_\\.]{2,}\\.[a-z]{2,4}.*?(?=[\\.\\?!,;:]?(?:[" + String.valueOf(org.bukkit.ChatColor.COLOR_CHAR) + " \\n]|$))))");
26 26 private static final Map<Character, EnumChatFormat> formatMap;
27 - private static final String COLOR_CHAR_STRING = String.valueOf(ChatColor.COLOR_CHAR);
28 27
29 28 static {
30 29 Builder<Character, EnumChatFormat> builder = ImmutableMap.builder();
31 30 for (EnumChatFormat format : EnumChatFormat.values()) {
32 31 builder.put(Character.toLowerCase(format.toString().charAt(1)), format);
33 32 }
34 33 formatMap = builder.build();
35 34 }
36 35
37 36 public static EnumChatFormat getColor(ChatColor color) {
209 208 return fromJSONOrString(message, false);
210 209 }
211 210
212 211 public static IChatBaseComponent fromJSONOrString(String message, boolean keepNewlines) {
213 212 return fromJSONOrString(message, false, keepNewlines);
214 213 }
215 214
216 215 private static IChatBaseComponent fromJSONOrString(String message, boolean nullable, boolean keepNewlines) {
217 216 if (message == null) message = "";
218 217 if (nullable && message.isEmpty()) return null;
219 - if (isLegacy(message)) {
220 - return fromString(message, keepNewlines)[0];
218 + IChatBaseComponent component = fromJSONOrNull(message);
219 + if (component != null) {
220 + return component;
221 221 } else {
222 - IChatBaseComponent component = fromJSONOrNull(message);
223 - if (component != null) {
224 - return component;
225 - } else {
226 - return fromString(message, keepNewlines)[0];
227 - }
222 + return fromString(message, keepNewlines)[0];
228 223 }
229 224 }
230 225
231 226 public static String fromJSONOrStringToJSON(String message) {
232 227 return fromJSONOrStringToJSON(message, false);
233 228 }
234 229
235 230 public static String fromJSONOrStringToJSON(String message, boolean keepNewlines) {
236 231 return fromJSONOrStringToJSON(message, false, keepNewlines, Integer.MAX_VALUE, false);
237 232 }
240 235 return fromJSONOrStringOrNullToJSON(message, false);
241 236 }
242 237
243 238 public static String fromJSONOrStringOrNullToJSON(String message, boolean keepNewlines) {
244 239 return fromJSONOrStringToJSON(message, true, keepNewlines, Integer.MAX_VALUE, false);
245 240 }
246 241
247 242 public static String fromJSONOrStringToJSON(String message, boolean nullable, boolean keepNewlines, int maxLength, boolean checkJsonContentLength) {
248 243 if (message == null) message = "";
249 244 if (nullable && message.isEmpty()) return null;
250 - if (isLegacy(message)) {
251 - message = trimMessage(message, maxLength);
252 - return fromStringToJSON(message, keepNewlines);
253 - } else {
254 - // If the input can be parsed as JSON, we use that:
255 - IChatBaseComponent component = fromJSONOrNull(message);
256 - if (component != null) {
257 - if (checkJsonContentLength) {
258 - String content = fromComponent(component);
259 - String trimmedContent = trimMessage(content, maxLength);
260 - if (content != trimmedContent) { // identity comparison is fine here
261 - // Note: The resulting text has all non-plain text features stripped.
262 - return fromStringToJSON(trimmedContent, keepNewlines);
263 - }
245 + // If the input can be parsed as JSON, we use that:
246 + IChatBaseComponent component = fromJSONOrNull(message);
247 + if (component != null) {
248 + if (checkJsonContentLength) {
249 + String content = fromComponent(component);
250 + String trimmedContent = trimMessage(content, maxLength);
251 + if (content != trimmedContent) { // identity comparison is fine here
252 + // Note: The resulting text has all non-plain text features stripped.
253 + return fromStringToJSON(trimmedContent, keepNewlines);
264 254 }
265 - return message;
266 - } else {
267 - // Else we interpret the input as legacy text:
268 - message = trimMessage(message, maxLength);
269 - return fromStringToJSON(message, keepNewlines);
270 255 }
256 + return message;
257 + } else {
258 + // Else we interpret the input as legacy text:
259 + message = trimMessage(message, maxLength);
260 + return fromStringToJSON(message, keepNewlines);
271 261 }
272 262 }
273 263
274 264 public static String trimMessage(String message, int maxLength) {
275 265 if (message != null && message.length() > maxLength) {
276 266 return message.substring(0, maxLength);
277 267 } else {
278 268 return message;
279 269 }
280 270 }
281 271
282 - // Heuristic detection of legacy (plain) text.
283 - // May produce false-negatives: I.e. a return value of false does not imply that the text represents modern (JSON-based) text.
284 - // We also consider empty Strings as legacy. The component deserializer cannot parse them (produces null).
285 - private static boolean isLegacy(String message) {
286 - // assert: message != null
287 - return message.trim().isEmpty() || message.contains(COLOR_CHAR_STRING);
288 - }
289 -
290 272 public static String fromStringToJSON(String message) {
291 273 return fromStringToJSON(message, false);
292 274 }
293 275
294 276 public static String fromStringToJSON(String message, boolean keepNewlines) {
295 277 IChatBaseComponent component = CraftChatMessage.fromString(message, keepNewlines)[0];
296 278 return CraftChatMessage.toJSON(component);
297 279 }
298 280
299 281 public static String fromStringOrNullToJSON(String message) {

Everything looks good. We'll let you know here if there's anything you should know about.

Add shortcut