diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 45a7304c4..efbf4442b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -31,6 +31,9 @@ import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistry import net.minecraftforge.registries.IForgeRegistry import ru.dbotthepony.mc.otm.core.math.Vector +import ru.dbotthepony.mc.otm.core.util.readInt +import java.io.InputStream +import java.io.OutputStream import java.lang.ref.Reference import java.math.BigInteger import java.util.Arrays @@ -169,6 +172,10 @@ fun FriendlyByteBuf.readItemType(): Item? { return ForgeRegistries.ITEMS.getValue(readInt()) } +fun InputStream.readItemType(): Item? { + return ForgeRegistries.ITEMS.getValue(readInt()) +} + operator fun > StateHolder<*, *>.get(property: Property): T { return getValue(property) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt index 13d79bef0..05f07044d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt @@ -262,3 +262,24 @@ fun FriendlyByteBuf.readBinaryComponent(): Component { fun FriendlyByteBuf.writeBinaryComponent(component: Component) { writeJson(Component.Serializer.toJsonTree(component)) } + +fun S.writeCollection(collection: Collection, writer: S.(V) -> Unit) { + writeVarIntLE(collection.size) + + for (value in collection) { + writer(this, value) + } +} + +fun > S.readCollection(reader: S.() -> V, factory: (Int) -> C): C { + val size = readVarIntLE() + val collection = factory.invoke(size) + + for (i in 0 until size) { + collection.add(reader(this)) + } + + return collection +} + +fun S.readCollection(reader: S.() -> V) = readCollection(reader, ::ArrayList) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt index 6367157d2..ffe05bf75 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt @@ -4,6 +4,10 @@ import net.minecraft.network.FriendlyByteBuf import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.readDecimal import ru.dbotthepony.mc.otm.core.math.writeDecimal +import ru.dbotthepony.mc.otm.core.util.readDouble +import ru.dbotthepony.mc.otm.core.util.writeDouble +import java.io.InputStream +import java.io.OutputStream interface IMatterValue : Comparable { val matter: Decimal @@ -51,6 +55,11 @@ fun FriendlyByteBuf.writeMatterValue(value: IMatterValue) { writeDouble(value.complexity) } +fun OutputStream.writeMatterValue(value: IMatterValue) { + writeDecimal(value.matter) + writeDouble(value.complexity) +} + fun FriendlyByteBuf.readMatterValue(): IMatterValue { val matter = readDecimal() val complexity = readDouble() @@ -62,6 +71,17 @@ fun FriendlyByteBuf.readMatterValue(): IMatterValue { } } +fun InputStream.readMatterValue(): IMatterValue { + val matter = readDecimal() + val complexity = readDouble() + + if (matter.isZero && complexity == 0.0) { + return IMatterValue.Companion + } else { + return MatterValue(matter, complexity) + } +} + data class MatterValue( override val matter: Decimal, override val complexity: Double diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt index 683c9953d..34056cfe8 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt @@ -10,6 +10,8 @@ import com.google.gson.JsonParseException import com.google.gson.JsonSyntaxException import com.mojang.brigadier.arguments.StringArgumentType import com.mojang.brigadier.context.CommandContext +import it.unimi.dsi.fastutil.io.FastByteArrayInputStream +import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream import it.unimi.dsi.fastutil.objects.Object2BooleanFunction import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap import it.unimi.dsi.fastutil.objects.Reference2BooleanFunction @@ -66,6 +68,7 @@ import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.filterNotNull +import ru.dbotthepony.mc.otm.core.getID import ru.dbotthepony.mc.otm.core.util.formatMatter import ru.dbotthepony.mc.otm.core.util.formatMatterFull import ru.dbotthepony.mc.otm.core.util.formatSiComponent @@ -78,19 +81,25 @@ 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.readCollection import ru.dbotthepony.mc.otm.core.util.writeBinaryComponent -import ru.dbotthepony.mc.otm.core.writeItemType +import ru.dbotthepony.mc.otm.core.util.writeCollection import ru.dbotthepony.mc.otm.milliTime import ru.dbotthepony.mc.otm.network.MatteryPacket import ru.dbotthepony.mc.otm.network.RegistryNetworkChannel import ru.dbotthepony.mc.otm.registry.RegistryDelegate import ru.dbotthepony.mc.otm.storage.ItemStackWrapper +import java.io.DataInputStream +import java.io.DataOutputStream import java.io.File +import java.io.OutputStream import java.math.BigInteger import java.util.* import java.util.function.BooleanSupplier import java.util.function.Supplier import java.util.stream.Stream +import java.util.zip.Deflater +import java.util.zip.Inflater import kotlin.ConcurrentModificationException import kotlin.collections.ArrayDeque import kotlin.collections.ArrayList @@ -1399,12 +1408,70 @@ object MatterManager { val time = SystemTime() - val result = SyncPacket( - buff.readMap(FriendlyByteBuf::readItemType, FriendlyByteBuf::readMatterValue), - buff.readMap(FriendlyByteBuf::readItemType) { self -> self.readCollection(::ArrayList, FriendlyByteBuf::readBinaryComponent) } - ) + val bbytes = ByteArray(buff.readableBytes()) + buff.readBytes(bbytes) - LOGGER.debug("Reading matter registry packet took ${time.millis}ms") + val chunks = ArrayList() + var size = 0 + val inflater = Inflater() + inflater.setInput(bbytes) + + while (!inflater.finished()) { + val chunk = ByteArray(4096) + val inflated = inflater.inflate(chunk) + + if (inflated == 0) { + break + } else { + size += inflated + + if (size >= 1 shl 24 /* 16 MiB */) { + throw IndexOutOfBoundsException("Pipe Bomb") + } + + if (inflated == 4096) { + chunks.add(chunk) + } else { + chunks.add(chunk.copyOfRange(0, inflated)) + } + } + } + + val spliced = ByteArray(size) + var pointer = 0 + + for (chunk in chunks) { + for (i in chunk.indices) { + spliced[pointer++] = chunk[i] + } + } + + val stream = FastByteArrayInputStream(spliced) + val data = DataInputStream(stream) + + val valuesSize = data.readInt() + val values = Reference2ObjectOpenHashMap(valuesSize) + val comments = Reference2ObjectOpenHashMap>() + + for (i in 0 until valuesSize) { + val type = data.readItemType() + check(values.put(type, data.readMatterValue()) == null) { "Duplicate item type $type" } + + if (data.read() > 0) { + comments[type] = data.readCollection { readBinaryComponent()!! } + } + } + + val commentsSize = data.readInt() + + for (i in 0 until commentsSize) { + val type = data.readItemType() + check(comments.put(type, data.readCollection { readBinaryComponent()!! }) == null) { "Duplicate commentary item type $type" } + } + + val result = SyncPacket(values, comments) + + LOGGER.debug("Reading matter registry packet took ${time.millis}ms (${bbytes.size} bytes compressed, $size bytes total)") return result } @@ -1415,9 +1482,54 @@ object MatterManager { ) : MatteryPacket { 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::writeBinaryComponent) } - LOGGER.debug("Encoding matter registry packet took ${time.millis}ms, written total ${buff.writerIndex() - 1} bytes") + + val stream = FastByteArrayOutputStream() + val data = DataOutputStream(stream) + + var commentsSize = comments.size + data.writeInt(values.size) + + for ((k, v) in values) { + data.writeInt(ForgeRegistries.ITEMS.getID(k)) + data.writeMatterValue(v) + + val comment = comments[k] + + if (comment != null) { + commentsSize-- + data.write(1) + data.writeCollection(comment, OutputStream::writeBinaryComponent) + } else { + data.write(0) + } + } + + data.writeInt(commentsSize) + + for ((k, v) in comments) { + if (!values.containsKey(k)) { + data.writeInt(ForgeRegistries.ITEMS.getID(k)) + data.writeCollection(v, OutputStream::writeBinaryComponent) + } + } + + val deflater = Deflater(5) + deflater.setInput(stream.array, 0, stream.length) + deflater.finish() + + val bytes = ByteArray(4096) + + while (true) { + val written = deflater.deflate(bytes) + + if (written == 0) { + break + } else { + buff.writeBytes(bytes, 0, written) + } + } + + LOGGER.debug("Encoding matter registry packet took ${time.millis}ms, (${stream.length} bytes total, ${buff.writerIndex() - 1} bytes compressed)") } override fun play(context: Supplier) { @@ -1432,7 +1544,7 @@ object MatterManager { commentary.clear() for ((k, v) in comments) { - commentary[k] = ArrayList(v.size).also { it.addAll(v) } + commentary[k] = ArrayList(v) } LOGGER.debug("Updating matter registry from packet took ${time.millis}ms")