mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-09 07:15:11 -04:00
removed the overly generic component saving again, items just have to be saved by their inventories. way too many problems with figuring out when to save otherwise (e.g. world unload triggers for each "sub-world", so when going from overworld to nether clearing the cache didn't really make sense; quite the opposite)
This commit is contained in:
parent
814f49b0ec
commit
1da52f8403
@ -12,7 +12,7 @@ import java.util.logging.Logger
|
||||
import li.cil.oc.client.{PacketHandler => ClientPacketHandler}
|
||||
import li.cil.oc.common.Proxy
|
||||
import li.cil.oc.server.{PacketHandler => ServerPacketHandler}
|
||||
import scala.reflect.runtime.universe._
|
||||
import scala.reflect.runtime.{universe => ru}
|
||||
|
||||
@Mod(modid = "OpenComputers", name = "OpenComputers", version = "0.0.0", dependencies = "required-after:Forge@[9.10.0.804,)", modLanguage = "scala")
|
||||
@NetworkMod(clientSideRequired = true, serverSideRequired = false,
|
||||
@ -24,6 +24,11 @@ object OpenComputers {
|
||||
/** Logger used all throughout this mod. */
|
||||
val log = Logger.getLogger("OpenComputers")
|
||||
|
||||
// Workaround for threading issues in Scala 2.10's runtime reflection: just
|
||||
// initialize it once in the beginning. For more on this issue see
|
||||
// http://docs.scala-lang.org/overviews/reflection/thread-safety.html
|
||||
val mirror = ru.runtimeMirror(OpenComputers.getClass.getClassLoader)
|
||||
|
||||
@SidedProxy(
|
||||
clientSide = "li.cil.oc.client.Proxy",
|
||||
serverSide = "li.cil.oc.server.Proxy")
|
||||
@ -37,9 +42,4 @@ object OpenComputers {
|
||||
|
||||
@EventHandler
|
||||
def postInit(e: FMLPostInitializationEvent) = proxy.postInit(e)
|
||||
|
||||
// Workaround for threading issues in Scala 2.10's runtime reflection: just
|
||||
// initialize it once in the beginning. For more on this issue see
|
||||
// http://docs.scala-lang.org/overviews/reflection/thread-safety.html
|
||||
val dummyMirror = runtimeMirror(OpenComputers.getClass.getClassLoader)
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package li.cil.oc.api
|
||||
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NBTTagCompound
|
||||
import scala.reflect.runtime.{universe => ru}
|
||||
|
||||
/**
|
||||
* An object that can be persisted to an NBT tag and restored back from it.
|
||||
@ -24,68 +22,4 @@ trait Persistable {
|
||||
* @param nbt the tag to save the state to.
|
||||
*/
|
||||
def save(nbt: NBTTagCompound)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows linking persistable instances to an item stack.
|
||||
* <p/>
|
||||
* This is used to track item components. It creates an entry in the item
|
||||
* stack's tag compound name "component", with a UUID that is used to link
|
||||
* it to the component instance.
|
||||
* Each type this is used with has to be registered first. For components, I
|
||||
* recommend registering them in the constructor of the item the component
|
||||
* belongs to.
|
||||
* <p/>
|
||||
* It is important to note that since there is no way for the persistent
|
||||
* object registry to know when a persistent object met its ultimate doom,
|
||||
* entries will live forever, unless they are explicitly `delete`d.
|
||||
*/
|
||||
object Persistable {
|
||||
/**
|
||||
* Registers a new type valid for lookups.
|
||||
* <p/>
|
||||
* The name has to be unique among all registered types. It is similar to
|
||||
* how TileEntities names have to be unique, since this is used to look
|
||||
* up the constructor when unpersisting instances.
|
||||
*
|
||||
* @param name the name of the type
|
||||
* @param constructor a function that can be used to create a new instance.
|
||||
* @tparam T the type of the object.
|
||||
*/
|
||||
def add[T <: Persistable : ru.TypeTag](name: String, constructor: () => T): Unit =
|
||||
instance.foreach(_.add(name, constructor))
|
||||
|
||||
/**
|
||||
* Retrieve the object associated to the specified item stack.
|
||||
* <p/>
|
||||
* If there is no instance yet, one will be created.
|
||||
*
|
||||
* @param stack the item stack to get the persistent object for.
|
||||
* @tparam T the type of the object.
|
||||
* @return the persistent object associated with that item stack.
|
||||
*/
|
||||
def get[T <: Persistable : ru.TypeTag](stack: ItemStack): Option[T] =
|
||||
instance.fold(None: Option[T])(_.get(stack))
|
||||
|
||||
/**
|
||||
* Marks the object associated to the specified item stack for deletion.
|
||||
* <p/>
|
||||
* Note that the persisted data is only deleted upon the next world save, to
|
||||
* avoid discrepancies between the persistent storage and the savegame.
|
||||
*
|
||||
* @param stack the item stack to delete the persistent object for.
|
||||
*/
|
||||
def delete(stack: ItemStack): Unit =
|
||||
instance.foreach(_.delete(stack))
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
/** Initialized in pre-init. */
|
||||
private[oc] var instance: Option[ {
|
||||
def add[T <: Persistable : ru.TypeTag](name: String, constructor: () => T): Unit
|
||||
|
||||
def get[T <: Persistable : ru.TypeTag](stack: ItemStack): Option[T]
|
||||
|
||||
def delete(stack: ItemStack): Unit
|
||||
}] = None
|
||||
}
|
@ -3,6 +3,7 @@ package li.cil.oc.api.driver
|
||||
import li.cil.oc.api.network.Node
|
||||
import net.minecraft.item.ItemStack
|
||||
import li.cil.oc.api.Driver
|
||||
import net.minecraft.nbt.NBTTagCompound
|
||||
|
||||
/**
|
||||
* Interface for item component drivers.
|
||||
@ -54,9 +55,30 @@ trait Item extends Driver {
|
||||
* added to a computer, for example. Components that are not part of the
|
||||
* component network probably don't make much sense (can't think of any uses
|
||||
* at this time), but you may still opt to not implement this.
|
||||
* <p/>
|
||||
* This is expected to return a *new instance* each time it is called.
|
||||
*
|
||||
* @param item the item instance for which to get the node.
|
||||
* @return the network node for that item.
|
||||
*/
|
||||
def node(item: ItemStack): Option[Node] = None
|
||||
|
||||
/**
|
||||
* Get the tag compound based on the item stack to use for persisting the
|
||||
* node associated with the specified item stack.
|
||||
* <p/>
|
||||
* This is only used if `node` is not `None`. This must always be a child
|
||||
* tag of the items own tag compound, it will not be saved otherwise. Use
|
||||
* this in the unlikely case that the default name collides with something.
|
||||
*
|
||||
* @param item the item to get the child tag to use for the `node`.
|
||||
* @return the tag to use for saving and loading.
|
||||
*/
|
||||
def nbt(item: ItemStack) = {
|
||||
val nbt = item.getTagCompound
|
||||
if (!nbt.hasKey("oc.node")) {
|
||||
nbt.setCompoundTag("oc.node", new NBTTagCompound())
|
||||
}
|
||||
nbt.getCompoundTag("oc.node")
|
||||
}
|
||||
}
|
@ -16,13 +16,13 @@ class Proxy {
|
||||
Config.load(e.getSuggestedConfigurationFile)
|
||||
|
||||
// Note: en_US is loaded automatically.
|
||||
// TODO Are others as well?
|
||||
LanguageRegistry.instance.loadLocalization(
|
||||
"/assets/" + Config.resourceDomain + "/lang/de_DE.lang", "de_DE", false)
|
||||
|
||||
api.Driver.instance = Some(driver.Registry)
|
||||
api.FileSystem.instance = Some(fs.FileSystem)
|
||||
api.Network.instance = Some(network.Network)
|
||||
api.Persistable.instance = Some(item.Persistable)
|
||||
}
|
||||
|
||||
def init(e: FMLInitializationEvent): Unit = {
|
||||
@ -39,7 +39,6 @@ class Proxy {
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(Computer)
|
||||
MinecraftForge.EVENT_BUS.register(network.Network)
|
||||
MinecraftForge.EVENT_BUS.register(item.Persistable)
|
||||
}
|
||||
|
||||
def postInit(e: FMLPostInitializationEvent): Unit = {
|
||||
|
@ -1,15 +1,11 @@
|
||||
package li.cil.oc.common.item
|
||||
|
||||
import li.cil.oc.Config
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.server.component
|
||||
import net.minecraft.client.renderer.texture.IconRegister
|
||||
|
||||
class GraphicsCard(val parent: Delegator) extends Delegate {
|
||||
def unlocalizedName = "GraphicsCard"
|
||||
|
||||
api.Persistable.add("oc.item." + unlocalizedName, () => new component.GraphicsCard())
|
||||
|
||||
override def registerIcons(iconRegister: IconRegister) {
|
||||
super.registerIcons(iconRegister)
|
||||
|
||||
|
@ -1,100 +0,0 @@
|
||||
package li.cil.oc.common.item
|
||||
|
||||
import java.io._
|
||||
import java.util.logging.Level
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.{OpenComputers, Config}
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.{NBTBase, NBTTagCompound, NBTTagString}
|
||||
import net.minecraftforge.common.DimensionManager
|
||||
import net.minecraftforge.event.ForgeSubscribe
|
||||
import net.minecraftforge.event.world.WorldEvent
|
||||
import scala.collection.mutable
|
||||
import scala.reflect.runtime.{universe => ru}
|
||||
|
||||
object Persistable {
|
||||
private val mirror = ru.runtimeMirror(getClass.getClassLoader)
|
||||
|
||||
private val types = mutable.Map.empty[ru.Type, (String, () => api.Persistable)]
|
||||
|
||||
private val cache = mutable.Map.empty[String, api.Persistable]
|
||||
|
||||
private val deleted = mutable.Set.empty[String]
|
||||
|
||||
def add[T <: api.Persistable : ru.TypeTag](name: String, constructor: () => T): Unit =
|
||||
types += ru.typeOf[T] ->(name, constructor)
|
||||
|
||||
def get[T <: api.Persistable : ru.TypeTag](stack: ItemStack): Option[T] = {
|
||||
val uuid = if (stack.hasTagCompound && stack.getTagCompound.hasKey("component")) {
|
||||
stack.getTagCompound.getString("component")
|
||||
} else {
|
||||
val uuid = java.util.UUID.randomUUID().toString
|
||||
stack.setTagInfo("component", new NBTTagString(null, uuid))
|
||||
uuid
|
||||
}
|
||||
Some(cache.getOrElseUpdate(uuid, load(uuid).getOrElse({
|
||||
val (_, constructor) = types(ru.typeOf[T])
|
||||
constructor()
|
||||
})).asInstanceOf[T])
|
||||
}
|
||||
|
||||
def delete(stack: ItemStack): Unit = if (stack.hasTagCompound && stack.getTagCompound.hasKey("component")) {
|
||||
val uuid = stack.getTagCompound.getString("component")
|
||||
cache -= uuid
|
||||
deleted += uuid
|
||||
}
|
||||
|
||||
private def saveDirectory = {
|
||||
val directory = new File(DimensionManager.getCurrentSaveRootDirectory, Config.resourceDomain + "/components")
|
||||
if (!directory.exists())
|
||||
directory.mkdirs()
|
||||
directory
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
def onWorldUnload(e: WorldEvent.Unload) = {
|
||||
cache.clear()
|
||||
deleted.clear()
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
def onWorldSave(e: WorldEvent.Save) {
|
||||
val directory = saveDirectory
|
||||
for ((uuid, instance) <- cache) try {
|
||||
val (name, _) = types(mirror.classSymbol(instance.getClass).toType)
|
||||
val nbt = new NBTTagCompound(name)
|
||||
instance.save(nbt)
|
||||
val file = new File(directory, uuid)
|
||||
val stream = new DataOutputStream(new FileOutputStream(file))
|
||||
NBTBase.writeNamedTag(nbt, stream)
|
||||
stream.close()
|
||||
} catch {
|
||||
case e: Throwable => OpenComputers.log.log(Level.WARNING, "Error saving component.", e)
|
||||
}
|
||||
for (uuid <- deleted) {
|
||||
try {
|
||||
new File(saveDirectory, uuid).delete()
|
||||
} catch {
|
||||
case e: Throwable => OpenComputers.log.log(Level.WARNING, "Error deleting component.", e)
|
||||
}
|
||||
}
|
||||
deleted.clear()
|
||||
}
|
||||
|
||||
def load(uuid: String) = {
|
||||
val file = new File(saveDirectory, uuid)
|
||||
if (file.exists()) try {
|
||||
val stream = new DataInputStream(new FileInputStream(file))
|
||||
val nbt = NBTBase.readNamedTag(stream).asInstanceOf[NBTTagCompound]
|
||||
stream.close()
|
||||
val (_, constructor) = types.values.find {
|
||||
case (name, _) => name == nbt.getName
|
||||
}.get
|
||||
val instance = constructor()
|
||||
instance.load(nbt)
|
||||
Some(instance)
|
||||
} catch {
|
||||
case e: Throwable => OpenComputers.log.log(Level.WARNING, "Error loading component.", e); None
|
||||
} else None
|
||||
}
|
||||
}
|
@ -1,15 +1,11 @@
|
||||
package li.cil.oc.common.item
|
||||
|
||||
import li.cil.oc.Config
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.server.component
|
||||
import net.minecraft.client.renderer.texture.IconRegister
|
||||
|
||||
class RedstoneCard(val parent: Delegator) extends Delegate {
|
||||
def unlocalizedName = "RedstoneCard"
|
||||
|
||||
api.Persistable.add("oc.item." + unlocalizedName, () => new component.RedstoneCard())
|
||||
|
||||
override def registerIcons(iconRegister: IconRegister) {
|
||||
super.registerIcons(iconRegister)
|
||||
|
||||
|
@ -13,12 +13,16 @@ import net.minecraft.world.World
|
||||
import li.cil.oc.Items
|
||||
|
||||
trait ComponentInventory extends IInventory with Node {
|
||||
protected val inventory = new Array[ItemStack](8)
|
||||
protected val inventory = new Array[ItemStack](inventorySize)
|
||||
|
||||
protected val itemComponents = Array.fill[Option[Node]](inventorySize)(None)
|
||||
|
||||
protected val computer: component.Computer
|
||||
|
||||
def world: World
|
||||
|
||||
def inventorySize = 8
|
||||
|
||||
def installedMemory = inventory.foldLeft(0)((sum, stack) => sum + (Registry.driverFor(stack) match {
|
||||
case Some(driver) if driver.slot(stack) == Slot.RAM => Items.multi.subItem(stack) match {
|
||||
case Some(ram: item.Memory) => ram.kiloBytes * 1024
|
||||
@ -36,6 +40,10 @@ trait ComponentInventory extends IInventory with Node {
|
||||
if (slot >= 0 && slot < inventory.length) {
|
||||
inventory(slot) = ItemStack.loadItemStackFromNBT(
|
||||
slotNbt.getCompoundTag("item"))
|
||||
itemComponents(slot) = Registry.driverFor(inventory(slot)) match {
|
||||
case None => None
|
||||
case Some(driver) => driver.node(inventory(slot))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,6 +57,14 @@ trait ComponentInventory extends IInventory with Node {
|
||||
case (stack, slot) => {
|
||||
val slotNbt = new NBTTagCompound
|
||||
slotNbt.setByte("slot", slot.toByte)
|
||||
|
||||
itemComponents(slot) match {
|
||||
case None => // Nothing special to save.
|
||||
case Some(node) =>
|
||||
// We're guaranteed to have a driver for entries.
|
||||
node.save(Registry.driverFor(stack).get.nbt(stack))
|
||||
}
|
||||
|
||||
val itemNbt = new NBTTagCompound
|
||||
stack.writeToNBT(itemNbt)
|
||||
slotNbt.setCompoundTag("item", itemNbt)
|
||||
@ -64,29 +80,14 @@ trait ComponentInventory extends IInventory with Node {
|
||||
|
||||
override protected def onConnect() {
|
||||
super.onConnect()
|
||||
for (slot <- 0 until inventory.length) {
|
||||
itemNode(slot) match {
|
||||
case None => // Ignore.
|
||||
case Some(node) =>
|
||||
network.foreach(_.connect(this, node))
|
||||
}
|
||||
}
|
||||
for (node <- itemComponents.filter(_.isDefined).map(_.get))
|
||||
network.foreach(_.connect(this, node))
|
||||
}
|
||||
|
||||
override protected def onDisconnect() {
|
||||
super.onDisconnect()
|
||||
for (slot <- 0 until inventory.length) {
|
||||
itemNode(slot) match {
|
||||
case None => // Ignore.
|
||||
case Some(node) =>
|
||||
node.network.foreach(_.remove(node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def itemNode(slot: Int) = Registry.driverFor(inventory(slot)) match {
|
||||
case None => None
|
||||
case Some(driver) => driver.node(inventory(slot))
|
||||
for (node <- itemComponents.filter(_.isDefined).map(_.get))
|
||||
node.network.foreach(_.remove(node))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
@ -122,9 +123,10 @@ trait ComponentInventory extends IInventory with Node {
|
||||
|
||||
def setInventorySlotContents(slot: Int, item: ItemStack) = {
|
||||
// Uninstall component previously in that slot.
|
||||
if (!world.isRemote) itemNode(slot) match {
|
||||
if (!world.isRemote) itemComponents(slot) match {
|
||||
case None => // Nothing to do.
|
||||
case Some(node) =>
|
||||
itemComponents(slot) = None
|
||||
node.network.foreach(_.remove(node))
|
||||
}
|
||||
|
||||
@ -132,10 +134,15 @@ trait ComponentInventory extends IInventory with Node {
|
||||
if (item != null && item.stackSize > getInventoryStackLimit)
|
||||
item.stackSize = getInventoryStackLimit
|
||||
|
||||
if (!world.isRemote) itemNode(slot) match {
|
||||
case None => // Nothing to do.
|
||||
case Some(node) =>
|
||||
network.foreach(_.connect(this, node))
|
||||
if (!world.isRemote) Registry.driverFor(inventory(slot)) match {
|
||||
case None => // No driver.
|
||||
case Some(driver) =>
|
||||
driver.node(inventory(slot)) match {
|
||||
case None => // No node.
|
||||
case Some(node) =>
|
||||
itemComponents(slot) = Some(node)
|
||||
network.foreach(_.connect(this, node))
|
||||
}
|
||||
}
|
||||
|
||||
computer.recomputeMemory()
|
||||
@ -147,7 +154,7 @@ trait ComponentInventory extends IInventory with Node {
|
||||
case (1 | 2 | 3, Some(driver)) => driver.slot(item) == Slot.PCI
|
||||
case (4 | 5, Some(driver)) => driver.slot(item) == Slot.RAM
|
||||
case (6 | 7, Some(driver)) => driver.slot(item) == Slot.HDD
|
||||
case (_, Some(_)) => false // Invalid slot.
|
||||
case _ => false // Invalid slot.
|
||||
}
|
||||
|
||||
def isInvNameLocalized = false
|
||||
|
@ -244,6 +244,11 @@ class Computer(val owner: Computer.Environment) extends component.Computer with
|
||||
OpenComputers.log.warning("Out of memory!") // TODO remove this when we have a component that can display crash messages
|
||||
owner.network.foreach(_.sendToAll(owner, "computer.crashed", "not enough memory"))
|
||||
close()
|
||||
case e: java.lang.Error if e.getMessage == "not enough memory" =>
|
||||
OpenComputers.log.warning("Out of memory!") // TODO remove this when we have a component that can display crash messages
|
||||
// TODO get this to the world as a computer.crashed message. problem: synchronizing it.
|
||||
owner.network.foreach(_.sendToAll(owner, "computer.crashed", "not enough memory"))
|
||||
close()
|
||||
case e: Throwable => {
|
||||
OpenComputers.log.log(Level.WARNING, "Faulty Lua implementation for synchronized calls.", e)
|
||||
close()
|
||||
|
@ -3,7 +3,6 @@ package li.cil.oc.server.component
|
||||
import li.cil.oc.api.Network
|
||||
import li.cil.oc.api.network.Message
|
||||
import li.cil.oc.api.network.Node
|
||||
import net.minecraft.nbt.NBTTagCompound
|
||||
import net.minecraft.tileentity.TileEntity
|
||||
import net.minecraftforge.common.ForgeDirection
|
||||
|
||||
|
@ -12,5 +12,5 @@ object Disk extends driver.Item {
|
||||
|
||||
override def slot(item: ItemStack) = Slot.HDD
|
||||
|
||||
override def node(item: ItemStack) = null
|
||||
override def node(item: ItemStack) = None
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package li.cil.oc.server.driver
|
||||
|
||||
import li.cil.oc.api.driver.Slot
|
||||
import li.cil.oc.api.{Persistable, driver}
|
||||
import li.cil.oc.api.driver
|
||||
import li.cil.oc.server.component
|
||||
import li.cil.oc.{Config, Items}
|
||||
import net.minecraft.item.ItemStack
|
||||
@ -13,5 +13,9 @@ object GraphicsCard extends driver.Item {
|
||||
|
||||
override def slot(item: ItemStack) = Slot.PCI
|
||||
|
||||
override def node(item: ItemStack) = Persistable.get[component.GraphicsCard](item)
|
||||
override def node(item: ItemStack) = {
|
||||
val instance = new component.GraphicsCard()
|
||||
instance.load(nbt(item))
|
||||
Some(instance)
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package li.cil.oc.server.driver
|
||||
|
||||
import li.cil.oc.api.driver.Slot
|
||||
import li.cil.oc.api.{Persistable, driver}
|
||||
import li.cil.oc.api.driver
|
||||
import li.cil.oc.server.component
|
||||
import li.cil.oc.{Config, Items}
|
||||
import net.minecraft.item.ItemStack
|
||||
@ -13,5 +13,9 @@ object Redstone extends driver.Item {
|
||||
|
||||
override def slot(item: ItemStack) = Slot.PCI
|
||||
|
||||
override def node(item: ItemStack) = Persistable.get[component.RedstoneCard](item)
|
||||
override def node(item: ItemStack) = {
|
||||
val instance = new component.RedstoneCard()
|
||||
instance.load(nbt(item))
|
||||
Some(instance)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user