diff --git a/src/main/java/de/bixilon/minosoft/data/inventory/click/FastMoveContainerAction.kt b/src/main/java/de/bixilon/minosoft/data/inventory/click/FastMoveContainerAction.kt index 9c4931016..f0b8bf6d1 100644 --- a/src/main/java/de/bixilon/minosoft/data/inventory/click/FastMoveContainerAction.kt +++ b/src/main/java/de/bixilon/minosoft/data/inventory/click/FastMoveContainerAction.kt @@ -14,6 +14,8 @@ package de.bixilon.minosoft.data.inventory.click @Deprecated("ToDo") -class FastMoveContainerAction : ContainerAction { +class FastMoveContainerAction( + val slot: Int, +) : ContainerAction { private val mode: Int get() = 1 } diff --git a/src/main/java/de/bixilon/minosoft/data/inventory/click/SimpleContainerAction.kt b/src/main/java/de/bixilon/minosoft/data/inventory/click/SimpleContainerAction.kt index f30f9b7b6..8451013fb 100644 --- a/src/main/java/de/bixilon/minosoft/data/inventory/click/SimpleContainerAction.kt +++ b/src/main/java/de/bixilon/minosoft/data/inventory/click/SimpleContainerAction.kt @@ -13,19 +13,77 @@ package de.bixilon.minosoft.data.inventory.click -@Deprecated("ToDo") +import de.bixilon.minosoft.data.inventory.stack.ItemStack +import de.bixilon.minosoft.data.registries.other.containers.Container +import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection +import de.bixilon.minosoft.protocol.packets.c2s.play.container.ContainerClickC2SP + class SimpleContainerAction( - val slot: Int, - val action: SimpleContainerActions, + val slot: Int?, + val count: ContainerCounts, ) : ContainerAction { private val mode: Int get() = 0 private val button: Int - get() = action.ordinal + get() = count.ordinal + private fun pickItem(connection: PlayConnection, containerId: Int, container: Container) { + val item = container[slot ?: return] ?: return + // ToDo: Check course of binding + val previous = item.copy() + val floatingItem: ItemStack + if (count == ContainerCounts.ALL) { + container.remove(slot) + floatingItem = item + } else { + // half + val stayCount = maxOf(item.item.count / 2, 1) + item.item.count = stayCount + floatingItem = previous.copy(count = previous.item.count - stayCount) + } + container.floatingItem = floatingItem + connection.sendPacket(ContainerClickC2SP(containerId, container.serverRevision, slot, 0, count.ordinal, container.createAction(this), mapOf(slot to item), previous)) + } - enum class SimpleContainerActions { - LEFT_MOUSE_CLICK, - RIGHT_MOUSE_CLICK, + private fun putItem(connection: PlayConnection, containerId: Int, container: Container, floatingItem: ItemStack) { + floatingItem.lock() + val target = container.slots[slot] + try { + if (count == ContainerCounts.ALL) { + floatingItem.item._count = 0 + } else { + floatingItem.item._count-- // don't use decrease, item + container is already locked + } + if (slot == null || target == null) { + return connection.sendPacket(ContainerClickC2SP(containerId, container.serverRevision, null, 0, count.ordinal, container.createAction(this), mapOf(), null)) + } + if (target.typeEquals(floatingItem)) { + // merge + val subtract = minOf(target.item.item.maxStackSize - target.item._count, floatingItem.item._count) + target.item._count += subtract + floatingItem.item._count -= subtract + if (!floatingItem._valid) { + container.floatingItem = null + } + return + } + // swap + container.floatingItem = target + container.slots[slot] = floatingItem + } finally { + floatingItem.commit() + target?.lock() // lock to prevent exception + target?.commit() + } + } + + override fun invoke(connection: PlayConnection, containerId: Int, container: Container) { + val floatingItem = container.floatingItem ?: return pickItem(connection, containerId, container) + return putItem(connection, containerId, container, floatingItem) + } + + enum class ContainerCounts { + ALL, + PART, ; } } diff --git a/src/main/java/de/bixilon/minosoft/data/inventory/stack/ItemStack.kt b/src/main/java/de/bixilon/minosoft/data/inventory/stack/ItemStack.kt index 0bdd23e4d..c3881f798 100644 --- a/src/main/java/de/bixilon/minosoft/data/inventory/stack/ItemStack.kt +++ b/src/main/java/de/bixilon/minosoft/data/inventory/stack/ItemStack.kt @@ -186,6 +186,10 @@ class ItemStack { return Objects.hash(item, _display, _durability, _enchanting, _hide, _nbt) } + private fun _equals(other: ItemStack): Boolean { + return _display == other._display && _durability == other._durability && _enchanting == other._enchanting && _hide == other._hide && _nbt == other._nbt + } + override fun equals(other: Any?): Boolean { if (other !is ItemStack) { return false @@ -193,7 +197,14 @@ class ItemStack { if (other.hashCode() != this.hashCode()) { return false } - return item == other.item && _display == other._display && _durability == other._durability && _enchanting == other._enchanting && _hide == other._hide && _nbt == other._nbt + return item == other.item && _equals(other) + } + + fun typeEquals(other: ItemStack?): Boolean { + if (other == null) { + return false + } + return item.item == other.item.item && _equals(other) } override fun toString(): String { diff --git a/src/main/java/de/bixilon/minosoft/data/registries/other/containers/Container.kt b/src/main/java/de/bixilon/minosoft/data/registries/other/containers/Container.kt index 01f2fae3a..f9db75244 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/other/containers/Container.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/other/containers/Container.kt @@ -17,13 +17,15 @@ import de.bixilon.kutil.collections.CollectionUtil.synchronizedBiMapOf import de.bixilon.kutil.collections.CollectionUtil.toSynchronizedMap import de.bixilon.kutil.collections.map.bi.SynchronizedBiMap import de.bixilon.kutil.concurrent.lock.SimpleLock +import de.bixilon.kutil.watcher.DataWatcher.Companion.observe import de.bixilon.kutil.watcher.DataWatcher.Companion.watched -import de.bixilon.kutil.watcher.map.MapDataWatcher +import de.bixilon.kutil.watcher.map.MapDataWatcher.Companion.watchedMap import de.bixilon.minosoft.data.inventory.click.ContainerAction import de.bixilon.minosoft.data.inventory.stack.ItemStack import de.bixilon.minosoft.data.inventory.stack.property.HolderProperty import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection +import de.bixilon.minosoft.protocol.packets.c2s.play.container.CloseContainerC2SP open class Container( protected val connection: PlayConnection, @@ -32,7 +34,7 @@ open class Container( val hasTitle: Boolean = false, ) : Iterable> { @Deprecated("Should not be accessed dirctly") - val slots: MutableMap by MapDataWatcher.watchedMap(mutableMapOf()) + val slots: MutableMap by watchedMap(mutableMapOf()) val lock = SimpleLock() var revision by watched(0L) var serverRevision = 0 @@ -40,6 +42,10 @@ open class Container( var actions: SynchronizedBiMap = synchronizedBiMapOf() var floatingItem: ItemStack? by watched(null) + init { + this::floatingItem.observe(this) { it?.holder?.container = this } + } + fun _validate() { var itemsRemoved = 0 for ((slot, itemStack) in slots.toSynchronizedMap()) { @@ -175,6 +181,13 @@ open class Container( actions.remove(actionId)?.revert(connection, connection.player.containers.getKey(this) ?: return, this) } + fun onClose() { + floatingItem = null // ToDo: Does not seem correct + + // minecraft behavior, when opening the inventory an open packet is never sent, but a close is + connection.sendPacket(CloseContainerC2SP(connection.player.containers.getKey(this) ?: return)) + } + override fun iterator(): Iterator> { return slots.toSynchronizedMap().iterator() } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/items/ContainerItemsElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/items/ContainerItemsElement.kt index 6dc311871..58809fa9f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/items/ContainerItemsElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/items/ContainerItemsElement.kt @@ -14,6 +14,10 @@ package de.bixilon.minosoft.gui.rendering.gui.elements.items import de.bixilon.kutil.watcher.map.MapDataWatcher.Companion.observeMap +import de.bixilon.minosoft.data.abilities.Gamemodes +import de.bixilon.minosoft.data.inventory.click.CloneContainerAction +import de.bixilon.minosoft.data.inventory.click.FastMoveContainerAction +import de.bixilon.minosoft.data.inventory.click.SimpleContainerAction import de.bixilon.minosoft.data.registries.other.containers.Container import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer import de.bixilon.minosoft.gui.rendering.gui.atlas.Vec2iBinding @@ -21,6 +25,7 @@ import de.bixilon.minosoft.gui.rendering.gui.elements.Element import de.bixilon.minosoft.gui.rendering.gui.gui.AbstractLayout import de.bixilon.minosoft.gui.rendering.gui.gui.dragged.Dragged import de.bixilon.minosoft.gui.rendering.gui.gui.dragged.elements.item.FloatingItem +import de.bixilon.minosoft.gui.rendering.gui.input.ModifierKeys import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer @@ -28,6 +33,7 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.isGreater import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.isSmaller +import de.bixilon.minosoft.util.delegate.RenderingDelegate.observeRendering import glm_.vec2.Vec2i import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap @@ -73,6 +79,11 @@ class ContainerItemsElement( this._size = size container::slots.observeMap(this) { update = true; } + container::floatingItem.observeRendering(this) { + this.floatingItem?.close() + this.floatingItem = null + this.floatingItem = FloatingItem(guiRenderer, it ?: return@observeRendering).apply { show() } + } } override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) { @@ -119,32 +130,29 @@ class ContainerItemsElement( } override fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions): Boolean { + // this is not in items element, because you can also click into "nothing" val consumed = super.onMouseAction(position, button, action) - if (action != MouseActions.PRESS || (button != MouseButtons.LEFT && button != MouseButtons.RIGHT)) { + if (action != MouseActions.PRESS) { return consumed } + val shiftDown = guiRenderer.isKeyDown(ModifierKeys.SHIFT) val activeElement = activeElement - val stack = activeElement?.stack - val containerId = renderWindow.connection.player.containers.getKey(container) ?: return consumed - var floatingItem = this.floatingItem - if ((floatingItem != null && floatingItem.visible) || stack?._valid != true) { - return consumed + if (button == MouseButtons.MIDDLE) { + if (guiRenderer.connection.player.gamemode != Gamemodes.CREATIVE) { + return true + } + container.invokeAction(CloneContainerAction(activeElement?.slotId ?: return true)) + return true + } + if (button == MouseButtons.LEFT || button == MouseButtons.RIGHT) { + container.invokeAction(if (shiftDown) { + FastMoveContainerAction(activeElement?.slotId ?: return true) + } else { + SimpleContainerAction(activeElement?.slotId, if (button == MouseButtons.LEFT) SimpleContainerAction.ContainerCounts.ALL else SimpleContainerAction.ContainerCounts.PART) + }) + return true } - // val clickAction: ContainerClickActions - // val stackToFloat: ItemStack - // if (button == MouseButtons.LEFT) { - // stackToFloat = stack - // clickAction = ContainerClickActions.LEFT_MOUSE_CLICK - // } else { - // stackToFloat = stack.copy(count = maxOf(stack.item.count / 2, 1)) - // clickAction = ContainerClickActions.RIGHT_MOUSE_CLICK - // stack.item.count = stack.item.count - stackToFloat.item.count - // } - // renderWindow.connection.sendPacket(ContainerClickC2SP(containerId, container.serverRevision, activeElement.slotId, clickAction, container.createAction(), mapOf(activeElement.slotId to stack), stack)) - // floatingItem = FloatingItem(guiRenderer, activeElement.slotId, stackToFloat) - // this.floatingItem = floatingItem - // floatingItem.show() return true } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/dragged/elements/item/FloatingItem.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/dragged/elements/item/FloatingItem.kt index c67995b5d..a2dbfcdbf 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/dragged/elements/item/FloatingItem.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/dragged/elements/item/FloatingItem.kt @@ -23,7 +23,6 @@ import glm_.vec2.Vec2i class FloatingItem( guiRenderer: GUIRenderer, - val sourceId: Int, val stack: ItemStack, size: Vec2i = RawItemElement.DEFAULT_SIZE, ) : Dragged(guiRenderer) { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/ContainerScreen.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/ContainerScreen.kt index eba37bf91..7c130c35a 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/ContainerScreen.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/container/ContainerScreen.kt @@ -26,7 +26,6 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.isGreater import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.isSmaller -import de.bixilon.minosoft.protocol.packets.c2s.play.container.CloseContainerC2SP import glm_.vec2.Vec2i import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap @@ -70,7 +69,6 @@ abstract class ContainerScreen( override fun onClose() { super.onClose() - // minecraft behavior, when opening the inventory an open packet is never sent, but a close is - renderWindow.connection.sendPacket(CloseContainerC2SP(renderWindow.connection.player.containers.getKey(container) ?: return)) + container.onClose() } }