Make sorting be performed on client, and sorted slot list be sent to server

This commit is contained in:
DBotThePony 2023-08-20 22:31:52 +07:00
parent 4c94e92b90
commit e94a267ac1
Signed by: DBot
GPG Key ID: DCC23B5715498507
10 changed files with 127 additions and 28 deletions

View File

@ -271,14 +271,8 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<Exopack
moveMousePosScaled(y = movePixels)
} as EditablePanel<ExopackInventoryScreen>)
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

View File

@ -308,7 +308,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
if (menu.sortInventoryInput != null) {
deviceControls.customSortingButtons(menu.playerSortSettings, menu.sortInventoryInput!!)
deviceControls.customSortingButtons(menu.sortInventoryInput!!)
}
if (menu.exopackChargeSlots.isNotEmpty()) {

View File

@ -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
}

View File

@ -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
}

View File

@ -410,7 +410,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
return result
}
fun customSortingButtons(settings: IItemStackSortingSettings, input: MatteryMenu.PlayerInput<Nothing?>) {
fun customSortingButtons(input: MatteryMenu.SortInput) {
addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls) {
var buttons: List<EditablePanel<*>>? = null
@ -427,15 +427,15 @@ class DeviceControls<out S : MatteryScreen<*>>(
}
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)
}

View File

@ -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<ItemStack> = 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<IContainerSlot>()
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<ItemStack> = 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<ItemStack, Pair<ItemStack, IntList>>(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
}

View File

@ -105,6 +105,35 @@ class StreamCodec<V>(
}
}
class CollectionStreamCodec<E, C : MutableCollection<E>>(val elementCodec: IStreamCodec<E>, val collectionFactory: (Int) -> C) : IStreamCodec<C> {
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 }

View File

@ -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<IntCollection>) {
container.sortWithIndices(it)
}
fun clientInput() {
input.accept(container.computeSortedIndices(settings.actualComparator))
}
}
fun oneWayInput(allowSpectators: Boolean = false, handler: () -> Unit): PlayerInput<Nothing?> {
return PlayerInput(NullValueCodec, allowSpectators) {
handler.invoke()
@ -206,7 +224,7 @@ abstract class MatteryMenu(
val exopackPowerLevel = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(mSynchronizer)
var sortInventoryInput: PlayerInput<Nothing?>? = 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

View File

@ -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) {

View File

@ -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) {