Compare commits

..

No commits in common. "8a20837f113c4dbf780619274a896ae662b982fd" and "f4f4c7ff03b45fcbe6edc4e22a54952985f887ef" have entirely different histories.

7 changed files with 54 additions and 161 deletions

View File

@ -14,11 +14,13 @@ import java.util.*
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), PacketSink { abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), Tickable, PacketSink {
var macAddress = MACAddress.random() var macAddress = MACAddress.random()
protected set protected set
private val sendQueue = LinkedList<Pair<Packet, WeakReference<PacketSink>>>()
override fun getMACAddress(): MACAddress { override fun getMACAddress(): MACAddress {
return macAddress return macAddress
} }
@ -31,7 +33,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), P
protected abstract fun handlePacket(packet: Packet) protected abstract fun handlePacket(packet: Packet)
protected open fun acceptsPacket(packet: Packet): Boolean { fun acceptsPacket(packet: Packet): Boolean {
return when (packet.destination.type) { return when (packet.destination.type) {
MACAddress.Type.BROADCAST -> true MACAddress.Type.BROADCAST -> true
MACAddress.Type.UNICAST -> macAddress == packet.destination MACAddress.Type.UNICAST -> macAddress == packet.destination
@ -43,28 +45,33 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), P
return false return false
} }
fun send(packet: Packet, destination: PacketSink) { fun enqueue(packet: Packet, destination: PacketSink) {
destination.handle(packet) sendQueue.add(packet to WeakReference(destination))
} }
// todo: better name for this // todo: better name for this
fun sendToSingle(packet: Packet) { fun enqueueToSingle(packet: Packet) {
val destinations = NetworkUtil.findDestinations(world!!, pos) val destinations = NetworkUtil.findDestinations(world!!, pos)
if (destinations.size != 1) { if (destinations.size != 1) {
// todo: handle this better // todo: handle this better
println("Can't send packet, multiple destinations available: $destinations") println("Can't send packet, multiple destinations available: $destinations")
return return
} }
send(packet, destinations.first()) enqueue(packet, destinations.first())
} }
fun sendToAll(packet: Packet) { fun enqueueToAll(packet: Packet) {
sendToAll(packet, NetworkUtil.findDestinations(world!!, pos)) enqueueToAll(packet, NetworkUtil.findDestinations(world!!, pos))
} }
fun sendToAll(packet: Packet, destinations: Iterable<PacketSink>) { fun enqueueToAll(packet: Packet, destinations: Iterable<PacketSink>) {
destinations.forEach { sendQueue.addAll(destinations.map { packet to WeakReference(it) })
it.handle(packet) }
override fun tick() {
if (sendQueue.isNotEmpty()) {
val (packet, destination) = sendQueue.pop()
destination.get()?.handle(packet)
} }
} }
@ -79,7 +86,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), P
} }
fun onBreak() { fun onBreak() {
sendToAll(DeviceRemovedPacket(this)) enqueueToAll(DeviceRemovedPacket(this))
} }
} }

View File

@ -43,21 +43,21 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE) {
private fun handleRequestInventory(packet: RequestInventoryPacket) { private fun handleRequestInventory(packet: RequestInventoryPacket) {
getInventory()?.also { inv -> getInventory()?.also { inv ->
sendToSingle(ReadInventoryPacket(inv, macAddress, packet.source)) enqueueToSingle(ReadInventoryPacket(inv, macAddress, packet.source))
} }
} }
private fun handleLocateStack(packet: LocateStackPacket) { private fun handleLocateStack(packet: LocateStackPacket) {
getInventory()?.also { inv -> getInventory()?.also { inv ->
val amount = inv.getAmount(packet.stack) val amount = inv.getAmount(packet.stack)
sendToSingle(StackLocationPacket(packet.stack, amount, this, macAddress, packet.source)) enqueueToSingle(StackLocationPacket(packet.stack, amount, this, macAddress, packet.source))
} }
} }
private fun handleExtractStack(packet: ExtractStackPacket) { private fun handleExtractStack(packet: ExtractStackPacket) {
getInventory()?.also { inv -> getInventory()?.also { inv ->
val extracted = inv.extract(packet.stack, packet.amount) val extracted = inv.extract(packet.stack, packet.amount)
sendToSingle(ItemStackPacket(extracted, macAddress, packet.source)) enqueueToSingle(ItemStackPacket(extracted, macAddress, packet.source))
} }
} }

View File

@ -1,7 +1,5 @@
package net.shadowfacts.phycon.network.block.netswitch package net.shadowfacts.phycon.network.block.netswitch
import net.minecraft.entity.ItemEntity
import net.minecraft.util.Tickable
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
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
@ -9,44 +7,30 @@ import net.shadowfacts.phycon.api.util.MACAddress
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.NetworkUtil import net.shadowfacts.phycon.network.NetworkUtil
import net.shadowfacts.phycon.network.packet.ItemStackPacket
import java.lang.RuntimeException import java.lang.RuntimeException
import javax.print.attribute.standard.Destination
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class SwitchBlockEntity: DeviceBlockEntity(PhyBlockEntities.SWITCH), Tickable { class SwitchBlockEntity: DeviceBlockEntity(PhyBlockEntities.SWITCH) {
companion object {
var SWITCHING_CAPACITY = 256
}
private val macTable = mutableMapOf<MACAddress, Direction>() private val macTable = mutableMapOf<MACAddress, Direction>()
private var packetsHandledThisTick = 0
override fun acceptsPacket(packet: Packet) = true
override fun handlePacket(packet: Packet) { override fun handlePacket(packet: Packet) {
if (packetsHandledThisTick >= SWITCHING_CAPACITY) { throw RuntimeException("Unreachable")
if (packet is ItemStackPacket) { }
// todo: calculate entity spawn point by finding non-obstructed location
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), packet.stack)
world!!.spawnEntity(entity)
}
return
}
packetsHandledThisTick++
override fun handle(packet: Packet) {
if (packet.destination == MACAddress.BROADCAST) { if (packet.destination == MACAddress.BROADCAST) {
val allDestinations = NetworkUtil.findDestinations(world!!, pos).filter { it.macAddress != packet.source } val allDestinations = NetworkUtil.findDestinations(world!!, pos).filter { it.macAddress != packet.source }
sendToAll(packet, allDestinations) enqueueToAll(packet, allDestinations)
} else { } else {
val direction = macTable[packet.destination] val direction = macTable[packet.destination]
if (direction != null) { if (direction != null) {
val dest = findDestination(direction) val dest = findDestination(direction)
if (dest != null && packet.destination == dest.macAddress) { if (dest != null && packet.destination == dest.macAddress) {
send(packet, dest) enqueue(packet, dest)
return return
} }
} }
@ -69,14 +53,10 @@ class SwitchBlockEntity: DeviceBlockEntity(PhyBlockEntities.SWITCH), Tickable {
val dest = findDestination(dir) val dest = findDestination(dir)
if (dest != null && packet.destination == dest.macAddress) { if (dest != null && packet.destination == dest.macAddress) {
macTable[packet.destination] = dir macTable[packet.destination] = dir
send(packet, dest) enqueue(packet, dest)
break break
} }
} }
} }
override fun tick() {
packetsHandledThisTick = 0
}
} }

View File

@ -13,7 +13,6 @@ import net.minecraft.inventory.InventoryListener
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.util.Tickable
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 net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
@ -21,7 +20,6 @@ import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.network.packet.* import net.shadowfacts.phycon.network.packet.*
import net.shadowfacts.phycon.util.fromTag import net.shadowfacts.phycon.util.fromTag
import net.shadowfacts.phycon.util.insert
import net.shadowfacts.phycon.util.toTag import net.shadowfacts.phycon.util.toTag
import java.util.* import java.util.*
import kotlin.math.min import kotlin.math.min
@ -29,14 +27,14 @@ import kotlin.math.min
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), InventoryListener, BlockEntityClientSerializable, Tickable { class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), InventoryListener, BlockEntityClientSerializable {
companion object { companion object {
val LOCATE_REQUEST_TIMEOUT = 40 // ticks val LOCATE_REQUEST_TIMEOUT = 40 // ticks
} }
private val inventoryCache = mutableMapOf<MACAddress, GroupedItemInv>() private val inventoryCache = mutableMapOf<MACAddress, GroupedItemInv>()
val internalBuffer = TerminalBufferInventory(18) val internalBuffer = BasicInventory(18)
private val pendingRequests = LinkedList<StackLocateRequest>() private val pendingRequests = LinkedList<StackLocateRequest>()
@ -84,12 +82,16 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
} }
private fun handleItemStack(packet: ItemStackPacket) { private fun handleItemStack(packet: ItemStackPacket) {
val remaining = internalBuffer.insertFromNetwork(packet.stack) // todo: handle merging stacks into the buffer better?
if (!remaining.isEmpty) { for (i in 0 until internalBuffer.invSize) {
// todo: calculate entity spawn point by finding non-obstructed location if (internalBuffer.getInvStack(i).isEmpty) {
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), remaining) internalBuffer.setInvStack(i, packet.stack)
world!!.spawnEntity(entity) return
}
} }
// todo: calculate entity spawn point by finding non-obstructed location
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), packet.stack)
world!!.spawnEntity(entity)
} }
private fun updateNetItems() { private fun updateNetItems() {
@ -117,6 +119,8 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
} }
override fun tick() { override fun tick() {
super.tick()
if (observers > 0 && (++counter % 20) == 0) { if (observers > 0 && (++counter % 20) == 0) {
if (world!!.isClient) { if (world!!.isClient) {
println(cachedNetItems) println(cachedNetItems)
@ -133,7 +137,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
sync() sync()
inventoryCache.clear() inventoryCache.clear()
sendToSingle(RequestInventoryPacket(macAddress)) enqueueToSingle(RequestInventoryPacket(macAddress))
ContainerProviderRegistry.INSTANCE.openContainer(TerminalContainer.ID, player) { buf -> ContainerProviderRegistry.INSTANCE.openContainer(TerminalContainer.ID, player) { buf ->
buf.writeBlockPos(pos) buf.writeBlockPos(pos)
} }
@ -143,7 +147,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
fun requestItem(stack: ItemStack, amount: Int = stack.count) { fun requestItem(stack: ItemStack, amount: Int = stack.count) {
pendingRequests.add(StackLocateRequest(stack, amount, counter)) pendingRequests.add(StackLocateRequest(stack, amount, counter))
sendToSingle(LocateStackPacket(stack, macAddress)) enqueueToSingle(LocateStackPacket(stack, macAddress))
} }
private fun stackLocateRequestCompleted(request: StackLocateRequest) { private fun stackLocateRequestCompleted(request: StackLocateRequest) {
@ -154,12 +158,12 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
val (sourceAmount, sourceInterface) = sortedResults.removeAt(0) val (sourceAmount, sourceInterface) = sortedResults.removeAt(0)
val amountToRequest = min(sourceAmount, request.amount - amountRequested) val amountToRequest = min(sourceAmount, request.amount - amountRequested)
amountRequested += amountToRequest amountRequested += amountToRequest
sendToSingle(ExtractStackPacket(request.stack, amountToRequest, macAddress, sourceInterface.macAddress)) enqueueToSingle(ExtractStackPacket(request.stack, amountToRequest, macAddress, sourceInterface.macAddress))
} }
} }
override fun onInvChange(inv: Inventory) { override fun onInvChange(inv: Inventory) {
if (inv == internalBuffer && world != null && !world!!.isClient) { if (inv == internalBuffer && !world!!.isClient) {
markDirty() markDirty()
sync() sync()
} }

View File

@ -1,59 +0,0 @@
package net.shadowfacts.phycon.network.block.terminal
import alexiil.mc.lib.attributes.item.ItemStackUtil
import net.minecraft.inventory.BasicInventory
import net.minecraft.item.ItemStack
import kotlin.math.min
/**
* @author shadowfacts
*/
class TerminalBufferInventory(size: Int): BasicInventory(size) {
enum class Mode {
TO_NETWORK, FROM_NETWORK, UNASSIGNED
}
private val modes = Array(size) { Mode.UNASSIGNED }
fun insertFromNetwork(stack: ItemStack): ItemStack {
var remaining = stack.copy()
for (slot in 0 until invSize) {
if (modes[slot] == Mode.TO_NETWORK) continue
remaining = insertFromNetwork(stack, slot)
if (remaining.isEmpty) {
break
}
}
return remaining
}
private fun insertFromNetwork(stack: ItemStack, slot: Int): ItemStack {
val mode = modes[slot]
if (mode == Mode.TO_NETWORK) return stack
val current = getInvStack(slot)
if (current.isEmpty) {
setInvStack(slot, stack)
modes[slot] = Mode.FROM_NETWORK
return ItemStack.EMPTY
} else if (ItemStackUtil.areEqualIgnoreAmounts(stack, current)) {
val toTransfer = min(current.maxCount - current.count, stack.count)
current.count += toTransfer
stack.count -= toTransfer
modes[slot] = Mode.FROM_NETWORK
return stack
} else {
return stack
}
}
override fun setInvStack(slot: Int, stack: ItemStack) {
if (stack.isEmpty) {
modes[slot] = Mode.UNASSIGNED
}
super.setInvStack(slot, stack)
}
}

View File

@ -8,8 +8,7 @@ import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import kotlin.math.ceil import kotlin.math.max
import kotlin.math.min
/** /**
* @author shadowfacts * @author shadowfacts
@ -58,25 +57,16 @@ class TerminalContainer(syncId: Int, playerInv: PlayerInventory, val terminal: T
} }
override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack { override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack {
if (slotId in 0 until 54) { if (actionType == SlotActionType.QUICK_MOVE) {
// the slot clicked was one of the network stacks if (slotId < 54) {
if (actionType == SlotActionType.QUICK_MOVE) { // the slot clicked was one of the network stacks
val slot = slotList[slotId]
val stack = slotList[slotId].stack val stack = slotList[slotId].stack
if (!stack.isEmpty && !player.world.isClient) { if (!stack.isEmpty && !player.world.isClient) {
terminal.requestItem(stack, min(stack.count, stack.maxCount)) terminal.requestItem(stack, max(stack.count, stack.maxCount))
}
} else if (actionType == SlotActionType.PICKUP && clickData == 1) {
if (clickData == 1) {
// right click, request half stack
val stack = slotList[slotId].stack
if (!stack.isEmpty && !player.world.isClient) {
terminal.requestItem(stack, ceil(min(stack.count, stack.maxCount) / 2f).toInt())
}
} else {
// todo: left click, show amount dialog
} }
return ItemStack.EMPTY
} }
return ItemStack.EMPTY
} }
return super.onSlotClick(slotId, clickData, actionType, player) return super.onSlotClick(slotId, clickData, actionType, player)
} }

View File

@ -1,12 +1,10 @@
package net.shadowfacts.phycon.util package net.shadowfacts.phycon.util
import alexiil.mc.lib.attributes.item.ItemStackUtil
import net.minecraft.inventory.BasicInventory import net.minecraft.inventory.BasicInventory
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import java.lang.RuntimeException import java.lang.RuntimeException
import kotlin.math.min
/** /**
* @author shadowfacts * @author shadowfacts
@ -34,30 +32,3 @@ fun BasicInventory.fromTag(list: ListTag) {
setInvStack(slot, stack) setInvStack(slot, stack)
} }
} }
fun BasicInventory.insert(stack: ItemStack, slot: Int): ItemStack {
@Suppress("NAME_SHADOWING") val stack = stack.copy()
val current = getInvStack(slot)
if (current.isEmpty) {
setInvStack(slot, stack)
return ItemStack.EMPTY
} else if (ItemStackUtil.areEqualIgnoreAmounts(stack, current)) {
val toTransfer = min(current.maxCount - current.count, stack.count)
current.count += toTransfer
stack.count -= toTransfer
return stack
} else {
return stack
}
}
fun BasicInventory.insert(stack: ItemStack): ItemStack {
var remaining = stack
for (slot in 0 until invSize) {
remaining = insert(stack, slot)
if (remaining.isEmpty) {
break
}
}
return remaining
}