blob: 5bf365b59a8f5c296c9ad793ef2e57e61cb5b88b [file] [log] [blame] [raw]
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
}
}