PhysicalConnectivity/src/main/kotlin/net/shadowfacts/phycon/network/block/netswitch/SwitchBlockEntity.kt

121 lines
3.8 KiB
Kotlin

package net.shadowfacts.phycon.network.block.netswitch
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntity
import net.minecraft.entity.ItemEntity
import net.minecraft.nbt.CompoundTag
import net.minecraft.util.Tickable
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.frame.EthernetFrame
import net.shadowfacts.phycon.api.frame.PacketFrame
import net.shadowfacts.phycon.api.util.MACAddress
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.NetworkUtil
import net.shadowfacts.phycon.network.packet.ItemStackPacket
import java.lang.ref.WeakReference
/**
* @author shadowfacts
*/
class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
BlockEntityClientSerializable,
Tickable {
companion object {
var SWITCHING_CAPACITY = 256 // 256 items/tick
}
val interfaces = Direction.values().map { SwitchInterface(it, WeakReference(this), MACAddress.random()) }
private val macTable = mutableMapOf<MACAddress, Direction>()
private var itemsHandledThisTick = 0
fun interfaceForSide(side: Direction): SwitchInterface {
return interfaces.find { it.side == side }!!
}
private fun handle(frame: EthernetFrame, fromItf: SwitchInterface) {
macTable[frame.source] = fromItf.side
if (frame is PacketFrame && frame.packet is ItemStackPacket) {
val packet = frame.packet as ItemStackPacket
if (itemsHandledThisTick + packet.stack.count > SWITCHING_CAPACITY) {
// todo: calculate entity spawn point by finding non-obstructed location
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), packet.stack)
world!!.spawnEntity(entity)
return
} else {
itemsHandledThisTick += packet.stack.count
}
}
if (frame.destination.type != MACAddress.Type.BROADCAST && macTable.containsKey(frame.destination)) {
val dir = macTable[frame.destination]!!
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}, {}) forwarding {} to side {}", this, fromItf.side, fromItf.macAddress, frame, dir)
interfaceForSide(dir).send(frame)
} else {
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}, {}) flooding {}", this, fromItf.side, fromItf.macAddress, frame)
flood(frame, fromItf)
}
}
private fun flood(frame: EthernetFrame, source: Interface) {
for (itf in interfaces) {
if (source == itf) continue
itf.send(frame)
}
}
private fun findDestination(fromItf: Interface): Interface? {
val side = (fromItf as SwitchInterface).side
return NetworkUtil.findConnectedInterface(world!!, pos, side)
}
override fun tick() {
itemsHandledThisTick = 0
}
override fun toTag(tag: CompoundTag): CompoundTag {
tag.putLongArray("InterfaceAddresses", interfaces.map { it.macAddress.address })
return super.toTag(tag)
}
override fun fromTag(state: BlockState, tag: CompoundTag) {
super.fromTag(state, tag)
tag.getLongArray("InterfaceAddresses")?.forEachIndexed { i, l ->
interfaces[i].macAddress = MACAddress(l)
}
}
override fun toClientTag(tag: CompoundTag): CompoundTag {
tag.putLongArray("InterfaceAddresses", interfaces.map { it.macAddress.address })
return tag
}
override fun fromClientTag(tag: CompoundTag) {
tag.getLongArray("InterfaceAddresses")?.forEachIndexed { i, l ->
interfaces[i].macAddress = MACAddress(l)
}
}
class SwitchInterface(
val side: Direction,
val switch: WeakReference<SwitchBlockEntity>,
@JvmField var macAddress: MACAddress,
): Interface {
override fun getMACAddress() = macAddress
override fun receive(frame: EthernetFrame) {
switch.get()?.handle(frame, this)
}
override fun send(frame: EthernetFrame) {
switch.get()?.findDestination(this)?.receive(frame)
}
}
}