Field Synchronizer
This commit is contained in:
parent
58bd62d1a2
commit
ab0c5e4cc3
@ -1,32 +1,83 @@
|
|||||||
package ru.dbotthepony.mc.otm
|
package ru.dbotthepony.mc.otm
|
||||||
|
|
||||||
import io.netty.buffer.ByteBufInputStream
|
|
||||||
import io.netty.handler.codec.EncoderException
|
import io.netty.handler.codec.EncoderException
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.nbt.NbtAccounter
|
import net.minecraft.nbt.NbtAccounter
|
||||||
import net.minecraft.nbt.NbtIo
|
import net.minecraft.nbt.NbtIo
|
||||||
import java.io.DataInputStream
|
import net.minecraft.world.item.Item
|
||||||
import java.io.DataOutputStream
|
import net.minecraft.world.item.ItemStack
|
||||||
import java.io.IOException
|
import net.minecraftforge.registries.ForgeRegistries
|
||||||
import java.io.InputStream
|
import net.minecraftforge.registries.ForgeRegistry
|
||||||
import java.io.OutputStream
|
import java.io.*
|
||||||
|
|
||||||
// But seriously, Mojang, why would you need to derive from ByteBuf directly, when you can implement
|
// 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?
|
// 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
|
// netty ByteBuf -> netty ByteBufInputStream -> Minecraft FriendlyInputStream
|
||||||
|
|
||||||
fun <T : OutputStream> T.writeNbt(value: CompoundTag): T {
|
fun OutputStream.writeInt(value: Int) {
|
||||||
|
if (this is DataOutputStream) {
|
||||||
|
writeInt(value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
write(value ushr 24)
|
||||||
|
write(value ushr 16)
|
||||||
|
write(value ushr 8)
|
||||||
|
write(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun InputStream.readInt(): Int {
|
||||||
|
if (this is DataInputStream) {
|
||||||
|
return readInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (read() shl 24) or (read() shl 16) or (read() shl 8) or read()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OutputStream.writeLong(value: Long) {
|
||||||
|
if (this is DataOutputStream) {
|
||||||
|
writeLong(value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
write((value ushr 48).toInt())
|
||||||
|
write((value ushr 40).toInt())
|
||||||
|
write((value ushr 32).toInt())
|
||||||
|
write((value ushr 24).toInt())
|
||||||
|
write((value ushr 16).toInt())
|
||||||
|
write((value ushr 8).toInt())
|
||||||
|
write(value.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun InputStream.readLong(): Long {
|
||||||
|
if (this is DataInputStream) {
|
||||||
|
return readLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (read().toLong() shl 48) or
|
||||||
|
(read().toLong() shl 40) or
|
||||||
|
(read().toLong() shl 32) or
|
||||||
|
(read().toLong() shl 24) or
|
||||||
|
(read().toLong() shl 16) or
|
||||||
|
(read().toLong() shl 8) or
|
||||||
|
read().toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OutputStream.writeFloat(value: Float) = writeInt(value.toBits())
|
||||||
|
fun InputStream.readFloat() = Float.fromBits(readInt())
|
||||||
|
fun OutputStream.writeDouble(value: Double) = writeLong(value.toBits())
|
||||||
|
fun InputStream.readDouble() = Double.fromBits(readLong())
|
||||||
|
|
||||||
|
fun OutputStream.writeNbt(value: CompoundTag) {
|
||||||
try {
|
try {
|
||||||
NbtIo.write(value, if (this is DataOutputStream) this else DataOutputStream(this))
|
NbtIo.write(value, if (this is DataOutputStream) this else DataOutputStream(this))
|
||||||
} catch (ioexception: IOException) {
|
} catch (ioexception: IOException) {
|
||||||
throw EncoderException(ioexception)
|
throw EncoderException(ioexception)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : InputStream> T.readNbt(accounter: NbtAccounter = NbtAccounter.UNLIMITED): CompoundTag {
|
fun InputStream.readNbt(accounter: NbtAccounter = NbtAccounter.UNLIMITED): CompoundTag {
|
||||||
return try {
|
return try {
|
||||||
NbtIo.read(if (this is DataInputStream) this else DataInputStream(this), accounter)
|
NbtIo.read(if (this is DataInputStream) this else DataInputStream(this), accounter)
|
||||||
} catch (ioexception: IOException) {
|
} catch (ioexception: IOException) {
|
||||||
@ -34,4 +85,82 @@ fun <T : InputStream> T.readNbt(accounter: NbtAccounter = NbtAccounter.UNLIMITED
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(): ItemStack {
|
||||||
|
if (read() == 0) {
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val item = (ForgeRegistries.ITEMS as ForgeRegistry<Item>).getValue(readInt())
|
||||||
|
val itemStack = ItemStack(item, readInt())
|
||||||
|
|
||||||
|
if (read() != 0) {
|
||||||
|
itemStack.readShareTag(readNbt())
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemStack
|
||||||
|
}
|
||||||
|
|
||||||
|
fun InputStream.readVarIntBE(): Int {
|
||||||
|
var result = 0
|
||||||
|
var read = read()
|
||||||
|
|
||||||
|
do {
|
||||||
|
result = (result shl 7) or (read and 127)
|
||||||
|
read = read()
|
||||||
|
} while (read and 128 != 0)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun InputStream.readVarIntLE(): Int {
|
||||||
|
var result = 0
|
||||||
|
var read = read()
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
while (read and 128 != 0) {
|
||||||
|
result = result or ((read and 127) shl i)
|
||||||
|
read = read()
|
||||||
|
i += 7
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result or ((read and 127) shl i)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OutputStream.writeVarIntLE(value: Int) {
|
||||||
|
require(value >= 0) { "Negative number provided: $value" }
|
||||||
|
var written = value
|
||||||
|
|
||||||
|
while (written >= 128) {
|
||||||
|
write((written and 127) or 128)
|
||||||
|
written = written ushr 7
|
||||||
|
}
|
||||||
|
|
||||||
|
write(written)
|
||||||
|
}
|
||||||
|
@ -4,6 +4,12 @@ import net.minecraft.nbt.ByteArrayTag
|
|||||||
import net.minecraft.nbt.StringTag
|
import net.minecraft.nbt.StringTag
|
||||||
import net.minecraft.nbt.Tag
|
import net.minecraft.nbt.Tag
|
||||||
import net.minecraft.network.FriendlyByteBuf
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import ru.dbotthepony.mc.otm.readDouble
|
||||||
|
import ru.dbotthepony.mc.otm.readVarIntLE
|
||||||
|
import ru.dbotthepony.mc.otm.writeDouble
|
||||||
|
import ru.dbotthepony.mc.otm.writeVarIntLE
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.math.MathContext
|
import java.math.MathContext
|
||||||
@ -641,3 +647,16 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
|
|||||||
|
|
||||||
fun FriendlyByteBuf.readImpreciseFraction() = ImpreciseFraction.read(this)
|
fun FriendlyByteBuf.readImpreciseFraction() = ImpreciseFraction.read(this)
|
||||||
fun FriendlyByteBuf.writeImpreciseFraction(value: ImpreciseFraction) = value.write(this)
|
fun FriendlyByteBuf.writeImpreciseFraction(value: ImpreciseFraction) = value.write(this)
|
||||||
|
|
||||||
|
fun InputStream.readImpreciseFraction(): ImpreciseFraction {
|
||||||
|
val bytes = ByteArray(readVarIntLE())
|
||||||
|
read(bytes)
|
||||||
|
return ImpreciseFraction(BigInteger(bytes), readDouble())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OutputStream.writeImpreciseFraction(value: ImpreciseFraction) {
|
||||||
|
val bytes = value.whole.toByteArray()
|
||||||
|
writeVarIntLE(bytes.size)
|
||||||
|
write(bytes)
|
||||||
|
writeDouble(value.decimal)
|
||||||
|
}
|
||||||
|
@ -0,0 +1,234 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.network
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
||||||
|
import ru.dbotthepony.mc.otm.core.readImpreciseFraction
|
||||||
|
import ru.dbotthepony.mc.otm.core.writeImpreciseFraction
|
||||||
|
import ru.dbotthepony.mc.otm.readItem
|
||||||
|
import ru.dbotthepony.mc.otm.writeItem
|
||||||
|
import java.io.DataInputStream
|
||||||
|
import java.io.DataOutputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
fun interface FieldReader<V> {
|
||||||
|
fun invoke(): V
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface FieldGetter<V> {
|
||||||
|
fun invoke(read: FieldReader<V>): V
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface FieldWriter<V> {
|
||||||
|
fun invoke(value: V)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface FieldSetter<V> {
|
||||||
|
fun invoke(value: V, write: FieldWriter<V>, setByRemote: Boolean): V
|
||||||
|
}
|
||||||
|
|
||||||
|
class NetworkValueCodec<V>(
|
||||||
|
val reader: (stream: DataInputStream) -> V,
|
||||||
|
val writer: (stream: DataOutputStream, value: V) -> Unit,
|
||||||
|
val copier: ((value: V) -> V) = { it },
|
||||||
|
val comparator: ((a: V, b: V) -> Boolean) = { a, b -> a == b }
|
||||||
|
)
|
||||||
|
|
||||||
|
val BooleanValueCodec = NetworkValueCodec(DataInputStream::readBoolean, DataOutputStream::writeBoolean)
|
||||||
|
val IntValueCodec = NetworkValueCodec(DataInputStream::readInt, DataOutputStream::writeInt)
|
||||||
|
val ItemStackValueCodec = NetworkValueCodec(DataInputStream::readItem, DataOutputStream::writeItem, ItemStack::copy, ItemStack::isSameItemSameTags)
|
||||||
|
val ImpreciseFractionValueCodec = NetworkValueCodec(DataInputStream::readImpreciseFraction, DataOutputStream::writeImpreciseFraction)
|
||||||
|
|
||||||
|
class FieldSynchronizer {
|
||||||
|
private val fields = ArrayList<Field<*>>()
|
||||||
|
private val observers = ArrayList<Field<*>>()
|
||||||
|
private val dirtyFields = ArrayList<Field<*>>()
|
||||||
|
|
||||||
|
fun bool(
|
||||||
|
value: Boolean = false,
|
||||||
|
getter: FieldGetter<Boolean>? = null,
|
||||||
|
setter: FieldSetter<Boolean>? = null,
|
||||||
|
): Field<Boolean> {
|
||||||
|
return Field(value, BooleanValueCodec, getter, setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun int(
|
||||||
|
value: Int = 0,
|
||||||
|
getter: FieldGetter<Int>? = null,
|
||||||
|
setter: FieldSetter<Int>? = null,
|
||||||
|
): Field<Int> {
|
||||||
|
return Field(value, IntValueCodec, getter, setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fraction(
|
||||||
|
value: ImpreciseFraction = ImpreciseFraction.ZERO,
|
||||||
|
getter: FieldGetter<ImpreciseFraction>? = null,
|
||||||
|
setter: FieldSetter<ImpreciseFraction>? = null,
|
||||||
|
): Field<ImpreciseFraction> {
|
||||||
|
return Field(value, ImpreciseFractionValueCodec, getter, setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun item(
|
||||||
|
value: ItemStack = ItemStack.EMPTY,
|
||||||
|
getter: FieldGetter<ItemStack>? = null,
|
||||||
|
setter: FieldSetter<ItemStack>? = null,
|
||||||
|
): Field<ItemStack> {
|
||||||
|
return Field(value, ItemStackValueCodec, getter, setter, isObserver = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class Field<V>(
|
||||||
|
private var value: V,
|
||||||
|
private val dispatcher: NetworkValueCodec<V>,
|
||||||
|
private val getter: FieldGetter<V>? = null,
|
||||||
|
private val setter: FieldSetter<V>? = null,
|
||||||
|
isObserver: Boolean = false,
|
||||||
|
) : ReadWriteProperty<Any, V> {
|
||||||
|
private var remote: V = dispatcher.copier.invoke(value)
|
||||||
|
|
||||||
|
val id = fields.size + 1
|
||||||
|
|
||||||
|
init {
|
||||||
|
fields.add(this)
|
||||||
|
|
||||||
|
if (isObserver) {
|
||||||
|
observers.add(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isDirty = false
|
||||||
|
|
||||||
|
private val write: FieldWriter<V> = FieldWriter {
|
||||||
|
if (!isDirty && !dispatcher.comparator.invoke(remote, value)) {
|
||||||
|
dirtyFields.add(this)
|
||||||
|
isDirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value = it
|
||||||
|
}
|
||||||
|
|
||||||
|
private val read: FieldReader<V> = FieldReader {
|
||||||
|
return@FieldReader this.value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun observe() {
|
||||||
|
if (!isDirty && !dispatcher.comparator.invoke(remote, value)) {
|
||||||
|
dirtyFields.add(this)
|
||||||
|
isDirty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getValue(): V {
|
||||||
|
val getter = this.getter
|
||||||
|
|
||||||
|
if (getter != null) {
|
||||||
|
return getter.invoke(read)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Any, property: KProperty<*>): V {
|
||||||
|
return getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(value: V) {
|
||||||
|
val setter = this.setter
|
||||||
|
|
||||||
|
if (setter != null) {
|
||||||
|
setter.invoke(value, write, false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.value == value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDirty && !dispatcher.comparator.invoke(remote, value)) {
|
||||||
|
dirtyFields.add(this)
|
||||||
|
isDirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any, property: KProperty<*>, value: V) {
|
||||||
|
setValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markDirty() {
|
||||||
|
if (!isDirty) {
|
||||||
|
dirtyFields.add(this)
|
||||||
|
isDirty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun write(stream: DataOutputStream) {
|
||||||
|
stream.write(id)
|
||||||
|
dispatcher.writer.invoke(stream, value)
|
||||||
|
isDirty = false
|
||||||
|
remote = dispatcher.copier.invoke(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun read(stream: DataInputStream) {
|
||||||
|
val value = dispatcher.reader.invoke(stream)
|
||||||
|
val setter = this.setter
|
||||||
|
|
||||||
|
if (setter != null) {
|
||||||
|
setter.invoke(value, write, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun invalidate() {
|
||||||
|
for (field in fields) {
|
||||||
|
field.markDirty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun collectNetworkPayload(): FastByteArrayOutputStream? {
|
||||||
|
if (observers.isNotEmpty()) {
|
||||||
|
for (field in observers) {
|
||||||
|
field.observe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirtyFields.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val stream = FastByteArrayOutputStream()
|
||||||
|
val dataStream = DataOutputStream(stream)
|
||||||
|
|
||||||
|
for (field in dirtyFields) {
|
||||||
|
field.write(dataStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
dirtyFields.clear()
|
||||||
|
dataStream.write(0)
|
||||||
|
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
|
||||||
|
fun applyNetworkPayload(stream: DataInputStream): Int {
|
||||||
|
var fieldId = stream.read()
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
while (fieldId != 0) {
|
||||||
|
val field = fields.getOrNull(fieldId - 1) ?: throw IndexOutOfBoundsException("Invalid field id $fieldId")
|
||||||
|
field.read(stream)
|
||||||
|
fieldId = stream.read()
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
fun applyNetworkPayload(stream: InputStream): Int {
|
||||||
|
return applyNetworkPayload(DataInputStream(stream))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.tests
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
|
||||||
|
object FieldSynchronizerTests {
|
||||||
|
@Test
|
||||||
|
@DisplayName("Field Synchronizer full read/write test")
|
||||||
|
fun test() {
|
||||||
|
val a = FieldSynchronizer()
|
||||||
|
val b = FieldSynchronizer()
|
||||||
|
|
||||||
|
val boolA = a.bool()
|
||||||
|
val boolB = b.bool()
|
||||||
|
|
||||||
|
val intA = a.int()
|
||||||
|
val intB = b.int()
|
||||||
|
|
||||||
|
val intA2 = a.int()
|
||||||
|
val intB2 = b.int()
|
||||||
|
|
||||||
|
val intA3 = a.int()
|
||||||
|
val intB3 = b.int()
|
||||||
|
|
||||||
|
boolA.setValue(true)
|
||||||
|
intA.setValue(8384)
|
||||||
|
intA2.setValue(348488)
|
||||||
|
intA3.setValue(-4)
|
||||||
|
|
||||||
|
b.applyNetworkPayload(ByteArrayInputStream(a.collectNetworkPayload()!!.array))
|
||||||
|
|
||||||
|
assertEquals(boolA.getValue(), boolB.getValue())
|
||||||
|
assertEquals(intA.getValue(), intB.getValue())
|
||||||
|
assertEquals(intA2.getValue(), intB2.getValue())
|
||||||
|
assertEquals(intA3.getValue(), intB3.getValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Field Synchronizer partial read/write test")
|
||||||
|
fun testPartial() {
|
||||||
|
val a = FieldSynchronizer()
|
||||||
|
val b = FieldSynchronizer()
|
||||||
|
|
||||||
|
val boolA = a.bool()
|
||||||
|
val boolB = b.bool()
|
||||||
|
|
||||||
|
val intA = a.int()
|
||||||
|
val intB = b.int()
|
||||||
|
|
||||||
|
val intA2 = a.int()
|
||||||
|
val intB2 = b.int()
|
||||||
|
|
||||||
|
val intA3 = a.int()
|
||||||
|
val intB3 = b.int()
|
||||||
|
|
||||||
|
boolA.setValue(true)
|
||||||
|
//intA.setValue(8384)
|
||||||
|
//intA2.setValue(348488)
|
||||||
|
intA3.setValue(-4)
|
||||||
|
|
||||||
|
b.applyNetworkPayload(ByteArrayInputStream(a.collectNetworkPayload()!!.array))
|
||||||
|
|
||||||
|
assertEquals(boolA.getValue(), boolB.getValue())
|
||||||
|
assertEquals(intA.getValue(), intB.getValue())
|
||||||
|
assertEquals(intA2.getValue(), intB2.getValue())
|
||||||
|
assertEquals(intA3.getValue(), intB3.getValue())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.tests
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.io.FastByteArrayInputStream
|
||||||
|
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import ru.dbotthepony.mc.otm.readInt
|
||||||
|
import ru.dbotthepony.mc.otm.readVarIntLE
|
||||||
|
import ru.dbotthepony.mc.otm.writeInt
|
||||||
|
import ru.dbotthepony.mc.otm.writeVarIntLE
|
||||||
|
|
||||||
|
object FriendlyStreams {
|
||||||
|
@Test
|
||||||
|
@DisplayName("Stream extension functions")
|
||||||
|
fun test() {
|
||||||
|
val output = FastByteArrayOutputStream()
|
||||||
|
|
||||||
|
output.writeInt(4)
|
||||||
|
output.writeInt(16)
|
||||||
|
output.writeInt(-1)
|
||||||
|
output.writeInt(1000000)
|
||||||
|
|
||||||
|
output.writeVarIntLE(0)
|
||||||
|
output.writeVarIntLE(1)
|
||||||
|
output.writeVarIntLE(4)
|
||||||
|
output.writeVarIntLE(15)
|
||||||
|
output.writeVarIntLE(16)
|
||||||
|
output.writeVarIntLE(127)
|
||||||
|
output.writeVarIntLE(128)
|
||||||
|
output.writeVarIntLE(129)
|
||||||
|
output.writeVarIntLE(10023)
|
||||||
|
output.writeVarIntLE(100000)
|
||||||
|
|
||||||
|
val input = FastByteArrayInputStream(output.array, 0, output.length)
|
||||||
|
|
||||||
|
assertEquals(input.readInt(), 4)
|
||||||
|
assertEquals(input.readInt(), 16)
|
||||||
|
assertEquals(input.readInt(), -1)
|
||||||
|
assertEquals(input.readInt(), 1000000)
|
||||||
|
|
||||||
|
assertEquals(input.readVarIntLE(), 0)
|
||||||
|
assertEquals(input.readVarIntLE(), 1)
|
||||||
|
assertEquals(input.readVarIntLE(), 4)
|
||||||
|
assertEquals(input.readVarIntLE(), 15)
|
||||||
|
assertEquals(input.readVarIntLE(), 16)
|
||||||
|
assertEquals(input.readVarIntLE(), 127)
|
||||||
|
assertEquals(input.readVarIntLE(), 128)
|
||||||
|
assertEquals(input.readVarIntLE(), 129)
|
||||||
|
assertEquals(input.readVarIntLE(), 10023)
|
||||||
|
assertEquals(input.readVarIntLE(), 100000)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user