PhysicalConnectivity/src/main/kotlin/net/shadowfacts/phycon/network/DeviceBlockEntity.kt

236 lines
7.4 KiB
Kotlin
Raw Normal View History

2019-10-27 01:36:31 +00:00
package net.shadowfacts.phycon.network
2021-02-13 23:24:36 +00:00
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
2021-02-10 23:55:49 +00:00
import net.minecraft.block.BlockState
2019-10-27 01:36:31 +00:00
import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.nbt.CompoundTag
2021-02-13 23:24:36 +00:00
import net.minecraft.util.Tickable
2019-10-27 01:36:31 +00:00
import net.shadowfacts.phycon.api.PacketSink
import net.shadowfacts.phycon.api.PacketSource
2021-02-13 23:24:36 +00:00
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.api.frame.EthernetFrame
import net.shadowfacts.phycon.api.frame.PacketFrame
2019-10-27 01:36:31 +00:00
import net.shadowfacts.phycon.api.packet.Packet
2021-02-13 23:24:36 +00:00
import net.shadowfacts.phycon.api.util.IPAddress
2019-10-27 01:36:31 +00:00
import net.shadowfacts.phycon.api.util.MACAddress
2021-02-13 23:24:36 +00:00
import net.shadowfacts.phycon.network.frame.ARPQueryFrame
import net.shadowfacts.phycon.network.frame.ARPResponseFrame
import net.shadowfacts.phycon.network.frame.BasePacketFrame
import net.shadowfacts.phycon.network.packet.DeviceRemovedPacket
2021-02-13 23:24:36 +00:00
import java.lang.RuntimeException
import java.util.*
2019-10-27 01:36:31 +00:00
/**
* @author shadowfacts
*/
2021-02-13 23:24:36 +00:00
abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
BlockEntityClientSerializable,
Tickable,
PacketSink,
PacketSource,
InterfaceDelegate {
companion object {
private const val ARP_RETRY_TIMEOUT = 200
}
// var macAddress: MACAddress = MACAddress.random()
// protected set
2019-10-27 01:36:31 +00:00
2021-02-13 23:24:36 +00:00
var ipAddress: IPAddress = IPAddress.random()
2019-10-27 01:36:31 +00:00
protected set
2021-02-13 23:24:36 +00:00
open val interfaces: List<BaseInterface> = listOf()
// abstract val itf: BaseInterface
private val arpTable = mutableMapOf<IPAddress, MACAddress>()
private val packetQueue = LinkedList<PendingPacket>()
protected var counter: Long = 0
2019-10-27 03:13:26 +00:00
2021-02-13 23:24:36 +00:00
override fun getIPAddress() = ipAddress
override fun getDeviceInterfaces() = interfaces
abstract override fun handle(packet: Packet, itf: Interface)
override fun handle(frame: EthernetFrame, fromItf: Interface) {
println("$this ($ipAddress, ${fromItf.macAddress}) received frame from ${frame.source}: $frame")
when (frame) {
is ARPQueryFrame -> handleARPQuery(frame, fromItf)
is ARPResponseFrame -> handleARPResponse(frame, fromItf)
is PacketFrame -> {
if (frame.packet.destination.isBroadcast || frame.packet.destination == ipAddress) {
handle(frame.packet, fromItf)
}
}
2019-10-27 01:36:31 +00:00
}
}
2021-02-13 23:24:36 +00:00
private fun handleARPQuery(frame: ARPQueryFrame, fromItf: Interface) {
println("$this ($ipAddress) received ARP query for ${frame.queryIP}")
arpTable[frame.sourceIP] = frame.source
if (frame.queryIP == ipAddress) {
println("$this ($ipAddress) sending ARP response to ${frame.source} with ${fromItf.macAddress}")
fromItf.send(ARPResponseFrame(ipAddress, fromItf.macAddress, frame.source))
2019-10-27 01:36:31 +00:00
}
}
2021-02-13 23:24:36 +00:00
private fun handleARPResponse(frame: ARPResponseFrame, fromItf: Interface) {
arpTable[frame.query] = frame.source
println("$this ($ipAddress) received ARP response for ${frame.query} with ${frame.source}")
packetQueue.removeIf { (packet, itf, _) ->
if (packet.destination == frame.query) {
itf.send(BasePacketFrame(packet, itf.macAddress, frame.source))
true
} else {
false
}
}
2019-10-27 01:36:31 +00:00
}
2021-02-13 23:24:36 +00:00
// protected abstract fun handlePacket(packet: Packet, itf: Interface)
//
// protected open fun acceptsPacket(packet: Packet, itf: Interface): Boolean {
// return when (packet.destination.type) {
// MACAddress.Type.BROADCAST -> true
// MACAddress.Type.UNICAST -> itf.macAddress == packet.destination
// MACAddress.Type.MULTICAST -> acceptsMulticastPacket(packet)
// }
// }
// open fun acceptsMulticastPacket(packet: Packet): Boolean {
// return false
// }
fun sendPacket(packet: Packet) {
sendPacket(packet, null)
}
2021-02-13 23:24:36 +00:00
override fun sendPacket(packet: Packet, itf: Interface?) {
@Suppress("NAME_SHADOWING") var itf = itf
if (itf == null) {
if (interfaces.size == 1) {
itf = interfaces.first()
} else {
throw RuntimeException("Cannot send packet from device with multiple interfaces without explicitly specifying interface")
}
}
val cached = arpTable[packet.destination]
if (cached != null) {
itf.send(BasePacketFrame(packet, itf.macAddress, cached))
} else {
// packetQueue.add(packet to itf)
packetQueue.add(PendingPacket(packet, itf, counter))
println("$this ($ipAddress) sending ARP query for ${packet.destination}")
itf.send(ARPQueryFrame(packet.destination, ipAddress, itf.macAddress))
// after sending an ARP query we expect to have received a response and added an entry to our ARP table
// todo: this makes the assumption that packets are sent the entire way synchronously, and then a response
// is immediately sent and forwarded also synchronously
// cached = arpTable[packet.destination]
}
2019-10-27 03:13:26 +00:00
}
2021-02-13 23:24:36 +00:00
// fun findMACAddressFor(ipAddress: IPAddress): MACAddress? {
// if (arpTable.containsKey(ipAddress)) {
// return arpTable[ipAddress]
// } else {
//
// }
// }
// override fun sendToSingle(packet: Packet) {
// val destinations = NetworkUtil.findDestinations(world!!, pos)
// if (destinations.size != 1) {
// // todo: handle this better
// println("Can't send packet, multiple destinations available: $destinations")
// return
// }
// send(packet))
// }
// override fun sendToAll(packet: Packet) {
// sendToAll(packet, NetworkUtil.findDestinations(world!!, pos))
// }
//
// override fun sendToAll(packet: Packet, destinations: Iterable<PacketSink>) {
// destinations.forEach {
// it.handle(packet)
// }
// }
override fun findDestination(fromItf: Interface): Interface? {
val sides = (cachedState.block as NetworkComponentBlock).getNetworkConnectedSides(cachedState, world!!, pos)
if (sides.size != 1) {
throw RuntimeException("DeviceBlockEntity.findDestination must be overridden by devices which have more than 1 network connected side")
}
return NetworkUtil.findConnectedInterface(world!!, pos, sides.first())
2019-10-27 01:36:31 +00:00
}
2021-02-13 23:24:36 +00:00
override fun tick() {
if (!world!!.isClient) {
counter++
packetQueue.removeIf { entry ->
val (packet, itf, timestamp) = entry
if (arpTable.containsKey(packet.destination)) {
itf.send(BasePacketFrame(packet, itf.macAddress, arpTable[packet.destination]!!))
true
} else if (counter - timestamp >= ARP_RETRY_TIMEOUT) {
itf.send(ARPQueryFrame(packet.destination, ipAddress, itf.macAddress))
entry.timestamp = counter
// todo: should there be a retry counter?
true
} else {
false
}
}
2019-10-27 01:36:31 +00:00
}
}
override fun toTag(tag: CompoundTag): CompoundTag {
2021-02-13 23:24:36 +00:00
tag.putInt("IPAddress", ipAddress.address)
// tag.putLong("MACAddress", macAddress.address)
tag.putLongArray("InterfaceAddresses", interfaces.map { it.macAddress.address })
2019-10-27 01:36:31 +00:00
return super.toTag(tag)
}
2021-02-10 23:55:49 +00:00
override fun fromTag(state: BlockState, tag: CompoundTag) {
super.fromTag(state, tag)
2021-02-13 23:24:36 +00:00
ipAddress = IPAddress(tag.getInt("IPAddress"))
// todo: what happens if the defined number of ports changes between mod versions?
tag.getLongArray("InterfaceAddresses")?.forEachIndexed { i, l ->
interfaces[i].macAddress = MACAddress(l)
}
}
override fun toClientTag(tag: CompoundTag): CompoundTag {
tag.putInt("IPAddress", ipAddress.address)
tag.putLongArray("InterfaceAddresses", interfaces.map { it.macAddress.address })
return tag
}
override fun fromClientTag(tag: CompoundTag) {
ipAddress = IPAddress(tag.getInt("IPAddress"))
tag.getLongArray("InterfaceAddresses")?.forEachIndexed { i, l ->
interfaces[i].macAddress = MACAddress(l)
}
2019-10-27 01:36:31 +00:00
}
fun onBreak() {
2021-02-13 23:24:36 +00:00
sendPacket(DeviceRemovedPacket(this))
}
2021-02-13 23:24:36 +00:00
data class PendingPacket(
val packet: Packet,
val sourceItf: Interface,
var timestamp: Long,
)
2019-10-27 01:36:31 +00:00
}