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:
parent
5efe3a2e35
commit
26a064fbe2
@ -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))
|
||||
}
|
@ -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")
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user