package net.shadowfacts.phycon.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.util.NetworkUtil import net.shadowfacts.phycon.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) } } }