Better menu data networking

This commit is contained in:
DBotThePony 2022-01-31 21:58:33 +07:00
parent 34f6d8e3aa
commit da583324b9
Signed by: DBot
GPG Key ID: DCC23B5715498507
17 changed files with 334 additions and 396 deletions

View File

@ -46,6 +46,10 @@ sourceSets {
} }
} }
tasks.test {
useJUnitPlatform()
}
dependencies { dependencies {
val jupiter_version: String by project val jupiter_version: String by project
val kotlin_version: String by project val kotlin_version: String by project

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -273,5 +273,14 @@ public class MatteryNetworking {
OneWayPlayerInputPacket::play, OneWayPlayerInputPacket::play,
Optional.of(NetworkDirection.PLAY_TO_SERVER) 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)
);
} }
} }

View File

@ -33,6 +33,7 @@ import ru.dbotthepony.mc.otm.menu.MenuEnergyCounter
import ru.dbotthepony.mc.otm.network.MatteryNetworking import ru.dbotthepony.mc.otm.network.MatteryNetworking
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.*
import java.util.function.Supplier import java.util.function.Supplier
data class EnergyCounterPacket(val pos: BlockPos, val thisTick: ImpreciseFraction, val total: ImpreciseFraction, val index: Int, val value: ImpreciseFraction) { 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_) { class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : BlockEntityMattery(MBlockEntities.ENERGY_COUNTER, p_155229_, p_155230_) {
var passed = ImpreciseFraction.ZERO var passed = ImpreciseFraction.ZERO
internal set
private val history = Array(10 * 20) { ImpreciseFraction.ZERO } private val history = Array(10 * 20) { ImpreciseFraction.ZERO }
var historyTick = 0 internal var historyTick = 0
internal set
fun size() = history.size fun size() = history.size
operator fun get(i: Int) = history[i] operator fun get(i: Int) = history[i]
@ -88,6 +87,15 @@ class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : Blo
var lastTick = ImpreciseFraction.ZERO var lastTick = ImpreciseFraction.ZERO
internal set 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<ImpreciseFraction> { fun getHistory(ticks: Int): Array<ImpreciseFraction> {
require(!(ticks < 1 || ticks >= history.size)) { "Invalid history length provided" } require(!(ticks < 1 || ticks >= history.size)) { "Invalid history length provided" }
@ -184,11 +192,21 @@ class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : Blo
val diff: ImpreciseFraction val diff: ImpreciseFraction
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 {
if (it is IMatteryEnergyStorage) { if (it is IMatteryEnergyStorage) {
diff = it.extractEnergyOuter(howMuch, simulate) diff = it.extractEnergyOuter(howMuch, simulate)
} else { } else {
diff = ImpreciseFraction(it.extractEnergy(howMuch, simulate)) diff = ImpreciseFraction(it.extractEnergy(howMuch, simulate))
} }
}
if (!simulate) { if (!simulate) {
passed += diff passed += diff
@ -214,11 +232,21 @@ class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : Blo
val diff: ImpreciseFraction val diff: ImpreciseFraction
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 {
if (it is IMatteryEnergyStorage) { if (it is IMatteryEnergyStorage) {
diff = it.receiveEnergyOuter(howMuch, simulate) diff = it.receiveEnergyOuter(howMuch, simulate)
} else { } else {
diff = ImpreciseFraction(it.receiveEnergy(howMuch, simulate)) diff = ImpreciseFraction(it.receiveEnergy(howMuch, simulate))
} }
}
if (!simulate) { if (!simulate) {
passed += diff passed += diff

View File

@ -42,7 +42,7 @@ class MenuChemicalGenerator @JvmOverloads constructor(id: Int, inv: Inventory, t
addSlot(fuelSlot) addSlot(fuelSlot)
addSlot(batterySlot) addSlot(batterySlot)
addSlot(residueSlot) addSlot(residueSlot)
addDataSlots(burnTime) addDataContainer(burnTime)
addInventorySlots() addInventorySlots()
} }

View File

@ -36,10 +36,10 @@ class MenuEnergyCounter @JvmOverloads constructor(
} }
} }
addDataSlots(passed) addDataContainer(passed)
addDataSlots(average) addDataContainer(average)
addDataSlots(last20Ticks) addDataContainer(last20Ticks)
addDataSlots(lastTick) addDataContainer(lastTick)
addDataSlots(inputDirection) addDataSlots(inputDirection)
} }

View File

@ -1,11 +1,15 @@
package ru.dbotthepony.mc.otm.menu 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.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.* import net.minecraft.world.inventory.*
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.entity.BlockEntity 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.menu.widget.AbstractWidget
import ru.dbotthepony.mc.otm.network.MatteryNetworking
import java.util.function.Consumer import java.util.function.Consumer
@JvmRecord @JvmRecord
@ -25,6 +29,7 @@ abstract class MatteryMenu protected @JvmOverloads constructor(
@JvmField @JvmField
val inventorySlots = ArrayList<MatterySlot>() val inventorySlots = ArrayList<MatterySlot>()
val multiByteContainers = ArrayList<MultiByteDataContainer>()
@JvmField @JvmField
protected val lockedInventorySlots: MutableSet<Int> = HashSet() protected val lockedInventorySlots: MutableSet<Int> = HashSet()
@ -57,6 +62,22 @@ abstract class MatteryMenu protected @JvmOverloads constructor(
return matteryWidgets[index] 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 @JvmField
protected var inventorySlotIndexStart = 0 protected var inventorySlotIndexStart = 0
@ -108,11 +129,20 @@ abstract class MatteryMenu protected @JvmOverloads constructor(
inventorySlotIndexEnd = last!!.index inventorySlotIndexEnd = last!!.index
} }
private val pdistributor = PacketDistributor.PLAYER.with { ply as ServerPlayer }
override fun broadcastChanges() { override fun broadcastChanges() {
for (widget in matteryWidgets) { for (widget in matteryWidgets) {
widget.updateServer() widget.updateServer()
} }
for (data in multiByteContainers) {
if (data.dirty) {
data.dirty = false
MatteryNetworking.CHANNEL.send(pdistributor, data.makePacket())
}
}
super.broadcastChanges() super.broadcastChanges()
} }
@ -121,6 +151,10 @@ abstract class MatteryMenu protected @JvmOverloads constructor(
widget.updateServer() widget.updateServer()
} }
for (data in multiByteContainers) {
MatteryNetworking.CHANNEL.send(pdistributor, data.makePacket())
}
super.broadcastFullState() super.broadcastFullState()
} }

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.menu.data package ru.dbotthepony.mc.otm.menu.data
import net.minecraft.world.inventory.ContainerData import net.minecraft.world.inventory.ContainerData
import java.util.*
class EnumDataContainer<T : Enum<T>>(val enum: Class<T>) : ContainerData { class EnumDataContainer<T : Enum<T>>(val enum: Class<T>) : ContainerData {
val buffer = intArrayOf(0) val buffer = intArrayOf(0)
@ -28,3 +29,19 @@ class EnumDataContainer<T : Enum<T>>(val enum: Class<T>) : ContainerData {
return 1 return 1
} }
} }
class UUIDDataContainer : ComplexDataContainer<UUID>(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)
}
}

View File

@ -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<BigDecimal>(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)
}
}

View File

@ -7,178 +7,72 @@ import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.network.NetworkHelper import ru.dbotthepony.mc.otm.network.NetworkHelper
import java.util.zip.CRC32 import java.util.zip.CRC32
class FractionDataContainer : ContainerData { class FractionDataContainer : ComplexDataContainer<Fraction>(128) {
companion object { companion object {
// CRC32 + Данные
const val NETWORK_PAYLOAD_SIZE = 64 + 2
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
} }
private var _value: Fraction? = Fraction.ZERO private var _value = Fraction.ZERO
private var _value2: Fraction? = Fraction.ZERO
fun hasComputedValue(): Boolean {
return _value != null
}
private var mute = false private var mute = false
var value: Fraction override var value: Fraction
get() { get() = _value
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!!
}
set(value) { set(value) {
if (_value == value)
return
_value = value _value = value
val _value = _value
if (_value == null) {
buffer.fill(0)
return
}
try { try {
val bytes = ByteArray(128) {0} buffer.array().fill(0)
_value.toByteArray().copyInto(bytes) value.toByteArray().copyInto(buffer.position(0).array())
dirty = true
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)
} catch(err: Throwable) { } catch(err: Throwable) {
if (!mute) { if (!mute) {
LOGGER.error("Unable to serialize value $value for network transportation", err) LOGGER.error("Unable to serialize value $value for network transportation", err)
mute = true mute = true
} }
buffer.fill(0) buffer.array().fill(0)
} }
} }
val buffer = ShortArray(NETWORK_PAYLOAD_SIZE) override fun construct() {
_value = Fraction.fromByteArray(buffer.array())
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
} }
} }
class ImpreciseFractionDataContainer : ContainerData { class ImpreciseFractionDataContainer : ComplexDataContainer<ImpreciseFraction>(128) {
companion object { companion object {
// CRC32 + Данные
const val NETWORK_PAYLOAD_SIZE = 64 + 2
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
} }
private var _value: ImpreciseFraction? = ImpreciseFraction.ZERO private var _value = ImpreciseFraction.ZERO
private var _value2: ImpreciseFraction? = ImpreciseFraction.ZERO
fun hasComputedValue(): Boolean {
return _value != null
}
private var mute = false private var mute = false
var value: ImpreciseFraction override var value: ImpreciseFraction
get() { get() = _value
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!!
}
set(value) { set(value) {
if (_value == value)
return
_value = value _value = value
val _value = _value
if (_value == null) {
buffer.fill(0)
return
}
try { try {
val bytes = ByteArray(128) {0} buffer.array().fill(0)
_value.toByteArray().copyInto(bytes) value.toByteArray().copyInto(buffer.position(0).array())
dirty = true
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)
} catch(err: Throwable) { } catch(err: Throwable) {
if (!mute) { if (!mute) {
LOGGER.error("Unable to serialize value $value for network transportation", err) LOGGER.error("Unable to serialize value $value for network transportation", err)
mute = true mute = true
} }
buffer.fill(0) buffer.array().fill(0)
} }
} }
val buffer = ShortArray(NETWORK_PAYLOAD_SIZE) override fun construct() {
_value = ImpreciseFraction.fromByteArray(buffer.array())
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
} }
} }

View File

@ -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<NetworkEvent.Context>) {
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<T>(bytes: Int) : MultiByteDataContainer(bytes) {
abstract var value: T
protected abstract fun construct()
override fun handle(packet: MultiByteDataContainerPacket) {
super.handle(packet)
construct()
}
}

View File

@ -62,59 +62,46 @@ class ByteDataContainer : ContainerData {
} }
} }
class IntDataContainer : ContainerData { class IntDataContainer : MultiByteDataContainer(4) {
val buffer = intArrayOf(0, 0)
var value: Int var value: Int
get() { get() {
return buffer[0] or (buffer[1] shl 16) return buffer.position(0).int
} }
set(value) { set(value) {
buffer[0] = value and 0xFFFF buffer.position(0).putInt(value)
buffer[1] = value ushr 16 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 { class FloatDataContainer : MultiByteDataContainer(4) {
val buffer = intArrayOf(0, 0, 0, 0) 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 var value: Long
get() { get() {
val a = buffer[0].toLong() return buffer.position(0).long
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)
} }
set(value) { set(value) {
buffer[0] = (value and 0xFFFF).toInt() buffer.position(0).putLong(value)
buffer[1] = ((value ushr 16) and 0xFFFF).toInt() dirty = true
buffer[2] = ((value ushr 32) and 0xFFFF).toInt() }
buffer[3] = ((value ushr 48) and 0xFFFF).toInt() }
}
class DoubleDataContainer : MultiByteDataContainer(8) {
override fun get(p_39284_: Int): Int { var value: Double
return buffer[p_39284_] get() {
} return buffer.position(0).double
}
override fun set(p_39285_: Int, p_39286_: Int) { set(value) {
buffer[p_39285_] = p_39286_ buffer.position(0).putDouble(value)
} dirty = true
override fun getCount(): Int {
return 4
} }
} }

View File

@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.menu.widget
import net.minecraft.world.inventory.ContainerData import net.minecraft.world.inventory.ContainerData
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.data.MultiByteDataContainer
import java.util.function.Consumer import java.util.function.Consumer
abstract class AbstractWidget constructor( abstract class AbstractWidget constructor(
@ -15,4 +16,8 @@ abstract class AbstractWidget constructor(
protected fun addDataSlots(slots: ContainerData) { protected fun addDataSlots(slots: ContainerData) {
menu.addDataSlots(slots) menu.addDataSlots(slots)
} }
protected fun addDataContainer(slot: MultiByteDataContainer) {
menu.addDataContainer(slot)
}
} }

View File

@ -11,11 +11,11 @@ import ru.dbotthepony.mc.otm.menu.data.ImpreciseFractionDataContainer
@Suppress("unused") @Suppress("unused")
class LevelGaugeWidget(menu: MatteryMenu) : AbstractWidget(menu) { class LevelGaugeWidget(menu: MatteryMenu) : AbstractWidget(menu) {
@JvmField var level = { ImpreciseFraction.ONE} var level = { ImpreciseFraction.ONE}
@JvmField var maxLevel = {ImpreciseFraction.ONE} var maxLevel = {ImpreciseFraction.ONE}
@JvmField val levelContainer = ImpreciseFractionDataContainer() val levelContainer = ImpreciseFractionDataContainer()
@JvmField val maxLevelContainer = ImpreciseFractionDataContainer() val maxLevelContainer = ImpreciseFractionDataContainer()
constructor( constructor(
menu: MatteryMenu, menu: MatteryMenu,
@ -57,8 +57,8 @@ class LevelGaugeWidget(menu: MatteryMenu) : AbstractWidget(menu) {
} }
init { init {
addDataSlots(levelContainer) addDataContainer(levelContainer)
addDataSlots(maxLevelContainer) addDataContainer(maxLevelContainer)
} }
override fun updateServer() { override fun updateServer() {

View File

@ -6,10 +6,10 @@ import ru.dbotthepony.mc.otm.menu.data.ShortDataContainer
@Suppress("unused") @Suppress("unused")
class ProgressGaugeWidget(menu: MatteryMenu) : AbstractWidget(menu) { class ProgressGaugeWidget(menu: MatteryMenu) : AbstractWidget(menu) {
@JvmField var progress = {0f} var progress = {0f}
@JvmField var stuck = {false} var stuck = {false}
@JvmField val progressContainer = ShortDataContainer() val progressContainer = ShortDataContainer()
@JvmField val stuckContainer = BooleanDataContainer() val stuckContainer = BooleanDataContainer()
init { init {
addDataSlots(progressContainer) addDataSlots(progressContainer)

View File

@ -15,17 +15,13 @@ object NetworkingTests {
var frac = Fraction(18, 4) var frac = Fraction(18, 4)
container.value = frac container.value = frac
container[1] = container[1] container.handle(container.makePacket())
assert(!container.hasComputedValue())
assert(container.value.compareTo(frac) == 0) { "${container.value} != $frac" } assert(container.value.compareTo(frac) == 0) { "${container.value} != $frac" }
frac = Fraction(2_000_000, 1) frac = Fraction(2_000_000, 1)
container.value = frac container.value = frac
container[1] = container[1] container.handle(container.makePacket())
assert(!container.hasComputedValue())
assert(container.value.compareTo(frac) == 0) { "${container.value} != $frac" } assert(container.value.compareTo(frac) == 0) { "${container.value} != $frac" }
} }