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