Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Wobbo 2014-02-22 15:28:25 +01:00
commit 36f93eae9e
56 changed files with 726 additions and 332 deletions

26
LICENSE Normal file
View File

@ -0,0 +1,26 @@
Copyright (c) 2013-2014 Florian "Sangar" Nücke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
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/

25
LICENSE-jnlua Normal file
View File

@ -0,0 +1,25 @@
OpenComputers uses the JNLua library, with a patch to allow memory limits:
http://code.google.com/p/jnlua/issues/detail?id=9
JNLua is copyrighted and licensed as follows:
Copyright (C) 2008,2012 Andre Naef
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
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.

19
LICENSE-luaj Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2007 LuaJ. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
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.

22
LICENSE-mods Normal file
View File

@ -0,0 +1,22 @@
OpenComputers uses APIs from a couple of other mods.
To support accepting energy from their respective power systems:
- BuildCraft [1], license: Minecraft Mod Public License, see homepage.
- Universal Electricity [3], license: Lesser General Public License [4].
To support bundled redstone input and output:
- RedLogic [6], license: MIT License, see thread.
- MineFactory Reloaded [7], license: custom, see thread.
- Project: Red [9], license: Restricted Public Software License [10].
For providing a peripheral and reading their floppy disks:
- ComputerCraft [8], copyright Daniel Ratcliffe.
[1] http://www.mod-buildcraft.com/
[3] http://universalelectricity.com/
[4] http://www.gnu.org/copyleft/lesser.html
[6] http://www.minecraftforum.net/topic/1852277-162-redlogic-wip-replacement-for-rp2-wiringlogiccontrollighting/
[7] http://www.minecraftforum.net/topic/2016680-162164-powercrystals-mods-minefactoryreloaded-powercrystalscore-and-netherores-updated/
[8] http://www.computercraft.info/
[9] http://projectredwiki.com/
[10] https://github.com/MrTJP/ProjectRed/blob/master/LICENSE.md

207
LICENSE-typesafe Normal file
View File

@ -0,0 +1,207 @@
OpenComputers uses the typesafe config library, it is copyrighted as follows:
Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
It is licensed as follows:
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,11 +1,13 @@
package li.cil.oc.api.fs;
import li.cil.oc.api.Persistable;
/**
* Used by file system components to get and set the file system's label.
*
* @see li.cil.oc.api.FileSystem#asManagedEnvironment(FileSystem, Label)
*/
public interface Label {
public interface Label extends Persistable {
/**
* Get the current value of this label.
* <p/>

View File

@ -37,5 +37,5 @@
@cpw.mods.fml.common.API(
owner = "OpenComputers|Core",
provides = "OpenComputersAPI",
apiVersion = "1.3.0")
apiVersion = "1.4.0")
package li.cil.oc.api;

View File

@ -489,7 +489,7 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with ISidedInventory w
// ----------------------------------------------------------------------- //
override def installedMemory = 96 * 1024
override def installedMemory = 128 * 1024
override def tier = 0

View File

@ -28,7 +28,7 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable
@Callback(doc = """function():boolean -- Returns whether the screen is currently on.""")
def isOn(computer: Context, args: Arguments): Array[AnyRef] = result(origin.isOn)
@Callback(doc = """function():boolean -- Turns off the screen. Returns true if it was on.""")
@Callback(doc = """function():boolean -- Turns the screen on. Returns true if it was off.""")
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(doc = """function():boolean -- Turns the screen on. Returns true if it was off.""")
@Callback(doc = """function():boolean -- Turns off the screen. Returns true if it was on.""")
def turnOff(computer: Context, args: Arguments): Array[AnyRef] = {
if (origin.isOn) {
origin.turnOff()

View File

@ -20,13 +20,15 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
// ----------------------------------------------------------------------- //
@Callback(direct = true)
def getLabel(context: Context, args: Arguments): Array[AnyRef] = label match {
case value: Label => result(label.getLabel)
case _ => null
def getLabel(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
label match {
case value: Label => result(label.getLabel)
case _ => null
}
}
@Callback
def setLabel(context: Context, args: Arguments): Array[AnyRef] = {
def setLabel(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
if (label == null) throw new Exception("filesystem does not support labeling")
if (args.checkAny(0) == null) label.setLabel(null)
else label.setLabel(args.checkString(0))
@ -34,12 +36,12 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
@Callback(direct = true)
def isReadOnly(context: Context, args: Arguments): Array[AnyRef] = {
def isReadOnly(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.isReadOnly)
}
@Callback(direct = true)
def spaceTotal(context: Context, args: Arguments): Array[AnyRef] = {
def spaceTotal(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val space = fileSystem.spaceTotal
if (space < 0)
Array("unlimited")
@ -48,32 +50,32 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
@Callback(direct = true)
def spaceUsed(context: Context, args: Arguments): Array[AnyRef] = {
def spaceUsed(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.spaceUsed)
}
@Callback(direct = true)
def exists(context: Context, args: Arguments): Array[AnyRef] = {
def exists(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.exists(clean(args.checkString(0))))
}
@Callback(direct = true)
def size(context: Context, args: Arguments): Array[AnyRef] = {
def size(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.size(clean(args.checkString(0))))
}
@Callback(direct = true)
def isDirectory(context: Context, args: Arguments): Array[AnyRef] = {
def isDirectory(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.isDirectory(clean(args.checkString(0))))
}
@Callback(direct = true)
def lastModified(context: Context, args: Arguments): Array[AnyRef] = {
def lastModified(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.lastModified(clean(args.checkString(0))))
}
@Callback
def list(context: Context, args: Arguments): Array[AnyRef] = {
def list(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
Option(fileSystem.list(clean(args.checkString(0)))) match {
case Some(list) => Array(list)
case _ => null
@ -81,26 +83,26 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
@Callback
def makeDirectory(context: Context, args: Arguments): Array[AnyRef] = {
def makeDirectory(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
def recurse(path: String): Boolean = !fileSystem.exists(path) && (fileSystem.makeDirectory(path) ||
(recurse(path.split("/").dropRight(1).mkString("/")) && fileSystem.makeDirectory(path)))
result(recurse(clean(args.checkString(0))))
}
@Callback
def remove(context: Context, args: Arguments): Array[AnyRef] = {
def remove(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
def recurse(parent: String): Boolean = (!fileSystem.isDirectory(parent) ||
fileSystem.list(parent).forall(child => recurse(parent + "/" + child))) && fileSystem.delete(parent)
result(recurse(clean(args.checkString(0))))
}
@Callback
def rename(context: Context, args: Arguments): Array[AnyRef] = {
def rename(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
result(fileSystem.rename(clean(args.checkString(0)), clean(args.checkString(1))))
}
@Callback
def close(context: Context, args: Arguments): Array[AnyRef] = {
@Callback(direct = true)
def close(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0)
Option(fileSystem.getHandle(handle)) match {
case Some(file) =>
@ -113,8 +115,8 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
null
}
@Callback
def open(context: Context, args: Arguments): Array[AnyRef] = {
@Callback(direct = true, limit = 4)
def open(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
if (owners.get(context.node.address).fold(false)(_.size >= Settings.get.maxHandles)) {
throw new IOException("too many open handles")
}
@ -127,8 +129,8 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
result(handle)
}
@Callback
def read(context: Context, args: Arguments): Array[AnyRef] = {
@Callback(direct = true, limit = 4)
def read(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0)
val n = math.min(Settings.get.maxReadBuffer, math.max(0, args.checkInteger(1)))
checkOwner(context.node.address, handle)
@ -158,8 +160,8 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
}
@Callback
def seek(context: Context, args: Arguments): Array[AnyRef] = {
@Callback(direct = true, limit = 4)
def seek(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0)
val whence = args.checkString(1)
val offset = args.checkInteger(2)
@ -178,7 +180,7 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
@Callback
def write(context: Context, args: Arguments): Array[AnyRef] = {
def write(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
val handle = args.checkInteger(0)
val value = args.checkByteArray(1)
if (!node.tryChangeBuffer(-Settings.get.hddWriteCost * value.length)) {
@ -243,6 +245,7 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
})
label.load(nbt.getCompoundTag("label"))
fileSystem.load(nbt.getCompoundTag("fs"))
}
@ -262,6 +265,7 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label) extends ManagedC
}
nbt.setTag("owners", ownersNbt)
nbt.setNewCompoundTag("label", label.save)
nbt.setNewCompoundTag("fs", fileSystem.save)
}

View File

@ -65,6 +65,15 @@ trait Architecture {
*/
def init(): Boolean
/**
* Called when the owning machine was connected to the component network.
*
* This can be useful for connecting custom file systems (read only memory)
* in case init() was called from the machine's load() method (where it was
* not yet connected to the network).
*/
def onConnect()
/**
* Called when a computer stopped. Used to clean up any handles, memory and
* so on. For example, for Lua this destroys the Lua state.

View File

@ -0,0 +1,34 @@
package li.cil.oc.server.component.machine
import li.cil.oc.api.FileSystem
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.{Settings, OpenComputers}
import net.minecraft.nbt.NBTTagCompound
abstract class LuaArchitecture(val machine: Machine) extends Architecture {
val rom = Option(FileSystem.asManagedEnvironment(FileSystem.
fromClass(OpenComputers.getClass, Settings.resourceDomain, "lua/rom"), "rom"))
override def init() = {
if (machine.node.network != null) {
rom.foreach(fs => machine.node.connect(fs.node))
}
true
}
override def onConnect() {
rom.foreach(fs => machine.node.connect(fs.node))
}
override def close() {
rom.foreach(_.node.remove())
}
override def load(nbt: NBTTagCompound) {
rom.foreach(fs => fs.load(nbt.getCompoundTag("rom")))
}
override def save(nbt: NBTTagCompound) {
rom.foreach(fs => nbt.setNewCompoundTag("rom", fs.save))
}
}

View File

@ -12,7 +12,7 @@ import org.luaj.vm3.lib.jse.JsePlatform
import scala.Some
import scala.collection.convert.WrapAsScala._
class LuaJLuaArchitecture(machine: Machine) extends Architecture {
class LuaJLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
private var lua: Globals = _
private var thread: LuaThread = _
@ -132,6 +132,8 @@ class LuaJLuaArchitecture(machine: Machine) extends Architecture {
// ----------------------------------------------------------------------- //
override def init() = {
super.init()
lua = JsePlatform.debugGlobals()
lua.set("package", LuaValue.NIL)
lua.set("io", LuaValue.NIL)
@ -251,13 +253,13 @@ class LuaJLuaArchitecture(machine: Machine) extends Architecture {
computer.set("pushSignal", (args: Varargs) => LuaValue.valueOf(machine.signal(args.checkjstring(1), toSimpleJavaObjects(args, 2): _*)))
// And its ROM address.
computer.set("romAddress", (_: Varargs) => machine.rom.fold(LuaValue.NIL)(rom => Option(rom.node.address) match {
computer.set("romAddress", (_: Varargs) => rom.fold(LuaValue.NIL)(fs => Option(fs.node.address) match {
case Some(address) => LuaValue.valueOf(address)
case _ => LuaValue.NIL
}))
// And it's /tmp address...
computer.set("tmpAddress", (_: Varargs) => machine.tmp.fold(LuaValue.NIL)(tmp => Option(tmp.node.address) match {
computer.set("tmpAddress", (_: Varargs) => machine.tmp.fold(LuaValue.NIL)(fs => Option(fs.node.address) match {
case Some(address) => LuaValue.valueOf(address)
case _ => LuaValue.NIL
}))
@ -375,6 +377,8 @@ class LuaJLuaArchitecture(machine: Machine) extends Architecture {
}
override def close() = {
super.close()
lua = null
thread = null
synchronizedCall = null
@ -385,11 +389,11 @@ class LuaJLuaArchitecture(machine: Machine) extends Architecture {
// ----------------------------------------------------------------------- //
override def load(nbt: NBTTagCompound) {
super.load(nbt)
if (machine.isRunning) {
machine.stop()
machine.start()
}
}
override def save(nbt: NBTTagCompound) {}
}

View File

@ -24,9 +24,6 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
withConnector(if (isRobot) Settings.get.bufferRobot + 30 * Settings.get.bufferPerLevel else Settings.get.bufferComputer).
create()
val rom = Option(FileSystem.asManagedEnvironment(FileSystem.
fromClass(OpenComputers.getClass, Settings.resourceDomain, "lua/rom"), "rom"))
val tmp = if (Settings.get.tmpSize > 0) {
Option(FileSystem.asManagedEnvironment(FileSystem.
fromMemory(Settings.get.tmpSize * 1024), "tmpfs"))
@ -208,7 +205,7 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
Option(node.network.node(address)) match {
case Some(component: server.network.Component) if component.canBeSeenFrom(node) || component == node =>
val direct = component.isDirect(method)
if (direct) callCounts.synchronized {
if (direct && architecture.isInitialized) callCounts.synchronized {
val limit = component.limit(method)
val counts = callCounts.getOrElseUpdate(component.address, mutable.Map.empty[String, Int])
val count = counts.getOrElseUpdate(method, 0)
@ -424,8 +421,8 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
override def onConnect(node: Node) {
if (node == this.node) {
components += this.node.address -> this.node.name
rom.foreach(rom => node.connect(rom.node))
tmp.foreach(tmp => node.connect(tmp.node))
architecture.onConnect()
}
else {
node match {
@ -439,8 +436,7 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
override def onDisconnect(node: Node) {
if (node == this.node) {
stop()
rom.foreach(_.node.remove())
close()
tmp.foreach(_.node.remove())
}
else {
@ -522,7 +518,6 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
components ++= nbt.getTagList("components").iterator[NBTTagCompound].map(c =>
c.getString("address") -> c.getString("name"))
rom.foreach(rom => rom.load(nbt.getCompoundTag("rom")))
tmp.foreach(tmp => tmp.load(nbt.getCompoundTag("tmp")))
if (state.size > 0 && state.top != Machine.State.Stopped && init()) {
@ -587,7 +582,6 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
}
nbt.setTag("components", componentsNbt)
rom.foreach(rom => nbt.setNewCompoundTag("rom", rom.save))
tmp.foreach(tmp => nbt.setNewCompoundTag("tmp", tmp.save))
if (state.top != Machine.State.Stopped) {
@ -637,7 +631,6 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi
// Connect the ROM and `/tmp` node to our owner. We're not in a network in
// case we're loading, which is why we have to check it here.
if (node.network != null) {
rom.foreach(rom => node.connect(rom.node))
tmp.foreach(tmp => node.connect(tmp.node))
}

View File

@ -12,7 +12,7 @@ import scala.Some
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable
class NativeLuaArchitecture(val machine: Machine) extends Architecture {
class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
private var lua: LuaState = null
private var kernelMemory = 0
@ -179,6 +179,8 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
// ----------------------------------------------------------------------- //
override def init(): Boolean = {
super.init()
// Creates a new state with all base libraries and the persistence library
// loaded into it. This means the state has much more power than it
// rightfully should have, so we sandbox it a bit in the following.
@ -321,7 +323,7 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
// And its ROM address.
lua.pushScalaFunction(lua => {
machine.rom.foreach(rom => Option(rom.node.address) match {
rom.foreach(fs => Option(fs.node.address) match {
case None => lua.pushNil()
case Some(address) => lua.pushString(address)
})
@ -331,7 +333,7 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
// And it's /tmp address...
lua.pushScalaFunction(lua => {
machine.tmp.foreach(tmp => Option(tmp.node.address) match {
machine.tmp.foreach(fs => Option(fs.node.address) match {
case None => lua.pushNil()
case Some(address) => lua.pushString(address)
})
@ -563,6 +565,8 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
}
override def close() {
super.close()
if (lua != null) {
lua.setTotalMemory(Integer.MAX_VALUE)
lua.close()
@ -574,6 +578,8 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
// ----------------------------------------------------------------------- //
override def load(nbt: NBTTagCompound) {
super.load(nbt)
// Unlimit memory use while unpersisting.
lua.setTotalMemory(Integer.MAX_VALUE)
@ -609,6 +615,8 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture {
}
override def save(nbt: NBTTagCompound) {
super.save(nbt)
// Unlimit memory while persisting.
lua.setTotalMemory(Integer.MAX_VALUE)

View File

@ -121,24 +121,22 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
val block = Block.blocksList(blockId)
val canActivate = block != null && Settings.get.allowActivateBlocks
val shouldActivate = canActivate && (!isSneaking || (item == null || item.shouldPassSneakingClickToBlock(world, x, y, z)))
if (shouldActivate && block.onBlockActivated(world, x, y, z, this, side, hitX, hitY, hitZ)) {
return ActivationType.BlockActivated
}
val result =
if (shouldActivate && block.onBlockActivated(world, x, y, z, this, side, hitX, hitY, hitZ))
ActivationType.BlockActivated
else if (tryPlaceBlockWhileHandlingFunnySpecialCases(stack, x, y, z, side, hitX, hitY, hitZ))
ActivationType.ItemPlaced
else if (tryUseItem(stack, duration))
ActivationType.ItemUsed
else
ActivationType.None
if (stack != null) {
val didPlace = tryPlaceBlockWhileHandlingFunnySpecialCases(stack, x, y, z, side, hitX, hitY, hitZ)
if (stack.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(this, stack)
if (stack.stackSize <= 0) inventory.setInventorySlotContents(0, null)
if (didPlace) {
return ActivationType.ItemPlaced
}
if (tryUseItem(stack, duration)) {
return ActivationType.ItemUsed
}
}
ActivationType.None
result
}
def useEquippedItem(duration: Double) = {
@ -324,14 +322,16 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
}
private def tryPlaceBlockWhileHandlingFunnySpecialCases(stack: ItemStack, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float) = {
val fakeEyeHeight = if (rotationPitch < 0 && isSomeKindOfPiston(stack)) 1.82 else 0
setPosition(posX, posY - fakeEyeHeight, posZ)
val didPlace = stack.tryPlaceItemIntoWorld(this, world, x, y, z, side, hitX, hitY, hitZ)
setPosition(posX, posY + fakeEyeHeight, posZ)
if (didPlace) {
robot.addXp(Settings.get.robotActionXp)
stack != null && stack.stackSize > 0 && {
val fakeEyeHeight = if (rotationPitch < 0 && isSomeKindOfPiston(stack)) 1.82 else 0
setPosition(posX, posY - fakeEyeHeight, posZ)
val didPlace = stack.tryPlaceItemIntoWorld(this, world, x, y, z, side, hitX, hitY, hitZ)
setPosition(posX, posY + fakeEyeHeight, posZ)
if (didPlace) {
robot.addXp(Settings.get.robotActionXp)
}
didPlace
}
didPlace
}
private def isSomeKindOfPiston(stack: ItemStack) =

View File

@ -72,17 +72,32 @@ object FileSystem extends Item {
override def setLabel(value: String) {
media.setLabel(stack, value)
}
override def load(nbt: NBTTagCompound) {}
override def save(nbt: NBTTagCompound) {}
}
private class ItemLabel(val stack: ItemStack) extends Label {
override def getLabel =
if (dataTag(stack).hasKey(Settings.namespace + "fs.label"))
dataTag(stack).getString(Settings.namespace + "fs.label")
else null
var label: Option[String] = None
override def getLabel = label.orNull
override def setLabel(value: String) {
dataTag(stack).setString(Settings.namespace + "fs.label",
if (value.length > 16) value.substring(0, 16) else value)
label = Option(if (value != null && value.length > 16) value.substring(0, 16) else value)
}
override def load(nbt: NBTTagCompound) {
if (dataTag(stack).hasKey(Settings.namespace + "fs.label")) {
label = Option(dataTag(stack).getString(Settings.namespace + "fs.label"))
}
}
override def save(nbt: NBTTagCompound) {
label match {
case Some(value) => dataTag(stack).setString(Settings.namespace + "fs.label", value)
case _ =>
}
}
}

View File

@ -8,6 +8,7 @@ import li.cil.oc.api.fs.Label
import li.cil.oc.server.component
import li.cil.oc.{Settings, api}
import net.minecraftforge.common.DimensionManager
import net.minecraft.nbt.NBTTagCompound
object FileSystem extends api.detail.FileSystemAPI {
override def fromClass(clazz: Class[_], domain: String, root: String): api.fs.FileSystem = {
@ -84,6 +85,10 @@ object FileSystem extends api.detail.FileSystemAPI {
def setLabel(value: String) = throw new IllegalArgumentException("label is read only")
def getLabel = label
override def load(nbt: NBTTagCompound) {}
override def save(nbt: NBTTagCompound) {}
}
private class ReadOnlyFileSystem(protected val root: io.File)

View File

@ -1,12 +1,12 @@
local event = require("event")
local fs = require("filesystem")
local shell = require("shell")
local process = require("process")
local proxy = ...
-- Install symlinks if they don't already exist.
local links = {}
local fsroot = fs.path(shell.running())
local fsroot = fs.path(process.running())
local function inject(path)
for file in fs.list(fs.concat(fsroot, path)) do
local source = fs.concat(fsroot, path, file)

View File

@ -6,7 +6,7 @@ local internet = require("internet")
local shell = require("shell")
if not component.isAvailable("internet") then
io.write("This program requires an internet card to run.")
io.stderr:write("This program requires an internet card to run.")
return
end
@ -16,7 +16,7 @@ local args, options = shell.parse(...)
local function get(pasteId, filename)
local f, reason = io.open(filename, "w")
if not f then
io.write("Failed opening file for writing: ", reason)
io.stderr:write("Failed opening file for writing: ", reason)
return
end
@ -35,9 +35,10 @@ local function get(pasteId, filename)
f:close()
io.write("Saved data to ", filename, "\n")
else
io.write("failed.\n")
f:close()
fs.remove(filename)
io.write("HTTP request failed: ", response, "\n")
io.stderr:write("HTTP request failed: ", response, "\n")
end
end
@ -61,7 +62,7 @@ function run(pasteId, ...)
local success, reason = shell.execute(tmpFile, nil, ...)
if not success then
io.write(reason)
io.stderr:write(reason)
end
fs.remove(tmpFile)
end
@ -73,14 +74,14 @@ function put(path)
if configFile then
local result, reason = pcall(configFile)
if not result then
io.write("Failed loading config: ", reason)
io.stderr:write("Failed loading config: ", reason)
end
end
config.key = config.key or "fd92bd40a84c127eeb6804b146793c97"
local file, reason = io.open(path, "r")
if not file then
io.write("Failed opening file for reading: ", reason)
io.stderr:write("Failed opening file for reading: ", reason)
return
end
@ -112,7 +113,8 @@ function put(path)
io.write('Run "pastebin get ', pasteId, '" to download anywhere.')
end
else
io.write("failed: ", response)
io.write("failed.\n")
io.stderr:write(response)
end
end
@ -127,7 +129,7 @@ elseif command == "get" then
local path = shell.resolve(args[3])
if fs.exists(path) then
if not options.f or not os.remove(path) then
io.write("file already exists")
io.stderr:write("file already exists")
return
end
end

View File

@ -5,7 +5,7 @@ local shell = require("shell")
local text = require("text")
if not component.isAvailable("internet") then
io.write("This program requires an internet card to run.")
io.stderr:write("This program requires an internet card to run.")
return
end
@ -33,21 +33,21 @@ if not filename then
end
filename = text.trim(filename)
if filename == "" then
io.write("could not infer filename, please specify one")
io.stderr:write("could not infer filename, please specify one")
return
end
filename = shell.resolve(filename)
if fs.exists(filename) then
if not options.f or not os.remove(filename) then
io.write("file already exists")
io.stderr:write("file already exists")
return
end
end
local f, reason = io.open(filename, "wb")
if not f then
io.write("Failed opening file for writing: ", reason)
io.stderr:write("failed opening file for writing: ", reason)
return
end
@ -68,7 +68,10 @@ if result then
io.write("Saved data to ", filename, "\n")
end
else
if not options.q then
io.write("failed.\n")
end
f:close()
fs.remove(filename)
io.write("HTTP request failed: ", response, "\n")
io.stderr:write("HTTP request failed: ", response, "\n")
end

View File

@ -1,12 +1,12 @@
local event = require("event")
local fs = require("filesystem")
local shell = require("shell")
local process = require("process")
local proxy = ...
-- Install symlinks if they don't already exist.
local links = {}
local fsroot = fs.path(shell.running())
local fsroot = fs.path(process.running())
local function inject(path)
for file in fs.list(fs.concat(fsroot, path)) do
local source = fs.concat(fsroot, path, file)

View File

@ -5,7 +5,8 @@ local shell = require("shell")
local sides = require("sides")
if not computer.isRobot() then
error("can only run on robots", 0)
io.stderr:write("can only run on robots")
return
end
local args, options = shell.parse(...)
@ -17,7 +18,8 @@ end
local size = tonumber(args[1])
if not size then
error("invalid size", 0)
io.stderr:write("invalid size")
return
end
local r = component.computer

View File

@ -50,12 +50,27 @@ end
-------------------------------------------------------------------------------
local running = setmetatable({}, {__mode="k"})
local function findProcess(co)
co = co or coroutine.running()
for _, process in pairs(running) do
for _, instance in pairs(process.instances) do
if instance == co then
return process
end
end
end
end
-------------------------------------------------------------------------------
--[[ This is the global environment we make available to userland programs. ]]
-- You'll notice that we do a lot of wrapping of native functions and adding
-- parameter checks in those wrappers. This is to avoid errors from the host
-- side that would push error objects - which are userdata and cannot be
-- persisted.
local sandbox
local sandbox, libprocess
sandbox = {
assert = assert,
dofile = nil, -- in boot/*_base.lua
@ -70,10 +85,7 @@ sandbox = {
if not allowBytecode() then
mode = "t"
end
pcall(function()
local _, penv = sandbox.require("shell").running()
env = env or penv
end)
env = env or select(2, libprocess.running())
return load(ld, source, mode, env or sandbox)
end,
loadfile = nil, -- in boot/*_base.lua
@ -96,9 +108,7 @@ sandbox = {
coroutine = {
create = function(f)
local co = coroutine.create(f)
pcall(function()
sandbox.require("shell").register(co)
end)
table.insert(findProcess().instances, co)
return co
end,
resume = function(co, ...) -- custom resume part for bubbling sysyields
@ -257,7 +267,7 @@ sandbox = {
sandbox._G = sandbox
-------------------------------------------------------------------------------
-- Start of non-standard stuff made available via package.preload.
-- Start of non-standard stuff.
local libcomponent
libcomponent = {
@ -359,6 +369,51 @@ local libcomputer = {
end
}
libprocess = {
load = function(path, env, init, name)
checkArg(1, path, "string")
checkArg(2, env, "table", "nil")
checkArg(3, init, "function", "nil")
checkArg(4, name, "string", "nil")
local process = findProcess()
if process then
env = env or process.env
end
env = setmetatable({}, {__index=env or sandbox})
local code, reason = sandbox.loadfile(path, "t", env)
if not code then
return nil, reason
end
local thread = coroutine.create(function(...)
if init then
init()
end
return code(...)
end)
running[thread] = {
path = path,
command = name,
env = env,
parent = process,
instances = setmetatable({thread}, {__mode="v"})
}
return thread
end,
running = function(level)
level = level or 1
local process = findProcess()
while level > 1 and process do
process = process.parent
level = level - 1
end
if process then
return process.path, process.env, process.command
end
end
}
local libunicode = {
char = function(...)
local args = table.pack(...)
@ -396,107 +451,103 @@ local libunicode = {
-------------------------------------------------------------------------------
local function main()
local args
local function bootstrap()
-- Minimalistic hard-coded pure async proxy for our ROM.
local rom = {}
function rom.invoke(method, ...)
return invoke(true, computer.romAddress(), method, ...)
end
function rom.open(file) return rom.invoke("open", file) end
function rom.read(handle) return rom.invoke("read", handle, math.huge) end
function rom.close(handle) return rom.invoke("close", handle) end
function rom.inits(file) return ipairs(rom.invoke("list", "boot")) end
function rom.isDirectory(path) return rom.invoke("isDirectory", path) end
-- Custom low-level loadfile/dofile implementation reading from our ROM.
local function loadfile(file)
local handle, reason = rom.open(file)
if not handle then
error(reason)
end
if handle then
local buffer = ""
repeat
local data, reason = rom.read(handle)
if not data and reason then
error(reason)
end
buffer = buffer .. (data or "")
until not data
rom.close(handle)
return load(buffer, "=" .. file, "t", sandbox)
end
end
local function dofile(file)
local program, reason = loadfile(file)
if program then
local result = table.pack(pcall(program))
if result[1] then
return table.unpack(result, 2, result.n)
else
error(result[2])
end
else
error(reason)
end
end
-- Make all calls in the bootstrapper direct to speed up booting. This is
-- safe because all invokes are rom fs related - or should be, anyway.
local realInvoke = invoke
invoke = function(_, ...) return realInvoke(true, ...) end
-- Load file system related libraries we need to load other stuff moree
-- comfortably. This is basically wrapper stuff for the file streams
-- provided by the filesystem components.
local buffer = loadfile("/lib/buffer.lua")
local fs = loadfile("/lib/filesystem.lua")
local io = loadfile("/lib/io.lua")
local package = dofile("/lib/package.lua")
-- Initialize the package module with some of our own APIs.
package.preload["buffer"] = buffer
package.preload["component"] = function() return libcomponent end
package.preload["computer"] = function() return libcomputer end
package.preload["filesystem"] = fs
package.preload["io"] = io
package.preload["unicode"] = function() return libunicode end
-- Inject the package and io modules into the global namespace, as in Lua.
sandbox.package = package
sandbox.io = sandbox.require("io")
-- Mount the ROM and temporary file systems to allow working on the file
-- system module from this point on.
sandbox.require("filesystem").mount(computer.romAddress(), "/")
if computer.tmpAddress() then
sandbox.require("filesystem").mount(computer.tmpAddress(), "/tmp")
end
-- Run library startup scripts. These mostly initialize event handlers.
local scripts = {}
for _, file in rom.inits() do
local path = "boot/" .. file
if not rom.isDirectory(path) then
table.insert(scripts, path)
end
end
table.sort(scripts)
for i = 1, #scripts do
dofile(scripts[i])
end
-- Step out of the fast lane, all the basic stuff should now be loaded.
invoke = realInvoke
-- Yield once to get a memory baseline.
coroutine.yield()
return coroutine.create(function() dofile("/init.lua") end)
local function bootstrap()
-- Minimalistic hard-coded pure async proxy for our ROM.
local rom = {}
function rom.invoke(method, ...)
return invoke(true, computer.romAddress(), method, ...)
end
local co, args = bootstrap(), {n=0}
function rom.open(file) return rom.invoke("open", file) end
function rom.read(handle) return rom.invoke("read", handle, math.huge) end
function rom.close(handle) return rom.invoke("close", handle) end
function rom.inits(file) return ipairs(rom.invoke("list", "boot")) end
function rom.isDirectory(path) return rom.invoke("isDirectory", path) end
-- Custom low-level loadfile/dofile implementation reading from our ROM.
local function loadfile(file)
local handle, reason = rom.open(file)
if not handle then
error(reason)
end
local buffer = ""
repeat
local data, reason = rom.read(handle)
if not data and reason then
error(reason)
end
buffer = buffer .. (data or "")
until not data
rom.close(handle)
return load(buffer, "=" .. file, "t", sandbox)
end
local function dofile(file)
local program, reason = loadfile(file)
if program then
local result = table.pack(pcall(program))
if result[1] then
return table.unpack(result, 2, result.n)
else
error(result[2])
end
else
error(reason)
end
end
-- Load file system related libraries we need to load other stuff moree
-- comfortably. This is basically wrapper stuff for the file streams
-- provided by the filesystem components.
local package = dofile("/lib/package.lua")
-- Initialize the package module with some of our own APIs.
package.preload["buffer"] = loadfile("/lib/buffer.lua")
package.preload["component"] = function() return libcomponent end
package.preload["computer"] = function() return libcomputer end
package.preload["filesystem"] = loadfile("/lib/filesystem.lua")
package.preload["io"] = loadfile("/lib/io.lua")
package.preload["process"] = function() return libprocess end
package.preload["unicode"] = function() return libunicode end
-- Inject the package and io modules into the global namespace, as in Lua.
sandbox.package = package
sandbox.io = sandbox.require("io")
-- Mount the ROM and temporary file systems to allow working on the file
-- system module from this point on.
sandbox.require("filesystem").mount(computer.romAddress(), "/")
if computer.tmpAddress() then
sandbox.require("filesystem").mount(computer.tmpAddress(), "/tmp")
end
-- Run library startup scripts. These mostly initialize event handlers.
local scripts = {}
for _, file in rom.inits() do
local path = "boot/" .. file
if not rom.isDirectory(path) then
table.insert(scripts, path)
end
end
table.sort(scripts)
for i = 1, #scripts do
dofile(scripts[i])
end
return coroutine.create(function() dofile("/init.lua") end), {n=0}
end
local function main()
-- Make all calls in the bootstrapper direct to speed up booting.
local realInvoke = invoke
invoke = function(_, ...) return realInvoke(true, ...) end
local co, args = bootstrap()
-- Step out of the fast lane, all the basic stuff should now be loaded.
invoke = realInvoke
-- Yield once to get a memory baseline.
coroutine.yield()
while true do
deadline = computer.realTime() + timeout -- timeout global is set by host
debug.sethook(co, checkDeadline, "", hookInterval)

View File

@ -11,7 +11,7 @@ elseif #args == 1 then
if value then
io.write(value)
else
io.write("no such alias")
io.stderr:write("no such alias")
end
else
shell.setAlias(args[1], args[2])

View File

@ -12,7 +12,7 @@ else
for i = 1, #args do
local file, reason = io.open(shell.resolve(args[i]))
if not file then
io.write(reason)
io.stderr:write(reason)
return
end
repeat

View File

@ -6,6 +6,6 @@ if #args == 0 then
else
local result, reason = shell.setWorkingDirectory(shell.resolve(args[1]))
if not result then
io.write(reason)
io.stderr:write(reason)
end
end

View File

@ -14,9 +14,10 @@ if fs.isDirectory(to) then
to = to .. "/" .. fs.name(from)
end
if fs.exists(to) and not options.f then
error("target file exists")
io.stderr:write("target file exists")
return
end
local result, reason = fs.copy(from, to)
if not result then
io.write(reason)
io.stderr:write(reason)
end

View File

@ -12,7 +12,7 @@ else
for i = 1, #args do
local proxy, path = fs.get(args[i])
if not proxy then
io.write(args[i], ": no such file or directory\n")
io.stderr:write(args[i], ": no such file or directory\n")
else
mounts[path] = proxy
end

View File

@ -22,7 +22,7 @@ local filename = shell.resolve(args[1])
local readonly = options.r or fs.get(filename) == nil or fs.get(filename).isReadOnly()
if fs.isDirectory(filename) or readonly and not fs.exists(filename) then
io.write("file not found")
io.stderr:write("file not found")
return
end

View File

@ -15,15 +15,15 @@ else
proxy, reaons = fs.get(args[1])
end
if not proxy then
io.write(reason)
io.stderr:write(reason)
return
end
if #args < 2 then
io.write(proxy.getLabel() or "no label")
io.stderr:write(proxy.getLabel() or "no label")
else
local result, reason = proxy.setLabel(args[2])
if not result then
io.write(reason or "could not set label")
io.stderr:write(reason or "could not set label")
end
end

View File

@ -18,5 +18,5 @@ end
local result, reason = fs.link(target, linkpath)
if not result then
io.write(reason)
io.stderr:write(reason)
end

View File

@ -46,7 +46,7 @@ while term.isAvailable() do
if type(result[2]) == "table" and result[2].reason == "terminated" then
os.exit(result[2].code)
end
io.write(tostring(result[2]), "\n")
io.stderr:write(tostring(result[2]), "\n")
else
for i = 2, result.n do
io.write(text.serialize(result[i], true), "\t")
@ -56,6 +56,6 @@ while term.isAvailable() do
end
end
else
io.write(reason, "\n")
io.stderr:write(reason, "\n")
end
end

View File

@ -16,4 +16,4 @@ for path in string.gmatch(os.getenv("MANPATH"), "[^:]+") do
os.exit()
end
end
io.write("No manual entry for " .. topic)
io.stderr:write("No manual entry for " .. topic)

View File

@ -18,6 +18,6 @@ for i = 1, #args do
reason = "unknown reason"
end
end
io.write(path, ": ", reason, "\n")
io.stderr:write(path, ": ", reason, "\n")
end
end

View File

@ -12,7 +12,7 @@ end
local file, reason = io.open(shell.resolve(args[1]))
if not file then
io.write(reason)
io.stderr:write(reason)
return
end

View File

@ -18,11 +18,11 @@ end
local proxy, reason = fs.proxy(args[1])
if not proxy then
io.write(reason)
io.stderr:write(reason)
return
end
local result, reason = fs.mount(proxy, shell.resolve(args[2]))
if not result then
io.write(reason)
io.stderr:write(reason)
end

View File

@ -15,11 +15,11 @@ if fs.isDirectory(to) then
end
if fs.exists(to) then
if not options.f then
io.write("target file exists")
io.stderr:write("target file exists")
end
fs.remove(to)
end
local result, reason = os.rename(from, to)
if not result then
io.write(reason or "unknown error")
io.stderr:write(reason or "unknown error")
end

View File

@ -13,7 +13,7 @@ local componentType = args[1]
if #args > 1 then
local address = args[2]
if not component.get(address) then
io.write("no component with this address")
io.stderr:write("no component with this address")
return
else
component.setPrimary(componentType, address)
@ -23,5 +23,5 @@ end
if component.isAvailable(componentType) then
io.write(component.getPrimary(componentType).address)
else
io.write("no primary component for this type")
io.stderr:write("no primary component for this type")
end

View File

@ -3,6 +3,10 @@ local component = require("component")
local shell = require("shell")
local sides = require("sides")
if not component.isAvailable("redstone") then
io.stderr:write("This program requires a redstone card or redstone I/O block.")
return
end
local rs = component.redstone
local args, options = shell.parse(...)
@ -17,7 +21,8 @@ end
local side = sides[args[1]]
if not side then
error("Invalid side.")
io.stderr:write("invalid side")
return
end
if type(side) == "string" then
side = sides[side]
@ -25,11 +30,13 @@ end
if options.b then
if not rs.setBundledOutput then
error("Bundled redstone not available.")
io.stderr:write("bundled redstone not available")
return
end
local color = colors[args[2]]
if not color then
error("Invalid color.")
io.stderr:write("invalid color")
return
end
if type(color) == "string" then
color = colors[color]

View File

@ -17,12 +17,13 @@ end
local w = tonumber(args[1])
local h = tonumber(args[2])
if not w or not h then
io.write("invalid width or height")
io.stderr:write("invalid width or height")
return
end
local result, reason = component.gpu.setResolution(w, h)
if not result then
io.write(reason)
io.stderr:write(reason)
return
end
term.clear()

View File

@ -9,6 +9,6 @@ end
for i = 1, #args do
local path = shell.resolve(args[i])
if not os.remove(path) then
io.write(path, ": no such file, or permission denied\n")
io.stderr:write(path, ": no such file, or permission denied\n")
end
end

View File

@ -2,7 +2,7 @@ local args = {...}
if #args < 1 then
for k,v in pairs(os.getenv()) do
io.write(k..'='..v..'\n')
io.write(k, "='", string.gsub(v, "'", [['"'"']]), "'\n")
end
else
local count = 0

View File

@ -1,6 +1,7 @@
local component = require("component")
local computer = require("computer")
local event = require("event")
local process = require("process")
local shell = require("shell")
local term = require("term")
local text = require("text")
@ -8,8 +9,8 @@ local text = require("text")
local args, options = shell.parse(...)
local history = {}
if options.v or not shell.running(2) then
print(_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)")
if options.v or not process.running(2) then
io.write(_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)\n")
end
while true do
@ -19,7 +20,7 @@ while true do
end
term.clear()
if options.v then
print(_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)")
io.write(_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)\n")
end
end
while term.isAvailable() do
@ -28,7 +29,7 @@ while true do
component.gpu.setForeground(foreground)
local command = term.read(history)
if not command then
print("exit")
io.write("exit\n")
return -- eof
end
while #history > 10 do
@ -40,10 +41,10 @@ while true do
elseif command ~= "" then
local result, reason = os.execute(command)
if not result then
print(reason)
io.stderr:write(reason .. "\n")
elseif term.getCursor() > 1 then
print()
io.write("\n")
end
end
end
end
end

View File

@ -21,16 +21,16 @@ else
if proxy then
proxy = reason -- = path
if proxy ~= path then
io.write("not a mount point")
io.stderr:write("not a mount point")
return
end
end
end
if not proxy then
io.write(reason)
io.stderr:write(reason)
return
end
if not fs.umount(proxy) then
io.write("nothing to unmount here")
io.stderr:write("nothing to unmount here")
end

View File

@ -8,7 +8,7 @@ end
local result = shell.getAlias(args[1])
if not result then
io.write("no such alias")
io.stderr:write("no such alias")
else
shell.setAlias(args[1], nil)
io.write("alias removed: ", args[1], " -> ", result)

View File

@ -1,9 +1,9 @@
local args = {...}
if #args < 1 then
print "You have to specify which arguments to unset!"
print("Usage: unset <varname>[ <varname2> [...]]")
else
for _, k in ipairs(args) do
os.setenv(k, nil)
end
end
end

View File

@ -9,5 +9,5 @@ end
local result, reason = computer.addUser(args[1])
if not result then
io.write(reason)
io.stderr:write(reason)
end

View File

@ -8,5 +8,5 @@ if #args < 1 then
end
if not computer.removeUser(args[1]) then
io.write("no such user")
io.stderr:write("no such user")
end

View File

@ -16,6 +16,6 @@ for i = 1, #args do
if result then
io.write(result, "\n")
else
io.write(args[i], ": ", reason, "\n")
io.stderr:write(args[i], ": ", reason, "\n")
end
end

View File

@ -3,6 +3,7 @@ local term = require("term")
local stdinStream = {handle="stdin"}
local stdoutStream = {handle="stdout"}
local stderrStream = {handle="stderr"}
local stdinHistory = {}
local function badFileDescriptor()
@ -13,6 +14,7 @@ function stdinStream:close()
return nil, "cannot close standard file"
end
stdoutStream.close = stdinStream.close
stderrStream.close = stdinStream.close
function stdinStream:read(n)
local result = term.read(stdinHistory)
@ -27,16 +29,32 @@ function stdoutStream:write(str)
return self
end
function stderrStream:write(str)
local component = require("component")
if component.isAvailable("gpu") and component.gpu.getDepth() > 1 then
local foreground = component.gpu.getForeground()
component.gpu.setForeground(0xFF0000)
term.write(str, true)
component.gpu.setForeground(foreground)
else
term.write(str, true)
end
return self
end
stdinStream.seek = badFileDescriptor
stdinStream.write = badFileDescriptor
stdoutStream.read = badFileDescriptor
stdoutStream.seek = badFileDescriptor
stderrStream.read = badFileDescriptor
stderrStream.seek = badFileDescriptor
io.stdin = buffer.new("r", stdinStream)
io.stdout = buffer.new("w", stdoutStream)
io.stderr = io.stdout
io.stderr = buffer.new("w", stderrStream)
io.stdout:setvbuf("no")
io.stderr:setvbuf("no")
io.input(io.stdin)
io.output(io.stdout)

View File

@ -3,7 +3,7 @@ local unicode = require("unicode")
local filesystem, fileStream = {}, {}
local isAutorunEnabled = true
local mtab = {children={}, links={}}
local mtab = {name="", children={}, links={}}
local function segments(path)
path = path:gsub("\\", "/")
@ -258,7 +258,7 @@ function filesystem.exists(path)
if not vrest or vnode.links[vrest] then -- virtual directory or symbolic link
return true
end
if node.fs then
if node and node.fs then
return node.fs.exists(rest)
end
return false
@ -472,18 +472,9 @@ function filesystem.open(path, mode)
local stream = {fs = node.fs, handle = handle}
-- stream:close does a syscall, which yields, and that's not possible in
-- the __gc metamethod. So we start a timer to do the yield/cleanup.
local function cleanup(self)
if not self.handle then return end
-- save non-gc'ed values as upvalues
local fs = self.fs
local handle = self.handle
local function close()
fs.close(handle)
end
-- Required locally because this is a bootstrapped file.
require("event").timer(0, close)
pcall(self.fs.close, self.handle)
end
local metatable = {__index = fileStream,
__gc = cleanup,

View File

@ -8,7 +8,6 @@ local loaded = {
["_G"] = _G,
["bit32"] = bit32,
["coroutine"] = coroutine,
["io"] = io,
["math"] = math,
["os"] = os,
["package"] = package,
@ -29,13 +28,14 @@ function package.searchpath(name, path, sep, rep)
rep = rep or '/'
sep, rep = '%' .. sep, rep
name = string.gsub(name, sep, rep)
local fs = require("filesystem")
local errorFiles = {}
for subPath in string.gmatch(path, "([^;]+)") do
subPath = string.gsub(subPath, "?", name)
if loaded.shell then
subPath = require("shell").resolve(subPath)
if subPath:sub(1, 1) ~= "/" and os.getenv then
subPath = fs.concat(os.getenv("PWD") or "/", subPath)
end
if require("filesystem").exists(subPath) then
if fs.exists(subPath) then
local file = io.open(subPath, "r")
if file then
file:close()
@ -80,8 +80,11 @@ function require(module)
loading[module] = true
local loader, value, errorMsg = nil, nil, {"module '" .. module .. "' not found:"}
for i = 1, #package.searchers do
local f, extra = package.searchers[i](module)
if f and type(f) ~= "string" then
-- the pcall is mostly for out of memory errors
local ok, f, extra = pcall(package.searchers[i], module)
if not ok then
table.insert(errorMsg, "\t" .. f)
elseif f and type(f) ~= "string" then
loader = f
value = extra
break

View File

@ -5,23 +5,13 @@ local text = require("text")
local shell = {}
local aliases = {}
local running = setmetatable({}, {__mode="k"})
local isLoading = false
local function findProcess(co)
co = co or coroutine.running()
for _, process in pairs(running) do
for _, instance in pairs(process.instances) do
if instance == co then
return process
end
end
end
end
local function findFile(name, ext)
checkArg(1, name, "string")
local function findIn(dir)
if dir:sub(1, 1) ~= "/" then
dir = shell.resolve(dir)
end
dir = fs.concat(fs.concat(dir, name), "..")
name = fs.name(name)
local list = fs.list(dir)
@ -475,52 +465,11 @@ function shell.execute(command, env, ...)
end
function shell.load(path, env, init, name)
checkArg(1, path, "string")
checkArg(2, env, "table", "nil")
checkArg(3, init, "function", "nil")
checkArg(4, name, "string", "nil")
local filename, reason = shell.resolve(path, "lua")
if not filename then
local path, reason = shell.resolve(path, "lua")
if not path then
return nil, reason
end
local process = findProcess()
if process then
env = env or process.env
end
env = setmetatable({}, {__index=env or _ENV})
local code, reason = loadfile(filename, "t", env)
if not code then
return nil, reason
end
isLoading = true
local thread = coroutine.create(function(...)
if init then
init()
end
return code(...)
end)
isLoading = false
running[thread] = {
path = filename,
command = name,
env = env,
parent = process,
instances = setmetatable({thread}, {__mode="v"})
}
return thread
end
function shell.register(thread) -- called from coroutine.create
checkArg(1, thread, "thread")
if findProcess(thread) then
return false -- already attached somewhere
end
if not isLoading then
table.insert(findProcess().instances, thread)
end
return true
return require("process").load(path, env, init, name)
end
function shell.parse(...)
@ -540,16 +489,8 @@ function shell.parse(...)
return args, options
end
function shell.running(level)
level = level or 1
local process = findProcess()
while level > 1 and process do
process = process.parent
level = level - 1
end
if process then
return process.path, process.env, process.command
end
function shell.running(level) -- deprecated
return require("process").running(level)
end
-------------------------------------------------------------------------------

View File

@ -588,7 +588,7 @@ opencomputers {
# allocations regardless of the amount of RAM installed in the computer it
# runs on. As a side effect this pretty much determines the read
# performance of file systems.
maxReadBuffer: 8192
maxReadBuffer: 2048
}
# Internet settings, security related.