mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-14 01:39:36 -04:00
started working on redstone interfacing; improved term.write performance by caching the screen's resolution and added a signal for screen size changes
This commit is contained in:
parent
484dfd6a83
commit
12da32bc6c
@ -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
|
||||
|
@ -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
|
38
assets/opencomputers/lua/redstone.lua
Normal file
38
assets/opencomputers/lua/redstone.lua
Normal file
@ -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
|
@ -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
|
@ -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))
|
||||
}
|
||||
}
|
@ -122,9 +122,9 @@ trait INetwork {
|
||||
* <p/>
|
||||
* 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".
|
||||
* <p/>
|
||||
* 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 {
|
||||
* <p/>
|
||||
* 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.
|
||||
|
@ -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) {}
|
||||
}
|
@ -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
|
||||
|
14
li/cil/oc/common/items/ItemRedstoneCard.scala
Normal file
14
li/cil/oc/common/items/ItemRedstoneCard.scala
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
@ -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.
|
||||
|
@ -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(
|
||||
|
@ -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) {
|
||||
|
@ -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() {}
|
||||
}
|
||||
|
@ -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)))
|
||||
|
13
li/cil/oc/server/components/ItemComponent.scala
Normal file
13
li/cil/oc/server/components/ItemComponent.scala
Normal file
@ -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)
|
||||
}
|
||||
}
|
46
li/cil/oc/server/components/RedstoneCard.scala
Normal file
46
li/cil/oc/server/components/RedstoneCard.scala
Normal file
@ -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.
|
||||
}
|
||||
}
|
15
li/cil/oc/server/components/RedstoneEnabled.scala
Normal file
15
li/cil/oc/server/components/RedstoneEnabled.scala
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
17
li/cil/oc/server/drivers/RedstoneDriver.scala
Normal file
17
li/cil/oc/server/drivers/RedstoneDriver.scala
Normal file
@ -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)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user