From e3b4505b56ea405c4e60e8b53a3daf31822b612f Mon Sep 17 00:00:00 2001 From: nea Date: Fri, 3 Nov 2023 18:05:59 +0100 Subject: First working version * Add general minecraft fabric mod skeleton * Add object formatter * Add command to test formatter on one packet --- .../com/walkerselby/wheelchair/client/Formatter.kt | 174 +++++++++++++++++++++ .../walkerselby/wheelchair/client/Wheelchair.kt | 45 ++++++ 2 files changed, 219 insertions(+) create mode 100644 src/main/kotlin/com/walkerselby/wheelchair/client/Formatter.kt create mode 100644 src/main/kotlin/com/walkerselby/wheelchair/client/Wheelchair.kt (limited to 'src/main/kotlin/com') diff --git a/src/main/kotlin/com/walkerselby/wheelchair/client/Formatter.kt b/src/main/kotlin/com/walkerselby/wheelchair/client/Formatter.kt new file mode 100644 index 0000000..c48d34c --- /dev/null +++ b/src/main/kotlin/com/walkerselby/wheelchair/client/Formatter.kt @@ -0,0 +1,174 @@ +package com.walkerselby.wheelchair.client + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap +import net.minecraft.item.ItemStack +import net.minecraft.network.packet.Packet +import net.minecraft.text.Text +import java.lang.reflect.Field +import java.lang.reflect.Modifier + +val Field.isStatic: Boolean + get() = (modifiers and Modifier.STATIC) != 0 + +interface Printer { + fun emitName(value: String) + fun emitValue(value: String) + fun beginObject(label: String): Printer + fun beginList(label: String): Printer +} + +class AccumulatingPrinter( + val indent: Int, + val stringBuilder: StringBuilder, + val isListBuilder: Boolean, +) : Printer { + + fun appendIndent() { + stringBuilder.append(" ".repeat(indent)) + } + + override fun emitName(value: String) { + if (isListBuilder) { + error("Cannot print name within a list") + } + appendIndent() + stringBuilder.append(value) + stringBuilder.append(": ") + } + + override fun emitValue(value: String) { + if (isListBuilder) + stringBuilder.append("* ") + else + stringBuilder.append("") + stringBuilder.append(value) + stringBuilder.append("\n") + } + + override fun beginObject(label: String): Printer { + if (isListBuilder) { + stringBuilder.append("* $label:\n") + } else { + stringBuilder.append("$label\n") + } + return AccumulatingPrinter(indent + 2, stringBuilder, false) + } + + override fun beginList(label: String): Printer { + if (isListBuilder) { + stringBuilder.append("* $label\n") + } else { + stringBuilder.append("$label:\n") + } + return AccumulatingPrinter(indent + 2, stringBuilder, true) + } + +} + +fun interface Formatter { + + fun prettyPrint(subject: T, printer: Printer) + + companion object { + private val formatters = mutableMapOf, Formatter<*>>() + private val dynamicFormatter = mutableListOf<(Class<*>) -> Formatter<*>?>() + private val processing = mutableSetOf>() + + fun putFormatter(clazz: Class, formatter: Formatter) { + formatters[clazz] = formatter + } + + fun putFormatter(function: (Class) -> Formatter?) { + dynamicFormatter.add(function as ((Class<*>) -> Formatter<*>)) + } + + init { + putFormatter( + java.lang.Integer.TYPE + ) { subject, printer -> printer.emitValue(subject.toString()) } + putFormatter( + java.lang.Integer::class.java + ) { subject, printer -> printer.emitValue(subject.toString()) } + putFormatter( + String::class.java + ) { subject, printer -> printer.emitValue(subject) } + putFormatter( + ItemStack::class.java + ) { subject, printer -> printer.emitValue("${subject.count}x${subject.name.string}§r") } + putFormatter { + if (Int2ObjectMap::class.java.isAssignableFrom(it)) { + return@putFormatter object : Formatter> { + override fun prettyPrint(subject: Int2ObjectMap<*>, printer: Printer) { + val child = printer.beginObject("Int2ObjectMap (length=${subject.size})") + subject.forEach { (k, v) -> + val valueFormatter = getPrettyPrinter(v.javaClass) + child.emitName(k.toString()) + valueFormatter.prettyPrint(v, child) + } + } + } + } + return@putFormatter null + } + } + + class LazyFormatter(val clazz: Class) : Formatter { + override fun prettyPrint(subject: T, printer: Printer) { + (formatters[clazz] as Formatter).prettyPrint(subject, printer) + } + } + + fun prettyPrintPacket(packet: Packet<*>): Text { + val sb = StringBuilder() + getPrettyPrinter(packet.javaClass).prettyPrint(packet, AccumulatingPrinter(0, sb, false)) + return Text.literal(sb.toString()) + } + + fun getPrettyPrinter(clazz: Class): Formatter { + if (clazz in processing) { + return LazyFormatter(clazz) + } + if (clazz.isEnum) { + return object : Formatter { + override fun prettyPrint(subject: T, printer: Printer) { + printer.emitValue(subject.toString()) + } + } + } + return formatters.getOrPut(clazz) { + try { + processing.add(clazz) + + val dynamic = dynamicFormatter.firstNotNullOfOrNull { dynamic -> dynamic(clazz) } + if (dynamic != null) { + return@getOrPut dynamic + } + + val fields = clazz.declaredFields + .filter { !it.isStatic } + .onEach { it.isAccessible = true } + .associateWith { getPrettyPrinter(it.type) } + val name = clazz.simpleName // TODO: remapping + object : Formatter { + override fun prettyPrint(subject: T, printer: Printer) { + val child = printer.beginObject(name) + fun printField(value: V, formatter: Formatter) { + formatter.prettyPrint(value, child) + } + for ((field, fieldFormatter) in fields) { + val value = field.get(subject) + child.emitName(field.name) // TODO: remapping + // We love `Nothing` being unable to be cast away + printField(value as Any, fieldFormatter as Formatter) + } + } + } + } finally { + processing.remove(clazz) + } + } as Formatter + } + + } +} + diff --git a/src/main/kotlin/com/walkerselby/wheelchair/client/Wheelchair.kt b/src/main/kotlin/com/walkerselby/wheelchair/client/Wheelchair.kt new file mode 100644 index 0000000..1970f5c --- /dev/null +++ b/src/main/kotlin/com/walkerselby/wheelchair/client/Wheelchair.kt @@ -0,0 +1,45 @@ +package com.walkerselby.wheelchair.client + +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps +import net.fabricmc.api.ClientModInitializer +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback +import net.minecraft.item.ItemStack +import net.minecraft.item.Items +import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket +import net.minecraft.screen.slot.SlotActionType +import net.minecraft.text.Text + + +class Wheelchair : ClientModInitializer { + override fun onInitializeClient() { + + ClientCommandRegistrationCallback.EVENT.register(ClientCommandRegistrationCallback { dispatcher, registryAccess -> + dispatcher.register( + literal("wheelchair") + .then( + literal("packet") + .executes { + val packet = ClickSlotC2SPacket( + 0, + 1, + 2, + 3, + SlotActionType.SWAP, + ItemStack(Items.IRON_HOE), + Int2ObjectMaps.emptyMap() + ) + try { + it.source.sendFeedback(Formatter.prettyPrintPacket(packet)) + } catch (e: Exception) { + e.printStackTrace() + it.source.sendFeedback(Text.literal("Excepted")) + } + return@executes 0 + } + ) + ) + }) + + } +} \ No newline at end of file -- cgit