mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2026-02-05 01:32:37 +01:00
Instead of storing the actual Entity, slots now contain an Item, which is a wrapper type that, aside from the Entity itself, provides its name, icon, and context menu entries. For the sake of generality, the value represented by an Item is called its element (so it doesn't have to an Entity). Each Item keeps a reference to its ItemPrototype, which, broadly speaking, encapsulates its essential characteristics. In Minecraft, ItemPrototype would be what you'd see in the creative mode's item selection tabs, and Item is what you'd store in inventories. In Ocelot Desktop, item selector widget shows a list of prototypes, and slots store items. Previously, slots could store any item whatsoever, but since SlotWidget subclasses only showed accepted entries in the selector and there was no other way of inserting an item into the slot, it worked, uhh, fine. However, this no longer proves adequate if I want to allow moving items between slots. Therefore, the slots are required to accept an item if and only if their `accepts` method returns `true` for the item's prototype. We check prototypes instead of the actual items so that we could populate the item selector list with accepted prototypes without having to build concrete items each time. For convenience, `accepts(Item)` is also defined in `SlotWidget` that delegates to `accepts(ItemPrototype)`; the method is marked `final` to prevent overriding. Another major change introduced by this commit concerns the responsibility for slot persistence. Again, previously, the persistence worked as follows. (InventorySlotWidget is an Ocelot Desktop widget; Inventory is ocelot-brain's trait; and Inventory#Slot is a reference to one of the Inventory's slots.) 1. An InventorySlotWidget was associated with its owning Inventory#Slot. Setting an item in the InventorySlotWidget would set it in the owning Inventory#Slot, and clearing the InventorySlotWidget would clear the Inventory#Slot. 2. The InventorySlotWidget didn't care about persistence whatsoever; it was the responsibility of Inventory, which would save its contents to NBT. 3. When a workspace was loaded, the entity in the Inventory#Slot was restored from NBT, and the InventorySlotWidget would initialize its item to the contents of the Inventory#Slot. 4. The item's icon and UI behavior was determined by the concrete InventorySlotWidget class: for instance, CPUSlot would check whether the item was a CPU or an APU, retrieve its tier, and update the icon accordingly. After the logic described in the 4th point had been moved to Item and ItemPrototype, a problem has appeared: an Item instance had be to be restored from the item's element. This commit now moves the burden of saving/loading SlotWidget contents to Ocelot Desktop — more specifically, the SlotGroup and InventorySlotGroup. (Accordingly, an InventorySlotWidget's associated Inventory#Slot is henceforth referred to as its slave slot.) The SlotGroup is a group of slots treated as a single logical inventory. It provides the `save` method that serializes the Items (instead of their element) to NBT and loads them afterwards. The InventorySlotGroup extends the SlotGroup by providing synchronization of InventorySlotWidgets with the slave Inventory#Slots. In addition, the Item recovery logic described in the previous paragraph is implemented there — mostly for migration purposes; however, if a de-sync of the kind "the slave slot has an Entity, but the owner thinks it doesn't" occurs (such as when an item is inserted by ocelot-brain for some reason), we also need to recover the Item to give the InventorySlotWidget. Recoverers and item prototypes are registered in the ItemRegistry. Its `recover(E)` method looks up the ItemRecoverer registered for the runtime classes of the provided element and uses it to build an `Item[E]`. The item prototypes, however, are stored in a tree consisting of group nodes whose children are other group nodes and leaves each containing an ItemPrototype. This is used by the item selector to group several entries together. Finally, the SlotWidget's `transient` boolean property indicates whether the slot is in the middle of updating its contents. This is used to ignore the events fired by an Inventory as a result of inserting an item into an InventorySlotWidget. Once the design solidifies, I'll add documentation to the key classes affected by the refactoring to explain the new behavior.