ContainerProxy, trick minecraft's click logic
This commit is contained in:
parent
b6bb0ed4b3
commit
9dfb534f9a
@ -0,0 +1,192 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import com.google.common.collect.ImmutableMap
|
||||||
|
import com.google.common.collect.ImmutableSet
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||||
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
||||||
|
import net.minecraft.world.Container
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import java.util.LinkedList
|
||||||
|
import java.util.function.Consumer
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import java.util.stream.Stream
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
open class ContainerProxy(containers: Stream<Pair<Container, Iterator<Int>>>) : Container {
|
||||||
|
data class ContainerSlot(val container: Container, val outerIndex: Int, val index: Int) : ReadWriteProperty<Any, ItemStack>, Supplier<ItemStack>, Consumer<ItemStack> {
|
||||||
|
var item: ItemStack
|
||||||
|
get() = container[index]
|
||||||
|
set(value) { container[index] = value }
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Any, property: KProperty<*>): ItemStack {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any, property: KProperty<*>, value: ItemStack) {
|
||||||
|
this.item = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(): ItemStack {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun accept(t: ItemStack) {
|
||||||
|
item = t
|
||||||
|
}
|
||||||
|
|
||||||
|
val isEmpty: Boolean get() = item.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
protected val slots: List<ContainerSlot>
|
||||||
|
protected val slotsMap: Map<Container, List<ContainerSlot>>
|
||||||
|
protected val containers: Set<Container>
|
||||||
|
protected val fullCoverage: List<Container>
|
||||||
|
protected val notFullCoverage: Map<Container, List<ContainerSlot>>
|
||||||
|
|
||||||
|
init {
|
||||||
|
val list = ImmutableList.Builder<ContainerSlot>()
|
||||||
|
var i = 0
|
||||||
|
val validationMap = Reference2ObjectOpenHashMap<Container, IntAVLTreeSet>()
|
||||||
|
val slotsMap = Reference2ObjectOpenHashMap<Container, ArrayList<ContainerSlot>>()
|
||||||
|
|
||||||
|
for ((container, slots) in containers) {
|
||||||
|
val validator = validationMap.computeIfAbsent(container, Object2ObjectFunction { IntAVLTreeSet() })
|
||||||
|
val slotList = slotsMap.computeIfAbsent(container, Object2ObjectFunction { ArrayList() })
|
||||||
|
|
||||||
|
for (slot in slots) {
|
||||||
|
if (validator.add(slot)) {
|
||||||
|
val slotObj = ContainerSlot(container, i++, slot)
|
||||||
|
list.add(slotObj)
|
||||||
|
slotList.add(slotObj)
|
||||||
|
} else {
|
||||||
|
throw IllegalArgumentException("Duplicate mapping for $container at $i for slot $slot")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slots = list.build()
|
||||||
|
this.containers = ImmutableSet.copyOf(validationMap.keys)
|
||||||
|
|
||||||
|
this.slotsMap = slotsMap.entries
|
||||||
|
.stream()
|
||||||
|
.map { it.key to ImmutableList.copyOf(it.value) }
|
||||||
|
.collect(ImmutableMap.toImmutableMap({ it.first }, { it.second }))
|
||||||
|
|
||||||
|
this.fullCoverage = this.slotsMap.entries
|
||||||
|
.stream()
|
||||||
|
.filter { it.value.size == it.key.containerSize }
|
||||||
|
.map { it.key }
|
||||||
|
.collect(ImmutableList.toImmutableList())
|
||||||
|
|
||||||
|
this.notFullCoverage = this.slotsMap.entries
|
||||||
|
.stream()
|
||||||
|
.filter { it.key !in this.fullCoverage }
|
||||||
|
.collect(ImmutableMap.toImmutableMap({ it.key }, { it.value }))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearContent() {
|
||||||
|
for (container in fullCoverage) {
|
||||||
|
container.clearContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (slots in notFullCoverage.values) {
|
||||||
|
for (slot in slots) {
|
||||||
|
slot.item = ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getContainerSize(): Int {
|
||||||
|
return slots.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean {
|
||||||
|
for (container in fullCoverage)
|
||||||
|
if (!container.isEmpty)
|
||||||
|
return false
|
||||||
|
|
||||||
|
for (slots in notFullCoverage.values)
|
||||||
|
for (slot in slots)
|
||||||
|
if (!slot.isEmpty)
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun slotAt(index: Int): ContainerSlot {
|
||||||
|
return slots[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItem(index: Int): ItemStack {
|
||||||
|
// do not violate contract of getItem not throwing exceptions when index is invalid
|
||||||
|
return slots.getOrNull(index)?.item ?: ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeItem(index: Int, count: Int): ItemStack {
|
||||||
|
val data = slots.getOrNull(index) ?: return ItemStack.EMPTY
|
||||||
|
return data.container.removeItem(data.index, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeItemNoUpdate(index: Int): ItemStack {
|
||||||
|
val data = slots[index]
|
||||||
|
return data.container.removeItemNoUpdate(data.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setItem(index: Int, value: ItemStack) {
|
||||||
|
slots[index].item = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setChanged() {
|
||||||
|
for (container in containers) {
|
||||||
|
container.setChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stillValid(player: Player): Boolean {
|
||||||
|
for (container in containers)
|
||||||
|
if (!container.stillValid(player))
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
class Builder {
|
||||||
|
private var built = false
|
||||||
|
private val values = LinkedList<Pair<Container, Iterator<Int>>>()
|
||||||
|
|
||||||
|
fun add(container: Container): Builder {
|
||||||
|
check(!built) { "Already built!" }
|
||||||
|
values.add(container to (0 until container.containerSize).iterator())
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ofRange(container: Container, from: Int = 0, to: Int = container.containerSize - 1): Builder {
|
||||||
|
check(!built) { "Already built!" }
|
||||||
|
values.add(container to (from .. to).iterator())
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun of(container: Container, slots: Iterator<Int>): Builder {
|
||||||
|
check(!built) { "Already built!" }
|
||||||
|
values.add(container to slots)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun of(container: Container, slots: Iterable<Int>): Builder {
|
||||||
|
check(!built) { "Already built!" }
|
||||||
|
values.add(container to slots.iterator())
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): ContainerProxy {
|
||||||
|
check(!built) { "Already built!" }
|
||||||
|
val value = ContainerProxy(values.stream())
|
||||||
|
values.clear()
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
|||||||
import ru.dbotthepony.mc.otm.client.screen.panels.SlotPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.SlotPanel
|
||||||
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots
|
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots
|
||||||
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
|
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
|
||||||
|
import ru.dbotthepony.mc.otm.container.ContainerProxy
|
||||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||||
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
|
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.AbstractWidget
|
import ru.dbotthepony.mc.otm.menu.widget.AbstractWidget
|
||||||
@ -158,39 +159,41 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
|||||||
|
|
||||||
protected fun addInventorySlots(autoFrame: Boolean = !ply.isSpectator) {
|
protected fun addInventorySlots(autoFrame: Boolean = !ply.isSpectator) {
|
||||||
check(_playerInventorySlots.isEmpty()) { "Already created inventory slots" }
|
check(_playerInventorySlots.isEmpty()) { "Already created inventory slots" }
|
||||||
autoCreateInventoryFrame = autoFrame
|
|
||||||
|
|
||||||
for (i in 9 .. 35) {
|
|
||||||
val slot = InventorySlot(inventory, i)
|
|
||||||
|
|
||||||
_playerInventorySlots.add(slot)
|
|
||||||
_playerMainSlots.add(slot)
|
|
||||||
_playerCombinedInventorySlots.add(slot)
|
|
||||||
addSlot(slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
val mattery = ply.matteryPlayer
|
val mattery = ply.matteryPlayer
|
||||||
|
|
||||||
|
// trick minecraft's code into thinking that slots come from contiguous container
|
||||||
|
val proxyBuilder = ContainerProxy.Builder()
|
||||||
|
|
||||||
|
proxyBuilder.of(inventory, 0 .. 35)
|
||||||
|
|
||||||
if (mattery != null && mattery.hasExoSuit) {
|
if (mattery != null && mattery.hasExoSuit) {
|
||||||
for (i in 0 until mattery.exoSuitContainer.containerSize) {
|
proxyBuilder.add(mattery.exoSuitContainer)
|
||||||
val slot = InventorySlot(mattery.exoSuitContainer, i)
|
}
|
||||||
|
|
||||||
|
val proxy = proxyBuilder.build()
|
||||||
|
|
||||||
|
autoCreateInventoryFrame = autoFrame
|
||||||
|
|
||||||
|
for (slotId in 0 until proxy.containerSize) {
|
||||||
|
val slot = InventorySlot(proxy, slotId)
|
||||||
|
|
||||||
|
if (slotId in 0 .. 8) {
|
||||||
|
_playerHotbarSlots.add(slot)
|
||||||
|
addSlot(slot)
|
||||||
|
continue
|
||||||
|
} else if (slotId in 9 .. 35) {
|
||||||
|
_playerMainSlots.add(slot)
|
||||||
|
} else {
|
||||||
|
_playerExoSuitSlots.add(slot)
|
||||||
|
}
|
||||||
|
|
||||||
_playerInventorySlots.add(slot)
|
_playerInventorySlots.add(slot)
|
||||||
_playerExoSuitSlots.add(slot)
|
|
||||||
_playerCombinedInventorySlots.add(slot)
|
_playerCombinedInventorySlots.add(slot)
|
||||||
addSlot(slot)
|
addSlot(slot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0..8) {
|
|
||||||
val slot = InventorySlot(inventory, i)
|
|
||||||
|
|
||||||
addSlot(slot)
|
|
||||||
_playerInventorySlots.add(slot)
|
|
||||||
_playerHotbarSlots.add(slot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun broadcastChanges() {
|
override fun broadcastChanges() {
|
||||||
for (widget in _matteryWidgets) {
|
for (widget in _matteryWidgets) {
|
||||||
widget.updateServer()
|
widget.updateServer()
|
||||||
|
Loading…
Reference in New Issue
Block a user