Payload may not be larger than 1048576 bytes

# Conflicts:
#	src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt
This commit is contained in:
DBotThePony 2023-03-03 08:05:41 +07:00
parent 5efe3a2e35
commit 26a064fbe2
Signed by: DBot
GPG Key ID: DCC23B5715498507
2 changed files with 268 additions and 2 deletions

View File

@ -0,0 +1,264 @@
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 io.netty.buffer.ByteBufInputStream
import io.netty.buffer.ByteBufOutputStream
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
import net.minecraftforge.registries.ForgeRegistries
import net.minecraftforge.registries.ForgeRegistry
import java.io.*
import java.math.BigDecimal
import java.math.BigInteger
import kotlin.math.absoluteValue
// But seriously, Mojang, why would you need to derive from ByteBuf directly, when you can implement
// your own InputStream and OutputStream, since ByteBuf is meant to be operated on most time like a stream anyway?
// netty ByteBuf -> netty ByteBufInputStream -> Minecraft FriendlyInputStream
fun OutputStream.writeNbt(value: CompoundTag) {
try {
NbtIo.write(value, if (this is DataOutputStream) this else DataOutputStream(this))
} catch (ioexception: IOException) {
throw EncoderException(ioexception)
}
}
fun InputStream.readNbt(accounter: NbtAccounter = NbtAccounter(1L shl 18 /* 256 KiB */)): CompoundTag {
return try {
NbtIo.read(if (this is DataInputStream) this else DataInputStream(this), accounter)
} catch (ioexception: IOException) {
throw EncoderException(ioexception)
}
}
fun OutputStream.writeItem(itemStack: ItemStack, limitedTag: Boolean = true) {
if (itemStack.isEmpty) {
write(0)
} else {
write(1)
val id = (ForgeRegistries.ITEMS as ForgeRegistry<Item>).getID(itemStack.item)
writeInt(id)
writeInt(itemStack.count)
var compoundtag: CompoundTag? = null
if (itemStack.item.isDamageable(itemStack) || itemStack.item.shouldOverrideMultiplayerNbt()) {
compoundtag = if (limitedTag) itemStack.shareTag else itemStack.tag
}
write(if (compoundtag != null) 1 else 0)
if (compoundtag != null) {
writeNbt(compoundtag)
}
}
}
fun InputStream.readItem(sizeLimit: NbtAccounter = NbtAccounter(1L shl 18 /* 256 KiB */)): ItemStack {
sizeLimit.accountBytes(1L)
if (read() == 0) {
return ItemStack.EMPTY
}
sizeLimit.accountBytes(9L)
val item = (ForgeRegistries.ITEMS as ForgeRegistry<Item>).getValue(readInt())
val itemStack = ItemStack(item, readInt())
if (read() != 0) {
itemStack.readShareTag(readNbt(sizeLimit))
}
return itemStack
}
fun OutputStream.writeBigDecimal(value: BigDecimal) {
writeInt(value.scale())
val bytes = value.unscaledValue().toByteArray()
writeVarIntLE(bytes.size)
write(bytes)
}
fun InputStream.readBigDecimal(sizeLimit: NbtAccounter = NbtAccounter(512L)): BigDecimal {
val scale = readInt()
val size = readVarIntLE(sizeLimit)
require(size >= 0) { "Negative payload size: $size" }
sizeLimit.accountBytes(size.toLong() + 4L)
val bytes = ByteArray(size)
read(bytes)
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
private fun fixSignedInt(read: Long): Long {
val sign = read and 0x1L
@Suppress("name_shadowing")
val read = read ushr 1
if (sign == 1L) {
return -read - 1L
} else {
return read
}
}
/**
* 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.writeJson(element: JsonElement) {
if (element is JsonObject) {
write(TYPE_OBJECT)
writeVarIntLE(element.size())
for ((k, v) in element.entrySet()) {
writeBinaryString(k)
writeJson(v)
}
} else if (element is JsonArray) {
write(TYPE_ARRAY)
writeVarIntLE(element.size())
for (v in element) {
writeJson(v)
}
} else if (element is JsonPrimitive) {
if (element.isNumber) {
val num = element.asNumber
if (num is Int || num is Long) {
write(TYPE_INT)
var int = num.toLong()
if (int < 0) {
int = int.absoluteValue.shl(1).or(1)
} else {
int.shl(1)
}
writeVarLongLE(int)
} 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 {
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.readJson(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(fixSignedInt(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(readJson(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, readJson(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(readJson())
}
fun OutputStream.writeBinaryComponent(component: Component) {
writeJson(Component.Serializer.toJsonTree(component))
}
fun FriendlyByteBuf.readJson(sizeLimit: NbtAccounter = NbtAccounter(1L shl 18)): JsonElement {
return ByteBufInputStream(this).readJson(sizeLimit)
}
fun FriendlyByteBuf.writeJson(value: JsonElement) {
ByteBufOutputStream(this).writeJson(value)
}
fun FriendlyByteBuf.readBinaryComponent(): Component {
return Component.Serializer.fromJson(readJson()) ?: throw NullPointerException("Received null component")
}
fun FriendlyByteBuf.writeBinaryComponent(component: Component) {
writeJson(Component.Serializer.toJsonTree(component))
}

View File

@ -77,6 +77,8 @@ import ru.dbotthepony.mc.otm.core.orNull
import ru.dbotthepony.mc.otm.core.readItemType
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.stream
import ru.dbotthepony.mc.otm.core.util.readBinaryComponent
import ru.dbotthepony.mc.otm.core.util.writeBinaryComponent
import ru.dbotthepony.mc.otm.core.writeItemType
import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.network.RegistryNetworkChannel
@ -1397,7 +1399,7 @@ object MatterManager {
val result = SyncPacket(
buff.readMap(FriendlyByteBuf::readItemType, FriendlyByteBuf::readMatterValue),
buff.readMap(FriendlyByteBuf::readItemType) { self -> self.readCollection(::ArrayList, FriendlyByteBuf::readComponent) }
buff.readMap(FriendlyByteBuf::readItemType) { self -> self.readCollection(::ArrayList, FriendlyByteBuf::readBinaryComponent) }
)
LOGGER.debug("Reading matter registry packet took ${time.millis}ms")
@ -1412,7 +1414,7 @@ object MatterManager {
override fun write(buff: FriendlyByteBuf) {
val time = SystemTime()
buff.writeMap(values, FriendlyByteBuf::writeItemType, FriendlyByteBuf::writeMatterValue)
buff.writeMap(comments, FriendlyByteBuf::writeItemType) { self, value -> self.writeCollection(value, FriendlyByteBuf::writeComponent) }
buff.writeMap(comments, FriendlyByteBuf::writeItemType) { self, value -> self.writeCollection(value, FriendlyByteBuf::writeBinaryComponent) }
LOGGER.debug("Encoding matter registry packet took ${time.millis}ms, written total ${buff.writerIndex() - 1} bytes")
}