diff --git a/src/main/java/net/shadowfacts/phycon/mixin/client/MixinHandledScreen.java b/src/main/java/net/shadowfacts/phycon/mixin/client/MixinHandledScreen.java index 55c278f..8adf475 100644 --- a/src/main/java/net/shadowfacts/phycon/mixin/client/MixinHandledScreen.java +++ b/src/main/java/net/shadowfacts/phycon/mixin/client/MixinHandledScreen.java @@ -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); + } } } diff --git a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt index 1d435a3..2adf327 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt @@ -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() } } diff --git a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivityClient.kt b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivityClient.kt index 6785b47..4575fbb 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivityClient.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivityClient.kt @@ -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) } } diff --git a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt index 117eb52..171daff 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt @@ -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) diff --git a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt index 22bede0..1e8c1bd 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt @@ -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) diff --git a/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt b/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt index d6254a3..c48c222 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt @@ -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) diff --git a/src/main/kotlin/net/shadowfacts/phycon/init/PhyScreens.kt b/src/main/kotlin/net/shadowfacts/phycon/init/PhyScreens.kt index e6c2e51..9bd4b80 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/init/PhyScreens.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/init/PhyScreens.kt @@ -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 + private set + + fun init() { + TERMINAL_SCREEN_HANDLER = ScreenHandlerRegistry.registerExtended(Identifier(PhysicalConnectivity.MODID, "terminal"), ::TerminalScreenHandler) + } } \ No newline at end of file diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/DeviceBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/network/DeviceBlockEntity.kt index 819fe4d..019a012 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/DeviceBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/DeviceBlockEntity.kt @@ -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)) { diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/netinterface/InterfaceBlock.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/netinterface/InterfaceBlock.kt index 68672fa..60a4680 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/netinterface/InterfaceBlock.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/netinterface/InterfaceBlock.kt @@ -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(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.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.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, 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 { -// 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) { -// 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(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.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.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, 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 { + 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) { + 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]) + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/netinterface/InterfaceBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/netinterface/InterfaceBlockEntity.kt index 2caf0f1..46457d6 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/netinterface/InterfaceBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/netinterface/InterfaceBlockEntity.kt @@ -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 + } + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/netswitch/SwitchBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/netswitch/SwitchBlockEntity.kt index af14031..e51943f 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/netswitch/SwitchBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/netswitch/SwitchBlockEntity.kt @@ -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() - 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 { diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlock.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlock.kt index 2b3e513..09e2ed3 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlock.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlock.kt @@ -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(Settings.of(Material.METAL)), -// NetworkComponentBlock, AttributeProvider { -// companion object { -// val ID = Identifier(PhysicalConnectivity.MODID, "terminal") -// } -// -// override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection { -// 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(Settings.of(Material.METAL)), + NetworkComponentBlock, + AttributeProvider { + + companion object { + val ID = Identifier(PhysicalConnectivity.MODID, "terminal") + } + + override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Collection { + 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)) + } +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt index c9db65f..52e713f 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt @@ -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() -// val internalBuffer = TerminalBufferInventory(18) -// -// private val locateRequestQueue = LinkedList() -// private val pendingRequests = LinkedList() -// private val pendingInsertions = Int2ObjectArrayMap() -// -// private var observers = 0 -// val cachedNetItems = ItemStackCollections.intMap() -// var cachedSortedNetItems = listOf() -// 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> = 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> = 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() + val internalBuffer = TerminalBufferInventory(18) + + private val locateRequestQueue = LinkedList() + private val pendingRequests = LinkedList() + private val pendingInsertions = Int2ObjectArrayMap() + + private var observers = 0 + val cachedNetItems = ItemStackCollections.intMap() + var cachedSortedNetItems = listOf() + + 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> = 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> = 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 + } +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalFakeSlot.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalFakeSlot.kt index d8f7e55..d30593c 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalFakeSlot.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalFakeSlot.kt @@ -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 + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt index 75bdaa3..5666be1 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt @@ -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(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(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()) + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt index d33b4f0..76af6ee 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt @@ -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 { -// val slotsInsertedInto = mutableListOf() -// 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 { + val slotsInsertedInto = mutableListOf() + 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 +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/packet/CapacityPacket.kt b/src/main/kotlin/net/shadowfacts/phycon/network/packet/CapacityPacket.kt index 4f0f5f2..707144b 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/packet/CapacityPacket.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/packet/CapacityPacket.kt @@ -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) { -//} \ No newline at end of file +class CapacityPacket(val stack: ItemStack, val capacity: Int, val receivingInterface: InterfaceBlockEntity, source: IPAddress, destination: IPAddress): BasePacket(source, destination) { +} \ No newline at end of file diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/packet/StackLocationPacket.kt b/src/main/kotlin/net/shadowfacts/phycon/network/packet/StackLocationPacket.kt index 710446a..964db6c 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/packet/StackLocationPacket.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/packet/StackLocationPacket.kt @@ -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) { +}