mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-18 03:36:47 -04:00
localized computer error messages and added explicit message when no cpu is installed, closes #96; cleaned up internet card's http logic a bit (since it should only ever be used by one computer at a time anyway it can be simpler than it was), and trying to persist its state (i.e. http request gets restarted after load automatically if it was running)
This commit is contained in:
parent
31f373de80
commit
94c3cc2286
@ -76,8 +76,15 @@ oc:gui.Analyzer.StoredEnergy=§6Gespeicherte Energie§f: %s
|
||||
oc:gui.Analyzer.TotalEnergy=§6Insgesamt gespeicherte Energie§f: %s
|
||||
oc:gui.Analyzer.Users=§6Benutzer§f: %s
|
||||
oc:gui.Chat.WarningLuaFallback=Die native Lua-Implementierung ist nicht verfügbar. Computer können ihren Ausführungszustand nicht speichern. Sie werden automatisch neu starten, sobald ein Chunk neu geladen wird.
|
||||
oc:gui.chat.WarningPower=Universal Electricity 3 ist nicht verfügbar. Computer, Bildschirme und alle anderen Komponenten werden §lkeine§f Energie benötigen. UE3 wird auch benötigt, um zusätzlich BuildCraft, IndustrialCraft2 und Thermal Expansion zu unterstützen.
|
||||
oc:gui.chat.WarningProjectRed=Die verwendete Version von Project: Red ist nicht mit OpenComputers kompatibel. Aktualisiere bitte deine Version von Project: Red.
|
||||
oc:gui.Chat.WarningPower=Universal Electricity 3 ist nicht verfügbar. Computer, Bildschirme und alle anderen Komponenten werden §lkeine§f Energie benötigen. UE3 wird auch benötigt, um zusätzlich BuildCraft, IndustrialCraft2 und Thermal Expansion zu unterstützen.
|
||||
oc:gui.Chat.WarningProjectRed=Die verwendete Version von Project: Red ist nicht mit OpenComputers kompatibel. Aktualisiere bitte deine Version von Project: Red.
|
||||
oc:gui.Error.ComponentOverflow=Zu viele Komponenten sind mit dem Computer verbunden.
|
||||
oc:gui.Error.DaylightCycle=Computer funktionieren nicht, solange die Zeit angehalten ist (Spielregel doDaylightCycle ist aus).
|
||||
oc:gui.Error.InternalError=Interner Fehler, bitte sieh in der Logdatei nach. Das ist wahrscheinlich ein Bug.
|
||||
oc:gui.Error.NoCPU=Im Computer ist keine CPU installiert.
|
||||
oc:gui.Error.NoEnergy=Nicht genug Energie.
|
||||
oc:gui.Error.NoRAM=Im Computer ist kein RAM installiert.
|
||||
oc:gui.Error.OutOfMemory=Nicht genug Arbeitsspeicher.
|
||||
oc:gui.Robot.Power=Energie
|
||||
oc:gui.Robot.TurnOff=Ausschalten
|
||||
oc:gui.Robot.TurnOn=Einschalten
|
||||
|
@ -76,8 +76,15 @@ oc:gui.Analyzer.StoredEnergy=§6Stored energy§f: %s
|
||||
oc:gui.Analyzer.TotalEnergy=§6Total stored energy§f: %s
|
||||
oc:gui.Analyzer.Users=§6Users§f: %s
|
||||
oc:gui.Chat.WarningLuaFallback=Native Lua libraries are not available, computers will not be able to persist their state. They will reboot on chunk reloads.
|
||||
oc:gui.chat.WarningPower=Universal Electricity 3 is not available. Computers, screens and all other components will §lnot§f require energy. Note that UE3 is also used to additionally support BuildCraft, IndustrialCraft2 and Thermal Expansion.
|
||||
oc:gui.chat.WarningProjectRed=You are using a version of Project: Red that is incompatible with OpenComputers. Try updating your version of Project: Red.
|
||||
oc:gui.Chat.WarningPower=Universal Electricity 3 is not available. Computers, screens and all other components will §lnot§f require energy. Note that UE3 is also used to additionally support BuildCraft, IndustrialCraft2 and Thermal Expansion.
|
||||
oc:gui.Chat.WarningProjectRed=You are using a version of Project: Red that is incompatible with OpenComputers. Try updating your version of Project: Red.
|
||||
oc:gui.Error.ComponentOverflow=Too many components connected to the computer.
|
||||
oc:gui.Error.DaylightCycle=Computers don't work while time is frozen (gamerule doDaylightCycle is false).
|
||||
oc:gui.Error.InternalError=Internal error, please see the log file. This is probably a bug.
|
||||
oc:gui.Error.NoCPU=No CPU is installed in the computer.
|
||||
oc:gui.Error.NoEnergy=Not enough energy.
|
||||
oc:gui.Error.NoRAM=No RAM is installed in the computer.
|
||||
oc:gui.Error.OutOfMemory=Out of memory.
|
||||
oc:gui.Robot.Power=Energy
|
||||
oc:gui.Robot.TurnOff=Turn off
|
||||
oc:gui.Robot.TurnOn=Turn on
|
||||
|
@ -16,13 +16,13 @@ object ConnectionHandler extends IConnectionHandler {
|
||||
if (netHandler.isServerHandler) player match {
|
||||
case p: EntityPlayerMP =>
|
||||
if (!LuaStateFactory.isAvailable) {
|
||||
p.sendChatToPlayer(ChatMessageComponent.createFromText("§aOpenComputers§f: ").addKey(Settings.namespace + "gui.chat.WarningLuaFallback"))
|
||||
p.sendChatToPlayer(ChatMessageComponent.createFromText("§aOpenComputers§f: ").addKey(Settings.namespace + "gui.Chat.WarningLuaFallback"))
|
||||
}
|
||||
if (ProjectRed.isAvailable && !ProjectRed.isAPIAvailable) {
|
||||
p.sendChatToPlayer(ChatMessageComponent.createFromText("§aOpenComputers§f: ").addKey(Settings.namespace + "gui.chat.WarningProjectRed"))
|
||||
p.sendChatToPlayer(ChatMessageComponent.createFromText("§aOpenComputers§f: ").addKey(Settings.namespace + "gui.Chat.WarningProjectRed"))
|
||||
}
|
||||
if (!Settings.get.pureIgnorePower && !Loader.isModLoaded("UniversalElectricity")) {
|
||||
p.sendChatToPlayer(ChatMessageComponent.createFromText("§aOpenComputers§f: ").addKey(Settings.namespace + "gui.chat.WarningPower"))
|
||||
p.sendChatToPlayer(ChatMessageComponent.createFromText("§aOpenComputers§f: ").addKey(Settings.namespace + "gui.Chat.WarningPower"))
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
|
@ -31,6 +31,14 @@ class Case(var tier: Int, isRemote: Boolean) extends Computer(isRemote) {
|
||||
case _ => 0
|
||||
}))
|
||||
|
||||
def hasCPU = items.exists {
|
||||
case Some(stack) => Registry.itemDriverFor(stack) match {
|
||||
case Some(driver) => driver.slot(stack) == Slot.Processor
|
||||
case _ => false
|
||||
}
|
||||
case _ => false
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def readFromNBT(nbt: NBTTagCompound) {
|
||||
|
@ -178,7 +178,7 @@ abstract class Computer(isRemote: Boolean) extends Environment with ComponentInv
|
||||
if (computer != null) computer.lastError match {
|
||||
case Some(value) =>
|
||||
player.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions(
|
||||
Settings.namespace + "gui.Analyzer.LastError", value))
|
||||
Settings.namespace + "gui.Analyzer.LastError", ChatMessageComponent.createFromTranslationKey(value)))
|
||||
case _ =>
|
||||
}
|
||||
val list = users
|
||||
|
@ -9,6 +9,7 @@ import net.minecraftforge.common.{ForgeDirection, DimensionManager}
|
||||
import scala.Some
|
||||
import li.cil.oc.Settings
|
||||
import li.cil.oc.server.component.machine.Machine
|
||||
import net.minecraft.util.ChatMessageComponent
|
||||
|
||||
class PacketHandler extends CommonPacketHandler {
|
||||
protected def world(player: Player, dimension: Int) =
|
||||
@ -49,7 +50,7 @@ class PacketHandler extends CommonPacketHandler {
|
||||
if (!computer.isPaused) {
|
||||
computer.start()
|
||||
computer.lastError match {
|
||||
case Some(message) => player.addChatMessage(message)
|
||||
case Some(message) => player.sendChatToPlayer(ChatMessageComponent.createFromTranslationKey(message))
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import java.util.regex.Matcher
|
||||
import li.cil.oc.Settings
|
||||
import li.cil.oc.api.Network
|
||||
import li.cil.oc.api.network._
|
||||
import li.cil.oc.util.ExtendedNBT._
|
||||
import li.cil.oc.util.ThreadPoolFactory
|
||||
import net.minecraft.nbt.NBTTagCompound
|
||||
import net.minecraft.server.MinecraftServer
|
||||
@ -22,8 +21,20 @@ class InternetCard(val owner: Context) extends ManagedComponent {
|
||||
|
||||
protected val connections = mutable.Map.empty[Int, SocketChannel]
|
||||
|
||||
// node address -> list per request -> list of signals as (request url, packets)
|
||||
protected val queues = mutable.Map.empty[String, mutable.Queue[(String, mutable.Queue[Array[Byte]])]]
|
||||
// For HTTP requests the state switches like so:
|
||||
// Pre: request == None && queue == None
|
||||
// request = value
|
||||
// thread {
|
||||
// queue = value
|
||||
// request = None
|
||||
// }
|
||||
// while (request == None && queue contains elements) { signal(queue.pop()) }
|
||||
// queue = None
|
||||
// Post: request == None && queue == None
|
||||
|
||||
protected var request: Option[(String, Option[String])] = None
|
||||
|
||||
protected var queue: Option[(String, mutable.Queue[Array[Byte]])] = None
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
@ -32,10 +43,26 @@ class InternetCard(val owner: Context) extends ManagedComponent {
|
||||
|
||||
@LuaCallback("request")
|
||||
def request(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
if (context.address != owner.address) {
|
||||
throw new IllegalArgumentException("can only be used by the owning computer")
|
||||
}
|
||||
val address = args.checkString(0)
|
||||
if (!Settings.get.httpEnabled) return result(false, "http requests are unavailable")
|
||||
val url = checkAddress(address)
|
||||
if (!Settings.get.httpEnabled) {
|
||||
return result(false, "http requests are unavailable")
|
||||
}
|
||||
val post = if (args.isString(1)) Option(args.checkString(1)) else None
|
||||
this.synchronized {
|
||||
if (request.isDefined || queue.isDefined) {
|
||||
return result(false, "already busy with another request")
|
||||
}
|
||||
scheduleRequest(address, post)
|
||||
}
|
||||
result(true)
|
||||
}
|
||||
|
||||
protected def scheduleRequest(address: String, post: Option[String]) {
|
||||
val url = checkAddress(address)
|
||||
request = Some((address, post))
|
||||
InternetCard.threadPool.submit(new Runnable {
|
||||
def run() = try {
|
||||
val proxy = Option(MinecraftServer.getServer.getServerProxy).getOrElse(java.net.Proxy.NO_PROXY)
|
||||
@ -67,24 +94,34 @@ class InternetCard(val owner: Context) extends ManagedComponent {
|
||||
}
|
||||
} while (count != -1)
|
||||
input.close()
|
||||
queues.synchronized(queues.getOrElseUpdate(context.address, mutable.Queue.empty) += address -> (mutable.Queue(data.toArray.grouped(Settings.get.maxNetworkPacketSize).toSeq: _*) ++ Iterable(null)))
|
||||
queue = Some(address -> (mutable.Queue(data.toArray.grouped(Settings.get.maxNetworkPacketSize).toSeq: _*) ++ Iterable(null)))
|
||||
}
|
||||
finally {
|
||||
http.disconnect()
|
||||
}
|
||||
case other => context.signal("http_response", address, Unit, "connection failed")
|
||||
case other => owner.signal("http_response", address, Unit, "connection failed")
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case e: FileNotFoundException =>
|
||||
context.signal("http_response", address, Unit, "not found: " + Option(e.getMessage).getOrElse(e.toString))
|
||||
owner.signal("http_response", address, Unit, "not found: " + Option(e.getMessage).getOrElse(e.toString))
|
||||
case _: SocketTimeoutException =>
|
||||
context.signal("http_response", address, Unit, "timeout")
|
||||
owner.signal("http_response", address, Unit, "timeout")
|
||||
case e: Throwable =>
|
||||
context.signal("http_response", address, Unit, Option(e.getMessage).getOrElse(e.toString))
|
||||
owner.signal("http_response", address, Unit, Option(e.getMessage).getOrElse(e.toString))
|
||||
}
|
||||
finally {
|
||||
InternetCard.this.synchronized {
|
||||
if (request.isDefined) {
|
||||
request = None
|
||||
}
|
||||
else {
|
||||
// Got disconnected in the meantime.
|
||||
queue = None
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
result(true)
|
||||
}
|
||||
|
||||
@LuaCallback(value = "isTcpEnabled", direct = true)
|
||||
@ -154,37 +191,43 @@ class InternetCard(val owner: Context) extends ManagedComponent {
|
||||
override def update() {
|
||||
super.update()
|
||||
|
||||
queues.synchronized {
|
||||
for ((nodeAddress, queue) <- queues if queue.nonEmpty) {
|
||||
node.network.node(nodeAddress) match {
|
||||
case computer: Node =>
|
||||
computer.host match {
|
||||
case context: Context =>
|
||||
val (address, packets) = queue.front
|
||||
if (context.signal("http_response", address, packets.front)) {
|
||||
packets.dequeue()
|
||||
}
|
||||
case _ => queue.clear()
|
||||
}
|
||||
case _ => queue.clear()
|
||||
this.synchronized {
|
||||
if (request.isEmpty && queue.isDefined) {
|
||||
val (address, packets) = queue.get
|
||||
if (owner.signal("http_response", address, packets.front)) {
|
||||
packets.dequeue()
|
||||
}
|
||||
if (packets.isEmpty) {
|
||||
queue = None
|
||||
}
|
||||
// Remove all responses that have no more packets (usually only the
|
||||
// first on when it has been processed).
|
||||
queue.dequeueAll(_._2.isEmpty)
|
||||
}
|
||||
// Remove all targets that have no more responses.
|
||||
queues.retain((_, queue) => queue.nonEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def onDisconnect(node: Node) {
|
||||
super.onDisconnect(node)
|
||||
if (node == this.node) {
|
||||
for ((_, socket) <- connections) {
|
||||
socket.close()
|
||||
}
|
||||
connections.clear()
|
||||
request = None
|
||||
queue = None
|
||||
}
|
||||
}
|
||||
|
||||
override def onMessage(message: Message) {
|
||||
super.onMessage(message)
|
||||
message.data match {
|
||||
case Array() if (message.name == "computer.stopped" || message.name == "computer.started") && message.source.address == owner.address =>
|
||||
connections.values.foreach(_.close())
|
||||
connections.clear()
|
||||
InternetCard.this.synchronized {
|
||||
request = None
|
||||
queue = None
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
@ -193,44 +236,43 @@ class InternetCard(val owner: Context) extends ManagedComponent {
|
||||
|
||||
override def load(nbt: NBTTagCompound) {
|
||||
super.load(nbt)
|
||||
queues.synchronized {
|
||||
queues.clear()
|
||||
if (nbt.hasKey("queues")) {
|
||||
queues ++= nbt.getTagList("queues").iterator[NBTTagCompound].map(nodeNbt => {
|
||||
val nodeAddress = nodeNbt.getString("nodeAddress")
|
||||
val responses = mutable.Queue(nodeNbt.getTagList("responses").iterator[NBTTagCompound].map(responseNbt => {
|
||||
val address = responseNbt.getString("address")
|
||||
val data = responseNbt.getByteArray("data")
|
||||
(address, mutable.Queue(data.grouped(Settings.get.maxNetworkPacketSize).toSeq: _*) ++ Iterable(null))
|
||||
}): _*)
|
||||
nodeAddress -> responses
|
||||
})
|
||||
if (nbt.hasKey("url")) {
|
||||
val address = nbt.getString("url")
|
||||
val data = nbt.getByteArray("data")
|
||||
queue = Some(address -> (mutable.Queue(data.grouped(Settings.get.maxNetworkPacketSize).toSeq: _*) ++ Iterable(null)))
|
||||
}
|
||||
if (nbt.hasKey("request")) {
|
||||
val address = nbt.getString("request")
|
||||
val post =
|
||||
if (nbt.hasKey("postData")) Option(nbt.getString("postData"))
|
||||
else None
|
||||
// Restart request?
|
||||
if (!queue.isDefined) {
|
||||
scheduleRequest(address, post)
|
||||
}
|
||||
// Otherwise this should have been None anyway...
|
||||
}
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound) {
|
||||
super.save(nbt)
|
||||
queues.synchronized {
|
||||
if (!queues.isEmpty) {
|
||||
nbt.setNewTagList("queues", queues.toIterable.map(
|
||||
node => {
|
||||
val (nodeAddress, responses) = node
|
||||
val nodeNbt = new NBTTagCompound()
|
||||
nodeNbt.setString("nodeAddress", nodeAddress)
|
||||
nodeNbt.setNewTagList("responses", responses.toIterable.map(
|
||||
response => {
|
||||
val (address, packets) = response
|
||||
val responseNbt = new NBTTagCompound()
|
||||
responseNbt.setString("address", address)
|
||||
val data = mutable.ArrayBuffer.empty[Byte]
|
||||
packets.toIterable.dropRight(1).foreach(data.appendAll(_))
|
||||
responseNbt.setByteArray("data", data.toArray)
|
||||
responseNbt
|
||||
}
|
||||
))
|
||||
this.synchronized {
|
||||
request match {
|
||||
case Some((address, data)) =>
|
||||
nbt.setString("request", address)
|
||||
data match {
|
||||
case Some(value) => nbt.setString("postData", value)
|
||||
case _ =>
|
||||
}
|
||||
))
|
||||
case _ =>
|
||||
}
|
||||
queue match {
|
||||
case Some((address, packets)) =>
|
||||
nbt.setString("url", address)
|
||||
val data = mutable.ArrayBuffer.empty[Byte]
|
||||
packets.toIterable.dropRight(1).foreach(data.appendAll(_))
|
||||
nbt.setByteArray("data", data.toArray)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,11 +96,14 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
|
||||
case Machine.State.Stopped =>
|
||||
val rules = owner.world.getWorldInfo.getGameRulesInstance
|
||||
if (rules.hasRule("doDaylightCycle") && !rules.getGameRuleBooleanValue("doDaylightCycle")) {
|
||||
crash("computers don't work while time is frozen (gamerule doDaylightCycle is false)")
|
||||
crash(Settings.namespace + "gui.Error.DaylightCycle")
|
||||
false
|
||||
}
|
||||
else if (components.size + addedComponents.size > owner.maxComponents) {
|
||||
message = Some("too many components")
|
||||
message = owner match {
|
||||
case t: tileentity.Case if !t.hasCPU => Some(Settings.namespace + "gui.Error.NoCPU")
|
||||
case _ => Some(Settings.namespace + "gui.Error.ComponentOverflow")
|
||||
}
|
||||
false
|
||||
}
|
||||
else if (owner.installedMemory > 0) {
|
||||
@ -113,12 +116,12 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
|
||||
}
|
||||
}
|
||||
else {
|
||||
message = Some("not enough energy")
|
||||
message = Some(Settings.namespace + "gui.Error.NoEnergy")
|
||||
false
|
||||
}
|
||||
}
|
||||
else {
|
||||
message = Some("no memory installed")
|
||||
message = Some(Settings.namespace + "gui.Error.NoRAM")
|
||||
false
|
||||
}
|
||||
case Machine.State.Paused if remainingPause > 0 =>
|
||||
@ -269,7 +272,7 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
|
||||
// Component overflow check, crash if too many components are connected, to
|
||||
// avoid confusion on the user's side due to components not showing up.
|
||||
if (components.size > owner.maxComponents) {
|
||||
crash("too many components")
|
||||
crash(Settings.namespace + "gui.Error.ComponentOverflow")
|
||||
}
|
||||
|
||||
// Update world time for time().
|
||||
@ -294,11 +297,11 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
|
||||
Machine.State.Stopped => // No power consumption.
|
||||
case Machine.State.Sleeping if remainIdle > 0 && signals.isEmpty =>
|
||||
if (!node.tryChangeBuffer(-cost * Settings.get.sleepCostFactor)) {
|
||||
crash("not enough energy")
|
||||
crash(Settings.namespace + "gui.Error.NoEnergy")
|
||||
}
|
||||
case _ =>
|
||||
if (!node.tryChangeBuffer(-cost)) {
|
||||
crash("not enough energy")
|
||||
crash(Settings.namespace + "gui.Error.NoEnergy")
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -368,10 +371,10 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
|
||||
}
|
||||
} catch {
|
||||
case e: java.lang.Error if e.getMessage == "not enough memory" =>
|
||||
crash("not enough memory")
|
||||
crash(Settings.namespace + "gui.Error.OutOfMemory")
|
||||
case e: Throwable =>
|
||||
OpenComputers.log.log(Level.WARNING, "Faulty architecture implementation for synchronized calls.", e)
|
||||
crash("protocol error")
|
||||
crash(Settings.namespace + "gui.Error.InternalError")
|
||||
}
|
||||
assert(state.top != Machine.State.Running)
|
||||
case _ => // Nothing special to do, just avoid match errors.
|
||||
@ -733,7 +736,7 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
|
||||
catch {
|
||||
case e: Throwable =>
|
||||
OpenComputers.log.log(Level.WARNING, "Architecture's runThreaded threw an error. This should never happen!", e)
|
||||
crash("kernel panic: architecture threw an exception, see log file")
|
||||
crash(Settings.namespace + "gui.Error.InternalError")
|
||||
}
|
||||
|
||||
// Keep track of time spent executing the computer.
|
||||
|
Loading…
x
Reference in New Issue
Block a user