Dispose of items when they become inaccessible

Closes #63.
This commit is contained in:
Fingercomp 2025-08-16 20:18:00 +03:00
parent 5bcc005143
commit f6c41f0184
No known key found for this signature in database
GPG Key ID: BBC71CEE45D86E37
5 changed files with 59 additions and 26 deletions

View File

@ -7,12 +7,16 @@ import totoro.ocelot.brain.event.NodeEvent
import scala.collection.mutable
/** Provides an inventory — a collection of [[Item]]s indexed by slots. */
/**
* Provides an inventory a collection of [[Item]]s indexed by slots.
*/
trait Inventory extends EventAware with Disposable {
// parallels totoro.ocelot.brain.entity.traits.Inventory
// this is intentional
/** The type of items stored in this inventory. */
/**
* The type of items stored in this inventory.
*/
type I <: Item
private type WeakHashSet[A] = mutable.WeakHashMap[A, Unit]
@ -23,21 +27,18 @@ trait Inventory extends EventAware with Disposable {
override def dispose(): Unit = {
super.dispose()
for (slot <- inventoryIterator) {
val item = slot.get
slot.remove()
item.foreach(_.dispose())
}
inventoryIterator.foreach(_.removeAndDispose())
}
/** Called after a new item is added to the inventory.
/**
* Called after a new item is added to the inventory.
*
* @param slot the slot the item was added to
*/
def onItemAdded(slot: Slot): Unit
/** Called after an item is removed from the inventory.
/**
* Called after an item is removed from the inventory.
*
* When the item is replaced by another one, the event are sequenced in the following order:
*
@ -52,7 +53,9 @@ trait Inventory extends EventAware with Disposable {
*/
def onItemRemoved(slot: Slot, removedItem: I, replacedBy: Option[I]): Unit
/** An iterator over all slots occupied in this inventory. */
/**
* An iterator over all slots occupied in this inventory.
*/
def inventoryIterator: Iterator[Slot] = slotItems.keysIterator.map(Slot(_))
def clearInventory(): Unit = {
@ -89,7 +92,9 @@ trait Inventory extends EventAware with Disposable {
}
}
/** A proxy to access a slot of the inventory. */
/**
* A proxy to access a slot of the inventory.
*/
final class Slot private[Inventory] (val index: Int) {
require(index >= 0)
@ -97,27 +102,46 @@ trait Inventory extends EventAware with Disposable {
def nonEmpty: Boolean = !isEmpty
/** Inserts the `item` into this slot (replacing the previous item if any). */
/**
* Inserts the `item` into this slot (replacing the previous item if any).
*/
def put(item: inventory.I): Unit = {
setSlot(index, Some(item))
}
/** Allows inserting/removing the item in this slot. */
/**
* Allows inserting/removing the item in this slot.
*/
def set(item: Option[inventory.I]): Unit = {
setSlot(index, item)
}
/** Removes the item contained in this slot if there is one. */
/**
* Removes the item contained in this slot if there is one.
*/
def remove(): Unit = {
setSlot(index, None)
}
/** The [[Item]] contained in this slot. */
/**
* Removes the item contained in this slot if there is one, calling the item's [[dispose]] method.
*/
def removeAndDispose(): Unit = {
for (item <- get) {
remove()
item.dispose()
}
}
/**
* The [[Item]] contained in this slot.
*/
def get: Option[inventory.I] = slotItems.get(index)
val inventory: Inventory.this.type = Inventory.this
/** Registers an observer to receive item added/removed events.
/**
* Registers an observer to receive item added/removed events.
*
* @note The inventory keeps a '''weak''' reference to the `observer`.
*/
@ -159,7 +183,9 @@ trait Inventory extends EventAware with Disposable {
}
final object Slot {
/** Creates a proxy to an inventory slot. */
/**
* Creates a proxy to an inventory slot.
*/
def apply(index: Int) = new Slot(index)
}
@ -216,13 +242,15 @@ trait Inventory extends EventAware with Disposable {
object Inventory {
trait SlotObserver {
/** Called after an item was inserted into this slot.
/**
* Called after an item was inserted into this slot.
*
* @note [[Inventory.onItemAdded]] is called before this method.
*/
def onItemAdded(): Unit
/** Called after an item was removed from this slot.
/**
* Called after an item was removed from this slot.
*
* In particular, the slot no longer contains the removed item.
*
@ -230,7 +258,9 @@ object Inventory {
*/
def onItemRemoved(removedItem: Item, replacedBy: Option[Item]): Unit
/** Called when an item contained in this slot sends a notification via [[Item.notifySlot]]. */
/**
* Called when an item contained in this slot sends a notification via [[Item.notifySlot]].
*/
def onItemNotification(notification: Item.Notification): Unit
}
}

View File

@ -104,7 +104,7 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
}
protected def onSlotLoadFailed(slotIndex: Int): Unit = {
Slot(slotIndex).remove()
Slot(slotIndex).removeAndDispose()
}
protected def saveEntityItem(slotNbt: NBTTagCompound, item: EntityItem): Unit = {

View File

@ -178,7 +178,7 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
)
logger.error("Breaking the loop forcefully by removing the items.")
Slot(slotIndex).remove()
Slot(slotIndex).removeAndDispose()
brainInventory.inventory(slotIndex).remove()
} else {
direction match {

View File

@ -110,7 +110,7 @@ class SlotWidget[I <: Item](private val slot: Inventory#Slot)(implicit slotItemT
item.fillRmbMenu(menu)
menu.addEntry(
ContextMenuEntry("Remove", IconSource.Delete, SoundSource.InterfaceClickLow) {
slot.remove()
slot.removeAndDispose()
}
)
}

View File

@ -117,8 +117,11 @@ trait ComputerAware
.minByOption(_.slotTier)
}
for (item <- items; newSlot <- findBestSlot(item, slots)) {
newSlot.item = item
for (item <- items) {
findBestSlot(item, slots) match{
case Some(newSlot) => newSlot.item = item
case None => item.dispose()
}
}
}