Convert Terminal and Interface blocks to layer 3

This commit is contained in:
Shadowfacts 2021-02-13 21:37:39 -05:00
parent fc3716153f
commit c8cdf425cb
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
18 changed files with 964 additions and 952 deletions

View File

@ -3,7 +3,7 @@ package net.shadowfacts.phycon.mixin.client;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.screen.slot.Slot;
//import net.shadowfacts.phycon.network.block.terminal.TerminalScreen;
import net.shadowfacts.phycon.network.block.terminal.TerminalScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@ -17,10 +17,10 @@ public class MixinHandledScreen {
@Inject(method = "drawSlot(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/screen/slot/Slot;)V", at = @At(value = "INVOKE", target = "enableDepthTest()V"))
private void drawSlot(MatrixStack matrixStack, Slot slot, CallbackInfo ci) {
// if ((Object)this instanceof TerminalScreen) {
// TerminalScreen self = (TerminalScreen)(Object)this;
// self.drawSlotUnderlay(matrixStack, slot);
// }
if ((Object)this instanceof TerminalScreen) {
TerminalScreen self = (TerminalScreen)(Object)this;
self.drawSlotUnderlay(matrixStack, slot);
}
}
}

View File

@ -4,6 +4,7 @@ import net.fabricmc.api.ModInitializer
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyItems
import net.shadowfacts.phycon.init.PhyScreens
/**
* @author shadowfacts
@ -16,6 +17,7 @@ object PhysicalConnectivity: ModInitializer {
PhyBlocks.init()
PhyBlockEntities.init()
PhyItems.init()
PhyScreens.init()
}
}

View File

@ -6,7 +6,7 @@ import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry
import net.minecraft.client.render.RenderLayer
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyScreens
//import net.shadowfacts.phycon.network.block.terminal.TerminalScreen
import net.shadowfacts.phycon.network.block.terminal.TerminalScreen
/**
* @author shadowfacts
@ -15,6 +15,6 @@ object PhysicalConnectivityClient: ClientModInitializer {
override fun onInitializeClient() {
BlockRenderLayerMap.INSTANCE.putBlock(PhyBlocks.CABLE, RenderLayer.getTranslucent())
// ScreenRegistry.register(PhyScreens.TERMINAL_SCREEN_HANDLER, ::TerminalScreen)
ScreenRegistry.register(PhyScreens.TERMINAL_SCREEN_HANDLER, ::TerminalScreen)
}
}

View File

@ -5,25 +5,24 @@ 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.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
import net.shadowfacts.phycon.network.block.test.DestBlock
import net.shadowfacts.phycon.network.block.test.DestBlockEntity
import net.shadowfacts.phycon.network.block.test.SourceBlock
import net.shadowfacts.phycon.network.block.test.SourceBlockEntity
//import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
//import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
//import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
//import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity
/**
* @author shadowfacts
*/
object PhyBlockEntities {
// val INTERFACE = create(::InterfaceBlockEntity, PhyBlocks.INTERFACE)
// val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL)
val INTERFACE = create(::InterfaceBlockEntity, PhyBlocks.INTERFACE)
val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL)
val SWITCH = create(::SwitchBlockEntity, PhyBlocks.SWITCH)
val SOURCE = create(::SourceBlockEntity, PhyBlocks.SOURCE)
val DEST = create(::DestBlockEntity, PhyBlocks.DEST)
@ -33,8 +32,8 @@ object PhyBlockEntities {
}
fun init() {
// register(InterfaceBlock.ID, INTERFACE)
// register(TerminalBlock.ID, TERMINAL)
register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH)
register(SourceBlock.ID, SOURCE)
register(DestBlock.ID, DEST)

View File

@ -4,7 +4,9 @@ import net.minecraft.block.Block
import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.network.block.cable.CableBlock
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
import net.shadowfacts.phycon.network.block.test.DestBlock
import net.shadowfacts.phycon.network.block.test.SourceBlock
@ -13,16 +15,16 @@ import net.shadowfacts.phycon.network.block.test.SourceBlock
*/
object PhyBlocks {
// val INTERFACE = InterfaceBlock()
// val TERMINAL = TerminalBlock()
val INTERFACE = InterfaceBlock()
val TERMINAL = TerminalBlock()
val SWITCH = SwitchBlock()
val CABLE = CableBlock()
val SOURCE = SourceBlock()
val DEST = DestBlock()
fun init() {
// register(InterfaceBlock.ID, INTERFACE)
// register(TerminalBlock.ID, TERMINAL)
register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH)
register(CableBlock.ID, CABLE)
register(SourceBlock.ID, SOURCE)

View File

@ -6,20 +6,19 @@ import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.item.ScrewdriverItem
import net.shadowfacts.phycon.network.block.cable.CableBlock
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
import net.shadowfacts.phycon.network.block.test.DestBlock
import net.shadowfacts.phycon.network.block.test.SourceBlock
//import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
//import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
/**
* @author shadowfacts
*/
object PhyItems {
// val INTERFACE = BlockItem(PhyBlocks.INTERFACE, Item.Settings())
// val TERMINAL = BlockItem(PhyBlocks.TERMINAL, Item.Settings())
val INTERFACE = BlockItem(PhyBlocks.INTERFACE, Item.Settings())
val TERMINAL = BlockItem(PhyBlocks.TERMINAL, Item.Settings())
val SWITCH = BlockItem(PhyBlocks.SWITCH, Item.Settings())
val CABLE = BlockItem(PhyBlocks.CABLE, Item.Settings())
val SOURCE = BlockItem(PhyBlocks.SOURCE, Item.Settings())
@ -28,8 +27,8 @@ object PhyItems {
val SCREWDRIVER = ScrewdriverItem()
fun init() {
// register(InterfaceBlock.ID, INTERFACE)
// register(TerminalBlock.ID, TERMINAL)
register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH)
register(CableBlock.ID, CABLE)
register(SourceBlock.ID, SOURCE)

View File

@ -1,12 +1,18 @@
package net.shadowfacts.phycon.init
import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry
import net.minecraft.screen.ScreenHandlerType
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
//import net.shadowfacts.phycon.network.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.network.block.terminal.TerminalScreenHandler
object PhyScreens {
// val TERMINAL_SCREEN_HANDLER = ScreenHandlerRegistry.registerExtended(Identifier(PhysicalConnectivity.MODID, "terminal"), ::TerminalScreenHandler)
lateinit var TERMINAL_SCREEN_HANDLER: ScreenHandlerType<TerminalScreenHandler>
private set
fun init() {
TERMINAL_SCREEN_HANDLER = ScreenHandlerRegistry.registerExtended(Identifier(PhysicalConnectivity.MODID, "terminal"), ::TerminalScreenHandler)
}
}

View File

@ -63,6 +63,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
is ARPResponseFrame -> handleARPResponse(frame)
is PacketFrame -> {
if (frame.packet.destination.isBroadcast || frame.packet.destination == ipAddress) {
println("$this ($ipAddress) received packet: ${frame.packet}")
handle(frame.packet)
}
}
@ -93,9 +94,10 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
}
override fun sendPacket(packet: Packet) {
val cached = arpTable[packet.destination]
if (cached != null) {
send(BasePacketFrame(packet, macAddress, cached))
if (packet.destination.isBroadcast) {
send(BasePacketFrame(packet, macAddress, MACAddress.BROADCAST))
} else if (arpTable.containsKey(packet.destination)) {
send(BasePacketFrame(packet, macAddress, arpTable[packet.destination]!!))
} else {
packetQueue.add(PendingPacket(packet, counter))
@ -113,9 +115,9 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
}
override fun tick() {
if (!world!!.isClient) {
counter++
counter++
if (!world!!.isClient) {
packetQueue.removeIf { entry ->
val (packet, timestamp) = entry
if (arpTable.containsKey(packet.destination)) {

View File

@ -1,128 +1,131 @@
package net.shadowfacts.phycon.network.block.netinterface
//
//import alexiil.mc.lib.attributes.AttributeList
//import alexiil.mc.lib.attributes.AttributeProvider
//import net.minecraft.block.Block
//import net.minecraft.block.BlockState
//import net.minecraft.block.Material
//import net.minecraft.block.ShapeContext
//import net.minecraft.entity.LivingEntity
//import net.minecraft.item.ItemPlacementContext
//import net.minecraft.item.ItemStack
//import net.minecraft.state.StateManager
//import net.minecraft.state.property.EnumProperty
//import net.minecraft.state.property.Properties
//import net.minecraft.util.Identifier
//import net.minecraft.util.math.BlockPos
//import net.minecraft.util.math.Direction
//import net.minecraft.util.shape.VoxelShape
//import net.minecraft.util.shape.VoxelShapes
//import net.minecraft.world.BlockView
//import net.minecraft.world.World
//import net.minecraft.world.WorldAccess
//import net.shadowfacts.phycon.PhysicalConnectivity
//import net.shadowfacts.phycon.api.NetworkComponentBlock
//import net.shadowfacts.phycon.api.Interface
//import net.shadowfacts.phycon.block.BlockWithEntity
//import net.shadowfacts.phycon.network.block.cable.CableBlock
//import java.util.*
//
///**
// * @author shadowfacts
// */
//class InterfaceBlock: BlockWithEntity<InterfaceBlockEntity>(Settings.of(Material.METAL)),
// NetworkComponentBlock,
// AttributeProvider {
//
// companion object {
// val ID = Identifier(PhysicalConnectivity.MODID, "network_interface")
// val FACING = Properties.FACING
// val CABLE_CONNECTION = EnumProperty.of("cable_connection", Direction::class.java)
// private val SIDE_SHAPES = mapOf<Direction, VoxelShape>(
// Direction.DOWN to createCuboidShape(2.0, 0.0, 2.0, 14.0, 2.0, 14.0),
// Direction.UP to createCuboidShape(2.0, 14.0, 2.0, 14.0, 16.0, 14.0),
// Direction.NORTH to createCuboidShape(2.0, 2.0, 0.0, 14.0, 14.0, 2.0),
// Direction.SOUTH to createCuboidShape(2.0, 2.0, 14.0, 14.0, 14.0, 16.0),
// Direction.WEST to createCuboidShape(0.0, 2.0, 2.0, 2.0, 14.0, 14.0),
// Direction.EAST to createCuboidShape(14.0, 2.0, 2.0, 16.0, 14.0, 14.0)
// )
// private val CENTER_SHAPES = mapOf<Direction, VoxelShape>(
// Direction.DOWN to createCuboidShape(6.0, 2.0, 6.0, 10.0, 10.0, 10.0),
// Direction.UP to createCuboidShape(6.0, 6.0, 6.0, 10.0, 14.0, 10.0),
// Direction.NORTH to createCuboidShape(6.0, 6.0, 2.0, 10.0, 10.0, 10.0),
// Direction.SOUTH to createCuboidShape(6.0, 6.0, 6.0, 10.0, 10.0, 14.0),
// Direction.WEST to createCuboidShape(2.0, 6.0, 6.0, 10.0, 10.0, 10.0),
// Direction.EAST to createCuboidShape(6.0, 6.0, 6.0, 14.0, 10.0, 10.0)
// )
//
// private val shapeCache = mutableMapOf<Pair<Direction, Direction>, VoxelShape>()
// fun getShape(facing: Direction, cableConnection: Direction): VoxelShape {
// return shapeCache.getOrPut(facing to cableConnection) {
// VoxelShapes.union(
// VoxelShapes.union(SIDE_SHAPES[facing], CENTER_SHAPES[facing]),
// CableBlock.SIDE_SHAPES[cableConnection]
// )
// }
// }
// }
//
// override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection<Direction> {
// return EnumSet.of(state[CABLE_CONNECTION])
// }
//
// override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: World, pos: BlockPos): Interface? {
// return getBlockEntity(world, pos)?.deviceInterfaces?.first()
// }
//
// override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
// super.appendProperties(builder)
// builder.add(FACING)
// builder.add(CABLE_CONNECTION)
// }
//
// override fun createBlockEntity(world: BlockView) = InterfaceBlockEntity()
//
// override fun getPlacementState(context: ItemPlacementContext): BlockState {
// val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerFacing.opposite
// val cableConnection = getCableConnectionSide(context.world, context.blockPos) ?: facing.opposite
// return defaultState.with(FACING, facing).with(CABLE_CONNECTION, cableConnection)
// }
//
// private fun getCableConnectionSide(world: World, pos: BlockPos): Direction? {
// for (side in Direction.values()) {
// val offsetPos = pos.offset(side)
// if (world.getBlockState(offsetPos).block is NetworkComponentBlock) {
// return side
// }
// }
// return null
// }
//
// override fun onPlaced(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity?, stack: ItemStack) {
// if (!world.isClient) {
// getBlockEntity(world, pos)!!.updateInventory()
// }
// }
//
// override fun neighborUpdate(state: BlockState, world: World, pos: BlockPos, neighborBlock: Block, neighborPos: BlockPos, boolean_1: Boolean) {
// if (!world.isClient) {
// getBlockEntity(world, pos)!!.updateInventory()
// }
// }
//
// override fun getStateForNeighborUpdate(state: BlockState, side: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState {
// if (neighborState.block is NetworkComponentBlock && world.getBlockState(pos.offset(state[CABLE_CONNECTION])).block !is NetworkComponentBlock) {
// return state.with(CABLE_CONNECTION, side)
// }
// return state
// }
//
// override fun addAllAttributes(world: World, pos: BlockPos, state: BlockState, to: AttributeList<*>) {
// to.offer(getBlockEntity(world, pos))
// }
//
// override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
// return getShape(state[FACING], state[CABLE_CONNECTION])
// }
//
//}
import alexiil.mc.lib.attributes.AttributeList
import alexiil.mc.lib.attributes.AttributeProvider
import net.minecraft.block.*
import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.state.StateManager
import net.minecraft.state.property.EnumProperty
import net.minecraft.state.property.Properties
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.network.DeviceBlock
import net.shadowfacts.phycon.network.block.cable.CableBlock
import java.util.*
/**
* @author shadowfacts
*/
class InterfaceBlock: DeviceBlock<InterfaceBlockEntity>(Settings.of(Material.METAL)),
NetworkComponentBlock,
AttributeProvider {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "network_interface")
val FACING = Properties.FACING
val CABLE_CONNECTION = EnumProperty.of("cable_connection", Direction::class.java)
private val SIDE_SHAPES = mapOf<Direction, VoxelShape>(
Direction.DOWN to createCuboidShape(2.0, 0.0, 2.0, 14.0, 2.0, 14.0),
Direction.UP to createCuboidShape(2.0, 14.0, 2.0, 14.0, 16.0, 14.0),
Direction.NORTH to createCuboidShape(2.0, 2.0, 0.0, 14.0, 14.0, 2.0),
Direction.SOUTH to createCuboidShape(2.0, 2.0, 14.0, 14.0, 14.0, 16.0),
Direction.WEST to createCuboidShape(0.0, 2.0, 2.0, 2.0, 14.0, 14.0),
Direction.EAST to createCuboidShape(14.0, 2.0, 2.0, 16.0, 14.0, 14.0)
)
private val CENTER_SHAPES = mapOf<Direction, VoxelShape>(
Direction.DOWN to createCuboidShape(6.0, 2.0, 6.0, 10.0, 10.0, 10.0),
Direction.UP to createCuboidShape(6.0, 6.0, 6.0, 10.0, 14.0, 10.0),
Direction.NORTH to createCuboidShape(6.0, 6.0, 2.0, 10.0, 10.0, 10.0),
Direction.SOUTH to createCuboidShape(6.0, 6.0, 6.0, 10.0, 10.0, 14.0),
Direction.WEST to createCuboidShape(2.0, 6.0, 6.0, 10.0, 10.0, 10.0),
Direction.EAST to createCuboidShape(6.0, 6.0, 6.0, 14.0, 10.0, 10.0)
)
private val shapeCache = mutableMapOf<Pair<Direction, Direction>, VoxelShape>()
fun getShape(facing: Direction, cableConnection: Direction): VoxelShape {
return shapeCache.getOrPut(facing to cableConnection) {
VoxelShapes.union(
VoxelShapes.union(SIDE_SHAPES[facing], CENTER_SHAPES[facing]),
CableBlock.SIDE_SHAPES[cableConnection]
)
}
}
}
override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection<Direction> {
val set = EnumSet.of(state[CABLE_CONNECTION])
set.remove(state[FACING])
return set
}
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: World, pos: BlockPos): Interface? {
return if (side == state[FACING]) {
null
} else {
getBlockEntity(world, pos)
}
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
super.appendProperties(builder)
builder.add(FACING)
builder.add(CABLE_CONNECTION)
}
override fun createBlockEntity(world: BlockView) = InterfaceBlockEntity()
override fun getPlacementState(context: ItemPlacementContext): BlockState {
val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerFacing.opposite
val cableConnection = getCableConnectionSide(context.world, context.blockPos) ?: facing.opposite
return defaultState.with(FACING, facing).with(CABLE_CONNECTION, cableConnection)
}
private fun getCableConnectionSide(world: World, pos: BlockPos): Direction? {
for (side in Direction.values()) {
val offsetPos = pos.offset(side)
if (world.getBlockState(offsetPos).block is NetworkComponentBlock) {
return side
}
}
return null
}
override fun onPlaced(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity?, stack: ItemStack) {
if (!world.isClient) {
getBlockEntity(world, pos)!!.updateInventory()
}
}
override fun neighborUpdate(state: BlockState, world: World, pos: BlockPos, neighborBlock: Block, neighborPos: BlockPos, boolean_1: Boolean) {
if (!world.isClient) {
getBlockEntity(world, pos)!!.updateInventory()
}
}
override fun getStateForNeighborUpdate(state: BlockState, side: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState {
if (neighborState.block is NetworkComponentBlock && world.getBlockState(pos.offset(state[CABLE_CONNECTION])).block !is NetworkComponentBlock) {
return state.with(CABLE_CONNECTION, side)
}
return state
}
override fun addAllAttributes(world: World, pos: BlockPos, state: BlockState, to: AttributeList<*>) {
to.offer(getBlockEntity(world, pos))
}
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
return getShape(state[FACING], state[CABLE_CONNECTION])
}
}

View File

@ -1,93 +1,89 @@
package net.shadowfacts.phycon.network.block.netinterface
//
//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.util.math.Direction
//import net.shadowfacts.phycon.api.Interface
//import net.shadowfacts.phycon.api.packet.Packet
//import net.shadowfacts.phycon.init.PhyBlockEntities
//import net.shadowfacts.phycon.network.BaseInterface
//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 {
//
// override val interfaces = listOf(BaseInterface(this))
//
// private val facing: Direction
// get() = world!!.getBlockState(pos)[InterfaceBlock.FACING]
//
// // todo: should this be a weak ref?
// private var inventory: GroupedItemInv? = null
//
// fun updateInventory() {
// val offsetPos = pos.offset(facing)
// val option = SearchOptions.inDirection(facing)
// inventory = ItemAttributes.GROUPED_INV.getFirstOrNull(world, offsetPos, option)
// }
//
// private fun getInventory(): GroupedItemInv? {
// // 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
// }
//
// override fun handle(packet: Packet, itf: Interface) {
// when (packet) {
// is RequestInventoryPacket -> handleRequestInventory(packet)
// is LocateStackPacket -> handleLocateStack(packet)
// is ExtractStackPacket -> handleExtractStack(packet)
// is CheckCapacityPacket -> handleCheckCapacity(packet)
// is ItemStackPacket -> handleItemStack(packet)
// }
// }
//
// private fun handleRequestInventory(packet: RequestInventoryPacket) {
// getInventory()?.also { inv ->
// sendPacket(ReadInventoryPacket(inv, ipAddress, packet.source))
// }
// }
//
// private fun handleLocateStack(packet: LocateStackPacket) {
// getInventory()?.also { inv ->
// val amount = inv.getAmount(packet.stack)
// sendPacket(StackLocationPacket(packet.stack, amount, this, ipAddress, packet.source))
// }
// }
//
// private fun handleExtractStack(packet: ExtractStackPacket) {
// getInventory()?.also { inv ->
// val extracted = inv.extract(packet.stack, packet.amount)
// sendPacket(ItemStackPacket(extracted, ipAddress, packet.source))
// }
// }
//
// private fun handleCheckCapacity(packet: CheckCapacityPacket) {
// getInventory()?.also { inv ->
// val remaining = inv.attemptInsertion(packet.stack, Simulation.SIMULATE)
// val couldAccept = packet.stack.count - remaining.count
// sendPacket(CapacityPacket(packet.stack, couldAccept, this, ipAddress, packet.source))
// }
// }
//
// override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
// 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
// } else {
// return packet.stack
// }
// }
//
//}
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.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 {
private val facing: Direction
get() = world!!.getBlockState(pos)[InterfaceBlock.FACING]
// todo: should this be a weak ref?
private var inventory: GroupedItemInv? = null
fun updateInventory() {
val offsetPos = pos.offset(facing)
val option = SearchOptions.inDirection(facing)
inventory = ItemAttributes.GROUPED_INV.getFirstOrNull(world, offsetPos, option)
}
private fun getInventory(): GroupedItemInv? {
// 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
}
override fun handle(packet: Packet) {
when (packet) {
is RequestInventoryPacket -> handleRequestInventory(packet)
is LocateStackPacket -> handleLocateStack(packet)
is ExtractStackPacket -> handleExtractStack(packet)
is CheckCapacityPacket -> handleCheckCapacity(packet)
is ItemStackPacket -> handleItemStack(packet)
}
}
private fun handleRequestInventory(packet: RequestInventoryPacket) {
getInventory()?.also { inv ->
sendPacket(ReadInventoryPacket(inv, ipAddress, packet.source))
}
}
private fun handleLocateStack(packet: LocateStackPacket) {
getInventory()?.also { inv ->
val amount = inv.getAmount(packet.stack)
sendPacket(StackLocationPacket(packet.stack, amount, this, ipAddress, packet.source))
}
}
private fun handleExtractStack(packet: ExtractStackPacket) {
getInventory()?.also { inv ->
val extracted = inv.extract(packet.stack, packet.amount)
sendPacket(ItemStackPacket(extracted, ipAddress, packet.source))
}
}
private fun handleCheckCapacity(packet: CheckCapacityPacket) {
getInventory()?.also { inv ->
val remaining = inv.attemptInsertion(packet.stack, Simulation.SIMULATE)
val couldAccept = packet.stack.count - remaining.count
sendPacket(CapacityPacket(packet.stack, couldAccept, this, ipAddress, packet.source))
}
}
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
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
} else {
return packet.stack
}
}
}

View File

@ -3,14 +3,17 @@ package net.shadowfacts.phycon.network.block.netswitch
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntity
import net.minecraft.entity.ItemEntity
import net.minecraft.nbt.CompoundTag
import net.minecraft.util.Tickable
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.frame.EthernetFrame
import net.shadowfacts.phycon.api.frame.PacketFrame
import net.shadowfacts.phycon.api.util.MACAddress
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.NetworkUtil
import net.shadowfacts.phycon.network.packet.ItemStackPacket
import java.lang.ref.WeakReference
/**
@ -21,45 +24,13 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
Tickable {
companion object {
var SWITCHING_CAPACITY = 256
var SWITCHING_CAPACITY = 256 // 256 items/tick
}
val interfaces = Direction.values().map { SwitchInterface(it, WeakReference(this), MACAddress.random()) }
private val macTable = mutableMapOf<MACAddress, Direction>()
private var packetsHandledThisTick = 0
// override fun handle(packet: Packet, itf: Interface) {
// if (packetsHandledThisTick >= SWITCHING_CAPACITY) {
// 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++
//
// if (packet.destination.isBroadcast) {
//// for (other in interfaces) {
//// if (other == itf) continue
//// sendPacket(packet, other)
//// }
// flood(packet, itf)
// } else {
// val direction = ipTable[packet.destination]
// if (direction != null) {
// sendPacket()
//// val dest = findDestination(direction)
//// if (dest != null && packet.destination == dest.macAddress) {
//// sendPacke(packet, dest)
//// return
//// }
// }
// flood(packet, itf)
// }
// }
private var itemsHandledThisTick = 0
fun interfaceForSide(side: Direction): SwitchInterface {
return interfaces.find { it.side == side }!!
@ -69,10 +40,22 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
val itfSide = (fromItf as SwitchInterface).side
macTable[frame.source] = itfSide
val knownDir = macTable[frame.destination]
if (knownDir != null) {
println("$this ($itfSide, ${fromItf.macAddress}) forwarding $frame to side $knownDir")
interfaceForSide(knownDir).send(frame)
if (frame is PacketFrame && frame.packet is ItemStackPacket) {
val packet = frame.packet as ItemStackPacket
if (itemsHandledThisTick + packet.stack.count > SWITCHING_CAPACITY) {
// 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
} else {
itemsHandledThisTick += packet.stack.count
}
}
if (frame.destination.type != MACAddress.Type.BROADCAST && macTable.containsKey(frame.destination)) {
val dir = macTable[frame.destination]!!
println("$this ($itfSide, ${fromItf.macAddress}) forwarding $frame to side $dir")
interfaceForSide(dir).send(frame)
} else {
println("$this ($itfSide, ${fromItf.macAddress}) flooding $frame")
flood(frame, fromItf)
@ -92,7 +75,7 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
}
override fun tick() {
packetsHandledThisTick = 0
itemsHandledThisTick = 0
}
override fun toTag(tag: CompoundTag): CompoundTag {

View File

@ -1,44 +1,46 @@
package net.shadowfacts.phycon.network.block.terminal
//
//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.entity.player.PlayerEntity
//import net.minecraft.util.ActionResult
//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.util.math.Direction
//import net.minecraft.world.BlockView
//import net.minecraft.world.World
//import net.shadowfacts.phycon.PhysicalConnectivity
//import net.shadowfacts.phycon.api.NetworkComponentBlock
//import net.shadowfacts.phycon.block.BlockWithEntity
//import java.util.*
//
///**
// * @author shadowfacts
// */
//class TerminalBlock: BlockWithEntity<TerminalBlockEntity>(Settings.of(Material.METAL)),
// NetworkComponentBlock, AttributeProvider {
// companion object {
// val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
// }
//
// override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection<Direction> {
// return EnumSet.allOf(Direction::class.java)
// }
//
// override fun createBlockEntity(world: BlockView) = TerminalBlockEntity()
//
// override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hitResult: BlockHitResult): ActionResult {
// getBlockEntity(world, pos)!!.onActivate(player)
// return ActionResult.SUCCESS
// }
//
// override fun addAllAttributes(world: World, pos: BlockPos, state: BlockState, to: AttributeList<*>) {
// to.offer(getBlockEntity(world, pos))
// }
//}
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.entity.player.PlayerEntity
import net.minecraft.util.ActionResult
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.util.math.Direction
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.network.DeviceBlock
import java.util.*
/**
* @author shadowfacts
*/
class TerminalBlock: DeviceBlock<TerminalBlockEntity>(Settings.of(Material.METAL)),
NetworkComponentBlock,
AttributeProvider {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
}
override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection<Direction> {
return EnumSet.allOf(Direction::class.java)
}
override fun createBlockEntity(world: BlockView) = TerminalBlockEntity()
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hitResult: BlockHitResult): ActionResult {
getBlockEntity(world, pos)!!.onActivate(player)
return ActionResult.SUCCESS
}
override fun addAllAttributes(world: World, pos: BlockPos, state: BlockState, to: AttributeList<*>) {
to.offer(getBlockEntity(world, pos))
}
}

View File

@ -1,330 +1,349 @@
package net.shadowfacts.phycon.network.block.terminal
//
//import alexiil.mc.lib.attributes.item.GroupedItemInv
//import alexiil.mc.lib.attributes.item.ItemStackCollections
//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.screenhandler.v1.ExtendedScreenHandlerFactory
//import net.minecraft.block.BlockState
//import net.minecraft.entity.player.PlayerEntity
//import net.minecraft.entity.player.PlayerInventory
//import net.minecraft.inventory.Inventory
//import net.minecraft.inventory.InventoryChangedListener
//import net.minecraft.item.ItemStack
//import net.minecraft.nbt.CompoundTag
//import net.minecraft.nbt.ListTag
//import net.minecraft.network.PacketByteBuf
//import net.minecraft.screen.ScreenHandler
//import net.minecraft.server.network.ServerPlayerEntity
//import net.minecraft.text.LiteralText
//import net.minecraft.util.Tickable
//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.block.netinterface.InterfaceBlockEntity
//import net.shadowfacts.phycon.network.component.ItemStackPacketHandler
//import net.shadowfacts.phycon.network.component.handleItemStack
//import net.shadowfacts.phycon.network.packet.*
//import java.util.*
//import kotlin.math.min
//
///**
// * @author shadowfacts
// */
//class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), InventoryChangedListener, BlockEntityClientSerializable, Tickable, ItemStackPacketHandler {
//
// companion object {
// val LOCATE_REQUEST_TIMEOUT = 40 // ticks
// val INSERTION_TIMEOUT = 40
// }
//
// 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>()
//
// private var observers = 0
// val cachedNetItems = ItemStackCollections.intMap()
// var cachedSortedNetItems = listOf<ItemStack>()
// var counter = 0
//
// init {
// internalBuffer.addListener(this)
// }
//
// override fun handlePacket(packet: Packet) {
// when (packet) {
// is ReadInventoryPacket -> handleReadInventory(packet)
// is DeviceRemovedPacket -> handleDeviceRemoved(packet)
// is StackLocationPacket -> handleStackLocation(packet)
// is ItemStackPacket -> handleItemStack(packet)
// is CapacityPacket -> handleCapacity(packet)
// }
// }
//
// private fun handleReadInventory(packet: ReadInventoryPacket) {
// inventoryCache[packet.source] = packet.inventory
// updateNetItems()
// sync()
// }
//
// private fun handleDeviceRemoved(packet: DeviceRemovedPacket) {
// inventoryCache.remove(packet.source)
// updateNetItems()
// sync()
// }
//
// private fun handleStackLocation(packet: StackLocationPacket) {
// val request = pendingRequests.firstOrNull {
// ItemStackUtil.areEqualIgnoreAmounts(it.stack, packet.stack)
// }
// 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) {
// stackLocateRequestCompleted(request)
// }
// }
// }
//
// override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
// val remaining = internalBuffer.insertFromNetwork(packet.stack)
//
// // 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) {
// val insertion = pendingInsertions.values.firstOrNull {
// ItemStackUtil.areEqualIgnoreAmounts(packet.stack, it.stack)
// }
// if (insertion != null) {
// insertion.results.add(packet.capacity to packet.receivingInterface)
// if (insertion.isFinishable(counter)) {
// finishInsertion(insertion)
// }
// }
// }
//
// private fun updateNetItems() {
// cachedNetItems.clear()
// for (inventory in inventoryCache.values) {
// for (stack in inventory.storedStacks) {
// val amount = inventory.getAmount(stack)
// cachedNetItems.mergeInt(stack, amount) { a, b -> a + b }
// }
// }
// // todo: is the map necessary or is just the sorted list enough?
// cachedSortedNetItems = cachedNetItems.object2IntEntrySet().sortedByDescending { it.intValue }.map {
// val stack = it.key.copy()
// stack.count = it.intValue
// stack
// }
// }
//
// private fun beginInsertions() {
// if (world!!.isClient) return
//
// for (slot in 0 until internalBuffer.size()) {
// if (internalBuffer.getMode(slot) != TerminalBufferInventory.Mode.TO_NETWORK) continue
// if (slot in pendingInsertions) continue
// val stack = internalBuffer.getStack(slot)
// pendingInsertions[slot] = PendingStackInsertion(slot, stack, counter)
// sendToSingle(CheckCapacityPacket(stack, macAddress, MACAddress.BROADCAST))
// }
// }
//
// 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++
// }
//
// fun removeObserver() {
// observers--
// }
//
// override fun tick() {
// if (observers > 0 && (++counter % 10) == 0) {
// if (world!!.isClient) {
// println(cachedNetItems)
// } else {
// updateNetItems()
// sendEnqueuedLocateRequests()
// finishPendingRequests()
// beginInsertions()
// finishPendingInsertions()
// sync()
// }
// }
// }
//
// fun onActivate(player: PlayerEntity) {
// if (!world!!.isClient) {
// updateNetItems()
// sync()
//
// inventoryCache.clear()
// sendToSingle(RequestInventoryPacket(macAddress))
// val factory = object: ExtendedScreenHandlerFactory {
// override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler? {
// return TerminalScreenHandler(syncId, playerInv, this@TerminalBlockEntity)
// }
//
// override fun getDisplayName() = LiteralText("Terminal")
//
// override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) {
// buf.writeBlockPos(this@TerminalBlockEntity.pos)
// }
// }
// player.openHandledScreen(factory)
// }
// addObserver()
// }
//
// fun requestItem(stack: ItemStack, amount: Int = stack.count) {
// locateRequestQueue.add(StackLocateRequest(stack, amount, counter))
// }
//
// 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
// while (amountRequested < request.amount && sortedResults.isNotEmpty()) {
// val (sourceAmount, sourceInterface) = sortedResults.removeAt(0)
// val amountToRequest = min(sourceAmount, request.amount - amountRequested)
// amountRequested += amountToRequest
// sendToSingle(ExtractStackPacket(request.stack, amountToRequest, macAddress, sourceInterface.macAddress))
// }
// }
//
// private fun finishInsertion(insertion: PendingStackInsertion) {
// pendingInsertions.remove(insertion.bufferSlot)
//
// // 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.removeAt(0)
// if (capacity <= 0) continue
// sendToSingle(ItemStackPacket(remaining.copy(), macAddress, receivingInterface.macAddress))
// // todo: the interface should confirm how much was actually inserted, in case of race condition
// remaining.count -= capacity
// }
// internalBuffer.setStack(insertion.bufferSlot, remaining)
//
// // as with extracting, we "know" the new amounts and so can update instantly without actually sending out packets
// updateNetItems()
// sync()
// }
//
// override fun onInventoryChanged(inv: Inventory) {
// if (inv == internalBuffer && world != null && !world!!.isClient) {
// markDirty()
// sync()
// }
// }
//
// override fun toTag(tag: CompoundTag): CompoundTag {
// tag.put("InternalBuffer", internalBuffer.toTag())
// return super.toTag(tag)
// }
//
// override fun fromTag(state: BlockState, tag: CompoundTag) {
// super.fromTag(state, tag)
// internalBuffer.fromTag(tag.getCompound("InternalBuffer"))
// }
//
// override fun toClientTag(tag: CompoundTag): CompoundTag {
// tag.put("InternalBuffer", internalBuffer.toTag())
// val list = ListTag()
// tag.put("CachedNetItems", list)
// for ((stack, amount) in cachedNetItems) {
// val entryTag = stack.toTag(CompoundTag())
// entryTag.putInt("NetAmount", amount)
// list.add(entryTag)
// }
// return tag
// }
//
// override fun fromClientTag(tag: CompoundTag) {
// internalBuffer.fromTag(tag.getCompound("InternalBuffer"))
// val list = tag.getList("CachedNetItems", 10)
// cachedNetItems.clear()
// for (entryTag in list) {
// val stack = ItemStack.fromTag(entryTag as CompoundTag)
// val netAmount = entryTag.getInt("NetAmount")
// cachedNetItems[stack] = netAmount
// }
// cachedSortedNetItems = cachedNetItems.object2IntEntrySet().sortedByDescending { it.intValue }.map {
// val stack = it.key.copy()
// stack.count = it.intValue
// stack
// }
// }
//
//}
//
//data class StackLocateRequest(
// val stack: ItemStack,
// val amount: Int,
// val timestamp: Int,
// var results: MutableSet<Pair<Int, InterfaceBlockEntity>> = mutableSetOf()
//) {
// 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(
// val bufferSlot: Int,
// val stack: ItemStack,
// val timestamp: Int,
// val results: MutableSet<Pair<Int, InterfaceBlockEntity>> = mutableSetOf(),
//) {
// 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
// }
//}
import alexiil.mc.lib.attributes.item.GroupedItemInv
import alexiil.mc.lib.attributes.item.ItemStackCollections
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.screenhandler.v1.ExtendedScreenHandlerFactory
import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.inventory.Inventory
import net.minecraft.inventory.InventoryChangedListener
import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.network.PacketByteBuf
import net.minecraft.screen.ScreenHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.LiteralText
import net.minecraft.util.Tickable
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.NetworkUtil
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 java.util.*
import kotlin.math.min
/**
* @author shadowfacts
*/
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), InventoryChangedListener, BlockEntityClientSerializable, Tickable, ItemStackPacketHandler {
companion object {
val LOCATE_REQUEST_TIMEOUT = 40 // ticks
val INSERTION_TIMEOUT = 40
}
private val inventoryCache = mutableMapOf<IPAddress, GroupedItemInv>()
val internalBuffer = TerminalBufferInventory(18)
private val locateRequestQueue = LinkedList<StackLocateRequest>()
private val pendingRequests = LinkedList<StackLocateRequest>()
private val pendingInsertions = Int2ObjectArrayMap<PendingStackInsertion>()
private var observers = 0
val cachedNetItems = ItemStackCollections.intMap()
var cachedSortedNetItems = listOf<ItemStack>()
init {
internalBuffer.addListener(this)
}
override fun findDestination(): Interface? {
for (dir in Direction.values()) {
val itf = NetworkUtil.findConnectedInterface(world!!, pos, dir)
if (itf != null) {
return itf
}
}
return null
}
override fun handle(packet: Packet) {
when (packet) {
is ReadInventoryPacket -> handleReadInventory(packet)
is DeviceRemovedPacket -> handleDeviceRemoved(packet)
is StackLocationPacket -> handleStackLocation(packet)
is ItemStackPacket -> handleItemStack(packet)
is CapacityPacket -> handleCapacity(packet)
}
}
private fun handleReadInventory(packet: ReadInventoryPacket) {
inventoryCache[packet.source] = packet.inventory
updateNetItems()
sync()
}
private fun handleDeviceRemoved(packet: DeviceRemovedPacket) {
inventoryCache.remove(packet.source)
updateNetItems()
sync()
}
private fun handleStackLocation(packet: StackLocationPacket) {
val request = pendingRequests.firstOrNull {
ItemStackUtil.areEqualIgnoreAmounts(it.stack, packet.stack)
}
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) {
stackLocateRequestCompleted(request)
}
}
}
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
val remaining = internalBuffer.insertFromNetwork(packet.stack)
// 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) {
val insertion = pendingInsertions.values.firstOrNull {
ItemStackUtil.areEqualIgnoreAmounts(packet.stack, it.stack)
}
if (insertion != null) {
insertion.results.add(packet.capacity to packet.receivingInterface)
if (insertion.isFinishable(counter)) {
finishInsertion(insertion)
}
}
}
private fun updateNetItems() {
cachedNetItems.clear()
for (inventory in inventoryCache.values) {
for (stack in inventory.storedStacks) {
val amount = inventory.getAmount(stack)
cachedNetItems.mergeInt(stack, amount) { a, b -> a + b }
}
}
// todo: is the map necessary or is just the sorted list enough?
cachedSortedNetItems = cachedNetItems.object2IntEntrySet().sortedByDescending { it.intValue }.map {
val stack = it.key.copy()
stack.count = it.intValue
stack
}
}
private fun beginInsertions() {
if (world!!.isClient) return
for (slot in 0 until internalBuffer.size()) {
if (internalBuffer.getMode(slot) != TerminalBufferInventory.Mode.TO_NETWORK) continue
if (slot in pendingInsertions) continue
val stack = internalBuffer.getStack(slot)
pendingInsertions[slot] = PendingStackInsertion(slot, stack, counter)
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() {
if (world!!.isClient) return
for (request in locateRequestQueue) {
pendingRequests.add(request)
sendPacket(LocateStackPacket(request.stack, ipAddress))
}
locateRequestQueue.clear()
}
private fun finishPendingRequests() {
if (world!!.isClient) return
for (request in pendingRequests) {
if (request.isFinishable(counter)) {
stackLocateRequestCompleted(request)
}
}
}
fun addObserver() {
observers++
}
fun removeObserver() {
observers--
}
override fun tick() {
super.tick()
if (counter % 20 == 0L) {
if (!world!!.isClient) {
sendEnqueuedLocateRequests()
finishPendingRequests()
beginInsertions()
finishPendingInsertions()
}
if (observers > 0) {
if (world!!.isClient) {
println(cachedNetItems)
} else {
updateNetItems()
sync()
}
}
}
}
fun onActivate(player: PlayerEntity) {
if (!world!!.isClient) {
updateNetItems()
sync()
inventoryCache.clear()
sendPacket(RequestInventoryPacket(ipAddress))
val factory = object: ExtendedScreenHandlerFactory {
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler? {
return TerminalScreenHandler(syncId, playerInv, this@TerminalBlockEntity)
}
override fun getDisplayName() = LiteralText("Terminal")
override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) {
buf.writeBlockPos(this@TerminalBlockEntity.pos)
}
}
player.openHandledScreen(factory)
}
addObserver()
}
fun requestItem(stack: ItemStack, amount: Int = stack.count) {
locateRequestQueue.add(StackLocateRequest(stack, amount, counter))
}
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
while (amountRequested < request.amount && sortedResults.isNotEmpty()) {
val (sourceAmount, sourceInterface) = sortedResults.removeAt(0)
val amountToRequest = min(sourceAmount, request.amount - amountRequested)
amountRequested += amountToRequest
sendPacket(ExtractStackPacket(request.stack, amountToRequest, ipAddress, sourceInterface.ipAddress))
}
}
private fun finishInsertion(insertion: PendingStackInsertion) {
pendingInsertions.remove(insertion.bufferSlot)
// 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.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)
// as with extracting, we "know" the new amounts and so can update instantly without actually sending out packets
updateNetItems()
sync()
}
override fun onInventoryChanged(inv: Inventory) {
if (inv == internalBuffer && world != null && !world!!.isClient) {
markDirty()
sync()
}
}
override fun toTag(tag: CompoundTag): CompoundTag {
tag.put("InternalBuffer", internalBuffer.toTag())
return super.toTag(tag)
}
override fun fromTag(state: BlockState, tag: CompoundTag) {
super.fromTag(state, tag)
internalBuffer.fromTag(tag.getCompound("InternalBuffer"))
}
override fun toClientTag(tag: CompoundTag): CompoundTag {
tag.put("InternalBuffer", internalBuffer.toTag())
val list = ListTag()
tag.put("CachedNetItems", list)
for ((stack, amount) in cachedNetItems) {
val entryTag = stack.toTag(CompoundTag())
entryTag.putInt("NetAmount", amount)
list.add(entryTag)
}
return tag
}
override fun fromClientTag(tag: CompoundTag) {
internalBuffer.fromTag(tag.getCompound("InternalBuffer"))
val list = tag.getList("CachedNetItems", 10)
cachedNetItems.clear()
for (entryTag in list) {
val stack = ItemStack.fromTag(entryTag as CompoundTag)
val netAmount = entryTag.getInt("NetAmount")
cachedNetItems[stack] = netAmount
}
cachedSortedNetItems = cachedNetItems.object2IntEntrySet().sortedByDescending { it.intValue }.map {
val stack = it.key.copy()
stack.count = it.intValue
stack
}
}
}
data class StackLocateRequest(
val stack: ItemStack,
val amount: Int,
val timestamp: Long,
var results: MutableSet<Pair<Int, InterfaceBlockEntity>> = mutableSetOf()
) {
val totalResultAmount: Int
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
fun isFinishable(currentTimestamp: Long): Boolean {
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, InterfaceBlockEntity>> = 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
}
}

View File

@ -1,61 +1,61 @@
package net.shadowfacts.phycon.network.block.terminal
//import net.minecraft.screen.slot.Slot
//import net.minecraft.entity.player.PlayerEntity
//import net.minecraft.inventory.Inventory
//import net.minecraft.item.ItemStack
//
///**
// * @author shadowfacts
// */
//class TerminalFakeSlot(val terminal: TerminalBlockEntity, slot: Int, x: Int, y: Int): Slot(FakeInventory(terminal, slot), slot, x, y) {
//
// override fun canInsert(stack: ItemStack): Boolean {
// return false
// }
//
// override fun setStack(stack: ItemStack) {
// }
//
// override fun canTakeItems(player: PlayerEntity): Boolean {
// return false
// }
//
//}
//
//class FakeInventory(val terminal: TerminalBlockEntity, val slot: Int): Inventory {
// override fun getStack(_slot: Int): ItemStack {
// if (slot >= terminal.cachedSortedNetItems.size) return ItemStack.EMPTY
// return terminal.cachedSortedNetItems[slot]
// }
//
// override fun markDirty() {
// }
//
// override fun clear() {
// }
//
// override fun setStack(p0: Int, p1: ItemStack?) {
// }
//
// override fun removeStack(p0: Int): ItemStack {
// return ItemStack.EMPTY
// }
//
// override fun canPlayerUse(p0: PlayerEntity?): Boolean {
// return false
// }
//
// override fun size(): Int {
// return 1
// }
//
// override fun removeStack(p0: Int, p1: Int): ItemStack {
// return ItemStack.EMPTY
// }
//
// override fun isEmpty(): Boolean {
// return false
// }
//
//}
import net.minecraft.screen.slot.Slot
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.Inventory
import net.minecraft.item.ItemStack
/**
* @author shadowfacts
*/
class TerminalFakeSlot(val terminal: TerminalBlockEntity, slot: Int, x: Int, y: Int): Slot(FakeInventory(terminal, slot), slot, x, y) {
override fun canInsert(stack: ItemStack): Boolean {
return false
}
override fun setStack(stack: ItemStack) {
}
override fun canTakeItems(player: PlayerEntity): Boolean {
return false
}
}
class FakeInventory(val terminal: TerminalBlockEntity, val slot: Int): Inventory {
override fun getStack(_slot: Int): ItemStack {
if (slot >= terminal.cachedSortedNetItems.size) return ItemStack.EMPTY
return terminal.cachedSortedNetItems[slot]
}
override fun markDirty() {
}
override fun clear() {
}
override fun setStack(p0: Int, p1: ItemStack?) {
}
override fun removeStack(p0: Int): ItemStack {
return ItemStack.EMPTY
}
override fun canPlayerUse(p0: PlayerEntity?): Boolean {
return false
}
override fun size(): Int {
return 1
}
override fun removeStack(p0: Int, p1: Int): ItemStack {
return ItemStack.EMPTY
}
override fun isEmpty(): Boolean {
return false
}
}

View File

@ -1,58 +1,57 @@
package net.shadowfacts.phycon.network.block.terminal
//
//import com.mojang.blaze3d.platform.GlStateManager
//import net.minecraft.client.gui.DrawableHelper
//import net.minecraft.client.gui.screen.ingame.HandledScreen
//import net.minecraft.client.util.math.MatrixStack
//import net.minecraft.entity.player.PlayerInventory
//import net.minecraft.screen.slot.Slot
//import net.minecraft.text.LiteralText
//import net.minecraft.text.Text
//import net.minecraft.util.Identifier
//import net.shadowfacts.phycon.PhysicalConnectivity
//
///**
// * @author shadowfacts
// */
//// todo: translate title
//class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, title: Text): HandledScreen<TerminalScreenHandler>(handler, playerInv, title) {
// companion object {
// val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png")
// }
//
// init {
// backgroundWidth = 252
// backgroundHeight = 222
// }
//
// override fun drawForeground(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) {
// textRenderer.draw(matrixStack, title, 65f, 6f, 0x404040)
// textRenderer.draw(matrixStack, playerInventory.displayName, 65f, backgroundHeight - 94f, 0x404040)
// // todo: translate this
// textRenderer.draw(matrixStack, "Buffer", 7f, 6f, 0x404040)
// }
//
// override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
// GlStateManager.color4f(1f, 1f, 1f, 1f)
// client!!.textureManager.bindTexture(BACKGROUND)
// val x = (width - backgroundWidth) / 2
// val y = (height - backgroundHeight) / 2
// drawTexture(matrixStack, x, y, 0, 0, backgroundWidth, backgroundHeight)
// }
//
// @ExperimentalUnsignedTypes
// fun drawSlotUnderlay(matrixStack: MatrixStack, slot: Slot) {
// if (!handler.isBufferSlot(slot.id)) {
// return
// }
//
// val mode = handler.terminal.internalBuffer.getMode(slot.id - handler.bufferSlotsStart)
// val color: UInt = when (mode) {
// TerminalBufferInventory.Mode.TO_NETWORK -> 0xFFFF0000u
// TerminalBufferInventory.Mode.FROM_NETWORK -> 0xFF00FF00u
// else -> return
// }
// DrawableHelper.fill(matrixStack, slot.x, slot.y, slot.x + 16, slot.y + 16, color.toInt())
// }
//
//}
import com.mojang.blaze3d.platform.GlStateManager
import net.minecraft.client.gui.DrawableHelper
import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.screen.slot.Slot
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
/**
* @author shadowfacts
*/
// todo: translate title
class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, title: Text): HandledScreen<TerminalScreenHandler>(handler, playerInv, title) {
companion object {
val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png")
}
init {
backgroundWidth = 252
backgroundHeight = 222
}
override fun drawForeground(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) {
textRenderer.draw(matrixStack, title, 65f, 6f, 0x404040)
textRenderer.draw(matrixStack, playerInventory.displayName, 65f, backgroundHeight - 94f, 0x404040)
// todo: translate this
textRenderer.draw(matrixStack, "Buffer", 7f, 6f, 0x404040)
}
override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
GlStateManager.color4f(1f, 1f, 1f, 1f)
client!!.textureManager.bindTexture(BACKGROUND)
val x = (width - backgroundWidth) / 2
val y = (height - backgroundHeight) / 2
drawTexture(matrixStack, x, y, 0, 0, backgroundWidth, backgroundHeight)
}
@ExperimentalUnsignedTypes
fun drawSlotUnderlay(matrixStack: MatrixStack, slot: Slot) {
if (!handler.isBufferSlot(slot.id)) {
return
}
val mode = handler.terminal.internalBuffer.getMode(slot.id - handler.bufferSlotsStart)
val color: UInt = when (mode) {
TerminalBufferInventory.Mode.TO_NETWORK -> 0xFFFF0000u
TerminalBufferInventory.Mode.FROM_NETWORK -> 0xFF00FF00u
else -> return
}
DrawableHelper.fill(matrixStack, slot.x, slot.y, slot.x + 16, slot.y + 16, color.toInt())
}
}

View File

@ -1,161 +1,161 @@
package net.shadowfacts.phycon.network.block.terminal
//
//import net.minecraft.screen.slot.Slot
//import net.minecraft.screen.slot.SlotActionType
//import net.minecraft.entity.player.PlayerEntity
//import net.minecraft.entity.player.PlayerInventory
//import net.minecraft.item.ItemStack
//import net.minecraft.network.PacketByteBuf
//import net.minecraft.screen.ScreenHandler
//import net.minecraft.util.Identifier
//import net.shadowfacts.phycon.PhysicalConnectivity
//import net.shadowfacts.phycon.init.PhyBlocks
//import net.shadowfacts.phycon.init.PhyScreens
//import kotlin.math.ceil
//import kotlin.math.min
//
///**
// * @author shadowfacts
// */
//class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val terminal: TerminalBlockEntity): ScreenHandler(PhyScreens.TERMINAL_SCREEN_HANDLER, syncId) {
//
// companion object {
// val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
// }
//
// constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf):
// this(syncId, playerInv, PhyBlocks.TERMINAL.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!)
//
// init {
// // network
// for (y in 0 until 6) {
// for (x in 0 until 9) {
// addSlot(TerminalFakeSlot(terminal, y * 9 + x, 66 + x * 18, 18 + y * 18))
// }
// }
//
// // internal buffer
// for (y in 0 until 6) {
// for (x in 0 until 3) {
// addSlot(Slot(terminal.internalBuffer, y * 3 + x, 8 + x * 18, 18 + y * 18))
// }
// }
//
// // player inv
// for (y in 0 until 3) {
// for (x in 0 until 9) {
// addSlot(Slot(playerInv, x + y * 9 + 9, 66 + x * 18, 140 + y * 18))
// }
// }
// // hotbar
// for (x in 0 until 9) {
// addSlot(Slot(playerInv, x, 66 + x * 18, 198))
// }
// }
//
// override fun canUse(player: PlayerEntity): Boolean {
// return true
// }
//
// override fun close(player: PlayerEntity) {
// super.close(player)
//
// terminal.removeObserver()
// }
//
// override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack {
// if (isNetworkSlot(slotId)) {
// // the slot clicked was one of the network stacks
// if (actionType == SlotActionType.QUICK_MOVE) {
// val stack = slots[slotId].stack
// if (!stack.isEmpty && !player.world.isClient) {
// terminal.requestItem(stack, min(stack.count, stack.maxCount))
// }
// } else if (actionType == SlotActionType.PICKUP && clickData == 1) {
// if (clickData == 1) {
// // right click, request half stack
// val stack = slots[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
// } else if (isBufferSlot(slotId)) {
// // internal buffer
// // todo: why does this think it's quick_craft sometimes?
// if ((actionType == SlotActionType.PICKUP || actionType == SlotActionType.QUICK_CRAFT) && !player.inventory.cursorStack.isEmpty) {
// // placing cursor stack into buffer
// val bufferSlot = slotId - bufferSlotsStart // subtract 54 to convert the handler slot ID to a valid buffer index
// terminal.internalBuffer.markSlot(bufferSlot, TerminalBufferInventory.Mode.TO_NETWORK)
// }
// }
// return super.onSlotClick(slotId, clickData, actionType, player)
// }
//
// override fun transferSlot(player: PlayerEntity, slotId: Int): ItemStack {
// if (isNetworkSlot(slotId)) {
// return ItemStack.EMPTY;
// }
//
// val slot = slots[slotId]
// if (!slot.hasStack()) {
// return ItemStack.EMPTY
// }
//
// val result = slot.stack.copy()
//
// if (isBufferSlot(slotId)) {
// if (!insertItem(slot.stack, playerSlotsStart, playerSlotsEnd, false)) {
// return ItemStack.EMPTY
// }
// if (slot.stack.isEmpty) {
// terminal.internalBuffer.markSlot(slotId - bufferSlotsStart, TerminalBufferInventory.Mode.UNASSIGNED)
// }
// } else if (isPlayerSlot(slotId)) {
// val slotsInsertedInto = tryInsertItem(slot.stack, bufferSlotsStart until playerSlotsStart) { terminal.internalBuffer.getMode(it - bufferSlotsStart) != TerminalBufferInventory.Mode.FROM_NETWORK }
// slotsInsertedInto.forEach { terminal.internalBuffer.markSlot(it - bufferSlotsStart, TerminalBufferInventory.Mode.TO_NETWORK) }
// if (slot.stack.isEmpty) {
// return ItemStack.EMPTY
// }
// }
//
// return result
// }
//
// private fun tryInsertItem(stack: ItemStack, slots: IntRange, slotPredicate: (Int) -> Boolean): Collection<Int> {
// val slotsInsertedInto = mutableListOf<Int>()
// for (index in slots) {
// if (stack.isEmpty) break
// if (!slotPredicate(index)) continue
//
// val slot = this.slots[index]
// val slotStack = slot.stack
// if (slotStack.isEmpty) {
// slot.stack = stack.copy()
// stack.count = 0
//
// slot.markDirty()
// slotsInsertedInto.add(index)
// } else if (canStacksCombine(slotStack, stack) && slotStack.count < slotStack.maxCount) {
// val maxToMove = slotStack.maxCount - slotStack.count
// val toMove = min(maxToMove, stack.count)
// slotStack.increment(toMove)
// stack.decrement(toMove)
//
// slot.markDirty()
// slotsInsertedInto.add(index)
// }
// }
// return slotsInsertedInto
// }
//
// val bufferSlotsStart = 54
// val playerSlotsStart = 72
// val playerSlotsEnd = 72 + 36
// fun isNetworkSlot(id: Int) = id in 0 until bufferSlotsStart
// fun isBufferSlot(id: Int) = id in bufferSlotsStart until playerSlotsStart
// fun isPlayerSlot(id: Int) = id >= playerSlotsStart
//}
import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.ItemStack
import net.minecraft.network.PacketByteBuf
import net.minecraft.screen.ScreenHandler
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyScreens
import kotlin.math.ceil
import kotlin.math.min
/**
* @author shadowfacts
*/
class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val terminal: TerminalBlockEntity): ScreenHandler(PhyScreens.TERMINAL_SCREEN_HANDLER, syncId) {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
}
constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf):
this(syncId, playerInv, PhyBlocks.TERMINAL.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!)
init {
// network
for (y in 0 until 6) {
for (x in 0 until 9) {
addSlot(TerminalFakeSlot(terminal, y * 9 + x, 66 + x * 18, 18 + y * 18))
}
}
// internal buffer
for (y in 0 until 6) {
for (x in 0 until 3) {
addSlot(Slot(terminal.internalBuffer, y * 3 + x, 8 + x * 18, 18 + y * 18))
}
}
// player inv
for (y in 0 until 3) {
for (x in 0 until 9) {
addSlot(Slot(playerInv, x + y * 9 + 9, 66 + x * 18, 140 + y * 18))
}
}
// hotbar
for (x in 0 until 9) {
addSlot(Slot(playerInv, x, 66 + x * 18, 198))
}
}
override fun canUse(player: PlayerEntity): Boolean {
return true
}
override fun close(player: PlayerEntity) {
super.close(player)
terminal.removeObserver()
}
override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack {
if (isNetworkSlot(slotId)) {
// the slot clicked was one of the network stacks
if (actionType == SlotActionType.QUICK_MOVE) {
val stack = slots[slotId].stack
if (!stack.isEmpty && !player.world.isClient) {
terminal.requestItem(stack, min(stack.count, stack.maxCount))
}
} else if (actionType == SlotActionType.PICKUP && clickData == 1) {
if (clickData == 1) {
// right click, request half stack
val stack = slots[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
} else if (isBufferSlot(slotId)) {
// internal buffer
// todo: why does this think it's quick_craft sometimes?
if ((actionType == SlotActionType.PICKUP || actionType == SlotActionType.QUICK_CRAFT) && !player.inventory.cursorStack.isEmpty) {
// placing cursor stack into buffer
val bufferSlot = slotId - bufferSlotsStart // subtract 54 to convert the handler slot ID to a valid buffer index
terminal.internalBuffer.markSlot(bufferSlot, TerminalBufferInventory.Mode.TO_NETWORK)
}
}
return super.onSlotClick(slotId, clickData, actionType, player)
}
override fun transferSlot(player: PlayerEntity, slotId: Int): ItemStack {
if (isNetworkSlot(slotId)) {
return ItemStack.EMPTY;
}
val slot = slots[slotId]
if (!slot.hasStack()) {
return ItemStack.EMPTY
}
val result = slot.stack.copy()
if (isBufferSlot(slotId)) {
if (!insertItem(slot.stack, playerSlotsStart, playerSlotsEnd, false)) {
return ItemStack.EMPTY
}
if (slot.stack.isEmpty) {
terminal.internalBuffer.markSlot(slotId - bufferSlotsStart, TerminalBufferInventory.Mode.UNASSIGNED)
}
} else if (isPlayerSlot(slotId)) {
val slotsInsertedInto = tryInsertItem(slot.stack, bufferSlotsStart until playerSlotsStart) { terminal.internalBuffer.getMode(it - bufferSlotsStart) != TerminalBufferInventory.Mode.FROM_NETWORK }
slotsInsertedInto.forEach { terminal.internalBuffer.markSlot(it - bufferSlotsStart, TerminalBufferInventory.Mode.TO_NETWORK) }
if (slot.stack.isEmpty) {
return ItemStack.EMPTY
}
}
return result
}
private fun tryInsertItem(stack: ItemStack, slots: IntRange, slotPredicate: (Int) -> Boolean): Collection<Int> {
val slotsInsertedInto = mutableListOf<Int>()
for (index in slots) {
if (stack.isEmpty) break
if (!slotPredicate(index)) continue
val slot = this.slots[index]
val slotStack = slot.stack
if (slotStack.isEmpty) {
slot.stack = stack.copy()
stack.count = 0
slot.markDirty()
slotsInsertedInto.add(index)
} else if (canStacksCombine(slotStack, stack) && slotStack.count < slotStack.maxCount) {
val maxToMove = slotStack.maxCount - slotStack.count
val toMove = min(maxToMove, stack.count)
slotStack.increment(toMove)
stack.decrement(toMove)
slot.markDirty()
slotsInsertedInto.add(index)
}
}
return slotsInsertedInto
}
val bufferSlotsStart = 54
val playerSlotsStart = 72
val playerSlotsEnd = 72 + 36
fun isNetworkSlot(id: Int) = id in 0 until bufferSlotsStart
fun isBufferSlot(id: Int) = id in bufferSlotsStart until playerSlotsStart
fun isPlayerSlot(id: Int) = id >= playerSlotsStart
}

View File

@ -2,10 +2,10 @@ package net.shadowfacts.phycon.network.packet
import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress
//import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
/**
* @author shadowfacts
*/
//class CapacityPacket(val stack: ItemStack, val capacity: Int, val receivingInterface: InterfaceBlockEntity, source: IPAddress, destination: IPAddress): BasePacket(source, destination) {
//}
class CapacityPacket(val stack: ItemStack, val capacity: Int, val receivingInterface: InterfaceBlockEntity, source: IPAddress, destination: IPAddress): BasePacket(source, destination) {
}

View File

@ -2,17 +2,17 @@ package net.shadowfacts.phycon.network.packet
import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress
//import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
/**
* @author shadowfacts
*/
// todo: better name with LocateStackPacket
//class StackLocationPacket(
// val stack: ItemStack,
// val amount: Int,
// val sourceInterface: InterfaceBlockEntity,
// source: IPAddress,
// destination: IPAddress
//): BasePacket(source, destination) {
//}
class StackLocationPacket(
val stack: ItemStack,
val amount: Int,
val sourceInterface: InterfaceBlockEntity,
source: IPAddress,
destination: IPAddress
): BasePacket(source, destination) {
}