From 6054d559e17983ff0acadb0349996214394c891c Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Fri, 19 Jul 2013 23:43:39 +0200 Subject: [PATCH] Add support for the new JSON chat message format. FIXES Ticket-109 In addition, the PacketConstructor can now handle non-wrapped objects, along with Class types instead of instances in withPacket(). --- .../protocol/error/BasicErrorReporter.java | 192 +++++------ .../protocol/error/ErrorReporter.java | 146 ++++---- .../com/comphenix/protocol/error/Report.java | 324 +++++++++--------- .../protocol/error/RethrowErrorReporter.java | 43 +++ .../protocol/injector/PacketConstructor.java | 49 ++- .../protocol/utility/ChatExtensions.java | 82 ++++- 6 files changed, 487 insertions(+), 349 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/error/RethrowErrorReporter.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/error/BasicErrorReporter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/error/BasicErrorReporter.java index 69226d57..dc4225d0 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/error/BasicErrorReporter.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/error/BasicErrorReporter.java @@ -1,96 +1,96 @@ -package com.comphenix.protocol.error; - -import java.io.PrintStream; -import org.bukkit.plugin.Plugin; - -import com.comphenix.protocol.error.Report.ReportBuilder; -import com.comphenix.protocol.reflect.PrettyPrinter; - -/** - * Represents a basic error reporter that prints error reports to the standard error stream. - *

- * Note that this implementation doesn't distinguish between {@link #reportWarning(Object, Report)} - * and {@link #reportDetailed(Object, Report)} - they both have the exact same behavior. - * @author Kristian - */ -public class BasicErrorReporter implements ErrorReporter { - private final PrintStream output; - - /** - * Construct a new basic error reporter that prints directly the standard error stream. - */ - public BasicErrorReporter() { - this(System.err); - } - - /** - * Construct a error reporter that prints to the given output stream. - * @param output - the output stream. - */ - public BasicErrorReporter(PrintStream output) { - this.output = output; - } - - @Override - public void reportMinimal(Plugin sender, String methodName, Throwable error) { - output.println("Unhandled exception occured in " + methodName + " for " + sender.getName()); - error.printStackTrace(output); - } - - @Override - public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) { - reportMinimal(sender, methodName, error); - - // Also print parameters - printParameters(parameters); - } - - @Override - public void reportWarning(Object sender, Report report) { - // Basic warning - output.println("[" + sender.getClass().getSimpleName() + "] " + report.getReportMessage()); - - if (report.getException() != null) { - report.getException().printStackTrace(output); - } - printParameters(report.getCallerParameters()); - } - - @Override - public void reportWarning(Object sender, ReportBuilder reportBuilder) { - reportWarning(sender, reportBuilder.build()); - } - - @Override - public void reportDetailed(Object sender, Report report) { - // No difference from warning - reportWarning(sender, report); - } - - @Override - public void reportDetailed(Object sender, ReportBuilder reportBuilder) { - reportWarning(sender, reportBuilder); - } - - /** - * Print the given parameters to the standard error stream. - * @param parameters - the output parameters. - */ - private void printParameters(Object[] parameters) { - if (parameters != null && parameters.length > 0) { - output.println("Parameters: "); - - try { - for (Object parameter : parameters) { - if (parameter == null) - output.println("[NULL]"); - else - output.println(PrettyPrinter.printObject(parameter)); - } - } catch (IllegalAccessException e) { - // Damn it - e.printStackTrace(); - } - } - } -} +package com.comphenix.protocol.error; + +import java.io.PrintStream; +import org.bukkit.plugin.Plugin; + +import com.comphenix.protocol.error.Report.ReportBuilder; +import com.comphenix.protocol.reflect.PrettyPrinter; + +/** + * Represents a basic error reporter that prints error reports to the standard error stream. + *

+ * Note that this implementation doesn't distinguish between {@link #reportWarning(Object, Report)} + * and {@link #reportDetailed(Object, Report)} - they both have the exact same behavior. + * @author Kristian + */ +public class BasicErrorReporter implements ErrorReporter { + private final PrintStream output; + + /** + * Construct a new basic error reporter that prints directly the standard error stream. + */ + public BasicErrorReporter() { + this(System.err); + } + + /** + * Construct a error reporter that prints to the given output stream. + * @param output - the output stream. + */ + public BasicErrorReporter(PrintStream output) { + this.output = output; + } + + @Override + public void reportMinimal(Plugin sender, String methodName, Throwable error) { + output.println("Unhandled exception occured in " + methodName + " for " + sender.getName()); + error.printStackTrace(output); + } + + @Override + public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) { + reportMinimal(sender, methodName, error); + + // Also print parameters + printParameters(parameters); + } + + @Override + public void reportWarning(Object sender, Report report) { + // Basic warning + output.println("[" + sender.getClass().getSimpleName() + "] " + report.getReportMessage()); + + if (report.getException() != null) { + report.getException().printStackTrace(output); + } + printParameters(report.getCallerParameters()); + } + + @Override + public void reportWarning(Object sender, ReportBuilder reportBuilder) { + reportWarning(sender, reportBuilder.build()); + } + + @Override + public void reportDetailed(Object sender, Report report) { + // No difference from warning + reportWarning(sender, report); + } + + @Override + public void reportDetailed(Object sender, ReportBuilder reportBuilder) { + reportWarning(sender, reportBuilder); + } + + /** + * Print the given parameters to the standard error stream. + * @param parameters - the output parameters. + */ + private void printParameters(Object[] parameters) { + if (parameters != null && parameters.length > 0) { + output.println("Parameters: "); + + try { + for (Object parameter : parameters) { + if (parameter == null) + output.println("[NULL]"); + else + output.println(PrettyPrinter.printObject(parameter)); + } + } catch (IllegalAccessException e) { + // Damn it + e.printStackTrace(); + } + } + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/error/ErrorReporter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/error/ErrorReporter.java index fde3334f..f2da5e51 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/error/ErrorReporter.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/error/ErrorReporter.java @@ -1,74 +1,74 @@ -/* - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. - * Copyright (C) 2012 Kristian S. Stangeland - * - * 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - */ - -package com.comphenix.protocol.error; - -import org.bukkit.plugin.Plugin; - -import com.comphenix.protocol.error.Report.ReportBuilder; - -/** - * Represents an object that can forward an error {@link Report} to the display and permanent storage. - * - * @author Kristian - */ -public interface ErrorReporter { - /** - * Prints a small minimal error report regarding an exception from another plugin. - * @param sender - the other plugin. - * @param methodName - name of the caller method. - * @param error - the exception itself. - */ - public abstract void reportMinimal(Plugin sender, String methodName, Throwable error); - - /** - * Prints a small minimal error report regarding an exception from another plugin. - * @param sender - the other plugin. - * @param methodName - name of the caller method. - * @param error - the exception itself. - * @param parameters - any relevant parameters to print. - */ - public abstract void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters); - - /** - * Prints a warning message from the current plugin. - * @param sender - the object containing the caller method. - * @param report - an error report to include. - */ - public abstract void reportWarning(Object sender, Report report); - - /** - * Prints a warning message from the current plugin. - * @param sender - the object containing the caller method. - * @param reportBuilder - an error report builder that will be used to get the report. - */ - public abstract void reportWarning(Object sender, ReportBuilder reportBuilder); - - /** - * Prints a detailed error report about an unhandled exception. - * @param sender - the object containing the caller method. - * @param report - an error report to include. - */ - public abstract void reportDetailed(Object sender, Report report); - - /** - * Prints a detailed error report about an unhandled exception. - * @param sender - the object containing the caller method. - * @param reportBuilder - an error report builder that will be used to get the report. - */ - public abstract void reportDetailed(Object sender, ReportBuilder reportBuilder); +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.comphenix.protocol.error; + +import org.bukkit.plugin.Plugin; + +import com.comphenix.protocol.error.Report.ReportBuilder; + +/** + * Represents an object that can forward an error {@link Report} to the display and permanent storage. + * + * @author Kristian + */ +public interface ErrorReporter { + /** + * Prints a small minimal error report regarding an exception from another plugin. + * @param sender - the other plugin. + * @param methodName - name of the caller method. + * @param error - the exception itself. + */ + public abstract void reportMinimal(Plugin sender, String methodName, Throwable error); + + /** + * Prints a small minimal error report regarding an exception from another plugin. + * @param sender - the other plugin. + * @param methodName - name of the caller method. + * @param error - the exception itself. + * @param parameters - any relevant parameters to print. + */ + public abstract void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters); + + /** + * Prints a warning message from the current plugin. + * @param sender - the object containing the caller method. + * @param report - an error report to include. + */ + public abstract void reportWarning(Object sender, Report report); + + /** + * Prints a warning message from the current plugin. + * @param sender - the object containing the caller method. + * @param reportBuilder - an error report builder that will be used to get the report. + */ + public abstract void reportWarning(Object sender, ReportBuilder reportBuilder); + + /** + * Prints a detailed error report about an unhandled exception. + * @param sender - the object containing the caller method. + * @param report - an error report to include. + */ + public abstract void reportDetailed(Object sender, Report report); + + /** + * Prints a detailed error report about an unhandled exception. + * @param sender - the object containing the caller method. + * @param reportBuilder - an error report builder that will be used to get the report. + */ + public abstract void reportDetailed(Object sender, ReportBuilder reportBuilder); } \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/error/Report.java b/ProtocolLib/src/main/java/com/comphenix/protocol/error/Report.java index 177c0407..b408d3b8 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/error/Report.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/error/Report.java @@ -1,162 +1,162 @@ -package com.comphenix.protocol.error; - -import javax.annotation.Nullable; - -/** - * Represents a error or warning report. - * - * @author Kristian - */ -public class Report { - private final ReportType type; - private final Throwable exception; - private final Object[] messageParameters; - private final Object[] callerParameters; - - /** - * Must be constructed through the factory method in Report. - */ - public static class ReportBuilder { - private ReportType type; - private Throwable exception; - private Object[] messageParameters; - private Object[] callerParameters; - - private ReportBuilder() { - // Don't allow - } - - /** - * Set the current report type. Cannot be NULL. - * @param type - report type. - * @return This builder, for chaining. - */ - public ReportBuilder type(ReportType type) { - if (type == null) - throw new IllegalArgumentException("Report type cannot be set to NULL."); - this.type = type; - return this; - } - - /** - * Set the current exception that occured. - * @param exception - exception that occured. - * @return This builder, for chaining. - */ - public ReportBuilder error(@Nullable Throwable exception) { - this.exception = exception; - return this; - } - - /** - * Set the message parameters that are used to construct a message text. - * @param messageParameters - parameters for the report type. - * @return This builder, for chaining. - */ - public ReportBuilder messageParam(@Nullable Object... messageParameters) { - this.messageParameters = messageParameters; - return this; - } - - /** - * Set the parameters in the caller method. This is optional. - * @param callerParameters - parameters of the caller method. - * @return This builder, for chaining. - */ - public ReportBuilder callerParam(@Nullable Object... callerParameters) { - this.callerParameters = callerParameters; - return this; - } - - /** - * Construct a new report with the provided input. - * @return A new report. - */ - public Report build() { - return new Report(type, exception, messageParameters, callerParameters); - } - } - - /** - * Construct a new report builder. - * @param type - the initial report type. - * @return Report builder. - */ - public static ReportBuilder newBuilder(ReportType type) { - return new ReportBuilder().type(type); - } - - /** - * Construct a new report with the given type and parameters. - * @param exception - exception that occured in the caller method. - * @param type - the report type that will be used to construct the message. - * @param messageParameters - parameters used to construct the report message. - * @param callerParameters - parameters from the caller method. - */ - protected Report(ReportType type, @Nullable Throwable exception, @Nullable Object[] messageParameters, @Nullable Object[] callerParameters) { - if (type == null) - throw new IllegalArgumentException("type cannot be NULL."); - this.type = type; - this.exception = exception; - this.messageParameters = messageParameters; - this.callerParameters = callerParameters; - } - - /** - * Format the current report type with the provided message parameters. - * @return The formated report message. - */ - public String getReportMessage() { - return type.getMessage(messageParameters); - } - - /** - * Retrieve the message parameters that will be used to construc the report message. - * 0; - } - - /** - * Determine if we have any caller parameters. - * @return TRUE if there are any caller parameters, FALSE otherwise. - */ - public boolean hasCallerParameters() { - return callerParameters != null && callerParameters.length > 0; - } -} +package com.comphenix.protocol.error; + +import javax.annotation.Nullable; + +/** + * Represents a error or warning report. + * + * @author Kristian + */ +public class Report { + private final ReportType type; + private final Throwable exception; + private final Object[] messageParameters; + private final Object[] callerParameters; + + /** + * Must be constructed through the factory method in Report. + */ + public static class ReportBuilder { + private ReportType type; + private Throwable exception; + private Object[] messageParameters; + private Object[] callerParameters; + + private ReportBuilder() { + // Don't allow + } + + /** + * Set the current report type. Cannot be NULL. + * @param type - report type. + * @return This builder, for chaining. + */ + public ReportBuilder type(ReportType type) { + if (type == null) + throw new IllegalArgumentException("Report type cannot be set to NULL."); + this.type = type; + return this; + } + + /** + * Set the current exception that occured. + * @param exception - exception that occured. + * @return This builder, for chaining. + */ + public ReportBuilder error(@Nullable Throwable exception) { + this.exception = exception; + return this; + } + + /** + * Set the message parameters that are used to construct a message text. + * @param messageParameters - parameters for the report type. + * @return This builder, for chaining. + */ + public ReportBuilder messageParam(@Nullable Object... messageParameters) { + this.messageParameters = messageParameters; + return this; + } + + /** + * Set the parameters in the caller method. This is optional. + * @param callerParameters - parameters of the caller method. + * @return This builder, for chaining. + */ + public ReportBuilder callerParam(@Nullable Object... callerParameters) { + this.callerParameters = callerParameters; + return this; + } + + /** + * Construct a new report with the provided input. + * @return A new report. + */ + public Report build() { + return new Report(type, exception, messageParameters, callerParameters); + } + } + + /** + * Construct a new report builder. + * @param type - the initial report type. + * @return Report builder. + */ + public static ReportBuilder newBuilder(ReportType type) { + return new ReportBuilder().type(type); + } + + /** + * Construct a new report with the given type and parameters. + * @param exception - exception that occured in the caller method. + * @param type - the report type that will be used to construct the message. + * @param messageParameters - parameters used to construct the report message. + * @param callerParameters - parameters from the caller method. + */ + protected Report(ReportType type, @Nullable Throwable exception, @Nullable Object[] messageParameters, @Nullable Object[] callerParameters) { + if (type == null) + throw new IllegalArgumentException("type cannot be NULL."); + this.type = type; + this.exception = exception; + this.messageParameters = messageParameters; + this.callerParameters = callerParameters; + } + + /** + * Format the current report type with the provided message parameters. + * @return The formated report message. + */ + public String getReportMessage() { + return type.getMessage(messageParameters); + } + + /** + * Retrieve the message parameters that will be used to construc the report message. + * 0; + } + + /** + * Determine if we have any caller parameters. + * @return TRUE if there are any caller parameters, FALSE otherwise. + */ + public boolean hasCallerParameters() { + return callerParameters != null && callerParameters.length > 0; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/error/RethrowErrorReporter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/error/RethrowErrorReporter.java new file mode 100644 index 00000000..2e9be463 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/error/RethrowErrorReporter.java @@ -0,0 +1,43 @@ +package com.comphenix.protocol.error; + +import org.bukkit.plugin.Plugin; + +import com.comphenix.protocol.error.Report.ReportBuilder; +import com.google.common.base.Joiner; + +/** + * Represents an error reporter that rethrows every exception instead. + * @author Kristian + */ +public class RethrowErrorReporter implements ErrorReporter { + @Override + public void reportMinimal(Plugin sender, String methodName, Throwable error) { + throw new RuntimeException("Minimal error by " + sender + " in " + methodName, error); + } + + @Override + public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) { + throw new RuntimeException( + "Minimal error by " + sender + " in " + methodName + " with " + Joiner.on(",").join(parameters), error); + } + + @Override + public void reportWarning(Object sender, ReportBuilder reportBuilder) { + reportWarning(sender, reportBuilder.build()); + } + + @Override + public void reportWarning(Object sender, Report report) { + throw new RuntimeException("Warning by " + sender + ": " + report); + } + + @Override + public void reportDetailed(Object sender, ReportBuilder reportBuilder) { + reportDetailed(sender, reportBuilder.build()); + } + + @Override + public void reportDetailed(Object sender, Report report) { + throw new RuntimeException("Detailed error " + sender + ": " + report, report.getException()); + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketConstructor.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketConstructor.java index 121dff89..7d7a3258 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketConstructor.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketConstructor.java @@ -21,6 +21,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.List; +import com.comphenix.protocol.error.RethrowErrorReporter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.injector.packet.PacketRegistry; import com.comphenix.protocol.reflect.FieldAccessException; @@ -51,15 +52,19 @@ public class PacketConstructor { // Used to unwrap Bukkit objects private List unwrappers; + // Parameters that need to be unwrapped + private boolean[] unwrappable; + private PacketConstructor(Constructor constructorMethod) { this.constructorMethod = constructorMethod; - this.unwrappers = Lists.newArrayList((Unwrapper) new BukkitUnwrapper()); + this.unwrappers = Lists.newArrayList((Unwrapper) new BukkitUnwrapper(new RethrowErrorReporter() )); } - private PacketConstructor(int packetID, Constructor constructorMethod, List unwrappers) { + private PacketConstructor(int packetID, Constructor constructorMethod, List unwrappers, boolean[] unwrappable) { this.packetID = packetID; this.constructorMethod = constructorMethod; this.unwrappers = unwrappers; + this.unwrappable = unwrappable; } public ImmutableList getUnwrappers() { @@ -80,31 +85,41 @@ public class PacketConstructor { * @return A constructor with a different set of unwrappers. */ public PacketConstructor withUnwrappers(List unwrappers) { - return new PacketConstructor(packetID, constructorMethod, unwrappers); + return new PacketConstructor(packetID, constructorMethod, unwrappers, unwrappable); } /** * Create a packet constructor that creates packets using the given types. + *

+ * Note that if you pass a Class as a value, it will use its type directly. * @param id - packet ID. - * @param values - types to create. + * @param values - the values that will match each parameter in the desired constructor. * @return A packet constructor with these types. * @throws IllegalArgumentException If no packet constructor could be created with these types. */ public PacketConstructor withPacket(int id, Object[] values) { - Class[] types = new Class[values.length]; + Throwable lastException = null; + boolean[] unwrappable = new boolean[values.length]; for (int i = 0; i < types.length; i++) { // Default type if (values[i] != null) { - types[i] = values[i].getClass(); + types[i] = (values[i] instanceof Class) ? (Class)values[i] : values[i].getClass(); for (Unwrapper unwrapper : unwrappers) { - Object result = unwrapper.unwrapItem(values[i]); + Object result = null; + + try { + result = unwrapper.unwrapItem(values[i]); + } catch (Throwable e) { + lastException = e; + } // Update type we're searching for if (result != null) { types[i] = result.getClass(); + unwrappable[i] = true; break; } } @@ -126,11 +141,11 @@ public class PacketConstructor { if (isCompatible(types, params)) { // Right, we've found our type - return new PacketConstructor(id, constructor, unwrappers); + return new PacketConstructor(id, constructor, unwrappers, unwrappable); } } - throw new IllegalArgumentException("No suitable constructor could be found."); + throw new IllegalArgumentException("No suitable constructor could be found.", lastException); } /** @@ -143,14 +158,16 @@ public class PacketConstructor { */ public PacketContainer createPacket(Object... values) throws FieldAccessException { try { - // Convert types + // Convert types that needs to be converted for (int i = 0; i < values.length; i++) { - for (Unwrapper unwrapper : unwrappers) { - Object converted = unwrapper.unwrapItem(values[i]); - - if (converted != null) { - values[i] = converted; - break; + if (unwrappable[i]) { + for (Unwrapper unwrapper : unwrappers) { + Object converted = unwrapper.unwrapItem(values[i]); + + if (converted != null) { + values[i] = converted; + break; + } } } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java index 7fc274d0..16aaf9e8 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java @@ -17,7 +17,11 @@ package com.comphenix.protocol.utility; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; @@ -26,8 +30,13 @@ import org.bukkit.entity.Player; import com.comphenix.protocol.Packets; import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.injector.PacketConstructor; +import com.comphenix.protocol.injector.packet.PacketRegistry; import com.comphenix.protocol.reflect.FieldAccessException; +import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers; +import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.google.common.base.Strings; +import com.google.common.collect.Iterables; /** * Utility methods for sending chat messages. @@ -35,11 +44,14 @@ import com.google.common.base.Strings; * @author Kristian */ public class ChatExtensions { - // Used to sent chat messages private PacketConstructor chatConstructor; private ProtocolManager manager; + // Whether or not we have to use the post-1.6.1 chat format + private static Constructor jsonConstructor = getJsonFormatConstructor(); + private static Method messageFactory; + public ChatExtensions(ProtocolManager manager) { this.manager = manager; } @@ -68,10 +80,59 @@ public class ChatExtensions { * Send a message without invoking the packet listeners. * @param player - the player to send it to. * @param message - the message to send. - * @return TRUE if the message was sent successfully, FALSE otherwise. * @throws InvocationTargetException If we were unable to send the message. */ private void sendMessageSilently(Player player, String message) throws InvocationTargetException { + if (jsonConstructor != null) { + sendMessageAsJson(player, message); + } else { + sendMessageAsString(player, message); + } + } + + /** + * Send a message using the new JSON format in 1.6.1. + * @param player - the player to send it to. + * @param message - the message to send. + * @throws InvocationTargetException InvocationTargetException If we were unable to send the message. + */ + private void sendMessageAsJson(Player player, String message) throws InvocationTargetException { + Object messageObject = null; + + if (chatConstructor == null) { + Class messageClass = jsonConstructor.getParameterTypes()[0]; + chatConstructor = manager.createPacketConstructor(Packets.Server.CHAT, messageClass); + + // Try one of the string constructors + messageFactory = FuzzyReflection.fromClass(messageClass).getMethod( + FuzzyMethodContract.newBuilder(). + requireModifier(Modifier.STATIC). + parameterCount(1). + parameterExactType(String.class). + returnTypeMatches(FuzzyMatchers.matchParent()). + build()); + } + + try { + messageObject = messageFactory.invoke(null, message); + } catch (Exception e) { + throw new InvocationTargetException(e); + } + + try { + manager.sendServerPacket(player, chatConstructor.createPacket(messageObject), false); + } catch (FieldAccessException e) { + throw new InvocationTargetException(e); + } + } + + /** + * Send a message as a pure string. + * @param player - the player. + * @param message - the message to send. + * @throws InvocationTargetException If anything went wrong. + */ + private void sendMessageAsString(Player player, String message) throws InvocationTargetException { if (chatConstructor == null) chatConstructor = manager.createPacketConstructor(Packets.Server.CHAT, message); @@ -143,4 +204,21 @@ public class ChatExtensions { } return current; } + + /** + * Retrieve a constructor for post-1.6.1 chat packets. + * @return A constructor for JSON-based packets. + */ + static Constructor getJsonFormatConstructor() { + Class chatPacket = PacketRegistry.getPacketClassFromID(3, true); + List> list = FuzzyReflection.fromClass(chatPacket).getConstructorList( + FuzzyMethodContract.newBuilder(). + parameterCount(1). + parameterMatches(MinecraftReflection.getMinecraftObjectMatcher()). + build() + ); + + // First element or NULL + return Iterables.getFirst(list, null); + } }