Compare commits
2 Commits
1c3e358f2e
...
26134cea9d
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 26134cea9d | |
Shadowfacts | d61ed8b7cc |
|
@ -0,0 +1,15 @@
|
||||||
|
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);
|
||||||
|
}
|
|
@ -4,18 +4,16 @@ import net.minecraft.block.BlockState
|
||||||
import net.minecraft.block.entity.BlockEntity
|
import net.minecraft.block.entity.BlockEntity
|
||||||
import net.minecraft.block.entity.BlockEntityType
|
import net.minecraft.block.entity.BlockEntityType
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.util.Tickable
|
|
||||||
import net.shadowfacts.phycon.api.PacketSink
|
import net.shadowfacts.phycon.api.PacketSink
|
||||||
|
import net.shadowfacts.phycon.api.PacketSource
|
||||||
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.network.packet.DeviceRemovedPacket
|
import net.shadowfacts.phycon.network.packet.DeviceRemovedPacket
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), PacketSink {
|
abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), PacketSink, PacketSource {
|
||||||
|
|
||||||
var macAddress = MACAddress.random()
|
var macAddress = MACAddress.random()
|
||||||
protected set
|
protected set
|
||||||
|
@ -48,8 +46,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), P
|
||||||
destination.handle(packet)
|
destination.handle(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: better name for this
|
override fun sendToSingle(packet: Packet) {
|
||||||
fun sendToSingle(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
|
||||||
|
@ -59,11 +56,11 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type), P
|
||||||
send(packet, destinations.first())
|
send(packet, destinations.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendToAll(packet: Packet) {
|
override fun sendToAll(packet: Packet) {
|
||||||
sendToAll(packet, NetworkUtil.findDestinations(world!!, pos))
|
sendToAll(packet, NetworkUtil.findDestinations(world!!, pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendToAll(packet: Packet, destinations: Iterable<PacketSink>) {
|
override fun sendToAll(packet: Packet, destinations: Iterable<PacketSink>) {
|
||||||
destinations.forEach {
|
destinations.forEach {
|
||||||
it.handle(packet)
|
it.handle(packet)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,19 @@ import alexiil.mc.lib.attributes.SearchOptions
|
||||||
import alexiil.mc.lib.attributes.Simulation
|
import alexiil.mc.lib.attributes.Simulation
|
||||||
import alexiil.mc.lib.attributes.item.GroupedItemInv
|
import alexiil.mc.lib.attributes.item.GroupedItemInv
|
||||||
import alexiil.mc.lib.attributes.item.ItemAttributes
|
import alexiil.mc.lib.attributes.item.ItemAttributes
|
||||||
import net.minecraft.entity.ItemEntity
|
import net.minecraft.item.ItemStack
|
||||||
import net.minecraft.util.math.Direction
|
import net.minecraft.util.math.Direction
|
||||||
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
|
||||||
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
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.*
|
import net.shadowfacts.phycon.network.packet.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE) {
|
class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE), ItemStackPacketHandler {
|
||||||
|
|
||||||
private val facing: Direction
|
private val facing: Direction
|
||||||
get() = world!!.getBlockState(pos)[InterfaceBlock.FACING]
|
get() = world!!.getBlockState(pos)[InterfaceBlock.FACING]
|
||||||
|
@ -73,21 +75,14 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleItemStack(packet: ItemStackPacket) {
|
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
|
||||||
val inventory = getInventory()
|
val inventory = getInventory()
|
||||||
if (inventory != null) {
|
if (inventory != null) {
|
||||||
val remaining = inventory.insert(packet.stack)
|
val remaining = inventory.insert(packet.stack)
|
||||||
if (!remaining.isEmpty) {
|
// whatever could not be inserted will be sent back to the packet's source
|
||||||
// todo: should this send whatever was left back to the terminal instead of dropping it?
|
return remaining
|
||||||
// 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 {
|
} else {
|
||||||
// todo: should the stack back to the terminal instead of dropping it?
|
return packet.stack
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap
|
||||||
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
|
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
|
||||||
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
|
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
|
||||||
import net.minecraft.block.BlockState
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.entity.ItemEntity
|
|
||||||
import net.minecraft.entity.player.PlayerEntity
|
import net.minecraft.entity.player.PlayerEntity
|
||||||
import net.minecraft.entity.player.PlayerInventory
|
import net.minecraft.entity.player.PlayerInventory
|
||||||
import net.minecraft.inventory.Inventory
|
import net.minecraft.inventory.Inventory
|
||||||
|
@ -25,16 +24,16 @@ 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.block.netinterface.InterfaceBlockEntity
|
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.network.packet.*
|
||||||
import net.shadowfacts.phycon.util.fromTag
|
|
||||||
import net.shadowfacts.phycon.util.toTag
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), InventoryChangedListener, BlockEntityClientSerializable, Tickable {
|
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), InventoryChangedListener, BlockEntityClientSerializable, Tickable, ItemStackPacketHandler {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val LOCATE_REQUEST_TIMEOUT = 40 // ticks
|
val LOCATE_REQUEST_TIMEOUT = 40 // ticks
|
||||||
|
@ -44,6 +43,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
private val inventoryCache = mutableMapOf<MACAddress, GroupedItemInv>()
|
private val inventoryCache = mutableMapOf<MACAddress, GroupedItemInv>()
|
||||||
val internalBuffer = TerminalBufferInventory(18)
|
val internalBuffer = TerminalBufferInventory(18)
|
||||||
|
|
||||||
|
private val locateRequestQueue = LinkedList<StackLocateRequest>()
|
||||||
private val pendingRequests = LinkedList<StackLocateRequest>()
|
private val pendingRequests = LinkedList<StackLocateRequest>()
|
||||||
private val pendingInsertions = Int2ObjectArrayMap<PendingStackInsertion>()
|
private val pendingInsertions = Int2ObjectArrayMap<PendingStackInsertion>()
|
||||||
|
|
||||||
|
@ -85,24 +85,20 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
if (request != null) {
|
if (request != null) {
|
||||||
request.results.add(packet.amount to packet.sourceInterface)
|
request.results.add(packet.amount to packet.sourceInterface)
|
||||||
if (request.totalResultAmount >= request.amount || counter - request.timestamp >= LOCATE_REQUEST_TIMEOUT || request.results.size >= inventoryCache.size) {
|
if (request.totalResultAmount >= request.amount || counter - request.timestamp >= LOCATE_REQUEST_TIMEOUT || request.results.size >= inventoryCache.size) {
|
||||||
pendingRequests.remove(request)
|
|
||||||
stackLocateRequestCompleted(request)
|
stackLocateRequestCompleted(request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleItemStack(packet: ItemStackPacket) {
|
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
|
||||||
val remaining = internalBuffer.insertFromNetwork(packet.stack)
|
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
|
// 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
|
// we "know" how much the count in the source inventory has changed
|
||||||
updateNetItems()
|
updateNetItems()
|
||||||
sync()
|
sync()
|
||||||
|
|
||||||
|
return remaining
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleCapacity(packet: CapacityPacket) {
|
private fun handleCapacity(packet: CapacityPacket) {
|
||||||
|
@ -111,9 +107,8 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
}
|
}
|
||||||
if (insertion != null) {
|
if (insertion != null) {
|
||||||
insertion.results.add(packet.capacity to packet.receivingInterface)
|
insertion.results.add(packet.capacity to packet.receivingInterface)
|
||||||
if (insertion.totalCapacity >= insertion.stack.count || counter - insertion.timestamp >= INSERTION_TIMEOUT || insertion.results.size >= inventoryCache.size) {
|
if (insertion.isFinishable(counter)) {
|
||||||
pendingInsertions.remove(insertion.bufferSlot)
|
finishInsertion(insertion)
|
||||||
completeInsertion(insertion)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,6 +141,35 @@ 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() {
|
fun addObserver() {
|
||||||
observers++
|
observers++
|
||||||
}
|
}
|
||||||
|
@ -155,12 +179,15 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tick() {
|
override fun tick() {
|
||||||
if (observers > 0 && (++counter % 20) == 0) {
|
if (observers > 0 && (++counter % 10) == 0) {
|
||||||
if (world!!.isClient) {
|
if (world!!.isClient) {
|
||||||
println(cachedNetItems)
|
println(cachedNetItems)
|
||||||
} else {
|
} else {
|
||||||
updateNetItems()
|
updateNetItems()
|
||||||
|
sendEnqueuedLocateRequests()
|
||||||
|
finishPendingRequests()
|
||||||
beginInsertions()
|
beginInsertions()
|
||||||
|
finishPendingInsertions()
|
||||||
sync()
|
sync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,11 +217,12 @@ 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))
|
locateRequestQueue.add(StackLocateRequest(stack, amount, counter))
|
||||||
sendToSingle(LocateStackPacket(stack, macAddress))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stackLocateRequestCompleted(request: StackLocateRequest) {
|
private fun stackLocateRequestCompleted(request: StackLocateRequest) {
|
||||||
|
pendingRequests.remove(request)
|
||||||
|
|
||||||
// todo: also sort results by interface priority
|
// todo: also sort results by interface priority
|
||||||
val sortedResults = request.results.sortedByDescending { it.first }.toMutableList()
|
val sortedResults = request.results.sortedByDescending { it.first }.toMutableList()
|
||||||
var amountRequested = 0
|
var amountRequested = 0
|
||||||
|
@ -206,7 +234,9 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun completeInsertion(insertion: PendingStackInsertion) {
|
private fun finishInsertion(insertion: PendingStackInsertion) {
|
||||||
|
pendingInsertions.remove(insertion.bufferSlot)
|
||||||
|
|
||||||
// todo: also sort results by interface priority
|
// todo: also sort results by interface priority
|
||||||
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList()
|
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList()
|
||||||
val remaining = insertion.stack
|
val remaining = insertion.stack
|
||||||
|
@ -279,6 +309,10 @@ data class StackLocateRequest(
|
||||||
) {
|
) {
|
||||||
val totalResultAmount: Int
|
val totalResultAmount: Int
|
||||||
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
|
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(
|
data class PendingStackInsertion(
|
||||||
|
@ -289,4 +323,8 @@ data class PendingStackInsertion(
|
||||||
) {
|
) {
|
||||||
val totalCapacity: Int
|
val totalCapacity: Int
|
||||||
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
|
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
|
||||||
|
|
||||||
|
fun isFinishable(currentTimestamp: Int): Boolean {
|
||||||
|
return totalCapacity > stack.count || currentTimestamp - timestamp >= TerminalBlockEntity.INSERTION_TIMEOUT
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,5 +6,6 @@ import net.shadowfacts.phycon.api.util.MACAddress
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
class ItemStackPacket(val stack: ItemStack, source: MACAddress, destination: MACAddress): BasePacket(source, destination) {
|
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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue