This commit is contained in:
Florian Nücke 2014-03-16 07:54:54 +01:00
commit f448cbbf57
9 changed files with 140 additions and 18 deletions

View File

@ -12,6 +12,21 @@ import net.minecraft.world.World;
* running in, to allow querying the time of day, for example. * running in, to allow querying the time of day, for example.
*/ */
public interface Owner extends Context { public interface Owner extends Context {
/**
* The X coordinate of this machine owner in the world, in block coordinates.
*/
int x();
/**
* The Y coordinate of this machine owner in the world, in block coordinates.
*/
int y();
/**
* The Z coordinate of this machine owner in the world, in block coordinates.
*/
int z();
/** /**
* The world the machine is running in, e.g. if the owner is a tile entity * The world the machine is running in, e.g. if the owner is a tile entity
* this is the world the tile entity lives in. * this is the world the tile entity lives in.

View File

@ -37,5 +37,5 @@
@cpw.mods.fml.common.API( @cpw.mods.fml.common.API(
owner = "OpenComputers|Core", owner = "OpenComputers|Core",
provides = "OpenComputersAPI", provides = "OpenComputersAPI",
apiVersion = "1.4.9") apiVersion = "1.4.10")
package li.cil.oc.api; package li.cil.oc.api;

View File

@ -78,5 +78,6 @@ class Proxy {
FMLCommonHandler.instance().bus().register(EventHandler) FMLCommonHandler.instance().bus().register(EventHandler)
FMLCommonHandler.instance().bus().register(SimpleComponentTickHandler.Instance) FMLCommonHandler.instance().bus().register(SimpleComponentTickHandler.Instance)
MinecraftForge.EVENT_BUS.register(WirelessNetwork) MinecraftForge.EVENT_BUS.register(WirelessNetwork)
MinecraftForge.EVENT_BUS.register(SaveHandler)
} }
} }

View File

@ -0,0 +1,83 @@
package li.cil.oc.common
import java.io
import java.util.logging.Level
import li.cil.oc.{OpenComputers, Settings}
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.world.ChunkCoordIntPair
import net.minecraftforge.common.DimensionManager
import net.minecraftforge.event.ForgeSubscribe
import net.minecraftforge.event.world.WorldEvent
import scala.collection.mutable
// TODO Save all data to an NBT compound and save it as a single file, to improve file I/O performance.
object SaveHandler {
val saveData = mutable.Map.empty[ChunkCoordIntPair, mutable.Map[String, Array[Byte]]]
var cachedNbt = new NBTTagCompound()
def savePath = new io.File(DimensionManager.getCurrentSaveRootDirectory, Settings.savePath + "state")
def scheduleSave(chunk: ChunkCoordIntPair, name: String, data: Array[Byte]) = saveData.synchronized {
if (chunk == null) OpenComputers.log.warning("Cannot save machines with non tile entity owners.")
else saveData.getOrElseUpdate(chunk, mutable.Map.empty[String, Array[Byte]]) += name -> data
}
def load(chunk: ChunkCoordIntPair, name: String): Array[Byte] = {
if (chunk == null) null
else {
val path = savePath
val chunkPath = new io.File(path, s"${chunk.chunkXPos}.${chunk.chunkZPos}")
val file = new io.File(chunkPath, name)
try {
// val bis = new io.BufferedInputStream(new GZIPInputStream(new io.FileInputStream(file)))
val bis = new io.BufferedInputStream(new io.FileInputStream(file))
val bos = new io.ByteArrayOutputStream
val buffer = new Array[Byte](8 * 1024)
var read = 0
do {
read = bis.read(buffer)
if (read > 0) {
bos.write(buffer, 0, read)
}
} while (read >= 0)
bis.close()
bos.toByteArray
}
catch {
case e: io.IOException =>
OpenComputers.log.log(Level.WARNING, "Error loading auxiliary tile entity data.", e)
Array.empty[Byte]
}
}
}
// Used by the native lua state to store kernel and stack data in auxiliary
// files instead of directly in the tile entity data, avoiding potential
// problems with the tile entity data becoming too large.
@ForgeSubscribe
def onWorldSave(e: WorldEvent.Save) = saveData.synchronized {
val path = savePath
path.mkdirs()
for ((chunk, entries) <- saveData) {
val chunkPath = new io.File(path, s"${chunk.chunkXPos}.${chunk.chunkZPos}")
chunkPath.mkdirs()
if (chunkPath.exists && chunkPath.isDirectory) {
for (file <- chunkPath.listFiles()) file.delete()
}
for ((name, data) <- entries) {
val file = new io.File(chunkPath, name)
try {
// val fos = new GZIPOutputStream(new io.FileOutputStream(file))
val fos = new io.BufferedOutputStream(new io.FileOutputStream(file))
fos.write(data)
fos.close()
}
catch {
case e: io.IOException => OpenComputers.log.log(Level.WARNING, s"Error saving auxiliary tile entity data to '${file.getAbsolutePath}.", e)
}
}
}
saveData.clear()
}
}

View File

@ -143,12 +143,22 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B
override def readFromNBT(nbt: NBTTagCompound) { override def readFromNBT(nbt: NBTTagCompound) {
super.readFromNBT(nbt) super.readFromNBT(nbt)
// God, this is so ugly... will need to rework the robot architecture.
// This is required for loading auxiliary data (kernel state), because the
// coordinates in the actual robot won't be set properly, otherwise.
this match {
case proxy: RobotProxy =>
proxy.robot.xCoord = xCoord
proxy.robot.yCoord = yCoord
proxy.robot.zCoord = zCoord
case _ =>
}
computer.load(nbt.getCompoundTag(Settings.namespace + "computer")) computer.load(nbt.getCompoundTag(Settings.namespace + "computer"))
} }
override def writeToNBT(nbt: NBTTagCompound) { override def writeToNBT(nbt: NBTTagCompound) {
super.writeToNBT(nbt) super.writeToNBT(nbt)
if (!new Exception().getStackTrace.exists(_.getClassName.startsWith("mcp.mobius.waila"))) { if (computer != null) {
nbt.setNewCompoundTag(Settings.namespace + "computer", computer.save) nbt.setNewCompoundTag(Settings.namespace + "computer", computer.save)
} }
} }

View File

@ -266,15 +266,13 @@ class Rack extends PowerAcceptor with Hub with PowerBalancer with Inventory with
// Side check for Waila (and other mods that may call this client side). // Side check for Waila (and other mods that may call this client side).
override def writeToNBT(nbt: NBTTagCompound) = if (isServer) { override def writeToNBT(nbt: NBTTagCompound) = if (isServer) {
if (!new Exception().getStackTrace.exists(_.getClassName.startsWith("mcp.mobius.waila"))) { nbt.setNewTagList(Settings.namespace + "servers", servers map {
nbt.setNewTagList(Settings.namespace + "servers", servers map { case Some(server) =>
case Some(server) => val serverNbt = new NBTTagCompound()
val serverNbt = new NBTTagCompound() server.save(serverNbt)
server.save(serverNbt) serverNbt
serverNbt case _ => new NBTTagCompound()
case _ => new NBTTagCompound() })
})
}
super.writeToNBT(nbt) super.writeToNBT(nbt)
nbt.setByteArray(Settings.namespace + "sides", sides.map(_.ordinal.toByte)) nbt.setByteArray(Settings.namespace + "sides", sides.map(_.ordinal.toByte))
nbt.setNewTagList(Settings.namespace + "terminals", terminals.map(t => { nbt.setNewTagList(Settings.namespace + "terminals", terminals.map(t => {

View File

@ -70,6 +70,12 @@ class Server(val rack: tileentity.Rack, val number: Int) extends Owner {
case _ => false case _ => false
} }
override def x = rack.x
override def y = rack.y
override def z = rack.z
override def world = rack.world override def world = rack.world
override def markAsChanged() = rack.markAsChanged() override def markAsChanged() = rack.markAsChanged()

View File

@ -247,7 +247,7 @@ class LuaJLuaArchitecture(val machine: api.machine.Machine) extends Architecture
// Are we a robot? (No this is not a CAPTCHA.) // Are we a robot? (No this is not a CAPTCHA.)
// TODO deprecate this // TODO deprecate this
computer.set("isRobot", (_: Varargs) => LuaValue.valueOf(machine.components.contains("robot"))) computer.set("isRobot", (_: Varargs) => LuaValue.valueOf(machine.components.containsValue("robot")))
computer.set("freeMemory", (_: Varargs) => LuaValue.valueOf(memory / 2)) computer.set("freeMemory", (_: Varargs) => LuaValue.valueOf(memory / 2))

View File

@ -6,11 +6,14 @@ import java.io.{IOException, FileNotFoundException}
import java.util.logging.Level import java.util.logging.Level
import li.cil.oc.api.machine.{Architecture, LimitReachedException, ExecutionResult} import li.cil.oc.api.machine.{Architecture, LimitReachedException, ExecutionResult}
import li.cil.oc.api.network.ComponentConnector import li.cil.oc.api.network.ComponentConnector
import li.cil.oc.common.SaveHandler
import li.cil.oc.server.component
import li.cil.oc.util.ExtendedLuaState.extendLuaState import li.cil.oc.util.ExtendedLuaState.extendLuaState
import li.cil.oc.util.{GameTimeFormatter, LuaStateFactory} import li.cil.oc.util.{GameTimeFormatter, LuaStateFactory}
import li.cil.oc.{api, OpenComputers, server, Settings} import li.cil.oc.{api, OpenComputers, server, Settings}
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import scala.Some import net.minecraft.tileentity.TileEntity
import net.minecraft.world.ChunkCoordIntPair
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
import scala.collection.mutable import scala.collection.mutable
@ -295,7 +298,7 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
// Are we a robot? (No this is not a CAPTCHA.) // Are we a robot? (No this is not a CAPTCHA.)
// TODO deprecate this // TODO deprecate this
lua.pushScalaFunction(lua => { lua.pushScalaFunction(lua => {
lua.pushBoolean(machine.components.contains("robot")) lua.pushBoolean(machine.components.containsValue("robot"))
1 1
}) })
lua.setField(-2, "isRobot") lua.setField(-2, "isRobot")
@ -595,14 +598,20 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
// on. First, clear the stack, meaning the current kernel. // on. First, clear the stack, meaning the current kernel.
lua.setTop(0) lua.setTop(0)
unpersist(nbt.getByteArray("kernel")) val kernel =
if (nbt.hasKey("kernel")) nbt.getByteArray("kernel")
else SaveHandler.load(new ChunkCoordIntPair(machine.owner.x >> 4, machine.owner.z >> 4), node.address + "_kernel")
unpersist(kernel)
if (!lua.isThread(1)) { if (!lua.isThread(1)) {
// This shouldn't really happen, but there's a chance it does if // This shouldn't really happen, but there's a chance it does if
// the save was corrupt (maybe someone modified the Lua files). // the save was corrupt (maybe someone modified the Lua files).
throw new IllegalArgumentException("Invalid kernel.") throw new IllegalArgumentException("Invalid kernel.")
} }
if (state.contains(Machine.State.SynchronizedCall) || state.contains(Machine.State.SynchronizedReturn)) { if (state.contains(Machine.State.SynchronizedCall) || state.contains(Machine.State.SynchronizedReturn)) {
unpersist(nbt.getByteArray("stack")) val stack =
if (nbt.hasKey("stack")) nbt.getByteArray("stack")
else SaveHandler.load(new ChunkCoordIntPair(machine.owner.x >> 4, machine.owner.z >> 4), node.address + "_stack")
unpersist(stack)
if (!(if (state.contains(Machine.State.SynchronizedCall)) lua.isFunction(2) else lua.isTable(2))) { if (!(if (state.contains(Machine.State.SynchronizedCall)) lua.isFunction(2) else lua.isTable(2))) {
// Same as with the above, should not really happen normally, but // Same as with the above, should not really happen normally, but
// could for the same reasons. // could for the same reasons.
@ -629,12 +638,12 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
// Try persisting Lua, because that's what all of the rest depends on. // Try persisting Lua, because that's what all of the rest depends on.
// Save the kernel state (which is always at stack index one). // Save the kernel state (which is always at stack index one).
assert(lua.isThread(1)) assert(lua.isThread(1))
nbt.setByteArray("kernel", persist(1)) SaveHandler.scheduleSave(new ChunkCoordIntPair(machine.owner.x >> 4, machine.owner.z >> 4), node.address + "_kernel", persist(1))
// While in a driver call we have one object on the global stack: either // While in a driver call we have one object on the global stack: either
// the function to call the driver with, or the result of the call. // the function to call the driver with, or the result of the call.
if (state.contains(Machine.State.SynchronizedCall) || state.contains(Machine.State.SynchronizedReturn)) { if (state.contains(Machine.State.SynchronizedCall) || state.contains(Machine.State.SynchronizedReturn)) {
assert(if (state.contains(Machine.State.SynchronizedCall)) lua.isFunction(2) else lua.isTable(2)) assert(if (state.contains(Machine.State.SynchronizedCall)) lua.isFunction(2) else lua.isTable(2))
nbt.setByteArray("stack", persist(2)) SaveHandler.scheduleSave(new ChunkCoordIntPair(machine.owner.x >> 4, machine.owner.z >> 4), node.address + "_stack", persist(2))
} }
nbt.setInteger("kernelMemory", math.ceil(kernelMemory / ramScale).toInt) nbt.setInteger("kernelMemory", math.ceil(kernelMemory / ramScale).toInt)