From e88ecd3215a2d16e796800c1bb94c86b79cef645 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Fri, 24 Dec 2021 12:34:16 -0500 Subject: [PATCH] Add P2P inventory system --- .../phycon/PhysicalConnectivity.kt | 4 + .../phycon/block/DeviceBlockEntity.kt | 3 + .../phycon/block/miner/MinerBlockEntity.kt | 4 +- .../netinterface/InterfaceBlockEntity.kt | 5 +- .../phycon/block/p2p/P2PInterfaceBlock.kt | 37 +++++ .../block/p2p/P2PInterfaceBlockEntity.kt | 47 +++++++ .../phycon/block/p2p/P2PReceiverBlock.kt | 40 ++++++ .../block/p2p/P2PReceiverBlockEntity.kt | 133 ++++++++++++++++++ .../RedstoneEmitterBlockEntity.kt | 8 +- .../terminal/AbstractTerminalBlockEntity.kt | 4 +- .../terminal/CraftingTerminalBlockEntity.kt | 2 +- .../block/terminal/TerminalBlockEntity.kt | 2 +- .../phycon/client/PhyModelProvider.kt | 6 + .../screen/console/DeviceConsoleScreen.kt | 10 ++ .../console/P2PReceiverViewController.kt | 58 ++++++++ .../phycon/init/PhyBlockEntities.kt | 8 ++ .../net/shadowfacts/phycon/init/PhyBlocks.kt | 6 + .../net/shadowfacts/phycon/init/PhyItems.kt | 17 +++ .../phycon/item/DeviceBlockItem.kt | 8 ++ .../shadowfacts/phycon/packet/PingPacket.kt | 10 ++ ...acket.kt => ReadGroupedInventoryPacket.kt} | 2 +- .../phycon/packet/ReadItemStoragePacket.kt | 14 ++ .../phycon/packet/RequestInventoryPacket.kt | 10 +- .../phycon/blockstates/p2p_interface.json | 7 + .../phycon/blockstates/p2p_receiver.json | 7 + .../resources/assets/phycon/lang/en_us.json | 8 ++ .../models/block/p2p_interface_side.json | 33 +++++ .../models/block/p2p_receiver_side.json | 33 +++++ 28 files changed, 513 insertions(+), 13 deletions(-) create mode 100644 src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PInterfaceBlock.kt create mode 100644 src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PInterfaceBlockEntity.kt create mode 100644 src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PReceiverBlock.kt create mode 100644 src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PReceiverBlockEntity.kt create mode 100644 src/main/kotlin/net/shadowfacts/phycon/client/screen/console/P2PReceiverViewController.kt create mode 100644 src/main/kotlin/net/shadowfacts/phycon/packet/PingPacket.kt rename src/main/kotlin/net/shadowfacts/phycon/packet/{ReadInventoryPacket.kt => ReadGroupedInventoryPacket.kt} (89%) create mode 100644 src/main/kotlin/net/shadowfacts/phycon/packet/ReadItemStoragePacket.kt create mode 100644 src/main/resources/assets/phycon/blockstates/p2p_interface.json create mode 100644 src/main/resources/assets/phycon/blockstates/p2p_receiver.json create mode 100644 src/main/resources/assets/phycon/models/block/p2p_interface_side.json create mode 100644 src/main/resources/assets/phycon/models/block/p2p_receiver_side.json diff --git a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt index e784c5b..bb9629f 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt @@ -2,8 +2,10 @@ package net.shadowfacts.phycon import net.fabricmc.api.ModInitializer import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking +import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage import net.fabricmc.loader.api.FabricLoader import net.shadowfacts.phycon.api.PhyConPlugin +import net.shadowfacts.phycon.block.p2p.P2PReceiverBlockEntity import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlocks import net.shadowfacts.phycon.init.PhyItems @@ -31,6 +33,8 @@ object PhysicalConnectivity: ModInitializer { registerGlobalReceiver(C2STerminalRequestItem) registerGlobalReceiver(C2STerminalUpdateDisplayedItems) + ItemStorage.SIDED.registerForBlockEntity(P2PReceiverBlockEntity::provideItemStorage, PhyBlockEntities.P2P_RECEIVER) + for (it in FabricLoader.getInstance().getEntrypoints("phycon", PhyConPlugin::class.java)) { it.initializePhyCon(PhyConAPIImpl) } diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/DeviceBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/DeviceBlockEntity.kt index f70e5b4..326df3a 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/DeviceBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/DeviceBlockEntity.kt @@ -57,6 +57,9 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: is DeviceRemovedPacket -> { arpTable.remove(packet.source) } + is PingPacket -> { + sendPacket(PongPacket(ipAddress, packet.source)) + } } handle(packet) } diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/miner/MinerBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/miner/MinerBlockEntity.kt index 8aee1bf..4411768 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/miner/MinerBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/miner/MinerBlockEntity.kt @@ -57,10 +57,10 @@ class MinerBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockEntity(PhyB } private fun handleRequestInventory(packet: RequestInventoryPacket) { - if (minerMode != MinerMode.ON_DEMAND) { + if (minerMode != MinerMode.ON_DEMAND || packet.kind != RequestInventoryPacket.Kind.GROUPED) { return } - sendPacket(ReadInventoryPacket(invProxy, ipAddress, packet.source)) + sendPacket(ReadGroupedInventoryPacket(invProxy, ipAddress, packet.source)) } private fun handleLocateStack(packet: LocateStackPacket) { diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/netinterface/InterfaceBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/netinterface/InterfaceBlockEntity.kt index a3b8c2e..011aa3b 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/netinterface/InterfaceBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/netinterface/InterfaceBlockEntity.kt @@ -64,8 +64,11 @@ class InterfaceBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockEntity( } private fun handleRequestInventory(packet: RequestInventoryPacket) { + if (packet.kind != RequestInventoryPacket.Kind.GROUPED) { + return + } getInventory()?.also { inv -> - sendPacket(ReadInventoryPacket(inv, ipAddress, packet.source)) + sendPacket(ReadGroupedInventoryPacket(inv, ipAddress, packet.source)) } } diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PInterfaceBlock.kt b/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PInterfaceBlock.kt new file mode 100644 index 0000000..78cc869 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PInterfaceBlock.kt @@ -0,0 +1,37 @@ +package net.shadowfacts.phycon.block.p2p + +import net.minecraft.block.BlockState +import net.minecraft.block.Material +import net.minecraft.sound.BlockSoundGroup +import net.minecraft.util.Identifier +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction +import net.shadowfacts.phycon.PhysicalConnectivity +import net.shadowfacts.phycon.block.FaceDeviceBlock + +/** + * @author shadowfacts + */ +class P2PInterfaceBlock: FaceDeviceBlock( + Settings.of(Material.METAL) + .strength(1.5f) + .sounds(BlockSoundGroup.METAL) +) { + + companion object { + val ID = Identifier(PhysicalConnectivity.MODID, "p2p_interface") + } + + override val faceThickness = 4.0 + override val faceShapes = mapOf( + Direction.DOWN to createCuboidShape(0.0, 0.0, 0.0, 16.0, 4.0, 16.0), + Direction.UP to createCuboidShape(0.0, 12.0, 0.0, 16.0, 16.0, 16.0), + Direction.NORTH to createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 4.0), + Direction.SOUTH to createCuboidShape(0.0, 0.0, 12.0, 16.0, 16.0, 16.0), + Direction.WEST to createCuboidShape(0.0, 0.0, 0.0, 4.0, 16.0, 16.0), + Direction.EAST to createCuboidShape(12.0, 0.0, 0.0, 16.0, 16.0, 16.0) + ) + + override fun createBlockEntity(pos: BlockPos, state: BlockState) = P2PInterfaceBlockEntity(pos, state) + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PInterfaceBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PInterfaceBlockEntity.kt new file mode 100644 index 0000000..4f326af --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PInterfaceBlockEntity.kt @@ -0,0 +1,47 @@ +package net.shadowfacts.phycon.block.p2p + +import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage +import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant +import net.fabricmc.fabric.api.transfer.v1.storage.Storage +import net.minecraft.block.BlockState +import net.minecraft.util.math.BlockPos +import net.shadowfacts.phycon.api.packet.Packet +import net.shadowfacts.phycon.block.DeviceBlockEntity +import net.shadowfacts.phycon.block.FaceDeviceBlock +import net.shadowfacts.phycon.init.PhyBlockEntities +import net.shadowfacts.phycon.packet.ReadItemStoragePacket +import net.shadowfacts.phycon.packet.RequestInventoryPacket + +/** + * @author shadowfacts + */ +class P2PInterfaceBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockEntity(PhyBlockEntities.P2P_INTERFACE, pos, state) { + + private var inventory: Storage? = null + + private fun updateInventory() { + val facing = cachedState[FaceDeviceBlock.FACING] + inventory = ItemStorage.SIDED.find(world!!, pos.offset(facing), facing.opposite) + } + + private fun getInventory(): Storage? { + if (inventory == null) updateInventory() + return inventory + } + + override fun handle(packet: Packet) { + when (packet) { + is RequestInventoryPacket -> handleRequestInventory(packet) + } + } + + private fun handleRequestInventory(packet: RequestInventoryPacket) { + if (packet.kind != RequestInventoryPacket.Kind.SIDED) { + return + } + getInventory()?.also { + sendPacket(ReadItemStoragePacket(it, ipAddress, packet.source)) + } + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PReceiverBlock.kt b/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PReceiverBlock.kt new file mode 100644 index 0000000..4df2b25 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PReceiverBlock.kt @@ -0,0 +1,40 @@ +package net.shadowfacts.phycon.block.p2p + +import net.minecraft.block.BlockState +import net.minecraft.block.InventoryProvider +import net.minecraft.block.Material +import net.minecraft.inventory.SidedInventory +import net.minecraft.sound.BlockSoundGroup +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.world.WorldAccess +import net.shadowfacts.phycon.PhysicalConnectivity +import net.shadowfacts.phycon.block.FaceDeviceBlock + +/** + * @author shadowfacts + */ +class P2PReceiverBlock: FaceDeviceBlock( + Settings.of(Material.METAL) + .strength(1.5f) + .sounds(BlockSoundGroup.METAL) +) { + + companion object { + val ID = Identifier(PhysicalConnectivity.MODID, "p2p_receiver") + } + + override val faceThickness = 4.0 + override val faceShapes = mapOf( + Direction.DOWN to createCuboidShape(0.0, 0.0, 0.0, 16.0, 4.0, 16.0), + Direction.UP to createCuboidShape(0.0, 12.0, 0.0, 16.0, 16.0, 16.0), + Direction.NORTH to createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 4.0), + Direction.SOUTH to createCuboidShape(0.0, 0.0, 12.0, 16.0, 16.0, 16.0), + Direction.WEST to createCuboidShape(0.0, 0.0, 0.0, 4.0, 16.0, 16.0), + Direction.EAST to createCuboidShape(12.0, 0.0, 0.0, 16.0, 16.0, 16.0) + ) + + override fun createBlockEntity(pos: BlockPos, state: BlockState) = P2PReceiverBlockEntity(pos, state) +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PReceiverBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PReceiverBlockEntity.kt new file mode 100644 index 0000000..6b3168f --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/p2p/P2PReceiverBlockEntity.kt @@ -0,0 +1,133 @@ +package net.shadowfacts.phycon.block.p2p + +import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant +import net.fabricmc.fabric.api.transfer.v1.storage.Storage +import net.minecraft.block.BlockState +import net.minecraft.nbt.NbtCompound +import net.minecraft.text.Text +import net.minecraft.text.TranslatableText +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction +import net.shadowfacts.phycon.api.packet.Packet +import net.shadowfacts.phycon.api.util.IPAddress +import net.shadowfacts.phycon.block.DeviceBlockEntity +import net.shadowfacts.phycon.block.FaceDeviceBlock +import net.shadowfacts.phycon.init.PhyBlockEntities +import net.shadowfacts.phycon.packet.* +import net.shadowfacts.phycon.util.ClientConfigurableDevice + +/** + * @author shadowfacts + */ +class P2PReceiverBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockEntity(PhyBlockEntities.P2P_RECEIVER, pos, state), + ClientConfigurableDevice { + + enum class Status { + OK, + NO_TARGET, + WAITING_FOR_RESPONSE; + + val displayName: Text + get() = when (this) { + OK -> TranslatableText("gui.phycon.p2p_receiver.status.ok") + NO_TARGET -> TranslatableText("gui.phycon.p2p_receiver.status.no_target") + WAITING_FOR_RESPONSE -> TranslatableText("gui.phycon.p2p_receiver.status.waiting_for_response") + } + } + + companion object { + fun provideItemStorage(be: P2PReceiverBlockEntity, side: Direction): Storage? { + if (side == be.cachedState[FaceDeviceBlock.FACING]) { + return be.getTargetInventory() + } + return null + } + } + + var target: IPAddress? = null + var status = Status.NO_TARGET + + var clientObserver: (() -> Unit)? = null + + private var isFirstTick = true + // todo: need some way of removing this when there's no network path to the p2p interface + private var targetInventory: Storage? = null + + override fun handle(packet: Packet) { + when (packet) { + is PongPacket -> if (packet.source == target) status = Status.OK + is ReadItemStoragePacket -> targetInventory = packet.inventory + is DeviceRemovedPacket -> if (packet.source == target) targetInventory = null + } + } + + override fun tick() { + super.tick() + + if (isFirstTick) { + isFirstTick = false + updateStatus() + } + } + + fun getTargetInventory(): Storage? { + if (target == null) { + return null + } + if (targetInventory == null) { + sendPacket(RequestInventoryPacket(RequestInventoryPacket.Kind.SIDED, ipAddress, target!!)) + } + return targetInventory + } + + private fun updateStatus() { + if (world?.isClient != false) { + return + } + assert(!world!!.isClient) + if (target == null) { + status = Status.NO_TARGET + } else { + status = Status.WAITING_FOR_RESPONSE + sendPacket(PingPacket(ipAddress, target!!)) + } + + markUpdate() + } + + override fun toCommonTag(tag: NbtCompound) { + super.toCommonTag(tag) + writeDeviceConfiguration(tag) + } + + override fun fromCommonTag(tag: NbtCompound) { + super.fromCommonTag(tag) + loadDeviceConfiguration(tag) + } + + override fun toClientTag(tag: NbtCompound): NbtCompound { + tag.putInt("Status", status.ordinal) + return super.toClientTag(tag) + } + + override fun fromClientTag(tag: NbtCompound) { + super.fromClientTag(tag) + status = Status.values()[tag.getInt("Status")] + clientObserver?.invoke() + } + + override fun writeDeviceConfiguration(tag: NbtCompound) { + target?.address?.let { tag.putInt("Target", it) } + } + + override fun loadDeviceConfiguration(tag: NbtCompound) { + target = if (tag.contains("Target")) { + IPAddress(tag.getInt("Target")) + } else { + null + } + + updateStatus() + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/redstone_emitter/RedstoneEmitterBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/redstone_emitter/RedstoneEmitterBlockEntity.kt index 858a8b5..c5b2b8c 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/redstone_emitter/RedstoneEmitterBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/redstone_emitter/RedstoneEmitterBlockEntity.kt @@ -12,7 +12,7 @@ import net.shadowfacts.phycon.block.DeviceBlockEntity import net.shadowfacts.phycon.block.FaceDeviceBlock import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.packet.DeviceRemovedPacket -import net.shadowfacts.phycon.packet.ReadInventoryPacket +import net.shadowfacts.phycon.packet.ReadGroupedInventoryPacket import net.shadowfacts.phycon.packet.RequestInventoryPacket import net.shadowfacts.phycon.util.ClientConfigurableDevice import net.shadowfacts.phycon.util.GhostInv @@ -42,12 +42,12 @@ class RedstoneEmitterBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockE override fun handle(packet: Packet) { when (packet) { - is ReadInventoryPacket -> handleReadInventory(packet) + is ReadGroupedInventoryPacket -> handleReadInventory(packet) is DeviceRemovedPacket -> handleDeviceRemoved(packet) } } - private fun handleReadInventory(packet: ReadInventoryPacket) { + private fun handleReadInventory(packet: ReadGroupedInventoryPacket) { inventoryCache[packet.source] = packet.inventory recalculateRedstone() } @@ -70,7 +70,7 @@ class RedstoneEmitterBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockE } private fun updateInventories() { - sendPacket(RequestInventoryPacket(ipAddress)) + sendPacket(RequestInventoryPacket(RequestInventoryPacket.Kind.GROUPED, ipAddress)) } private fun recalculateRedstone() { diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalBlockEntity.kt index 412a3e7..57fcbf7 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalBlockEntity.kt @@ -70,7 +70,7 @@ abstract class AbstractTerminalBlockEntity(type: BlockEntityType<*>, pos: BlockP override fun handle(packet: Packet) { when (packet) { - is ReadInventoryPacket -> handleReadInventory(packet) + is ReadGroupedInventoryPacket -> handleReadInventory(packet) is DeviceRemovedPacket -> handleDeviceRemoved(packet) is StackLocationPacket -> handleStackLocation(packet) is ItemStackPacket -> handleItemStack(packet) @@ -78,7 +78,7 @@ abstract class AbstractTerminalBlockEntity(type: BlockEntityType<*>, pos: BlockP } } - private fun handleReadInventory(packet: ReadInventoryPacket) { + private fun handleReadInventory(packet: ReadGroupedInventoryPacket) { inventoryCache[packet.source] = packet.inventory updateAndSync() } diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/CraftingTerminalBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/CraftingTerminalBlockEntity.kt index cbe3fa1..5852a34 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/CraftingTerminalBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/CraftingTerminalBlockEntity.kt @@ -37,7 +37,7 @@ class CraftingTerminalBlockEntity(pos: BlockPos, state: BlockState): AbstractTer updateAndSync() inventoryCache.clear() - sendPacket(RequestInventoryPacket(ipAddress)) + sendPacket(RequestInventoryPacket(RequestInventoryPacket.Kind.GROUPED, ipAddress)) val factory = object: ExtendedScreenHandlerFactory { override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler? { return CraftingTerminalScreenHandler(syncId, playerInv, this@CraftingTerminalBlockEntity) diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalBlockEntity.kt index 04670fa..a9a3285 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalBlockEntity.kt @@ -22,7 +22,7 @@ class TerminalBlockEntity(pos: BlockPos, state: BlockState): AbstractTerminalBlo updateAndSync() inventoryCache.clear() - sendPacket(RequestInventoryPacket(ipAddress)) + sendPacket(RequestInventoryPacket(RequestInventoryPacket.Kind.GROUPED, ipAddress)) val factory = object: ExtendedScreenHandlerFactory { override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler { return TerminalScreenHandler(syncId, playerInv, this@TerminalBlockEntity) diff --git a/src/main/kotlin/net/shadowfacts/phycon/client/PhyModelProvider.kt b/src/main/kotlin/net/shadowfacts/phycon/client/PhyModelProvider.kt index 00345ba..f43e3af 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/client/PhyModelProvider.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/client/PhyModelProvider.kt @@ -25,6 +25,10 @@ class PhyModelProvider(resourceManager: ResourceManager) : ModelResourceProvider val EXTRACTOR_SIDE = Identifier(PhysicalConnectivity.MODID, "block/extractor_side") val INSERTER = Identifier(PhysicalConnectivity.MODID, "block/inserter") val INSERTER_SIDE = Identifier(PhysicalConnectivity.MODID, "block/inserter_side") + val P2P_INTERFACE = Identifier(PhysicalConnectivity.MODID, "block/p2p_interface") + val P2P_INTERFACE_SIDE = Identifier(PhysicalConnectivity.MODID, "block/p2p_interface_side") + val P2P_RECEIVER = Identifier(PhysicalConnectivity.MODID, "block/p2p_receiver") + val P2P_RECEIVER_SIDE = Identifier(PhysicalConnectivity.MODID, "block/p2p_receiver_side") val CABLES = DyeColor.values().map { Identifier(PhysicalConnectivity.MODID, "block/cable/${it.getName()}") to it @@ -38,6 +42,8 @@ class PhyModelProvider(resourceManager: ResourceManager) : ModelResourceProvider REDSTONE_EMITTER -> SimpleFaceDeviceModel(REDSTONE_EMITTER_SIDE) EXTRACTOR -> SimpleFaceDeviceModel(EXTRACTOR_SIDE) INSERTER -> SimpleFaceDeviceModel(INSERTER_SIDE) + P2P_INTERFACE -> SimpleFaceDeviceModel(P2P_INTERFACE_SIDE) + P2P_RECEIVER -> SimpleFaceDeviceModel(P2P_RECEIVER_SIDE) in CABLES -> ColoredCableModel(CABLES[resourceId]!!) else -> null } diff --git a/src/main/kotlin/net/shadowfacts/phycon/client/screen/console/DeviceConsoleScreen.kt b/src/main/kotlin/net/shadowfacts/phycon/client/screen/console/DeviceConsoleScreen.kt index 04ab2ba..021a330 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/client/screen/console/DeviceConsoleScreen.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/client/screen/console/DeviceConsoleScreen.kt @@ -14,6 +14,7 @@ import net.shadowfacts.cacao.window.Window import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.phycon.block.DeviceBlockEntity import net.shadowfacts.phycon.block.miner.MinerBlockEntity +import net.shadowfacts.phycon.block.p2p.P2PReceiverBlockEntity import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlockEntity import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlockEntity import net.shadowfacts.phycon.component.ActivationController @@ -90,6 +91,15 @@ class DeviceConsoleScreen( RedstoneEmitterViewController(device) )) } + if (device is P2PReceiverBlockEntity) { + tabs.add(TabViewController.SimpleTab( + TextureView(Texture(Identifier("textures/item/ender_pearl.png"), 0, 0, 16, 16)).apply { + intrinsicContentSize = Size(16.0, 16.0) + }, + TranslatableText("block.phycon.p2p_receiver"), + P2PReceiverViewController(device) + )) + } tabController = TabViewController(tabs) diff --git a/src/main/kotlin/net/shadowfacts/phycon/client/screen/console/P2PReceiverViewController.kt b/src/main/kotlin/net/shadowfacts/phycon/client/screen/console/P2PReceiverViewController.kt new file mode 100644 index 0000000..e57bf78 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/client/screen/console/P2PReceiverViewController.kt @@ -0,0 +1,58 @@ +package net.shadowfacts.phycon.client.screen.console + +import net.minecraft.client.MinecraftClient +import net.minecraft.text.TranslatableText +import net.shadowfacts.cacao.util.Color +import net.shadowfacts.cacao.view.Label +import net.shadowfacts.cacao.view.textfield.TextField +import net.shadowfacts.cacao.viewcontroller.ViewController +import net.shadowfacts.kiwidsl.dsl +import net.shadowfacts.phycon.api.util.IPAddress +import net.shadowfacts.phycon.block.p2p.P2PReceiverBlockEntity +import net.shadowfacts.phycon.networking.C2SConfigureDevice + +/** + * @author shadowfacts + */ +class P2PReceiverViewController( + val device: P2PReceiverBlockEntity, +): ViewController() { + + override fun viewDidLoad() { + super.viewDidLoad() + + val label = Label(TranslatableText("gui.phycon.console.p2p_receiver.target")).apply { + textColor = Color.TEXT + } + view.addSubview(label) + + val textField = + TextField(device.target?.toString() ?: "") { + device.target = IPAddress.parse(it.text) + MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device)) + } + view.addSubview(textField) + + val status = Label(device.status.displayName).apply { + textColor = Color.TEXT + } + view.addSubview(status) + device.clientObserver = { + status.text = device.status.displayName + } + + view.solver.dsl { + textField.widthAnchor equalTo 100 + textField.heightAnchor equalTo 20 + textField.topAnchor equalTo view.topAnchor + textField.rightAnchor equalTo view.rightAnchor + + label.centerYAnchor equalTo textField.centerYAnchor + label.rightAnchor equalTo (textField.leftAnchor - 4) + + status.centerXAnchor equalTo view.centerXAnchor + status.centerYAnchor equalTo (view.centerYAnchor - 10) + } + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt index 4e41c7b..e7ace30 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt @@ -17,6 +17,10 @@ import net.shadowfacts.phycon.block.netinterface.InterfaceBlock import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity import net.shadowfacts.phycon.block.netswitch.SwitchBlock import net.shadowfacts.phycon.block.netswitch.SwitchBlockEntity +import net.shadowfacts.phycon.block.p2p.P2PInterfaceBlock +import net.shadowfacts.phycon.block.p2p.P2PInterfaceBlockEntity +import net.shadowfacts.phycon.block.p2p.P2PReceiverBlock +import net.shadowfacts.phycon.block.p2p.P2PReceiverBlockEntity import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlockEntity import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock @@ -40,6 +44,8 @@ object PhyBlockEntities { val MINER = create(::MinerBlockEntity, PhyBlocks.MINER) val REDSTONE_CONTROLLER = create(::RedstoneControllerBlockEntity, PhyBlocks.REDSTONE_CONTROLLER) val REDSTONE_EMITTER = create(::RedstoneEmitterBlockEntity, PhyBlocks.REDSTONE_EMITTER) + val P2P_INTERFACE = create(::P2PInterfaceBlockEntity, PhyBlocks.P2P_INTERFACE) + val P2P_RECEIVER = create(::P2PReceiverBlockEntity, PhyBlocks.P2P_RECEIVER) private fun create(builder: (BlockPos, BlockState) -> T, block: Block): BlockEntityType { return BlockEntityType.Builder.create(builder, block).build(null) @@ -55,6 +61,8 @@ object PhyBlockEntities { register(MinerBlock.ID, MINER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER) + register(P2PInterfaceBlock.ID, P2P_INTERFACE) + register(P2PReceiverBlock.ID, P2P_RECEIVER) } private fun register(id: Identifier, type: BlockEntityType<*>) { diff --git a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt index 1a0cefa..4d6a6e8 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt @@ -11,6 +11,8 @@ import net.shadowfacts.phycon.block.inserter.InserterBlock import net.shadowfacts.phycon.block.miner.MinerBlock import net.shadowfacts.phycon.block.netinterface.InterfaceBlock import net.shadowfacts.phycon.block.netswitch.SwitchBlock +import net.shadowfacts.phycon.block.p2p.P2PInterfaceBlock +import net.shadowfacts.phycon.block.p2p.P2PReceiverBlock import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock @@ -32,6 +34,8 @@ object PhyBlocks { val MINER = MinerBlock() val REDSTONE_CONTROLLER = RedstoneControllerBlock() val REDSTONE_EMITTER = RedstoneEmitterBlock() + val P2P_INTERFACE = P2PInterfaceBlock() + val P2P_RECEIVER = P2PReceiverBlock() fun init() { for ((color, block) in CABLES) { @@ -47,6 +51,8 @@ object PhyBlocks { register(MinerBlock.ID, MINER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER) + register(P2PInterfaceBlock.ID, P2P_INTERFACE) + register(P2PReceiverBlock.ID, P2P_RECEIVER) } private fun register(id: Identifier, block: Block) { diff --git a/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt b/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt index dad46de..3b09dc2 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt @@ -12,12 +12,15 @@ import net.shadowfacts.phycon.block.inserter.InserterBlock import net.shadowfacts.phycon.block.miner.MinerBlock import net.shadowfacts.phycon.block.netinterface.InterfaceBlock import net.shadowfacts.phycon.block.netswitch.SwitchBlock +import net.shadowfacts.phycon.block.p2p.P2PInterfaceBlock +import net.shadowfacts.phycon.block.p2p.P2PReceiverBlock import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock import net.shadowfacts.phycon.block.terminal.TerminalBlock import net.shadowfacts.phycon.item.DeviceBlockItem import net.shadowfacts.phycon.item.FaceDeviceBlockItem +import net.shadowfacts.phycon.util.text /** * @author shadowfacts @@ -37,6 +40,8 @@ object PhyItems { val MINER = DeviceBlockItem(PhyBlocks.MINER, Item.Settings()) val REDSTONE_CONTROLLER = FaceDeviceBlockItem(PhyBlocks.REDSTONE_CONTROLLER, Item.Settings()) val REDSTONE_EMITTER = FaceDeviceBlockItem(PhyBlocks.REDSTONE_EMITTER, Item.Settings()) + val P2P_INTERFACE = FaceDeviceBlockItem(PhyBlocks.P2P_INTERFACE, Item.Settings()) + val P2P_RECEIVER = FaceDeviceBlockItem(PhyBlocks.P2P_RECEIVER, Item.Settings()) val SCREWDRIVER = ScrewdriverItem() val CONSOLE = ConsoleItem() @@ -61,6 +66,18 @@ object PhyItems { register(MinerBlock.ID, MINER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER) + register(P2PInterfaceBlock.ID, P2P_INTERFACE) + P2P_INTERFACE.addTooltip(text { + withStyle(darkGray) { + +translate("tooltip.phycon.p2p_interface") + } + }) + register(P2PReceiverBlock.ID, P2P_RECEIVER) + P2P_RECEIVER.addTooltip(text { + withStyle(darkGray) { + +translate("tooltip.phycon.p2p_receiver") + } + }) register(ScrewdriverItem.ID, SCREWDRIVER) register(ConsoleItem.ID, CONSOLE) diff --git a/src/main/kotlin/net/shadowfacts/phycon/item/DeviceBlockItem.kt b/src/main/kotlin/net/shadowfacts/phycon/item/DeviceBlockItem.kt index 22742b6..dcb8c89 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/item/DeviceBlockItem.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/item/DeviceBlockItem.kt @@ -14,7 +14,15 @@ import net.shadowfacts.phycon.util.text */ open class DeviceBlockItem(block: DeviceBlock<*>, settings: Settings = Settings()): BlockItem(block, settings) { + private var tooltip = mutableListOf() + + fun addTooltip(tooltip: Text) { + this.tooltip.add(tooltip) + } + override fun appendTooltip(stack: ItemStack, world: World?, list: MutableList, context: TooltipContext) { + list.addAll(tooltip) + val beTag = stack.getSubNbt("BlockEntityTag") if (beTag != null) { val ip = IPAddress(beTag.getInt("IPAddress")) diff --git a/src/main/kotlin/net/shadowfacts/phycon/packet/PingPacket.kt b/src/main/kotlin/net/shadowfacts/phycon/packet/PingPacket.kt new file mode 100644 index 0000000..e8054bd --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/packet/PingPacket.kt @@ -0,0 +1,10 @@ +package net.shadowfacts.phycon.packet + +import net.shadowfacts.phycon.api.util.IPAddress + +/** + * @author shadowfacts + */ +class PingPacket(source: IPAddress, destination: IPAddress): BasePacket(source, destination) + +class PongPacket(source: IPAddress, destination: IPAddress): BasePacket(source, destination) diff --git a/src/main/kotlin/net/shadowfacts/phycon/packet/ReadInventoryPacket.kt b/src/main/kotlin/net/shadowfacts/phycon/packet/ReadGroupedInventoryPacket.kt similarity index 89% rename from src/main/kotlin/net/shadowfacts/phycon/packet/ReadInventoryPacket.kt rename to src/main/kotlin/net/shadowfacts/phycon/packet/ReadGroupedInventoryPacket.kt index 107c02c..1c260da 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/packet/ReadInventoryPacket.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/packet/ReadGroupedInventoryPacket.kt @@ -6,7 +6,7 @@ import net.shadowfacts.phycon.api.util.IPAddress /** * @author shadowfacts */ -class ReadInventoryPacket( +class ReadGroupedInventoryPacket( val inventory: GroupedItemInvView, source: IPAddress, destination: IPAddress diff --git a/src/main/kotlin/net/shadowfacts/phycon/packet/ReadItemStoragePacket.kt b/src/main/kotlin/net/shadowfacts/phycon/packet/ReadItemStoragePacket.kt new file mode 100644 index 0000000..8726928 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/packet/ReadItemStoragePacket.kt @@ -0,0 +1,14 @@ +package net.shadowfacts.phycon.packet + +import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant +import net.fabricmc.fabric.api.transfer.v1.storage.Storage +import net.shadowfacts.phycon.api.util.IPAddress + +/** + * @author shadowfacts + */ +class ReadItemStoragePacket( + val inventory: Storage, + source: IPAddress, + destination: IPAddress +): BasePacket(source, destination) diff --git a/src/main/kotlin/net/shadowfacts/phycon/packet/RequestInventoryPacket.kt b/src/main/kotlin/net/shadowfacts/phycon/packet/RequestInventoryPacket.kt index 9893eaf..62096d2 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/packet/RequestInventoryPacket.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/packet/RequestInventoryPacket.kt @@ -5,4 +5,12 @@ import net.shadowfacts.phycon.api.util.IPAddress /** * @author shadowfacts */ -class RequestInventoryPacket(source: IPAddress, destination: IPAddress = IPAddress.BROADCAST): BasePacket(source, destination) +class RequestInventoryPacket( + val kind: Kind, + source: IPAddress, + destination: IPAddress = IPAddress.BROADCAST +): BasePacket(source, destination) { + enum class Kind { + GROUPED, SIDED + } +} diff --git a/src/main/resources/assets/phycon/blockstates/p2p_interface.json b/src/main/resources/assets/phycon/blockstates/p2p_interface.json new file mode 100644 index 0000000..ffdb213 --- /dev/null +++ b/src/main/resources/assets/phycon/blockstates/p2p_interface.json @@ -0,0 +1,7 @@ +{ + "multipart": [ + { + "apply": { "model": "phycon:block/p2p_interface" } + } + ] +} diff --git a/src/main/resources/assets/phycon/blockstates/p2p_receiver.json b/src/main/resources/assets/phycon/blockstates/p2p_receiver.json new file mode 100644 index 0000000..b39f64c --- /dev/null +++ b/src/main/resources/assets/phycon/blockstates/p2p_receiver.json @@ -0,0 +1,7 @@ +{ + "multipart": [ + { + "apply": { "model": "phycon:block/p2p_receiver" } + } + ] +} diff --git a/src/main/resources/assets/phycon/lang/en_us.json b/src/main/resources/assets/phycon/lang/en_us.json index b520be1..64c0764 100644 --- a/src/main/resources/assets/phycon/lang/en_us.json +++ b/src/main/resources/assets/phycon/lang/en_us.json @@ -24,6 +24,8 @@ "block.phycon.miner": "Block Miner", "block.phycon.redstone_controller": "Redstone Controller", "block.phycon.redstone_emitter": "Redstone Emitter", + "block.phycon.p2p_interface": "P2P Interface", + "block.phycon.p2p_receiver": "P2P Receiver", "item.phycon.screwdriver": "Screwdriver", "item.phycon.console": "Console", @@ -56,6 +58,7 @@ "gui.phycon.console.receiver.priority_desc": "When a device puts items into the network, it starts with receiver (e.g., interfaces) with higher priorities. Priorities can be negative.", "gui.phycon.console.receiver.sync": "Sync with Provider Priority", "gui.phycon.console.emitter.mode": "Measurement Mode", + "gui.phycon.console.p2p_receiver.target": "P2P Target", "gui.phycon.redstone_mode.high": "High", "gui.phycon.redstone_mode.low": "Low", "gui.phycon.redstone_mode.toggle": "Toggle", @@ -68,9 +71,14 @@ "gui.phycon.miner_mode.on_demand": "On Demand", "gui.phycon.redstone_emitter_mode.analog": "Analog", "gui.phycon.redstone_emitter_mode.digital": "Digital", + "gui.phycon.p2p_receiver.status.ok": "OK", + "gui.phycon.p2p_receiver.status.no_target": "No target", + "gui.phycon.p2p_receiver.status.waiting_for_response": "Waiting for response", "tooltip.phycon.device.configured": "Configured", "tooltip.phycon.device.ip": "IP: ", + "tooltip.phycon.p2p_interface": "Attach to the point-to-point target inventory", + "tooltip.phycon.p2p_receiver": "Point-to-point inventory destination", "advancements.phycon.root.title": "Physical Connectivity", "advancements.phycon.root.description": "Mass item storage and local networking", diff --git a/src/main/resources/assets/phycon/models/block/p2p_interface_side.json b/src/main/resources/assets/phycon/models/block/p2p_interface_side.json new file mode 100644 index 0000000..6c2fe8a --- /dev/null +++ b/src/main/resources/assets/phycon/models/block/p2p_interface_side.json @@ -0,0 +1,33 @@ +{ + "parent": "block/block", + "textures": { + "cable": "phycon:block/cable_straight", + "front": "phycon:block/p2p_interface_front", + "back": "phycon:block/p2p_interface_back", + "side": "phycon:block/p2p_interface_back" + }, + "elements": [ + { + "from": [0, 0, 0], + "to": [16, 4, 16], + "faces": { + "down": { "texture": "#front" }, + "up": { "texture": "#back" }, + "north": { "texture": "#side" }, + "south": { "texture": "#side" }, + "west": { "texture": "#side" }, + "east": { "texture": "#side" } + } + }, + { + "from": [6, 4, 6], + "to": [10, 6, 10], + "faces": { + "north": { "texture": "#cable" }, + "south": { "texture": "#cable" }, + "west": { "texture": "#cable" }, + "east": { "texture": "#cable" } + } + } + ] +} diff --git a/src/main/resources/assets/phycon/models/block/p2p_receiver_side.json b/src/main/resources/assets/phycon/models/block/p2p_receiver_side.json new file mode 100644 index 0000000..f68ab53 --- /dev/null +++ b/src/main/resources/assets/phycon/models/block/p2p_receiver_side.json @@ -0,0 +1,33 @@ +{ + "parent": "block/block", + "textures": { + "cable": "phycon:block/cable_straight", + "front": "phycon:block/p2p_receiver_front", + "back": "phycon:block/p2p_receiver_back", + "side": "phycon:block/p2p_receiver_back" + }, + "elements": [ + { + "from": [0, 0, 0], + "to": [16, 4, 16], + "faces": { + "down": { "texture": "#front" }, + "up": { "texture": "#back" }, + "north": { "texture": "#side" }, + "south": { "texture": "#side" }, + "west": { "texture": "#side" }, + "east": { "texture": "#side" } + } + }, + { + "from": [6, 4, 6], + "to": [10, 6, 10], + "faces": { + "north": { "texture": "#cable" }, + "south": { "texture": "#cable" }, + "west": { "texture": "#cable" }, + "east": { "texture": "#cable" } + } + } + ] +}