package net.shadowfacts.phycon.block.p2p import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant import net.fabricmc.fabric.api.transfer.v1.storage.Storage import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.block.DeviceBlockEntity import net.shadowfacts.phycon.block.FaceDeviceBlock import net.shadowfacts.phycon.frame.NetworkSplitFrame import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.packet.* import net.shadowfacts.phycon.util.ClientConfigurableDevice import java.lang.ref.WeakReference /** * @author shadowfacts */ class P2PReceiverBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockEntity(PhyBlockEntities.P2P_RECEIVER, pos, state), ClientConfigurableDevice { enum class Status { OK, NO_TARGET, WAITING_FOR_RESPONSE; val displayName: Text get() = when (this) { OK -> TranslatableText("gui.phycon.p2p_receiver.status.ok") NO_TARGET -> TranslatableText("gui.phycon.p2p_receiver.status.no_target") WAITING_FOR_RESPONSE -> TranslatableText("gui.phycon.p2p_receiver.status.waiting_for_response") } } companion object { const val REQUEST_INVENTORY_TIMEOUT: Long = 100 // ticks fun provideItemStorage(be: P2PReceiverBlockEntity, side: Direction): Storage? { if (side == be.cachedState[FaceDeviceBlock.FACING]) { return be.getTargetInventory() } return null } } var target: IPAddress? = null var status = Status.NO_TARGET private var requestTimestamp: Long = 0 var clientObserver: (() -> Unit)? = null private var isFirstTick = true private var targetInventory: WeakReference>? = null override fun handle(packet: Packet) { when (packet) { is PongPacket -> if (packet.source == target) status = Status.OK is ReadItemStoragePacket -> targetInventory = WeakReference(packet.inventory) is DeviceRemovedPacket -> if (packet.source == target) targetInventory = null } } override fun handleNetworkSplit() { super.handleNetworkSplit() targetInventory = null } override fun tick() { super.tick() if (isFirstTick) { isFirstTick = false updateStatus() } } fun getTargetInventory(): Storage? { if (target == null) { return null } if (targetInventory?.get() == null && (counter - requestTimestamp) >= REQUEST_INVENTORY_TIMEOUT) { status = Status.WAITING_FOR_RESPONSE requestTimestamp = counter sendPacket(RequestInventoryPacket(RequestInventoryPacket.Kind.SIDED, ipAddress, target!!)) } return targetInventory?.get() } private fun updateStatus() { if (world?.isClient != false) { return } assert(!world!!.isClient) if (target == null) { status = Status.NO_TARGET } else { status = Status.WAITING_FOR_RESPONSE sendPacket(PingPacket(ipAddress, target!!)) } markUpdate() } override fun toCommonTag(tag: NbtCompound) { super.toCommonTag(tag) writeDeviceConfiguration(tag) } override fun fromCommonTag(tag: NbtCompound) { super.fromCommonTag(tag) loadDeviceConfiguration(tag) } override fun toClientTag(tag: NbtCompound): NbtCompound { tag.putInt("Status", status.ordinal) return super.toClientTag(tag) } override fun fromClientTag(tag: NbtCompound) { super.fromClientTag(tag) status = Status.values()[tag.getInt("Status")] clientObserver?.invoke() } override fun writeDeviceConfiguration(tag: NbtCompound) { target?.address?.let { tag.putInt("Target", it) } } override fun loadDeviceConfiguration(tag: NbtCompound) { target = if (tag.contains("Target")) { IPAddress(tag.getInt("Target")) } else { null } updateStatus() } }