Add search field to terminal
This commit is contained in:
parent
249416b8c2
commit
c6a5602ec1
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<ItemStack>()
|
||||
|
||||
var netItemObserver: WeakReference<NetItemObserver>? = 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(
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<ItemStack>()
|
||||
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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue