aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author alemi <me@alemi.dev>2023-03-14 22:33:19 +0100
committer alemi <me@alemi.dev>2023-03-14 22:33:19 +0100
commitc942c352639fc53b00741345f44ebb18ce0a4cc8 (patch)
tree06f1733f253b2194d8ac161be5bc719882dbbae2
parent924601fc906ce3072db21b0abee8fb9ccfeea31d (diff)
feat: added PacketLogger module
-rw-r--r--src/main/java/ftbsc/bscv/modules/network/PacketLogger.java207
1 files changed, 207 insertions, 0 deletions
diff --git a/src/main/java/ftbsc/bscv/modules/network/PacketLogger.java b/src/main/java/ftbsc/bscv/modules/network/PacketLogger.java
new file mode 100644
index 0000000..5709b75
--- /dev/null
+++ b/src/main/java/ftbsc/bscv/modules/network/PacketLogger.java
@@ -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.;
+ }
+}