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
|
2021-02-12 04:11:50 +00:00
|
|
|
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
|
2019-10-28 21:48:10 +00:00
|
|
|
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)
|
2019-10-27 02:09:16 +00:00
|
|
|
}
|
|
|
|
|
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 02:09:16 +00:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2019-10-28 21:48:10 +00:00
|
|
|
fun onBreak() {
|
2021-02-13 23:24:36 +00:00
|
|
|
sendPacket(DeviceRemovedPacket(this))
|
2019-10-28 21:48:10 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|