2021-02-28 18:48:39 +00:00
|
|
|
package net.shadowfacts.phycon.block.terminal
|
2021-02-14 02:37:39 +00:00
|
|
|
|
|
|
|
import net.minecraft.screen.slot.Slot
|
|
|
|
import net.minecraft.screen.slot.SlotActionType
|
|
|
|
import net.minecraft.entity.player.PlayerEntity
|
|
|
|
import net.minecraft.entity.player.PlayerInventory
|
|
|
|
import net.minecraft.item.ItemStack
|
|
|
|
import net.minecraft.network.PacketByteBuf
|
|
|
|
import net.minecraft.screen.ScreenHandler
|
2021-02-21 17:00:32 +00:00
|
|
|
import net.minecraft.server.network.ServerPlayerEntity
|
2021-02-14 02:37:39 +00:00
|
|
|
import net.minecraft.util.Identifier
|
2021-02-16 03:51:33 +00:00
|
|
|
import net.minecraft.util.registry.Registry
|
2021-02-14 02:37:39 +00:00
|
|
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
|
|
|
import net.shadowfacts.phycon.init.PhyBlocks
|
|
|
|
import net.shadowfacts.phycon.init.PhyScreens
|
2021-02-21 17:00:32 +00:00
|
|
|
import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
|
2021-02-21 16:13:49 +00:00
|
|
|
import net.shadowfacts.phycon.util.SortMode
|
2021-02-16 03:51:33 +00:00
|
|
|
import java.lang.ref.WeakReference
|
2021-02-14 02:37:39 +00:00
|
|
|
import kotlin.math.min
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @author shadowfacts
|
|
|
|
*/
|
2021-02-21 17:00:32 +00:00
|
|
|
class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val terminal: TerminalBlockEntity): ScreenHandler(PhyScreens.TERMINAL_SCREEN_HANDLER, syncId),
|
2021-02-16 03:51:33 +00:00
|
|
|
TerminalBlockEntity.NetItemObserver {
|
2021-02-14 02:37:39 +00:00
|
|
|
|
|
|
|
companion object {
|
|
|
|
val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
|
|
|
|
}
|
|
|
|
|
2021-02-16 03:51:33 +00:00
|
|
|
private val fakeInv = FakeInventory(this)
|
|
|
|
private var searchQuery: String = ""
|
2021-02-21 16:13:49 +00:00
|
|
|
var sortMode = SortMode.COUNT_HIGH_FIRST
|
2021-02-21 17:00:32 +00:00
|
|
|
private set
|
|
|
|
private var itemEntries = listOf<Entry>()
|
|
|
|
set(value) {
|
|
|
|
field = value
|
|
|
|
if (terminal.world!!.isClient) {
|
|
|
|
itemsForDisplay = value.map {
|
|
|
|
val stack = it.stack.copy()
|
|
|
|
stack.count = it.amount
|
|
|
|
stack
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-16 03:51:33 +00:00
|
|
|
var itemsForDisplay = listOf<ItemStack>()
|
|
|
|
private set
|
|
|
|
|
2021-02-14 02:37:39 +00:00
|
|
|
constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf):
|
|
|
|
this(syncId, playerInv, PhyBlocks.TERMINAL.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!)
|
|
|
|
|
|
|
|
init {
|
2021-02-21 17:00:32 +00:00
|
|
|
if (!terminal.world!!.isClient) {
|
|
|
|
terminal.netItemObserver = WeakReference(this)
|
|
|
|
netItemsChanged()
|
|
|
|
}
|
2021-02-16 03:51:33 +00:00
|
|
|
|
2021-02-14 02:37:39 +00:00
|
|
|
// network
|
|
|
|
for (y in 0 until 6) {
|
|
|
|
for (x in 0 until 9) {
|
2021-02-16 03:51:33 +00:00
|
|
|
addSlot(TerminalFakeSlot(fakeInv, y * 9 + x, 66 + x * 18, 18 + y * 18))
|
2021-02-14 02:37:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// internal buffer
|
|
|
|
for (y in 0 until 6) {
|
|
|
|
for (x in 0 until 3) {
|
|
|
|
addSlot(Slot(terminal.internalBuffer, y * 3 + x, 8 + x * 18, 18 + y * 18))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// player inv
|
|
|
|
for (y in 0 until 3) {
|
|
|
|
for (x in 0 until 9) {
|
|
|
|
addSlot(Slot(playerInv, x + y * 9 + 9, 66 + x * 18, 140 + y * 18))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// hotbar
|
|
|
|
for (x in 0 until 9) {
|
|
|
|
addSlot(Slot(playerInv, x, 66 + x * 18, 198))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-16 03:51:33 +00:00
|
|
|
override fun netItemsChanged() {
|
2021-02-21 17:00:32 +00:00
|
|
|
val player = playerInv.player
|
|
|
|
assert(player is ServerPlayerEntity)
|
|
|
|
|
2021-02-21 16:13:49 +00:00
|
|
|
val filtered = terminal.cachedNetItems.object2IntEntrySet().filter {
|
2021-02-16 03:51:33 +00:00
|
|
|
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)
|
2021-02-21 16:13:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
val sorted =
|
|
|
|
when (sortMode) {
|
|
|
|
SortMode.COUNT_HIGH_FIRST -> filtered.sortedByDescending { it.intValue }
|
|
|
|
SortMode.COUNT_LOW_FIRST -> filtered.sortedBy { it.intValue }
|
|
|
|
SortMode.ALPHABETICAL -> filtered.sortedBy { it.key.name.string }
|
|
|
|
}
|
|
|
|
|
2021-02-21 17:00:32 +00:00
|
|
|
itemEntries = sorted.map { Entry(it.key, it.intValue) }
|
|
|
|
|
|
|
|
(player as ServerPlayerEntity).networkHandler.sendPacket(S2CTerminalUpdateDisplayedItems(terminal, itemEntries, searchQuery, sortMode))
|
2021-02-16 03:51:33 +00:00
|
|
|
}
|
|
|
|
|
2021-02-21 17:00:32 +00:00
|
|
|
fun sendUpdatedItemsToClient(player: ServerPlayerEntity, query: String, sortMode: SortMode) {
|
|
|
|
this.searchQuery = query
|
|
|
|
this.sortMode = sortMode
|
2021-02-16 03:51:33 +00:00
|
|
|
netItemsChanged()
|
|
|
|
}
|
|
|
|
|
2021-02-21 17:00:32 +00:00
|
|
|
fun receivedUpdatedItemsFromServer(entries: List<Entry>, query: String, sortMode: SortMode) {
|
|
|
|
assert(playerInv.player.world.isClient)
|
|
|
|
|
|
|
|
this.searchQuery = query
|
|
|
|
this.sortMode = sortMode
|
|
|
|
itemEntries = entries
|
|
|
|
}
|
|
|
|
|
2021-02-14 02:37:39 +00:00
|
|
|
override fun canUse(player: PlayerEntity): Boolean {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun close(player: PlayerEntity) {
|
|
|
|
super.close(player)
|
|
|
|
|
|
|
|
terminal.removeObserver()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack {
|
2021-02-21 17:13:06 +00:00
|
|
|
if (isBufferSlot(slotId)) {
|
2021-02-14 02:37:39 +00:00
|
|
|
// todo: why does this think it's quick_craft sometimes?
|
|
|
|
if ((actionType == SlotActionType.PICKUP || actionType == SlotActionType.QUICK_CRAFT) && !player.inventory.cursorStack.isEmpty) {
|
|
|
|
// placing cursor stack into buffer
|
|
|
|
val bufferSlot = slotId - bufferSlotsStart // subtract 54 to convert the handler slot ID to a valid buffer index
|
|
|
|
terminal.internalBuffer.markSlot(bufferSlot, TerminalBufferInventory.Mode.TO_NETWORK)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return super.onSlotClick(slotId, clickData, actionType, player)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun transferSlot(player: PlayerEntity, slotId: Int): ItemStack {
|
|
|
|
if (isNetworkSlot(slotId)) {
|
|
|
|
return ItemStack.EMPTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
val slot = slots[slotId]
|
|
|
|
if (!slot.hasStack()) {
|
|
|
|
return ItemStack.EMPTY
|
|
|
|
}
|
|
|
|
|
|
|
|
val result = slot.stack.copy()
|
|
|
|
|
|
|
|
if (isBufferSlot(slotId)) {
|
2021-02-20 20:28:55 +00:00
|
|
|
// last boolean param is fromLast
|
|
|
|
if (!insertItem(slot.stack, playerSlotsStart, playerSlotsEnd, true)) {
|
2021-02-14 02:37:39 +00:00
|
|
|
return ItemStack.EMPTY
|
|
|
|
}
|
|
|
|
if (slot.stack.isEmpty) {
|
|
|
|
terminal.internalBuffer.markSlot(slotId - bufferSlotsStart, TerminalBufferInventory.Mode.UNASSIGNED)
|
|
|
|
}
|
|
|
|
} else if (isPlayerSlot(slotId)) {
|
|
|
|
val slotsInsertedInto = tryInsertItem(slot.stack, bufferSlotsStart until playerSlotsStart) { terminal.internalBuffer.getMode(it - bufferSlotsStart) != TerminalBufferInventory.Mode.FROM_NETWORK }
|
|
|
|
slotsInsertedInto.forEach { terminal.internalBuffer.markSlot(it - bufferSlotsStart, TerminalBufferInventory.Mode.TO_NETWORK) }
|
|
|
|
if (slot.stack.isEmpty) {
|
|
|
|
return ItemStack.EMPTY
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun tryInsertItem(stack: ItemStack, slots: IntRange, slotPredicate: (Int) -> Boolean): Collection<Int> {
|
|
|
|
val slotsInsertedInto = mutableListOf<Int>()
|
|
|
|
for (index in slots) {
|
|
|
|
if (stack.isEmpty) break
|
|
|
|
if (!slotPredicate(index)) continue
|
|
|
|
|
|
|
|
val slot = this.slots[index]
|
|
|
|
val slotStack = slot.stack
|
|
|
|
if (slotStack.isEmpty) {
|
|
|
|
slot.stack = stack.copy()
|
|
|
|
stack.count = 0
|
|
|
|
|
|
|
|
slot.markDirty()
|
|
|
|
slotsInsertedInto.add(index)
|
|
|
|
} else if (canStacksCombine(slotStack, stack) && slotStack.count < slotStack.maxCount) {
|
|
|
|
val maxToMove = slotStack.maxCount - slotStack.count
|
|
|
|
val toMove = min(maxToMove, stack.count)
|
|
|
|
slotStack.increment(toMove)
|
|
|
|
stack.decrement(toMove)
|
|
|
|
|
|
|
|
slot.markDirty()
|
|
|
|
slotsInsertedInto.add(index)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return slotsInsertedInto
|
|
|
|
}
|
|
|
|
|
|
|
|
val bufferSlotsStart = 54
|
|
|
|
val playerSlotsStart = 72
|
|
|
|
val playerSlotsEnd = 72 + 36
|
|
|
|
fun isNetworkSlot(id: Int) = id in 0 until bufferSlotsStart
|
|
|
|
fun isBufferSlot(id: Int) = id in bufferSlotsStart until playerSlotsStart
|
|
|
|
fun isPlayerSlot(id: Int) = id >= playerSlotsStart
|
2021-02-21 17:00:32 +00:00
|
|
|
|
|
|
|
data class Entry(val stack: ItemStack, val amount: Int)
|
2021-02-14 02:37:39 +00:00
|
|
|
}
|