From da583324b9bb393940c630ba6e5ac63466960505 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Mon, 31 Jan 2022 21:58:33 +0700 Subject: [PATCH] Better menu data networking --- build.gradle.kts | 4 + .../menu/data/BigDecimalDataContainer.java | 111 ------------ .../mc/otm/menu/data/UUIDDataContainer.java | 81 --------- .../mc/otm/network/MatteryNetworking.java | 9 + .../block/entity/BlockEntityEnergyCounter.kt | 46 ++++- .../mc/otm/menu/MenuChemicalGenerator.kt | 2 +- .../mc/otm/menu/MenuEnergyCounter.kt | 8 +- .../ru/dbotthepony/mc/otm/menu/MenuMattery.kt | 34 ++++ .../mc/otm/menu/data/BasicContainers.kt | 17 ++ .../otm/menu/data/BigDecimalDataContainer.kt | 48 ++++++ .../mc/otm/menu/data/FractionDataContainer.kt | 158 +++--------------- .../otm/menu/data/MultiByteDataContainer.kt | 108 ++++++++++++ .../mc/otm/menu/data/PrimitiveContainers.kt | 71 ++++---- .../mc/otm/menu/widget/AbstractWidget.kt | 5 + .../mc/otm/menu/widget/LevelGaugeWidget.kt | 12 +- .../mc/otm/menu/widget/ProgressGaugeWidget.kt | 8 +- .../mc/otm/tests/NetworkingTests.kt | 8 +- 17 files changed, 334 insertions(+), 396 deletions(-) delete mode 100644 src/main/java/ru/dbotthepony/mc/otm/menu/data/BigDecimalDataContainer.java delete mode 100644 src/main/java/ru/dbotthepony/mc/otm/menu/data/UUIDDataContainer.java create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/BigDecimalDataContainer.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/MultiByteDataContainer.kt diff --git a/build.gradle.kts b/build.gradle.kts index 3afe23854..39c93d46b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,6 +46,10 @@ sourceSets { } } +tasks.test { + useJUnitPlatform() +} + dependencies { val jupiter_version: String by project val kotlin_version: String by project diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/data/BigDecimalDataContainer.java b/src/main/java/ru/dbotthepony/mc/otm/menu/data/BigDecimalDataContainer.java deleted file mode 100644 index 8fc03c63a..000000000 --- a/src/main/java/ru/dbotthepony/mc/otm/menu/data/BigDecimalDataContainer.java +++ /dev/null @@ -1,111 +0,0 @@ -package ru.dbotthepony.mc.otm.menu.data; - -import net.minecraft.world.inventory.ContainerData; -import org.apache.commons.lang3.ArrayUtils; -import ru.dbotthepony.mc.otm.OverdriveThatMatters; - -import java.math.BigDecimal; -import java.math.BigInteger; - -public class BigDecimalDataContainer implements ContainerData { - public static final int NETWORK_PAYLOAD_SIZE = 16; - - private BigDecimal value; - // working with ints - // networking as shorts - // crazy. - private final int[] buffer = new int[NETWORK_PAYLOAD_SIZE + 2]; - - public BigDecimal getDecimal() { - if (value != null) { - return value; - } - - byte[] _build = new byte[NETWORK_PAYLOAD_SIZE * 2]; - - int build_index = 0; - - // read payload - for (int i = 1; i <= NETWORK_PAYLOAD_SIZE; i++) { - _build[build_index] = (byte) ((buffer[i] & 0xFF00) >> 8); - _build[build_index + 1] = (byte) (buffer[i] & 0xFF); - - build_index += 2; - } - - byte[] build = new byte[_build[0] & 0xFF]; - - if (_build[0] != 0) { - if (build.length > _build.length - 1) { - OverdriveThatMatters.LOGGER.fatal("Tried to read {} bytes, while buffer allow only up to {} bytes to be read!", build.length, _build.length - 1); - OverdriveThatMatters.LOGGER.fatal("buffer state: {}", ArrayUtils.toString(buffer)); - OverdriveThatMatters.LOGGER.fatal("decoded state: {}", ArrayUtils.toString(_build)); - return value = BigDecimal.ZERO; - } - - System.arraycopy(_build, 1, build, 0, build.length); - - return value = new BigDecimal(new BigInteger(build), buffer[0]); - } - - return value = BigDecimal.ZERO; - } - - public void setDecimal(BigDecimal decimal) { - if (decimal.equals(value)) { - return; - } - - value = decimal; - - int scale = decimal.scale(); - BigInteger integer = decimal.unscaledValue(); - - buffer[0] = scale & 0xFFFF; - buffer[1] = (scale & 0xFFFF0000) >>> 16; - byte[] _build = integer.toByteArray(); - byte[] build = new byte[NETWORK_PAYLOAD_SIZE * 2]; - - // insert, shift by one - System.arraycopy(_build, 0, build, 1, Math.min(_build.length, NETWORK_PAYLOAD_SIZE * 4 - 1)); - - // tell how many bytes are in there - build[0] = (byte) _build.length; - - int build_index = 0; - - // write - for (int i = 1; i <= NETWORK_PAYLOAD_SIZE; i++) { - buffer[i] = - (build[build_index] & 0xFF) << 8 | - (build[build_index + 1] & 0xFF); - - build_index += 2; - } - } - - // override - protected void updateValue() { - - } - - @Override - public int get(int index) { - updateValue(); - return buffer[index]; - } - - @Override - public void set(int index, int _value) { - if (buffer[index] == _value) - return; - - value = null; - buffer[index] = _value; - } - - @Override - public int getCount() { - return NETWORK_PAYLOAD_SIZE + 2; - } -} diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/data/UUIDDataContainer.java b/src/main/java/ru/dbotthepony/mc/otm/menu/data/UUIDDataContainer.java deleted file mode 100644 index 09117c5a4..000000000 --- a/src/main/java/ru/dbotthepony/mc/otm/menu/data/UUIDDataContainer.java +++ /dev/null @@ -1,81 +0,0 @@ -package ru.dbotthepony.mc.otm.menu.data; - -import net.minecraft.world.inventory.ContainerData; - -import java.util.UUID; - -public class UUIDDataContainer implements ContainerData { - public static final int NETWORK_PAYLOAD_SIZE = 8; - - private UUID value; - private final int[] buffer = new int[NETWORK_PAYLOAD_SIZE]; - - public UUID getValue() { - if (value != null) { - return value; - } - - long a = buffer[0]; - long b = buffer[1]; - long c = buffer[2]; - long d = buffer[3]; - - long upper = a | b << 16 | c << 32 | d << 48; - - a = buffer[4]; - b = buffer[5]; - c = buffer[6]; - d = buffer[7]; - - long lower = a | b << 16 | c << 32 | d << 48; - - return value = new UUID(upper, lower); - } - - public void setValue(UUID value) { - if (value.equals(this.value)) { - return; - } - - this.value = value; - - long long_value = value.getMostSignificantBits(); - - buffer[0] = (int) ((long_value) & 0xFFFF); - buffer[1] = (int) ((long_value >>> 16) & 0xFFFF); - buffer[2] = (int) ((long_value >>> 32) & 0xFFFF); - buffer[3] = (int) ((long_value >>> 48) & 0xFFFF); - - long_value = value.getLeastSignificantBits(); - - buffer[4] = (int) ((long_value) & 0xFFFF); - buffer[5] = (int) ((long_value >>> 16) & 0xFFFF); - buffer[6] = (int) ((long_value >>> 32) & 0xFFFF); - buffer[7] = (int) ((long_value >>> 48) & 0xFFFF); - } - - // override - protected void updateValue() { - - } - - @Override - public int get(int index) { - updateValue(); - return buffer[index]; - } - - @Override - public void set(int index, int _value) { - if (buffer[index] == _value) - return; - - value = null; - buffer[index] = _value; - } - - @Override - public int getCount() { - return NETWORK_PAYLOAD_SIZE; - } -} diff --git a/src/main/java/ru/dbotthepony/mc/otm/network/MatteryNetworking.java b/src/main/java/ru/dbotthepony/mc/otm/network/MatteryNetworking.java index 5d0b5fc57..bd4c85924 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/network/MatteryNetworking.java +++ b/src/main/java/ru/dbotthepony/mc/otm/network/MatteryNetworking.java @@ -273,5 +273,14 @@ public class MatteryNetworking { OneWayPlayerInputPacket::play, Optional.of(NetworkDirection.PLAY_TO_SERVER) ); + + CHANNEL.registerMessage( + next_network_id++, + MultiByteDataContainerPacket.class, + MultiByteDataContainerPacket::write, + MultiByteDataContainerPacket.Companion::read, + MultiByteDataContainerPacket::play, + Optional.of(NetworkDirection.PLAY_TO_CLIENT) + ); } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityEnergyCounter.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityEnergyCounter.kt index 0227c9879..f7a3084fd 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityEnergyCounter.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityEnergyCounter.kt @@ -33,6 +33,7 @@ import ru.dbotthepony.mc.otm.menu.MenuEnergyCounter import ru.dbotthepony.mc.otm.network.MatteryNetworking import ru.dbotthepony.mc.otm.registry.MBlockEntities import java.lang.ref.WeakReference +import java.util.* import java.util.function.Supplier data class EnergyCounterPacket(val pos: BlockPos, val thisTick: ImpreciseFraction, val total: ImpreciseFraction, val index: Int, val value: ImpreciseFraction) { @@ -73,11 +74,9 @@ data class EnergyCounterPacket(val pos: BlockPos, val thisTick: ImpreciseFractio class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : BlockEntityMattery(MBlockEntities.ENERGY_COUNTER, p_155229_, p_155230_) { var passed = ImpreciseFraction.ZERO - internal set private val history = Array(10 * 20) { ImpreciseFraction.ZERO } - var historyTick = 0 - internal set + internal var historyTick = 0 fun size() = history.size operator fun get(i: Int) = history[i] @@ -88,6 +87,15 @@ class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : Blo var lastTick = ImpreciseFraction.ZERO internal set + var ioLimit: ImpreciseFraction? = null + + fun resetStats() { + historyTick = 0 + lastTick = ImpreciseFraction.ZERO + passed = ImpreciseFraction.ZERO + Arrays.fill(history, ImpreciseFraction.ZERO) + } + fun getHistory(ticks: Int): Array { require(!(ticks < 1 || ticks >= history.size)) { "Invalid history length provided" } @@ -184,10 +192,20 @@ class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : Blo val diff: ImpreciseFraction - if (it is IMatteryEnergyStorage) { - diff = it.extractEnergyOuter(howMuch, simulate) + val ioLimit = ioLimit + + if (ioLimit != null) { + if (it is IMatteryEnergyStorage) { + diff = it.extractEnergyOuter(howMuch.min(ioLimit), simulate) + } else { + diff = ImpreciseFraction(it.extractEnergy(howMuch.min(ioLimit), simulate)) + } } else { - diff = ImpreciseFraction(it.extractEnergy(howMuch, simulate)) + if (it is IMatteryEnergyStorage) { + diff = it.extractEnergyOuter(howMuch, simulate) + } else { + diff = ImpreciseFraction(it.extractEnergy(howMuch, simulate)) + } } if (!simulate) { @@ -214,10 +232,20 @@ class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : Blo val diff: ImpreciseFraction - if (it is IMatteryEnergyStorage) { - diff = it.receiveEnergyOuter(howMuch, simulate) + val ioLimit = ioLimit + + if (ioLimit != null) { + if (it is IMatteryEnergyStorage) { + diff = it.receiveEnergyOuter(howMuch.min(ioLimit), simulate) + } else { + diff = ImpreciseFraction(it.receiveEnergy(howMuch.min(ioLimit), simulate)) + } } else { - diff = ImpreciseFraction(it.receiveEnergy(howMuch, simulate)) + if (it is IMatteryEnergyStorage) { + diff = it.receiveEnergyOuter(howMuch, simulate) + } else { + diff = ImpreciseFraction(it.receiveEnergy(howMuch, simulate)) + } } if (!simulate) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuChemicalGenerator.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuChemicalGenerator.kt index b17009a0e..661bef5bc 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuChemicalGenerator.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuChemicalGenerator.kt @@ -42,7 +42,7 @@ class MenuChemicalGenerator @JvmOverloads constructor(id: Int, inv: Inventory, t addSlot(fuelSlot) addSlot(batterySlot) addSlot(residueSlot) - addDataSlots(burnTime) + addDataContainer(burnTime) addInventorySlots() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuEnergyCounter.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuEnergyCounter.kt index 05239fe10..ad116f0d3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuEnergyCounter.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuEnergyCounter.kt @@ -36,10 +36,10 @@ class MenuEnergyCounter @JvmOverloads constructor( } } - addDataSlots(passed) - addDataSlots(average) - addDataSlots(last20Ticks) - addDataSlots(lastTick) + addDataContainer(passed) + addDataContainer(average) + addDataContainer(last20Ticks) + addDataContainer(lastTick) addDataSlots(inputDirection) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuMattery.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuMattery.kt index 590f9a6be..cfbe79566 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuMattery.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MenuMattery.kt @@ -1,11 +1,15 @@ package ru.dbotthepony.mc.otm.menu +import net.minecraft.server.level.ServerPlayer import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.* import net.minecraft.world.item.ItemStack import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraftforge.network.PacketDistributor +import ru.dbotthepony.mc.otm.menu.data.MultiByteDataContainer import ru.dbotthepony.mc.otm.menu.widget.AbstractWidget +import ru.dbotthepony.mc.otm.network.MatteryNetworking import java.util.function.Consumer @JvmRecord @@ -25,6 +29,7 @@ abstract class MatteryMenu protected @JvmOverloads constructor( @JvmField val inventorySlots = ArrayList() + val multiByteContainers = ArrayList() @JvmField protected val lockedInventorySlots: MutableSet = HashSet() @@ -57,6 +62,22 @@ abstract class MatteryMenu protected @JvmOverloads constructor( return matteryWidgets[index] } + fun addDataContainer(container: MultiByteDataContainer) { + if (multiByteContainers.contains(container)) + throw IllegalStateException("Already containing $container") + + multiByteContainers.add(container) + container.slotID = multiByteContainers.size - 1 + } + + fun getDataContainer(index: Int): MultiByteDataContainer? { + if (index !in 0 until multiByteContainers.size) { + return null + } + + return multiByteContainers[index] + } + @JvmField protected var inventorySlotIndexStart = 0 @@ -108,11 +129,20 @@ abstract class MatteryMenu protected @JvmOverloads constructor( inventorySlotIndexEnd = last!!.index } + private val pdistributor = PacketDistributor.PLAYER.with { ply as ServerPlayer } + override fun broadcastChanges() { for (widget in matteryWidgets) { widget.updateServer() } + for (data in multiByteContainers) { + if (data.dirty) { + data.dirty = false + MatteryNetworking.CHANNEL.send(pdistributor, data.makePacket()) + } + } + super.broadcastChanges() } @@ -121,6 +151,10 @@ abstract class MatteryMenu protected @JvmOverloads constructor( widget.updateServer() } + for (data in multiByteContainers) { + MatteryNetworking.CHANNEL.send(pdistributor, data.makePacket()) + } + super.broadcastFullState() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/BasicContainers.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/BasicContainers.kt index 66778218b..9a551876c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/BasicContainers.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/BasicContainers.kt @@ -1,6 +1,7 @@ package ru.dbotthepony.mc.otm.menu.data import net.minecraft.world.inventory.ContainerData +import java.util.* class EnumDataContainer>(val enum: Class) : ContainerData { val buffer = intArrayOf(0) @@ -28,3 +29,19 @@ class EnumDataContainer>(val enum: Class) : ContainerData { return 1 } } + +class UUIDDataContainer : ComplexDataContainer(16) { + private var _value = UUID(0, 0) + + override var value: UUID + get() = _value + set(value) { + buffer.position(0).putLong(value.mostSignificantBits).putLong(value.leastSignificantBits) + _value = value + dirty = true + } + + override fun construct() { + _value = UUID(buffer.position(0).long, buffer.long) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/BigDecimalDataContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/BigDecimalDataContainer.kt new file mode 100644 index 000000000..2a2693294 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/BigDecimalDataContainer.kt @@ -0,0 +1,48 @@ +package ru.dbotthepony.mc.otm.menu.data + +import org.apache.logging.log4j.LogManager +import java.math.BigDecimal +import java.math.BigInteger + +class BigDecimalDataContainer(bytes: Int = 128) : ComplexDataContainer(bytes) { + companion object { + private val LOGGER = LogManager.getLogger() + } + + private var _value = BigDecimal.ZERO + private var mute = false + + override var value: BigDecimal + get() = _value + set(value) { + if (value == _value) + return + + try { + buffer.array().fill(0) + buffer.position(0) + buffer.putInt(value.scale()) + val bytes = value.unscaledValue().toByteArray() + + buffer.putInt(bytes.size) + + bytes.copyInto(buffer.array(), destinationOffset = 8) + + dirty = true + } catch(err: Throwable) { + if (!mute) { + LOGGER.error("Unable to serialize value $value for network transportation", err) + mute = true + } + + buffer.array().fill(0) + } + } + + override fun construct() { + val scale = buffer.position(0).int + val length = buffer.int + val bytes = buffer.array().copyOfRange(8, 8 + length) + _value = BigDecimal(BigInteger(bytes), scale) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/FractionDataContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/FractionDataContainer.kt index 6157cf150..cea377097 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/FractionDataContainer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/FractionDataContainer.kt @@ -7,178 +7,72 @@ import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.network.NetworkHelper import java.util.zip.CRC32 -class FractionDataContainer : ContainerData { +class FractionDataContainer : ComplexDataContainer(128) { companion object { - // CRC32 + Данные - const val NETWORK_PAYLOAD_SIZE = 64 + 2 private val LOGGER = LogManager.getLogger() } - private var _value: Fraction? = Fraction.ZERO - private var _value2: Fraction? = Fraction.ZERO - - fun hasComputedValue(): Boolean { - return _value != null - } - + private var _value = Fraction.ZERO private var mute = false - var value: Fraction - get() { - if (_value != null) - return _value!! - - val bytes = NetworkHelper.shortsToBytes(buffer, 2) - - val crc = CRC32() - crc.update(bytes) - val crcValue = crc.value - - val a = (crcValue and 0xFFFF).toShort() - val b = ((crcValue and 0xFFFF0000) ushr 16).toShort() - - if (a == buffer[0] && b == buffer[1]) { - _value = Fraction.fromByteArray(bytes) - } else { - _value = _value2 - } - - return _value!! - } + override var value: Fraction + get() = _value set(value) { + if (_value == value) + return + _value = value - val _value = _value - if (_value == null) { - buffer.fill(0) - return - } - try { - val bytes = ByteArray(128) {0} - _value.toByteArray().copyInto(bytes) - - val crc = CRC32() - crc.update(bytes) - val crcValue = crc.value - - val a = (crcValue and 0xFFFF).toShort() - val b = ((crcValue and 0xFFFF0000) ushr 16).toShort() - buffer[0] = a - buffer[1] = b - - NetworkHelper.bytesToShorts(bytes).copyInto(buffer, destinationOffset = 2) + buffer.array().fill(0) + value.toByteArray().copyInto(buffer.position(0).array()) + dirty = true } catch(err: Throwable) { if (!mute) { LOGGER.error("Unable to serialize value $value for network transportation", err) mute = true } - buffer.fill(0) + buffer.array().fill(0) } } - val buffer = ShortArray(NETWORK_PAYLOAD_SIZE) - - override operator fun get(p_39284_: Int): Int { - return buffer[p_39284_].toInt() - } - - override operator fun set(p_39285_: Int, p_39286_: Int) { - buffer[p_39285_] = p_39286_.toShort() - if (_value != null) _value2 = _value - _value = null - } - - override fun getCount(): Int { - return buffer.size + override fun construct() { + _value = Fraction.fromByteArray(buffer.array()) } } -class ImpreciseFractionDataContainer : ContainerData { +class ImpreciseFractionDataContainer : ComplexDataContainer(128) { companion object { - // CRC32 + Данные - const val NETWORK_PAYLOAD_SIZE = 64 + 2 private val LOGGER = LogManager.getLogger() } - private var _value: ImpreciseFraction? = ImpreciseFraction.ZERO - private var _value2: ImpreciseFraction? = ImpreciseFraction.ZERO - - fun hasComputedValue(): Boolean { - return _value != null - } - + private var _value = ImpreciseFraction.ZERO private var mute = false - var value: ImpreciseFraction - get() { - if (_value != null) - return _value!! - - val bytes = NetworkHelper.shortsToBytes(buffer, 2) - - val crc = CRC32() - crc.update(bytes) - val crcValue = crc.value - - val a = (crcValue and 0xFFFF).toShort() - val b = ((crcValue and 0xFFFF0000) ushr 16).toShort() - - if (a == buffer[0] && b == buffer[1]) { - _value = ImpreciseFraction.fromByteArray(bytes) - } else { - _value = _value2 - } - - return _value!! - } + override var value: ImpreciseFraction + get() = _value set(value) { + if (_value == value) + return + _value = value - val _value = _value - if (_value == null) { - buffer.fill(0) - return - } - try { - val bytes = ByteArray(128) {0} - _value.toByteArray().copyInto(bytes) - - val crc = CRC32() - crc.update(bytes) - val crcValue = crc.value - - val a = (crcValue and 0xFFFF).toShort() - val b = ((crcValue and 0xFFFF0000) ushr 16).toShort() - buffer[0] = a - buffer[1] = b - - NetworkHelper.bytesToShorts(bytes).copyInto(buffer, destinationOffset = 2) + buffer.array().fill(0) + value.toByteArray().copyInto(buffer.position(0).array()) + dirty = true } catch(err: Throwable) { if (!mute) { LOGGER.error("Unable to serialize value $value for network transportation", err) mute = true } - buffer.fill(0) + buffer.array().fill(0) } } - val buffer = ShortArray(NETWORK_PAYLOAD_SIZE) - - override operator fun get(p_39284_: Int): Int { - return buffer[p_39284_].toInt() - } - - override operator fun set(p_39285_: Int, p_39286_: Int) { - buffer[p_39285_] = p_39286_.toShort() - if (_value != null) _value2 = _value - _value = null - } - - override fun getCount(): Int { - return buffer.size + override fun construct() { + _value = ImpreciseFraction.fromByteArray(buffer.array()) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/MultiByteDataContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/MultiByteDataContainer.kt new file mode 100644 index 000000000..efcb0cda4 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/MultiByteDataContainer.kt @@ -0,0 +1,108 @@ +package ru.dbotthepony.mc.otm.menu.data + +import net.minecraft.client.Minecraft +import net.minecraft.network.FriendlyByteBuf +import net.minecraftforge.network.NetworkEvent +import ru.dbotthepony.mc.otm.menu.MatteryMenu +import java.nio.ByteBuffer +import java.util.function.Supplier + +class MultiByteDataContainerPacket(val slotID: Int, val offset: Int, val payload: ByteArray) { + fun play(context: Supplier) { + context.get().packetHandled = true + (Minecraft.getInstance().player?.containerMenu as? MatteryMenu)?.getDataContainer(slotID)?.handle(this) + } + + fun write(buff: FriendlyByteBuf) { + buff.writeInt(slotID) + buff.writeInt(offset) + buff.writeByteArray(payload) + } + + companion object { + private val empty = ByteArray(0) + fun empty(slotID: Int) = MultiByteDataContainerPacket(slotID, 0, empty) + + fun read(buff: FriendlyByteBuf): MultiByteDataContainerPacket { + return MultiByteDataContainerPacket(buff.readInt(), buff.readInt(), buff.readByteArray()) + } + } +} + +open class MultiByteDataContainer(bytes: Int) { + init { + require(bytes >= 1) { "Buffer size of $bytes does not make any sense" } + } + + protected val buffer = ByteBuffer.allocate(bytes) + var slotID = 0 + var dirty = false + + open fun handle(packet: MultiByteDataContainerPacket) { + if (packet.payload.isEmpty()) { + buffer.array().fill(0) + return + } + + val backed = buffer.array() + + for (i in 0 until packet.offset) { + backed[i] = bzero + } + + for (i in packet.payload.indices) { + backed[i + packet.offset] = packet.payload[i] + } + + for (i in packet.payload.size + packet.offset until backed.size) { + backed[i] = bzero + } + } + + open fun makePacket(): MultiByteDataContainerPacket { + var firstNonZero = 0 + val backed = buffer.array() + + for (i in 0 until backed.size) { + if (backed[i] != bzero) { + firstNonZero = i + break + } + } + + if (firstNonZero == 0 && backed[0] == bzero) { + return MultiByteDataContainerPacket.empty(slotID) + } + + var lastNonZero = firstNonZero + + for (i in firstNonZero + 1 until backed.size) { + if (backed[i] != bzero) { + lastNonZero = i + } + } + + val payload = ByteArray(lastNonZero - firstNonZero + 1) + var pi = 0 + + for (i in firstNonZero .. lastNonZero) { + payload[pi++] = backed[i] + } + + return MultiByteDataContainerPacket(slotID, firstNonZero, payload) + } + + companion object { + private const val bzero: Byte = 0 + } +} + +abstract class ComplexDataContainer(bytes: Int) : MultiByteDataContainer(bytes) { + abstract var value: T + protected abstract fun construct() + + override fun handle(packet: MultiByteDataContainerPacket) { + super.handle(packet) + construct() + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/PrimitiveContainers.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/PrimitiveContainers.kt index 1dcdcd17c..48b1c43ad 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/PrimitiveContainers.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/PrimitiveContainers.kt @@ -62,59 +62,46 @@ class ByteDataContainer : ContainerData { } } -class IntDataContainer : ContainerData { - val buffer = intArrayOf(0, 0) - +class IntDataContainer : MultiByteDataContainer(4) { var value: Int get() { - return buffer[0] or (buffer[1] shl 16) + return buffer.position(0).int } set(value) { - buffer[0] = value and 0xFFFF - buffer[1] = value ushr 16 + buffer.position(0).putInt(value) + dirty = true } - - override fun get(p_39284_: Int): Int { - return buffer[p_39284_] - } - - override fun set(p_39285_: Int, p_39286_: Int) { - buffer[p_39285_] = p_39286_ - } - - override fun getCount(): Int { - return 2 - } } -class LongDataContainer : ContainerData { - val buffer = intArrayOf(0, 0, 0, 0) +class FloatDataContainer : MultiByteDataContainer(4) { + var value: Float + get() { + return buffer.position(0).float + } + set(value) { + buffer.position(0).putFloat(value) + dirty = true + } +} +class LongDataContainer : MultiByteDataContainer(8) { var value: Long get() { - val a = buffer[0].toLong() - val b = buffer[1].toLong() - val c = buffer[2].toLong() - val d = buffer[3].toLong() - - return a or (b shl 16) or (c shl 32) or (d shl 48) + return buffer.position(0).long } set(value) { - buffer[0] = (value and 0xFFFF).toInt() - buffer[1] = ((value ushr 16) and 0xFFFF).toInt() - buffer[2] = ((value ushr 32) and 0xFFFF).toInt() - buffer[3] = ((value ushr 48) and 0xFFFF).toInt() + buffer.position(0).putLong(value) + dirty = true + } +} + +class DoubleDataContainer : MultiByteDataContainer(8) { + var value: Double + get() { + return buffer.position(0).double + } + set(value) { + buffer.position(0).putDouble(value) + dirty = true } - - override fun get(p_39284_: Int): Int { - return buffer[p_39284_] - } - - override fun set(p_39285_: Int, p_39286_: Int) { - buffer[p_39285_] = p_39286_ - } - - override fun getCount(): Int { - return 4 - } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/AbstractWidget.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/AbstractWidget.kt index 3a33193f6..4eba8badc 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/AbstractWidget.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/AbstractWidget.kt @@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.menu.widget import net.minecraft.world.inventory.ContainerData import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.menu.MatteryMenu +import ru.dbotthepony.mc.otm.menu.data.MultiByteDataContainer import java.util.function.Consumer abstract class AbstractWidget constructor( @@ -15,4 +16,8 @@ abstract class AbstractWidget constructor( protected fun addDataSlots(slots: ContainerData) { menu.addDataSlots(slots) } + + protected fun addDataContainer(slot: MultiByteDataContainer) { + menu.addDataContainer(slot) + } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/LevelGaugeWidget.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/LevelGaugeWidget.kt index 8e55227bd..1d0e522b1 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/LevelGaugeWidget.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/LevelGaugeWidget.kt @@ -11,11 +11,11 @@ import ru.dbotthepony.mc.otm.menu.data.ImpreciseFractionDataContainer @Suppress("unused") class LevelGaugeWidget(menu: MatteryMenu) : AbstractWidget(menu) { - @JvmField var level = { ImpreciseFraction.ONE} - @JvmField var maxLevel = {ImpreciseFraction.ONE} + var level = { ImpreciseFraction.ONE} + var maxLevel = {ImpreciseFraction.ONE} - @JvmField val levelContainer = ImpreciseFractionDataContainer() - @JvmField val maxLevelContainer = ImpreciseFractionDataContainer() + val levelContainer = ImpreciseFractionDataContainer() + val maxLevelContainer = ImpreciseFractionDataContainer() constructor( menu: MatteryMenu, @@ -57,8 +57,8 @@ class LevelGaugeWidget(menu: MatteryMenu) : AbstractWidget(menu) { } init { - addDataSlots(levelContainer) - addDataSlots(maxLevelContainer) + addDataContainer(levelContainer) + addDataContainer(maxLevelContainer) } override fun updateServer() { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/ProgressGaugeWidget.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/ProgressGaugeWidget.kt index a98379b48..7b36f086f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/ProgressGaugeWidget.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/widget/ProgressGaugeWidget.kt @@ -6,10 +6,10 @@ import ru.dbotthepony.mc.otm.menu.data.ShortDataContainer @Suppress("unused") class ProgressGaugeWidget(menu: MatteryMenu) : AbstractWidget(menu) { - @JvmField var progress = {0f} - @JvmField var stuck = {false} - @JvmField val progressContainer = ShortDataContainer() - @JvmField val stuckContainer = BooleanDataContainer() + var progress = {0f} + var stuck = {false} + val progressContainer = ShortDataContainer() + val stuckContainer = BooleanDataContainer() init { addDataSlots(progressContainer) diff --git a/src/test/kotlin/ru/dbotthepony/mc/otm/tests/NetworkingTests.kt b/src/test/kotlin/ru/dbotthepony/mc/otm/tests/NetworkingTests.kt index 67fd5c569..7cef3adc1 100644 --- a/src/test/kotlin/ru/dbotthepony/mc/otm/tests/NetworkingTests.kt +++ b/src/test/kotlin/ru/dbotthepony/mc/otm/tests/NetworkingTests.kt @@ -15,17 +15,13 @@ object NetworkingTests { var frac = Fraction(18, 4) container.value = frac - container[1] = container[1] - assert(!container.hasComputedValue()) - + container.handle(container.makePacket()) assert(container.value.compareTo(frac) == 0) { "${container.value} != $frac" } frac = Fraction(2_000_000, 1) container.value = frac - container[1] = container[1] - assert(!container.hasComputedValue()) - + container.handle(container.makePacket()) assert(container.value.compareTo(frac) == 0) { "${container.value} != $frac" } }