summaryrefslogtreecommitdiff
path: root/src/main/kotlin/com/walkerselby/wheelchair
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/com/walkerselby/wheelchair')
-rw-r--r--src/main/kotlin/com/walkerselby/wheelchair/client/Formatter.kt174
-rw-r--r--src/main/kotlin/com/walkerselby/wheelchair/client/Wheelchair.kt45
2 files changed, 219 insertions, 0 deletions
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<T> {
+
+ fun prettyPrint(subject: T, printer: Printer)
+
+ companion object {
+ private val formatters = mutableMapOf<Class<*>, Formatter<*>>()
+ private val dynamicFormatter = mutableListOf<(Class<*>) -> Formatter<*>?>()
+ private val processing = mutableSetOf<Class<*>>()
+
+ fun <V> putFormatter(clazz: Class<V>, formatter: Formatter<V>) {
+ formatters[clazz] = formatter
+ }
+
+ fun <V> putFormatter(function: (Class<in V>) -> Formatter<V>?) {
+ 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<Int2ObjectMap<*>> {
+ 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<T>(val clazz: Class<T>) : Formatter<T> {
+ override fun prettyPrint(subject: T, printer: Printer) {
+ (formatters[clazz] as Formatter<T>).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 <T> getPrettyPrinter(clazz: Class<T>): Formatter<T> {
+ if (clazz in processing) {
+ return LazyFormatter(clazz)
+ }
+ if (clazz.isEnum) {
+ return object : Formatter<T> {
+ 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<T> {
+ override fun prettyPrint(subject: T, printer: Printer) {
+ val child = printer.beginObject(name)
+ fun <V> printField(value: V, formatter: Formatter<V>) {
+ 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<Any>)
+ }
+ }
+ }
+ } finally {
+ processing.remove(clazz)
+ }
+ } as Formatter<T>
+ }
+
+ }
+}
+
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