Made architecture dynamically defined via the host, reads arch from installed CPU.

This commit is contained in:
Florian Nücke 2014-09-20 09:20:59 +02:00
parent 691fbfd0c9
commit c4989e44e7
18 changed files with 136 additions and 91 deletions

View File

@ -46,24 +46,7 @@ public final class Machine {
}
/**
* Creates a new machine using the specified architecture.
* <p/>
* You are responsible for calling update and save / load functions on the
* machine for it to work correctly.
*
* @param host the owner object of the machine, providing context.
* @param architecture the architecture to use for running code on the machine.
* @return the newly created machine.
* @throws IllegalArgumentException if the specified architecture is invalid.
*/
public static li.cil.oc.api.machine.Machine create(MachineHost host, Class<? extends Architecture> architecture) {
if (instance != null)
return instance.create(host, architecture);
return null;
}
/**
* Creates a new machine using the default architecture (Lua).
* Creates a new machine for the specified host.
* <p/>
* You are responsible for calling update and save / load functions on the
* machine for it to work correctly.
@ -73,7 +56,7 @@ public final class Machine {
*/
public static li.cil.oc.api.machine.Machine create(MachineHost host) {
if (instance != null)
return instance.create(host, LuaArchitecture);
return instance.create(host);
return null;
}

View File

@ -20,7 +20,7 @@ public interface MachineAPI {
* A list of all <em>registered</em> architectures.
* <p/>
* Note that registration is optional, although automatic when calling
* {@link #create(li.cil.oc.api.machine.MachineHost, Class)} with a not yet
* {@link #create(li.cil.oc.api.machine.MachineHost)} with a not yet
* registered architecture. What this means is that unless a mod providing
* a custom architecture also registers it, you may not see it in this list
* until it also created a new machine using that architecture.
@ -28,15 +28,14 @@ public interface MachineAPI {
Iterable<Class<? extends Architecture>> architectures();
/**
* Creates a new machine using the specified architecture.
* Creates a new machine for the specified host.
* <p/>
* You are responsible for calling update and save / load functions on the
* machine for it to work correctly.
*
* @param host the owner object of the machine, providing context.
* @param architecture the architecture to use for running code on the machine.
* @param host the owner object of the machine, providing context.
* @return the newly created machine.
* @throws IllegalArgumentException if the specified architecture is invalid.
*/
Machine create(MachineHost host, Class<? extends Architecture> architecture);
Machine create(MachineHost host);
}

View File

@ -29,7 +29,8 @@ public interface Processor extends Item {
* as computer cases, server racks and robots, it my not be true for third-
* party computers).
*
* @param stack the stack representing the CPU to get the architecture for.
* @return the type of this CPU's architecture.
*/
Class<? extends Architecture> architecture();
Class<? extends Architecture> architecture(ItemStack stack);
}

View File

@ -24,11 +24,28 @@ public interface Machine extends ManagedEnvironment, Context {
* <p/>
* This is what actually evaluates code running on the machine, where the
* machine class itself serves as a scheduler.
* <p/>
* This may be <tt>null</tt>, for example when the hosting computer has
* no CPU installed.
*
* @return the architecture of this machine.
*/
Architecture architecture();
/**
* Get the address of the file system component from which to try to boot.
* <p/>
* The underlying architecture may choose to ignore this setting.
*/
String getBootAddress();
/**
* Set the address of the file system component from which to try to boot.
*
* @param value the new address to try to boot from.
*/
void setBootAddress(String value);
/**
* The list of components attached to this machine.
* <p/>

View File

@ -34,6 +34,17 @@ public interface MachineHost extends Context {
*/
World world();
/**
* Get the architecture to use in the hosted machine.
* <p/>
* This can be a static architecture type, but will usually be based on the
* CPU installed in the host (for example, this is true for computer cases,
* servers, robots and tablets).
*
* @return the architecture of the installed CPU, or <tt>null</tt>.
*/
Class<? extends Architecture> cpuArchitecture();
/**
* The amount of memory (RAM) made available to the machine, in bytes.
* <p/>

View File

@ -108,7 +108,7 @@ class RobotProxy extends RedstoneAware with SpecialBlock {
val bounds = getCollisionBoundingBoxFromPool(world, x, y, z)
world.getTileEntity(x, y, z) match {
case proxy: tileentity.RobotProxy if proxy.robot.animationTicksLeft <= 0 && bounds.isVecInside(origin) => null
case _ => super.collisionRayTrace(world, x, y, z, origin, direction)
case _ => super.intersect(world, x, y, z, origin, direction)
}
}

View File

@ -7,12 +7,12 @@ import com.google.common.cache.{CacheBuilder, RemovalListener, RemovalNotificati
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.common.gameevent.TickEvent.{ClientTickEvent, ServerTickEvent}
import cpw.mods.fml.relauncher.{Side, SideOnly}
import li.cil.oc.api.Machine
import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.machine.MachineHost
import li.cil.oc.api.{Driver, Machine}
import li.cil.oc.api.driver.{Processor, EnvironmentHost}
import li.cil.oc.api.machine.{Architecture, MachineHost}
import li.cil.oc.api.network.{Connector, Message, Node}
import li.cil.oc.api.tileentity.Rotatable
import li.cil.oc.common.GuiType
import li.cil.oc.common.{Slot, GuiType}
import li.cil.oc.common.inventory.ComponentInventory
import li.cil.oc.util.ItemUtils.TabletData
import li.cil.oc.util.RotationHelper
@ -184,6 +184,17 @@ class TabletWrapper(var stack: ItemStack, var holder: Entity) extends ComponentI
override def world = holder.worldObj
override def cpuArchitecture: Class[_ <: Architecture] = {
for (i <- 0 until getSizeInventory if isComponentSlot(i)) Option(getStackInSlot(i)) match {
case Some(s) => Option(Driver.driverFor(s)) match {
case Some(driver: Processor) if driver.slot(s) == Slot.CPU => return driver.architecture(s)
case _ =>
}
case _ =>
}
null
}
override def installedMemory = items.foldLeft(0)((acc, itemOption) => acc + (itemOption match {
case Some(item) => Option(api.Driver.driverFor(item)) match {
case Some(driver: api.driver.Memory) => driver.amount(item)

View File

@ -60,13 +60,7 @@ class Case(var tier: Int) extends traits.PowerAcceptor with traits.Computer with
override def componentSlot(address: String) = components.indexWhere(_.exists(env => env.node != null && env.node.address == address))
def hasCPU = items.exists {
case Some(stack) => Option(Driver.driverFor(stack)) match {
case Some(driver) => driver.slot(stack) == Slot.CPU
case _ => false
}
case _ => false
}
def hasCPU = cpuArchitecture != null
// ----------------------------------------------------------------------- //
@ -98,15 +92,23 @@ class Case(var tier: Int) extends traits.PowerAcceptor with traits.Computer with
override protected def onItemAdded(slot: Int, stack: ItemStack) {
super.onItemAdded(slot, stack)
if (InventorySlots.computer(tier)(slot).slot == Slot.Floppy) {
common.Sound.playDiskInsert(this)
if (isServer) {
if (InventorySlots.computer(tier)(slot).slot == Slot.Floppy) {
common.Sound.playDiskInsert(this)
}
}
}
override protected def onItemRemoved(slot: Int, stack: ItemStack) {
super.onItemRemoved(slot, stack)
if (InventorySlots.computer(tier)(slot).slot == Slot.Floppy) {
common.Sound.playDiskEject(this)
if (isServer) {
val slotType = InventorySlots.computer(tier)(slot).slot
if (slotType == Slot.Floppy) {
common.Sound.playDiskEject(this)
}
if (slotType == Slot.CPU) {
stop()
}
}
}

View File

@ -323,7 +323,7 @@ class Robot extends traits.Computer with traits.PowerInformation with tileentity
info.load(nbt)
updateInventorySize()
computer.architecture.recomputeMemory()
Option(computer.architecture).foreach(_.recomputeMemory())
bot.load(nbt.getCompoundTag(Settings.namespace + "robot"))
if (nbt.hasKey(Settings.namespace + "owner")) {

View File

@ -2,11 +2,13 @@ package li.cil.oc.common.tileentity.traits
import cpw.mods.fml.common.Optional
import cpw.mods.fml.relauncher.{Side, SideOnly}
import li.cil.oc.api.Machine
import li.cil.oc.api.machine.MachineHost
import li.cil.oc.api.driver.Processor
import li.cil.oc.api.machine.{Architecture, MachineHost}
import li.cil.oc.api.network.Node
import li.cil.oc.api.tileentity.Analyzable
import li.cil.oc.api.{Driver, Machine}
import li.cil.oc.client.Sound
import li.cil.oc.common.Slot
import li.cil.oc.common.tileentity.RobotProxy
import li.cil.oc.server.{driver, PacketSender => ServerPacketSender}
import li.cil.oc.util.ExtendedNBT._
@ -68,6 +70,17 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B
// ----------------------------------------------------------------------- //
override def cpuArchitecture: Class[_ <: Architecture] = {
for (i <- 0 until getSizeInventory if isComponentSlot(i)) Option(getStackInSlot(i)) match {
case Some(s) => Option(Driver.driverFor(s)) match {
case Some(driver: Processor) if driver.slot(s) == Slot.CPU => return driver.architecture(s)
case _ =>
}
case _ =>
}
null
}
override def markAsChanged() = hasChanged = true
override def installedComponents = components collect {
@ -176,7 +189,7 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B
override def markDirty() {
super.markDirty()
if (isServer) {
computer.architecture.recomputeMemory()
Option(computer.architecture).foreach(_.recomputeMemory())
isOutputEnabled = hasRedstoneCard
isAbstractBusAvailable = hasAbstractBusCard
}

View File

@ -1,7 +1,8 @@
package li.cil.oc.server.component
import li.cil.oc.Items
import li.cil.oc.api.machine.MachineHost
import li.cil.oc.api.driver.Processor
import li.cil.oc.api.machine.{Architecture, MachineHost}
import li.cil.oc.api.network.{Message, Node}
import li.cil.oc.api.{Driver, Machine, driver}
import li.cil.oc.common.inventory.{ComponentInventory, ServerInventory}
@ -40,6 +41,17 @@ class Server(val rack: tileentity.ServerRack, val number: Int) extends MachineHo
// ----------------------------------------------------------------------- //
override def cpuArchitecture: Class[_ <: Architecture] = {
for (i <- 0 until inventory.getSizeInventory if inventory.isComponentSlot(i)) Option(inventory.getStackInSlot(i)) match {
case Some(s) => Option(Driver.driverFor(s)) match {
case Some(driver: Processor) if driver.slot(s) == Slot.CPU => return driver.architecture(s)
case _ =>
}
case _ =>
}
null
}
override def installedMemory = inventory.items.foldLeft(0)((sum, stack) => sum + (stack match {
case Some(item) => Option(Driver.driverFor(item)) match {
case Some(driver: driver.Memory) => driver.amount(item)

View File

@ -25,5 +25,5 @@ object CPU extends Item with driver.Processor {
case _ => 0
}
override def architecture = Machine.LuaArchitecture
override def architecture(stack: ItemStack) = Machine.LuaArchitecture
}

View File

@ -25,5 +25,5 @@ object ComponentBus extends Item with driver.Processor {
case _ => 0
}
override def architecture = null
override def architecture(stack: ItemStack) = null
}

View File

@ -1,6 +1,5 @@
package li.cil.oc.server.machine
import java.lang.reflect.Constructor
import java.util.concurrent.TimeUnit
import li.cil.oc.api.detail.MachineAPI
@ -24,7 +23,7 @@ import net.minecraftforge.common.util.Constants.NBT
import scala.Array.canBuildFrom
import scala.collection.mutable
class Machine(val host: MachineHost, constructor: Constructor[_ <: Architecture]) extends ManagedComponent with machine.Machine with Runnable {
class Machine(val host: MachineHost) extends ManagedComponent with machine.Machine with Runnable {
val node = Network.newNode(this, Visibility.Network).
withComponent("computer", Visibility.Neighbors).
withConnector(Settings.get.bufferComputer).
@ -35,7 +34,9 @@ class Machine(val host: MachineHost, constructor: Constructor[_ <: Architecture]
fromMemory(Settings.get.tmpSize * 1024), "tmpfs"))
} else None
val architecture = constructor.newInstance(this)
var architecture: Architecture = _
private var bootAddress = ""
private[machine] val state = mutable.Stack(Machine.State.Stopped)
@ -71,6 +72,10 @@ class Machine(val host: MachineHost, constructor: Constructor[_ <: Architecture]
// ----------------------------------------------------------------------- //
override def getBootAddress = bootAddress
override def setBootAddress(value: String) = bootAddress = Option(value).map(_.take(36)).getOrElse("")
override def components = scala.collection.convert.WrapAsJava.mapAsJavaMap(_components)
def componentCount = _components.count {
@ -115,12 +120,12 @@ class Machine(val host: MachineHost, constructor: Constructor[_ <: Architecture]
case Machine.State.Stopped =>
processAddedComponents()
verifyComponents()
if (componentCount > host.maxComponents) {
crash(host match {
case t: tileentity.Case if !t.hasCPU => "gui.Error.NoCPU"
case s: server.component.Server if !s.hasCPU => "gui.Error.NoCPU"
case _ => "gui.Error.ComponentOverflow"
})
if (host.cpuArchitecture() == null) {
crash("gui.Error.NoCPU")
false
}
else if (componentCount > host.maxComponents) {
crash("gui.Error.ComponentOverflow")
false
}
else if (host.maxComponents == 0) {
@ -481,7 +486,7 @@ class Machine(val host: MachineHost, constructor: Constructor[_ <: Architecture]
if (node == this.node) {
_components += this.node.address -> this.node.name
tmp.foreach(fs => node.connect(fs.node))
architecture.onConnect()
Option(architecture).foreach(_.onConnect())
}
else {
node match {
@ -531,7 +536,7 @@ class Machine(val host: MachineHost, constructor: Constructor[_ <: Architecture]
_components.synchronized(_components += component.address -> component.name)
// Skip the signal if we're not initialized yet, since we'd generate a
// duplicate in the startup script otherwise.
if (architecture.isInitialized) {
if (architecture != null && architecture.isInitialized) {
signal("component_added", component.address, component.name)
}
}
@ -568,6 +573,8 @@ class Machine(val host: MachineHost, constructor: Constructor[_ <: Architecture]
super.load(nbt)
bootAddress = nbt.getString("bootAddress")
state.pushAll(nbt.getIntArray("state").reverse.map(Machine.State(_)))
nbt.getTagList("users", NBT.TAG_STRING).foreach((list, index) => _users += list.getStringTagAt(index))
if (nbt.hasKey("message")) {
@ -626,10 +633,6 @@ class Machine(val host: MachineHost, constructor: Constructor[_ <: Architecture]
else {
// Clean up in case we got a weird state stack.
close()
// Architectures must check for "isRunning" themselves, to get a chance
// to load data their APIs stored that should persist across runs.
architecture.load(nbt)
}
}
@ -641,6 +644,10 @@ class Machine(val host: MachineHost, constructor: Constructor[_ <: Architecture]
super.save(nbt)
if (bootAddress != null) {
nbt.setString("bootAddress", bootAddress)
}
// Make sure the component list is up-to-date.
processAddedComponents()
@ -702,6 +709,14 @@ class Machine(val host: MachineHost, constructor: Constructor[_ <: Architecture]
// ----------------------------------------------------------------------- //
private def init(): Boolean = {
// Recreate architecture if necessary.
val hostArchitecture = host.cpuArchitecture
if (architecture == null || architecture.getClass != hostArchitecture) {
if (hostArchitecture == null) return false
architecture = hostArchitecture.getConstructor(classOf[machine.Machine]).newInstance(this)
if (node.network != null) architecture.onConnect()
}
// Reset error state.
message = None
@ -729,7 +744,7 @@ class Machine(val host: MachineHost, constructor: Constructor[_ <: Architecture]
if (state.size == 0 || state.top != Machine.State.Stopped) {
state.clear()
state.push(Machine.State.Stopped)
architecture.close()
Option(architecture).foreach(_.close())
signals.clear()
uptime = 0
cpuTotal = 0
@ -866,10 +881,7 @@ object Machine extends MachineAPI {
override def architectures() = scala.collection.convert.WrapAsJava.asJavaIterable(checked)
override def create(host: MachineHost, architecture: Class[_ <: Architecture]) = {
add(architecture)
new Machine(host, architecture.getConstructor(classOf[machine.Machine]))
}
override def create(host: MachineHost) = new Machine(host)
/** Possible states of the computer, and in particular its executor. */
private[machine] object State extends Enumeration {

View File

@ -39,7 +39,7 @@ class ComputerAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) {
// Get/set address of boot device.
lua.pushScalaFunction(lua => {
owner.bootAddress match {
owner.machine.getBootAddress match {
case "" => lua.pushNil()
case address => lua.pushString(address)
}
@ -48,8 +48,8 @@ class ComputerAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) {
lua.setField(-2, "getBootAddress")
lua.pushScalaFunction(lua => {
if (lua.isNoneOrNil(1)) owner.bootAddress = ""
else owner.bootAddress = lua.checkString(1).take(36)
if (lua.isNoneOrNil(1)) owner.machine.setBootAddress("")
else owner.machine.setBootAddress(lua.checkString(1).take(36))
0
})
lua.setField(-2, "setBootAddress")

View File

@ -19,8 +19,6 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
private[machine] val ramScale = if (LuaStateFactory.is64Bit) Settings.get.ramScaleFor64Bit else 1.0
private[machine] var bootAddress = ""
private val persistence = new PersistenceAPI(this)
private val apis = Array(
@ -314,8 +312,6 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
private def state = machine.asInstanceOf[Machine].state
override def load(nbt: NBTTagCompound) {
bootAddress = nbt.getString("bootAddress")
if (!machine.isRunning) return
// Unlimit memory use while unpersisting.
@ -363,10 +359,6 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
}
override def save(nbt: NBTTagCompound) {
if (bootAddress != null) {
nbt.setString("bootAddress", bootAddress)
}
// Unlimit memory while persisting.
if (Settings.get.limitMemory) {
lua.setTotalMemory(Integer.MAX_VALUE)

View File

@ -27,14 +27,14 @@ class ComputerAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) {
})
// Get/set address of boot device.
computer.set("getBootAddress", (_: Varargs) => owner.bootAddress match {
computer.set("getBootAddress", (_: Varargs) => owner.machine.getBootAddress match {
case "" => LuaValue.NIL
case address => LuaValue.valueOf(address)
})
computer.set("setBootAddress", (args: Varargs) => {
if (args.isnoneornil(1)) owner.bootAddress = ""
else owner.bootAddress = args.checkjstring(1).take(36)
if (args.isnoneornil(1)) owner.machine.setBootAddress("")
else owner.machine.setBootAddress(args.checkjstring(1).take(36))
LuaValue.NIL
})

View File

@ -25,8 +25,6 @@ class LuaJLuaArchitecture(val machine: api.machine.Machine) extends Architecture
private[machine] var memory = 0
private[machine] var bootAddress = ""
private val apis = Array(
new ComponentAPI(this),
new ComputerAPI(this),
@ -236,17 +234,11 @@ class LuaJLuaArchitecture(val machine: api.machine.Machine) extends Architecture
// ----------------------------------------------------------------------- //
override def load(nbt: NBTTagCompound) {
bootAddress = nbt.getString("bootAddress")
if (machine.isRunning) {
machine.stop()
machine.start()
}
}
override def save(nbt: NBTTagCompound) {
if (bootAddress != null) {
nbt.setString("bootAddress", bootAddress)
}
}
override def save(nbt: NBTTagCompound) {}
}