Split FriendlyStreams

This commit is contained in:
DBotThePony 2023-07-26 11:30:49 +07:00
parent ea42b19e21
commit 03403eb54d
Signed by: DBot
GPG Key ID: DCC23B5715498507
3 changed files with 172 additions and 161 deletions

View File

@ -0,0 +1,129 @@
package ru.dbotthepony.mc.otm.core.util
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonNull
import com.google.gson.JsonObject
import com.google.gson.JsonParseException
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSyntaxException
import net.minecraft.nbt.NbtAccounter
import java.io.InputStream
import java.io.OutputStream
private const val TYPE_NULL = 0x01
private const val TYPE_DOUBLE = 0x02
private const val TYPE_BOOLEAN = 0x03
private const val TYPE_INT = 0x04
private const val TYPE_STRING = 0x05
private const val TYPE_ARRAY = 0x06
private const val TYPE_OBJECT = 0x07
/**
* Writes binary json to stream in Starbound Object Notation format
*
* just copy pasted this code from my another project because i was lazy
*/
fun OutputStream.writeBinaryJson(element: JsonElement) {
if (element is JsonObject) {
write(TYPE_OBJECT)
writeVarIntLE(element.size())
for ((k, v) in element.entrySet()) {
writeBinaryString(k)
writeBinaryJson(v)
}
} else if (element is JsonArray) {
write(TYPE_ARRAY)
writeVarIntLE(element.size())
for (v in element) {
writeBinaryJson(v)
}
} else if (element is JsonPrimitive) {
if (element.isNumber) {
val num = element.asNumber
if (num is Int || num is Long || num is Short || num is Byte) {
write(TYPE_INT)
writeVarLongLE(num.toLong())
} else if (num is Float || num is Double) {
write(TYPE_DOUBLE)
writeDouble(num.toDouble())
} else {
throw IllegalArgumentException("Unknown number type: ${num::class.qualifiedName}")
}
} else if (element.isString) {
write(TYPE_STRING)
writeBinaryString(element.asString)
} else if (element.isBoolean) {
write(TYPE_BOOLEAN)
write(if (element.asBoolean) 1 else 0)
} else {
write(TYPE_NULL)
}
} else if (element is JsonNull) {
write(TYPE_NULL)
} else {
throw IllegalArgumentException("Unknown element type: ${element::class.qualifiedName}")
}
}
/**
* Reads binary json from stream in Starbound Object Notation format
*
* just copy pasted this code from my another project because i was lazy
*/
fun InputStream.readBinaryJson(sizeLimit: NbtAccounter = NbtAccounter(1L shl 18 /* 256 KiB */)): JsonElement {
sizeLimit.accountBytes(1L)
return when (val id = read()) {
TYPE_NULL -> JsonNull.INSTANCE
TYPE_DOUBLE -> {
sizeLimit.accountBytes(8L)
JsonPrimitive(readDouble())
}
TYPE_BOOLEAN -> {
sizeLimit.accountBytes(1L)
JsonPrimitive(read() > 1)
}
TYPE_INT -> JsonPrimitive(readVarLongLE(sizeLimit))
TYPE_STRING -> JsonPrimitive(readBinaryString(sizeLimit))
TYPE_ARRAY -> {
val values = readVarIntLE(sizeLimit)
if (values == 0) return JsonArray()
if (values < 0) throw JsonSyntaxException("Tried to read json array with $values elements in it")
val build = JsonArray(values)
for (i in 0 until values) build.add(readBinaryJson(sizeLimit))
return build
}
TYPE_OBJECT -> {
val values = readVarIntLE(sizeLimit)
if (values == 0) return JsonObject()
if (values < 0) throw JsonSyntaxException("Tried to read json object with $values elements in it")
val build = JsonObject()
for (i in 0 until values) {
val key: String
try {
key = readBinaryString(sizeLimit)
} catch(err: Throwable) {
throw JsonSyntaxException("Reading json object at $i", err)
}
try {
build.add(key, readBinaryJson(sizeLimit))
} catch(err: Throwable) {
throw JsonSyntaxException("Reading json object at $i with name $key", err)
}
}
return build
}
else -> throw JsonParseException("Unknown element type $id")
}
}

View File

@ -0,0 +1,43 @@
package ru.dbotthepony.mc.otm.core.util
import com.google.gson.JsonElement
import com.mojang.serialization.Codec
import com.mojang.serialization.JsonOps
import io.netty.buffer.ByteBufInputStream
import io.netty.buffer.ByteBufOutputStream
import io.netty.handler.codec.DecoderException
import io.netty.handler.codec.EncoderException
import net.minecraft.nbt.NbtAccounter
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
fun FriendlyByteBuf.readBinaryJson(sizeLimit: NbtAccounter = NbtAccounter(1L shl 18)): JsonElement {
return ByteBufInputStream(this).readBinaryJson(sizeLimit)
}
fun FriendlyByteBuf.writeBinaryJson(value: JsonElement) {
ByteBufOutputStream(this).writeBinaryJson(value)
}
fun <S> FriendlyByteBuf.writeBinaryJsonWithCodec(codec: Codec<S>, value: S) {
writeBinaryJson(codec.encode(value, JsonOps.INSTANCE, JsonOps.INSTANCE.empty())
.get().map({ it }, { throw EncoderException("Failed to encode input data: ${it.message()}") }))
}
fun <S> FriendlyByteBuf.readBinaryJsonWithCodec(codec: Codec<S>, sizeLimit: NbtAccounter = NbtAccounter(1L shl 18)): S {
return codec.decode(JsonOps.INSTANCE, readBinaryJson(sizeLimit))
.get().map({ it.first }, { throw DecoderException("Failed to decode data from network: ${it.message()}") })
}
fun FriendlyByteBuf.readBinaryComponent(): Component {
return Component.Serializer.fromJson(readBinaryJson()) ?: throw NullPointerException("Received null component")
}
fun FriendlyByteBuf.writeBinaryComponent(component: Component) {
writeBinaryJson(Component.Serializer.toJsonTree(component))
}
// обратный порядок аргументов у лямбда выражения
fun <T> FriendlyByteBuf.writeCollection(collection: Collection<T>, writer: (T, FriendlyByteBuf) -> Unit) {
writeCollection(collection) { a, b -> writer.invoke(b, a) }
}

View File

@ -1,22 +1,9 @@
package ru.dbotthepony.mc.otm.core.util
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonNull
import com.google.gson.JsonObject
import com.google.gson.JsonParseException
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSyntaxException
import com.mojang.serialization.Codec
import com.mojang.serialization.JsonOps
import io.netty.buffer.ByteBufInputStream
import io.netty.buffer.ByteBufOutputStream
import io.netty.handler.codec.DecoderException
import io.netty.handler.codec.EncoderException
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtAccounter
import net.minecraft.nbt.NbtIo
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
@ -143,123 +130,6 @@ fun InputStream.readBigDecimal(sizeLimit: NbtAccounter = NbtAccounter(512L)): Bi
return BigDecimal(BigInteger(bytes), scale)
}
private const val TYPE_NULL = 0x01
private const val TYPE_DOUBLE = 0x02
private const val TYPE_BOOLEAN = 0x03
private const val TYPE_INT = 0x04
private const val TYPE_STRING = 0x05
private const val TYPE_ARRAY = 0x06
private const val TYPE_OBJECT = 0x07
/**
* Writes binary json to stream in Starbound Object Notation format
*
* just copy pasted this code from my another project because i was lazy
*/
fun OutputStream.writeBinaryJson(element: JsonElement) {
if (element is JsonObject) {
write(TYPE_OBJECT)
writeVarIntLE(element.size())
for ((k, v) in element.entrySet()) {
writeBinaryString(k)
writeBinaryJson(v)
}
} else if (element is JsonArray) {
write(TYPE_ARRAY)
writeVarIntLE(element.size())
for (v in element) {
writeBinaryJson(v)
}
} else if (element is JsonPrimitive) {
if (element.isNumber) {
val num = element.asNumber
if (num is Int || num is Long || num is Short || num is Byte) {
write(TYPE_INT)
writeVarLongLE(num.toLong())
} else if (num is Float || num is Double) {
write(TYPE_DOUBLE)
writeDouble(num.toDouble())
} else {
throw IllegalArgumentException("Unknown number type: ${num::class.qualifiedName}")
}
} else if (element.isString) {
write(TYPE_STRING)
writeBinaryString(element.asString)
} else if (element.isBoolean) {
write(TYPE_BOOLEAN)
write(if (element.asBoolean) 1 else 0)
} else {
write(TYPE_NULL)
}
} else if (element is JsonNull) {
write(TYPE_NULL)
} else {
throw IllegalArgumentException("Unknown element type: ${element::class.qualifiedName}")
}
}
/**
* Reads binary json from stream in Starbound Object Notation format
*
* just copy pasted this code from my another project because i was lazy
*/
fun InputStream.readBinaryJson(sizeLimit: NbtAccounter = NbtAccounter(1L shl 18 /* 256 KiB */)): JsonElement {
sizeLimit.accountBytes(1L)
return when (val id = read()) {
TYPE_NULL -> JsonNull.INSTANCE
TYPE_DOUBLE -> {
sizeLimit.accountBytes(8L)
JsonPrimitive(readDouble())
}
TYPE_BOOLEAN -> {
sizeLimit.accountBytes(1L)
JsonPrimitive(read() > 1)
}
TYPE_INT -> JsonPrimitive(readVarLongLE(sizeLimit))
TYPE_STRING -> JsonPrimitive(readBinaryString(sizeLimit))
TYPE_ARRAY -> {
val values = readVarIntLE(sizeLimit)
if (values == 0) return JsonArray()
if (values < 0) throw JsonSyntaxException("Tried to read json array with $values elements in it")
val build = JsonArray(values)
for (i in 0 until values) build.add(readBinaryJson(sizeLimit))
return build
}
TYPE_OBJECT -> {
val values = readVarIntLE(sizeLimit)
if (values == 0) return JsonObject()
if (values < 0) throw JsonSyntaxException("Tried to read json object with $values elements in it")
val build = JsonObject()
for (i in 0 until values) {
val key: String
try {
key = readBinaryString(sizeLimit)
} catch(err: Throwable) {
throw JsonSyntaxException("Reading json object at $i", err)
}
try {
build.add(key, readBinaryJson(sizeLimit))
} catch(err: Throwable) {
throw JsonSyntaxException("Reading json object at $i with name $key", err)
}
}
return build
}
else -> throw JsonParseException("Unknown element type $id")
}
}
fun InputStream.readBinaryComponent(): Component? {
return Component.Serializer.fromJson(readBinaryJson())
}
@ -268,37 +138,6 @@ fun OutputStream.writeBinaryComponent(component: Component) {
writeBinaryJson(Component.Serializer.toJsonTree(component))
}
fun FriendlyByteBuf.readBinaryJson(sizeLimit: NbtAccounter = NbtAccounter(1L shl 18)): JsonElement {
return ByteBufInputStream(this).readBinaryJson(sizeLimit)
}
fun FriendlyByteBuf.writeBinaryJson(value: JsonElement) {
ByteBufOutputStream(this).writeBinaryJson(value)
}
fun <S> FriendlyByteBuf.writeBinaryJsonWithCodec(codec: Codec<S>, value: S) {
writeBinaryJson(codec.encode(value, JsonOps.INSTANCE, JsonOps.INSTANCE.empty())
.get().map({ it }, { throw EncoderException("Failed to encode input data: ${it.message()}") }))
}
fun <S> FriendlyByteBuf.readBinaryJsonWithCodec(codec: Codec<S>, sizeLimit: NbtAccounter = NbtAccounter(1L shl 18)): S {
return codec.decode(JsonOps.INSTANCE, readBinaryJson(sizeLimit))
.get().map({ it.first }, { throw DecoderException("Failed to decode data from network: ${it.message()}") })
}
fun FriendlyByteBuf.readBinaryComponent(): Component {
return Component.Serializer.fromJson(readBinaryJson()) ?: throw NullPointerException("Received null component")
}
fun FriendlyByteBuf.writeBinaryComponent(component: Component) {
writeBinaryJson(Component.Serializer.toJsonTree(component))
}
// обратный порядок аргументов у лямбда выражения
fun <T> FriendlyByteBuf.writeCollection(collection: Collection<T>, writer: (T, FriendlyByteBuf) -> Unit) {
writeCollection(collection) { a, b -> writer.invoke(b, a) }
}
fun <S : OutputStream, V> S.writeCollection(collection: Collection<V>, writer: S.(V) -> Unit) {
writeVarIntLE(collection.size)