removed mention of udp from internet cards, those were a lie; text.serialize now line breaks and indents in pretty print mode; tostringing tables with metatable that has a __tostring method in text.serialize with pretty print; corrected comment in config; explicitly putting textures and localizations into the public domain in the license info; deleting empty file system folders from disk on save to reduce clutter in <saves>/opencomputers dir; doc field of callback annotations can now be read in computers, methods in proxies are tostring'ed to the docstring; returning false from component.modem.close() when no ports are open, now

This commit is contained in:
Florian Nücke 2014-02-11 23:35:46 +01:00
parent f48d3d304e
commit c4da02b65c
22 changed files with 146 additions and 69 deletions

View File

@ -46,15 +46,15 @@ class RobotProxy(val robot: Robot) extends Computer(robot.isClient) with ISidedI
// ----------------------------------------------------------------------- //
@Callback
@Callback(doc = """function():boolean -- Starts the robot. Returns true if the state changed.""")
def start(context: Context, args: Arguments): Array[AnyRef] =
result(!computer.isPaused && computer.start())
@Callback
@Callback(doc = """function():boolean -- Stops the robot. Returns true if the state changed.""")
def stop(context: Context, args: Arguments): Array[AnyRef] =
result(computer.stop())
@Callback(direct = true)
@Callback(direct = true, doc = """function():boolean -- Returns whether the robot is running.""")
def isRunning(context: Context, args: Arguments): Array[AnyRef] =
result(computer.isRunning)

View File

@ -25,10 +25,10 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable
// ----------------------------------------------------------------------- //
override protected val _buffer = new component.Buffer(this) {
@Callback
@Callback(doc = """function():boolean -- Returns whether the screen is currently on.""")
def isOn(computer: Context, args: Arguments): Array[AnyRef] = result(origin.isOn)
@Callback
@Callback(doc = """function():boolean -- Turns off the screen. Returns true if it was on.""")
def turnOn(computer: Context, args: Arguments): Array[AnyRef] = {
if (!origin.isOn) {
origin.turnOn()
@ -37,7 +37,7 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable
else result(false, origin.isOn)
}
@Callback
@Callback(doc = """function():boolean -- Turns the screen on. Returns true if it was off.""")
def turnOff(computer: Context, args: Arguments): Array[AnyRef] = {
if (origin.isOn) {
origin.turnOff()

View File

@ -54,25 +54,25 @@ class AbstractBus(val device: IBusDevice) extends ManagedComponent with IBusDriv
// ----------------------------------------------------------------------- //
@Callback
@Callback(doc = """function():boolean -- Whether the local bus interface is enabled.""")
def getEnabled(context: Context, args: Arguments): Array[AnyRef] = result(isEnabled)
@Callback
@Callback(doc = """function(enabled:boolean):boolean -- Sets whether the local bus interface should be enabled.""")
def setEnabled(context: Context, args: Arguments): Array[AnyRef] = {
isEnabled = args.checkBoolean(0)
result(isEnabled)
}
@Callback
@Callback(doc = """function():number -- Get the local interface address.""")
def getAddress(context: Context, args: Arguments): Array[AnyRef] = result(address)
@Callback
@Callback(doc = """function(address:number):number -- Sets the local interface address.""")
def setAddress(context: Context, args: Arguments): Array[AnyRef] = {
address = args.checkInteger(0) & 0xFFFF
result(address)
}
@Callback
@Callback(doc = """function(address:number, data:table):boolean -- Sends data across the abstract bus.""")
def send(context: Context, args: Arguments): Array[AnyRef] = {
val target = args.checkInteger(0) & 0xFFFF
val data = args.checkTable(1)
@ -84,7 +84,7 @@ class AbstractBus(val device: IBusDevice) extends ManagedComponent with IBusDriv
else result(false, "not enough energy")
}
@Callback(direct = true)
@Callback(direct = true, doc = """function():number -- The maximum packet size that can be sent over the bus.""")
def maxPacketSize(context: Context, args: Arguments): Array[AnyRef] = result(Settings.get.maxNetworkPacketSize)
// ----------------------------------------------------------------------- //

View File

@ -5,21 +5,21 @@ import li.cil.oc.common.tileentity.BundledRedstoneAware
class BundledRedstone(override val owner: BundledRedstoneAware) extends Redstone(owner) {
@Callback(direct = true)
@Callback(direct = true, doc = """function(side:number, color:number):number -- Get the bundled redstone input on the specified side and with the specified color.""")
def getBundledInput(context: Context, args: Arguments): Array[AnyRef] = {
val side = checkSide(args, 0)
val color = checkColor(args, 1)
result(owner.bundledInput(side, color))
}
@Callback(direct = true)
@Callback(direct = true, doc = """function(side:number, color:number):number -- Get the bundled redstone output on the specified side and with the specified color.""")
def getBundledOutput(context: Context, args: Arguments): Array[AnyRef] = {
val side = checkSide(args, 0)
val color = checkColor(args, 1)
result(owner.bundledOutput(side, color))
}
@Callback
@Callback(doc = """function(side:number, color:number, value:number):number -- Set the bundled redstone output on the specified side and with the specified color.""")
def setBundledOutput(context: Context, args: Arguments): Array[AnyRef] = {
val side = checkSide(args, 0)
val color = checkColor(args, 1)

View File

@ -44,10 +44,10 @@ class InternetCard extends ManagedComponent {
// ----------------------------------------------------------------------- //
@Callback(direct = true)
@Callback(direct = true, doc = """function():boolean -- Returns whether HTTP requests can be made (config setting).""")
def isHttpEnabled(context: Context, args: Arguments): Array[AnyRef] = result(Settings.get.httpEnabled)
@Callback
@Callback(doc = """function():boolean -- Starts an HTTP request. If this returns true, further results will be pushed using `http_response` signals.""")
def request(context: Context, args: Arguments): Array[AnyRef] = {
if (owner.isEmpty || context.node.address != owner.get.node.address) {
throw new IllegalArgumentException("can only be used by the owning computer")
@ -130,10 +130,10 @@ class InternetCard extends ManagedComponent {
})
}
@Callback(direct = true)
@Callback(direct = true, doc = """function():boolean -- Returns whether TCP connections can be made (config setting).""")
def isTcpEnabled(context: Context, args: Arguments): Array[AnyRef] = result(Settings.get.httpEnabled)
@Callback
@Callback(doc = """function(address:string[, port:number]):number -- Opens a new TCP connection. Returns the handle of the connection.""")
def connect(context: Context, args: Arguments): Array[AnyRef] = {
val address = args.checkString(0)
val port = if (args.count > 1) args.checkInteger(1) else -1
@ -151,7 +151,7 @@ class InternetCard extends ManagedComponent {
result(handle)
}
@Callback
@Callback(doc = """function(handle:number) -- Closes an open socket stream.""")
def close(context: Context, args: Arguments): Array[AnyRef] = {
val handle = args.checkInteger(0)
connections.remove(handle) match {
@ -161,7 +161,7 @@ class InternetCard extends ManagedComponent {
null
}
@Callback
@Callback(doc = """function(handle:number, data:string):number -- Tries to write data to the socket stream. Returns the number of bytes written.""")
def write(context: Context, args: Arguments): Array[AnyRef] = {
val handle = args.checkInteger(0)
val value = args.checkByteArray(1)
@ -173,7 +173,7 @@ class InternetCard extends ManagedComponent {
}
}
@Callback
@Callback(doc = """function(handle:number, n:number):string -- Tries to read data from the socket stream. Returns the read byte array.""")
def read(context: Context, args: Arguments): Array[AnyRef] = {
val handle = args.checkInteger(0)
val n = math.min(Settings.get.maxReadBuffer, math.max(0, args.checkInteger(1)))

View File

@ -17,17 +17,18 @@ class NetworkCard extends ManagedComponent {
// ----------------------------------------------------------------------- //
@Callback
@Callback(doc = """function(port:number):boolean -- Opens the specified port. Returns true if the port was opened.""")
def open(context: Context, args: Arguments): Array[AnyRef] = {
val port = checkPort(args.checkInteger(0))
result(openPorts.add(port))
}
@Callback
@Callback(doc = """function([port:number]):boolean -- Closes the specified port (default: all ports). Returns true if ports were closed.""")
def close(context: Context, args: Arguments): Array[AnyRef] = {
if (args.count == 0) {
val closed = openPorts.size > 0
openPorts.clear()
result(true)
result(closed)
}
else {
val port = checkPort(args.checkInteger(0))
@ -35,16 +36,16 @@ class NetworkCard extends ManagedComponent {
}
}
@Callback(direct = true)
@Callback(direct = true, doc = """function(port:number):boolean -- Whether the specified port is open.""")
def isOpen(context: Context, args: Arguments): Array[AnyRef] = {
val port = checkPort(args.checkInteger(0))
result(openPorts.contains(port))
}
@Callback(direct = true)
@Callback(direct = true, doc = """function():boolean -- Whether this is a wireless network card.""")
def isWireless(context: Context, args: Arguments): Array[AnyRef] = result(false)
@Callback
@Callback(doc = """function(address:string, port:number, data...) -- Sends the specified data to the specified target.""")
def send(context: Context, args: Arguments): Array[AnyRef] = {
val address = args.checkString(0)
val port = checkPort(args.checkInteger(1))
@ -53,7 +54,7 @@ class NetworkCard extends ManagedComponent {
result(true)
}
@Callback
@Callback(doc = """function(port:number, data...) -- Broadcasts the specified data on the specified port.""")
def broadcast(context: Context, args: Arguments): Array[AnyRef] = {
val port = checkPort(args.checkInteger(0))
checkPacketSize(args.drop(1))
@ -61,7 +62,7 @@ class NetworkCard extends ManagedComponent {
result(true)
}
@Callback(direct = true)
@Callback(direct = true, doc = """function():number -- Gets the maximum packet size (config setting).""")
def maxPacketSize(context: Context, args: Arguments): Array[AnyRef] = result(Settings.get.maxNetworkPacketSize)
// ----------------------------------------------------------------------- //

View File

@ -12,19 +12,19 @@ class Redstone(val owner: RedstoneAware) extends ManagedComponent {
// ----------------------------------------------------------------------- //
@Callback(direct = true)
@Callback(direct = true, doc = """function(side:number):number -- Get the redstone input on the specified side.""")
def getInput(context: Context, args: Arguments): Array[AnyRef] = {
val side = checkSide(args, 0)
result(owner.input(side))
}
@Callback(direct = true)
@Callback(direct = true, doc = """function(side:number):number -- Get the redstone output on the specified side.""")
def getOutput(context: Context, args: Arguments): Array[AnyRef] = {
val side = checkSide(args, 0)
result(owner.output(side))
}
@Callback()
@Callback(doc = """function(side:number, value:number):number -- Set the redstone output on the specified side.""")
def setOutput(context: Context, args: Arguments): Array[AnyRef] = {
val side = checkSide(args, 0)
val value = args.checkInteger(1)

View File

@ -17,7 +17,7 @@ class UpgradeCrafting(val owner: MCTileEntity) extends ManagedComponent {
withComponent("crafting").
create()
@Callback
@Callback(doc = """function([count:number]):number -- Tries to craft the specified number of items in the top left area of the inventory.""")
def craft(context: RobotContext, args: Arguments): Array[AnyRef] = {
val count = if (args.count > 0) args.checkInteger(0) else Int.MaxValue
result(CraftingInventory.craft(context, count))

View File

@ -21,7 +21,7 @@ class UpgradeGenerator(val owner: TileEntity) extends ManagedComponent {
// ----------------------------------------------------------------------- //
@Callback
@Callback(doc = """function([count:number]):boolean -- Tries to insert fuel from the selected slot into the generator's queue.""")
def insert(context: RobotContext, args: Arguments): Array[AnyRef] = {
val count = if (args.count > 0) args.checkInteger(0) else 64
val player = context.player
@ -48,7 +48,7 @@ class UpgradeGenerator(val owner: TileEntity) extends ManagedComponent {
result(true)
}
@Callback
@Callback(doc = """function():number -- Get the size of the item stack in the generator's queue.""")
def count(context: Context, args: Arguments): Array[AnyRef] = {
inventory match {
case Some(stack) => result(stack.stackSize)
@ -56,7 +56,7 @@ class UpgradeGenerator(val owner: TileEntity) extends ManagedComponent {
}
}
@Callback
@Callback(doc = """function([count:number]):boolean -- Tries to remove items from the generator's queue.""")
def remove(context: RobotContext, args: Arguments): Array[AnyRef] = {
val count = if (args.count > 0) args.checkInteger(0) else Int.MaxValue
inventory match {

View File

@ -11,7 +11,7 @@ class UpgradeNavigation(val owner: TileEntity, val xCenter: Int, val zCenter: In
// ----------------------------------------------------------------------- //
@Callback
@Callback(doc = """function():number, number, number -- Get the current relative position of the robot.""")
def getPosition(context: Context, args: Arguments): Array[AnyRef] = {
val x = owner.xCoord
val y = owner.yCoord
@ -25,7 +25,7 @@ class UpgradeNavigation(val owner: TileEntity, val xCenter: Int, val zCenter: In
result(Unit, "out of range")
}
@Callback
@Callback(doc = """function():number -- Get the current orientation of the robot.""")
def getFacing(context: Context, args: Arguments): Array[AnyRef] = {
owner match {
case rotatable: Rotatable => result(rotatable.facing.ordinal)
@ -33,7 +33,7 @@ class UpgradeNavigation(val owner: TileEntity, val xCenter: Int, val zCenter: In
}
}
@Callback
@Callback(doc = """function():number -- Get the operational range of the navigation upgrade.""")
def getRange(context: Context, args: Arguments): Array[AnyRef] = {
result(size / 2)
}

View File

@ -12,7 +12,7 @@ class UpgradeSign(val owner: TileEntity) extends ManagedComponent {
// ----------------------------------------------------------------------- //
@Callback
@Callback(doc = """function():string -- Get the text on the sign in front of the robot.""")
def getValue(context: Context, args: Arguments): Array[AnyRef] = {
val facing = owner match {
case rotatable: Rotatable => rotatable.facing
@ -24,7 +24,7 @@ class UpgradeSign(val owner: TileEntity) extends ManagedComponent {
}
}
@Callback
@Callback(doc = """function(value:string):string -- Set the text on the sign in front of the robot.""")
def setValue(context: Context, args: Arguments): Array[AnyRef] = {
val text = args.checkString(0).lines.padTo(4, "").map(line => if (line.length > 15) line.substring(0, 15) else line)
val facing = owner match {

View File

@ -20,10 +20,10 @@ class WirelessNetworkCard(val owner: TileEntity) extends NetworkCard {
// ----------------------------------------------------------------------- //
@Callback(direct = true)
@Callback(direct = true, doc = """function():number -- Get the signal strength (range) used when sending messages.""")
def getStrength(context: Context, args: Arguments): Array[AnyRef] = result(strength)
@Callback
@Callback(doc = """function(strength:number):number -- Set the signal strength (range) used when sending messages.""")
def setStrength(context: Context, args: Arguments): Array[AnyRef] = {
strength = math.max(args.checkDouble(0), math.min(0, Settings.get.maxWirelessRange))
result(strength)

View File

@ -218,7 +218,12 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
counts(method) += 1
}
component.invoke(method, this, args: _*)
case _ => throw new Exception("no such component")
case _ => throw new IllegalArgumentException("no such component")
}
private[component] def doc(address: String, method: String) = Option(node.network.node(address)) match {
case Some(component: server.network.Component) if component.canBeSeenFrom(node) || component == node => component.doc(method)
case _ => throw new IllegalArgumentException("no such component")
}
private[component] def addUser(name: String) {
@ -248,15 +253,15 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
// ----------------------------------------------------------------------- //
@Callback
@Callback(doc = """function():boolean -- Starts the computer. Returns true if the state changed.""")
def start(context: Context, args: Arguments): Array[AnyRef] =
result(!isPaused && start())
@Callback
@Callback(doc = """function():boolean -- Stops the computer. Returns true if the state changed.""")
def stop(context: Context, args: Arguments): Array[AnyRef] =
result(stop())
@Callback(direct = true)
@Callback(direct = true, doc = """function():boolean -- Returns whether the computer is running.""")
def isRunning(context: Context, args: Arguments): Array[AnyRef] =
result(isRunning)

View File

@ -1,5 +1,6 @@
package li.cil.oc.server.component.machine
import com.google.common.base.Strings
import com.naef.jnlua._
import java.io.{IOException, FileNotFoundException}
import java.util.logging.Level
@ -528,6 +529,29 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
})
lua.setField(-2, "invoke")
lua.pushScalaFunction(lua => {
val address = lua.checkString(1)
val method = lua.checkString(2)
try {
val doc = machine.doc(address, method)
if (Strings.isNullOrEmpty(doc))
lua.pushNil()
else
lua.pushString(doc)
1
} catch {
case e: NoSuchMethodException =>
lua.pushNil()
lua.pushString("no such method")
2
case t: Throwable =>
lua.pushNil()
lua.pushString(if (t.getMessage != null) t.getMessage else t.toString)
2
}
})
lua.setField(-2, "doc")
lua.setGlobal("component")
initPerms()

View File

@ -55,7 +55,10 @@ trait Buffered extends OutputStreamFileSystem {
}
setLastModified(path, directory.lastModified())
}
recurse("", fileRoot)
if (fileRoot.list() == null || fileRoot.list().length == 0) {
fileRoot.delete()
}
else recurse("", fileRoot)
super.load(nbt)
}
@ -94,6 +97,9 @@ trait Buffered extends OutputStreamFileSystem {
}
directory.setLastModified(lastModified(path))
}
recurse("")
if (list("") == null || list("").length == 0) {
fileRoot.delete()
}
else recurse("")
}
}

View File

@ -99,6 +99,11 @@ trait Component extends network.Component with Node {
def methods = callbacks.keySet
def doc(name: String) = callbacks.get(name) match {
case Some(callback) => callback.doc
case _ => throw new NoSuchMethodException()
}
def invoke(method: String, context: Context, arguments: AnyRef*) =
callbacks.get(method) match {
case Some(callback) => hosts(method) match {
@ -173,7 +178,7 @@ object Component {
val a = m.getAnnotation[network.Callback](classOf[network.Callback])
val name = if (a.value != null && a.value.trim != "") a.value else m.getName
if (!callbacks.contains(name)) {
callbacks += name -> new ComponentCallback(m, a.direct, a.limit)
callbacks += name -> new ComponentCallback(m, a.direct, a.limit, a.doc)
}
}
)
@ -201,11 +206,11 @@ object Component {
// ----------------------------------------------------------------------- //
abstract class Callback(val direct: Boolean, val limit: Int) {
abstract class Callback(val direct: Boolean, val limit: Int, val doc: String = "") {
def apply(instance: Environment, context: Context, args: Arguments): Array[AnyRef]
}
class ComponentCallback(val method: Method, direct: Boolean, limit: Int) extends Callback(direct, limit) {
class ComponentCallback(val method: Method, direct: Boolean, limit: Int, doc: String) extends Callback(direct, limit, doc) {
override def apply(instance: Environment, context: Context, args: Arguments) = try {
method.invoke(instance, context, args).asInstanceOf[Array[AnyRef]]
} catch {

View File

@ -17,3 +17,10 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-------------------------------------------------------------------------------
All images / textures and localization strings (resources) are put in the
public domain. More specicially, see CC0 1.0 Universal:
http://creativecommons.org/publicdomain/zero/1.0/

View File

@ -133,7 +133,7 @@ oc:tooltip.Disk=Sehr einfaches Speichermedium, das verwendet werden kann, um Dis
oc:tooltip.DiskDrive.CC=ComputerCraft-Disketten werden §aunterstützt§7.
oc:tooltip.DiskDrive=Erlaubt es, Disketten zu lesen und zu beschreiben.
oc:tooltip.GraphicsCard=Erlaubt es, den angezeigten Inhalt von Bildschirmen zu ändern.[nl] Höchstauflösung: §f%sx%s§7.[nl] Maximale Farbtiefe: §f%s§7.[nl] Operationen/Tick: §f%s§7.
oc:tooltip.InternetCard=Diese Karte erlaubt es, HTTP-Anfragen zu senden und echte TCP und UDP Sockets zu verwenden.
oc:tooltip.InternetCard=Diese Karte erlaubt es, HTTP-Anfragen zu senden und echte TCP Sockets zu verwenden.
oc:tooltip.IronNugget=Ein Nugget, das aus Eisen besteht, darum wird es ja auch Eisennugget genannt, duh...
oc:tooltip.Keyboard=Kann an Bildschirmen befestigt werden, um auf ihnen zu tippen.
oc:tooltip.Memory=Braucht ein jeder Computer, um zu starten. Je mehr vorhanden, desto komplexere Programme können ausgeführt werden.

View File

@ -133,7 +133,7 @@ oc:tooltip.Disk=Primitive medium that can be used to build persistent storage de
oc:tooltip.DiskDrive.CC=ComputerCraft floppies are §asupported§7.
oc:tooltip.DiskDrive=Allows reading and writing floppies.
oc:tooltip.GraphicsCard=Used to change what's displayed on screens.[nl] Maximum resolution: §f%sx%s§7.[nl] Maximum color depth: §f%s§7.[nl] Operations/tick: §f%s§7.
oc:tooltip.InternetCard=This card allows making HTTP requests and using real TCP and UDP sockets.
oc:tooltip.InternetCard=This card allows making HTTP requests and using real TCP sockets.
oc:tooltip.IronNugget=A nugget made of iron, that's why it's called an Iron Nugget, duh...
oc:tooltip.Keyboard=Can be attached to screens to allow typing on them.
oc:tooltip.Memory=Required to get computers to run. The more you have, the more complex the programs you can run.

View File

@ -259,7 +259,17 @@ sandbox._G = sandbox
-------------------------------------------------------------------------------
-- Start of non-standard stuff made available via package.preload.
local libcomponent = {
local libcomponent
libcomponent = {
doc = function(address, method)
checkArg(1, address, "string")
checkArg(2, method, "string")
local result, reason = component.doc(address, method)
if not result and reason then
error(reason, 2)
end
return result
end,
invoke = function(address, method, ...)
checkArg(1, address, "string")
checkArg(2, method, "string")
@ -288,9 +298,14 @@ local libcomponent = {
return nil, reason
end
for method, direct in pairs(methods) do
proxy[method] = function(...)
proxy[method] = setmetatable({}, {
__call = function(_, ...)
return invoke(direct, address, method, ...)
end,
__tostring = function()
return libcomponent.doc(address, method) or "function"
end
})
end
return proxy
end,

View File

@ -95,7 +95,7 @@ function text.serialize(value, pretty)
["until"]=true, ["while"]=true}
local id = "^[%a_][%w_]*$"
local ts = {}
local function s(v)
local function s(v, l)
local t = type(v)
if t == "nil" then
return "nil"
@ -113,6 +113,8 @@ function text.serialize(value, pretty)
end
elseif t == "string" then
return string.format("%q", v)
elseif t == "table" and pretty and getmetatable(v) and getmetatable(v).__tostring then
return tostring(v)
elseif t == "table" then
if ts[v] then
if pretty then
@ -122,24 +124,36 @@ function text.serialize(value, pretty)
end
end
ts[v] = true
local i, r = 1, nil
for k, v in pairs(v) do
local f, i, r = 1, nil
if pretty then
local ks = {}
for k in pairs(v) do table.insert(ks, k) end
table.sort(ks)
local k = 0
f = function()
k = k + 1
return ks[k], ks[k] and v[ks[k]] or nil
end
else
f = pairs(v)
end
for k, v in f do
if r then
r = r .. ","
r = r .. "," .. (pretty and ("\n" .. string.rep(" ", l)) or "")
else
r = "{"
end
local tk = type(k)
if tk == "number" and k == i then
i = i + 1
r = r .. s(v)
r = r .. s(v, l + 1)
else
if tk == "string" and not kw[k] and string.match(k, id) then
r = r .. k
else
r = r .. "[" .. s(k) .. "]"
r = r .. "[" .. s(k, l + 1) .. "]"
end
r = r .. "=" .. s(v)
r = r .. "=" .. s(v, l + 1)
end
end
ts[v] = nil -- allow writing same table more than once
@ -152,7 +166,7 @@ function text.serialize(value, pretty)
end
end
end
local result = s(value)
local result = s(value, 1)
local limit = type(pretty) == "number" and pretty or 1000
if pretty and unicode.len(result) > limit then
return result:sub(1, limit) .. "..."

View File

@ -101,7 +101,7 @@ opencomputers {
activeGC: true
# The sizes of the three tiers of RAM, in kilobytes. This list must
# contain exactly three entries, or it will be ignored.
# contain exactly five entries, or it will be ignored.
ramSizes: [
64
128