diff --git a/src/api/java/stargatetech2/api/IStackManager.java b/src/api/java/stargatetech2/api/IStackManager.java new file mode 100644 index 000000000..a1493c727 --- /dev/null +++ b/src/api/java/stargatetech2/api/IStackManager.java @@ -0,0 +1,34 @@ +package stargatetech2.api; + +import java.util.Collection; + +import net.minecraft.item.ItemStack; + +/** + * The way to get ItemStacks added to the game by StargateTech 2 + * + * @author LordFokas + */ +public interface IStackManager { + /** + * Used to fetch an ItemStack by it's name, with a default size of 1. + * + * @param stack The stack we want to fetch. + * @return The stack, or null if none was found. + */ + public ItemStack get(String stack); + + /** + * Used to fetch an ItemStack by it's name with a given size. + * + * @param stack The stack we want to fetch. + * @param size The size the stack comes with. Must be in the range 1 - 64. + * @return The stack, or null if none was found. + */ + public ItemStack get(String stack, int size); + + /** + * @return A list with the names of all the existing stacks. + */ + public Collection getAllStacks(); +} \ No newline at end of file diff --git a/src/api/java/stargatetech2/api/IStargateTechAPI.java b/src/api/java/stargatetech2/api/IStargateTechAPI.java index ab00336e3..04e0ad6ed 100644 --- a/src/api/java/stargatetech2/api/IStargateTechAPI.java +++ b/src/api/java/stargatetech2/api/IStargateTechAPI.java @@ -24,4 +24,9 @@ public interface IStargateTechAPI { * @return The current IFactory instance. */ public IFactory getFactory(); + + /** + * @return The current IStackManager instance. + */ + public IStackManager getStackManager(); } \ No newline at end of file diff --git a/src/api/java/stargatetech2/api/shields/IShieldController.java b/src/api/java/stargatetech2/api/shields/IShieldController.java new file mode 100644 index 000000000..4f3fa4763 --- /dev/null +++ b/src/api/java/stargatetech2/api/shields/IShieldController.java @@ -0,0 +1,37 @@ +package stargatetech2.api.shields; + +/** + * Implemented by the Shield Controller TileEntities. + * + * @author LordFokas + */ +public interface IShieldController { + /** + * Given the way shield emitters work together, you cannot + * directly access their ShieldPermissions object. + * This means you cannot use this method to change + * permissions on a shield. + * + * @return A deep clone of the ShieldPermissions object that + * defines this controller's shield behavior. + */ + public ShieldPermissions getPermissions(); + + /** + * @return True if the shield is activated, false otherwise. + */ + public boolean isShieldOn(); + + /** + * @return The name of the player who owns this Shield Controller. + */ + public String getOwner(); + + /** + * Checks if a player can access this device. + * + * @param player The player's name. + * @return Whether or not this player can access this device. + */ + public boolean hasAccess(String player); +} \ No newline at end of file diff --git a/src/api/java/stargatetech2/api/shields/IShieldable.java b/src/api/java/stargatetech2/api/shields/IShieldable.java index b185defe8..50003e0e3 100644 --- a/src/api/java/stargatetech2/api/shields/IShieldable.java +++ b/src/api/java/stargatetech2/api/shields/IShieldable.java @@ -10,16 +10,16 @@ import net.minecraft.world.World; public interface IShieldable { /** * Called by shield emitters to make blocks raise their shields. - * The block on {px, py, pz} contains an ITileShieldEmitter from which - * you can get the current ShieldPermissions object. + * The block on {px, py, pz} contains an IShieldController from + * which you can get the current ShieldPermissions object. * * @param world The world this IShieldable is on. * @param x This IShieldable's X Coordinate. * @param y This IShieldable's Y Coordinate. * @param z This IShieldable's Z Coordinate. - * @param px The X Coordinate of the shield emitter raising a shield on this block. - * @param py The Y Coordinate of the shield emitter raising a shield on this block. - * @param pz The Z Coordinate of the shield emitter raising a shield on this block. + * @param px The X Coordinate of the shield controller in charge of the shield on this block. + * @param py The Y Coordinate of the shield controller in charge of the shield on this block. + * @param pz The Z Coordinate of the shield controller in charge of the shield on this block. */ public void onShield(World world, int x, int y, int z, int px, int py, int pz); diff --git a/src/api/java/stargatetech2/api/shields/ShieldPermissions.java b/src/api/java/stargatetech2/api/shields/ShieldPermissions.java index 54517c40a..c9ee2b399 100644 --- a/src/api/java/stargatetech2/api/shields/ShieldPermissions.java +++ b/src/api/java/stargatetech2/api/shields/ShieldPermissions.java @@ -1,6 +1,7 @@ package stargatetech2.api.shields; -import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityMinecart; @@ -13,21 +14,22 @@ import net.minecraft.nbt.NBTTagCompound; public class ShieldPermissions { // Permission Flags - public static final int PERM_PLAYER = 0x01; - public static final int PERM_VILLAGER = 0x02; - public static final int PERM_ANIMAL = 0x04; - public static final int PERM_MONSTER = 0x08; - public static final int PERM_MINECART = 0x10; + public static final int PERM_FRIEND = 0x01; + public static final int PERM_PLAYER = 0x02; + public static final int PERM_VILLAGER = 0x04; + public static final int PERM_ANIMAL = 0x08; + public static final int PERM_MONSTER = 0x10; + public static final int PERM_VESSEL = 0x20; private int permValue = 0; - private ArrayList playerExceptions = new ArrayList(); + private LinkedList playerExceptions = new LinkedList(); /** * @return A default ShieldPermissions object. */ public static ShieldPermissions getDefault(){ ShieldPermissions perm = new ShieldPermissions(); - perm.allow(PERM_PLAYER); + perm.allow(PERM_FRIEND | PERM_PLAYER); return perm; } @@ -68,7 +70,7 @@ public class ShieldPermissions { * @return A list of strings containing the names of all the players * who currently are exceptions to the player permission setting. */ - public ArrayList getExceptionList(){ + public List getExceptionList(){ return playerExceptions; } @@ -96,7 +98,7 @@ public class ShieldPermissions { }else if(entity instanceof EntityMob){ allow = hasBit(PERM_MONSTER); }else if(entity instanceof EntityMinecart){ - allow = hasBit(PERM_MINECART); + allow = hasBit(PERM_VESSEL); } if(allow && entity.riddenByEntity != null && doDismount && entity.worldObj.isRemote == false){ if(!isEntityAllowed(entity.riddenByEntity, true)){ @@ -141,7 +143,7 @@ public class ShieldPermissions { if(nbt != null){ int exceptions = nbt.getInteger("exceptions"); permissions.permValue = nbt.getInteger("permValue"); - permissions.playerExceptions = new ArrayList(exceptions); + permissions.playerExceptions = new LinkedList(); for(int i = 0; i < exceptions; i++){ permissions.setPlayerException(nbt.getString("pex" + i)); } diff --git a/src/main/resources/assets/opencomputers/lua/kernel.lua b/src/main/resources/assets/opencomputers/lua/kernel.lua index 384ccc5c0..ef8945ac4 100644 --- a/src/main/resources/assets/opencomputers/lua/kernel.lua +++ b/src/main/resources/assets/opencomputers/lua/kernel.lua @@ -1,8 +1,13 @@ local hookInterval = 100 local deadline = math.huge +local hitDeadline = false local function checkDeadline() if computer.realTime() > deadline then debug.sethook(coroutine.running(), checkDeadline, "", 1) + if not hitDeadline then + deadline = deadline + 0.5 + end + hitDeadline = true error("too long without yielding", 0) end end @@ -570,6 +575,7 @@ local function main() while true do deadline = computer.realTime() + timeout -- timeout global is set by host + hitDeadline = false debug.sethook(co, checkDeadline, "", hookInterval) local result = table.pack(coroutine.resume(co, table.unpack(args, 1, args.n))) if not result[1] then @@ -584,4 +590,4 @@ end -- JNLua converts the coroutine to a string immediately, so we can't get the -- traceback later. Because of that we have to do the error handling here. -return pcall(main) \ No newline at end of file +return pcall(main) 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 342f9f04b..e3f31636f 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/bin/sh.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/bin/sh.lua @@ -130,7 +130,7 @@ if #args == 0 and (io.input() == io.stdin or options.i) and not options.c then end else -- execute command. - local result = table.pack(execute(table.unpack(args))) + local result = table.pack(execute(...)) if not result[1] then error(result[2]) end diff --git a/src/main/resources/assets/opencomputers/robot.names b/src/main/resources/assets/opencomputers/robot.names index 7ed760a47..f0dde3dd0 100644 --- a/src/main/resources/assets/opencomputers/robot.names +++ b/src/main/resources/assets/opencomputers/robot.names @@ -34,6 +34,8 @@ Skynet Terminator T-1000 Terminator T-800 Wheatley +Rosie +Uniblab # Perry Rhodan Robots, definitly not all... Anson Argyris diff --git a/src/main/scala/li/cil/oc/client/renderer/MonospaceFontRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/MonospaceFontRenderer.scala index 9ae19dd4d..c58e33df6 100644 --- a/src/main/scala/li/cil/oc/client/renderer/MonospaceFontRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/MonospaceFontRenderer.scala @@ -4,7 +4,6 @@ import li.cil.oc.client.Textures import li.cil.oc.util.{RenderState, PackedColor} import li.cil.oc.{OpenComputers, Settings} import net.minecraft.client.renderer.GLAllocation -import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.texture.TextureManager import org.lwjgl.opengl.GL11 import scala.io.Source @@ -46,7 +45,6 @@ object MonospaceFontRenderer { val s = Settings.get.fontCharScale val dw = charWidth * s - charWidth val dh = charHeight * s - charHeight - val t = Tessellator.instance // Now create lists for all printable chars. for (index <- 1 until 0xFF) { val x = (index - 1) % cols @@ -54,12 +52,16 @@ object MonospaceFontRenderer { val u = x * uStep val v = y * vStep GL11.glNewList(charLists + index, GL11.GL_COMPILE) - t.startDrawingQuads() - t.addVertexWithUV(-dw, charHeight * s, 0, u, v + vSize) - t.addVertexWithUV(charWidth * s, charHeight * s, 0, u + uSize, v + vSize) - t.addVertexWithUV(charWidth * s, -dh, 0, u + uSize, v) - t.addVertexWithUV(-dw, -dh, 0, u, v) - t.draw() + GL11.glBegin(GL11.GL_QUADS) + GL11.glTexCoord2d(u, v + vSize) + GL11.glVertex3d(-dw, charHeight * s, 0) + GL11.glTexCoord2d(u + uSize, v + vSize) + GL11.glVertex3d(charWidth * s, charHeight * s, 0) + GL11.glTexCoord2d(u + uSize, v) + GL11.glVertex3d(charWidth * s, -dh, 0) + GL11.glTexCoord2d(u, v) + GL11.glVertex3d(-dw, -dh, 0) + GL11.glEnd() GL11.glTranslatef(charWidth, 0, 0) GL11.glEndList() } diff --git a/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala b/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala index 6eee2c185..015dc30ef 100644 --- a/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala +++ b/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala @@ -86,13 +86,12 @@ class ClassTransformer extends IClassTransformer { def ensureStargateTechCompatibility(basicClass: Array[Byte]): Array[Byte] = { if (!Mods.StargateTech2.isAvailable) { - return basicClass + // No SGT2 or version is too old, abstract bus API doesn't exist. + val classNode = newClassNode(basicClass) + classNode.interfaces.remove("stargatetech2/api/bus/IBusDevice") + writeClass(classNode) } - - // Version of SGT2 is too old, abstract bus API doesn't exist. - val classNode = newClassNode(basicClass) - classNode.interfaces.remove("stargatetech2/api/bus/IBusDevice") - writeClass(classNode) + else basicClass } def injectEnvironmentImplementation(classNode: ClassNode, basicClass: Array[Byte]): Array[Byte] = { diff --git a/src/main/scala/li/cil/oc/server/component/robot/Player.scala b/src/main/scala/li/cil/oc/server/component/robot/Player.scala index 69cf2f7e2..ac905fdcc 100644 --- a/src/main/scala/li/cil/oc/server/component/robot/Player.scala +++ b/src/main/scala/li/cil/oc/server/component/robot/Player.scala @@ -319,7 +319,7 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Play }) } - private def isItemUseAllowed(stack: ItemStack) = { + private def isItemUseAllowed(stack: ItemStack) = stack == null || { (Settings.get.allowUseItemsWithDuration || stack.getMaxItemUseDuration <= 0) && (!PortalGun.isPortalGun(stack) || PortalGun.isStandardPortalGun(stack)) && !stack.isItemEqual(new ItemStack(Items.lead)) diff --git a/src/main/scala/li/cil/oc/server/component/robot/Robot.scala b/src/main/scala/li/cil/oc/server/component/robot/Robot.scala index 24f20db56..41661a311 100644 --- a/src/main/scala/li/cil/oc/server/component/robot/Robot.scala +++ b/src/main/scala/li/cil/oc/server/component/robot/Robot.scala @@ -233,7 +233,8 @@ class Robot(val robot: tileentity.Robot) extends ManagedComponent { Iterable(checkSideForFace(args, 1, facing)) } else { - ForgeDirection.VALID_DIRECTIONS.filter(_ != facing.getOpposite).toIterable + // Always try the direction we're looking first. + Iterable(facing) ++ ForgeDirection.VALID_DIRECTIONS.filter(side => side != facing && side != facing.getOpposite).toIterable } val sneaky = args.isBoolean(2) && args.checkBoolean(2) val stack = player.robotInventory.selectedItemStack @@ -339,7 +340,8 @@ class Robot(val robot: tileentity.Robot) extends ManagedComponent { Iterable(checkSideForFace(args, 1, facing)) } else { - ForgeDirection.VALID_DIRECTIONS.filter(_ != facing.getOpposite).toIterable + // Always try the direction we're looking first. + Iterable(facing) ++ ForgeDirection.VALID_DIRECTIONS.filter(side => side != facing && side != facing.getOpposite).toIterable } val sneaky = args.isBoolean(2) && args.checkBoolean(2) @@ -420,7 +422,8 @@ class Robot(val robot: tileentity.Robot) extends ManagedComponent { Iterable(checkSideForFace(args, 1, facing)) } else { - ForgeDirection.VALID_DIRECTIONS.filter(_ != facing.getOpposite).toIterable + // Always try the direction we're looking first. + Iterable(facing) ++ ForgeDirection.VALID_DIRECTIONS.filter(side => side != facing && side != facing.getOpposite).toIterable } val sneaky = args.isBoolean(2) && args.checkBoolean(2) val duration = diff --git a/src/main/scala/li/cil/oc/server/driver/item/AbstractBusCard.scala b/src/main/scala/li/cil/oc/server/driver/item/AbstractBusCard.scala index a7bf25d28..717a5aa94 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/AbstractBusCard.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/AbstractBusCard.scala @@ -5,13 +5,13 @@ import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import li.cil.oc.util.mods.Mods import net.minecraft.item.ItemStack -import net.minecraft.tileentity.{TileEntity => MCTileEntity} +import net.minecraft.tileentity.TileEntity import stargatetech2.api.bus.IBusDevice object AbstractBusCard extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, Items.abstractBus) - override def createEnvironment(stack: ItemStack, container: MCTileEntity) = if (Mods.StargateTech2.isAvailable) container match { + override def createEnvironment(stack: ItemStack, container: TileEntity) = if (Mods.StargateTech2.isAvailable) container match { case device: IBusDevice => new component.AbstractBus(device) case _ => null } diff --git a/src/main/scala/li/cil/oc/server/fs/Buffered.scala b/src/main/scala/li/cil/oc/server/fs/Buffered.scala index c90e3a383..c9d0dde26 100644 --- a/src/main/scala/li/cil/oc/server/fs/Buffered.scala +++ b/src/main/scala/li/cil/oc/server/fs/Buffered.scala @@ -94,7 +94,7 @@ trait Buffered extends OutputStreamFileSystem { FileUtils.deleteQuietly(childFile) childFile.createNewFile() val out = new io.FileOutputStream(childFile).getChannel - val in = java.nio.channels.Channels.newChannel(openInputStream(childPath).get) + val in = openInputChannel(childPath).get out.transferFrom(in, 0, Long.MaxValue) out.close() in.close() diff --git a/src/main/scala/li/cil/oc/server/fs/CompositeReadOnlyFileSystem.scala b/src/main/scala/li/cil/oc/server/fs/CompositeReadOnlyFileSystem.scala index 12cf6ad55..86840765e 100644 --- a/src/main/scala/li/cil/oc/server/fs/CompositeReadOnlyFileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/CompositeReadOnlyFileSystem.scala @@ -35,16 +35,26 @@ class CompositeReadOnlyFileSystem(factories: mutable.LinkedHashMap[String, Calla override def lastModified(path: String) = findFileSystem(path).fold(0L)(_.lastModified(path)) - override def list(path: String) = parts.values.foldLeft(Array.empty[String])((acc, fs) => { - if (fs.exists(path)) try { - val l = fs.list(path) - if (l != null) acc ++ l else acc - } - catch { - case _: Throwable => acc - } - else acc - }) + override def list(path: String) = if (isDirectory(path)) { + parts.values.foldLeft(mutable.Set.empty[String])((acc, fs) => { + if (fs.exists(path)) try { + val l = fs.list(path) + if (l != null) for (e <- l) { + val f = e.stripSuffix("/") + val d = f + "/" + // Avoid duplicates and always only use the latest entry. + acc -= f + acc -= d + acc += e + } + } + catch { + case _: Throwable => + } + acc + }).toArray + } + else null // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/server/fs/ComputerCraftFileSystem.scala b/src/main/scala/li/cil/oc/server/fs/ComputerCraftFileSystem.scala index b3bfb89e9..a478acdf9 100644 --- a/src/main/scala/li/cil/oc/server/fs/ComputerCraftFileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/ComputerCraftFileSystem.scala @@ -25,8 +25,8 @@ class ComputerCraftFileSystem(val mount: IMount) extends InputStreamFileSystem { // ----------------------------------------------------------------------- // - protected def openInputStream(path: String) = try { - Some(mount.openForRead(path)) + protected def openInputChannel(path: String) = try { + Some(new InputStreamChannel(mount.openForRead(path))) } catch { case _: Throwable => None } diff --git a/src/main/scala/li/cil/oc/server/fs/FileInputStreamFileSystem.scala b/src/main/scala/li/cil/oc/server/fs/FileInputStreamFileSystem.scala index 90e840f25..56322c7d4 100644 --- a/src/main/scala/li/cil/oc/server/fs/FileInputStreamFileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/FileInputStreamFileSystem.scala @@ -43,6 +43,23 @@ trait FileInputStreamFileSystem extends InputStreamFileSystem { // ----------------------------------------------------------------------- // - override protected def openInputStream(path: String): Option[io.InputStream] = - Some(new io.FileInputStream(new io.File(root, path))) + override protected def openInputChannel(path: String) = Some(new FileChannel(new io.File(root, path))) + + protected class FileChannel(file: io.File) extends InputChannel { + val channel = new io.RandomAccessFile(file, "r").getChannel + + override def position(newPosition: Long) = { + channel.position(newPosition) + channel.position + } + + override def position = channel.position + + override def close() = channel.close() + + override def isOpen = channel.isOpen + + override def read(dst: Array[Byte]) = channel.read(java.nio.ByteBuffer.wrap(dst)) + } + } diff --git a/src/main/scala/li/cil/oc/server/fs/InputStreamFileSystem.scala b/src/main/scala/li/cil/oc/server/fs/InputStreamFileSystem.scala index 01069e359..856fac148 100644 --- a/src/main/scala/li/cil/oc/server/fs/InputStreamFileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/InputStreamFileSystem.scala @@ -1,11 +1,13 @@ package li.cil.oc.server.fs -import java.io.{FileNotFoundException, IOException, InputStream} +import java.io.{FileNotFoundException, IOException} import li.cil.oc.api import li.cil.oc.api.fs.Mode import net.minecraft.nbt.{NBTTagList, NBTTagCompound} import net.minecraftforge.common.util.Constants.NBT import scala.collection.mutable +import java.nio.channels.ReadableByteChannel +import java.nio.ByteBuffer trait InputStreamFileSystem extends api.fs.FileSystem { private val handles = mutable.Map.empty[Int, Handle] @@ -26,9 +28,9 @@ trait InputStreamFileSystem extends api.fs.FileSystem { override def open(path: String, mode: Mode) = this.synchronized(if (mode == Mode.Read && exists(path) && !isDirectory(path)) { val handle = Iterator.continually((Math.random() * Int.MaxValue).toInt + 1).filterNot(handles.contains).next() - openInputStream(path) match { - case Some(stream) => - handles += handle -> new Handle(this, handle, path, stream) + openInputChannel(path) match { + case Some(channel) => + handles += handle -> new Handle(this, handle, path, channel) handle case _ => throw new FileNotFoundException() } @@ -50,10 +52,10 @@ trait InputStreamFileSystem extends api.fs.FileSystem { val handle = handleNbt.getInteger("handle") val path = handleNbt.getString("path") val position = handleNbt.getLong("position") - openInputStream(path) match { - case Some(stream) => - val fileHandle = new Handle(this, handle, path, stream) - fileHandle.position = stream.skip(position) // May be != position if the file changed since we saved. + openInputChannel(path) match { + case Some(channel) => + val fileHandle = new Handle(this, handle, path, channel) + channel.position(position) handles += handle -> fileHandle case _ => // The source file seems to have disappeared since last time. } @@ -63,7 +65,7 @@ trait InputStreamFileSystem extends api.fs.FileSystem { override def save(nbt: NBTTagCompound) = this.synchronized { val handlesNbt = new NBTTagList() for (file <- handles.values) { - assert(!file.isClosed) + assert(file.channel.isOpen) val handleNbt = new NBTTagCompound() handleNbt.setInteger("handle", file.handle) handleNbt.setString("path", file.path) @@ -75,34 +77,73 @@ trait InputStreamFileSystem extends api.fs.FileSystem { // ----------------------------------------------------------------------- // - protected def openInputStream(path: String): Option[InputStream] + protected def openInputChannel(path: String): Option[InputChannel] + + protected trait InputChannel extends ReadableByteChannel { + def isOpen: Boolean + + def close() + + def position: Long + + def position(newPosition: Long): Long + + def read(dst: Array[Byte]): Int + + override def read(dst: ByteBuffer) = { + if (dst.hasArray) { + read(dst.array()) + } + else { + val count = dst.limit - dst.position + val buffer = new Array[Byte](count) + val n = read(buffer) + dst.put(buffer, 0, n) + n + } + } + } + + protected class InputStreamChannel(val inputStream: java.io.InputStream) extends InputChannel { + var isOpen = true + + private var position_ = 0L + + override def close() = if (isOpen) { + isOpen = false + inputStream.close() + } + + override def position = position_ + + override def position(newPosition: Long) = { + inputStream.reset() + position_ = inputStream.skip(newPosition) + position_ + } + + override def read(dst: Array[Byte]) = { + val read = inputStream.read(dst) + position_ += read + read + } + } // ----------------------------------------------------------------------- // - private class Handle(val owner: InputStreamFileSystem, val handle: Int, val path: String, val stream: InputStream) extends api.fs.Handle { - var isClosed = false - var position = 0L + private class Handle(val owner: InputStreamFileSystem, val handle: Int, val path: String, val channel: InputChannel) extends api.fs.Handle { + override def position = channel.position override def length = owner.size(path) - override def close() = if (!isClosed) { - isClosed = true + override def close() = if (channel.isOpen) { owner.handles -= handle - stream.close() + channel.close() } - override def read(into: Array[Byte]) = { - val read = stream.read(into) - if (read >= 0) - position += read - read - } + override def read(into: Array[Byte]) = channel.read(into) - override def seek(to: Long) = { - stream.reset() - position = stream.skip(to) - position - } + override def seek(to: Long) = channel.position(to) override def write(value: Array[Byte]) = throw new IOException("bad file descriptor") } diff --git a/src/main/scala/li/cil/oc/server/fs/VirtualFileSystem.scala b/src/main/scala/li/cil/oc/server/fs/VirtualFileSystem.scala index 4b05766a6..6c523686f 100644 --- a/src/main/scala/li/cil/oc/server/fs/VirtualFileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/VirtualFileSystem.scala @@ -98,9 +98,9 @@ trait VirtualFileSystem extends OutputStreamFileSystem { // ----------------------------------------------------------------------- // - protected def openInputStream(path: String) = + protected def openInputChannel(path: String) = root.get(segments(path)) match { - case Some(obj: VirtualFile) => obj.openInputStream() + case Some(obj: VirtualFile) => obj.openInputStream().map(new InputStreamChannel(_)) case _ => None } diff --git a/src/main/scala/li/cil/oc/server/fs/ZipFileInputStreamFileSystem.scala b/src/main/scala/li/cil/oc/server/fs/ZipFileInputStreamFileSystem.scala index ceda7b062..790cd5761 100644 --- a/src/main/scala/li/cil/oc/server/fs/ZipFileInputStreamFileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/ZipFileInputStreamFileSystem.scala @@ -58,8 +58,8 @@ class ZipFileInputStreamFileSystem(private val archive: ArchiveDirectory) extend // ----------------------------------------------------------------------- // - override protected def openInputStream(path: String) = ZipFileInputStreamFileSystem.synchronized { - entry(path).map(_.openStream()) + override protected def openInputChannel(path: String) = ZipFileInputStreamFileSystem.synchronized { + entry(path).map(entry => new InputStreamChannel(entry.openStream())) } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/util/LuaStateFactory.scala b/src/main/scala/li/cil/oc/util/LuaStateFactory.scala index 6c3066800..94dc7efaa 100644 --- a/src/main/scala/li/cil/oc/util/LuaStateFactory.scala +++ b/src/main/scala/li/cil/oc/util/LuaStateFactory.scala @@ -220,12 +220,12 @@ object LuaStateFactory { case 0 => lua.pushNumber(random.nextDouble()) case 1 => val u = lua.checkNumber(1).toInt - lua.checkArg(1, 1 < u, "interval is empty") + lua.checkArg(1, 1 <= u, "interval is empty") lua.pushInteger(1 + random.nextInt(u)) case 2 => val l = lua.checkNumber(1).toInt val u = lua.checkNumber(2).toInt - lua.checkArg(1, l < u, "interval is empty") + lua.checkArg(1, l <= u, "interval is empty") lua.pushInteger(l + random.nextInt(u - (l - 1))) case _ => throw new IllegalArgumentException("wrong number of arguments") }