Compare commits

...

8 Commits

Author SHA1 Message Date
Shadowfacts a95621e3f1
Add switches 2019-10-26 23:13:26 -04:00
Shadowfacts 0908ccbb3a
Fix incorrect MAC address multicast check 2019-10-26 23:13:14 -04:00
Shadowfacts d3c1cbdb5b
Rename things 2019-10-26 22:14:19 -04:00
Shadowfacts 6e87092dde
Remove Interface right-click printing 2019-10-26 22:10:48 -04:00
Shadowfacts a8b53e1117
Explicitly specify packet destinations instead of broadcasting to all
connected sinks
2019-10-26 22:09:16 -04:00
Shadowfacts fa7c499f29
Fix Interfaces not reading inventory contents immediately on world load
This happened because the after a world load, there was no neighbor
change to trigger an inventory update.
2019-10-26 22:06:00 -04:00
Shadowfacts 19c035495c
Fix stacks of same type in different inventories not grouping 2019-10-26 21:52:41 -04:00
Shadowfacts 6fae09d410
Cleanup NetworkUtil 2019-10-26 21:52:38 -04:00
12 changed files with 177 additions and 56 deletions

View File

@ -1,6 +1,7 @@
package net.shadowfacts.phycon.api; package net.shadowfacts.phycon.api;
import net.shadowfacts.phycon.api.packet.Packet; import net.shadowfacts.phycon.api.packet.Packet;
import net.shadowfacts.phycon.api.util.MACAddress;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@ -8,6 +9,9 @@ import org.jetbrains.annotations.NotNull;
*/ */
public interface PacketSink { public interface PacketSink {
@NotNull
MACAddress getMACAddress();
void handle(@NotNull Packet packet); void handle(@NotNull Packet packet);
} }

View File

@ -48,7 +48,7 @@ public final class MACAddress {
public Type getType() { public Type getType() {
if (this == BROADCAST) { if (this == BROADCAST) {
return Type.BROADCAST; return Type.BROADCAST;
} else if (((address >> 46) & 1) == 1) { } else if (((address >> 40) & 1) == 1) {
return Type.MULTICAST; return Type.MULTICAST;
} else { } else {
return Type.UNICAST; return Type.UNICAST;

View File

@ -5,8 +5,10 @@ import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityType import net.minecraft.block.entity.BlockEntityType
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.network.block.networkinterface.NetworkInterfaceBlock import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.networkinterface.NetworkInterfaceBlockEntity import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlockEntity
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity
@ -15,16 +17,18 @@ import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity
*/ */
object PhyBlockEntities { object PhyBlockEntities {
val NETWORK_INTERFACE = create(::NetworkInterfaceBlockEntity, PhyBlocks.NETWORK_INTERFACE) val INTERFACE = create(::InterfaceBlockEntity, PhyBlocks.INTERFACE)
val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL) val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL)
val SWITCH = create(::SwitchBlockEntity, PhyBlocks.SWITCH)
private fun <T: BlockEntity> create(builder: () -> T, block: Block): BlockEntityType<T> { private fun <T: BlockEntity> create(builder: () -> T, block: Block): BlockEntityType<T> {
return BlockEntityType.Builder.create(builder, arrayOf(block)).build(null) return BlockEntityType.Builder.create(builder, arrayOf(block)).build(null)
} }
fun init() { fun init() {
register(NetworkInterfaceBlock.ID, NETWORK_INTERFACE) register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL) register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH)
} }
private fun register(id: Identifier, type: BlockEntityType<*>) { private fun register(id: Identifier, type: BlockEntityType<*>) {

View File

@ -3,7 +3,8 @@ package net.shadowfacts.phycon.init
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.network.block.networkinterface.NetworkInterfaceBlock import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
/** /**
@ -11,12 +12,14 @@ import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
*/ */
object PhyBlocks { object PhyBlocks {
val NETWORK_INTERFACE = NetworkInterfaceBlock() val INTERFACE = InterfaceBlock()
val TERMINAL = TerminalBlock() val TERMINAL = TerminalBlock()
val SWITCH = SwitchBlock()
fun init() { fun init() {
register(NetworkInterfaceBlock.ID, NETWORK_INTERFACE) register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL) register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH)
} }
private fun register(id: Identifier, block: Block) { private fun register(id: Identifier, block: Block) {

View File

@ -4,7 +4,8 @@ import net.minecraft.item.BlockItem
import net.minecraft.item.Item import net.minecraft.item.Item
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.network.block.networkinterface.NetworkInterfaceBlock import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
/** /**
@ -12,12 +13,14 @@ import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
*/ */
object PhyItems { object PhyItems {
val NETWORK_INTERFACE = BlockItem(PhyBlocks.NETWORK_INTERFACE, Item.Settings()) val INTERFACE = BlockItem(PhyBlocks.INTERFACE, Item.Settings())
val TERMINAL = BlockItem(PhyBlocks.TERMINAL, Item.Settings()) val TERMINAL = BlockItem(PhyBlocks.TERMINAL, Item.Settings())
val SWITCH = BlockItem(PhyBlocks.SWITCH, Item.Settings())
fun init() { fun init() {
register(NetworkInterfaceBlock.ID, NETWORK_INTERFACE) register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL) register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH)
} }
private fun register(id: Identifier, item: Item) { private fun register(id: Identifier, item: Item) {

View File

@ -7,6 +7,7 @@ import net.minecraft.util.Tickable
import net.shadowfacts.phycon.api.PacketSink import net.shadowfacts.phycon.api.PacketSink
import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.MACAddress import net.shadowfacts.phycon.api.util.MACAddress
import java.lang.ref.WeakReference
import java.util.* import java.util.*
/** /**
@ -17,7 +18,11 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), T
var macAddress = MACAddress.random() var macAddress = MACAddress.random()
protected set protected set
private val sendQueue = LinkedList<Packet>() private val sendQueue = LinkedList<Pair<Packet, WeakReference<PacketSink>>>()
override fun getMACAddress(): MACAddress {
return macAddress
}
override fun handle(packet: Packet) { override fun handle(packet: Packet) {
if (acceptsPacket(packet)) { if (acceptsPacket(packet)) {
@ -25,7 +30,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), T
} }
} }
abstract fun handlePacket(packet: Packet) protected abstract fun handlePacket(packet: Packet)
fun acceptsPacket(packet: Packet): Boolean { fun acceptsPacket(packet: Packet): Boolean {
return when (packet.destination.type) { return when (packet.destination.type) {
@ -35,21 +40,37 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), T
} }
} }
fun acceptsMulticastPacket(packet: Packet): Boolean { open fun acceptsMulticastPacket(packet: Packet): Boolean {
return false return false
} }
fun enqueue(packet: Packet) { fun enqueue(packet: Packet, destination: PacketSink) {
sendQueue.add(packet) sendQueue.add(packet to WeakReference(destination))
}
// todo: better name for this
fun enqueueToSingle(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
}
enqueue(packet, destinations.first())
}
fun enqueueToAll(packet: Packet) {
enqueueToAll(packet, NetworkUtil.findDestinations(world!!, pos))
}
fun enqueueToAll(packet: Packet, destinations: Iterable<PacketSink>) {
sendQueue.addAll(destinations.map { packet to WeakReference(it) })
} }
override fun tick() { override fun tick() {
if (sendQueue.isNotEmpty()) { if (sendQueue.isNotEmpty()) {
val packet = sendQueue.pop() val (packet, destination) = sendQueue.pop()
val destinations = NetworkUtil.findDestinations(world!!, pos) destination.get()?.handle(packet)
destinations.forEach {
it.handle(packet)
}
} }
} }

View File

@ -13,26 +13,35 @@ import java.util.*
*/ */
object NetworkUtil { object NetworkUtil {
fun findDestinations(world: World, startPos: BlockPos): List<PacketSink> { fun findDestinations(world: World, startPos: BlockPos, direction: Direction? = null): List<PacketSink> {
val results = mutableListOf<PacketSink>() val results = LinkedList<PacketSink>()
val visited = hashSetOf(startPos) val visited = hashSetOf(startPos)
val queue = LinkedList<BlockPos>() val queue = LinkedList<BlockPos>()
addAdjacent(queue, visited, startPos) if (direction != null) {
queue.add(startPos.offset(direction))
} else {
addAdjacent(queue, visited, startPos)
}
while (queue.isNotEmpty()) { while (queue.isNotEmpty()) {
val pos = queue.pop() val pos = queue.pop()
val sink = PhyAttributes.PACKET_SINK.getFirstOrNull(world, pos) val sink = PhyAttributes.PACKET_SINK.getFirstOrNull(world, pos)
if (sink != null) { if (sink != null) {
results.add(sink) results.add(sink)
} }
if (pos === startPos || world.getBlockState(pos).block is NetworkCable) {
if (world.getBlockState(pos).block is NetworkCable) {
addAdjacent(queue, visited, pos) addAdjacent(queue, visited, pos)
} }
visited.add(pos) visited.add(pos)
} }
return results return results
} }
fun addAdjacent(queue: MutableList<BlockPos>, visited: Set<BlockPos>, pos: BlockPos) { private fun addAdjacent(queue: MutableList<BlockPos>, visited: Set<BlockPos>, pos: BlockPos) {
for (dir in Direction.values()) { for (dir in Direction.values()) {
val newPos = pos.offset(dir) val newPos = pos.offset(dir)
if (newPos !in visited) { if (newPos !in visited) {

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.block.networkinterface package net.shadowfacts.phycon.network.block.netinterface
import alexiil.mc.lib.attributes.AttributeList import alexiil.mc.lib.attributes.AttributeList
import alexiil.mc.lib.attributes.AttributeProvider import alexiil.mc.lib.attributes.AttributeProvider
@ -6,14 +6,11 @@ import net.minecraft.block.Block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.Material import net.minecraft.block.Material
import net.minecraft.entity.LivingEntity import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.state.StateFactory import net.minecraft.state.StateFactory
import net.minecraft.state.property.Properties import net.minecraft.state.property.Properties
import net.minecraft.util.Hand
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.BlockView import net.minecraft.world.BlockView
import net.minecraft.world.World import net.minecraft.world.World
@ -23,7 +20,7 @@ import net.shadowfacts.phycon.block.BlockWithEntity
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class NetworkInterfaceBlock: BlockWithEntity<NetworkInterfaceBlockEntity>(Settings.of(Material.METAL)), AttributeProvider { class InterfaceBlock: BlockWithEntity<InterfaceBlockEntity>(Settings.of(Material.METAL)), AttributeProvider {
companion object { companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "network_interface") val ID = Identifier(PhysicalConnectivity.MODID, "network_interface")
val FACING = Properties.FACING val FACING = Properties.FACING
@ -34,7 +31,7 @@ class NetworkInterfaceBlock: BlockWithEntity<NetworkInterfaceBlockEntity>(Settin
builder.add(FACING) builder.add(FACING)
} }
override fun createBlockEntity(world: BlockView) = NetworkInterfaceBlockEntity() override fun createBlockEntity(world: BlockView) = InterfaceBlockEntity()
override fun getPlacementState(context: ItemPlacementContext): BlockState { override fun getPlacementState(context: ItemPlacementContext): BlockState {
val facing = if (context.player?.isSneaking == true) context.playerFacing else context.playerFacing.opposite val facing = if (context.player?.isSneaking == true) context.playerFacing else context.playerFacing.opposite
@ -53,13 +50,6 @@ class NetworkInterfaceBlock: BlockWithEntity<NetworkInterfaceBlockEntity>(Settin
} }
} }
override fun activate(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, result: BlockHitResult): Boolean {
if (!world.isClient) {
getBlockEntity(world, pos)!!.printContents()
}
return true
}
override fun addAllAttributes(world: World, pos: BlockPos, state: BlockState, to: AttributeList<*>) { override fun addAllAttributes(world: World, pos: BlockPos, state: BlockState, to: AttributeList<*>) {
to.offer(getBlockEntity(world, pos)) to.offer(getBlockEntity(world, pos))
} }

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.block.networkinterface package net.shadowfacts.phycon.network.block.netinterface
import alexiil.mc.lib.attributes.SearchOptions import alexiil.mc.lib.attributes.SearchOptions
import alexiil.mc.lib.attributes.item.GroupedItemInv import alexiil.mc.lib.attributes.item.GroupedItemInv
@ -14,11 +14,12 @@ import net.shadowfacts.phycon.network.packet.RequestReadAllPacket
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class NetworkInterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.NETWORK_INTERFACE) { class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE) {
private val facing: Direction private val facing: Direction
get() = world!!.getBlockState(pos)[NetworkInterfaceBlock.FACING] get() = world!!.getBlockState(pos)[InterfaceBlock.FACING]
// todo: should this be a weak ref?
private var inventory: GroupedItemInv? = null private var inventory: GroupedItemInv? = null
fun updateInventory() { fun updateInventory() {
@ -27,14 +28,6 @@ class NetworkInterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.NETWORK_IN
inventory = ItemAttributes.GROUPED_INV.getFirstOrNull(world, offsetPos, option) inventory = ItemAttributes.GROUPED_INV.getFirstOrNull(world, offsetPos, option)
} }
fun printContents() {
inventory?.also { inv ->
inv.storedStacks.forEach {
println(it)
}
}
}
override fun handlePacket(packet: Packet) { override fun handlePacket(packet: Packet) {
when (packet) { when (packet) {
is RequestReadAllPacket -> handleReadAll(packet) is RequestReadAllPacket -> handleReadAll(packet)
@ -42,10 +35,14 @@ class NetworkInterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.NETWORK_IN
} }
fun handleReadAll(packet: RequestReadAllPacket) { fun handleReadAll(packet: RequestReadAllPacket) {
enqueue(ReadAllPacket(readAll(), macAddress, packet.source)) enqueueToSingle(ReadAllPacket(readAll(), macAddress, packet.source))
} }
fun readAll(): Map<ItemStack, Int> { fun readAll(): Map<ItemStack, Int> {
// if we don't have an inventory, try to get one
// this happens when readAll is called before a neighbor state changes, such as immediately after world load
if (inventory == null) updateInventory()
return inventory?.let { return inventory?.let {
it.storedStacks.associateWith(it::getAmount) it.storedStacks.associateWith(it::getAmount)
} ?: mapOf() } ?: mapOf()

View File

@ -0,0 +1,27 @@
package net.shadowfacts.phycon.network.block.netswitch
import alexiil.mc.lib.attributes.AttributeList
import alexiil.mc.lib.attributes.AttributeProvider
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.block.BlockWithEntity
/**
* @author shadowfacts
*/
class SwitchBlock: BlockWithEntity<SwitchBlockEntity>(Settings.of(Material.METAL)), AttributeProvider {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "switch")
}
override fun createBlockEntity(world: BlockView) = SwitchBlockEntity()
override fun addAllAttributes(world: World, pos: BlockPos, state: BlockState, to: AttributeList<*>) {
to.offer(getBlockEntity(world, pos))
}
}

View File

@ -0,0 +1,62 @@
package net.shadowfacts.phycon.network.block.netswitch
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.PacketSink
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.MACAddress
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.NetworkUtil
import java.lang.RuntimeException
import javax.print.attribute.standard.Destination
/**
* @author shadowfacts
*/
class SwitchBlockEntity: DeviceBlockEntity(PhyBlockEntities.SWITCH) {
private val macTable = mutableMapOf<MACAddress, Direction>()
override fun handlePacket(packet: Packet) {
throw RuntimeException("Unreachable")
}
override fun handle(packet: Packet) {
if (packet.destination == MACAddress.BROADCAST) {
val allDestinations = NetworkUtil.findDestinations(world!!, pos).filter { it.macAddress != packet.source }
enqueueToAll(packet, allDestinations)
} else {
val direction = macTable[packet.destination]
if (direction != null) {
val dest = findDestination(direction)
if (dest != null && packet.destination == dest.macAddress) {
enqueue(packet, dest)
return
}
}
flood(packet)
}
}
private fun findDestination(direction: Direction): PacketSink? {
val allDestinations = NetworkUtil.findDestinations(world!!, pos, direction)
if (allDestinations.size > 1) {
// todo: do this better
println("Can't send packet, multiple destinations: $allDestinations")
return null
}
return allDestinations.firstOrNull()
}
private fun flood(packet: Packet) {
for (dir in Direction.values()) {
val dest = findDestination(dir)
if (dest != null && packet.destination == dest.macAddress) {
macTable[packet.destination] = dir
enqueue(packet, dest)
break
}
}
}
}

View File

@ -1,5 +1,6 @@
package net.shadowfacts.phycon.network.block.terminal package net.shadowfacts.phycon.network.block.terminal
import alexiil.mc.lib.attributes.item.ItemStackCollections
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
@ -12,7 +13,7 @@ import net.shadowfacts.phycon.network.packet.RequestReadAllPacket
*/ */
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL) { class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL) {
private var cachedItems = mutableMapOf<ItemStack, Int>() private var cachedItems = ItemStackCollections.intMap()
override fun handlePacket(packet: Packet) { override fun handlePacket(packet: Packet) {
when (packet) { when (packet) {
@ -22,7 +23,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL) {
fun handleReadAll(packet: ReadAllPacket) { fun handleReadAll(packet: ReadAllPacket) {
packet.items.forEach { (stack, amount) -> packet.items.forEach { (stack, amount) ->
cachedItems.merge(stack, amount) { a, b -> a + b } cachedItems.mergeInt(stack, amount) { a, b -> a + b }
} }
println("new cached items: $cachedItems") println("new cached items: $cachedItems")
} }
@ -30,7 +31,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL) {
fun onActivate() { fun onActivate() {
if (!world!!.isClient) { if (!world!!.isClient) {
cachedItems.clear() cachedItems.clear()
enqueue(RequestReadAllPacket(macAddress)) enqueueToSingle(RequestReadAllPacket(macAddress))
} }
} }