Add P2P inventory system

This commit is contained in:
Shadowfacts 2021-12-24 12:34:16 -05:00
parent 9cbad193e2
commit e88ecd3215
28 changed files with 513 additions and 13 deletions

View File

@ -2,8 +2,10 @@ package net.shadowfacts.phycon
import net.fabricmc.api.ModInitializer import net.fabricmc.api.ModInitializer
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking 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.fabricmc.loader.api.FabricLoader
import net.shadowfacts.phycon.api.PhyConPlugin import net.shadowfacts.phycon.api.PhyConPlugin
import net.shadowfacts.phycon.block.p2p.P2PReceiverBlockEntity
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.init.PhyBlocks import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyItems import net.shadowfacts.phycon.init.PhyItems
@ -31,6 +33,8 @@ object PhysicalConnectivity: ModInitializer {
registerGlobalReceiver(C2STerminalRequestItem) registerGlobalReceiver(C2STerminalRequestItem)
registerGlobalReceiver(C2STerminalUpdateDisplayedItems) registerGlobalReceiver(C2STerminalUpdateDisplayedItems)
ItemStorage.SIDED.registerForBlockEntity(P2PReceiverBlockEntity::provideItemStorage, PhyBlockEntities.P2P_RECEIVER)
for (it in FabricLoader.getInstance().getEntrypoints("phycon", PhyConPlugin::class.java)) { for (it in FabricLoader.getInstance().getEntrypoints("phycon", PhyConPlugin::class.java)) {
it.initializePhyCon(PhyConAPIImpl) it.initializePhyCon(PhyConAPIImpl)
} }

View File

@ -57,6 +57,9 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state:
is DeviceRemovedPacket -> { is DeviceRemovedPacket -> {
arpTable.remove(packet.source) arpTable.remove(packet.source)
} }
is PingPacket -> {
sendPacket(PongPacket(ipAddress, packet.source))
}
} }
handle(packet) handle(packet)
} }

View File

@ -57,10 +57,10 @@ class MinerBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockEntity(PhyB
} }
private fun handleRequestInventory(packet: RequestInventoryPacket) { private fun handleRequestInventory(packet: RequestInventoryPacket) {
if (minerMode != MinerMode.ON_DEMAND) { if (minerMode != MinerMode.ON_DEMAND || packet.kind != RequestInventoryPacket.Kind.GROUPED) {
return return
} }
sendPacket(ReadInventoryPacket(invProxy, ipAddress, packet.source)) sendPacket(ReadGroupedInventoryPacket(invProxy, ipAddress, packet.source))
} }
private fun handleLocateStack(packet: LocateStackPacket) { private fun handleLocateStack(packet: LocateStackPacket) {

View File

@ -64,8 +64,11 @@ class InterfaceBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockEntity(
} }
private fun handleRequestInventory(packet: RequestInventoryPacket) { private fun handleRequestInventory(packet: RequestInventoryPacket) {
if (packet.kind != RequestInventoryPacket.Kind.GROUPED) {
return
}
getInventory()?.also { inv -> getInventory()?.also { inv ->
sendPacket(ReadInventoryPacket(inv, ipAddress, packet.source)) sendPacket(ReadGroupedInventoryPacket(inv, ipAddress, packet.source))
} }
} }

View File

@ -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<P2PInterfaceBlockEntity>(
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)
}

View File

@ -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<ItemVariant>? = null
private fun updateInventory() {
val facing = cachedState[FaceDeviceBlock.FACING]
inventory = ItemStorage.SIDED.find(world!!, pos.offset(facing), facing.opposite)
}
private fun getInventory(): Storage<ItemVariant>? {
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))
}
}
}

View File

@ -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<P2PReceiverBlockEntity>(
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)
}

View File

@ -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<ItemVariant>? {
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<ItemVariant>? = 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<ItemVariant>? {
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()
}
}

View File

@ -12,7 +12,7 @@ import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.block.FaceDeviceBlock import net.shadowfacts.phycon.block.FaceDeviceBlock
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.packet.DeviceRemovedPacket 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.packet.RequestInventoryPacket
import net.shadowfacts.phycon.util.ClientConfigurableDevice import net.shadowfacts.phycon.util.ClientConfigurableDevice
import net.shadowfacts.phycon.util.GhostInv import net.shadowfacts.phycon.util.GhostInv
@ -42,12 +42,12 @@ class RedstoneEmitterBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockE
override fun handle(packet: Packet) { override fun handle(packet: Packet) {
when (packet) { when (packet) {
is ReadInventoryPacket -> handleReadInventory(packet) is ReadGroupedInventoryPacket -> handleReadInventory(packet)
is DeviceRemovedPacket -> handleDeviceRemoved(packet) is DeviceRemovedPacket -> handleDeviceRemoved(packet)
} }
} }
private fun handleReadInventory(packet: ReadInventoryPacket) { private fun handleReadInventory(packet: ReadGroupedInventoryPacket) {
inventoryCache[packet.source] = packet.inventory inventoryCache[packet.source] = packet.inventory
recalculateRedstone() recalculateRedstone()
} }
@ -70,7 +70,7 @@ class RedstoneEmitterBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockE
} }
private fun updateInventories() { private fun updateInventories() {
sendPacket(RequestInventoryPacket(ipAddress)) sendPacket(RequestInventoryPacket(RequestInventoryPacket.Kind.GROUPED, ipAddress))
} }
private fun recalculateRedstone() { private fun recalculateRedstone() {

View File

@ -70,7 +70,7 @@ abstract class AbstractTerminalBlockEntity(type: BlockEntityType<*>, pos: BlockP
override fun handle(packet: Packet) { override fun handle(packet: Packet) {
when (packet) { when (packet) {
is ReadInventoryPacket -> handleReadInventory(packet) is ReadGroupedInventoryPacket -> handleReadInventory(packet)
is DeviceRemovedPacket -> handleDeviceRemoved(packet) is DeviceRemovedPacket -> handleDeviceRemoved(packet)
is StackLocationPacket -> handleStackLocation(packet) is StackLocationPacket -> handleStackLocation(packet)
is ItemStackPacket -> handleItemStack(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 inventoryCache[packet.source] = packet.inventory
updateAndSync() updateAndSync()
} }

View File

@ -37,7 +37,7 @@ class CraftingTerminalBlockEntity(pos: BlockPos, state: BlockState): AbstractTer
updateAndSync() updateAndSync()
inventoryCache.clear() inventoryCache.clear()
sendPacket(RequestInventoryPacket(ipAddress)) sendPacket(RequestInventoryPacket(RequestInventoryPacket.Kind.GROUPED, ipAddress))
val factory = object: ExtendedScreenHandlerFactory { val factory = object: ExtendedScreenHandlerFactory {
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler? { override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler? {
return CraftingTerminalScreenHandler(syncId, playerInv, this@CraftingTerminalBlockEntity) return CraftingTerminalScreenHandler(syncId, playerInv, this@CraftingTerminalBlockEntity)

View File

@ -22,7 +22,7 @@ class TerminalBlockEntity(pos: BlockPos, state: BlockState): AbstractTerminalBlo
updateAndSync() updateAndSync()
inventoryCache.clear() inventoryCache.clear()
sendPacket(RequestInventoryPacket(ipAddress)) sendPacket(RequestInventoryPacket(RequestInventoryPacket.Kind.GROUPED, ipAddress))
val factory = object: ExtendedScreenHandlerFactory { val factory = object: ExtendedScreenHandlerFactory {
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler { override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler {
return TerminalScreenHandler(syncId, playerInv, this@TerminalBlockEntity) return TerminalScreenHandler(syncId, playerInv, this@TerminalBlockEntity)

View File

@ -25,6 +25,10 @@ class PhyModelProvider(resourceManager: ResourceManager) : ModelResourceProvider
val EXTRACTOR_SIDE = Identifier(PhysicalConnectivity.MODID, "block/extractor_side") val EXTRACTOR_SIDE = Identifier(PhysicalConnectivity.MODID, "block/extractor_side")
val INSERTER = Identifier(PhysicalConnectivity.MODID, "block/inserter") val INSERTER = Identifier(PhysicalConnectivity.MODID, "block/inserter")
val INSERTER_SIDE = Identifier(PhysicalConnectivity.MODID, "block/inserter_side") 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 { val CABLES = DyeColor.values().map {
Identifier(PhysicalConnectivity.MODID, "block/cable/${it.getName()}") to it Identifier(PhysicalConnectivity.MODID, "block/cable/${it.getName()}") to it
@ -38,6 +42,8 @@ class PhyModelProvider(resourceManager: ResourceManager) : ModelResourceProvider
REDSTONE_EMITTER -> SimpleFaceDeviceModel(REDSTONE_EMITTER_SIDE) REDSTONE_EMITTER -> SimpleFaceDeviceModel(REDSTONE_EMITTER_SIDE)
EXTRACTOR -> SimpleFaceDeviceModel(EXTRACTOR_SIDE) EXTRACTOR -> SimpleFaceDeviceModel(EXTRACTOR_SIDE)
INSERTER -> SimpleFaceDeviceModel(INSERTER_SIDE) INSERTER -> SimpleFaceDeviceModel(INSERTER_SIDE)
P2P_INTERFACE -> SimpleFaceDeviceModel(P2P_INTERFACE_SIDE)
P2P_RECEIVER -> SimpleFaceDeviceModel(P2P_RECEIVER_SIDE)
in CABLES -> ColoredCableModel(CABLES[resourceId]!!) in CABLES -> ColoredCableModel(CABLES[resourceId]!!)
else -> null else -> null
} }

View File

@ -14,6 +14,7 @@ import net.shadowfacts.cacao.window.Window
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.block.DeviceBlockEntity import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.block.miner.MinerBlockEntity 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_controller.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlockEntity import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlockEntity
import net.shadowfacts.phycon.component.ActivationController import net.shadowfacts.phycon.component.ActivationController
@ -90,6 +91,15 @@ class DeviceConsoleScreen(
RedstoneEmitterViewController(device) 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) tabController = TabViewController(tabs)

View File

@ -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)
}
}
}

View File

@ -17,6 +17,10 @@ import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.block.netswitch.SwitchBlock import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.block.netswitch.SwitchBlockEntity 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.RedstoneControllerBlock
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlockEntity import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
@ -40,6 +44,8 @@ object PhyBlockEntities {
val MINER = create(::MinerBlockEntity, PhyBlocks.MINER) val MINER = create(::MinerBlockEntity, PhyBlocks.MINER)
val REDSTONE_CONTROLLER = create(::RedstoneControllerBlockEntity, PhyBlocks.REDSTONE_CONTROLLER) val REDSTONE_CONTROLLER = create(::RedstoneControllerBlockEntity, PhyBlocks.REDSTONE_CONTROLLER)
val REDSTONE_EMITTER = create(::RedstoneEmitterBlockEntity, PhyBlocks.REDSTONE_EMITTER) 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 <T: BlockEntity> create(builder: (BlockPos, BlockState) -> T, block: Block): BlockEntityType<T> { private fun <T: BlockEntity> create(builder: (BlockPos, BlockState) -> T, block: Block): BlockEntityType<T> {
return BlockEntityType.Builder.create(builder, block).build(null) return BlockEntityType.Builder.create(builder, block).build(null)
@ -55,6 +61,8 @@ object PhyBlockEntities {
register(MinerBlock.ID, MINER) register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER) register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER)
register(P2PInterfaceBlock.ID, P2P_INTERFACE)
register(P2PReceiverBlock.ID, P2P_RECEIVER)
} }
private fun register(id: Identifier, type: BlockEntityType<*>) { private fun register(id: Identifier, type: BlockEntityType<*>) {

View File

@ -11,6 +11,8 @@ import net.shadowfacts.phycon.block.inserter.InserterBlock
import net.shadowfacts.phycon.block.miner.MinerBlock import net.shadowfacts.phycon.block.miner.MinerBlock
import net.shadowfacts.phycon.block.netinterface.InterfaceBlock import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.block.netswitch.SwitchBlock 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_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock
@ -32,6 +34,8 @@ object PhyBlocks {
val MINER = MinerBlock() val MINER = MinerBlock()
val REDSTONE_CONTROLLER = RedstoneControllerBlock() val REDSTONE_CONTROLLER = RedstoneControllerBlock()
val REDSTONE_EMITTER = RedstoneEmitterBlock() val REDSTONE_EMITTER = RedstoneEmitterBlock()
val P2P_INTERFACE = P2PInterfaceBlock()
val P2P_RECEIVER = P2PReceiverBlock()
fun init() { fun init() {
for ((color, block) in CABLES) { for ((color, block) in CABLES) {
@ -47,6 +51,8 @@ object PhyBlocks {
register(MinerBlock.ID, MINER) register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER) register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER)
register(P2PInterfaceBlock.ID, P2P_INTERFACE)
register(P2PReceiverBlock.ID, P2P_RECEIVER)
} }
private fun register(id: Identifier, block: Block) { private fun register(id: Identifier, block: Block) {

View File

@ -12,12 +12,15 @@ import net.shadowfacts.phycon.block.inserter.InserterBlock
import net.shadowfacts.phycon.block.miner.MinerBlock import net.shadowfacts.phycon.block.miner.MinerBlock
import net.shadowfacts.phycon.block.netinterface.InterfaceBlock import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.block.netswitch.SwitchBlock 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_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock import net.shadowfacts.phycon.block.terminal.CraftingTerminalBlock
import net.shadowfacts.phycon.block.terminal.TerminalBlock import net.shadowfacts.phycon.block.terminal.TerminalBlock
import net.shadowfacts.phycon.item.DeviceBlockItem import net.shadowfacts.phycon.item.DeviceBlockItem
import net.shadowfacts.phycon.item.FaceDeviceBlockItem import net.shadowfacts.phycon.item.FaceDeviceBlockItem
import net.shadowfacts.phycon.util.text
/** /**
* @author shadowfacts * @author shadowfacts
@ -37,6 +40,8 @@ object PhyItems {
val MINER = DeviceBlockItem(PhyBlocks.MINER, Item.Settings()) val MINER = DeviceBlockItem(PhyBlocks.MINER, Item.Settings())
val REDSTONE_CONTROLLER = FaceDeviceBlockItem(PhyBlocks.REDSTONE_CONTROLLER, Item.Settings()) val REDSTONE_CONTROLLER = FaceDeviceBlockItem(PhyBlocks.REDSTONE_CONTROLLER, Item.Settings())
val REDSTONE_EMITTER = FaceDeviceBlockItem(PhyBlocks.REDSTONE_EMITTER, 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 SCREWDRIVER = ScrewdriverItem()
val CONSOLE = ConsoleItem() val CONSOLE = ConsoleItem()
@ -61,6 +66,18 @@ object PhyItems {
register(MinerBlock.ID, MINER) register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER) 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(ScrewdriverItem.ID, SCREWDRIVER)
register(ConsoleItem.ID, CONSOLE) register(ConsoleItem.ID, CONSOLE)

View File

@ -14,7 +14,15 @@ import net.shadowfacts.phycon.util.text
*/ */
open class DeviceBlockItem(block: DeviceBlock<*>, settings: Settings = Settings()): BlockItem(block, settings) { open class DeviceBlockItem(block: DeviceBlock<*>, settings: Settings = Settings()): BlockItem(block, settings) {
private var tooltip = mutableListOf<Text>()
fun addTooltip(tooltip: Text) {
this.tooltip.add(tooltip)
}
override fun appendTooltip(stack: ItemStack, world: World?, list: MutableList<Text>, context: TooltipContext) { override fun appendTooltip(stack: ItemStack, world: World?, list: MutableList<Text>, context: TooltipContext) {
list.addAll(tooltip)
val beTag = stack.getSubNbt("BlockEntityTag") val beTag = stack.getSubNbt("BlockEntityTag")
if (beTag != null) { if (beTag != null) {
val ip = IPAddress(beTag.getInt("IPAddress")) val ip = IPAddress(beTag.getInt("IPAddress"))

View File

@ -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)

View File

@ -6,7 +6,7 @@ import net.shadowfacts.phycon.api.util.IPAddress
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class ReadInventoryPacket( class ReadGroupedInventoryPacket(
val inventory: GroupedItemInvView, val inventory: GroupedItemInvView,
source: IPAddress, source: IPAddress,
destination: IPAddress destination: IPAddress

View File

@ -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<ItemVariant>,
source: IPAddress,
destination: IPAddress
): BasePacket(source, destination)

View File

@ -5,4 +5,12 @@ import net.shadowfacts.phycon.api.util.IPAddress
/** /**
* @author shadowfacts * @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
}
}

View File

@ -0,0 +1,7 @@
{
"multipart": [
{
"apply": { "model": "phycon:block/p2p_interface" }
}
]
}

View File

@ -0,0 +1,7 @@
{
"multipart": [
{
"apply": { "model": "phycon:block/p2p_receiver" }
}
]
}

View File

@ -24,6 +24,8 @@
"block.phycon.miner": "Block Miner", "block.phycon.miner": "Block Miner",
"block.phycon.redstone_controller": "Redstone Controller", "block.phycon.redstone_controller": "Redstone Controller",
"block.phycon.redstone_emitter": "Redstone Emitter", "block.phycon.redstone_emitter": "Redstone Emitter",
"block.phycon.p2p_interface": "P2P Interface",
"block.phycon.p2p_receiver": "P2P Receiver",
"item.phycon.screwdriver": "Screwdriver", "item.phycon.screwdriver": "Screwdriver",
"item.phycon.console": "Console", "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.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.receiver.sync": "Sync with Provider Priority",
"gui.phycon.console.emitter.mode": "Measurement Mode", "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.high": "High",
"gui.phycon.redstone_mode.low": "Low", "gui.phycon.redstone_mode.low": "Low",
"gui.phycon.redstone_mode.toggle": "Toggle", "gui.phycon.redstone_mode.toggle": "Toggle",
@ -68,9 +71,14 @@
"gui.phycon.miner_mode.on_demand": "On Demand", "gui.phycon.miner_mode.on_demand": "On Demand",
"gui.phycon.redstone_emitter_mode.analog": "Analog", "gui.phycon.redstone_emitter_mode.analog": "Analog",
"gui.phycon.redstone_emitter_mode.digital": "Digital", "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.configured": "Configured",
"tooltip.phycon.device.ip": "IP: ", "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.title": "Physical Connectivity",
"advancements.phycon.root.description": "Mass item storage and local networking", "advancements.phycon.root.description": "Mass item storage and local networking",

View File

@ -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" }
}
}
]
}

View File

@ -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" }
}
}
]
}