From e94a267ac150f637156280b92e61e6d21aa4b190 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 20 Aug 2023 22:31:52 +0700 Subject: [PATCH] Make sorting be performed on client, and sorted slot list be sent to server --- .../client/screen/ExopackInventoryScreen.kt | 8 +-- .../mc/otm/client/screen/MatteryScreen.kt | 2 +- .../screen/decorative/CargoCrateScreen.kt | 2 +- .../decorative/MinecartCargoCrateScreen.kt | 2 +- .../client/screen/panels/button/Buttons.kt | 8 +-- .../mc/otm/container/ContainerHelpers.kt | 64 ++++++++++++++++++- .../mc/otm/core/util/StreamCodecs.kt | 29 +++++++++ .../ru/dbotthepony/mc/otm/menu/MatteryMenu.kt | 24 +++++-- .../mc/otm/menu/decorative/CargoCrateMenu.kt | 8 +-- .../menu/decorative/MinecartCargoCrateMenu.kt | 8 +-- 10 files changed, 127 insertions(+), 28 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt index 5a43c252d..7b4ba00f9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt @@ -271,14 +271,8 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen) - if (menu.sortInventoryInput != null) { - controls.addButton(LargeRectangleButtonPanel.input( - this, - controls, - menu.sortInventoryInput!!, - icon = Widgets18.SORT_NOW - ).also { it.tooltips.add(TranslatableComponent("otm.gui.sorting.sort_now")) }) + controls.customSortingButtons(menu.sortInventoryInput!!) } var x = -4f diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt index cdb1be997..051719dfa 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt @@ -308,7 +308,7 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit } if (menu.sortInventoryInput != null) { - deviceControls.customSortingButtons(menu.playerSortSettings, menu.sortInventoryInput!!) + deviceControls.customSortingButtons(menu.sortInventoryInput!!) } if (menu.exopackChargeSlots.isNotEmpty()) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/CargoCrateScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/CargoCrateScreen.kt index 55967a4c6..642fc76eb 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/CargoCrateScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/CargoCrateScreen.kt @@ -28,7 +28,7 @@ class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Compon val controls = DeviceControls(this, frame) - controls.customSortingButtons(menu.playerSortSettings, menu.sort) + controls.customSortingButtons(menu.sort) return frame } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/MinecartCargoCrateScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/MinecartCargoCrateScreen.kt index 30b8af8cd..3b9cfbc8f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/MinecartCargoCrateScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/MinecartCargoCrateScreen.kt @@ -23,7 +23,7 @@ class MinecartCargoCrateScreen(menu: MinecartCargoCrateMenu, inventory: Inventor val controls = DeviceControls(this, frame) - controls.customSortingButtons(menu.playerSortSettings, menu.sort) + controls.customSortingButtons(menu.sort) return frame } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt index 4abe11ac9..0a3b3a331 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt @@ -410,7 +410,7 @@ class DeviceControls>( return result } - fun customSortingButtons(settings: IItemStackSortingSettings, input: MatteryMenu.PlayerInput) { + fun customSortingButtons(input: MatteryMenu.SortInput) { addButton(object : LargeRectangleButtonPanel(screen, this@DeviceControls) { var buttons: List>? = null @@ -427,15 +427,15 @@ class DeviceControls>( } override var isDisabled: Boolean - get() = !input.test(minecraft.player) + get() = !input.input.test(minecraft.player) set(value) {} override fun onClick(mouseButton: Int) { if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) { - input.accept(null) + input.clientInput() } else { if (buttons == null) { - buttons = sortingButtons(settings::isAscending.asGetterSetter(), settings::sorting.asGetterSetter(), ItemStackSorter.DEFAULT) { + buttons = sortingButtons(input.settings::isAscending.asGetterSetter(), input.settings::sorting.asGetterSetter(), ItemStackSorter.DEFAULT) { for (v in ItemStackSorter.entries) { add(v, v.icon, v.title) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHelpers.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHelpers.kt index 627492193..ad2c35539 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHelpers.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHelpers.kt @@ -3,8 +3,10 @@ package ru.dbotthepony.mc.otm.container import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import it.unimi.dsi.fastutil.ints.IntArrayList import it.unimi.dsi.fastutil.ints.IntArraySet +import it.unimi.dsi.fastutil.ints.IntCollection import it.unimi.dsi.fastutil.ints.IntIterable import it.unimi.dsi.fastutil.ints.IntIterator +import it.unimi.dsi.fastutil.ints.IntList import it.unimi.dsi.fastutil.ints.IntSet import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap @@ -16,11 +18,10 @@ import net.minecraft.world.item.enchantment.EnchantmentHelper.hasVanishingCurse import net.minecraftforge.fluids.capability.IFluidHandler import ru.dbotthepony.mc.otm.container.util.IContainerSlot import ru.dbotthepony.mc.otm.container.util.ItemStackHashStrategy +import ru.dbotthepony.mc.otm.container.util.containerSlot import ru.dbotthepony.mc.otm.container.util.slotIterator import ru.dbotthepony.mc.otm.core.addAll import ru.dbotthepony.mc.otm.core.collect.filter -import ru.dbotthepony.mc.otm.core.collect.map -import ru.dbotthepony.mc.otm.core.collect.max import ru.dbotthepony.mc.otm.core.collect.toList import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.map @@ -318,3 +319,62 @@ fun Container.sort(comparator: Comparator = ItemStackSorter.DEFAULT) slots.forEach { it.remove() } sortedItems.forEach { addItem(it, false) } } + +fun Container.sortWithIndices(sortedSlots: IntCollection) { + if (sortedSlots.isEmpty() || isEmpty) + return + + val seen = IntAVLTreeSet() + val valid = ArrayList() + + val iterator = sortedSlots.intIterator() + + while (iterator.hasNext()) { + val value = iterator.nextInt() + + if (value in 0 until containerSize && seen.add(value)) { + val slot = containerSlot(value) + + if (slot.isNotEmpty && !slot.isForbiddenForAutomation && slot.item.count <= slot.getMaxStackSize()) { + valid.add(slot) + } + } + } + + if (valid.isEmpty()) + return + + val items = valid.map { it.item.copy() } + valid.forEach { it.remove() } + items.forEach { addItem(it, false) } +} + +fun Container.computeSortedIndices(comparator: Comparator = ItemStackSorter.DEFAULT): IntList { + if (isEmpty) + return IntList.of() + + val slots = slotIterator().filter { !it.isForbiddenForAutomation && it.getMaxStackSize() >= it.item.count }.toList() + + if (slots.isEmpty()) + return IntList.of() + + val items = Object2ObjectOpenCustomHashMap>(ItemStackHashStrategy) + + slots.forEach { + val get = items[it.item] + + if (get == null) { + items[it.item] = it.item.copy() to IntArrayList().also { s -> s.add(it.slot) } + } else { + get.first.count += it.item.count + get.second.add(it.slot) + } + } + + val sortedItems = ObjectArrayList(items.values) + sortedItems.sortWith(comparator.map { it.first }) + + val result = IntArrayList() + sortedItems.forEach { result.addAll(it.second) } + return result +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/StreamCodecs.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/StreamCodecs.kt index 55148826b..b7b55910e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/StreamCodecs.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/StreamCodecs.kt @@ -105,6 +105,35 @@ class StreamCodec( } } +class CollectionStreamCodec>(val elementCodec: IStreamCodec, val collectionFactory: (Int) -> C) : IStreamCodec { + override fun read(stream: DataInputStream, sizeLimit: NbtAccounter): C { + val size = stream.readVarIntLE(sizeLimit) + + if (size <= 0) { + return collectionFactory.invoke(0) + } + + val collection = collectionFactory.invoke(size) + + for (i in 0 until size) { + collection.add(elementCodec.read(stream, sizeLimit)) + } + + return collection + } + + override fun write(stream: DataOutputStream, value: C) { + stream.writeVarIntLE(value.size) + value.forEach { elementCodec.write(stream, it) } + } + + override fun copy(value: C): C { + val new = collectionFactory.invoke(value.size) + value.forEach { new.add(elementCodec.copy(it)) } + return new + } +} + val NullValueCodec = StreamCodec({ _, _ -> null }, { _, _ -> }) val BooleanValueCodec = StreamCodec(DataInputStream::readBoolean, 1L, DataOutputStream::writeBoolean) { a, b -> a == b } val ByteValueCodec = StreamCodec(DataInputStream::readByte, 1L, { s, v -> s.writeByte(v.toInt()) }) { a, b -> a == b } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt index c114b676b..11dda4464 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt @@ -2,6 +2,9 @@ package ru.dbotthepony.mc.otm.menu import com.google.common.collect.ImmutableList import com.mojang.datafixers.util.Pair +import it.unimi.dsi.fastutil.ints.IntArrayList +import it.unimi.dsi.fastutil.ints.IntCollection +import it.unimi.dsi.fastutil.ints.IntList import it.unimi.dsi.fastutil.io.FastByteArrayInputStream import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction @@ -36,7 +39,9 @@ import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot import ru.dbotthepony.mc.otm.container.IMatteryContainer import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.UpgradeContainer +import ru.dbotthepony.mc.otm.container.computeSortedIndices import ru.dbotthepony.mc.otm.container.sort +import ru.dbotthepony.mc.otm.container.sortWithIndices import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.collect.ConditionalEnumSet import ru.dbotthepony.mc.otm.core.collect.ConditionalSet @@ -45,6 +50,7 @@ import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.util.BigDecimalValueCodec import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec +import ru.dbotthepony.mc.otm.core.util.CollectionStreamCodec import ru.dbotthepony.mc.otm.core.util.IStreamCodec import ru.dbotthepony.mc.otm.core.util.ItemStackValueCodec import ru.dbotthepony.mc.otm.core.util.ItemValueCodec @@ -173,6 +179,18 @@ abstract class MatteryMenu( } } + inner class SortInput(val container: Container, settings: IItemStackSortingSettings?) { + val settings = IItemStackSortingSettings.inputs(this@MatteryMenu, settings) + + val input = PlayerInput(CollectionStreamCodec(VarIntValueCodec, ::IntArrayList) as IStreamCodec) { + container.sortWithIndices(it) + } + + fun clientInput() { + input.accept(container.computeSortedIndices(settings.actualComparator)) + } + } + fun oneWayInput(allowSpectators: Boolean = false, handler: () -> Unit): PlayerInput { return PlayerInput(NullValueCodec, allowSpectators) { handler.invoke() @@ -206,7 +224,7 @@ abstract class MatteryMenu( val exopackPowerLevel = ProfiledLevelGaugeWidget>(mSynchronizer) - var sortInventoryInput: PlayerInput? = null + var sortInventoryInput: SortInput? = null private set val playerSortSettings = IItemStackSortingSettings.inputs(this, player.matteryPlayer?.sortingSettings) @@ -353,9 +371,7 @@ abstract class MatteryMenu( exopackPowerLevel.with(mattery.exopackEnergy) } - sortInventoryInput = PlayerInput(NullValueCodec) { - mattery.inventoryAndExopackNoHotbar.sort(playerSortSettings.actualComparator) - } + sortInventoryInput = SortInput(mattery.inventoryAndExopackNoHotbar, playerSortSettings) } private var broadcastOnce = false diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/CargoCrateMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/CargoCrateMenu.kt index 2e51dcd82..eb438a19b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/CargoCrateMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/CargoCrateMenu.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.mc.otm.menu.decorative +import net.minecraft.world.Container import net.minecraft.world.SimpleContainer import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player @@ -19,12 +20,11 @@ class CargoCrateMenu( inventory: Inventory, tile: CargoCrateBlockEntity? = null ) : MatteryMenu(MMenus.CARGO_CRATE, containerId, inventory, tile) { - val storageSlots = makeSlots(tile?.container ?: SimpleContainer(CargoCrateBlockEntity.CAPACITY), ::UserFilteredSlot) + val actualContainer: Container = tile?.container ?: SimpleContainer(CargoCrateBlockEntity.CAPACITY) + val storageSlots = makeSlots(actualContainer, ::UserFilteredSlot) private val trackedPlayerOpen = !inventory.player.isSpectator - val sort = PlayerInput(NullValueCodec) { - tile?.container?.sort(playerSortSettings.actualComparator) - } + val sort = SortInput(actualContainer, playerSortSettings) init { if (trackedPlayerOpen) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/MinecartCargoCrateMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/MinecartCargoCrateMenu.kt index a8d86e87c..7ce6c9f23 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/MinecartCargoCrateMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/MinecartCargoCrateMenu.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.mc.otm.menu.decorative +import net.minecraft.world.Container import net.minecraft.world.SimpleContainer import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player @@ -17,13 +18,12 @@ class MinecartCargoCrateMenu( inventory: Inventory, val cart: MinecartCargoCrate? = null ) : MatteryMenu(MMenus.CARGO_CRATE, containerId, inventory) { - val storageSlots = makeSlots(cart ?: SimpleContainer(CargoCrateBlockEntity.CAPACITY), ::MatterySlot) + val actualContainer: Container = cart ?: SimpleContainer(CargoCrateBlockEntity.CAPACITY) + val storageSlots = makeSlots(actualContainer, ::MatterySlot) private val trackedPlayerOpen = !inventory.player.isSpectator - val sort = PlayerInput(NullValueCodec) { - cart?.sort(playerSortSettings.actualComparator) - } + val sort = SortInput(actualContainer, playerSortSettings) init { if (trackedPlayerOpen) {