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

View File

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

View File

@ -5,8 +5,10 @@ import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.network.block.networkinterface.NetworkInterfaceBlock
import net.shadowfacts.phycon.network.block.networkinterface.NetworkInterfaceBlockEntity
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
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.TerminalBlockEntity
@ -15,16 +17,18 @@ import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity
*/
object PhyBlockEntities {
val NETWORK_INTERFACE = create(::NetworkInterfaceBlockEntity, PhyBlocks.NETWORK_INTERFACE)
val INTERFACE = create(::InterfaceBlockEntity, PhyBlocks.INTERFACE)
val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL)
val SWITCH = create(::SwitchBlockEntity, PhyBlocks.SWITCH)
private fun <T: BlockEntity> create(builder: () -> T, block: Block): BlockEntityType<T> {
return BlockEntityType.Builder.create(builder, arrayOf(block)).build(null)
}
fun init() {
register(NetworkInterfaceBlock.ID, NETWORK_INTERFACE)
register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH)
}
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.util.Identifier
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
/**
@ -11,12 +12,14 @@ import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
*/
object PhyBlocks {
val NETWORK_INTERFACE = NetworkInterfaceBlock()
val INTERFACE = InterfaceBlock()
val TERMINAL = TerminalBlock()
val SWITCH = SwitchBlock()
fun init() {
register(NetworkInterfaceBlock.ID, NETWORK_INTERFACE)
register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH)
}
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.util.Identifier
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
/**
@ -12,12 +13,14 @@ import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
*/
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 SWITCH = BlockItem(PhyBlocks.SWITCH, Item.Settings())
fun init() {
register(NetworkInterfaceBlock.ID, NETWORK_INTERFACE)
register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH)
}
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.packet.Packet
import net.shadowfacts.phycon.api.util.MACAddress
import java.lang.ref.WeakReference
import java.util.*
/**
@ -17,7 +18,11 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), T
var macAddress = MACAddress.random()
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) {
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 {
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
}
fun enqueue(packet: Packet) {
sendQueue.add(packet)
fun enqueue(packet: Packet, destination: PacketSink) {
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() {
if (sendQueue.isNotEmpty()) {
val packet = sendQueue.pop()
val destinations = NetworkUtil.findDestinations(world!!, pos)
destinations.forEach {
it.handle(packet)
}
val (packet, destination) = sendQueue.pop()
destination.get()?.handle(packet)
}
}

View File

@ -13,26 +13,35 @@ import java.util.*
*/
object NetworkUtil {
fun findDestinations(world: World, startPos: BlockPos): List<PacketSink> {
val results = mutableListOf<PacketSink>()
fun findDestinations(world: World, startPos: BlockPos, direction: Direction? = null): List<PacketSink> {
val results = LinkedList<PacketSink>()
val visited = hashSetOf(startPos)
val queue = LinkedList<BlockPos>()
addAdjacent(queue, visited, startPos)
if (direction != null) {
queue.add(startPos.offset(direction))
} else {
addAdjacent(queue, visited, startPos)
}
while (queue.isNotEmpty()) {
val pos = queue.pop()
val sink = PhyAttributes.PACKET_SINK.getFirstOrNull(world, pos)
if (sink != null) {
results.add(sink)
}
if (pos === startPos || world.getBlockState(pos).block is NetworkCable) {
if (world.getBlockState(pos).block is NetworkCable) {
addAdjacent(queue, visited, pos)
}
visited.add(pos)
}
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()) {
val newPos = pos.offset(dir)
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.AttributeProvider
@ -6,14 +6,11 @@ import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.state.StateFactory
import net.minecraft.state.property.Properties
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.world.BlockView
import net.minecraft.world.World
@ -23,7 +20,7 @@ import net.shadowfacts.phycon.block.BlockWithEntity
/**
* @author shadowfacts
*/
class NetworkInterfaceBlock: BlockWithEntity<NetworkInterfaceBlockEntity>(Settings.of(Material.METAL)), AttributeProvider {
class InterfaceBlock: BlockWithEntity<InterfaceBlockEntity>(Settings.of(Material.METAL)), AttributeProvider {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "network_interface")
val FACING = Properties.FACING
@ -34,7 +31,7 @@ class NetworkInterfaceBlock: BlockWithEntity<NetworkInterfaceBlockEntity>(Settin
builder.add(FACING)
}
override fun createBlockEntity(world: BlockView) = NetworkInterfaceBlockEntity()
override fun createBlockEntity(world: BlockView) = InterfaceBlockEntity()
override fun getPlacementState(context: ItemPlacementContext): BlockState {
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<*>) {
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.item.GroupedItemInv
@ -14,11 +14,12 @@ import net.shadowfacts.phycon.network.packet.RequestReadAllPacket
/**
* @author shadowfacts
*/
class NetworkInterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.NETWORK_INTERFACE) {
class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE) {
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
fun updateInventory() {
@ -27,14 +28,6 @@ class NetworkInterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.NETWORK_IN
inventory = ItemAttributes.GROUPED_INV.getFirstOrNull(world, offsetPos, option)
}
fun printContents() {
inventory?.also { inv ->
inv.storedStacks.forEach {
println(it)
}
}
}
override fun handlePacket(packet: Packet) {
when (packet) {
is RequestReadAllPacket -> handleReadAll(packet)
@ -42,10 +35,14 @@ class NetworkInterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.NETWORK_IN
}
fun handleReadAll(packet: RequestReadAllPacket) {
enqueue(ReadAllPacket(readAll(), macAddress, packet.source))
enqueueToSingle(ReadAllPacket(readAll(), macAddress, packet.source))
}
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 {
it.storedStacks.associateWith(it::getAmount)
} ?: 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
import alexiil.mc.lib.attributes.item.ItemStackCollections
import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.init.PhyBlockEntities
@ -12,7 +13,7 @@ import net.shadowfacts.phycon.network.packet.RequestReadAllPacket
*/
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL) {
private var cachedItems = mutableMapOf<ItemStack, Int>()
private var cachedItems = ItemStackCollections.intMap()
override fun handlePacket(packet: Packet) {
when (packet) {
@ -22,7 +23,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL) {
fun handleReadAll(packet: ReadAllPacket) {
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")
}
@ -30,7 +31,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL) {
fun onActivate() {
if (!world!!.isClient) {
cachedItems.clear()
enqueue(RequestReadAllPacket(macAddress))
enqueueToSingle(RequestReadAllPacket(macAddress))
}
}