Conflicts:
	li/cil/oc/common/tileentity/Router.scala
This commit is contained in:
Florian Nücke 2014-01-05 23:04:35 +01:00
commit a08ccbe7d0
23 changed files with 132 additions and 35 deletions

View File

@ -69,6 +69,7 @@ oc:gui.Robot.TurnOff=关闭
oc:gui.Robot.TurnOn=开启
# Containers
oc:container.Adapter=适配器
oc:container.Case=计算机
oc:container.DiskDrive=磁盘驱动

View File

@ -66,8 +66,10 @@ sandbox = {
end,
ipairs = ipairs,
load = function(ld, source, mode, env)
assert((mode or "t") == "t", "unsupported mode")
return load(ld, source, "t", env or sandbox)
if not allowBytecode() then
mode = "t"
end
return load(ld, source, mode, env or sandbox)
end,
loadfile = nil, -- in lib/base.lua
next = next,

View File

@ -209,6 +209,7 @@ local function insert(value)
end
local function enter()
term.setCursorBlink(false)
local cx, cy = term.getCursor()
local cbx, cby = getCursor()
local w, h = getSize()

View File

@ -84,7 +84,7 @@ end
function shell.setPath(value)
checkArg(1, value, "string")
path = {}
for p in string:gmatch(value, "[^:]") do
for p in string.gmatch(value, "[^:]+") do
p = fs.canonical(text.trim(p))
if unicode.sub(p, 1, 1) ~= "/" then
p = "/" .. p

View File

@ -345,7 +345,7 @@ function term.write(value, wrap)
while wrap and unicode.len(line) > w - (cursorX - 1) do
local partial = unicode.sub(line, 1, w - (cursorX - 1))
local wordWrapped = partial:match("(.*[^a-zA-Z0-9._])")
if wordWrapped or unicode.len(partial) > w then
if wordWrapped or unicode.len(line) > w then
partial = wordWrapped or partial
line = unicode.sub(line, unicode.len(partial) + 1)
component.gpu.set(cursorX, cursorY, partial)

View File

@ -222,7 +222,7 @@ diskDrive {
powerConverter {
input: [["", "oc:circuitAdvanced", ""]
[plateAluminium, craftingGenerator, plateAluminium]
[plateAluminium, {item=Electric, subID=4}, plateAluminium]
["oc:craftingCircuitBoardPrinted", craftingToolWrench, "oc:craftingCircuitBoardPrinted"]]
}
powerDistributor {
@ -251,12 +251,12 @@ screen1 {
[plateAluminium, plateAluminium, craftingToolScrewdriver]]
}
screen2 {
input: [[plateStainlessSteel, {item=dyePowder, subID=1}, craftingToolWrench]
["oc:circuitAdvanced", {item=dyePowder, subID=2}, "oc:craftingScreenBasic"]
[plateStainlessSteel, {item=dyePowder, subID=4}, craftingToolScrewdriver]]
input: [[plateStainlessSteel, screwStainlessSteel, craftingToolWrench]
["oc:circuitAdvanced", "oc:craftingScreenBasic", craftingMonitorTier02]
[plateStainlessSteel, screwStainlessSteel, craftingToolScrewdriver]]
}
screen3 {
input: [[plateTitanium, "oc:craftingCircuitBoardPrinted", craftingToolWrench]
["oc:circuitElite", "oc:circuitElite", "oc:craftingScreenAdvanced"]
["oc:circuitElite", "oc:craftingScreenAdvanced", "oc:circuitElite"]
[plateTitanium, "oc:craftingCircuitBoardPrinted", craftingToolScrewdriver]]
}

View File

@ -22,6 +22,7 @@ class Settings(config: Config) {
val maxScreenTextRenderDistance = config.getDouble("client.maxScreenTextRenderDistance")
val textLinearFiltering = config.getBoolean("client.textLinearFiltering")
val textAntiAlias = config.getBoolean("client.textAntiAlias")
val pasteShortcut = config.getStringList("client.pasteShortcut").toSet
val rTreeDebugRenderer = false // *Not* to be configurable via config file.
// ----------------------------------------------------------------------- //
@ -41,6 +42,7 @@ class Settings(config: Config) {
val canComputersBeOwned = config.getBoolean("computer.canComputersBeOwned")
val maxUsers = config.getInt("computer.maxUsers") max 0
val maxUsernameLength = config.getInt("computer.maxUsernameLength") max 0
val allowBytecode = config.getBoolean("computer.allowBytecode")
// ----------------------------------------------------------------------- //
// robot

View File

@ -1,5 +1,6 @@
package li.cil.oc.client.gui
import li.cil.oc.Settings
import li.cil.oc.client.PacketSender
import li.cil.oc.client.renderer.MonospaceFontRenderer
import li.cil.oc.client.renderer.gui.BufferRenderer
@ -70,12 +71,8 @@ trait Buffer extends GuiScreen {
if (NEI.isInputFocused) return
val code = Keyboard.getEventKey
if (code != Keyboard.KEY_ESCAPE && code != Keyboard.KEY_F11)
if (code == Keyboard.KEY_INSERT && GuiScreen.isShiftKeyDown) {
if (Keyboard.getEventKeyState)
PacketSender.sendClipboard(buffer.owner, GuiScreen.getClipboardString)
}
else if (Keyboard.getEventKeyState) {
if (code != Keyboard.KEY_ESCAPE && code != Keyboard.KEY_F11) {
if (Keyboard.getEventKeyState) {
val char = Keyboard.getEventCharacter
if (!pressedKeys.contains(code) || !ignoreRepeat(char, code)) {
PacketSender.sendKeyDown(buffer.owner, char, code)
@ -86,6 +83,11 @@ trait Buffer extends GuiScreen {
case Some(char) => PacketSender.sendKeyUp(buffer.owner, char, code)
case _ => // Wasn't pressed while viewing the screen.
}
if (isPasteShortcutPressed && Keyboard.getEventKeyState) {
PacketSender.sendClipboard(buffer.owner, GuiScreen.getClipboardString)
}
}
}
override protected def mouseClicked(x: Int, y: Int, button: Int) {
@ -107,4 +109,9 @@ trait Buffer extends GuiScreen {
code == Keyboard.KEY_LMETA ||
code == Keyboard.KEY_RMETA
}
private def isPasteShortcutPressed = {
val want = Settings.get.pasteShortcut.map(name => Keyboard.getKeyIndex(name.toUpperCase)).filter(_ != Keyboard.KEY_NONE)
pressedKeys.keySet.equals(want)
}
}

View File

@ -59,8 +59,8 @@ class Screen(val screen: tileentity.Screen) extends Buffer {
}
private def clickOrDrag(mouseX: Int, mouseY: Int) {
val bx = (mouseX - x - bufferMargin) / MonospaceFontRenderer.fontWidth + 1
val by = (mouseY - y - bufferMargin) / MonospaceFontRenderer.fontHeight + 1
val bx = ((mouseX - x - bufferMargin) / scale / MonospaceFontRenderer.fontWidth).toInt + 1
val by = ((mouseY - y - bufferMargin) / scale / MonospaceFontRenderer.fontHeight).toInt + 1
val (bw, bh) = screen.buffer.resolution
if (bx > 0 && by > 0 && bx <= bw && by <= bh) {
if (bx != mx || by != my) {

View File

@ -7,6 +7,7 @@ import li.cil.oc.server.component.Keyboard
import li.cil.oc.server.driver
import li.cil.oc.server.fs
import li.cil.oc.server.network
import li.cil.oc.server.network.Network
import li.cil.oc.util.WirelessNetwork
import net.minecraftforge.common.MinecraftForge
@ -50,5 +51,6 @@ class Proxy {
GameRegistry.registerPlayerTracker(Keyboard)
MinecraftForge.EVENT_BUS.register(WirelessNetwork)
MinecraftForge.EVENT_BUS.register(Network)
}
}

View File

@ -5,7 +5,7 @@ import li.cil.oc.{Blocks, api, common}
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.nbt.NBTTagCompound
class Cable extends Environment with Analyzable {
class Cable extends Environment with Analyzable with PassiveNode {
val node = api.Network.newNode(this, Visibility.None).create()
def onAnalyze(stats: NBTTagCompound, player: EntityPlayer, side: Int, hitX: Float, hitY: Float, hitZ: Float) = null
@ -16,7 +16,7 @@ class Cable extends Environment with Analyzable {
override def validate() {
super.validate()
world.scheduleBlockUpdateFromLoad(x, y, z, Blocks.cable.parent.blockID, Int.MinValue, 0)
world.scheduleBlockUpdateFromLoad(x, y, z, Blocks.cable.parent.blockID, 0, 0)
}
override def getRenderBoundingBox = common.block.Cable.bounds(world, x, y, z).offset(x, y, z)

View File

@ -5,7 +5,7 @@ import li.cil.oc.{Blocks, Settings, api}
import net.minecraftforge.common.ForgeDirection
import scala.collection.convert.WrapAsScala._
class Capacitor extends Environment {
class Capacitor extends Environment with PassiveNode {
// Start with maximum theoretical capacity, gets reduced after validation.
// This is done so that we don't lose energy while loading.
val node = api.Network.newNode(this, Visibility.Network).
@ -16,7 +16,7 @@ class Capacitor extends Environment {
override def validate() {
super.validate()
world.scheduleBlockUpdateFromLoad(x, y, z, Blocks.capacitor.parent.blockID, Int.MinValue, 0)
world.scheduleBlockUpdateFromLoad(x, y, z, Blocks.capacitor.parent.blockID, 0, 0)
}
override def invalidate() {

View File

@ -155,7 +155,7 @@ abstract class Computer(isRemote: Boolean) extends Environment with ComponentInv
override protected def onRedstoneInputChanged(side: ForgeDirection) {
super.onRedstoneInputChanged(side)
computer.signal("redstone_changed", computer.address, Int.box(side.ordinal()))
computer.signal("redstone_changed", computer.address, Int.box(toLocal(side).ordinal()))
}
// ----------------------------------------------------------------------- //

View File

@ -8,7 +8,7 @@ import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
class DiskDrive extends Environment with ComponentInventory with Rotatable with Analyzable {
class DiskDrive extends Environment with ComponentInventory with Rotatable with Analyzable with PassiveNode {
val node = api.Network.newNode(this, Visibility.None).create()
// ----------------------------------------------------------------------- //
@ -23,7 +23,7 @@ class DiskDrive extends Environment with ComponentInventory with Rotatable with
override def validate() = {
super.validate()
world.scheduleBlockUpdateFromLoad(x, y, z, Blocks.diskDrive.parent.blockID, Int.MinValue, 0)
world.scheduleBlockUpdateFromLoad(x, y, z, Blocks.diskDrive.parent.blockID, 0, 0)
}
// ----------------------------------------------------------------------- //

View File

@ -9,7 +9,7 @@ import net.minecraft.entity.player.EntityPlayer
import net.minecraft.nbt.NBTTagCompound
import net.minecraftforge.common.ForgeDirection
class Keyboard(isRemote: Boolean) extends Environment with SidedEnvironment with Analyzable with Rotatable {
class Keyboard(isRemote: Boolean) extends Environment with SidedEnvironment with Analyzable with Rotatable with PassiveNode {
def this() = this(false)
val keyboard = if (isRemote) null else new component.Keyboard(this)
@ -37,7 +37,7 @@ class Keyboard(isRemote: Boolean) extends Environment with SidedEnvironment with
override def validate() {
super.validate()
world.scheduleBlockUpdateFromLoad(x, y, z, Blocks.keyboard.parent.blockID, Int.MinValue, 0)
world.scheduleBlockUpdateFromLoad(x, y, z, Blocks.keyboard.parent.blockID, 0, 0)
}
override def readFromNBT(nbt: NBTTagCompound) {

View File

@ -0,0 +1,5 @@
package li.cil.oc.common.tileentity
// Marker for tile entities that don't tick and therefore have to be added to
// the component network manually (see Network singleton).
trait PassiveNode

View File

@ -11,7 +11,7 @@ import net.minecraftforge.common.ForgeDirection
import scala.collection.mutable
@Optional.Interface(iface = "dan200.computer.api.IPeripheral", modid = "ComputerCraft")
class Router extends TileEntity with api.network.SidedEnvironment with IPeripheral {
class Router extends TileEntity with api.network.SidedEnvironment with IPeripheral with PassiveNode {
private val plugs = ForgeDirection.VALID_DIRECTIONS.map(side => new Plug(side))
// ----------------------------------------------------------------------- //
@ -27,7 +27,7 @@ class Router extends TileEntity with api.network.SidedEnvironment with IPeripher
override def validate() {
super.validate()
worldObj.scheduleBlockUpdateFromLoad(xCoord, yCoord, zCoord, Blocks.router.parent.blockID, Int.MinValue, 0)
worldObj.scheduleBlockUpdateFromLoad(xCoord, yCoord, zCoord, Blocks.router.parent.blockID, 0, 0)
}
override def invalidate() {

View File

@ -90,11 +90,12 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable
if (ax <= border || ay <= border || ax >= width - border || ay >= height - border) {
return false
}
val (rx, ry) = ((ax - border) / (width - border * 2), (ay - border) / (height - border * 2))
val (iw, ih) = (width - border * 2, height - border * 2)
val (rx, ry) = ((ax - border) / iw, (ay - border) / ih)
// Make it a relative position in the displayed buffer.
val (bw, bh) = origin.buffer.resolution
val (bpw, bph) = (bw * MonospaceFontRenderer.fontWidth, bh * MonospaceFontRenderer.fontHeight)
val (bpw, bph) = (bw * MonospaceFontRenderer.fontWidth / iw.toDouble, bh * MonospaceFontRenderer.fontHeight / ih.toDouble)
val (brx, bry) = if (bpw > bph) {
val rh = bph.toDouble / bpw.toDouble
val bry = (ry - (1 - rh) * 0.5) / rh
@ -218,7 +219,11 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable
}
val buffer = screen.buffer
val (w, h) = buffer.resolution
buffer.buffer.fill(0, 0, w, h, ' ')
buffer.foreground = 0xFFFFFF
buffer.background = 0x000000
if (buffer.buffer.fill(0, 0, w, h, ' ')) {
onScreenFill(0, 0, w, h, ' ')
}
}
)
}

View File

@ -108,7 +108,12 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
def start() = state.synchronized(state.top match {
case Computer.State.Stopped =>
if (owner.installedMemory > 0) {
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)")
false
}
else if (owner.installedMemory > 0) {
if (Settings.get.ignorePower || node.globalBuffer > cost) {
init() && {
switchTo(Computer.State.Starting)
@ -960,6 +965,13 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
})
lua.setGlobal("print")
// Whether bytecode may be loaded directly.
lua.pushScalaFunction(lua => {
lua.pushBoolean(Settings.get.allowBytecode)
1
})
lua.setGlobal("allowBytecode")
// How long programs may run without yielding before we stop them.
lua.pushNumber(Settings.get.timeout)
lua.setGlobal("timeout")

View File

@ -33,7 +33,7 @@ abstract class GraphicsCard extends ManagedComponent {
override def update() {
super.update()
if (screenInstance.isEmpty && screenAddress.isDefined) {
if (node.network != null && screenInstance.isEmpty && screenAddress.isDefined) {
Option(node.network.node(screenAddress.get)) match {
case Some(node: Node) if node.host.isInstanceOf[Buffer] =>
screenInstance = Some(node.host.asInstanceOf[Buffer])
@ -202,6 +202,23 @@ abstract class GraphicsCard extends ManagedComponent {
// ----------------------------------------------------------------------- //
override def onMessage(message: Message) {
super.onMessage(message)
if (message.name == "computer.stopped" && node.isNeighborOf(message.source)) {
screenInstance match {
case Some(buffer) => buffer.synchronized {
val (w, h) = buffer.resolution
buffer.foreground = 0xFFFFFF
buffer.background = 0x000000
if (buffer.buffer.fill(0, 0, w, h, ' ')) {
buffer.owner.onScreenFill(0, 0, w, h, ' ')
}
}
case _ =>
}
}
}
override def onDisconnect(node: Node) {
super.onDisconnect(node)
if (node == this.node || screenAddress.exists(_ == node.address)) {

View File

@ -60,6 +60,9 @@ class NetworkCard extends ManagedComponent {
result(true)
}
@LuaCallback(value = "maxPacketSize", direct = true)
def maxPacketSize(context: Context, args: Arguments): Array[AnyRef] = result(Settings.get.maxNetworkPacketSize)
// ----------------------------------------------------------------------- //
override def onDisconnect(node: Node) {

View File

@ -3,11 +3,15 @@ package li.cil.oc.server.network
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.relauncher.Side
import li.cil.oc.api.network.{Node => ImmutableNode, SidedEnvironment, Environment, Visibility}
import li.cil.oc.common.tileentity.PassiveNode
import li.cil.oc.server.network.{Node => MutableNode}
import li.cil.oc.{Settings, api}
import net.minecraft.tileentity.TileEntity
import net.minecraftforge.common.ForgeDirection
import net.minecraftforge.event.ForgeSubscribe
import net.minecraftforge.event.world.{ChunkEvent, WorldEvent}
import scala.collection.JavaConverters._
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
@ -345,6 +349,28 @@ private class Network private(private val data: mutable.Map[String, Network.Vert
}
object Network extends api.detail.NetworkAPI {
@ForgeSubscribe
def onWorldLoad(e: WorldEvent.Load) {
val world = e.world
if (!world.isRemote) {
for (t <- world.loadedTileEntityList) t match {
case p: TileEntity with PassiveNode => p.getBlockType.updateTick(world, p.xCoord, p.yCoord, p.zCoord, world.rand)
case _ =>
}
}
}
@ForgeSubscribe
def onChunkLoad(e: ChunkEvent.Load) {
val world = e.world
if (!world.isRemote) {
for (t <- e.getChunk.chunkTileEntityMap.values) t match {
case p: TileEntity with PassiveNode => p.getBlockType.updateTick(world, p.xCoord, p.yCoord, p.zCoord, world.rand)
case _ =>
}
}
}
override def joinOrCreateNetwork(tileEntity: TileEntity): Unit =
if (!tileEntity.getWorldObj.isRemote) {
for (side <- ForgeDirection.VALID_DIRECTIONS) {

View File

@ -47,6 +47,12 @@ opencomputers {
# If you prefer the text on the screens to be aliased (you know, *not*
# anti-aliased / smoothed) turn this option off.
textAntiAlias: true
# The keyboard shortcut that is used to send the current text in the
# clipboard to the currently opened screen (if said screen has a keyboard
# attached to it). For valid key names, please see the following list:
# https://github.com/LWJGL/lwjgl/blob/master/src/java/org/lwjgl/input/Keyboard.java#L73
pasteShortcut: [LSHIFT, INSERT]
}
# Computer related settings, concerns server performance and security.
@ -66,6 +72,14 @@ opencomputers {
# already running - they'll have to be rebooted for this to take effect.
timeout: 1.0
# Whether to allow loading precompiled bytecode via Lua's `load` function,
# or related functions (`loadfile`, `dofile`). Enable this only if you
# absolutely trust all users on your server and all Lua code you run. This
# can be a MASSIVE SECURITY RISK, since precompiled code can easily be
# used for exploits, running arbitrary code on the real server! I cannot
# stress this enough: only enable this is you know what you're doing.
allowBytecode: false
# The time in seconds to wait after a computer has been restored before it
# continues to run. This is meant to allow the world around the computer
# to settle, avoiding issues such as components in neighboring chunks
@ -490,7 +504,7 @@ opencomputers {
# strength one, which means the signal reaches one block. This is
# scaled up linearly, so for example to send a signal 400 blocks a
# signal strength of 400 is required, costing a total of
# 400 * `wirelessCostPerRange`. In other words, the higher this value,
# 400 * `wirelessStrength`. In other words, the higher this value,
# the higher the cost of wireless messages.
# See also: `maxWirelessRange`.
wirelessStrength: 0.05
@ -623,7 +637,7 @@ opencomputers {
# This is used to limit the search range in which to check for modems,
# which may or may not lead to performance issues for ridiculous ranges -
# like, you know, more than the loaded area.
# See also: `wirelessCostPerRange`.
# See also: `wirelessStrength`.
maxWirelessRange: 400
# The user name to specify when executing a command via a command block.