Compare commits
No commits in common. "8d1c2854f69df7c6b7547744420b1312927ceaa6" and "c6a5602ec146fb6cf28ec17ff6c5897186c15dd3" have entirely different histories.
8d1c2854f6
...
c6a5602ec1
|
@ -3,7 +3,7 @@ package net.shadowfacts.phycon.api;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.world.WorldAccess;
|
import net.minecraft.world.World;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -15,9 +15,9 @@ import java.util.Collection;
|
||||||
public interface NetworkComponentBlock {
|
public interface NetworkComponentBlock {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
Collection<Direction> getNetworkConnectedSides(@NotNull BlockState state, @NotNull WorldAccess world, @NotNull BlockPos pos);
|
Collection<Direction> getNetworkConnectedSides(@NotNull BlockState state, @NotNull World world, @NotNull BlockPos pos);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Interface getNetworkInterfaceForSide(@NotNull Direction side, @NotNull BlockState state, @NotNull WorldAccess world, @NotNull BlockPos pos);
|
Interface getNetworkInterfaceForSide(@NotNull Direction side, @NotNull BlockState state, @NotNull World world, @NotNull BlockPos pos);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import net.minecraft.entity.player.PlayerEntity
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.Direction
|
import net.minecraft.util.math.Direction
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
import net.minecraft.world.WorldAccess
|
|
||||||
import net.shadowfacts.phycon.api.Interface
|
import net.shadowfacts.phycon.api.Interface
|
||||||
import net.shadowfacts.phycon.api.NetworkComponentBlock
|
import net.shadowfacts.phycon.api.NetworkComponentBlock
|
||||||
import net.shadowfacts.phycon.block.BlockWithEntity
|
import net.shadowfacts.phycon.block.BlockWithEntity
|
||||||
|
@ -15,9 +14,9 @@ import net.shadowfacts.phycon.block.BlockWithEntity
|
||||||
*/
|
*/
|
||||||
abstract class DeviceBlock<T: DeviceBlockEntity>(settings: Settings): BlockWithEntity<T>(settings), NetworkComponentBlock {
|
abstract class DeviceBlock<T: DeviceBlockEntity>(settings: Settings): BlockWithEntity<T>(settings), NetworkComponentBlock {
|
||||||
|
|
||||||
abstract override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction>
|
abstract override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection<Direction>
|
||||||
|
|
||||||
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: WorldAccess, pos: BlockPos): Interface? {
|
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: World, pos: BlockPos): Interface? {
|
||||||
return getBlockEntity(world, pos)!!
|
return getBlockEntity(world, pos)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
|
||||||
private val arpTable = mutableMapOf<IPAddress, MACAddress>()
|
private val arpTable = mutableMapOf<IPAddress, MACAddress>()
|
||||||
private val packetQueue = LinkedList<PendingPacket>()
|
private val packetQueue = LinkedList<PendingPacket>()
|
||||||
|
|
||||||
var counter: Long = 0
|
protected var counter: Long = 0
|
||||||
|
|
||||||
override fun getIPAddress() = ipAddress
|
override fun getIPAddress() = ipAddress
|
||||||
override fun getMACAddress() = macAddress
|
override fun getMACAddress() = macAddress
|
||||||
|
|
|
@ -67,7 +67,7 @@ class CableBlock: Block(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> {
|
override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection<Direction> {
|
||||||
val set = EnumSet.noneOf(Direction::class.java)
|
val set = EnumSet.noneOf(Direction::class.java)
|
||||||
for ((side, prop) in CONNECTIONS) {
|
for ((side, prop) in CONNECTIONS) {
|
||||||
if (state[prop] == CableConnection.ON) {
|
if (state[prop] == CableConnection.ON) {
|
||||||
|
@ -100,10 +100,8 @@ class CableBlock: Block(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getConnectionStateInDirection(world: WorldAccess, pos: BlockPos, direction: Direction): CableConnection {
|
private fun getConnectionStateInDirection(world: WorldAccess, pos: BlockPos, direction: Direction): CableConnection {
|
||||||
val offsetPos = pos.offset(direction)
|
val state = world.getBlockState(pos.offset(direction))
|
||||||
val state = world.getBlockState(offsetPos)
|
return when (state.block) {
|
||||||
val block = state.block
|
|
||||||
return when (block) {
|
|
||||||
this -> {
|
this -> {
|
||||||
val prop = CONNECTIONS[direction.opposite]
|
val prop = CONNECTIONS[direction.opposite]
|
||||||
when (state[prop]) {
|
when (state[prop]) {
|
||||||
|
@ -111,18 +109,12 @@ class CableBlock: Block(
|
||||||
else -> CableConnection.ON
|
else -> CableConnection.ON
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is NetworkComponentBlock -> {
|
is NetworkComponentBlock -> CableConnection.ON
|
||||||
if (block.getNetworkConnectedSides(state, world, offsetPos).contains(direction.opposite)) {
|
|
||||||
CableConnection.ON
|
|
||||||
} else {
|
|
||||||
CableConnection.OFF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> CableConnection.OFF
|
else -> CableConnection.OFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: WorldAccess, pos: BlockPos): Interface? {
|
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: World, pos: BlockPos): Interface? {
|
||||||
// cables don't have network interfaces
|
// cables don't have network interfaces
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ import net.minecraft.util.shape.VoxelShape
|
||||||
import net.minecraft.util.shape.VoxelShapes
|
import net.minecraft.util.shape.VoxelShapes
|
||||||
import net.minecraft.world.BlockView
|
import net.minecraft.world.BlockView
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
import net.minecraft.world.WorldAccess
|
|
||||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||||
import net.shadowfacts.phycon.api.Interface
|
import net.shadowfacts.phycon.api.Interface
|
||||||
import net.shadowfacts.phycon.network.DeviceBlock
|
import net.shadowfacts.phycon.network.DeviceBlock
|
||||||
|
@ -68,11 +67,11 @@ class ExtractorBlock: DeviceBlock<ExtractorBlockEntity>(Settings.of(Material.MET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> {
|
override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection<Direction> {
|
||||||
return EnumSet.of(state[FACING].opposite)
|
return EnumSet.of(state[FACING].opposite)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: WorldAccess, pos: BlockPos): Interface? {
|
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: World, pos: BlockPos): Interface? {
|
||||||
return if (side == state[FACING].opposite) {
|
return if (side == state[FACING].opposite) {
|
||||||
getBlockEntity(world, pos)
|
getBlockEntity(world, pos)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.Direction
|
import net.minecraft.util.math.Direction
|
||||||
import net.minecraft.world.BlockView
|
import net.minecraft.world.BlockView
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
import net.minecraft.world.WorldAccess
|
|
||||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||||
import net.shadowfacts.phycon.api.Interface
|
import net.shadowfacts.phycon.api.Interface
|
||||||
import net.shadowfacts.phycon.network.DeviceBlock
|
import net.shadowfacts.phycon.network.DeviceBlock
|
||||||
|
@ -30,11 +29,11 @@ class MinerBlock: DeviceBlock<MinerBlockEntity>(Settings.of(Material.METAL)) {
|
||||||
val FACING = Properties.FACING
|
val FACING = Properties.FACING
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> {
|
override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection<Direction> {
|
||||||
return EnumSet.of(state[FACING].opposite)
|
return EnumSet.of(state[FACING].opposite)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: WorldAccess, pos: BlockPos): Interface? {
|
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: World, pos: BlockPos): Interface? {
|
||||||
return if (side == state[FACING]) {
|
return if (side == state[FACING]) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,11 +14,7 @@ import net.shadowfacts.phycon.api.packet.Packet
|
||||||
import net.shadowfacts.phycon.api.util.IPAddress
|
import net.shadowfacts.phycon.api.util.IPAddress
|
||||||
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.terminal.TerminalBlockEntity
|
|
||||||
import net.shadowfacts.phycon.network.component.NetworkStackDispatcher
|
|
||||||
import net.shadowfacts.phycon.network.component.NetworkStackProvider
|
import net.shadowfacts.phycon.network.component.NetworkStackProvider
|
||||||
import net.shadowfacts.phycon.network.component.handleItemStack
|
|
||||||
import net.shadowfacts.phycon.network.component.spawnItemStack
|
|
||||||
import net.shadowfacts.phycon.network.packet.*
|
import net.shadowfacts.phycon.network.packet.*
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
@ -26,24 +22,18 @@ import kotlin.math.min
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
|
class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
|
||||||
NetworkStackProvider,
|
NetworkStackProvider {
|
||||||
NetworkStackDispatcher<MinerBlockEntity.PendingInsertion> {
|
|
||||||
|
|
||||||
private val facing: Direction
|
private val facing: Direction
|
||||||
get() = cachedState[MinerBlock.FACING]
|
get() = cachedState[MinerBlock.FACING]
|
||||||
|
|
||||||
private val invProxy = MinerInvProxy(this)
|
private val invProxy = MinerInvProxy(this)
|
||||||
|
|
||||||
override val pendingInsertions = mutableListOf<PendingInsertion>()
|
|
||||||
override val dispatchStackTimeout = TerminalBlockEntity.INSERTION_TIMEOUT
|
|
||||||
|
|
||||||
override fun handle(packet: Packet) {
|
override fun handle(packet: Packet) {
|
||||||
when (packet) {
|
when (packet) {
|
||||||
is RequestInventoryPacket -> handleRequestInventory(packet)
|
is RequestInventoryPacket -> handleRequestInventory(packet)
|
||||||
is LocateStackPacket -> handleLocateStack(packet)
|
is LocateStackPacket -> handleLocateStack(packet)
|
||||||
is ExtractStackPacket -> handleExtractStack(packet)
|
is ExtractStackPacket -> handleExtractStack(packet)
|
||||||
is CapacityPacket -> handleCapacity(packet)
|
|
||||||
is ItemStackPacket -> handleItemStack(packet)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +50,7 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
|
||||||
|
|
||||||
private fun handleExtractStack(packet: ExtractStackPacket) {
|
private fun handleExtractStack(packet: ExtractStackPacket) {
|
||||||
// always recalculate immediately before breaking
|
// always recalculate immediately before breaking
|
||||||
val drops = invProxy.getDrops(recalculate = true)
|
val drops = invProxy.getDrops(true)
|
||||||
if (invProxy.getAmount(packet.stack) > 0) {
|
if (invProxy.getAmount(packet.stack) > 0) {
|
||||||
world!!.breakBlock(pos.offset(facing), false)
|
world!!.breakBlock(pos.offset(facing), false)
|
||||||
|
|
||||||
|
@ -87,18 +77,11 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
|
||||||
// dump any remaining drops into the network
|
// dump any remaining drops into the network
|
||||||
for (droppedStack in drops) {
|
for (droppedStack in drops) {
|
||||||
if (droppedStack.isEmpty) continue
|
if (droppedStack.isEmpty) continue
|
||||||
dispatchItemStack(droppedStack)
|
sendPacket(ItemStackPacket(droppedStack, ipAddress, IPAddress.BROADCAST))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
|
|
||||||
// miner can't receive stacks, so remaining is the entire packet stack
|
|
||||||
return packet.stack
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createPendingInsertion(stack: ItemStack) = PendingInsertion(stack, counter)
|
|
||||||
|
|
||||||
class MinerInvProxy(val miner: MinerBlockEntity): GroupedItemInvView {
|
class MinerInvProxy(val miner: MinerBlockEntity): GroupedItemInvView {
|
||||||
private var cachedState: BlockState? = null
|
private var cachedState: BlockState? = null
|
||||||
private var cachedDrops: List<ItemStack>? = null
|
private var cachedDrops: List<ItemStack>? = null
|
||||||
|
@ -113,7 +96,7 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
|
||||||
fun getDrops(recalculate: Boolean = false): List<ItemStack> {
|
fun getDrops(recalculate: Boolean = false): List<ItemStack> {
|
||||||
val targetPos = pos.offset(facing)
|
val targetPos = pos.offset(facing)
|
||||||
val realState = world.getBlockState(targetPos)
|
val realState = world.getBlockState(targetPos)
|
||||||
// todo: does BlockState.equals actually work or is reference equality fine for BlockStates?
|
// todo: does BlockState.equals actually work?
|
||||||
if (cachedDrops == null || realState != cachedState || recalculate) {
|
if (cachedDrops == null || realState != cachedState || recalculate) {
|
||||||
cachedState = realState
|
cachedState = realState
|
||||||
val be = if (realState.block.hasBlockEntity()) world.getBlockEntity(targetPos) else null
|
val be = if (realState.block.hasBlockEntity()) world.getBlockEntity(targetPos) else null
|
||||||
|
@ -141,7 +124,4 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PendingInsertion(stack: ItemStack, timestamp: Long): NetworkStackDispatcher.PendingInsertion<PendingInsertion>(stack, timestamp) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -63,13 +63,13 @@ class InterfaceBlock: DeviceBlock<InterfaceBlockEntity>(Settings.of(Material.MET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> {
|
override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection<Direction> {
|
||||||
val set = EnumSet.of(state[CABLE_CONNECTION])
|
val set = EnumSet.of(state[CABLE_CONNECTION])
|
||||||
set.remove(state[FACING])
|
set.remove(state[FACING])
|
||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: WorldAccess, pos: BlockPos): Interface? {
|
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: World, pos: BlockPos): Interface? {
|
||||||
return if (side == state[FACING]) {
|
return if (side == state[FACING]) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.Direction
|
import net.minecraft.util.math.Direction
|
||||||
import net.minecraft.world.BlockView
|
import net.minecraft.world.BlockView
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
import net.minecraft.world.WorldAccess
|
|
||||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||||
import net.shadowfacts.phycon.api.Interface
|
import net.shadowfacts.phycon.api.Interface
|
||||||
import net.shadowfacts.phycon.api.NetworkComponentBlock
|
import net.shadowfacts.phycon.api.NetworkComponentBlock
|
||||||
|
@ -27,11 +26,11 @@ class SwitchBlock: BlockWithEntity<SwitchBlockEntity>(Settings.of(Material.METAL
|
||||||
val ID = Identifier(PhysicalConnectivity.MODID, "switch")
|
val ID = Identifier(PhysicalConnectivity.MODID, "switch")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> {
|
override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection<Direction> {
|
||||||
return EnumSet.allOf(Direction::class.java)
|
return EnumSet.allOf(Direction::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: WorldAccess, pos: BlockPos): Interface? {
|
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: World, pos: BlockPos): Interface? {
|
||||||
return getBlockEntity(world, pos)?.interfaces?.find { it.side == side }
|
return getBlockEntity(world, pos)?.interfaces?.find { it.side == side }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.Direction
|
import net.minecraft.util.math.Direction
|
||||||
import net.minecraft.world.BlockView
|
import net.minecraft.world.BlockView
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
import net.minecraft.world.WorldAccess
|
|
||||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||||
import net.shadowfacts.phycon.api.NetworkComponentBlock
|
import net.shadowfacts.phycon.api.NetworkComponentBlock
|
||||||
import net.shadowfacts.phycon.network.DeviceBlock
|
import net.shadowfacts.phycon.network.DeviceBlock
|
||||||
|
@ -30,7 +29,7 @@ class TerminalBlock: DeviceBlock<TerminalBlockEntity>(Settings.of(Material.METAL
|
||||||
val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
|
val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> {
|
override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection<Direction> {
|
||||||
return EnumSet.allOf(Direction::class.java)
|
return EnumSet.allOf(Direction::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package net.shadowfacts.phycon.network.block.terminal
|
||||||
import alexiil.mc.lib.attributes.item.GroupedItemInvView
|
import alexiil.mc.lib.attributes.item.GroupedItemInvView
|
||||||
import alexiil.mc.lib.attributes.item.ItemStackCollections
|
import alexiil.mc.lib.attributes.item.ItemStackCollections
|
||||||
import alexiil.mc.lib.attributes.item.ItemStackUtil
|
import alexiil.mc.lib.attributes.item.ItemStackUtil
|
||||||
|
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
|
||||||
|
@ -25,26 +26,23 @@ import net.shadowfacts.phycon.api.util.IPAddress
|
||||||
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.component.*
|
import net.shadowfacts.phycon.network.component.ItemStackPacketHandler
|
||||||
|
import net.shadowfacts.phycon.network.component.NetworkStackProvider
|
||||||
|
import net.shadowfacts.phycon.network.component.NetworkStackReceiver
|
||||||
|
import net.shadowfacts.phycon.network.component.handleItemStack
|
||||||
import net.shadowfacts.phycon.network.packet.*
|
import net.shadowfacts.phycon.network.packet.*
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.properties.Delegates
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
|
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), InventoryChangedListener, BlockEntityClientSerializable, Tickable, ItemStackPacketHandler {
|
||||||
InventoryChangedListener,
|
|
||||||
BlockEntityClientSerializable,
|
|
||||||
Tickable,
|
|
||||||
ItemStackPacketHandler,
|
|
||||||
NetworkStackDispatcher<TerminalBlockEntity.PendingInsertion> {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val LOCATE_REQUEST_TIMEOUT: Long = 40 // ticks
|
val LOCATE_REQUEST_TIMEOUT = 40 // ticks
|
||||||
val INSERTION_TIMEOUT: Long = 40
|
val INSERTION_TIMEOUT = 40
|
||||||
}
|
}
|
||||||
|
|
||||||
private val inventoryCache = mutableMapOf<IPAddress, GroupedItemInvView>()
|
private val inventoryCache = mutableMapOf<IPAddress, GroupedItemInvView>()
|
||||||
|
@ -52,8 +50,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
|
||||||
|
|
||||||
private val locateRequestQueue = LinkedList<StackLocateRequest>()
|
private val locateRequestQueue = LinkedList<StackLocateRequest>()
|
||||||
private val pendingRequests = LinkedList<StackLocateRequest>()
|
private val pendingRequests = LinkedList<StackLocateRequest>()
|
||||||
override val pendingInsertions = mutableListOf<PendingInsertion>()
|
private val pendingInsertions = Int2ObjectArrayMap<PendingStackInsertion>()
|
||||||
override val dispatchStackTimeout = INSERTION_TIMEOUT
|
|
||||||
|
|
||||||
private var observers = 0
|
private var observers = 0
|
||||||
val cachedNetItems = ItemStackCollections.intMap()
|
val cachedNetItems = ItemStackCollections.intMap()
|
||||||
|
@ -120,6 +117,18 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
|
||||||
return remaining
|
return remaining
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleCapacity(packet: CapacityPacket) {
|
||||||
|
val insertion = pendingInsertions.values.firstOrNull {
|
||||||
|
ItemStackUtil.areEqualIgnoreAmounts(packet.stack, it.stack)
|
||||||
|
}
|
||||||
|
if (insertion != null) {
|
||||||
|
insertion.results.add(packet.capacity to packet.stackReceiver)
|
||||||
|
if (insertion.isFinishable(counter)) {
|
||||||
|
finishInsertion(insertion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateNetItems() {
|
private fun updateNetItems() {
|
||||||
cachedNetItems.clear()
|
cachedNetItems.clear()
|
||||||
for (inventory in inventoryCache.values) {
|
for (inventory in inventoryCache.values) {
|
||||||
|
@ -141,12 +150,20 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
|
||||||
|
|
||||||
for (slot in 0 until internalBuffer.size()) {
|
for (slot in 0 until internalBuffer.size()) {
|
||||||
if (internalBuffer.getMode(slot) != TerminalBufferInventory.Mode.TO_NETWORK) continue
|
if (internalBuffer.getMode(slot) != TerminalBufferInventory.Mode.TO_NETWORK) continue
|
||||||
if (pendingInsertions.any { it.bufferSlot == slot }) continue
|
if (slot in pendingInsertions) continue
|
||||||
val stack = internalBuffer.getStack(slot)
|
val stack = internalBuffer.getStack(slot)
|
||||||
dispatchItemStack(stack) { insertion ->
|
pendingInsertions[slot] = PendingStackInsertion(slot, stack, counter)
|
||||||
insertion.bufferSlot = slot
|
sendPacket(CheckCapacityPacket(stack, ipAddress, IPAddress.BROADCAST))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun finishPendingInsertions() {
|
||||||
|
if (world!!.isClient) return
|
||||||
|
|
||||||
|
for (insertion in pendingInsertions.values) {
|
||||||
|
if (!insertion.isFinishable(counter)) continue
|
||||||
|
finishInsertion(insertion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendEnqueuedLocateRequests() {
|
private fun sendEnqueuedLocateRequests() {
|
||||||
|
@ -185,7 +202,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
|
||||||
sendEnqueuedLocateRequests()
|
sendEnqueuedLocateRequests()
|
||||||
finishPendingRequests()
|
finishPendingRequests()
|
||||||
beginInsertions()
|
beginInsertions()
|
||||||
finishTimedOutPendingInsertions()
|
finishPendingInsertions()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (observers > 0) {
|
if (observers > 0) {
|
||||||
|
@ -240,17 +257,24 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createPendingInsertion(stack: ItemStack) = PendingInsertion(stack, counter)
|
private fun finishInsertion(insertion: PendingStackInsertion) {
|
||||||
|
pendingInsertions.remove(insertion.bufferSlot)
|
||||||
|
|
||||||
override fun finishInsertion(insertion: PendingInsertion): ItemStack {
|
// todo: also sort results by interface priority
|
||||||
val remaining = super.finishInsertion(insertion)
|
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList()
|
||||||
|
val remaining = insertion.stack
|
||||||
|
while (!remaining.isEmpty && sortedResults.isNotEmpty()) {
|
||||||
|
val (capacity, receivingInterface) = sortedResults.removeAt(0)
|
||||||
|
if (capacity <= 0) continue
|
||||||
|
sendPacket(ItemStackPacket(remaining.copy(), ipAddress, receivingInterface.ipAddress))
|
||||||
|
// todo: the interface should confirm how much was actually inserted, in case of race condition
|
||||||
|
remaining.count -= capacity
|
||||||
|
}
|
||||||
internalBuffer.setStack(insertion.bufferSlot, remaining)
|
internalBuffer.setStack(insertion.bufferSlot, remaining)
|
||||||
|
|
||||||
// as with extracting, we "know" the new amounts and so can update instantly without actually sending out packets
|
// as with extracting, we "know" the new amounts and so can update instantly without actually sending out packets
|
||||||
updateNetItems()
|
updateNetItems()
|
||||||
sync()
|
sync()
|
||||||
|
|
||||||
return remaining
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInventoryChanged(inv: Inventory) {
|
override fun onInventoryChanged(inv: Inventory) {
|
||||||
|
@ -303,10 +327,6 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
|
||||||
fun netItemsChanged()
|
fun netItemsChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
class PendingInsertion(stack: ItemStack, timestamp: Long): NetworkStackDispatcher.PendingInsertion<PendingInsertion>(stack, timestamp) {
|
|
||||||
var bufferSlot by Delegates.notNull<Int>()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class StackLocateRequest(
|
data class StackLocateRequest(
|
||||||
|
@ -322,3 +342,17 @@ data class StackLocateRequest(
|
||||||
return totalResultAmount >= amount || currentTimestamp - timestamp >= TerminalBlockEntity.LOCATE_REQUEST_TIMEOUT
|
return totalResultAmount >= amount || currentTimestamp - timestamp >= TerminalBlockEntity.LOCATE_REQUEST_TIMEOUT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class PendingStackInsertion(
|
||||||
|
val bufferSlot: Int,
|
||||||
|
val stack: ItemStack,
|
||||||
|
val timestamp: Long,
|
||||||
|
val results: MutableSet<Pair<Int, NetworkStackReceiver>> = mutableSetOf(),
|
||||||
|
) {
|
||||||
|
val totalCapacity: Int
|
||||||
|
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
|
||||||
|
|
||||||
|
fun isFinishable(currentTimestamp: Long): Boolean {
|
||||||
|
return totalCapacity >= stack.count || currentTimestamp - timestamp >= TerminalBlockEntity.INSERTION_TIMEOUT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.Direction
|
import net.minecraft.util.math.Direction
|
||||||
import net.minecraft.world.BlockView
|
import net.minecraft.world.BlockView
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
import net.minecraft.world.WorldAccess
|
|
||||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||||
import net.shadowfacts.phycon.network.DeviceBlock
|
import net.shadowfacts.phycon.network.DeviceBlock
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -29,7 +28,7 @@ class DestBlock: DeviceBlock<DestBlockEntity>(Settings.of(Material.METAL)) {
|
||||||
return DestBlockEntity()
|
return DestBlockEntity()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): MutableCollection<Direction> {
|
override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): MutableCollection<Direction> {
|
||||||
return EnumSet.allOf(Direction::class.java)
|
return EnumSet.allOf(Direction::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import net.minecraft.util.Identifier
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.Direction
|
import net.minecraft.util.math.Direction
|
||||||
import net.minecraft.world.BlockView
|
import net.minecraft.world.BlockView
|
||||||
import net.minecraft.world.WorldAccess
|
import net.minecraft.world.World
|
||||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||||
import net.shadowfacts.phycon.network.DeviceBlock
|
import net.shadowfacts.phycon.network.DeviceBlock
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -24,7 +24,7 @@ class SourceBlock: DeviceBlock<SourceBlockEntity>(Settings.of(Material.METAL)) {
|
||||||
return SourceBlockEntity()
|
return SourceBlockEntity()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): MutableCollection<Direction> {
|
override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): MutableCollection<Direction> {
|
||||||
return EnumSet.allOf(Direction::class.java)
|
return EnumSet.allOf(Direction::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,22 +14,18 @@ interface ItemStackPacketHandler: PacketSink, PacketSource {
|
||||||
fun doHandleItemStack(packet: ItemStackPacket): ItemStack
|
fun doHandleItemStack(packet: ItemStackPacket): ItemStack
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <Self> Self.handleItemStack(packet: ItemStackPacket) where Self: BlockEntity, Self: ItemStackPacketHandler {
|
fun <BE> BE.handleItemStack(packet: ItemStackPacket) where BE: BlockEntity, BE: ItemStackPacketHandler {
|
||||||
// todo: is 5 a good number?
|
// todo: is 5 a good number?
|
||||||
// the max bounce count should always be odd, so the item is spawned in-world at the
|
// the max bounce count should always be odd, so the item is spawned in-world at the
|
||||||
if (packet.bounceCount == 5) {
|
if (packet.bounceCount == 5) {
|
||||||
spawnItemStack(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)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val remainder = doHandleItemStack(packet)
|
val remainder = doHandleItemStack(packet)
|
||||||
// if there are any items remaining, send them back to the source with incremented bounce count
|
// if there are any items remaining, send them back to the source with incremented bounce count
|
||||||
if (!remainder.isEmpty) {
|
if (!remainder.isEmpty) {
|
||||||
sendPacket(ItemStackPacket(remainder, packet.bounceCount + 1, ipAddress, packet.source))
|
// sendToSingle(ItemStackPacket(remainder, packet.bounceCount + 1, macAddress, packet.source))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <Self> Self.spawnItemStack(stack: ItemStack) where Self: BlockEntity, Self: ItemStackPacketHandler {
|
|
||||||
// todo: calculate entity spawn point by finding non-obstructed location
|
|
||||||
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), stack)
|
|
||||||
world!!.spawnEntity(entity)
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
package net.shadowfacts.phycon.network.component
|
|
||||||
|
|
||||||
import alexiil.mc.lib.attributes.item.ItemStackUtil
|
|
||||||
import net.minecraft.block.entity.BlockEntity
|
|
||||||
import net.minecraft.item.ItemStack
|
|
||||||
import net.shadowfacts.phycon.api.util.IPAddress
|
|
||||||
import net.shadowfacts.phycon.network.packet.CapacityPacket
|
|
||||||
import net.shadowfacts.phycon.network.packet.CheckCapacityPacket
|
|
||||||
import net.shadowfacts.phycon.network.packet.ItemStackPacket
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author shadowfacts
|
|
||||||
*/
|
|
||||||
interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsertion<Insertion>>: ItemStackPacketHandler {
|
|
||||||
|
|
||||||
val counter: Long
|
|
||||||
val dispatchStackTimeout: Long
|
|
||||||
val pendingInsertions: MutableList<Insertion>
|
|
||||||
|
|
||||||
fun createPendingInsertion(stack: ItemStack): Insertion
|
|
||||||
|
|
||||||
fun dispatchItemStack(stack: ItemStack, modifyInsertion: ((Insertion) -> Unit)? = null) {
|
|
||||||
val insertion = createPendingInsertion(stack)
|
|
||||||
modifyInsertion?.invoke(insertion)
|
|
||||||
pendingInsertions.add(insertion)
|
|
||||||
sendPacket(CheckCapacityPacket(insertion.stack, ipAddress, IPAddress.BROADCAST))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun handleCapacity(packet: CapacityPacket) {
|
|
||||||
val insertion = pendingInsertions.firstOrNull {
|
|
||||||
ItemStackUtil.areEqualIgnoreAmounts(packet.stack, it.stack)
|
|
||||||
}
|
|
||||||
if (insertion != null) {
|
|
||||||
insertion.results.add(packet.capacity to packet.stackReceiver)
|
|
||||||
if (insertion.isFinishable(this)) {
|
|
||||||
finishInsertion(insertion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun finishInsertion(insertion: Insertion): ItemStack {
|
|
||||||
pendingInsertions.remove(insertion)
|
|
||||||
|
|
||||||
// todo: also sort results by interface priority
|
|
||||||
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList()
|
|
||||||
val remaining = insertion.stack
|
|
||||||
while (!remaining.isEmpty && sortedResults.isNotEmpty()) {
|
|
||||||
val (capacity, receivingInterface) = sortedResults.removeFirst()
|
|
||||||
if (capacity <= 0) continue
|
|
||||||
sendPacket(ItemStackPacket(remaining.copy(), ipAddress, receivingInterface.ipAddress))
|
|
||||||
// todo: the destination should confirm how much was actually inserted, in case of race condition
|
|
||||||
remaining.count -= capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
return remaining
|
|
||||||
}
|
|
||||||
|
|
||||||
open class PendingInsertion<Self: PendingInsertion<Self>>(
|
|
||||||
val stack: ItemStack,
|
|
||||||
val timestamp: Long
|
|
||||||
) {
|
|
||||||
val results = mutableSetOf<Pair<Int, NetworkStackReceiver>>()
|
|
||||||
|
|
||||||
val totalCapacity: Int
|
|
||||||
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
|
|
||||||
|
|
||||||
fun isFinishable(owner: NetworkStackDispatcher<Self>): Boolean {
|
|
||||||
return totalCapacity >= stack.count || owner.counter - timestamp >= owner.dispatchStackTimeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Self, Insertion: NetworkStackDispatcher.PendingInsertion<Insertion>> Self.finishTimedOutPendingInsertions() where Self: BlockEntity, Self: NetworkStackDispatcher<Insertion> {
|
|
||||||
if (world!!.isClient) return
|
|
||||||
|
|
||||||
pendingInsertions
|
|
||||||
.filter { it.isFinishable(this) }
|
|
||||||
.forEach(::finishInsertion)
|
|
||||||
// todo: if a timed-out insertion can't be finished, we should probably retry after some time (exponential backoff?)
|
|
||||||
}
|
|
Loading…
Reference in New Issue