Merge branch 'refactor/entity-id' into develop

This commit is contained in:
Fingercomp 2025-02-01 16:02:44 +03:00
commit 6f9558bed1
No known key found for this signature in database
GPG Key ID: BBC71CEE45D86E37
30 changed files with 159 additions and 143 deletions

@ -1 +1 @@
Subproject commit e1968df8a58fab83572e657d981b7263c7edd72a Subproject commit 648e47c42ca2ab4eb676ccc3dcddf129eac771af

View File

@ -2,7 +2,7 @@ package ocelot.desktop.inventory
import ocelot.desktop.OcelotDesktop import ocelot.desktop.OcelotDesktop
import ocelot.desktop.inventory.PersistedInventory._ import ocelot.desktop.inventory.PersistedInventory._
import ocelot.desktop.inventory.traits.{ComponentItem, PersistableItem} import ocelot.desktop.inventory.traits.{EntityItem, PersistableItem}
import ocelot.desktop.util.ReflectionUtils.findUnaryConstructor import ocelot.desktop.util.ReflectionUtils.findUnaryConstructor
import ocelot.desktop.util.{Logging, Persistable} import ocelot.desktop.util.{Logging, Persistable}
import totoro.ocelot.brain.nbt.ExtendedNBT.{extendNBTTagCompound, extendNBTTagList} import totoro.ocelot.brain.nbt.ExtendedNBT.{extendNBTTagCompound, extendNBTTagList}
@ -13,9 +13,9 @@ import scala.collection.mutable
/** Provides persistence for an [[Inventory]]. /** Provides persistence for an [[Inventory]].
* *
* [[ComponentItem]]s are treated specially: their [[ComponentItem.component component]] is persisted * [[EntityItem]]s are treated specially: their [[EntityItem.entity entities]] are persisted
* along with the item when saving. When loading the item, it first loads the component and uses it to instantiate * along with the item when saving. When loading the item, it first loads the entity and uses it to instantiate
* the item (as required by [[ComponentItem]]). * the item (as required by [[EntityItem]]).
*/ */
trait PersistedInventory extends Inventory with Logging with Persistable { trait PersistedInventory extends Inventory with Logging with Persistable {
override type I <: Item with PersistableItem override type I <: Item with PersistableItem
@ -33,8 +33,8 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
val itemClass = Class.forName(slotNbt.getString(SlotItemClassTag)) val itemClass = Class.forName(slotNbt.getString(SlotItemClassTag))
try { try {
val item = if (classOf[ComponentItem].isAssignableFrom(itemClass)) val item = if (classOf[EntityItem].isAssignableFrom(itemClass))
loadComponentItem(itemClass, slotNbt) loadEntityItem(itemClass, slotNbt)
else else
loadPlainItem(itemClass) loadPlainItem(itemClass)
@ -43,7 +43,7 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
} catch { } catch {
case ItemLoadException(message) => case ItemLoadException(message) =>
logger.error( logger.error(
s"Could not restore an item in the slot $slotIndex of " + s"Could not load an item in the slot $slotIndex of " +
s"the inventory $this (class ${this.getClass.getName}): $message" s"the inventory $this (class ${this.getClass.getName}): $message"
) )
onSlotLoadFailed(slotIndex) onSlotLoadFailed(slotIndex)
@ -72,7 +72,7 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
slotNbt.setString(SlotItemClassTag, item.getClass.getName) slotNbt.setString(SlotItemClassTag, item.getClass.getName)
item match { item match {
case item: ComponentItem => saveComponentItem(slotNbt, item) case item: EntityItem => saveEntityItem(slotNbt, item)
case _ => case _ =>
} }
@ -82,7 +82,7 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
} }
@throws[ItemLoadException]("if the item could not be loaded") @throws[ItemLoadException]("if the item could not be loaded")
protected def loadComponentItem(itemClass: Class[_], slotNbt: NBTTagCompound): I = { protected def loadEntityItem(itemClass: Class[_], slotNbt: NBTTagCompound): I = {
val entityNbt = slotNbt.getCompoundTag(SlotEntityTag) val entityNbt = slotNbt.getCompoundTag(SlotEntityTag)
val entity = NBTPersistence.load(entityNbt, OcelotDesktop.workspace) val entity = NBTPersistence.load(entityNbt, OcelotDesktop.workspace)
@ -107,8 +107,8 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
Slot(slotIndex).remove() Slot(slotIndex).remove()
} }
protected def saveComponentItem(slotNbt: NBTTagCompound, item: ComponentItem): Unit = { protected def saveEntityItem(slotNbt: NBTTagCompound, item: EntityItem): Unit = {
slotNbt.setTag(SlotEntityTag, NBTPersistence.save(item.component)) slotNbt.setTag(SlotEntityTag, NBTPersistence.save(item.entity))
} }
} }

View File

@ -5,22 +5,22 @@ import ocelot.desktop.inventory.PersistedInventory.ItemLoadException
import ocelot.desktop.inventory.SyncedInventory.SlotStatus.SlotStatus import ocelot.desktop.inventory.SyncedInventory.SlotStatus.SlotStatus
import ocelot.desktop.inventory.SyncedInventory.SyncDirection.SyncDirection import ocelot.desktop.inventory.SyncedInventory.SyncDirection.SyncDirection
import ocelot.desktop.inventory.SyncedInventory._ import ocelot.desktop.inventory.SyncedInventory._
import ocelot.desktop.inventory.traits.ComponentItem import ocelot.desktop.inventory.traits.EntityItem
import ocelot.desktop.ui.event.{BrainEvent, EventAware} import ocelot.desktop.ui.event.{BrainEvent, EventAware}
import ocelot.desktop.util.Logging import ocelot.desktop.util.Logging
import ocelot.desktop.util.ReflectionUtils.findUnaryConstructor import ocelot.desktop.util.ReflectionUtils.findUnaryConstructor
import totoro.ocelot.brain.entity.traits.{Entity, Environment, Inventory => BrainInventory} import totoro.ocelot.brain.entity.traits.{Entity, Environment, Inventory => BrainInventory}
import totoro.ocelot.brain.event.{InventoryEntityAddedEvent, InventoryEntityRemovedEvent} import totoro.ocelot.brain.event.{InventoryEntityAddedEvent, InventoryEntityRemovedEvent}
import totoro.ocelot.brain.nbt.NBTTagCompound import totoro.ocelot.brain.nbt.NBTTagCompound
import totoro.ocelot.brain.network.Network
import java.util.UUID
import scala.annotation.tailrec import scala.annotation.tailrec
/** A [[PersistedInventory]] backed by a [[BrainInventory brain Inventory]]. /** A [[PersistedInventory]] backed by a [[BrainInventory brain Inventory]].
* *
* Synchronizes the contents of the two inventories, propagating changes from one to the other. * Synchronizes the contents of the two inventories, propagating changes from one to the other.
* *
* When a new [[Item]] is added to the Desktop inventory, its [[ComponentItem.component]] is added to * When a new [[Item]] is added to the Desktop inventory, its [[EntityItem.entity]] is added to
* the [[brainInventory]]. When a new [[Entity]] is added to the [[brainInventory]], an [[Item]] is recovered from it * the [[brainInventory]]. When a new [[Entity]] is added to the [[brainInventory]], an [[Item]] is recovered from it
* by using an appropriate [[ItemRecoverer]] from [[Items the registry]]. * by using an appropriate [[ItemRecoverer]] from [[Items the registry]].
* *
@ -28,7 +28,7 @@ import scala.annotation.tailrec
* by limiting the recursion depth. * by limiting the recursion depth.
*/ */
trait SyncedInventory extends PersistedInventory with EventAware with Logging { trait SyncedInventory extends PersistedInventory with EventAware with Logging {
override type I <: Item with ComponentItem override type I <: EntityItem
// to avoid synchronization while we're loading stuff // to avoid synchronization while we're loading stuff
private var isLoading = false private var isLoading = false
@ -54,20 +54,41 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
} }
@throws[ItemLoadException]("if the item could not be loaded") @throws[ItemLoadException]("if the item could not be loaded")
override protected def loadComponentItem(itemClass: Class[_], slotNbt: NBTTagCompound): I = { override protected def loadEntityItem(itemClass: Class[_], slotNbt: NBTTagCompound): I = {
val entityAddress = slotNbt.getString(SlotEntityAddressTag) trait Matcher {
def matches(entity: Entity): Boolean
// why not OcelotDesktop.workspace.entityByAddress?
// well, that one only looks for tile entities (computers, screens, etc.), and our items are none of that...
val matchingEntities = brainInventory.inventory.iterator
.flatMap(_.get)
.collect { case env: Environment if env.node.address == entityAddress => env }
val entity = matchingEntities.nextOption() match {
case Some(entity) => entity
case None => throw ItemLoadException(s"entity $entityAddress has disappeared")
} }
val matcher = if (slotNbt.hasKey(SlotEntityIdTag)) {
new Matcher {
private val entityId = UUID.fromString(slotNbt.getString(SlotEntityIdTag))
override def toString: String = s"entity with id `$entityId`"
override def matches(entity: Entity): Boolean = entity.entityId == entityId
}
} else {
// migration code for older saves, which relied on entity addresses.
new Matcher {
private val entityAddress = slotNbt.getString(SlotEntityAddressTag)
override def toString: String = s"entity with address `$entityAddress"
override def matches(entity: Entity): Boolean = entity match {
// why not OcelotDesktop.workspace.entityByAddress?
// well, that one only looks for tile entities (computers, screens, etc.),
// and our items are none of that...
case env: Environment => env.node.address == entityAddress
case _ => false
}
}
}
val entity = brainInventory.inventory.iterator
.flatMap(_.get)
.find(matcher.matches)
.getOrElse(throw ItemLoadException(s"$matcher has disappeared"))
findUnaryConstructor(itemClass, entity.getClass) match { findUnaryConstructor(itemClass, entity.getClass) match {
case Some(constructor) => constructor.newInstance(entity).asInstanceOf[I] case Some(constructor) => constructor.newInstance(entity).asInstanceOf[I]
@ -83,8 +104,8 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
// we'll deal with it the during synchronization // we'll deal with it the during synchronization
} }
override protected def saveComponentItem(slotNbt: NBTTagCompound, item: ComponentItem): Unit = { override protected def saveEntityItem(slotNbt: NBTTagCompound, item: EntityItem): Unit = {
slotNbt.setString(SlotEntityAddressTag, item.component.node.address) slotNbt.setString(SlotEntityIdTag, item.entity.entityId.toString)
} }
eventHandlers += { eventHandlers += {
@ -97,17 +118,6 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
override def onItemAdded(slot: Slot): Unit = { override def onItemAdded(slot: Slot): Unit = {
if (!isLoading) { if (!isLoading) {
// FIXME: the sheer scope of this atrocity is mind-blowing:
// we strip nodes of their free will and forcefully imprint onto them an address
// and only because there is no other way to identify brain entities!
// in an ideal world, persistable objects should have a stable identifier
// which is in no way tied to environments and their addresses.
// alas, for the time being we have to bear the burden of grave sins and war crimes.
val component = slot.get.get.asInstanceOf[ComponentItem].component
if (component.node.address == null) {
Network.joinNewNetwork(component.node)
}
sync(slot.index, SyncDirection.DesktopToBrain) sync(slot.index, SyncDirection.DesktopToBrain)
} }
} }
@ -177,7 +187,7 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
case SyncDirection.DesktopToBrain => case SyncDirection.DesktopToBrain =>
// the `asInstanceOf` is indeed entirely superfluous, but IntelliJ IDEA complains otherwise... // the `asInstanceOf` is indeed entirely superfluous, but IntelliJ IDEA complains otherwise...
OcelotDesktop.withTickLockAcquired { OcelotDesktop.withTickLockAcquired {
brainInventory.inventory(slotIndex).set(Slot(slotIndex).get.map(_.asInstanceOf[ComponentItem].component)) brainInventory.inventory(slotIndex).set(Slot(slotIndex).get.map(_.asInstanceOf[EntityItem].entity))
} }
case SyncDirection.BrainToDesktop => case SyncDirection.BrainToDesktop =>
@ -248,7 +258,7 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
private def checkSlotStatus(slotIndex: Int): SlotStatus = { private def checkSlotStatus(slotIndex: Int): SlotStatus = {
(Slot(slotIndex).get, brainInventory.inventory(slotIndex).get) match { (Slot(slotIndex).get, brainInventory.inventory(slotIndex).get) match {
case (Some(item), Some(entity)) if item.component eq entity => SlotStatus.Synchronized case (Some(item), Some(entity)) if item.entity eq entity => SlotStatus.Synchronized
case (Some(_), Some(_)) => SlotStatus.Conflict case (Some(_), Some(_)) => SlotStatus.Conflict
case (Some(_), None) => SlotStatus.DesktopNonEmpty case (Some(_), None) => SlotStatus.DesktopNonEmpty
case (None, Some(_)) => SlotStatus.BrainNonEmpty case (None, Some(_)) => SlotStatus.BrainNonEmpty
@ -259,6 +269,7 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
object SyncedInventory { object SyncedInventory {
private val SlotEntityAddressTag = "entity" private val SlotEntityAddressTag = "entity"
private val SlotEntityIdTag = "entity_id"
private val InitialSyncFuel = 5 private val InitialSyncFuel = 5

View File

@ -8,7 +8,7 @@ import totoro.ocelot.brain.entity.traits.{Entity, GenericCPU, GenericGPU}
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
class ApuItem(val apu: APU) extends Item with ComponentItem with PersistableItem with CpuLikeItem with GpuLikeItem { class ApuItem(val apu: APU) extends Item with ComponentItem with PersistableItem with CpuLikeItem with GpuLikeItem {
override def component: Entity with GenericCPU with GenericGPU = apu override def entity: Entity with GenericCPU with GenericGPU = apu
override def factory: ApuItem.Factory = new ApuItem.Factory(apu.tier) override def factory: ApuItem.Factory = new ApuItem.Factory(apu.tier)
} }

View File

@ -1,16 +1,14 @@
package ocelot.desktop.inventory.item package ocelot.desktop.inventory.item
import ocelot.desktop.graphics.IconSource import ocelot.desktop.graphics.IconSource
import ocelot.desktop.inventory.traits.ComponentItem import ocelot.desktop.inventory.traits.EntityItem
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
import ocelot.desktop.ui.widget.tooltip.ItemTooltip import ocelot.desktop.ui.widget.tooltip.ItemTooltip
import totoro.ocelot.brain.entity.ComponentBus import totoro.ocelot.brain.entity.ComponentBus
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
class ComponentBusItem(val componentBus: ComponentBus) extends Item with ComponentItem { class ComponentBusItem(val componentBus: ComponentBus) extends Item with EntityItem {
override def component: Entity with Environment = componentBus override def entity: ComponentBus = componentBus
override def showAddress: Boolean = false
override def factory: ItemFactory = new ComponentBusItem.Factory(componentBus.tier) override def factory: ItemFactory = new ComponentBusItem.Factory(componentBus.tier)
override def fillTooltip(tooltip: ItemTooltip): Unit = { override def fillTooltip(tooltip: ItemTooltip): Unit = {

View File

@ -8,7 +8,7 @@ import totoro.ocelot.brain.entity.traits.{Entity, GenericCPU}
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
class CpuItem(val cpu: CPU) extends Item with ComponentItem with PersistableItem with CpuLikeItem { class CpuItem(val cpu: CPU) extends Item with ComponentItem with PersistableItem with CpuLikeItem {
override def component: Entity with GenericCPU = cpu override def entity: Entity with GenericCPU = cpu
override def factory: CpuItem.Factory = new CpuItem.Factory(cpu.tier) override def factory: CpuItem.Factory = new CpuItem.Factory(cpu.tier)
} }

View File

@ -28,7 +28,7 @@ object DataCardItem {
} }
class Tier1(val dataCard: DataCard.Tier1) extends DataCardItem { class Tier1(val dataCard: DataCard.Tier1) extends DataCardItem {
override def component: Entity with Environment = dataCard override def entity: Entity with Environment = dataCard
override def factory: Factory = DataCardItem.Tier1.Factory override def factory: Factory = DataCardItem.Tier1.Factory
} }
@ -48,7 +48,7 @@ object DataCardItem {
} }
class Tier2(val dataCard: DataCard.Tier2) extends DataCardItem { class Tier2(val dataCard: DataCard.Tier2) extends DataCardItem {
override def component: Entity with Environment = dataCard override def entity: Entity with Environment = dataCard
override def factory: Factory = DataCardItem.Tier2.Factory override def factory: Factory = DataCardItem.Tier2.Factory
} }
@ -68,7 +68,7 @@ object DataCardItem {
} }
class Tier3(val dataCard: DataCard.Tier3) extends DataCardItem { class Tier3(val dataCard: DataCard.Tier3) extends DataCardItem {
override def component: Entity with Environment = dataCard override def entity: Entity with Environment = dataCard
override def factory: Factory = DataCardItem.Tier3.Factory override def factory: Factory = DataCardItem.Tier3.Factory
} }

View File

@ -12,7 +12,7 @@ import totoro.ocelot.brain.util.Tier.Tier
class DiskDriveMountableItem(val diskDriveMountable: DiskDriveMountable) extends RackMountableItem with DiskDriveAware { class DiskDriveMountableItem(val diskDriveMountable: DiskDriveMountable) extends RackMountableItem with DiskDriveAware {
override def floppyDiskDrive: FloppyDiskDrive = diskDriveMountable override def floppyDiskDrive: FloppyDiskDrive = diskDriveMountable
override def component: DiskDriveMountable = diskDriveMountable override def entity: DiskDriveMountable = diskDriveMountable
override def factory: ItemFactory = DiskDriveMountableItem.Factory override def factory: ItemFactory = DiskDriveMountableItem.Factory
override def fillTooltip(tooltip: ItemTooltip): Unit = { override def fillTooltip(tooltip: ItemTooltip): Unit = {

View File

@ -18,7 +18,7 @@ import javax.swing.JFileChooser
import scala.util.Try import scala.util.Try
class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with PersistableItem { class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with PersistableItem {
override def component: Entity with Environment = eeprom override def entity: Entity with Environment = eeprom
override def fillTooltip(tooltip: ItemTooltip): Unit = { override def fillTooltip(tooltip: ItemTooltip): Unit = {
super.fillTooltip(tooltip) super.fillTooltip(tooltip)

View File

@ -12,7 +12,7 @@ import totoro.ocelot.brain.util.DyeColor
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
class FloppyItem(var floppy: Floppy) extends Item with ComponentItem with PersistableItem with DiskItem { class FloppyItem(var floppy: Floppy) extends Item with ComponentItem with PersistableItem with DiskItem {
override def component: Entity with Disk = floppy override def entity: Entity with Disk = floppy
override def diskKind: String = "Floppy" override def diskKind: String = "Floppy"

View File

@ -9,7 +9,7 @@ import totoro.ocelot.brain.util.Tier.Tier
class GraphicsCardItem(val gpu: GraphicsCard) class GraphicsCardItem(val gpu: GraphicsCard)
extends Item with ComponentItem with PersistableItem with CardItem with GpuLikeItem { extends Item with ComponentItem with PersistableItem with CardItem with GpuLikeItem {
override def component: Entity with GenericGPU = gpu override def entity: Entity with GenericGPU = gpu
override def factory: GraphicsCardItem.Factory = new GraphicsCardItem.Factory(gpu.tier) override def factory: GraphicsCardItem.Factory = new GraphicsCardItem.Factory(gpu.tier)
} }

View File

@ -20,7 +20,7 @@ class HddItem(var hdd: Hdd) extends Item with ComponentItem with PersistableItem
this(Hdd.Unmanaged(hdd)) this(Hdd.Unmanaged(hdd))
} }
override def component: Entity with Disk = hdd.hdd override def entity: Entity with Disk = hdd.hdd
override def diskKind: String = "HDD" override def diskKind: String = "HDD"

View File

@ -9,7 +9,7 @@ import totoro.ocelot.brain.util.Tier
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
class InternetCardItem(val card: InternetCard) extends Item with ComponentItem with PersistableItem with CardItem { class InternetCardItem(val card: InternetCard) extends Item with ComponentItem with PersistableItem with CardItem {
override def component: Entity with Environment = card override def entity: Entity with Environment = card
override def factory: ItemFactory = InternetCardItem.Factory override def factory: ItemFactory = InternetCardItem.Factory
} }

View File

@ -12,7 +12,7 @@ import totoro.ocelot.brain.util.Tier
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
class LinkedCardItem(val linkedCard: LinkedCard) extends Item with ComponentItem with PersistableItem with CardItem { class LinkedCardItem(val linkedCard: LinkedCard) extends Item with ComponentItem with PersistableItem with CardItem {
override def component: Entity with Environment = linkedCard override def entity: Entity with Environment = linkedCard
override def fillTooltip(tooltip: ItemTooltip): Unit = { override def fillTooltip(tooltip: ItemTooltip): Unit = {
super.fillTooltip(tooltip) super.fillTooltip(tooltip)

View File

@ -10,7 +10,7 @@ import totoro.ocelot.brain.util.ExtendedTier.ExtendedTier
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
class MemoryItem(val memory: Memory) extends Item with ComponentItem with PersistableItem { class MemoryItem(val memory: Memory) extends Item with ComponentItem with PersistableItem {
override def component: Entity with Environment = memory override def entity: Entity with Environment = memory
override def factory: ItemFactory = new MemoryItem.Factory(memory.memoryTier) override def factory: ItemFactory = new MemoryItem.Factory(memory.memoryTier)

View File

@ -10,7 +10,7 @@ import totoro.ocelot.brain.util.Tier
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
class NetworkCardItem(val card: NetworkCard) extends Item with ComponentItem with PersistableItem with CardItem { class NetworkCardItem(val card: NetworkCard) extends Item with ComponentItem with PersistableItem with CardItem {
override def component: Entity with NetworkCard = card override def entity: Entity with NetworkCard = card
override def factory: ItemFactory = NetworkCardItem.Factory override def factory: ItemFactory = NetworkCardItem.Factory

View File

@ -16,7 +16,7 @@ import totoro.ocelot.brain.util.Tier.Tier
class OcelotCardItem(val ocelotCard: OcelotCard) class OcelotCardItem(val ocelotCard: OcelotCard)
extends Item with ComponentItem with OcelotInterfaceLogStorage with PersistableItem with CardItem with Logging { extends Item with ComponentItem with OcelotInterfaceLogStorage with PersistableItem with CardItem with Logging {
override def component: OcelotCard = ocelotCard override def entity: OcelotCard = ocelotCard
override def ocelotInterface: OcelotInterface = ocelotCard override def ocelotInterface: OcelotInterface = ocelotCard

View File

@ -23,7 +23,7 @@ object RedstoneCardItem {
} }
class Tier1(val redstoneCard: Redstone.Tier1) extends RedstoneCardItem { class Tier1(val redstoneCard: Redstone.Tier1) extends RedstoneCardItem {
override def component: Entity with Environment = redstoneCard override def entity: Entity with Environment = redstoneCard
override def factory: Factory = RedstoneCardItem.Tier1.Factory override def factory: Factory = RedstoneCardItem.Tier1.Factory
@ -80,7 +80,7 @@ object RedstoneCardItem {
} }
class Tier2(override val redstoneCard: Redstone.Tier2) extends RedstoneCardItem.Tier1(redstoneCard) { class Tier2(override val redstoneCard: Redstone.Tier2) extends RedstoneCardItem.Tier1(redstoneCard) {
override def component: Entity with Environment = redstoneCard override def entity: Entity with Environment = redstoneCard
override def factory: Factory = RedstoneCardItem.Tier2.Factory override def factory: Factory = RedstoneCardItem.Tier2.Factory

View File

@ -12,7 +12,7 @@ import totoro.ocelot.brain.util.Tier.Tier
class SelfDestructingCardItem(val card: SelfDestructingCard) class SelfDestructingCardItem(val card: SelfDestructingCard)
extends Item with ComponentItem with PersistableItem with CardItem { extends Item with ComponentItem with PersistableItem with CardItem {
override def component: Entity with Environment = card override def entity: Entity with Environment = card
override def factory: ItemFactory = SelfDestructingCardItem.Factory override def factory: ItemFactory = SelfDestructingCardItem.Factory

View File

@ -12,7 +12,7 @@ import totoro.ocelot.brain.util.Tier
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
class ServerItem(val server: Server) extends RackMountableItem with AudibleComputerAware { class ServerItem(val server: Server) extends RackMountableItem with AudibleComputerAware {
override def component: Server = server override def entity: Server = server
override def factory: ItemFactory = new ServerItem.Factory(server.tier) override def factory: ItemFactory = new ServerItem.Factory(server.tier)
override def onRemoved(): Unit = { override def onRemoved(): Unit = {

View File

@ -16,7 +16,7 @@ class SoundCardItem(val soundCard: SoundCard)
override def createWindow(): SoundCardWindow = new SoundCardWindow(soundCard) override def createWindow(): SoundCardWindow = new SoundCardWindow(soundCard)
override def component: Entity with Environment = soundCard override def entity: Entity with Environment = soundCard
override def fillRmbMenu(menu: ContextMenu): Unit = { override def fillRmbMenu(menu: ContextMenu): Unit = {
menu.addEntry(ContextMenuEntry("Open card interface", IconSource.Window) { menu.addEntry(ContextMenuEntry("Open card interface", IconSource.Window) {

View File

@ -2,7 +2,7 @@ package ocelot.desktop.inventory.item
import ocelot.desktop.OcelotDesktop import ocelot.desktop.OcelotDesktop
import ocelot.desktop.graphics.IconSource import ocelot.desktop.graphics.IconSource
import ocelot.desktop.inventory.traits.ComponentItem import ocelot.desktop.inventory.traits.EntityItem
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
import ocelot.desktop.ui.widget.tooltip.ItemTooltip import ocelot.desktop.ui.widget.tooltip.ItemTooltip
import ocelot.desktop.util.SizeFormatting.formatSize import ocelot.desktop.util.SizeFormatting.formatSize
@ -10,12 +10,10 @@ import totoro.ocelot.brain.entity.tape.Tape
import totoro.ocelot.brain.nbt.NBTTagCompound import totoro.ocelot.brain.nbt.NBTTagCompound
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
class TapeItem(var tape: Tape) extends Item with ComponentItem { class TapeItem(var tape: Tape) extends Item with EntityItem {
def this() = this(new Tape) def this() = this(new Tape)
override def component: Tape = tape override def entity: Tape = tape
override def showAddress: Boolean = false
override def fillTooltip(tooltip: ItemTooltip): Unit = { override def fillTooltip(tooltip: ItemTooltip): Unit = {
super.fillTooltip(tooltip) super.fillTooltip(tooltip)

View File

@ -1,70 +1,30 @@
package ocelot.desktop.inventory.traits package ocelot.desktop.inventory.traits
import ocelot.desktop.OcelotDesktop
import ocelot.desktop.graphics.IconSource import ocelot.desktop.graphics.IconSource
import ocelot.desktop.inventory.Item import ocelot.desktop.inventory.Item
import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
import ocelot.desktop.ui.widget.tooltip.ItemTooltip import ocelot.desktop.ui.widget.tooltip.ItemTooltip
import totoro.ocelot.brain.entity.traits.{Entity, Environment, WorkspaceAware} import totoro.ocelot.brain.entity.traits.{Entity, Environment}
/** Implemented by [[Item]]s that wrap a component. /** Implemented by [[Item]]s that wrap a component. */
* trait ComponentItem extends EntityItem {
* @note Subclasses must provide a unary constructor that accepts the [[component]]. override def entity: Entity with Environment
*/
trait ComponentItem extends Item with PersistableItem {
/** The component wrapped by this item.
*
* @note The value must already be available during the initialization.
*/
def component: Entity with Environment
private def setWorkspace(): Unit = component match {
case component: WorkspaceAware => component.workspace = OcelotDesktop.workspace
case _ =>
}
override def reinserting(f: => Unit): Unit = super.reinserting {
// removal may have set the workspace to null
setWorkspace()
f
// since we have absolutely no idea what f does, workspace could've been set to null again
setWorkspace()
}
setWorkspace()
override def onInserted(): Unit = {
super.onInserted()
// this one isn't strictly necessary, I believe, but I'll keep this for the sake of symmetry and safety
setWorkspace()
}
override def onRemoved(): Unit = {
super.onRemoved()
// fix up ComponentInventory's zeroing out the workspace
setWorkspace()
}
override def shouldReceiveEventsFor(address: String): Boolean = override def shouldReceiveEventsFor(address: String): Boolean =
super.shouldReceiveEventsFor(address) || Option(component.node).exists(_.address == address) super.shouldReceiveEventsFor(address) || Option(entity.node).exists(_.address == address)
override def fillTooltip(tooltip: ItemTooltip): Unit = { override def fillTooltip(tooltip: ItemTooltip): Unit = {
super.fillTooltip(tooltip) super.fillTooltip(tooltip)
if (showAddress) { if (showAddress) {
tooltip.addLine(s"Address: ${component.node.address}") tooltip.addLine(s"Address: ${entity.node.address}")
} }
} }
override def fillRmbMenu(menu: ContextMenu): Unit = { override def fillRmbMenu(menu: ContextMenu): Unit = {
menu.addEntry(ContextMenuEntry("Copy address", IconSource.Copy) { menu.addEntry(ContextMenuEntry("Copy address", IconSource.Copy) {
UiHandler.clipboard = component.node.address UiHandler.clipboard = entity.node.address
}) })
super.fillRmbMenu(menu) super.fillRmbMenu(menu)

View File

@ -10,19 +10,19 @@ import totoro.ocelot.brain.entity.traits.{Entity, GenericCPU}
/** An [[Item]] that acts as a CPU and can therefore be inserted into a CPU slot. */ /** An [[Item]] that acts as a CPU and can therefore be inserted into a CPU slot. */
trait CpuLikeItem extends ComponentItem { trait CpuLikeItem extends ComponentItem {
override def component: Entity with GenericCPU override def entity: Entity with GenericCPU
override def fillRmbMenu(menu: ContextMenu): Unit = { override def fillRmbMenu(menu: ContextMenu): Unit = {
menu.addEntry(new ContextMenuSubmenu("Set architecture", Some(ContextMenuIcon(IconSource.Microchip))) { menu.addEntry(new ContextMenuSubmenu("Set architecture", Some(ContextMenuIcon(IconSource.Microchip))) {
for (arch <- component.allArchitectures) { for (arch <- entity.allArchitectures) {
val name = MachineAPI.getArchitectureName(arch) + val name = MachineAPI.getArchitectureName(arch) +
(if (arch == component.architecture) " (current)" else "") (if (arch == entity.architecture) " (current)" else "")
val entry = ContextMenuEntry(name) { val entry = ContextMenuEntry(name) {
component.setArchitecture(arch) entity.setArchitecture(arch)
notifySlot(CpuArchitectureChangedNotification) notifySlot(CpuArchitectureChangedNotification)
} }
entry.setEnabled(arch != component.architecture) entry.setEnabled(arch != entity.architecture)
addEntry(entry) addEntry(entry)
} }
}) })
@ -32,8 +32,8 @@ trait CpuLikeItem extends ComponentItem {
override def fillTooltip(tooltip: ItemTooltip): Unit = { override def fillTooltip(tooltip: ItemTooltip): Unit = {
super.fillTooltip(tooltip) super.fillTooltip(tooltip)
if (component != null) { if (entity != null) {
tooltip.addLine(s"Components: ${component.supportedComponents}") tooltip.addLine(s"Components: ${entity.supportedComponents}")
} }
} }
} }

View File

@ -15,7 +15,7 @@ import scala.util.Try
/** A utility mixin for HDDs and floppies. */ /** A utility mixin for HDDs and floppies. */
trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] { trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
override def component: Entity with Disk override def entity: Entity with Disk
def diskKind: String def diskKind: String
@ -32,7 +32,7 @@ trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
def setManaged(managed: Boolean): Unit def setManaged(managed: Boolean): Unit
def lock(): Unit = reinserting { def lock(): Unit = reinserting {
component.setLocked(OcelotDesktop.player.nickname) entity.setLocked(OcelotDesktop.player.nickname)
} }
override def createWindow(): DiskEditWindow = new DiskEditWindow(DiskItem.this) override def createWindow(): DiskEditWindow = new DiskEditWindow(DiskItem.this)
@ -47,7 +47,7 @@ trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
override def fillRmbMenu(menu: ContextMenu): Unit = { override def fillRmbMenu(menu: ContextMenu): Unit = {
if (isEditingAllowed) { if (isEditingAllowed) {
// Real path // Real path
component match { entity match {
case diskManaged: DiskManaged => case diskManaged: DiskManaged =>
DiskItem.addRealPathContextMenuEntries(menu, diskManaged, DiskItem.addRealPathContextMenuEntries(menu, diskManaged,
realPathSetter => { realPathSetter => {
@ -86,8 +86,8 @@ trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
override def fillTooltip(tooltip: ItemTooltip): Unit = { override def fillTooltip(tooltip: ItemTooltip): Unit = {
super.fillTooltip(tooltip) super.fillTooltip(tooltip)
if (component != null) { if (entity != null) {
tooltip.addLine(s"Capacity: ${component.capacity / 1024} kB") tooltip.addLine(s"Capacity: ${entity.capacity / 1024} kB")
} }
} }
} }

View File

@ -0,0 +1,48 @@
package ocelot.desktop.inventory.traits
import ocelot.desktop.OcelotDesktop
import ocelot.desktop.inventory.Item
import totoro.ocelot.brain.entity.traits.{Entity, WorkspaceAware}
/** Implemented by [[Item]]s that wrap an [[Entity]].
*
* @note Subclasses must provide a unary constructor that accepts the [[entity]].
*/
trait EntityItem extends Item with PersistableItem {
/** The entity wrapped by this item.
*
* @note The value must already be available during the trait's initialization.
*/
def entity: Entity
private def setWorkspace(): Unit = entity match {
case entity: WorkspaceAware => entity.workspace = OcelotDesktop.workspace
case _ =>
}
override def reinserting(f: => Unit): Unit = super.reinserting {
// removal may have set the workspace to null
setWorkspace()
f
// since we have absolutely no idea what f does, workspace could've been set to null again
setWorkspace()
}
setWorkspace()
override def onInserted(): Unit = {
super.onInserted()
// this one isn't strictly necessary, I believe, but I'll keep this for the sake of symmetry and safety
setWorkspace()
}
override def onRemoved(): Unit = {
super.onRemoved()
// fix up ComponentInventory's zeroing out the workspace
setWorkspace()
}
}

View File

@ -6,20 +6,20 @@ import totoro.ocelot.brain.entity.traits.{Entity, GenericGPU}
import totoro.ocelot.brain.util.ColorDepth import totoro.ocelot.brain.util.ColorDepth
trait GpuLikeItem extends ComponentItem { trait GpuLikeItem extends ComponentItem {
override def component: Entity with GenericGPU override def entity: Entity with GenericGPU
override def fillTooltip(tooltip: ItemTooltip): Unit = { override def fillTooltip(tooltip: ItemTooltip): Unit = {
super.fillTooltip(tooltip) super.fillTooltip(tooltip)
if (component != null) { if (entity != null) {
val resolution = Settings.screenResolutionsByTier(component.tier.id) val resolution = Settings.screenResolutionsByTier(entity.tier.id)
tooltip.addLine(s"Max resolution: ${resolution._1}×${resolution._2}") tooltip.addLine(s"Max resolution: ${resolution._1}×${resolution._2}")
val depth = Settings.screenDepthsByTier(component.tier.id) match { val depth = Settings.screenDepthsByTier(entity.tier.id) match {
case ColorDepth.OneBit => "1 bit" case ColorDepth.OneBit => "1 bit"
case ColorDepth.FourBit => "4 bit" case ColorDepth.FourBit => "4 bit"
case ColorDepth.EightBit => "8 bit" case ColorDepth.EightBit => "8 bit"
} }
tooltip.addLine(s"Max color depth: $depth") tooltip.addLine(s"Max color depth: $depth")
tooltip.addLine(s"VRAM: ${component.totalVRAM.toInt}") tooltip.addLine(s"VRAM: ${entity.totalVRAM.toInt}")
} }
} }
} }

View File

@ -7,7 +7,7 @@ import totoro.ocelot.brain.entity.result
import totoro.ocelot.brain.entity.traits.{Entity, RackMountable} import totoro.ocelot.brain.entity.traits.{Entity, RackMountable}
trait RackMountableItem extends Item with ComponentItem { trait RackMountableItem extends Item with ComponentItem {
override def component: Entity with RackMountable override def entity: Entity with RackMountable
def isInRack: Boolean = slot.exists(_.inventory.isInstanceOf[RackNode]) def isInRack: Boolean = slot.exists(_.inventory.isInstanceOf[RackNode])

View File

@ -37,6 +37,7 @@ class RackNode(val rack: Rack) extends ComputerAwareNode(rack) with WindowedNode
case mountable: RackMountable if mountable.node.address == address => true case mountable: RackMountable if mountable.node.address == address => true
case mountable: RackMountable with ComponentInventory => mountable.inventory.entities.exists { case mountable: RackMountable with ComponentInventory => mountable.inventory.entities.exists {
case environment: Environment => environment.node.address == address case environment: Environment => environment.node.address == address
case _ => false
} }
case _ => false case _ => false
} }

View File

@ -14,23 +14,23 @@ import totoro.ocelot.brain.entity.traits.{DiskManaged, DiskUnmanaged}
import totoro.ocelot.brain.util.DyeColor import totoro.ocelot.brain.util.DyeColor
class DiskEditWindow(item: DiskItem) extends PanelWindow { class DiskEditWindow(item: DiskItem) extends PanelWindow {
private def diskSize: Option[Long] = Some(item.component).collect { case disk: DiskManaged => private def diskSize: Option[Long] = Some(item.entity).collect { case disk: DiskManaged =>
disk.fileSystem.fileSystem.spaceUsed disk.fileSystem.fileSystem.spaceUsed
} }
private def diskCapacity: Long = item.component.capacity private def diskCapacity: Long = item.entity.capacity
private def isWriteable: Boolean = item.component match { private def isWriteable: Boolean = item.entity match {
case disk: DiskManaged => !disk.fileSystem.fileSystem.isReadOnly case disk: DiskManaged => !disk.fileSystem.fileSystem.isReadOnly
case disk: DiskUnmanaged => !disk.isLocked case disk: DiskUnmanaged => !disk.isLocked
} }
private def isManaged: Boolean = item.component match { private def isManaged: Boolean = item.entity match {
case _: DiskManaged => true case _: DiskManaged => true
case _: DiskUnmanaged => false case _: DiskUnmanaged => false
} }
private def address: Option[String] = Option(item.component.node).flatMap(node => Option(node.address)) private def address: Option[String] = Option(item.entity.node).flatMap(node => Option(node.address))
override protected def title: String = s"${item.diskKind} " + address.getOrElse("") override protected def title: String = s"${item.diskKind} " + address.getOrElse("")