diff --git a/assets/opencomputers/lua/init.lua b/assets/opencomputers/lua/init.lua index 0b6f71786..47c0e9898 100644 --- a/assets/opencomputers/lua/init.lua +++ b/assets/opencomputers/lua/init.lua @@ -125,6 +125,7 @@ end) --[[ Setup terminal API. ]] local idGpu, idScreen = 0, 0 +local screenWidth, screenHeight = 0, 0 local boundGpu = nil local cursorX, cursorY = 1, 1 @@ -157,22 +158,36 @@ event.listen("component_uninstalled", function(_, id) end end) +event.listen("screen_resized", function(_, address, w, h) + local id = component.id(address) + if id == idScreen then + screenWidth = w + screenHeight = h + end +end) + term = {} function term.gpu() return boundGpu end +function term.screenSize() + return screenWidth, screenHeight +end + local function bindIfPossible() if idGpu > 0 and idScreen > 0 then if not boundGpu then local function gpu() return component.address(idGpu) end local function screen() return component.address(idScreen) end boundGpu = driver.gpu.bind(gpu, screen) + screenWidth, screenHeight = boundGpu.getResolution() event.fire("term_available") end elseif boundGpu then boundGpu = nil + screenWidth, screenHeight = 0, 0 event.fire("term_unavailable") end end @@ -208,18 +223,18 @@ end function term.write(value, wrap) value = tostring(value) - local gpu = term.gpu() - if not gpu or value:len() == 0 then return end - local w, h = gpu.getResolution() - if not w or not h or w < 1 or h < 1 then return end + local w, h = screenWidth, screenHeight + if value:len() == 0 or not boundGpu or w < 1 or h < 1 then + return + end local function checkCursor() if cursorX > w then cursorX = 1 cursorY = cursorY + 1 end if cursorY > h then - gpu.copy(1, 1, w, h, 0, -1) - gpu.fill(1, h, w, 1, " ") + boundGpu.copy(1, 1, w, h, 0, -1) + boundGpu.fill(1, h, w, 1, " ") cursorY = h end end @@ -227,12 +242,12 @@ function term.write(value, wrap) while wrap and line:len() > w - cursorX + 1 do local partial = line:sub(1, w - cursorX + 1) line = line:sub(partial:len() + 1) - gpu.set(cursorX, cursorY, partial) + boundGpu.set(cursorX, cursorY, partial) cursorX = cursorX + partial:len() checkCursor() end if line:len() > 0 then - gpu.set(cursorX, cursorY, line) + boundGpu.set(cursorX, cursorY, line) cursorX = cursorX + line:len() end if nl:len() == 1 then @@ -244,10 +259,8 @@ function term.write(value, wrap) end function term.clear() - local gpu = term.gpu() - if not gpu then return end - local w, h = gpu.getResolution() - gpu.fill(1, 1, w, h, " ") + if not boundGpu then return end + boundGpu.fill(1, 1, screenWidth, screenHeight, " ") cursorX, cursorY = 1, 1 end @@ -332,7 +345,7 @@ while true do if gpu then local x, y = term.getCursor() if blinkState then - term.gpu().set(x, y, string.char(9608)) -- Solid block. + term.gpu().set(x, y, string.char(0x2588)) -- Solid block. else term.gpu().set(x, y, " ") end diff --git a/assets/opencomputers/lua/keyboard.lua b/assets/opencomputers/lua/keyboard.lua index cd09c3388..bf9dfda81 100644 --- a/assets/opencomputers/lua/keyboard.lua +++ b/assets/opencomputers/lua/keyboard.lua @@ -137,5 +137,5 @@ for k, v in pairs(driver.keyboard.keys) do end function driver.keyboard.keys.isControl(char) - return char < 0x20 or (char >= 0x7F and char < 0x9F) + return char < 0x20 or (char >= 0x7F and char <= 0x9F) end \ No newline at end of file diff --git a/assets/opencomputers/lua/redstone.lua b/assets/opencomputers/lua/redstone.lua new file mode 100644 index 000000000..1a56f7176 --- /dev/null +++ b/assets/opencomputers/lua/redstone.lua @@ -0,0 +1,38 @@ +--[[ API for Redstone Cards. ]] + +driver.redstone = {} + +driver.redstone.sides = {"top", "bottom", "left", "right", "front", "back"} + +-- Add inverse mapping and aliases. +for k, v in pairs(sides) do sides[v] = k end +sides.up = sides.top +sides.down = sides.bottom + +function driver.redstone.getAnalogInput(card, side) + checkArg(1, side, "number") + sendToNode(card, os.address(), "redstone.input", side) +end + +function driver.redstone.getAnalogOutput(card, side) + checkArg(1, side, "number") + sendToNode(card, os.address(), "redstone.output", side) +end + +function driver.redstone.setAnalogOutput(card, side, value) + checkArg(1, side, "number") + checkArg(2, side, "number") + sendToNode(card, os.address(), "redstone.output=", side, value) +end + +function getInput(side) + return getAnalogInput(side) > 0 +end + +function getOutput(side) + return getAnalogOutput(side) > 0 +end + +function setOutput(side, value) + rs.setAnalogOutput(side, value and 15 or 0) +end diff --git a/assets/opencomputers/lua/rs.lua b/assets/opencomputers/lua/rs.lua deleted file mode 100644 index c204b52a3..000000000 --- a/assets/opencomputers/lua/rs.lua +++ /dev/null @@ -1,25 +0,0 @@ ---[[ - The Redstone API, provided by Redstone components. -]] - -sides = {"top", "bottom", "left", "right", "front", "back"} - -function getSides() - return sides -end - -function getInput(side) - return getAnalogInput(side) > 0 -end - -function getOutput(side) - return getAnalogOutput(side) > 0 -end - -function setOutput(side, value) - rs.setAnalogOutput(side, value and 15 or 0) -end - -getAnalogInput = _G.getAnalogInput -getAnalogOutput = _G.getAnalogOutput -setAnalogOutput = _G.setAnalogOutput \ No newline at end of file diff --git a/li/cil/oc/Items.scala b/li/cil/oc/Items.scala index 70b06db7f..4e4988b36 100644 --- a/li/cil/oc/Items.scala +++ b/li/cil/oc/Items.scala @@ -1,18 +1,20 @@ package li.cil.oc -import li.cil.oc.common.items.ItemGraphicsCard -import li.cil.oc.common.items.ItemHDD +import li.cil.oc.common.items.{ItemRedstoneCard, ItemGraphicsCard, ItemHDD} import li.cil.oc.common.util.ItemComponentCache -import li.cil.oc.server.components.GraphicsCard +import li.cil.oc.server.components.{RedstoneCard, GraphicsCard} object Items { var gpu: ItemGraphicsCard = null var hdd: ItemHDD = null + var rs: ItemRedstoneCard = null def init() { gpu = new ItemGraphicsCard hdd = new ItemHDD + rs = new ItemRedstoneCard ItemComponentCache.register(gpu.itemID, nbt => new GraphicsCard(nbt)) + ItemComponentCache.register(rs.itemID, nbt => new RedstoneCard(nbt)) } } \ No newline at end of file diff --git a/li/cil/oc/api/INetwork.scala b/li/cil/oc/api/INetwork.scala index 2090f66b1..b2998f262 100644 --- a/li/cil/oc/api/INetwork.scala +++ b/li/cil/oc/api/INetwork.scala @@ -122,9 +122,9 @@ trait INetwork { *

* Messages should have a unique name to allow differentiating them when * handling them in a network node. For example, computers will try to parse - * messages named "signal" by converting the message data to a signal and - * inject that signal into the Lua VM, so no message not used for this purpose - * should be named "signal". + * messages named "computer.signal" by converting the message data to a + * signal and inject that signal into the Lua VM, so no message not used for + * this purpose should be named "computer.signal". *

* Note that message handlers may also return results. In this case that * result will be returned from this function. In the case that there are @@ -144,9 +144,9 @@ trait INetwork { *

* Messages should have a unique name to allow differentiating them when * handling them in a network node. For example, computers will try to parse - * messages named "signal" by converting the message data to a signal and - * inject that signal into the Lua VM, so no message not used for this purpose - * should be named "signal". + * messages named "computer.signal" by converting the message data to a + * signal and inject that signal into the Lua VM, so no message not used for + * this purpose should be named "computer.signal". * * @param source the node that sends the message. * @param data the message to send. diff --git a/li/cil/oc/common/components/IScreenEnvironment.scala b/li/cil/oc/common/components/IScreenEnvironment.scala index 58a3d1865..2e7e040a3 100644 --- a/li/cil/oc/common/components/IScreenEnvironment.scala +++ b/li/cil/oc/common/components/IScreenEnvironment.scala @@ -50,11 +50,13 @@ trait IScreenEnvironment extends INetworkNode { } - def onScreenResolutionChange(w: Int, h: Int) + def onScreenResolutionChange(w: Int, h: Int) = if (network != null) { + network.sendToAll(this, "computer.signal", "screen_resized", this.address, w, h) + } - def onScreenSet(col: Int, row: Int, s: String) + def onScreenSet(col: Int, row: Int, s: String) {} - def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) + def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) {} - def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) + def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {} } \ No newline at end of file diff --git a/li/cil/oc/common/items/ItemGraphicsCard.scala b/li/cil/oc/common/items/ItemGraphicsCard.scala index c59e17429..61fead897 100644 --- a/li/cil/oc/common/items/ItemGraphicsCard.scala +++ b/li/cil/oc/common/items/ItemGraphicsCard.scala @@ -8,7 +8,7 @@ import net.minecraft.world.World class ItemGraphicsCard extends Item(Config.itemGPUId) { setMaxStackSize(1) //setHasSubtypes(true) - setUnlocalizedName("oc.gpu") + setUnlocalizedName("Graphics Card") setCreativeTab(CreativeTab) override def shouldPassSneakingClickToBlock(world: World, x: Int, y: Int, z: Int) = true diff --git a/li/cil/oc/common/items/ItemRedstoneCard.scala b/li/cil/oc/common/items/ItemRedstoneCard.scala new file mode 100644 index 000000000..e1d71274a --- /dev/null +++ b/li/cil/oc/common/items/ItemRedstoneCard.scala @@ -0,0 +1,14 @@ +package li.cil.oc.common.items + +import li.cil.oc.{CreativeTab, Config} +import net.minecraft.item.Item +import net.minecraft.world.World + +class ItemRedstoneCard extends Item(Config.itemGPUId + 1) { + setMaxStackSize(1) + //setHasSubtypes(true) + setUnlocalizedName("Redstone Card") + setCreativeTab(CreativeTab) + + override def shouldPassSneakingClickToBlock(world: World, x: Int, y: Int, z: Int) = true +} diff --git a/li/cil/oc/common/tileentity/TileEntityComputer.scala b/li/cil/oc/common/tileentity/TileEntityComputer.scala index 30218ccb0..1cd9c5301 100644 --- a/li/cil/oc/common/tileentity/TileEntityComputer.scala +++ b/li/cil/oc/common/tileentity/TileEntityComputer.scala @@ -4,13 +4,15 @@ import java.util.concurrent.atomic.AtomicBoolean import li.cil.oc.api.INetworkMessage import li.cil.oc.client.computer.{Computer => ClientComputer} import li.cil.oc.client.{PacketSender => ClientPacketSender} +import li.cil.oc.server.components.RedstoneEnabled import li.cil.oc.server.computer.IComputerEnvironment import li.cil.oc.server.computer.{Computer => ServerComputer} import li.cil.oc.server.{PacketSender => ServerPacketSender} import net.minecraft.entity.player.EntityPlayer import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.ForgeDirection -class TileEntityComputer(isClient: Boolean) extends TileEntityRotatable with IComputerEnvironment with ItemComponentProxy { +class TileEntityComputer(isClient: Boolean) extends TileEntityRotatable with IComputerEnvironment with ItemComponentProxy with RedstoneEnabled { def this() = this(false) protected val computer = @@ -36,7 +38,7 @@ class TileEntityComputer(isClient: Boolean) extends TileEntityRotatable with ICo computer.signal("component_removed", message.source.address); None case Array(oldAddress: Integer) if message.name == "network.reconnect" && isRunning => computer.signal("component_changed", message.source.address, oldAddress); None - case Array(name: String, args@_*) if message.name == "signal" => + case Array(name: String, args@_*) if message.name == "computer.signal" => computer.signal(name, args: _*); None case _ => None } @@ -100,6 +102,15 @@ class TileEntityComputer(isClient: Boolean) extends TileEntityRotatable with ICo ClientPacketSender.sendComputerStateRequest(this) } + // ----------------------------------------------------------------------- // + // RedstoneEnabled + // ----------------------------------------------------------------------- // + + def input(side: ForgeDirection): Int = worldObj.isBlockProvidingPowerTo( + xCoord + side.offsetX, yCoord + side.offsetY, zCoord + side.offsetZ, side.getOpposite.ordinal) + + // TODO output + // ----------------------------------------------------------------------- // // Interfaces and updating // ----------------------------------------------------------------------- // diff --git a/li/cil/oc/common/tileentity/TileEntityKeyboard.scala b/li/cil/oc/common/tileentity/TileEntityKeyboard.scala index d3b03dbf0..df8589246 100644 --- a/li/cil/oc/common/tileentity/TileEntityKeyboard.scala +++ b/li/cil/oc/common/tileentity/TileEntityKeyboard.scala @@ -12,15 +12,15 @@ class TileEntityKeyboard extends TileEntityRotatable with INetworkNode { super.receive(message) message.data match { case Array(p: Player, char: Char, code: Int) if message.name == "keyboard.keyDown" => if (isUseableByPlayer(p)) { - network.sendToAll(this, "signal", "key_down", char, code) + network.sendToAll(this, "computer.signal", "key_down", char, code) message.cancel() // One keyboard is enough. } case Array(p: Player, char: Char, code: Int) if message.name == "keyboard.keyUp" => if (isUseableByPlayer(p)) { - network.sendToAll(this, "signal", "key_up", char, code) + network.sendToAll(this, "computer.signal", "key_up", char, code) message.cancel() // One keyboard is enough. } case Array(p: Player, value: String) if message.name == "keyboard.clipboard" => if (isUseableByPlayer(p)) { - network.sendToAll(this, "signal", "clipboard", value) + network.sendToAll(this, "computer.signal", "clipboard", value) message.cancel() } case _ => // Ignore. diff --git a/li/cil/oc/common/tileentity/TileEntityScreen.scala b/li/cil/oc/common/tileentity/TileEntityScreen.scala index 9dd27356e..478d6c7ce 100644 --- a/li/cil/oc/common/tileentity/TileEntityScreen.scala +++ b/li/cil/oc/common/tileentity/TileEntityScreen.scala @@ -32,7 +32,8 @@ class TileEntityScreen extends TileEntityRotatable with IScreenEnvironment { // IScreenEnvironment // ----------------------------------------------------------------------- // - def onScreenResolutionChange(w: Int, h: Int) = + override def onScreenResolutionChange(w: Int, h: Int) = { + super.onScreenResolutionChange(w, h) if (worldObj.isRemote) { gui.foreach(_.setSize(w, h)) } @@ -40,8 +41,10 @@ class TileEntityScreen extends TileEntityRotatable with IScreenEnvironment { markAsChanged() ServerPacketSender.sendScreenResolutionChange(this, w, h) } + } - def onScreenSet(col: Int, row: Int, s: String) = + override def onScreenSet(col: Int, row: Int, s: String) = { + super.onScreenSet(col, row, s) if (worldObj.isRemote) { gui.foreach(_.updateText()) } @@ -49,8 +52,10 @@ class TileEntityScreen extends TileEntityRotatable with IScreenEnvironment { markAsChanged() ServerPacketSender.sendScreenSet(this, col, row, s) } + } - def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) = + override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) = { + super.onScreenFill(col, row, w, h, c) if (worldObj.isRemote) { gui.foreach(_.updateText()) } @@ -58,8 +63,10 @@ class TileEntityScreen extends TileEntityRotatable with IScreenEnvironment { markAsChanged() ServerPacketSender.sendScreenFill(this, col, row, w, h, c) } + } - def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) = + override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) = { + super.onScreenCopy(col, row, w, h, tx, ty) if (worldObj.isRemote) { gui.foreach(_.updateText()) } @@ -67,6 +74,7 @@ class TileEntityScreen extends TileEntityRotatable with IScreenEnvironment { markAsChanged() ServerPacketSender.sendScreenCopy(this, col, row, w, h, tx, ty) } + } private def markAsChanged(): Unit = worldObj.updateTileEntityChunkAndDoNothing( diff --git a/li/cil/oc/common/util/ItemComponentCache.scala b/li/cil/oc/common/util/ItemComponentCache.scala index 030038b8a..d5b45fdbd 100644 --- a/li/cil/oc/common/util/ItemComponentCache.scala +++ b/li/cil/oc/common/util/ItemComponentCache.scala @@ -1,6 +1,7 @@ package li.cil.oc.common.util import com.google.common.collect.MapMaker +import li.cil.oc.server.components.ItemComponent import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import scala.collection.{JavaConversions, mutable} @@ -13,12 +14,12 @@ import scala.collection.{JavaConversions, mutable} object ItemComponentCache { private val caches = mutable.Map.empty[Int, Cache[_]] - def get[T](item: ItemStack) = caches.get(item.itemID) match { + def get[T <: ItemComponent](item: ItemStack) = caches.get(item.itemID) match { case None => None case Some(cache) => cache.asInstanceOf[Cache[T]].get(item) } - def register[T](id: Int, constructor: (NBTTagCompound) => T): Unit = + def register[T <: ItemComponent](id: Int, constructor: (NBTTagCompound) => T): Unit = caches += id -> new Cache[T](id, constructor) private class Cache[T](val id: Int, val constructor: (NBTTagCompound) => T) { diff --git a/li/cil/oc/server/components/Disk.scala b/li/cil/oc/server/components/Disk.scala index 173128088..a0adb812e 100644 --- a/li/cil/oc/server/components/Disk.scala +++ b/li/cil/oc/server/components/Disk.scala @@ -1,5 +1,9 @@ package li.cil.oc.server.components -class Disk { +import li.cil.oc.api.INetworkNode + +class Disk extends INetworkNode{ + override def name = "disk" + def close() {} } diff --git a/li/cil/oc/server/components/GraphicsCard.scala b/li/cil/oc/server/components/GraphicsCard.scala index 79d793d59..2927ed824 100644 --- a/li/cil/oc/server/components/GraphicsCard.scala +++ b/li/cil/oc/server/components/GraphicsCard.scala @@ -3,21 +3,13 @@ package li.cil.oc.server.components import li.cil.oc.api.{INetworkNode, INetworkMessage} import net.minecraft.nbt.NBTTagCompound -class GraphicsCard(val nbt: NBTTagCompound) extends INetworkNode { - address = nbt.getInteger("address") - +class GraphicsCard(nbt: NBTTagCompound) extends ItemComponent(nbt) { val supportedResolutions = List(List(40, 24), List(80, 24)) override def name = "gpu" - override def address_=(value: Int) = { - super.address_=(value) - nbt.setInteger("address", address) - } - override def receive(message: INetworkMessage) = { - // We don't need onConnect / onDisconnect / onAddressChange yet, so no need to call super. - // super.receive(message) + super.receive(message) message.data match { case Array(screen: Double, w: Double, h: Double) if message.name == "gpu.resolution=" => if (supportedResolutions.contains((w.toInt, h.toInt))) diff --git a/li/cil/oc/server/components/ItemComponent.scala b/li/cil/oc/server/components/ItemComponent.scala new file mode 100644 index 000000000..d35153a64 --- /dev/null +++ b/li/cil/oc/server/components/ItemComponent.scala @@ -0,0 +1,13 @@ +package li.cil.oc.server.components + +import li.cil.oc.api.INetworkNode +import net.minecraft.nbt.NBTTagCompound + +abstract class ItemComponent(val nbt: NBTTagCompound) extends INetworkNode { + address = nbt.getInteger("address") + + override def address_=(value: Int) = { + super.address_=(value) + nbt.setInteger("address", address) + } +} diff --git a/li/cil/oc/server/components/RedstoneCard.scala b/li/cil/oc/server/components/RedstoneCard.scala new file mode 100644 index 000000000..2ee2d9c51 --- /dev/null +++ b/li/cil/oc/server/components/RedstoneCard.scala @@ -0,0 +1,46 @@ +package li.cil.oc.server.components + +import li.cil.oc.api.INetworkMessage +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.tileentity.TileEntity +import net.minecraftforge.common.ForgeDirection + +class RedstoneCard(nbt: NBTTagCompound) extends ItemComponent(nbt) { + override def name = "redstone" + + override def receive(message: INetworkMessage): Option[Array[Any]] = { + super.receive(message) + message.data match { + case Array(target: Double, side: Double) if message.name == "redstone.input" => + input(target.toInt, side.toInt) + case Array(target: Double, side: Double) if message.name == "redstone.output" => + output(target.toInt, side.toInt) + case Array(target: Double, side: Double, value: Double) if message.name == "redstone.output=" => + output(target.toInt, side.toInt, value.toInt); None + case _ => None // Ignore. + } + } + + private def input(target: Int, side: Int) = if (side >= 0 && side < 6) network.node(target) match { + case Some(r: RedstoneEnabled) => Some(Array(r.input(ForgeDirection.getOrientation(side)).asInstanceOf[Any])) + case Some(t: TileEntity) => + val face = ForgeDirection.getOrientation(side.toInt) + val power = t.worldObj.isBlockProvidingPowerTo( + t.xCoord + face.offsetX, t.yCoord + face.offsetY, t.zCoord + face.offsetZ, face.getOpposite.ordinal) + Some(Array(power.asInstanceOf[Any])) + case _ => None // Can't work with this node. + } else None + + private def output(target: Int, side: Int) = if (side >= 0 && side < 6) network.node(target) match { + case Some(r: RedstoneEnabled) => Some(Array(r.output(ForgeDirection.getOrientation(side)).asInstanceOf[Any])) + case Some(t: TileEntity) => + val power = t.worldObj.isBlockProvidingPowerTo(t.xCoord, t.yCoord, t.zCoord, side.toInt) + Some(Array(power.asInstanceOf[Any])) + case _ => None // Can't work with this node. + } else None + + private def output(target: Int, side: Int, value: Int) = if (side >= 0 && side < 6) network.node(target) match { + case Some(r: RedstoneEnabled) => r.output(ForgeDirection.getOrientation(side)) = value + case _ => // Can't work with this node. + } +} diff --git a/li/cil/oc/server/components/RedstoneEnabled.scala b/li/cil/oc/server/components/RedstoneEnabled.scala new file mode 100644 index 000000000..a53176066 --- /dev/null +++ b/li/cil/oc/server/components/RedstoneEnabled.scala @@ -0,0 +1,15 @@ +package li.cil.oc.server.components + +import net.minecraftforge.common.ForgeDirection + +trait RedstoneEnabled { + protected val _output = Array.fill(6)(0) + + def input(side: ForgeDirection): Int + + def output = new { + def apply(side: ForgeDirection) = _output(side.ordinal) + + def update(side: ForgeDirection, value: Int) = _output(side.ordinal) = value + } +} diff --git a/li/cil/oc/server/computer/Computer.scala b/li/cil/oc/server/computer/Computer.scala index 6da65bc4a..262baeb88 100644 --- a/li/cil/oc/server/computer/Computer.scala +++ b/li/cil/oc/server/computer/Computer.scala @@ -43,7 +43,7 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable * resume the computers main thread, if at all, and whether to accept new * signals or not. */ - private var state = State.Stopped + private var state = Computer.State.Stopped /** The internal Lua state. Only set while the computer is running. */ private[computer] var lua: LuaState = null @@ -62,7 +62,7 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable * means to communicate actively with the computer (passively only message * handlers can interact with the computer by returning some result). */ - private val signals = new LinkedBlockingQueue[Signal](100) + private val signals = new LinkedBlockingQueue[Computer.Signal](100) // ----------------------------------------------------------------------- // @@ -121,23 +121,23 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable }.toArray stateMonitor.synchronized(state match { // We don't push new signals when stopped or shutting down. - case State.Stopped | State.Stopping => false + case Computer.State.Stopped | Computer.State.Stopping => false // Currently sleeping. Cancel that and start immediately. - case State.Sleeping => + case Computer.State.Sleeping => val v = values // Map first, may error. future.get.cancel(true) - state = State.Suspended - signals.offer(new Signal(name, v)) + state = Computer.State.Suspended + signals.offer(new Computer.Signal(name, v)) future = Some(Computer.Executor.pool.submit(this)) true // Basically running, but had nothing to do so we stopped. Resume. - case State.Suspended if !future.isDefined => - signals.offer(new Signal(name, values)) + case Computer.State.Suspended if !future.isDefined => + signals.offer(new Computer.Signal(name, values)) future = Some(Computer.Executor.pool.submit(this)) true // Running or in synchronized call, just push the signal. case _ => - signals.offer(new Signal(name, values)) + signals.offer(new Computer.Signal(name, values)) true }) } @@ -147,8 +147,8 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable // ----------------------------------------------------------------------- // override def start() = stateMonitor.synchronized( - state == State.Stopped && init() && { - state = State.Suspended + state == Computer.State.Stopped && init() && { + state = Computer.State.Suspended // Mark state change in owner, to send it to clients. owner.markAsChanged() @@ -165,8 +165,8 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable }) override def stop() = saveMonitor.synchronized(stateMonitor.synchronized { - if (state != State.Stopped) { - if (state != State.Running) { + if (state != Computer.State.Stopped) { + if (state != Computer.State.Running) { // If the computer is not currently running we can simply close it, // and cancel any pending future - which may already be running and // waiting for the stateMonitor, so we do a hard abort. @@ -178,26 +178,26 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable // truly stopped, before switching back to stopped to allow starting // the computer again. The executor will check for this state and // call close. - state = State.Stopping + state = Computer.State.Stopping } true } else false }) - override def isRunning = stateMonitor.synchronized(state != State.Stopped) + override def isRunning = stateMonitor.synchronized(state != Computer.State.Stopped) override def update() { stateMonitor.synchronized(state match { - case State.Stopped | State.Stopping => return - case State.SynchronizedCall => { + case Computer.State.Stopped | Computer.State.Stopping => return + case Computer.State.SynchronizedCall => { assert(lua.getTop == 2) assert(lua.isThread(1)) assert(lua.isFunction(2)) try { lua.call(0, 1) lua.checkType(2, LuaType.TABLE) - state = State.SynchronizedReturn + state = Computer.State.SynchronizedReturn assert(!future.isDefined) future = Some(Computer.Executor.pool.submit(this)) } catch { @@ -212,26 +212,26 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable } } } - case State.Paused => { - state = State.Suspended + case Computer.State.Paused => { + state = Computer.State.Suspended assert(!future.isDefined) future = Some(Computer.Executor.pool.submit(this)) } - case State.SynchronizedReturnPaused => { - state = State.SynchronizedReturn + case Computer.State.SynchronizedReturnPaused => { + state = Computer.State.SynchronizedReturn assert(!future.isDefined) future = Some(Computer.Executor.pool.submit(this)) } case _ => /* Nothing special to do. */ }) + // Update world time for computer threads. + worldTime = owner.world.getWorldInfo.getWorldTotalTime + // Remember when we started the computer for os.clock(). We do this in the // update because only then can we be sure the world is available. if (timeStarted == 0) - timeStarted = owner.world.getWorldInfo.getWorldTotalTime - - // Update world time for computer threads. - worldTime = owner.world.getWorldInfo.getWorldTotalTime + timeStarted = worldTime // Update last time run to let our executor thread know it doesn't have to // pause. @@ -244,13 +244,13 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable saveMonitor.synchronized(this.synchronized { // Clear out what we currently have, if anything. stateMonitor.synchronized { - assert(state != State.Running) // Lock on 'this' should guarantee this. + assert(state != Computer.State.Running) // Lock on 'this' should guarantee this. stop() } - state = State(nbt.getInteger("state")) + state = Computer.State(nbt.getInteger("state")) - if (state != State.Stopped && init()) { + if (state != Computer.State.Stopped && init()) { // Unlimit memory use while unpersisting. val memory = lua.getTotalMemory lua.setTotalMemory(Integer.MAX_VALUE) @@ -264,10 +264,10 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable // the save was corrupt (maybe someone modified the Lua files). throw new IllegalStateException("Could not restore kernel.") } - if (state == State.SynchronizedCall || state == State.SynchronizedReturn) { + if (state == Computer.State.SynchronizedCall || state == Computer.State.SynchronizedReturn) { if (!unpersist(nbt.getByteArray("stack")) || - (state == State.SynchronizedCall && !lua.isFunction(2)) || - (state == State.SynchronizedReturn && !lua.isTable(2))) { + (state == Computer.State.SynchronizedCall && !lua.isFunction(2)) || + (state == Computer.State.SynchronizedReturn && !lua.isTable(2))) { // Same as with the above, should not really happen normally, but // could for the same reasons. throw new IllegalStateException("Could not restore stack.") @@ -282,7 +282,7 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable map(signal => { val argsTag = signal.getCompoundTag("args") val argsLength = argsTag.getInteger("length") - new Signal(signal.getString("name"), + new Computer.Signal(signal.getString("name"), (0 until argsLength).map("arg" + _).map(argsTag.getTag).map { case tag: NBTTagByte if tag.data == -1 => Unit case tag: NBTTagByte => tag.data == 1 @@ -300,7 +300,7 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable // Start running our worker thread. assert(!future.isDefined) state match { - case State.Suspended | State.Sleeping | State.SynchronizedReturn => + case Computer.State.Suspended | Computer.State.Sleeping | Computer.State.SynchronizedReturn => future = Some(Computer.Executor.pool.submit(this)) case _ => // Wasn't running before. } @@ -320,12 +320,12 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable override def save(nbt: NBTTagCompound): Unit = saveMonitor.synchronized(this.synchronized { stateMonitor.synchronized { - assert(state != State.Running) // Lock on 'this' should guarantee this. - assert(state != State.Stopping) // Only set while executor is running. + assert(state != Computer.State.Running) // Lock on 'this' should guarantee this. + assert(state != Computer.State.Stopping) // Only set while executor is running. } nbt.setInteger("state", state.id) - if (state == State.Stopped) { + if (state == Computer.State.Stopped) { return } @@ -336,8 +336,8 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable // Try persisting Lua, because that's what all of the rest depends on. // 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. - if (state == State.SynchronizedCall || state == State.SynchronizedReturn) { - assert(if (state == State.SynchronizedCall) lua.isFunction(2) else lua.isTable(2)) + if (state == Computer.State.SynchronizedCall || state == Computer.State.SynchronizedReturn) { + assert(if (state == Computer.State.SynchronizedCall) lua.isFunction(2) else lua.isTable(2)) nbt.setByteArray("stack", persist(2)) } // Save the kernel state (which is always at stack index one). @@ -366,7 +366,7 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable catch { case t: Throwable => { t.printStackTrace() - nbt.setInteger("state", State.Stopped.id) + nbt.setInteger("state", Computer.State.Stopped.id) } } finally { @@ -456,6 +456,13 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable })) lua.setField(-2, "romSize") + // Allow the computer to figure out its own id in the component network. + lua.pushJavaFunction(ScalaFunction(lua => { + lua.pushInteger(owner.address) + 1 + })) + lua.setField(-2, "address") + // Pop the os table. lua.pop(1) @@ -596,8 +603,8 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable } private def close(): Unit = stateMonitor.synchronized( - if (state != State.Stopped) { - state = State.Stopped + if (state != Computer.State.Stopped) { + state = Computer.State.Stopped lua.setTotalMemory(Integer.MAX_VALUE) lua.close() lua = null @@ -616,21 +623,21 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable if (System.currentTimeMillis - lastUpdate > 200) stateMonitor.synchronized { state = - if (state == State.SynchronizedReturn) State.SynchronizedReturnPaused - else State.Paused + if (state == Computer.State.SynchronizedReturn) Computer.State.SynchronizedReturnPaused + else Computer.State.Paused future = None return } val callReturn = stateMonitor.synchronized { - if (state == State.Stopped) return + if (state == Computer.State.Stopped) return val oldState = state - state = State.Running + state = Computer.State.Running future = None oldState } match { - case State.SynchronizedReturn | State.SynchronizedReturnPaused => true - case State.Stopped | State.Paused | State.Suspended | State.Sleeping => false + case Computer.State.SynchronizedReturn | Computer.State.SynchronizedReturnPaused => true + case Computer.State.Stopped | Computer.State.Paused | Computer.State.Suspended | Computer.State.Sleeping => false case s => OpenComputers.log.warning("Running computer from invalid state " + s.toString + "!") stateMonitor.synchronized { @@ -678,7 +685,7 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable if (lua.status(1) == LuaState.YIELD) { // Lua state yielded normally, see what we have. stateMonitor.synchronized { - if (state == State.Stopping) { + if (state == Computer.State.Stopping) { // Someone called stop() in the meantime. close() } @@ -688,25 +695,25 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable val sleep = (lua.toNumber(2) * 1000).toLong lua.pop(results) if (signals.isEmpty) { - state = State.Sleeping + state = Computer.State.Sleeping assert(!future.isDefined) future = Some(Computer.Executor.pool.schedule(this, sleep, TimeUnit.MILLISECONDS)) } else { - state = State.Suspended + state = Computer.State.Suspended assert(!future.isDefined) future = Some(Computer.Executor.pool.submit(this)) } } else if (results == 1 && lua.isFunction(2)) { // If we get one function it's a wrapper for a synchronized call. - state = State.SynchronizedCall + state = Computer.State.SynchronizedCall assert(!future.isDefined) } else { // Something else, just pop the results and try again. lua.pop(results) - state = State.Suspended + state = Computer.State.Suspended assert(!future.isDefined) if (!signals.isEmpty) future = Some(Computer.Executor.pool.submit(this)) } @@ -736,6 +743,19 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable // If we come here there was an error or we stopped, kill off the state. close() } +} + +object Computer { + @ForgeSubscribe + def onChunkUnload(e: ChunkEvent.Unload) = + onUnload(e.world, e.getChunk.chunkTileEntityMap.values.map(_.asInstanceOf[TileEntity])) + + private def onUnload(w: World, tileEntities: Iterable[TileEntity]) = if (!w.isRemote) { + tileEntities. + filter(_.isInstanceOf[TileEntityComputer]). + map(_.asInstanceOf[TileEntityComputer]). + foreach(_.turnOff()) + } /** Signals are messages sent to the Lua state from Java asynchronously. */ private class Signal(val name: String, val args: Array[Any]) @@ -770,20 +790,6 @@ class Computer(val owner: IComputerEnvironment) extends IComputer with Runnable val SynchronizedReturnPaused = Value("SynchronizedReturnPaused") } -} - -object Computer { - @ForgeSubscribe - def onChunkUnload(e: ChunkEvent.Unload) = - onUnload(e.world, e.getChunk.chunkTileEntityMap.values.map(_.asInstanceOf[TileEntity])) - - private def onUnload(w: World, tileEntities: Iterable[TileEntity]) = if (!w.isRemote) { - tileEntities. - filter(_.isInstanceOf[TileEntityComputer]). - map(_.asInstanceOf[TileEntityComputer]). - foreach(_.turnOff()) - } - /** Singleton for requesting executors that run our Lua states. */ private object Executor { val pool = Executors.newScheduledThreadPool(Config.threads, diff --git a/li/cil/oc/server/drivers/RedstoneDriver.scala b/li/cil/oc/server/drivers/RedstoneDriver.scala new file mode 100644 index 000000000..66ed52f75 --- /dev/null +++ b/li/cil/oc/server/drivers/RedstoneDriver.scala @@ -0,0 +1,17 @@ +package li.cil.oc.server.drivers + +import li.cil.oc.api.{ComponentType, IItemDriver} +import li.cil.oc.common.util.ItemComponentCache +import li.cil.oc.server.components.RedstoneCard +import net.minecraft.item.ItemStack +import li.cil.oc.Items + +object RedstoneDriver extends IItemDriver { + override def api = Option(getClass.getResourceAsStream("/assets/opencomputers/lua/redstone.lua")) + + override def worksWith(item: ItemStack) = item.itemID == Items.rs.itemID + + override def componentType(item: ItemStack) = ComponentType.PCI + + override def node(item: ItemStack) = ItemComponentCache.get[RedstoneCard](item) +}