diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..68dd6e055 --- /dev/null +++ b/LICENSE @@ -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/ \ No newline at end of file diff --git a/LICENSE-jnlua b/LICENSE-jnlua new file mode 100644 index 000000000..73295abd7 --- /dev/null +++ b/LICENSE-jnlua @@ -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. \ No newline at end of file diff --git a/LICENSE-luaj b/LICENSE-luaj new file mode 100644 index 000000000..2cc180864 --- /dev/null +++ b/LICENSE-luaj @@ -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. \ No newline at end of file diff --git a/LICENSE-mods b/LICENSE-mods new file mode 100644 index 000000000..b28c33776 --- /dev/null +++ b/LICENSE-mods @@ -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 \ No newline at end of file diff --git a/LICENSE-typesafe b/LICENSE-typesafe new file mode 100644 index 000000000..5f05643c0 --- /dev/null +++ b/LICENSE-typesafe @@ -0,0 +1,207 @@ +OpenComputers uses the typesafe config library, it is copyrighted as follows: + +Copyright (C) 2011-2012 Typesafe Inc. + +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. diff --git a/src/main/java/li/cil/oc/api/fs/Label.java b/src/main/java/li/cil/oc/api/fs/Label.java index 475d70bec..e98e45cdb 100644 --- a/src/main/java/li/cil/oc/api/fs/Label.java +++ b/src/main/java/li/cil/oc/api/fs/Label.java @@ -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. *

diff --git a/src/main/java/li/cil/oc/api/package-info.java b/src/main/java/li/cil/oc/api/package-info.java index 49ff8516f..366b2ed04 100644 --- a/src/main/java/li/cil/oc/api/package-info.java +++ b/src/main/java/li/cil/oc/api/package-info.java @@ -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; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/common/tileentity/Robot.scala b/src/main/java/li/cil/oc/common/tileentity/Robot.scala index 054811d5b..7efc967eb 100644 --- a/src/main/java/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/java/li/cil/oc/common/tileentity/Robot.scala @@ -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 diff --git a/src/main/java/li/cil/oc/common/tileentity/Screen.scala b/src/main/java/li/cil/oc/common/tileentity/Screen.scala index 43012e953..ad0892b77 100644 --- a/src/main/java/li/cil/oc/common/tileentity/Screen.scala +++ b/src/main/java/li/cil/oc/common/tileentity/Screen.scala @@ -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() diff --git a/src/main/java/li/cil/oc/server/component/FileSystem.scala b/src/main/java/li/cil/oc/server/component/FileSystem.scala index f743985bb..2001b763f 100644 --- a/src/main/java/li/cil/oc/server/component/FileSystem.scala +++ b/src/main/java/li/cil/oc/server/component/FileSystem.scala @@ -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) } diff --git a/src/main/java/li/cil/oc/server/component/machine/Architecture.scala b/src/main/java/li/cil/oc/server/component/machine/Architecture.scala index 4b08ae131..5df5eeeae 100644 --- a/src/main/java/li/cil/oc/server/component/machine/Architecture.scala +++ b/src/main/java/li/cil/oc/server/component/machine/Architecture.scala @@ -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. diff --git a/src/main/java/li/cil/oc/server/component/machine/LuaArchitecture.scala b/src/main/java/li/cil/oc/server/component/machine/LuaArchitecture.scala new file mode 100644 index 000000000..37d318975 --- /dev/null +++ b/src/main/java/li/cil/oc/server/component/machine/LuaArchitecture.scala @@ -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)) + } +} diff --git a/src/main/java/li/cil/oc/server/component/machine/LuaJLuaArchitecture.scala b/src/main/java/li/cil/oc/server/component/machine/LuaJLuaArchitecture.scala index 9c555925a..04ce6366f 100644 --- a/src/main/java/li/cil/oc/server/component/machine/LuaJLuaArchitecture.scala +++ b/src/main/java/li/cil/oc/server/component/machine/LuaJLuaArchitecture.scala @@ -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) {} } diff --git a/src/main/java/li/cil/oc/server/component/machine/Machine.scala b/src/main/java/li/cil/oc/server/component/machine/Machine.scala index a94dce97d..14b50583c 100644 --- a/src/main/java/li/cil/oc/server/component/machine/Machine.scala +++ b/src/main/java/li/cil/oc/server/component/machine/Machine.scala @@ -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)) } diff --git a/src/main/java/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala b/src/main/java/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala index ce360f5ca..5572cb4a3 100644 --- a/src/main/java/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala +++ b/src/main/java/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala @@ -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) diff --git a/src/main/java/li/cil/oc/server/component/robot/Player.scala b/src/main/java/li/cil/oc/server/component/robot/Player.scala index bb969cec8..a7782a4e1 100644 --- a/src/main/java/li/cil/oc/server/component/robot/Player.scala +++ b/src/main/java/li/cil/oc/server/component/robot/Player.scala @@ -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) = diff --git a/src/main/java/li/cil/oc/server/driver/item/FileSystem.scala b/src/main/java/li/cil/oc/server/driver/item/FileSystem.scala index e6e601442..474ab300e 100644 --- a/src/main/java/li/cil/oc/server/driver/item/FileSystem.scala +++ b/src/main/java/li/cil/oc/server/driver/item/FileSystem.scala @@ -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 _ => + } } } diff --git a/src/main/java/li/cil/oc/server/fs/FileSystem.scala b/src/main/java/li/cil/oc/server/fs/FileSystem.scala index 178ab2a8e..8442a8d3a 100644 --- a/src/main/java/li/cil/oc/server/fs/FileSystem.scala +++ b/src/main/java/li/cil/oc/server/fs/FileSystem.scala @@ -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) diff --git a/src/main/resources/assets/opencomputers/lua/component/internet/.autorun.lua b/src/main/resources/assets/opencomputers/lua/component/internet/.autorun.lua index a194a8f98..9aff88263 100644 --- a/src/main/resources/assets/opencomputers/lua/component/internet/.autorun.lua +++ b/src/main/resources/assets/opencomputers/lua/component/internet/.autorun.lua @@ -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) diff --git a/src/main/resources/assets/opencomputers/lua/component/internet/bin/pastebin.lua b/src/main/resources/assets/opencomputers/lua/component/internet/bin/pastebin.lua index 8ba13101e..604f1c1d7 100644 --- a/src/main/resources/assets/opencomputers/lua/component/internet/bin/pastebin.lua +++ b/src/main/resources/assets/opencomputers/lua/component/internet/bin/pastebin.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/component/internet/bin/wget.lua b/src/main/resources/assets/opencomputers/lua/component/internet/bin/wget.lua index 3d1b11d0d..e2b16448b 100644 --- a/src/main/resources/assets/opencomputers/lua/component/internet/bin/wget.lua +++ b/src/main/resources/assets/opencomputers/lua/component/internet/bin/wget.lua @@ -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 \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/component/robot/.autorun.lua b/src/main/resources/assets/opencomputers/lua/component/robot/.autorun.lua index a194a8f98..9aff88263 100644 --- a/src/main/resources/assets/opencomputers/lua/component/robot/.autorun.lua +++ b/src/main/resources/assets/opencomputers/lua/component/robot/.autorun.lua @@ -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) diff --git a/src/main/resources/assets/opencomputers/lua/component/robot/bin/dig.lua b/src/main/resources/assets/opencomputers/lua/component/robot/bin/dig.lua index 38d050bdf..b68022c99 100644 --- a/src/main/resources/assets/opencomputers/lua/component/robot/bin/dig.lua +++ b/src/main/resources/assets/opencomputers/lua/component/robot/bin/dig.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/kernel.lua b/src/main/resources/assets/opencomputers/lua/kernel.lua index cf313eb7a..4e0e6dd37 100644 --- a/src/main/resources/assets/opencomputers/lua/kernel.lua +++ b/src/main/resources/assets/opencomputers/lua/kernel.lua @@ -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) diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/alias.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/alias.lua index 37a819afc..4c7ad2658 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/alias.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/alias.lua @@ -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]) diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/cat.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/cat.lua index 85c978208..d4e1505f3 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/cat.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/cat.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/cd.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/cd.lua index dd51febd1..6080c1b06 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/cd.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/cd.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/cp.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/cp.lua index dbb4b6c8b..232ca1795 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/cp.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/cp.lua @@ -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 \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/df.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/df.lua index 734325590..4a304f465 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/df.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/df.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/edit.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/edit.lua index 1b22fb0b9..27e4e35b8 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/edit.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/edit.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/label.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/label.lua index f1b851a09..f9a231529 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/label.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/label.lua @@ -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 \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/ln.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/ln.lua index d5ace214c..659fe5063 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/ln.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/ln.lua @@ -18,5 +18,5 @@ end local result, reason = fs.link(target, linkpath) if not result then - io.write(reason) + io.stderr:write(reason) end \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/lua.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/lua.lua index d9d2a52a0..60d95eba7 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/lua.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/lua.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/man.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/man.lua index 2286baf21..8978409c8 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/man.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/man.lua @@ -16,4 +16,4 @@ for path in string.gmatch(os.getenv("MANPATH"), "[^:]+") do os.exit() end end -io.write("No manual entry for " .. topic) \ No newline at end of file +io.stderr:write("No manual entry for " .. topic) \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/mkdir.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/mkdir.lua index 003e98d08..833db7b2c 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/mkdir.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/mkdir.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/more.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/more.lua index e8deabda3..c105f14f9 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/more.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/more.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/mount.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/mount.lua index d720b40f4..a18d2ce2d 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/mount.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/mount.lua @@ -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 \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/mv.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/mv.lua index 44edf226c..9e1313a18 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/mv.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/mv.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/primary.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/primary.lua index 11499b680..c966abd37 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/primary.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/primary.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/redstone.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/redstone.lua index 455c65219..8fd7978a2 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/redstone.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/redstone.lua @@ -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] diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/resolution.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/resolution.lua index 6d43221c5..528981221 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/resolution.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/resolution.lua @@ -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() \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/rm.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/rm.lua index bd37499c3..72abfad63 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/rm.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/rm.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/set.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/set.lua index 4112f4a14..0a049dcda 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/set.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/set.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/sh.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/sh.lua index 89a4ee932..b60c13183 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/sh.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/sh.lua @@ -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 \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/umount.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/umount.lua index 81268c505..054a4e560 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/umount.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/umount.lua @@ -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 \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/unalias.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/unalias.lua index 3d4998484..47bbed0e5 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/unalias.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/unalias.lua @@ -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) diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/unset.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/unset.lua index 9f9801c43..8691b8ad6 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/unset.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/unset.lua @@ -1,9 +1,9 @@ local args = {...} if #args < 1 then - print "You have to specify which arguments to unset!" + print("Usage: unset [ [...]]") else for _, k in ipairs(args) do os.setenv(k, nil) end -end \ No newline at end of file +end \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/useradd.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/useradd.lua index 20e7f83fa..968c72e30 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/useradd.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/useradd.lua @@ -9,5 +9,5 @@ end local result, reason = computer.addUser(args[1]) if not result then - io.write(reason) + io.stderr:write(reason) end \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/userdel.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/userdel.lua index c25eba84a..4f8a157d2 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/userdel.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/userdel.lua @@ -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 \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/bin/which.lua b/src/main/resources/assets/opencomputers/lua/rom/bin/which.lua index 32e2cb7d9..911b81e3c 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/which.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/which.lua @@ -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 \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/boot/02_os.lua b/src/main/resources/assets/opencomputers/lua/rom/boot/01_os.lua similarity index 100% rename from src/main/resources/assets/opencomputers/lua/rom/boot/02_os.lua rename to src/main/resources/assets/opencomputers/lua/rom/boot/01_os.lua diff --git a/src/main/resources/assets/opencomputers/lua/rom/boot/01_io.lua b/src/main/resources/assets/opencomputers/lua/rom/boot/02_io.lua similarity index 60% rename from src/main/resources/assets/opencomputers/lua/rom/boot/01_io.lua rename to src/main/resources/assets/opencomputers/lua/rom/boot/02_io.lua index 024f66004..ff9386cab 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/boot/01_io.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/boot/02_io.lua @@ -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) \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/rom/lib/filesystem.lua b/src/main/resources/assets/opencomputers/lua/rom/lib/filesystem.lua index 8914c91ff..b01472b7b 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/lib/filesystem.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/lib/filesystem.lua @@ -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, diff --git a/src/main/resources/assets/opencomputers/lua/rom/lib/package.lua b/src/main/resources/assets/opencomputers/lua/rom/lib/package.lua index d97ac8bb4..d0dcd8eab 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/lib/package.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/lib/package.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/rom/lib/shell.lua b/src/main/resources/assets/opencomputers/lua/rom/lib/shell.lua index 7d46415bc..901ca2e03 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/lib/shell.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/lib/shell.lua @@ -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 ------------------------------------------------------------------------------- diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index d0bf6a74c..cb2bf7eff 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -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.