feat: added PacketLogger module

This commit is contained in:
əlemi 2023-03-14 22:33:19 +01:00
parent 924601fc90
commit c942c35263
Signed by: alemi
GPG key ID: A4895B84D311642C

View file

@ -0,0 +1,207 @@
package ftbsc.bscv.modules.network;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.google.auto.service.AutoService;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import ftbsc.bscv.Boscovicino;
import ftbsc.bscv.api.ILoadable;
import ftbsc.bscv.modules.QuickModule;
import ftbsc.bscv.patches.PacketPatch.PacketEvent;
import net.minecraft.network.IPacket;
import net.minecraft.util.math.vector.Vector2f;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@AutoService(ILoadable.class)
public class PacketLogger extends QuickModule {
public static final SimpleDateFormat CAPTURE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
@Override
public void enable() {
this.capture = new JsonArray();
super.enable();
}
@Override
public void disable() {
super.disable();
try {
this.store();
} catch (IOException e) {
e.printStackTrace();
Boscovicino.LOGGER.error("dumping capture into logs");
Boscovicino.LOGGER.error(this.capture.toString());
}
}
private JsonArray capture;
@SubscribeEvent
public void onPacketOutgoing(PacketEvent.Outgoing event) {
try {
this.capture.add(this.packet_to_json(event.packet, this.pretty_time())); // get send time here
} catch (IllegalAccessException e) {
Boscovicino.LOGGER.warn("Could not process fields of packet {}", event.packet.toString());
}
}
@SubscribeEvent
public void onPacketIncoming(PacketEvent.Incoming event) {
try {
this.capture.add(this.packet_to_json(event.packet, this.pretty_time())); // get recv time here
} catch (IllegalAccessException e) {
Boscovicino.LOGGER.warn("Could not process fields of packet {}", event.packet.toString());
}
}
private void store() throws IOException {
// TODO make sure this folder exists and don't check for it each time we save a capture
Path basePath = Paths.get("logs/packets/");
if (!Files.isDirectory(basePath)) {
Files.createDirectories(basePath);
}
String capturePath = String.format("logs/packets/%s.json", CAPTURE_DATE_FORMAT.format(new Date()));
try (Writer writer = new FileWriter(capturePath)) {
Gson gson = new GsonBuilder().create();
gson.toJson(this.capture, writer);
}
}
private JsonElement packet_to_json(IPacket<?> packet, double time) throws IllegalAccessException {
Class<?> clazz = packet.getClass();
List<String> classNames = new ArrayList<>();
JsonObject fields = new JsonObject();
while (clazz != null && !clazz.equals(Object.class)) {
classNames.add(clazz.getSimpleName());
for (Field field : clazz.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers())) {
field.setAccessible(true);
fields.add(field.getName(), this.format_value(field.get(packet))); // TODO deobfuscate field.getName()
}
}
clazz = clazz.getSuperclass();
}
JsonObject json = new JsonObject();
json.addProperty("time", time);
json.addProperty("name", this.compose_packet_name(classNames));
json.add("fields", fields);
return json;
}
private JsonElement format_value(Object value) {
if (value == null) return null;
if (value instanceof ITextComponent) {
ITextComponent component = (ITextComponent) value;
JsonObject obj = new JsonObject();
obj.add(value.getClass().getSimpleName(), ITextComponent.Serializer.toJsonTree(component));
return obj;
}
if (value.getClass().isArray()) {
return this.array_to_string(value);
}
if (value instanceof Number) {
return new JsonPrimitive((Number) value);
}
if (value instanceof Boolean) {
return new JsonPrimitive((Boolean) value);
}
if (value instanceof String) {
return new JsonPrimitive((String) value);
}
if (value instanceof Character) {
return new JsonPrimitive((Character) value);
}
if (value instanceof Vector3d) {
Vector3d vec = (Vector3d) value;
return this.array_to_string(new double[] { vec.x(), vec.y(), vec.z() });
}
if (value instanceof Vector2f) {
Vector2f vec = (Vector2f) value;
return this.array_to_string(new float[] { vec.x, vec.y });
}
// TODO pretty print some very noisy toStrings
return new JsonPrimitive(value.toString());
}
private JsonArray array_to_string(Object value) {
JsonArray out = new JsonArray();
if (value instanceof byte[]) {
byte[] arr = (byte[]) value;
for (byte b : arr) out.add(b);
return out;
}
if (value instanceof short[]) {
short[] arr = (short[]) value;
for (short s : arr) out.add(s);
return out;
}
if (value instanceof int[]) {
int[] arr = (int[]) value;
for (int s : arr) out.add(s);
return out;
}
if (value instanceof long[]) {
long[] arr = (long[]) value;
for (long s : arr) out.add(s);
return out;
}
if (value instanceof float[]) {
float[] arr = (float[]) value;
for (float s : arr) out.add(s);
return out;
}
if (value instanceof double[]) {
double[] arr = (double[]) value;
for (double s : arr) out.add(s);
return out;
}
if (value instanceof boolean[]) {
boolean[] arr = (boolean[]) value;
for (boolean s : arr) out.add(s);
return out;
}
if (value instanceof char[]) {
char[] arr = (char[]) value;
for (char s : arr) out.add(s);
return out;
}
Object[] arr = (Object[]) value;
for (Object o : arr) out.add(o.toString());
return out;
}
private String compose_packet_name(List<String> classNames) {
StringBuilder builder = new StringBuilder();
for (int i = classNames.size() - 1; i >= 0; i--) {
builder.append(classNames.get(i));
if (i > 0) builder.append(".");
}
return builder.toString();
}
private double pretty_time() {
return (double) System.nanoTime() / 1000000000.;
}
}