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) moveMousePosScaled(y = movePixels)
} as EditablePanel<ExopackInventoryScreen>) } as EditablePanel<ExopackInventoryScreen>)
if (menu.sortInventoryInput != null) { if (menu.sortInventoryInput != null) {
controls.addButton(LargeRectangleButtonPanel.input( controls.customSortingButtons(menu.sortInventoryInput!!)
this,
controls,
menu.sortInventoryInput!!,
icon = Widgets18.SORT_NOW
).also { it.tooltips.add(TranslatableComponent("otm.gui.sorting.sort_now")) })
} }
var x = -4f var x = -4f

View File

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

View File

@ -28,7 +28,7 @@ class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Compon
val controls = DeviceControls(this, frame) val controls = DeviceControls(this, frame)
controls.customSortingButtons(menu.playerSortSettings, menu.sort) controls.customSortingButtons(menu.sort)
return frame return frame
} }

View File

@ -23,7 +23,7 @@ class MinecartCargoCrateScreen(menu: MinecartCargoCrateMenu, inventory: Inventor
val controls = DeviceControls(this, frame) val controls = DeviceControls(this, frame)
controls.customSortingButtons(menu.playerSortSettings, menu.sort) controls.customSortingButtons(menu.sort)
return frame return frame
} }

View File

@ -410,7 +410,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
return result return result
} }
fun customSortingButtons(settings: IItemStackSortingSettings, input: MatteryMenu.PlayerInput<Nothing?>) { fun customSortingButtons(input: MatteryMenu.SortInput) {
addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls) { addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls) {
var buttons: List<EditablePanel<*>>? = null var buttons: List<EditablePanel<*>>? = null
@ -427,15 +427,15 @@ class DeviceControls<out S : MatteryScreen<*>>(
} }
override var isDisabled: Boolean override var isDisabled: Boolean
get() = !input.test(minecraft.player) get() = !input.input.test(minecraft.player)
set(value) {} set(value) {}
override fun onClick(mouseButton: Int) { override fun onClick(mouseButton: Int) {
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) { if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
input.accept(null) input.clientInput()
} else { } else {
if (buttons == null) { 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) { for (v in ItemStackSorter.entries) {
add(v, v.icon, v.title) 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.IntAVLTreeSet
import it.unimi.dsi.fastutil.ints.IntArrayList import it.unimi.dsi.fastutil.ints.IntArrayList
import it.unimi.dsi.fastutil.ints.IntArraySet 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.IntIterable
import it.unimi.dsi.fastutil.ints.IntIterator 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.ints.IntSet
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap 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 net.minecraftforge.fluids.capability.IFluidHandler
import ru.dbotthepony.mc.otm.container.util.IContainerSlot import ru.dbotthepony.mc.otm.container.util.IContainerSlot
import ru.dbotthepony.mc.otm.container.util.ItemStackHashStrategy 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.container.util.slotIterator
import ru.dbotthepony.mc.otm.core.addAll import ru.dbotthepony.mc.otm.core.addAll
import ru.dbotthepony.mc.otm.core.collect.filter 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.collect.toList
import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.map import ru.dbotthepony.mc.otm.core.map
@ -318,3 +319,62 @@ fun Container.sort(comparator: Comparator<ItemStack> = ItemStackSorter.DEFAULT)
slots.forEach { it.remove() } slots.forEach { it.remove() }
sortedItems.forEach { addItem(it, false) } 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 NullValueCodec = StreamCodec({ _, _ -> null }, { _, _ -> })
val BooleanValueCodec = StreamCodec(DataInputStream::readBoolean, 1L, DataOutputStream::writeBoolean) { a, b -> a == b } 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 } 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.google.common.collect.ImmutableList
import com.mojang.datafixers.util.Pair 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.FastByteArrayInputStream
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction 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.IMatteryContainer
import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.container.UpgradeContainer 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.sort
import ru.dbotthepony.mc.otm.container.sortWithIndices
import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.collect.ConditionalEnumSet import ru.dbotthepony.mc.otm.core.collect.ConditionalEnumSet
import ru.dbotthepony.mc.otm.core.collect.ConditionalSet 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.BigDecimalValueCodec
import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec 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.IStreamCodec
import ru.dbotthepony.mc.otm.core.util.ItemStackValueCodec import ru.dbotthepony.mc.otm.core.util.ItemStackValueCodec
import ru.dbotthepony.mc.otm.core.util.ItemValueCodec 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?> { fun oneWayInput(allowSpectators: Boolean = false, handler: () -> Unit): PlayerInput<Nothing?> {
return PlayerInput(NullValueCodec, allowSpectators) { return PlayerInput(NullValueCodec, allowSpectators) {
handler.invoke() handler.invoke()
@ -206,7 +224,7 @@ abstract class MatteryMenu(
val exopackPowerLevel = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(mSynchronizer) val exopackPowerLevel = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(mSynchronizer)
var sortInventoryInput: PlayerInput<Nothing?>? = null var sortInventoryInput: SortInput? = null
private set private set
val playerSortSettings = IItemStackSortingSettings.inputs(this, player.matteryPlayer?.sortingSettings) val playerSortSettings = IItemStackSortingSettings.inputs(this, player.matteryPlayer?.sortingSettings)
@ -353,9 +371,7 @@ abstract class MatteryMenu(
exopackPowerLevel.with(mattery.exopackEnergy) exopackPowerLevel.with(mattery.exopackEnergy)
} }
sortInventoryInput = PlayerInput(NullValueCodec) { sortInventoryInput = SortInput(mattery.inventoryAndExopackNoHotbar, playerSortSettings)
mattery.inventoryAndExopackNoHotbar.sort(playerSortSettings.actualComparator)
}
} }
private var broadcastOnce = false private var broadcastOnce = false

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.menu.decorative package ru.dbotthepony.mc.otm.menu.decorative
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
@ -19,12 +20,11 @@ class CargoCrateMenu(
inventory: Inventory, inventory: Inventory,
tile: CargoCrateBlockEntity? = null tile: CargoCrateBlockEntity? = null
) : MatteryMenu(MMenus.CARGO_CRATE, containerId, inventory, tile) { ) : 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 private val trackedPlayerOpen = !inventory.player.isSpectator
val sort = PlayerInput(NullValueCodec) { val sort = SortInput(actualContainer, playerSortSettings)
tile?.container?.sort(playerSortSettings.actualComparator)
}
init { init {
if (trackedPlayerOpen) { if (trackedPlayerOpen) {

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.menu.decorative package ru.dbotthepony.mc.otm.menu.decorative
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
@ -17,13 +18,12 @@ class MinecartCargoCrateMenu(
inventory: Inventory, inventory: Inventory,
val cart: MinecartCargoCrate? = null val cart: MinecartCargoCrate? = null
) : MatteryMenu(MMenus.CARGO_CRATE, containerId, inventory) { ) : 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 private val trackedPlayerOpen = !inventory.player.isSpectator
val sort = PlayerInput(NullValueCodec) { val sort = SortInput(actualContainer, playerSortSettings)
cart?.sort(playerSortSettings.actualComparator)
}
init { init {
if (trackedPlayerOpen) { if (trackedPlayerOpen) {