Skip to content

Instantly share code, notes, and snippets.

@aadnk
Created September 11, 2014 02:17
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save aadnk/8f21faa9276988530691 to your computer and use it in GitHub Desktop.
Save aadnk/8f21faa9276988530691 to your computer and use it in GitHub Desktop.
Change the "unknown command message" with TinyProtocol.
package com.comphenix.example;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
import org.json.simple.JSONObject;
public class ChangingUnknownCommand extends JavaPlugin implements Listener {
private MessageTransformer transformer;
@Override
public void onEnable() {
transformer = new MessageTransformer(this) {
@Override
protected Object onSendingChat(Player receiver, Object json) {
return transformPrimitives(json, new Processor() {
@Override
public Object process(Object value, Object parent) {
return processValue(value);
}
});
}
};
}
@SuppressWarnings("unchecked")
private Object processValue(Object value) {
if (value instanceof String) {
String stripped = ChatColor.stripColor((String) value);
if ("Unknown command. Type \"/help\" for help.".equals(stripped)) {
JSONObject result = new JSONObject();
// Add a color
result.put("color", "red");
result.put("text", "Wrong. Try again! Also note that we can apply a color to the " +
"entire multi-lined messages, unlike ChatColor.RED.");
return result;
}
}
return value;
}
@Override
public void onDisable() {
super.onDisable();
transformer.close();
}
}
package com.comphenix.example;
import net.minecraft.util.com.google.gson.Gson;
import net.minecraft.util.io.netty.channel.Channel;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import com.comphenix.example.Reflection.FieldAccessor;
import com.google.common.base.Objects;
public abstract class MessageTransformer {
// Explosion packet
private Class<?> chatClass = Reflection.getClass("{nms}.PacketPlayOutChat");
// For looking up the base component
private Class<Object> componentClass = Reflection.getUntypedClass("{nms}.IChatBaseComponent");
private FieldAccessor<Object> componentField = Reflection.getField(chatClass, componentClass, 0);
// Converting between the base component and JSON
private Class<?> serializerClass = Reflection.getClass("{nms}.ChatSerializer");
private Gson chatGson = Reflection.getField(serializerClass, Gson.class, 0).get(null);
// A thread-safe JSON parser
private ThreadLocal<JSONParser> parser = new ThreadLocal<JSONParser>() {
protected JSONParser initialValue() {
return new JSONParser();
}
};
// For processing JSON
public interface Processor {
public Object process(Object value, Object parent);
}
// Private instance
private TinyProtocol protocol;
/**
* Construct a new chat message transformer.
* @param plugin - the parent plugin.
*/
public MessageTransformer(Plugin plugin) {
protocol = new TinyProtocol(plugin) {
@Override
public Object onPacketOutAsync(Player reciever, Channel channel, Object packet) {
// Ignore other packets
if (chatClass.isInstance(packet)) {
processChatPacket(reciever, packet);
return packet;
}
return super.onPacketOutAsync(reciever, channel, packet);
}
};
}
/**
* Invoked when the server is sending a chat message to a player.
* @param receiver - the receiving player.
* @param json - the message that is being sent.
* @return The message to send.
*/
protected abstract Object onSendingChat(Player receiver, Object json);
/**
* Handle a chat packet.
* @param reciever - the receiving player.
* @param packet
*/
private void processChatPacket(Player reciever, Object packet) {
try {
// We get an IChatBaseComponent, and then convert it to JSON
Object componentValue = componentField.get(packet);
String componentJson = getJson(componentValue);
// Get the output JSON
Object data = parser.get().parse(componentJson);
String output = JSONValue.toJSONString(onSendingChat(reciever, data));
// Write back the changed string
if (!Objects.equal(componentJson, output)) {
componentField.set(packet, getComponent(output));
}
} catch (ParseException e) {
e.printStackTrace();
}
}
/**
* Retrieve the JSON representation of the given chat component.
* @param component - the chat component.
* @return The JSON.
*/
private String getJson(Object component) {
return chatGson.toJson(component);
}
/**
* Retrieve the chat component from the given JSON.
* @param json - the JSON.
* @return The chat component.
*/
private Object getComponent(String json) {
return chatGson.fromJson(json, componentClass);
}
/**
* Tranform an arbitrary JSON object, array or primitive value.
* @param value - the value.
* @param processor - the processor.
* @return The processed value.
*/
protected Object transformPrimitives(Object value, Processor processor) {
return transformPrimitives(value, null, processor);
}
/**
* Tranform an arbitrary JSON object, array or primitive value.
* @param value - the value.
* @param parent - the parent object or array.
* @param processor - the processor.
* @return The processed value.
*/
protected Object transformPrimitives(Object value, Object parent, Processor processor) {
// Check its type
if (value instanceof JSONObject) {
return transformPrimitives((JSONObject) value, processor);
} else if (value instanceof JSONArray) {
return transformPrimitives((JSONArray) value, processor);
} else {
return processor.process(value, parent);
}
}
/**
* Transform every key-value mapping in the given JSON object.
* @param source - the object.
* @param processor - the processor.
* @return The processed object.
*/
@SuppressWarnings("unchecked")
protected JSONObject transformPrimitives(JSONObject source, Processor processor) {
for (Object key : source.keySet().toArray()) {
Object value = source.get(key);
source.put(key, transformPrimitives(value, source, processor));
}
return source;
}
/**
* Transform every descendant in the given JSON array and all
* @param source - the array.
* @param processor - the processor.
* @return The processed array.
*/
@SuppressWarnings("unchecked")
protected JSONArray transformPrimitives(JSONArray source, Processor processor) {
for (int i = 0; i < source.size(); i++) {
Object value = source.get(i);
source.set(i, transformPrimitives(value, source, processor));
}
return source;
}
/**
* Close the current transformer.
*/
public void close() {
if (protocol != null) {
protocol.close();
}
}
}
@JuiciFruit
Copy link

chatClass labelled as explosion packet :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment