Refactor DeviceBlockEntity to only have 1 interface

This commit is contained in:
Shadowfacts 2021-02-13 21:28:44 -05:00
parent e8425b80fd
commit fc3716153f
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
11 changed files with 61 additions and 204 deletions

View File

@ -1,7 +1,6 @@
package net.shadowfacts.phycon.api;
import net.shadowfacts.phycon.api.frame.EthernetFrame;
import net.shadowfacts.phycon.api.packet.Packet;
import net.shadowfacts.phycon.api.util.MACAddress;
import org.jetbrains.annotations.NotNull;
@ -17,6 +16,4 @@ public interface Interface {
void send(@NotNull EthernetFrame frame);
// void send(@NotNull Packet packet);
}

View File

@ -8,9 +8,6 @@ import org.jetbrains.annotations.NotNull;
*/
public interface PacketSink extends NetworkDevice {
@NotNull
Iterable<Interface> getDeviceInterfaces();
void handle(@NotNull Packet packet, @NotNull Interface itf);
void handle(@NotNull Packet packet);
}

View File

@ -10,7 +10,7 @@ import org.jetbrains.annotations.Nullable;
// todo: does PacketSource actually need to extend NetworkDevice?
public interface PacketSource extends NetworkDevice {
// todo: better name for this
void sendPacket(@NotNull Packet packet, @Nullable Interface itf);
void sendPacket(@NotNull Packet packet);
// void sendToAll(@NotNull Packet packet);
//

View File

@ -1,53 +0,0 @@
package net.shadowfacts.phycon.network
import net.shadowfacts.phycon.api.frame.EthernetFrame
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.PacketSink
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.MACAddress
import net.shadowfacts.phycon.network.frame.BaseFrame
import java.lang.ref.WeakReference
/**
* @author shadowfacts
*/
open class BaseInterface(
var macAddress: MACAddress,
private val delegate: WeakReference<InterfaceDelegate>
): Interface {
constructor(delegate: InterfaceDelegate): this(MACAddress.random(), WeakReference(delegate))
override fun getMACAddress(): MACAddress {
return macAddress
}
override fun receive(frame: EthernetFrame) {
delegate.get()?.handle(frame, this)
}
override fun send(frame: EthernetFrame) {
delegate.get()?.findDestination(this)?.receive(frame)
}
// override fun send(packet: Packet) {
// delegate.get()?.findDestination(this)?.also {
// it.receive(BaseFrame(packet, this.macAddress, it.macAddress))
// }
// }
override fun equals(other: Any?): Boolean {
return (other as? BaseInterface)?.macAddress == macAddress
}
override fun hashCode(): Int {
return macAddress.hashCode()
}
}
interface InterfaceDelegate {
fun findDestination(fromItf: Interface): Interface?
fun handle(frame: EthernetFrame, fromItf: Interface)
}

View File

@ -3,7 +3,9 @@ package net.shadowfacts.phycon.network
import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.World
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.block.BlockWithEntity
@ -12,6 +14,10 @@ import net.shadowfacts.phycon.block.BlockWithEntity
*/
abstract class DeviceBlock<T: DeviceBlockEntity>(settings: Settings): BlockWithEntity<T>(settings), NetworkComponentBlock {
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: World, pos: BlockPos): Interface? {
return getBlockEntity(world, pos)!!
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
super.onBreak(world, pos, state, player)
getBlockEntity(world, pos)!!.onBreak()

View File

@ -30,61 +30,61 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
Tickable,
PacketSink,
PacketSource,
InterfaceDelegate {
Interface {
companion object {
private const val ARP_RETRY_TIMEOUT = 200
}
// var macAddress: MACAddress = MACAddress.random()
// protected set
var macAddress: MACAddress = MACAddress.random()
protected set
var ipAddress: IPAddress = IPAddress.random()
protected set
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
override fun getIPAddress() = ipAddress
override fun getMACAddress() = macAddress
override fun getDeviceInterfaces() = interfaces
abstract override fun handle(packet: Packet)
abstract override fun handle(packet: Packet, itf: Interface)
override fun send(frame: EthernetFrame) {
findDestination()?.receive(frame)
}
override fun handle(frame: EthernetFrame, fromItf: Interface) {
println("$this ($ipAddress, ${fromItf.macAddress}) received frame from ${frame.source}: $frame")
override fun receive(frame: EthernetFrame) {
println("$this ($ipAddress, ${macAddress}) received frame from ${frame.source}: $frame")
when (frame) {
is ARPQueryFrame -> handleARPQuery(frame, fromItf)
is ARPResponseFrame -> handleARPResponse(frame, fromItf)
is ARPQueryFrame -> handleARPQuery(frame)
is ARPResponseFrame -> handleARPResponse(frame)
is PacketFrame -> {
if (frame.packet.destination.isBroadcast || frame.packet.destination == ipAddress) {
handle(frame.packet, fromItf)
handle(frame.packet)
}
}
}
}
private fun handleARPQuery(frame: ARPQueryFrame, fromItf: Interface) {
private fun handleARPQuery(frame: ARPQueryFrame) {
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))
println("$this ($ipAddress) sending ARP response to ${frame.source} with $macAddress")
send(ARPResponseFrame(ipAddress, macAddress, frame.source))
}
}
private fun handleARPResponse(frame: ARPResponseFrame, fromItf: Interface) {
private fun handleARPResponse(frame: ARPResponseFrame) {
arpTable[frame.query] = frame.source
println("$this ($ipAddress) received ARP response for ${frame.query} with ${frame.source}")
packetQueue.removeIf { (packet, itf, _) ->
packetQueue.removeIf { (packet, _) ->
if (packet.destination == frame.query) {
itf.send(BasePacketFrame(packet, itf.macAddress, frame.source))
send(BasePacketFrame(packet, macAddress, frame.source))
true
} else {
false
@ -92,79 +92,19 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
}
}
// 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)
}
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")
}
}
override fun sendPacket(packet: Packet) {
val cached = arpTable[packet.destination]
if (cached != null) {
itf.send(BasePacketFrame(packet, itf.macAddress, cached))
send(BasePacketFrame(packet, macAddress, cached))
} else {
// packetQueue.add(packet to itf)
packetQueue.add(PendingPacket(packet, itf, counter))
packetQueue.add(PendingPacket(packet, 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]
send(ARPQueryFrame(packet.destination, ipAddress, macAddress))
}
}
// 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? {
open fun findDestination(): 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")
@ -177,12 +117,12 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
counter++
packetQueue.removeIf { entry ->
val (packet, itf, timestamp) = entry
val (packet, timestamp) = entry
if (arpTable.containsKey(packet.destination)) {
itf.send(BasePacketFrame(packet, itf.macAddress, arpTable[packet.destination]!!))
send(BasePacketFrame(packet, macAddress, arpTable[packet.destination]!!))
true
} else if (counter - timestamp >= ARP_RETRY_TIMEOUT) {
itf.send(ARPQueryFrame(packet.destination, ipAddress, itf.macAddress))
send(ARPQueryFrame(packet.destination, ipAddress, macAddress))
entry.timestamp = counter
// todo: should there be a retry counter?
true
@ -195,31 +135,25 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
override fun toTag(tag: CompoundTag): CompoundTag {
tag.putInt("IPAddress", ipAddress.address)
// tag.putLong("MACAddress", macAddress.address)
tag.putLongArray("InterfaceAddresses", interfaces.map { it.macAddress.address })
tag.putLong("MACAddress", macAddress.address)
return super.toTag(tag)
}
override fun fromTag(state: BlockState, tag: CompoundTag) {
super.fromTag(state, tag)
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)
}
macAddress = MACAddress(tag.getLong("MACAddress"))
}
override fun toClientTag(tag: CompoundTag): CompoundTag {
tag.putInt("IPAddress", ipAddress.address)
tag.putLongArray("InterfaceAddresses", interfaces.map { it.macAddress.address })
tag.putLong("MACAddress", 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)
}
macAddress = MACAddress(tag.getLong("MACAddress"))
}
fun onBreak() {
@ -228,7 +162,6 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
data class PendingPacket(
val packet: Packet,
val sourceItf: Interface,
var timestamp: Long,
)

View File

@ -8,27 +8,23 @@ import net.minecraft.util.Tickable
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.frame.EthernetFrame
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.api.util.MACAddress
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.BaseInterface
import net.shadowfacts.phycon.network.InterfaceDelegate
import net.shadowfacts.phycon.network.NetworkUtil
import java.lang.ref.WeakReference
/**
* @author shadowfacts
*/
class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
BlockEntityClientSerializable,
InterfaceDelegate,
Tickable {
companion object {
var SWITCHING_CAPACITY = 256
}
val interfaces = Direction.values().map { SwitchInterface(it, this) }
val interfaces = Direction.values().map { SwitchInterface(it, WeakReference(this), MACAddress.random()) }
private val macTable = mutableMapOf<MACAddress, Direction>()
private var packetsHandledThisTick = 0
@ -69,7 +65,7 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
return interfaces.find { it.side == side }!!
}
override fun handle(frame: EthernetFrame, fromItf: Interface) {
private fun handle(frame: EthernetFrame, fromItf: Interface) {
val itfSide = (fromItf as SwitchInterface).side
macTable[frame.source] = itfSide
@ -90,7 +86,7 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
}
}
override fun findDestination(fromItf: Interface): Interface? {
private fun findDestination(fromItf: Interface): Interface? {
val side = (fromItf as SwitchInterface).side
return NetworkUtil.findConnectedInterface(world!!, pos, side)
}
@ -122,8 +118,20 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
}
}
class SwitchInterface(val side: Direction, delegate: InterfaceDelegate): BaseInterface(delegate) {
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)
}
}
}

View File

@ -12,7 +12,6 @@ import net.minecraft.util.math.Direction
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.network.DeviceBlock
import java.util.*
@ -33,10 +32,6 @@ class DestBlock: DeviceBlock<DestBlockEntity>(Settings.of(Material.METAL)) {
return EnumSet.allOf(Direction::class.java)
}
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: World, pos: BlockPos): Interface? {
return getBlockEntity(world, pos)!!.interfaces.first()
}
override fun onUse(
blockState: BlockState?,
world: World,

View File

@ -2,10 +2,8 @@ package net.shadowfacts.phycon.network.block.test
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.frame.EthernetFrame
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.BaseInterface
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.NetworkUtil
@ -14,18 +12,11 @@ import net.shadowfacts.phycon.network.NetworkUtil
*/
class DestBlockEntity: DeviceBlockEntity(PhyBlockEntities.DEST) {
override val interfaces = listOf(BaseInterface(this))
override fun handle(packet: Packet, itf: Interface) {
override fun handle(packet: Packet) {
println("$this ($ipAddress) received packet: $packet")
}
override fun handle(frame: EthernetFrame, fromItf: Interface) {
// println("dest ${fromItf.macAddress} received frame from ${frame.source}")
super.handle(frame, fromItf)
}
override fun findDestination(fromItf: Interface): Interface? {
override fun findDestination(): Interface? {
for (dir in Direction.values()) {
val itf = NetworkUtil.findConnectedInterface(world!!, pos, dir)
if (itf != null) {

View File

@ -8,7 +8,6 @@ import net.minecraft.util.math.Direction
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.network.DeviceBlock
import java.util.*
@ -29,8 +28,4 @@ class SourceBlock: DeviceBlock<SourceBlockEntity>(Settings.of(Material.METAL)) {
return EnumSet.allOf(Direction::class.java)
}
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: World, pos: BlockPos): Interface? {
return getBlockEntity(world, pos)!!.interfaces.first()
}
}

View File

@ -3,41 +3,29 @@ package net.shadowfacts.phycon.network.block.test
import net.minecraft.util.Tickable
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.frame.EthernetFrame
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.BaseInterface
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.NetworkUtil
import net.shadowfacts.phycon.network.frame.BaseFrame
/**
* @author shadowfacts
*/
class SourceBlockEntity: DeviceBlockEntity(PhyBlockEntities.SOURCE), Tickable {
override val interfaces = listOf(BaseInterface(this))
override fun handle(packet: Packet, itf: Interface) {
override fun handle(packet: Packet) {
TODO("Not yet implemented")
}
override fun handle(frame: EthernetFrame, fromItf: Interface) {
// println("${fromItf.macAddress} received frame from ${frame.source}")
super.handle(frame, fromItf)
}
// var counter = 0
override fun tick() {
super.tick()
if (!world!!.isClient && counter % 40 == 0L) {
sendPacket(TestPacket(ipAddress, IPAddress(67, 237, 255, 168)))
sendPacket(TestPacket(ipAddress, IPAddress(170, 171, 101, 168)))
}
}
override fun findDestination(fromItf: Interface): Interface? {
override fun findDestination(): Interface? {
for (dir in Direction.values()) {
val itf = NetworkUtil.findConnectedInterface(world!!, pos, dir)
if (itf != null) {