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 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. * This interface abstracts away any language specific details for the Machine.
* <p/> * <p/>
@ -11,12 +16,6 @@ import net.minecraft.nbt.NBTTagCompound;
* Java Lua architecture (using LuaJ). * Java Lua architecture (using LuaJ).
*/ */
public interface Architecture { 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 * Used to check if the machine is fully initialized. If this is false no
* signals for detected components will be generated. Avoids duplicate * signals for detected components will be generated. Avoids duplicate
@ -133,4 +132,16 @@ public interface Architecture {
* @param nbt the tag compound to save to. * @param nbt the tag compound to save to.
*/ */
void save(NBTTagCompound nbt); 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.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.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=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.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.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! 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.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.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=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.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.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! 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 return cachedValue
end end
local methods, reason = spcall(component.methods, address) local methods, reason = spcall(component.methods, address)
if methods then if not methods then
return false
end
for name, info in pairs(methods) do for name, info in pairs(methods) do
if name == method then if name == method then
directCache[cacheKey] = info.direct directCache[cacheKey] = info.direct
return info.direct return info.direct
end end
end end
end
error("no such method", 1) error("no such method", 1)
end end

View File

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

View File

@ -1,6 +1,19 @@
package li.cil.oc.common.item package li.cil.oc.common.item
import java.util
import li.cil.oc.Settings 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 { class CPU(val parent: Delegator, val tier: Int) extends Delegate with ItemTier {
override val unlocalizedName = super.unlocalizedName + tier 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 tooltipName = Option(super.unlocalizedName)
override protected def tooltipData = Seq(Settings.get.cpuComponentSupport(tier)) 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 package li.cil.oc.server.driver.item
import li.cil.oc.OpenComputers
import li.cil.oc.Settings import li.cil.oc.Settings
import li.cil.oc.api import li.cil.oc.api
import li.cil.oc.api.Machine
import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.driver.item.Processor 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.Slot
import li.cil.oc.common.init.Items import li.cil.oc.common.init.Items
import li.cil.oc.common.item import li.cil.oc.common.item
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import scala.collection.convert.WrapAsScala._
object CPU extends Item with Processor { object CPU extends Item with Processor {
override def worksWith(stack: ItemStack) = override def worksWith(stack: ItemStack) =
isOneOf(stack, api.Items.get("cpu1"), api.Items.get("cpu2"), api.Items.get("cpu3")) 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 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 @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(). 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() 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) val callCost = math.max(1.0 / limit, 0.001)
if (callCost >= callBudget) { if (callCost >= callBudget) {
throw new LimitReachedException() throw new LimitReachedException()
@ -421,16 +424,13 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
} }
// Perform a synchronized call (message sending). // Perform a synchronized call (message sending).
case Machine.State.SynchronizedCall => 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 // We switch into running state, since we'll behave as though the call
// were performed from our executor thread. // were performed from our executor thread.
switchTo(Machine.State.Running) switchTo(Machine.State.Running)
try { try {
inSynchronizedCall = true
architecture.runSynchronized() architecture.runSynchronized()
inSynchronizedCall = false
// Check if the callback called pause() or stop(). // Check if the callback called pause() or stop().
state.top match { state.top match {
case Machine.State.Running => 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) OpenComputers.log.warn("Faulty architecture implementation for synchronized calls.", e)
crash("gui.Error.InternalError") crash("gui.Error.InternalError")
} }
finally {
inSynchronizedCall = false
}
assert(state.top != Machine.State.Running) assert(state.top != Machine.State.Running)
case _ => // Nothing special to do, just avoid match errors. 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 { 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]) { override def add(architecture: Class[_ <: Architecture]) {
if (!checked.contains(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) override def create(host: MachineHost) = new Machine(host)

View File

@ -4,6 +4,9 @@ import java.io.FileNotFoundException
import java.io.IOException import java.io.IOException
import com.google.common.base.Strings 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.Architecture
import li.cil.oc.api.machine.ExecutionResult import li.cil.oc.api.machine.ExecutionResult
import li.cil.oc.api.machine.LimitReachedException 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.server.machine.Machine
import li.cil.oc.util.ExtendedLuaState.extendLuaState import li.cil.oc.util.ExtendedLuaState.extendLuaState
import li.cil.oc.util.LuaStateFactory 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 li.cil.repack.com.naef.jnlua._
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
@Architecture.Name("Lua")
class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architecture { class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architecture {
private[machine] var lua: LuaState = null 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 isInitialized = kernelMemory > 0
override def recomputeMemory() = Option(lua) match { override def recomputeMemory() = Option(lua) match {

View File

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