PhysicalConnectivity/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PReceiverBlockEntity.kt

145 lines
4.4 KiB
Kotlin

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<ItemVariant>? {
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<Storage<ItemVariant>>? = 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<ItemVariant>? {
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()
}
}