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

121 lines
3.8 KiB
Kotlin

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<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)
}
}
}