diff --git a/src/main/kotlin/net/shadowfacts/cacao/CacaoHandledScreen.kt b/src/main/kotlin/net/shadowfacts/cacao/CacaoHandledScreen.kt index ef10a71..18352f8 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/CacaoHandledScreen.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/CacaoHandledScreen.kt @@ -16,7 +16,7 @@ import java.util.* /** * @author shadowfacts */ -class CacaoHandledScreen( +open class CacaoHandledScreen( handler: Handler, playerInv: PlayerInventory, title: Text, @@ -90,4 +90,4 @@ class CacaoHandledScreen( } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt index d43573f..653ef3c 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt @@ -28,6 +28,7 @@ object PhysicalConnectivity: ModInitializer { registerGlobalReceiver(C2STerminalUpdateDisplayedItems) registerGlobalReceiver(C2SConfigureActivationMode) registerGlobalReceiver(C2SConfigureRedstoneController) + registerGlobalReceiver(C2SConfigureInserterAmount) } private fun registerGlobalReceiver(receiver: ServerReceiver) { diff --git a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivityClient.kt b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivityClient.kt index 145a624..c92be8b 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivityClient.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivityClient.kt @@ -5,6 +5,7 @@ import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry import net.minecraft.client.render.RenderLayer +import net.shadowfacts.phycon.block.inserter.InserterScreen import net.shadowfacts.phycon.init.PhyBlocks import net.shadowfacts.phycon.init.PhyScreens import net.shadowfacts.phycon.block.terminal.TerminalScreen @@ -17,9 +18,8 @@ import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems object PhysicalConnectivityClient: ClientModInitializer { override fun onInitializeClient() { - BlockRenderLayerMap.INSTANCE.putBlock(PhyBlocks.CABLE, RenderLayer.getTranslucent()) - ScreenRegistry.register(PhyScreens.TERMINAL_SCREEN_HANDLER, ::TerminalScreen) + ScreenRegistry.register(PhyScreens.INSERTER_SCREEN_HANDLER, ::InserterScreen) registerGlobalReceiver(S2CTerminalUpdateDisplayedItems) } diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/extractor/ExtractorBlock.kt b/src/main/kotlin/net/shadowfacts/phycon/block/extractor/ExtractorBlock.kt index 4e3747a..21d3871 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/extractor/ExtractorBlock.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/extractor/ExtractorBlock.kt @@ -88,7 +88,7 @@ class ExtractorBlock: DeviceBlock(Settings.of(Material.MET override fun createBlockEntity(world: BlockView) = ExtractorBlockEntity() override fun getPlacementState(context: ItemPlacementContext): BlockState { - val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerFacing.opposite + val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerLookDirection.opposite return defaultState.with(FACING, facing) } diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterBlock.kt b/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterBlock.kt new file mode 100644 index 0000000..8fd6690 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterBlock.kt @@ -0,0 +1,143 @@ +package net.shadowfacts.phycon.block.inserter + +import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory +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.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.item.ItemPlacementContext +import net.minecraft.item.ItemStack +import net.minecraft.network.PacketByteBuf +import net.minecraft.screen.ScreenHandler +import net.minecraft.server.network.ServerPlayerEntity +import net.minecraft.state.StateManager +import net.minecraft.state.property.Properties +import net.minecraft.text.Text +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.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.Interface +import net.shadowfacts.phycon.block.DeviceBlock +import net.shadowfacts.phycon.block.extractor.ExtractorBlock +import java.util.* + +/** + * @author shadowfacts + */ +class InserterBlock: DeviceBlock(Settings.of(Material.METAL)) { + companion object { + val ID = Identifier(PhysicalConnectivity.MODID, "inserter") + val FACING = Properties.FACING + private val INSERTER_SHAPES = mutableMapOf() + + init { + val components = arrayOf( + doubleArrayOf(4.0, 0.0, 4.0, 12.0, 2.0, 12.0), + doubleArrayOf(2.0, 2.0, 2.0, 14.0, 4.0, 14.0), + doubleArrayOf(0.0, 4.0, 0.0, 16.0, 6.0, 16.0), + doubleArrayOf(6.0, 6.0, 6.0, 10.0, 16.0, 10.0) + ) + val directions = arrayOf( + Triple(Direction.DOWN, null, false), + Triple(Direction.UP, null, true), + Triple(Direction.NORTH, 2, false), + Triple(Direction.SOUTH, 2, true), + Triple(Direction.WEST, 1, false), + Triple(Direction.EAST, 1, true), + ) + for ((dir, rotate, flip) in directions) { + val shapes = components.map { it -> + val arr = it.copyOf() + if (rotate != null) { + for (i in 0 until 3) { + arr[i] = it[(i + rotate) % 3] + arr[3 + i] = it[3 + ((i + rotate) % 3)] + } + } + if (flip) { + for (i in arr.indices) { + arr[i] = 16.0 - arr[i] + } + } + createCuboidShape(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]) + } + INSERTER_SHAPES[dir] = shapes.reduce { a, b -> VoxelShapes.union(a, b) } + } + } + } + + override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection { + return EnumSet.of(state[FACING].opposite) + } + + override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: WorldAccess, pos: BlockPos): Interface? { + return if (side == state[FACING].opposite) { + getBlockEntity(world, pos) + } else { + null + } + } + + override fun appendProperties(builder: StateManager.Builder) { + super.appendProperties(builder) + builder.add(FACING) + } + + override fun createBlockEntity(world: BlockView) = InserterBlockEntity() + + override fun getPlacementState(context: ItemPlacementContext): BlockState { + val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerLookDirection.opposite + return defaultState.with(FACING, facing) + } + + override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape { + return INSERTER_SHAPES[state[FACING]]!! + } + + override fun onPlaced(world: World, pos: BlockPos, state: BlockState, entity: LivingEntity?, stack: ItemStack) { + if (!world.isClient) { + getBlockEntity(world, pos)!!.updateInventory() + } + } + + override fun neighborUpdate(state: BlockState, world: World, pos: BlockPos, neighborBlock: Block, neighborPos: BlockPos, bl: Boolean) { + if (!world.isClient) { + getBlockEntity(world, pos)!!.updateInventory() + } + } + + override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hitResult: BlockHitResult): ActionResult { + if (!world.isClient) { + val be = getBlockEntity(world, pos)!! + + be.sync() + + val factory = object: ExtendedScreenHandlerFactory { + override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler { + return InserterScreenHandler(syncId, playerInv, be) + } + + override fun getDisplayName() = this@InserterBlock.name + + override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) { + buf.writeBlockPos(be.pos) + } + } + player.openHandledScreen(factory) + } + return ActionResult.SUCCESS + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterBlockEntity.kt new file mode 100644 index 0000000..f6e9030 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterBlockEntity.kt @@ -0,0 +1,168 @@ +package net.shadowfacts.phycon.block.inserter + +import alexiil.mc.lib.attributes.SearchOptions +import alexiil.mc.lib.attributes.Simulation +import alexiil.mc.lib.attributes.item.ItemAttributes +import alexiil.mc.lib.attributes.item.ItemInsertable +import alexiil.mc.lib.attributes.item.ItemStackUtil +import net.minecraft.block.BlockState +import net.minecraft.item.ItemStack +import net.minecraft.nbt.CompoundTag +import net.minecraft.util.math.Direction +import net.shadowfacts.phycon.api.packet.Packet +import net.shadowfacts.phycon.block.DeviceBlockEntity +import net.shadowfacts.phycon.component.ActivationController +import net.shadowfacts.phycon.component.ItemStackPacketHandler +import net.shadowfacts.phycon.component.NetworkStackProvider +import net.shadowfacts.phycon.component.handleItemStack +import net.shadowfacts.phycon.init.PhyBlockEntities +import net.shadowfacts.phycon.packet.* +import net.shadowfacts.phycon.util.ActivationMode +import kotlin.math.min + +/** + * @author shadowfacts + */ +class InserterBlockEntity: DeviceBlockEntity(PhyBlockEntities.INSERTER), + ItemStackPacketHandler, + ActivationController.ActivatableDevice { + + companion object { + val SLEEP_TIME = 40L + val REQUEST_TIMEOUT = 40 + } + + private val facing: Direction + get() = cachedState[InserterBlock.FACING] + + private var inventory: ItemInsertable? = null + private var currentRequest: PendingExtractRequest? = null + var stackToExtract = ItemStack.EMPTY + var amountToExtract = 1 + override val controller = ActivationController(SLEEP_TIME, this) + + fun updateInventory() { + val offsetPos = pos.offset(facing) + val option = SearchOptions.inDirection(facing) + inventory = ItemAttributes.INSERTABLE.getFirstOrNull(world, offsetPos, option) + } + + private fun getInventory(): ItemInsertable? { + if (inventory == null) updateInventory() + return inventory + } + + override fun handle(packet: Packet) { + when (packet) { + is RemoteActivationPacket -> controller.handleRemoteActivation(packet) + is StackLocationPacket -> handleStackLocation(packet) + is ItemStackPacket -> handleItemStack(packet) + } + } + + override fun doHandleItemStack(packet: ItemStackPacket): ItemStack { + val inventory = getInventory() + return if (inventory != null) { + inventory.attemptInsertion(packet.stack, Simulation.ACTION) + } else { + // no inventory, entire stack remains + packet.stack + } + } + + private fun handleStackLocation(packet: StackLocationPacket) { + val request = currentRequest + if (request != null && ItemStackUtil.areEqualIgnoreAmounts(request.stack, packet.stack)) { + request.results.add(packet.amount to packet.stackProvider) + if (request.isFinishable(counter)) { + finishRequest() + } + } + } + + override fun tick() { + super.tick() + + if (!world!!.isClient) { + controller.tick() + + val request = currentRequest + if (request != null) { + if (request.isFinishable(counter)) { + finishRequest() + } else if (counter - request.timestamp >= REQUEST_TIMEOUT && request.totalAmount == 0) { + currentRequest = null + } + } + } + } + + override fun activate(): Boolean { + if (currentRequest != null || stackToExtract.isEmpty) { + return false + } + + // todo: configure me + currentRequest = PendingExtractRequest(stackToExtract, counter) + sendPacket(LocateStackPacket(stackToExtract, ipAddress)) + return true + } + + private fun finishRequest() { + val request = currentRequest ?: return + + // todo: dedup with TerminalBlockEntity.stackLocateRequestCompleted + val actualAmount = min(min(request.stack.maxCount, request.totalAmount), amountToExtract) + val sortedResults = request.results.sortedByDescending { it.first }.toMutableList() + var amountRequested = 0 + while (amountRequested < actualAmount && sortedResults.isNotEmpty()) { + val (sourceAmount, source) = sortedResults.removeAt(0) + val amountToRequest = min(sourceAmount, actualAmount - amountRequested) + amountRequested += amountToRequest + sendPacket(ExtractStackPacket(request.stack, amountToRequest, ipAddress, source.ipAddress)) + } + + currentRequest = null + } + + override fun toTag(tag: CompoundTag): CompoundTag { + tag.putString("ActivationMode", controller.activationMode.name) + tag.put("StackToExtract", stackToExtract.toTag(CompoundTag())) + tag.putInt("AmountToExtract", amountToExtract) + return super.toTag(tag) + } + + override fun fromTag(state: BlockState, tag: CompoundTag) { + super.fromTag(state, tag) + controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode")) + stackToExtract = ItemStack.fromTag(tag.getCompound("StackToExtract")) + amountToExtract = tag.getInt("AmountToExtract") + } + + override fun toClientTag(tag: CompoundTag): CompoundTag { + tag.putString("ActivationMode", controller.activationMode.name) + tag.put("StackToExtract", stackToExtract.toTag(CompoundTag())) + tag.putInt("AmountToExtract", amountToExtract) + return super.toClientTag(tag) + } + + override fun fromClientTag(tag: CompoundTag) { + super.fromClientTag(tag) + controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode")) + stackToExtract = ItemStack.fromTag(tag.getCompound("StackToExtract")) + amountToExtract = tag.getInt("AmountToExtract") + } + + class PendingExtractRequest( + val stack: ItemStack, + val timestamp: Long, + var results: MutableSet> = mutableSetOf() + ) { + val totalAmount: Int + get() = results.fold(0) { acc, (amount, _) -> acc + amount } + + fun isFinishable(currentTimestamp: Long): Boolean { + return totalAmount >= stack.maxCount || (currentTimestamp - timestamp >= REQUEST_TIMEOUT && totalAmount > 0) + } + } +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterScreen.kt b/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterScreen.kt new file mode 100644 index 0000000..699b704 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterScreen.kt @@ -0,0 +1,123 @@ +package net.shadowfacts.phycon.block.inserter + +import com.mojang.blaze3d.systems.RenderSystem +import net.minecraft.client.gui.screen.ingame.HandledScreen +import net.minecraft.client.gui.widget.TextFieldWidget +import net.minecraft.client.util.math.MatrixStack +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.screen.slot.Slot +import net.minecraft.screen.slot.SlotActionType +import net.minecraft.text.LiteralText +import net.minecraft.text.Text +import net.minecraft.text.TranslatableText +import net.minecraft.util.Identifier +import net.shadowfacts.phycon.PhysicalConnectivity +import net.shadowfacts.phycon.networking.C2SConfigureInserterAmount +import java.lang.NumberFormatException + +/** + * @author shadowfacts + */ +class InserterScreen( + handler: InserterScreenHandler, + playerInv: PlayerInventory, + title: Text, +): HandledScreen( + handler, + playerInv, + title +) { + + companion object { + val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/inserter.png") + } + + private lateinit var amountField: TextFieldWidget + + init { + backgroundWidth = 176 + backgroundHeight = 133 + playerInventoryTitleY = backgroundHeight - 94 + } + + override fun init() { + super.init() + + amountField = TextFieldWidget(textRenderer, x + 57, y + 24, 80, 9, LiteralText("Amount")) + amountField.text = handler.inserter.amountToExtract.toString() + amountField.setHasBorder(false) + amountField.isVisible = true + amountField.setSelected(true) + amountField.setEditableColor(0xffffff) + amountField.setTextPredicate { + if (it.isEmpty()) { + true + } else { + try { + val value = Integer.parseInt(it) + value in 1..64 + } catch (e: NumberFormatException) { + false + } + } + } + addChild(amountField) + } + + fun amountUpdated() { + if (amountField.text.isNotEmpty()) { + handler.inserter.amountToExtract = Integer.parseInt(amountField.text) + client!!.player!!.networkHandler.sendPacket(C2SConfigureInserterAmount(handler.inserter)) + } + } + + override fun tick() { + super.tick() + amountField.tick() + } + + override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) { + RenderSystem.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) + } + + override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) { + super.render(matrixStack, mouseX, mouseY, delta) + + amountField.render(matrixStack, mouseX, mouseY, delta) + + drawMouseoverTooltip(matrixStack, mouseX, mouseY) + } + + override fun onMouseClick(slot: Slot?, invSlot: Int, clickData: Int, slotActionType: SlotActionType?) { + super.onMouseClick(slot, invSlot, clickData, slotActionType) + + amountField.setSelected(true) + } + + override fun charTyped(c: Char, i: Int): Boolean { + val oldText = amountField.text + if (amountField.charTyped(c, i)) { + if (oldText != amountField.text) { + amountUpdated() + } + return true + } + return super.charTyped(c, i) + } + + override fun keyPressed(i: Int, j: Int, k: Int): Boolean { + val oldText = amountField.text + if (amountField.keyPressed(i, j, k)) { + if (oldText != amountField.text) { + amountUpdated() + } + return true + } + return super.keyPressed(i, j, k) + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterScreenHandler.kt b/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterScreenHandler.kt new file mode 100644 index 0000000..5a8ffdf --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterScreenHandler.kt @@ -0,0 +1,122 @@ +package net.shadowfacts.phycon.block.inserter + +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.inventory.Inventory +import net.minecraft.item.ItemStack +import net.minecraft.network.PacketByteBuf +import net.minecraft.screen.ScreenHandler +import net.minecraft.screen.slot.Slot +import net.minecraft.screen.slot.SlotActionType +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.min + +/** + * @author shadowfacts + */ +class InserterScreenHandler( + syncId: Int, + playerInv: PlayerInventory, + val inserter: InserterBlockEntity, +): ScreenHandler(PhyScreens.INSERTER_SCREEN_HANDLER, syncId) { + + companion object { + val ID = Identifier(PhysicalConnectivity.MODID, "inserter") + } + + private val fakeInv = FakeInventory(inserter) + + constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf): + this( + syncId, + playerInv, + PhyBlocks.INSERTER.getBlockEntity(playerInv.player.world, buf.readBlockPos())!! + ) + + init { + // fake slot + addSlot(FakeSlot(fakeInv, 31, 20)) + + // player inv + for (y in 0 until 3) { + for (x in 0 until 9) { + addSlot(Slot(playerInv, x + y * 9 + 9, 8 + x * 18, 51 + y * 18)) + } + } + + // hotbar + for (x in 0 until 9) { + addSlot(Slot(playerInv, x, 8 + x * 18, 109)) + } + } + + private fun stackToExtractChanged() { + inserter.amountToExtract = min(inserter.stackToExtract.maxCount, inserter.amountToExtract) + } + + override fun canUse(player: PlayerEntity): Boolean { + return true + } + + override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack { + // fake slot + if (slotId == 0) { + if (player.inventory.cursorStack.isEmpty) { + inserter.stackToExtract = ItemStack.EMPTY + } else { + val copy = player.inventory.cursorStack.copy() + copy.count = 1 + inserter.stackToExtract = copy + } + stackToExtractChanged() + } + return super.onSlotClick(slotId, clickData, actionType, player) + } + + override fun transferSlot(player: PlayerEntity, slotId: Int): ItemStack { + val slot = slots[slotId] + val copy = slot.stack.copy() + copy.count = 1 + inserter.stackToExtract = copy + stackToExtractChanged() + return ItemStack.EMPTY + } + + class FakeSlot(inv: FakeInventory, x: Int, y: Int): Slot(inv, 0, x, y) { + override fun canInsert(stack: ItemStack) = false + + override fun setStack(stack: ItemStack) { + } + + override fun canTakeItems(player: PlayerEntity) = false + } + + class FakeInventory(val inserter: InserterBlockEntity): Inventory { + override fun clear() { + inserter.stackToExtract = ItemStack.EMPTY + } + + override fun size() = 1 + + override fun isEmpty() = inserter.stackToExtract.isEmpty + + override fun getStack(i: Int): ItemStack { + return if (i == 0) inserter.stackToExtract else ItemStack.EMPTY + } + + override fun removeStack(i: Int, j: Int) = ItemStack.EMPTY + + override fun removeStack(i: Int) = ItemStack.EMPTY + + override fun setStack(i: Int, itemStack: ItemStack?) {} + + override fun markDirty() {} + + override fun canPlayerUse(playerEntity: PlayerEntity?) = true + + } + +} 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 de7fc8c..986dd0f 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalBlockEntity.kt @@ -183,7 +183,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), inventoryCache.clear() sendPacket(RequestInventoryPacket(ipAddress)) 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) } diff --git a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt index 227c344..ad659a5 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlockEntities.kt @@ -7,6 +7,8 @@ import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry import net.shadowfacts.phycon.block.extractor.ExtractorBlock import net.shadowfacts.phycon.block.extractor.ExtractorBlockEntity +import net.shadowfacts.phycon.block.inserter.InserterBlock +import net.shadowfacts.phycon.block.inserter.InserterBlockEntity import net.shadowfacts.phycon.block.miner.MinerBlock import net.shadowfacts.phycon.block.miner.MinerBlockEntity import net.shadowfacts.phycon.block.netinterface.InterfaceBlock @@ -27,6 +29,7 @@ object PhyBlockEntities { val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL) val SWITCH = create(::SwitchBlockEntity, PhyBlocks.SWITCH) val EXTRACTOR = create(::ExtractorBlockEntity, PhyBlocks.EXTRACTOR) + val INSERTER = create(::InserterBlockEntity, PhyBlocks.INSERTER) val MINER = create(::MinerBlockEntity, PhyBlocks.MINER) val REDSTONE_CONTROLLER = create(::RedstoneControllerBlockEntity, PhyBlocks.REDSTONE_CONTROLLER) @@ -39,6 +42,7 @@ object PhyBlockEntities { register(TerminalBlock.ID, TERMINAL) register(SwitchBlock.ID, SWITCH) register(ExtractorBlock.ID, EXTRACTOR) + register(InserterBlock.ID, INSERTER) register(MinerBlock.ID, MINER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) } diff --git a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt index 41cd8df..48cd5b1 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/init/PhyBlocks.kt @@ -5,6 +5,7 @@ import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry import net.shadowfacts.phycon.block.cable.CableBlock import net.shadowfacts.phycon.block.extractor.ExtractorBlock +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 @@ -21,6 +22,7 @@ object PhyBlocks { val SWITCH = SwitchBlock() val CABLE = CableBlock() val EXTRACTOR = ExtractorBlock() + val INSERTER = InserterBlock() val MINER = MinerBlock() val REDSTONE_CONTROLLER = RedstoneControllerBlock() @@ -30,6 +32,7 @@ object PhyBlocks { register(SwitchBlock.ID, SWITCH) register(CableBlock.ID, CABLE) register(ExtractorBlock.ID, EXTRACTOR) + register(InserterBlock.ID, INSERTER) register(MinerBlock.ID, MINER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) } diff --git a/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt b/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt index de01063..4d0e15f 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/init/PhyItems.kt @@ -8,6 +8,7 @@ import net.shadowfacts.phycon.item.ConsoleItem import net.shadowfacts.phycon.item.ScrewdriverItem import net.shadowfacts.phycon.block.cable.CableBlock import net.shadowfacts.phycon.block.extractor.ExtractorBlock +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 @@ -24,6 +25,7 @@ object PhyItems { val SWITCH = BlockItem(PhyBlocks.SWITCH, Item.Settings()) val CABLE = BlockItem(PhyBlocks.CABLE, Item.Settings()) val EXTRACTOR = BlockItem(PhyBlocks.EXTRACTOR, Item.Settings()) + val INSERTER = BlockItem(PhyBlocks.INSERTER, Item.Settings()) val MINER = BlockItem(PhyBlocks.MINER, Item.Settings()) val REDSTONE_CONTROLLER = BlockItem(PhyBlocks.REDSTONE_CONTROLLER, Item.Settings()) @@ -36,6 +38,7 @@ object PhyItems { register(SwitchBlock.ID, SWITCH) register(CableBlock.ID, CABLE) register(ExtractorBlock.ID, EXTRACTOR) + register(InserterBlock.ID, INSERTER) register(MinerBlock.ID, MINER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) diff --git a/src/main/kotlin/net/shadowfacts/phycon/init/PhyScreens.kt b/src/main/kotlin/net/shadowfacts/phycon/init/PhyScreens.kt index 75bfd8d..f8f36e5 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/init/PhyScreens.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/init/PhyScreens.kt @@ -4,15 +4,19 @@ 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.block.inserter.InserterScreenHandler import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler object PhyScreens { lateinit var TERMINAL_SCREEN_HANDLER: ScreenHandlerType private set + lateinit var INSERTER_SCREEN_HANDLER: ScreenHandlerType + private set fun init() { - TERMINAL_SCREEN_HANDLER = ScreenHandlerRegistry.registerExtended(Identifier(PhysicalConnectivity.MODID, "terminal"), ::TerminalScreenHandler) + TERMINAL_SCREEN_HANDLER = ScreenHandlerRegistry.registerExtended(TerminalScreenHandler.ID, ::TerminalScreenHandler) + INSERTER_SCREEN_HANDLER = ScreenHandlerRegistry.registerExtended(InserterScreenHandler.ID, ::InserterScreenHandler) } } diff --git a/src/main/kotlin/net/shadowfacts/phycon/networking/C2SConfigureInserterAmount.kt b/src/main/kotlin/net/shadowfacts/phycon/networking/C2SConfigureInserterAmount.kt new file mode 100644 index 0000000..86c23a0 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/networking/C2SConfigureInserterAmount.kt @@ -0,0 +1,45 @@ +package net.shadowfacts.phycon.networking + +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.fabricmc.fabric.api.networking.v1.PacketSender +import net.minecraft.network.Packet +import net.minecraft.network.PacketByteBuf +import net.minecraft.server.MinecraftServer +import net.minecraft.server.network.ServerPlayNetworkHandler +import net.minecraft.server.network.ServerPlayerEntity +import net.minecraft.util.Identifier +import net.minecraft.util.registry.Registry +import net.minecraft.util.registry.RegistryKey +import net.shadowfacts.phycon.PhysicalConnectivity +import net.shadowfacts.phycon.block.inserter.InserterBlockEntity + +/** + * @author shadowfacts + */ +object C2SConfigureInserterAmount: ServerReceiver { + override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "configure_inserter_amount") + + operator fun invoke(be: InserterBlockEntity): Packet<*> { + val buf = PacketByteBufs.create() + + buf.writeIdentifier(be.world!!.registryKey.value) + buf.writeBlockPos(be.pos) + buf.writeVarInt(be.amountToExtract) + + return createPacket(buf) + } + + override fun receive(server: MinecraftServer, player: ServerPlayerEntity, handler: ServerPlayNetworkHandler, buf: PacketByteBuf, responseSender: PacketSender) { + val dimID = buf.readIdentifier() + val pos = buf.readBlockPos() + val amount = buf.readVarInt() + + server.execute { + val key = RegistryKey.of(Registry.DIMENSION, dimID) + val world = server.getWorld(key) ?: return@execute + val be = world.getBlockEntity(pos) as? InserterBlockEntity ?: return@execute + be.amountToExtract = amount + be.markDirty() + } + } +} diff --git a/src/main/resources/assets/phycon/blockstates/inserter.json b/src/main/resources/assets/phycon/blockstates/inserter.json new file mode 100644 index 0000000..ef844a4 --- /dev/null +++ b/src/main/resources/assets/phycon/blockstates/inserter.json @@ -0,0 +1,29 @@ +{ + "variants": { + "facing=down": { + "model": "phycon:block/inserter" + }, + "facing=up": { + "model": "phycon:block/inserter", + "x": 180 + }, + "facing=north": { + "model": "phycon:block/inserter", + "x": 270 + }, + "facing=south": { + "model": "phycon:block/inserter", + "x": 90 + }, + "facing=west": { + "model": "phycon:block/inserter", + "x": 90, + "y": 90 + }, + "facing=east": { + "model": "phycon:block/inserter", + "x": 90, + "y": 270 + } + } +} diff --git a/src/main/resources/assets/phycon/lang/en_us.json b/src/main/resources/assets/phycon/lang/en_us.json index 389a51c..e558bfb 100644 --- a/src/main/resources/assets/phycon/lang/en_us.json +++ b/src/main/resources/assets/phycon/lang/en_us.json @@ -4,6 +4,7 @@ "block.phycon.terminal": "Terminal", "block.phycon.cable": "Cable", "block.phycon.extractor": "Inventory Extractor", + "block.phycon.inserter": "Inventory Inserter", "block.phycon.miner": "Block Miner", "block.phycon.redstone_controller": "Redstone Controller", diff --git a/src/main/resources/assets/phycon/models/block/extractor.json b/src/main/resources/assets/phycon/models/block/extractor.json index fbbba21..c0ad572 100644 --- a/src/main/resources/assets/phycon/models/block/extractor.json +++ b/src/main/resources/assets/phycon/models/block/extractor.json @@ -1,11 +1,15 @@ { "parent": "block/block", + "textures": { + "cable_side": "phycon:block/cable_straight", + "cable_end": "phycon:block/cable_cap_end" + }, "elements": [ { "from": [0, 0, 0], "to": [16, 2, 16], "faces": { - "down": {"texture": "phycon:block/extractor_front"}, + "down": {"texture": "phycon:block/extractor_front", "cullface": "down"}, "up": {"texture": "phycon:block/extractor_back"}, "north": {"texture": "phycon:block/extractor_side"}, "south": {"texture": "phycon:block/extractor_side"}, @@ -39,12 +43,12 @@ "from": [6, 6, 6], "to": [10, 16, 10], "faces": { - "up": {"texture": "phycon:block/cable_side"}, - "north": {"texture": "phycon:block/cable_side"}, - "south": {"texture": "phycon:block/cable_side"}, - "west": {"texture": "phycon:block/cable_side"}, - "east": {"texture": "phycon:block/cable_side"} + "up": {"texture": "#cable_end", "cullface": "up"}, + "north": {"texture": "#cable_side"}, + "south": {"texture": "#cable_side"}, + "west": {"texture": "#cable_side"}, + "east": {"texture": "#cable_side"} } } ] -} \ No newline at end of file +} diff --git a/src/main/resources/assets/phycon/models/block/inserter.json b/src/main/resources/assets/phycon/models/block/inserter.json new file mode 100644 index 0000000..e11da66 --- /dev/null +++ b/src/main/resources/assets/phycon/models/block/inserter.json @@ -0,0 +1,54 @@ +{ + "parent": "block/block", + "textures": { + "cable_side": "phycon:block/cable_straight", + "cable_end": "phycon:block/cable_cap_end" + }, + "elements": [ + { + "from": [4, 0, 4], + "to": [12, 2, 12], + "faces": { + "down": {"texture": "phycon:block/extractor_front", "cullface": "down"}, + "north": {"texture": "phycon:block/extractor_side"}, + "south": {"texture": "phycon:block/extractor_side"}, + "west": {"texture": "phycon:block/extractor_side"}, + "east": {"texture": "phycon:block/extractor_side"} + } + }, + { + "from": [2, 2, 2], + "to": [14, 4, 14], + "faces": { + "down": {"texture": "phycon:block/extractor_front"}, + "north": {"texture": "phycon:block/extractor_side"}, + "south": {"texture": "phycon:block/extractor_side"}, + "west": {"texture": "phycon:block/extractor_side"}, + "east": {"texture": "phycon:block/extractor_side"} + } + }, + { + "from": [0, 4, 0], + "to": [16, 6, 16], + "faces": { + "down": {"texture": "phycon:block/extractor_front"}, + "up": {"texture": "phycon:block/extractor_back"}, + "north": {"texture": "phycon:block/extractor_side"}, + "south": {"texture": "phycon:block/extractor_side"}, + "west": {"texture": "phycon:block/extractor_side"}, + "east": {"texture": "phycon:block/extractor_side"} + } + }, + { + "from": [6, 6, 6], + "to": [10, 16, 10], + "faces": { + "up": {"texture": "#cable_end", "cullface": "up"}, + "north": {"texture": "#cable_side"}, + "south": {"texture": "#cable_side"}, + "west": {"texture": "#cable_side"}, + "east": {"texture": "#cable_side"} + } + } + ] +} diff --git a/src/main/resources/assets/phycon/textures/gui/inserter.png b/src/main/resources/assets/phycon/textures/gui/inserter.png new file mode 100644 index 0000000..8fe950f Binary files /dev/null and b/src/main/resources/assets/phycon/textures/gui/inserter.png differ