Added architecture switching on CPUs by shift-right clicking them.

Fixed an issue with synchronized calls potentially throwing limit reached exceptions.
This commit is contained in:
Florian Nücke 2014-10-07 15:12:58 +02:00
parent ca68021166
commit 092bd17a84
10 changed files with 120 additions and 32 deletions

View File

@ -2,6 +2,11 @@ package li.cil.oc.api.machine;
import net.minecraft.nbt.NBTTagCompound;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This interface abstracts away any language specific details for the Machine.
* <p/>
@ -11,12 +16,6 @@ import net.minecraft.nbt.NBTTagCompound;
* Java Lua architecture (using LuaJ).
*/
public interface Architecture {
/**
* A display friendly name of the architecture, mainly intended to be used
* when iterating all {@link li.cil.oc.api.Machine#architectures()}.
*/
String name();
/**
* Used to check if the machine is fully initialized. If this is false no
* signals for detected components will be generated. Avoids duplicate
@ -133,4 +132,16 @@ public interface Architecture {
* @param nbt the tag compound to save to.
*/
void save(NBTTagCompound nbt);
/**
* Architectures can be annotated with this to provide a nice display name.
* <p/>
* This is used when the name of an architecture has to be displayed to the
* user, such as when cycling architectures on a CPU.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
static @interface Name {
String value();
}
}

View File

@ -195,6 +195,7 @@ oc:tooltip.CircuitBoard=Mühsam ernährt sich das Eichhörnchen. Wenn es groß w
oc:tooltip.ControlUnit=Klingt wichtig, ist es auch. Man baut daraus immerhin CPUs. Wie könnte es da nicht wichtig sein.
oc:tooltip.ComponentBus=Diese Erweiterung erlaubt es es Servern, mit noch mehr Komponenten gleichzeitig zu kommunizieren, ähnlich wie CPUs.[nl] Supported components: §f%s§7.
oc:tooltip.CPU=Kernstück eines jeden Computers. Die Taktrate hat einen leichten Schatten, aber was kann man von einer Taschensonnenuhr schon erwarten?[nl] Unterstützte Komponenten: §f%s§7.
oc:tooltip.CPU.Architecture=Architektur: §f%s§7
oc:tooltip.CuttingWire=Wird gebraucht, um Tonblöcke in Leiterplattenform zu bekommen. Vermutlich das ineffizienteste Werkzeug in der Geschichte der Menschheit, da es nach einer Verwendung kaputt geht.
oc:tooltip.DebugCard=Kreativ-Modus-Gegenstand, erlaubt es die Welt zu manipulieren um das Testen zu erleichtern. Verwendung auf eigene Gefahr.
oc:tooltip.Disassembler=Zerlegt Gegenstände in ihre Einzelteile. §lWarnung§7: zurückgewonnene Gegenstände haben eine %s%%-ige Chance beim Extrahieren kaputt zu gehen!

View File

@ -195,6 +195,7 @@ oc:tooltip.CircuitBoard=Now we're getting somewhere. Can be etched to obtain a p
oc:tooltip.ControlUnit=This is the unit that... controls... stuff. You need it to build a CPU. So yeah, totally important.
oc:tooltip.ComponentBus=This expansion allows servers to communicate with more components at the same time, similar to how CPUs do.[nl] Supported components: §f%s§7.
oc:tooltip.CPU=An essential component of all computers. The clock rate is a bit unreliable, but what do you expect when it runs on a pocket sundial?[nl] Supported components: §f%s§7.
oc:tooltip.CPU.Architecture=Architecture: §f%s§7
oc:tooltip.CuttingWire=Used to cut clay blocks into circuit board shape. Breaks after one use, which probably makes it the most inefficient tool ever.
oc:tooltip.DebugCard=Creative mode item, allows manipulating the world to make testing easier. Use at your own peril.
oc:tooltip.Disassembler=Separates items into their original components. §lWarning§7: returned items have a %s%% chance of breaking in the process!

View File

@ -480,14 +480,15 @@ local function isDirect(address, method)
return cachedValue
end
local methods, reason = spcall(component.methods, address)
if methods then
if not methods then
return false
end
for name, info in pairs(methods) do
if name == method then
directCache[cacheKey] = info.direct
return info.direct
end
end
end
error("no such method", 1)
end

View File

@ -62,6 +62,7 @@ class Proxy {
api.Machine.LuaArchitecture =
if (LuaStateFactory.isAvailable && !Settings.get.forceLuaJ) classOf[NativeLuaArchitecture]
else classOf[LuaJLuaArchitecture]
api.Machine.add(api.Machine.LuaArchitecture)
api.Network.instance = network.Network
if (Mods.ForgeMultipart.isAvailable) {

View File

@ -1,6 +1,19 @@
package li.cil.oc.common.item
import java.util
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.machine.Architecture
import li.cil.oc.util.Tooltip
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.ChatComponentTranslation
import net.minecraft.world.World
import scala.collection.convert.WrapAsScala._
import scala.language.existentials
class CPU(val parent: Delegator, val tier: Int) extends Delegate with ItemTier {
override val unlocalizedName = super.unlocalizedName + tier
@ -8,4 +21,49 @@ class CPU(val parent: Delegator, val tier: Int) extends Delegate with ItemTier {
override protected def tooltipName = Option(super.unlocalizedName)
override protected def tooltipData = Seq(Settings.get.cpuComponentSupport(tier))
override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]) {
(if (stack.hasTagCompound) {
Option(stack.getTagCompound.getString(Settings.namespace + "archName"))
}
else {
val architectures = allArchitectures
architectures.headOption.map(_._2)
}) match {
case Some(archName) => tooltip.addAll(Tooltip.get("CPU.Architecture", archName))
case _ => // No architecture.
}
}
override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer) = {
if (player.isSneaking) {
if (!world.isRemote) {
val architectures = allArchitectures
if (architectures.length > 0) {
val currentIndex = if (stack.hasTagCompound) {
val currentArch = stack.getTagCompound.getString(Settings.namespace + "archClass")
architectures.indexWhere(_._1.getName == currentArch)
}
else {
stack.setTagCompound(new NBTTagCompound())
-1
}
val index = (currentIndex + 1) % architectures.length
val (archClass, archName) = architectures(index)
stack.getTagCompound.setString(Settings.namespace + "archClass", archClass.getName)
stack.getTagCompound.setString(Settings.namespace + "archName", archName)
player.addChatMessage(new ChatComponentTranslation(Settings.namespace + "tooltip.CPU.Architecture", archName))
}
player.swingItem()
}
}
stack
}
private def allArchitectures = api.Machine.architectures.map { arch =>
arch.getAnnotation(classOf[Architecture.Name]) match {
case annotation: Architecture.Name => (arch, annotation.value)
case _ => (arch, arch.getSimpleName)
}
}.toArray
}

View File

@ -1,15 +1,18 @@
package li.cil.oc.server.driver.item
import li.cil.oc.OpenComputers
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.Machine
import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.driver.item.Processor
import li.cil.oc.api.machine.Architecture
import li.cil.oc.common.Slot
import li.cil.oc.common.init.Items
import li.cil.oc.common.item
import net.minecraft.item.ItemStack
import scala.collection.convert.WrapAsScala._
object CPU extends Item with Processor {
override def worksWith(stack: ItemStack) =
isOneOf(stack, api.Items.get("cpu1"), api.Items.get("cpu2"), api.Items.get("cpu3"))
@ -30,5 +33,15 @@ object CPU extends Item with Processor {
case _ => 0
}
override def architecture(stack: ItemStack) = Machine.LuaArchitecture
override def architecture(stack: ItemStack) = {
if (stack.hasTagCompound) {
val archClass = stack.getTagCompound.getString(Settings.namespace + "archClass")
try Class.forName(archClass).asSubclass(classOf[Architecture]) catch {
case t: Throwable =>
OpenComputers.log.warn("Failed getting class for CPU architecture.", t)
null
}
}
else api.Machine.architectures.headOption.orNull
}
}

View File

@ -58,6 +58,9 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
@volatile private var callBudget = 0.0
// We want to ignore the call limit in synchronized calls to avoid errors.
private var inSynchronizedCall = false
// ----------------------------------------------------------------------- //
var worldTime = 0L // Game-world time for os.time().
@ -268,7 +271,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
case _ => throw new NoSuchMethodException()
}
private def checkLimit(limit: Int) {
private def checkLimit(limit: Int): Unit = if (!inSynchronizedCall) {
val callCost = math.max(1.0 / limit, 0.001)
if (callCost >= callBudget) {
throw new LimitReachedException()
@ -421,16 +424,13 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
}
// Perform a synchronized call (message sending).
case Machine.State.SynchronizedCall =>
// Reset direct call budget again, just to be on the safe side...
// Theoretically it'd be possible for the executor to do some direct
// calls between the clear and the state check, which could in turn
// make this synchronized call fail due the limit still being maxed.
callBudget = maxCallBudget
// We switch into running state, since we'll behave as though the call
// were performed from our executor thread.
switchTo(Machine.State.Running)
try {
inSynchronizedCall = true
architecture.runSynchronized()
inSynchronizedCall = false
// Check if the callback called pause() or stop().
state.top match {
case Machine.State.Running =>
@ -451,6 +451,9 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
OpenComputers.log.warn("Faulty architecture implementation for synchronized calls.", e)
crash("gui.Error.InternalError")
}
finally {
inSynchronizedCall = false
}
assert(state.top != Machine.State.Running)
case _ => // Nothing special to do, just avoid match errors.
@ -871,7 +874,8 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
}
object Machine extends MachineAPI {
val checked = mutable.Set.empty[Class[_ <: Architecture]]
// Keep registration order, to allow deterministic iteration of the architectures.
val checked = mutable.LinkedHashSet.empty[Class[_ <: Architecture]]
override def add(architecture: Class[_ <: Architecture]) {
if (!checked.contains(architecture)) {
@ -885,7 +889,7 @@ object Machine extends MachineAPI {
}
}
override def architectures() = scala.collection.convert.WrapAsJava.asJavaIterable(checked)
override def architectures = scala.collection.convert.WrapAsJava.asJavaIterable(checked)
override def create(host: MachineHost) = new Machine(host)

View File

@ -4,6 +4,9 @@ import java.io.FileNotFoundException
import java.io.IOException
import com.google.common.base.Strings
import li.cil.oc.OpenComputers
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.machine.Architecture
import li.cil.oc.api.machine.ExecutionResult
import li.cil.oc.api.machine.LimitReachedException
@ -11,12 +14,10 @@ import li.cil.oc.common.SaveHandler
import li.cil.oc.server.machine.Machine
import li.cil.oc.util.ExtendedLuaState.extendLuaState
import li.cil.oc.util.LuaStateFactory
import li.cil.oc.OpenComputers
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.repack.com.naef.jnlua._
import net.minecraft.nbt.NBTTagCompound
@Architecture.Name("Lua")
class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architecture {
private[machine] var lua: LuaState = null
@ -127,8 +128,6 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
// ----------------------------------------------------------------------- //
override def name() = "Lua"
override def isInitialized = kernelMemory > 0
override def recomputeMemory() = Option(lua) match {

View File

@ -4,19 +4,20 @@ import java.io.FileNotFoundException
import java.io.IOException
import com.google.common.base.Strings
import li.cil.oc.OpenComputers
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.machine.Architecture
import li.cil.oc.api.machine.ExecutionResult
import li.cil.oc.api.machine.LimitReachedException
import li.cil.oc.server.machine.Machine
import li.cil.oc.util.ScalaClosure
import li.cil.oc.util.ScalaClosure._
import li.cil.oc.OpenComputers
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.repack.org.luaj.vm2._
import li.cil.repack.org.luaj.vm2.lib.jse.JsePlatform
import net.minecraft.nbt.NBTTagCompound
@Architecture.Name("LuaJ")
class LuaJLuaArchitecture(val machine: api.machine.Machine) extends Architecture {
private[machine] var lua: Globals = _
@ -90,8 +91,6 @@ class LuaJLuaArchitecture(val machine: api.machine.Machine) extends Architecture
// ----------------------------------------------------------------------- //
override def name() = "LuaJ"
override def isInitialized = doneWithInitRun
override def recomputeMemory() = memory = machine.host.installedMemory