From c6a5602ec146fb6cf28ec17ff6c5897186c15dd3 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 15 Feb 2021 22:51:33 -0500 Subject: [PATCH] Add search field to terminal --- .../phycon/PhysicalConnectivity.kt | 9 +++ .../shadowfacts/phycon/item/ConsoleItem.kt | 4 -- .../block/terminal/TerminalBlockEntity.kt | 8 +++ .../block/terminal/TerminalFakeSlot.kt | 10 ++-- .../network/block/terminal/TerminalScreen.kt | 59 +++++++++++++++++++ .../block/terminal/TerminalScreenHandler.kt | 58 ++++++++++++++++-- .../networking/C2STerminalRequestItem.kt | 48 +++++++++++++++ .../phycon/networking/ServerReceiver.kt | 11 ++++ 8 files changed, 192 insertions(+), 15 deletions(-) create mode 100644 src/main/kotlin/net/shadowfacts/phycon/networking/C2STerminalRequestItem.kt create mode 100644 src/main/kotlin/net/shadowfacts/phycon/networking/ServerReceiver.kt diff --git a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt index 2adf327..1a1459b 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/PhysicalConnectivity.kt @@ -1,10 +1,13 @@ package net.shadowfacts.phycon import net.fabricmc.api.ModInitializer +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlocks import net.shadowfacts.phycon.init.PhyItems import net.shadowfacts.phycon.init.PhyScreens +import net.shadowfacts.phycon.networking.C2STerminalRequestItem +import net.shadowfacts.phycon.networking.ServerReceiver /** * @author shadowfacts @@ -18,6 +21,12 @@ object PhysicalConnectivity: ModInitializer { PhyBlockEntities.init() PhyItems.init() PhyScreens.init() + + registerGlobalReceiver(C2STerminalRequestItem) + } + + private fun registerGlobalReceiver(receiver: ServerReceiver) { + ServerPlayNetworking.registerGlobalReceiver(receiver.CHANNEL, receiver) } } diff --git a/src/main/kotlin/net/shadowfacts/phycon/item/ConsoleItem.kt b/src/main/kotlin/net/shadowfacts/phycon/item/ConsoleItem.kt index cd4ef8f..a6570af 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/item/ConsoleItem.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/item/ConsoleItem.kt @@ -1,11 +1,8 @@ package net.shadowfacts.phycon.item -import net.fabricmc.api.EnvType -import net.fabricmc.api.Environment import net.minecraft.client.MinecraftClient import net.minecraft.item.Item import net.minecraft.item.ItemUsageContext -import net.minecraft.text.LiteralText import net.minecraft.util.ActionResult import net.minecraft.util.Identifier import net.shadowfacts.phycon.PhysicalConnectivity @@ -38,7 +35,6 @@ class ConsoleItem: Item(Settings()) { return ActionResult.PASS } - @Environment(EnvType.CLIENT) private fun openScreen(be: DeviceBlockEntity) { val screen = DeviceConsoleScreen(be) MinecraftClient.getInstance().openScreen(screen) diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt index 578760a..88631ac 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalBlockEntity.kt @@ -31,6 +31,7 @@ import net.shadowfacts.phycon.network.component.NetworkStackProvider import net.shadowfacts.phycon.network.component.NetworkStackReceiver import net.shadowfacts.phycon.network.component.handleItemStack import net.shadowfacts.phycon.network.packet.* +import java.lang.ref.WeakReference import java.util.* import kotlin.math.min @@ -55,6 +56,8 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento val cachedNetItems = ItemStackCollections.intMap() var cachedSortedNetItems = listOf() + var netItemObserver: WeakReference? = null + init { internalBuffer.addListener(this) } @@ -312,6 +315,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento val netAmount = entryTag.getInt("NetAmount") cachedNetItems[stack] = netAmount } + netItemObserver?.get()?.netItemsChanged() cachedSortedNetItems = cachedNetItems.object2IntEntrySet().sortedByDescending { it.intValue }.map { val stack = it.key.copy() stack.count = it.intValue @@ -319,6 +323,10 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento } } + interface NetItemObserver { + fun netItemsChanged() + } + } data class StackLocateRequest( diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalFakeSlot.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalFakeSlot.kt index d30593c..5fd22c4 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalFakeSlot.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalFakeSlot.kt @@ -8,7 +8,7 @@ import net.minecraft.item.ItemStack /** * @author shadowfacts */ -class TerminalFakeSlot(val terminal: TerminalBlockEntity, slot: Int, x: Int, y: Int): Slot(FakeInventory(terminal, slot), slot, x, y) { +class TerminalFakeSlot(fakeInv: FakeInventory, slot: Int, x: Int, y: Int): Slot(fakeInv, slot, x, y) { override fun canInsert(stack: ItemStack): Boolean { return false @@ -23,10 +23,10 @@ class TerminalFakeSlot(val terminal: TerminalBlockEntity, slot: Int, x: Int, y: } -class FakeInventory(val terminal: TerminalBlockEntity, val slot: Int): Inventory { - override fun getStack(_slot: Int): ItemStack { - if (slot >= terminal.cachedSortedNetItems.size) return ItemStack.EMPTY - return terminal.cachedSortedNetItems[slot] +class FakeInventory(val screenHandler: TerminalScreenHandler): Inventory { + override fun getStack(slot: Int): ItemStack { + if (slot >= screenHandler.itemsForDisplay.size) return ItemStack.EMPTY + return screenHandler.itemsForDisplay[slot] } override fun markDirty() { diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt index 6cf90b8..4ddb166 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreen.kt @@ -3,12 +3,15 @@ package net.shadowfacts.phycon.network.block.terminal import com.mojang.blaze3d.platform.GlStateManager import net.minecraft.client.gui.DrawableHelper import net.minecraft.client.gui.screen.ingame.HandledScreen +import net.minecraft.client.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.text.LiteralText import net.minecraft.text.Text import net.minecraft.util.Identifier import net.shadowfacts.phycon.PhysicalConnectivity +import org.lwjgl.glfw.GLFW /** * @author shadowfacts @@ -19,11 +22,33 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png") } + private lateinit var searchBox: TextFieldWidget + init { backgroundWidth = 252 backgroundHeight = 222 } + override fun init() { + super.init() + + client!!.keyboard.setRepeatEvents(true) + + searchBox = TextFieldWidget(textRenderer, x + 138, y + 6, 80, 9, LiteralText("Search")) + searchBox.setMaxLength(50) + // setHasBorder is actually setDrawsBackground + searchBox.setHasBorder(false) + searchBox.isVisible = true + searchBox.setSelected(true) + searchBox.setEditableColor(0xffffff) + children.add(searchBox) + } + + override fun tick() { + super.tick() + searchBox.tick() + } + override fun drawForeground(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) { textRenderer.draw(matrixStack, title, 65f, 6f, 0x404040) textRenderer.draw(matrixStack, playerInventory.displayName, 65f, backgroundHeight - 94f, 0x404040) @@ -43,6 +68,9 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) { super.render(matrixStack, mouseX, mouseY, delta) + + searchBox.render(matrixStack, mouseX, mouseY, delta) + drawMouseoverTooltip(matrixStack, mouseX, mouseY) } @@ -61,4 +89,35 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, DrawableHelper.fill(matrixStack, slot.x, slot.y, slot.x + 16, slot.y + 16, color.toInt()) } + override fun charTyped(c: Char, i: Int): Boolean { + val oldText = searchBox.text + if (searchBox.charTyped(c, i)) { + if (searchBox.text != oldText) { + search() + } + return true + } + + return super.charTyped(c, i) + } + + override fun keyPressed(key: Int, j: Int, k: Int): Boolean { + val oldText = searchBox.text + if (searchBox.keyPressed(key, j, k)) { + if (searchBox.text != oldText) { + search() + } + return true + } + return if (searchBox.isFocused && searchBox.isVisible && key != GLFW.GLFW_KEY_ESCAPE) { + true + } else { + super.keyPressed(key, j, k) + } + } + + private fun search() { + screenHandler.search(searchBox.text) + } + } diff --git a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt index 76af6ee..e648f07 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/network/block/terminal/TerminalScreenHandler.kt @@ -1,5 +1,6 @@ package net.shadowfacts.phycon.network.block.terminal +import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.SlotActionType import net.minecraft.entity.player.PlayerEntity @@ -8,29 +9,41 @@ import net.minecraft.item.ItemStack import net.minecraft.network.PacketByteBuf import net.minecraft.screen.ScreenHandler import net.minecraft.util.Identifier +import net.minecraft.util.registry.Registry import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.init.PhyBlocks import net.shadowfacts.phycon.init.PhyScreens +import net.shadowfacts.phycon.networking.C2STerminalRequestItem +import java.lang.ref.WeakReference import kotlin.math.ceil import kotlin.math.min /** * @author shadowfacts */ -class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val terminal: TerminalBlockEntity): ScreenHandler(PhyScreens.TERMINAL_SCREEN_HANDLER, syncId) { +class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val terminal: TerminalBlockEntity): ScreenHandler(PhyScreens.TERMINAL_SCREEN_HANDLER, syncId), + TerminalBlockEntity.NetItemObserver { companion object { val ID = Identifier(PhysicalConnectivity.MODID, "terminal") } + private val fakeInv = FakeInventory(this) + private var searchQuery: String = "" + var itemsForDisplay = listOf() + private set + constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf): this(syncId, playerInv, PhyBlocks.TERMINAL.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!) init { + terminal.netItemObserver = WeakReference(this) + netItemsChanged() + // network for (y in 0 until 6) { for (x in 0 until 9) { - addSlot(TerminalFakeSlot(terminal, y * 9 + x, 66 + x * 18, 18 + y * 18)) + addSlot(TerminalFakeSlot(fakeInv, y * 9 + x, 66 + x * 18, 18 + y * 18)) } } @@ -53,6 +66,31 @@ class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val termina } } + override fun netItemsChanged() { + itemsForDisplay = terminal.cachedNetItems.object2IntEntrySet().filter { + if (searchQuery.isBlank()) return@filter true + if (searchQuery.startsWith('@')) { + val unprefixed = searchQuery.drop(1) + val key = Registry.ITEM.getKey(it.key.item) + if (key.isPresent && key.get().value.namespace.startsWith(unprefixed, true)) { + return@filter true + } + } + it.key.name.string.contains(searchQuery, true) + }.sortedByDescending { + it.intValue + }.map { + val stack = it.key.copy() + stack.count = it.intValue + stack + } + } + + fun search(query: String) { + searchQuery = query + netItemsChanged() + } + override fun canUse(player: PlayerEntity): Boolean { return true } @@ -68,15 +106,16 @@ class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val termina // the slot clicked was one of the network stacks if (actionType == SlotActionType.QUICK_MOVE) { val stack = slots[slotId].stack - if (!stack.isEmpty && !player.world.isClient) { - terminal.requestItem(stack, min(stack.count, stack.maxCount)) + if (!stack.isEmpty && player.world.isClient) { + requestItem(player, stack, min(stack.count, stack.maxCount)) + } } else if (actionType == SlotActionType.PICKUP && clickData == 1) { if (clickData == 1) { // right click, request half stack val stack = slots[slotId].stack - if (!stack.isEmpty && !player.world.isClient) { - terminal.requestItem(stack, ceil(min(stack.count, stack.maxCount) / 2f).toInt()) + if (!stack.isEmpty && player.world.isClient) { + requestItem(player, stack, ceil(min(stack.count, stack.maxCount) / 2f).toInt()) } } else { // todo: left click, show amount dialog @@ -95,6 +134,13 @@ class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val termina return super.onSlotClick(slotId, clickData, actionType, player) } + private fun requestItem(player: PlayerEntity, stack: ItemStack, amount: Int) { + if (!player.world.isClient) return + val handler = (player as ClientPlayerEntity).networkHandler + val packet = C2STerminalRequestItem(terminal, stack, amount) + handler.sendPacket(packet) + } + override fun transferSlot(player: PlayerEntity, slotId: Int): ItemStack { if (isNetworkSlot(slotId)) { return ItemStack.EMPTY; diff --git a/src/main/kotlin/net/shadowfacts/phycon/networking/C2STerminalRequestItem.kt b/src/main/kotlin/net/shadowfacts/phycon/networking/C2STerminalRequestItem.kt new file mode 100644 index 0000000..1d28025 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/networking/C2STerminalRequestItem.kt @@ -0,0 +1,48 @@ +package net.shadowfacts.phycon.networking + +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.fabricmc.fabric.api.networking.v1.PacketSender +import net.minecraft.item.ItemStack +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.network.block.terminal.TerminalBlockEntity + +/** + * @author shadowfacts + */ +object C2STerminalRequestItem: ServerReceiver { + + override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "terminal_request_item") + + operator fun invoke(terminal: TerminalBlockEntity, stack: ItemStack, amount: Int): Packet<*> { + val buf = PacketByteBufs.create() + buf.writeIdentifier(terminal.world!!.registryKey.value) + buf.writeBlockPos(terminal.pos) + buf.writeItemStack(stack) + buf.writeVarInt(amount) + return ClientPlayNetworking.createC2SPacket(CHANNEL, buf) + } + + override fun receive(server: MinecraftServer, player: ServerPlayerEntity, handler: ServerPlayNetworkHandler, buf: PacketByteBuf, responseSender: PacketSender) { + val dimID = buf.readIdentifier() + val pos = buf.readBlockPos() + val stack = buf.readItemStack() + val amount = buf.readVarInt() + + server.execute { + val key = RegistryKey.of(Registry.DIMENSION, dimID) + val world = server.getWorld(key) ?: return@execute + val terminal = world.getBlockEntity(pos) as? TerminalBlockEntity ?: return@execute + terminal.requestItem(stack, amount) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/shadowfacts/phycon/networking/ServerReceiver.kt b/src/main/kotlin/net/shadowfacts/phycon/networking/ServerReceiver.kt new file mode 100644 index 0000000..0ea41e9 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/networking/ServerReceiver.kt @@ -0,0 +1,11 @@ +package net.shadowfacts.phycon.networking + +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking +import net.minecraft.util.Identifier + +/** + * @author shadowfacts + */ +interface ServerReceiver: ServerPlayNetworking.PlayChannelHandler { + val CHANNEL: Identifier +} \ No newline at end of file