Compare commits

..

No commits in common. "26134cea9dbc07ea0ca68566d6c5af05cd3fb80d" and "1c3e358f2e639dd237eee5a88cd386c2a8a99efd" have entirely different histories.

6 changed files with 40 additions and 117 deletions

View File

@ -1,15 +0,0 @@
package net.shadowfacts.phycon.api;
import net.shadowfacts.phycon.api.packet.Packet;
/**
* @author shadowfacts
*/
public interface PacketSource {
// todo: better name for this
void sendToSingle(Packet packet);
void sendToAll(Packet packet);
void sendToAll(Packet packet, Iterable<PacketSink> destinations);
}

View File

@ -4,16 +4,18 @@ import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.nbt.CompoundTag
import net.minecraft.util.Tickable
import net.shadowfacts.phycon.api.PacketSink
import net.shadowfacts.phycon.api.PacketSource
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.MACAddress
import net.shadowfacts.phycon.network.packet.DeviceRemovedPacket
import java.lang.ref.WeakReference
import java.util.*
/**
* @author shadowfacts
*/
abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), PacketSink, PacketSource {
abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), PacketSink {
var macAddress = MACAddress.random()
protected set
@ -46,7 +48,8 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), P
destination.handle(packet)
}
override fun sendToSingle(packet: Packet) {
// todo: better name for this
fun sendToSingle(packet: Packet) {
val destinations = NetworkUtil.findDestinations(world!!, pos)
if (destinations.size != 1) {
// todo: handle this better
@ -56,11 +59,11 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), P
send(packet, destinations.first())
}
override fun sendToAll(packet: Packet) {
fun sendToAll(packet: Packet) {
sendToAll(packet, NetworkUtil.findDestinations(world!!, pos))
}
override fun sendToAll(packet: Packet, destinations: Iterable<PacketSink>) {
fun sendToAll(packet: Packet, destinations: Iterable<PacketSink>) {
destinations.forEach {
it.handle(packet)
}

View File

@ -4,19 +4,17 @@ import alexiil.mc.lib.attributes.SearchOptions
import alexiil.mc.lib.attributes.Simulation
import alexiil.mc.lib.attributes.item.GroupedItemInv
import alexiil.mc.lib.attributes.item.ItemAttributes
import net.minecraft.item.ItemStack
import net.minecraft.entity.ItemEntity
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.component.ItemStackPacketHandler
import net.shadowfacts.phycon.network.component.handleItemStack
import net.shadowfacts.phycon.network.packet.*
/**
* @author shadowfacts
*/
class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE), ItemStackPacketHandler {
class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE) {
private val facing: Direction
get() = world!!.getBlockState(pos)[InterfaceBlock.FACING]
@ -75,14 +73,21 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE), ItemS
}
}
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
private fun handleItemStack(packet: ItemStackPacket) {
val inventory = getInventory()
if (inventory != null) {
val remaining = inventory.insert(packet.stack)
// whatever could not be inserted will be sent back to the packet's source
return remaining
if (!remaining.isEmpty) {
// todo: should this send whatever was left back to the terminal instead of dropping it?
// todo: calculate entity spawn point by finding non-obstructed location
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), remaining)
world!!.spawnEntity(entity)
}
} else {
return packet.stack
// todo: should the stack back to the terminal instead of dropping it?
// 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)
}
}

View File

@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
import net.minecraft.block.BlockState
import net.minecraft.entity.ItemEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.inventory.Inventory
@ -24,16 +25,16 @@ import net.shadowfacts.phycon.api.util.MACAddress
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.network.component.ItemStackPacketHandler
import net.shadowfacts.phycon.network.component.handleItemStack
import net.shadowfacts.phycon.network.packet.*
import net.shadowfacts.phycon.util.fromTag
import net.shadowfacts.phycon.util.toTag
import java.util.*
import kotlin.math.min
/**
* @author shadowfacts
*/
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), InventoryChangedListener, BlockEntityClientSerializable, Tickable, ItemStackPacketHandler {
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), InventoryChangedListener, BlockEntityClientSerializable, Tickable {
companion object {
val LOCATE_REQUEST_TIMEOUT = 40 // ticks
@ -43,7 +44,6 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
private val inventoryCache = mutableMapOf<MACAddress, GroupedItemInv>()
val internalBuffer = TerminalBufferInventory(18)
private val locateRequestQueue = LinkedList<StackLocateRequest>()
private val pendingRequests = LinkedList<StackLocateRequest>()
private val pendingInsertions = Int2ObjectArrayMap<PendingStackInsertion>()
@ -85,20 +85,24 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
if (request != null) {
request.results.add(packet.amount to packet.sourceInterface)
if (request.totalResultAmount >= request.amount || counter - request.timestamp >= LOCATE_REQUEST_TIMEOUT || request.results.size >= inventoryCache.size) {
pendingRequests.remove(request)
stackLocateRequestCompleted(request)
}
}
}
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
private fun handleItemStack(packet: ItemStackPacket) {
val remaining = internalBuffer.insertFromNetwork(packet.stack)
if (!remaining.isEmpty) {
// todo: calculate entity spawn point by finding non-obstructed location
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), remaining)
world!!.spawnEntity(entity)
}
// this happens outside the normal update loop because by receiving the item stack packet
// we "know" how much the count in the source inventory has changed
updateNetItems()
sync()
return remaining
}
private fun handleCapacity(packet: CapacityPacket) {
@ -107,8 +111,9 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
}
if (insertion != null) {
insertion.results.add(packet.capacity to packet.receivingInterface)
if (insertion.isFinishable(counter)) {
finishInsertion(insertion)
if (insertion.totalCapacity >= insertion.stack.count || counter - insertion.timestamp >= INSERTION_TIMEOUT || insertion.results.size >= inventoryCache.size) {
pendingInsertions.remove(insertion.bufferSlot)
completeInsertion(insertion)
}
}
}
@ -141,35 +146,6 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
}
}
private fun finishPendingInsertions() {
if (world!!.isClient) return
for (insertion in pendingInsertions.values) {
if (!insertion.isFinishable(counter)) continue
finishInsertion(insertion)
}
}
private fun sendEnqueuedLocateRequests() {
if (world!!.isClient) return
for (request in locateRequestQueue) {
pendingRequests.add(request)
sendToSingle(LocateStackPacket(request.stack, macAddress))
}
locateRequestQueue.clear()
}
private fun finishPendingRequests() {
if (world!!.isClient) return
for (request in pendingRequests) {
if (request.isFinishable(counter)) {
stackLocateRequestCompleted(request)
}
}
}
fun addObserver() {
observers++
}
@ -179,15 +155,12 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
}
override fun tick() {
if (observers > 0 && (++counter % 10) == 0) {
if (observers > 0 && (++counter % 20) == 0) {
if (world!!.isClient) {
println(cachedNetItems)
} else {
updateNetItems()
sendEnqueuedLocateRequests()
finishPendingRequests()
beginInsertions()
finishPendingInsertions()
sync()
}
}
@ -217,12 +190,11 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
}
fun requestItem(stack: ItemStack, amount: Int = stack.count) {
locateRequestQueue.add(StackLocateRequest(stack, amount, counter))
pendingRequests.add(StackLocateRequest(stack, amount, counter))
sendToSingle(LocateStackPacket(stack, macAddress))
}
private fun stackLocateRequestCompleted(request: StackLocateRequest) {
pendingRequests.remove(request)
// todo: also sort results by interface priority
val sortedResults = request.results.sortedByDescending { it.first }.toMutableList()
var amountRequested = 0
@ -234,9 +206,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
}
}
private fun finishInsertion(insertion: PendingStackInsertion) {
pendingInsertions.remove(insertion.bufferSlot)
private fun completeInsertion(insertion: PendingStackInsertion) {
// todo: also sort results by interface priority
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList()
val remaining = insertion.stack
@ -309,10 +279,6 @@ data class StackLocateRequest(
) {
val totalResultAmount: Int
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
fun isFinishable(currentTimestamp: Int): Boolean {
return totalResultAmount > amount || currentTimestamp - timestamp >= TerminalBlockEntity.LOCATE_REQUEST_TIMEOUT
}
}
data class PendingStackInsertion(
@ -323,8 +289,4 @@ data class PendingStackInsertion(
) {
val totalCapacity: Int
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
fun isFinishable(currentTimestamp: Int): Boolean {
return totalCapacity > stack.count || currentTimestamp - timestamp >= TerminalBlockEntity.INSERTION_TIMEOUT
}
}

View File

@ -1,31 +0,0 @@
package net.shadowfacts.phycon.network.component
import net.minecraft.block.entity.BlockEntity
import net.minecraft.entity.ItemEntity
import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.PacketSink
import net.shadowfacts.phycon.api.PacketSource
import net.shadowfacts.phycon.network.packet.ItemStackPacket
/**
* @author shadowfacts
*/
interface ItemStackPacketHandler: PacketSink, PacketSource {
fun doHandleItemStack(packet: ItemStackPacket): ItemStack
}
fun <BE> BE.handleItemStack(packet: ItemStackPacket) where BE: BlockEntity, BE: ItemStackPacketHandler {
// todo: is 5 a good number?
// the max bounce count should always be odd, so the item is spawned in-world at the
if (packet.bounceCount == 5) {
// 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
}
val remainder = doHandleItemStack(packet)
// if there are any items remaining, send them back to the source with incremented bounce count
if (!remainder.isEmpty) {
sendToSingle(ItemStackPacket(remainder, packet.bounceCount + 1, macAddress, packet.source))
}
}

View File

@ -6,6 +6,5 @@ import net.shadowfacts.phycon.api.util.MACAddress
/**
* @author shadowfacts
*/
class ItemStackPacket(val stack: ItemStack, val bounceCount: Int, source: MACAddress, destination: MACAddress): BasePacket(source, destination) {
constructor(stack: ItemStack, source: MACAddress, destination: MACAddress): this(stack, 0, source, destination)
class ItemStackPacket(val stack: ItemStack, source: MACAddress, destination: MACAddress): BasePacket(source, destination) {
}