From f7386842d8a4ef957f1d2fecba594a8f1ab87b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Tue, 28 Jul 2015 22:01:00 +0200 Subject: [PATCH 1/5] Barely tested bit32 implementation in Lua 5.3 for backwards compat. --- .../opencomputers/loot/OpenOS/lib/bit32.lua | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/main/resources/assets/opencomputers/loot/OpenOS/lib/bit32.lua diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/bit32.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/bit32.lua new file mode 100644 index 000000000..8a05d80f2 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/bit32.lua @@ -0,0 +1,90 @@ +--[[ Backwards compat for Lua 5.3; only loaded in 5.3 because package.loaded is + prepopulated with the existing global bit32 in 5.2. ]] + +local bit32 = {} + +------------------------------------------------------------------------------- + +local function fold(init, op, ...) + local result = init + local args = table.pack(...) + for i = 1, args.n do + result = op(result, args[i]) + end + return result +end + +local function trim(n) + return n & 0xFFFFFFFF +end + +local function mask(w) + return ~(0xFFFFFFFF << w) +end + +function bit32.arshift(x, disp) + return x // (2 ^ disp) +end + +function bit32.band(...) + return fold(0xFFFFFFFF, function(a, b) return a & b end, ...) +end + +function bit32.bnot(x) + return ~x +end + +function bit32.bor(...) + return fold(0, function(a, b) return a | b end, ...) +end + +function bit32.btest(...) + return bit32.band(...) ~= 0 +end + +function bit32.bxor(...) + return fold(0, function(a, b) return a ~ b end, ...) +end + +local function fieldargs(f, w) + w = w or 1 + assert(f >= 0, "field cannot be negative") + assert(w > 0, "width must be positive") + assert(f + w <= 32, "trying to access non-existent bits") + return f, w +end + +function bit32.extract(n, field, width) + local f, w = fieldargs(field, width) + return (n >> f) & mask(w) +end + +function bit32.replace(n, v, field, width) + local f, w = fieldargs(field, width) + local m = mask(w) + return (n & ~(m << f)) | ((v & m) << f) +end + +function bit32.lrotate(x, disp) + if disp == 0 then return x + elseif disp < 0 then return bit32.rrotate(x, -disp) + else return trim((x << disp) | (x >> (32 - disp))) end +end + +function bit32.lshift(x, disp) + return trim(x << disp) +end + +function bit32.rrotate(x, disp) + if disp == 0 then return x + elseif disp < 0 then return bit32.lrotate(x, -disp) + else return trim((x >> disp) | (x << (32 - disp))) end +end + +function bit32.rshift(x, disp) + return trim(x >> disp) +end + +------------------------------------------------------------------------------- + +return bit32 From e92eb687118eea334cc0f05f5c3579660d69b154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Tue, 28 Jul 2015 22:12:58 +0200 Subject: [PATCH 2/5] Should fix potential race conditions between ServerThread and ClientShutdownThread, which both fire WorldEvent.Unload on the same world at the same time. --- src/main/scala/li/cil/oc/common/EventHandler.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index c3a24d744..8afcfd44d 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -338,8 +338,12 @@ object EventHandler { else false } + // This is called from the ServerThread *and* the ClientShutdownThread, which + // can potentially happen at the same time... for whatever reason. So let's + // synchronize what we're doing here to avoid race conditions (e.g. when + // disposing networks, where this actually triggered an assert). @SubscribeEvent - def onWorldUnload(e: WorldEvent.Unload) { + def onWorldUnload(e: WorldEvent.Unload): Unit = this.synchronized { if (!e.world.isRemote) { e.world.loadedTileEntityList.collect { case te: tileentity.traits.TileEntity => te.dispose() From a30dc39775d2ea2c0f6d2a972dfaa5a89817f507 Mon Sep 17 00:00:00 2001 From: Aaron Dandy Date: Sat, 1 Aug 2015 18:54:23 -0700 Subject: [PATCH 3/5] Remove outdated lua 5.3 warning in app config file --- src/main/resources/application.conf | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index fae53c971..e42498420 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -205,9 +205,7 @@ opencomputers { allowBytecode: false # Whether to make the Lua 5.3 architecture available. If enabled, you - # can reconfigure any CPU to use the Lua 5.3 architecture. This is - # not enabled by default for the time being, because it needs some - # more stability testing. + # can reconfigure any CPU to use the Lua 5.3 architecture. enableLua53: true # The sizes of the six levels of RAM, in kilobytes. This list must From a05c83e5e533a9c561888cc761c985f8a7e25af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 2 Aug 2015 21:11:13 +0200 Subject: [PATCH 4/5] Added unmanaged mode for hard drives and floppies. Refactored GUIs a bit. --- src/main/resources/application.conf | 14 + .../assets/opencomputers/lang/en_US.lang | 6 + .../textures/gui/button_drive_mode.png | Bin 0 -> 208 bytes .../opencomputers/textures/gui/drive.png | Bin 0 -> 326 bytes src/main/scala/li/cil/oc/Localization.scala | 12 + src/main/scala/li/cil/oc/Settings.scala | 9 + .../scala/li/cil/oc/client/GuiHandler.scala | 2 + .../scala/li/cil/oc/client/PacketSender.scala | 8 + .../scala/li/cil/oc/client/Textures.scala | 2 + .../li/cil/oc/client/gui/Assembler.scala | 24 +- .../scala/li/cil/oc/client/gui/Case.scala | 6 - .../oc/client/gui/CustomGuiContainer.scala | 6 +- .../scala/li/cil/oc/client/gui/Database.scala | 17 +- .../li/cil/oc/client/gui/Disassembler.scala | 4 +- .../scala/li/cil/oc/client/gui/Drive.scala | 47 +++ .../scala/li/cil/oc/client/gui/Drone.scala | 8 +- .../oc/client/gui/DynamicGuiContainer.scala | 2 +- .../scala/li/cil/oc/client/gui/Manual.scala | 30 +- .../scala/li/cil/oc/client/gui/Printer.scala | 14 +- .../scala/li/cil/oc/client/gui/Robot.scala | 4 - .../scala/li/cil/oc/client/gui/Server.scala | 15 +- .../li/cil/oc/client/gui/ServerRack.scala | 6 +- .../scala/li/cil/oc/client/gui/Switch.scala | 11 +- .../scala/li/cil/oc/client/gui/Tablet.scala | 17 +- .../oc/client/gui/traits/LockedHotbar.scala | 17 ++ .../li/cil/oc/client/gui/traits/Window.scala | 44 +++ src/main/scala/li/cil/oc/common/GuiType.scala | 1 + .../scala/li/cil/oc/common/PacketType.scala | 1 + .../scala/li/cil/oc/common/SaveHandler.scala | 2 +- .../li/cil/oc/common/item/FloppyDisk.scala | 17 +- .../li/cil/oc/common/item/HardDiskDrive.scala | 28 +- .../cil/oc/common/item/data/DriveData.scala | 22 ++ .../common/item/traits/FileSystemLike.scala | 46 +++ .../opencomputers/DriverFileSystem.scala | 20 +- .../li/cil/oc/server/PacketHandler.scala | 16 ++ .../li/cil/oc/server/component/Drive.scala | 268 ++++++++++++++++++ .../cil/oc/server/component/FileSystem.scala | 6 +- .../scala/li/cil/oc/server/fs/Buffered.scala | 4 +- 38 files changed, 586 insertions(+), 170 deletions(-) create mode 100644 src/main/resources/assets/opencomputers/textures/gui/button_drive_mode.png create mode 100644 src/main/resources/assets/opencomputers/textures/gui/drive.png create mode 100644 src/main/scala/li/cil/oc/client/gui/Drive.scala create mode 100644 src/main/scala/li/cil/oc/client/gui/traits/LockedHotbar.scala create mode 100644 src/main/scala/li/cil/oc/client/gui/traits/Window.scala create mode 100644 src/main/scala/li/cil/oc/common/item/data/DriveData.scala create mode 100644 src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala create mode 100644 src/main/scala/li/cil/oc/server/component/Drive.scala diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index fae53c971..e685d21f4 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -852,6 +852,20 @@ opencomputers { # runs on. As a side effect this pretty much determines the read # performance of file systems. maxReadBuffer: 2048 + + # Number of physical platters to pretend a disk has in unmanged mode. This + # controls seek times, in how it emulates sectors overlapping (thus sharing + # a common head position for access). + hddPlatterCounts: [ 2, 4, 6 ] + + # When skipping more than this number of sectors in unmanaged mode, the + # pause specified in sectorSeekTime will be enforced. We use this instead + # of linear scaling for movement because those values would have to be + # really small, which is hard to conceptualize and configure. + sectorSeekThreshold: 128 + + # The time to pause when the head movement threshold is exceeded. + sectorSeekTime: 0.1 } # Internet settings, security related. diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index 7f5fc50f1..6d8d2cdb6 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -191,6 +191,9 @@ oc:gui.Chat.WarningPower=No supported power providing mod available. Computers, oc:gui.Chat.WarningProjectRed=You are using a version of Project: Red that is incompatible with OpenComputers. Try updating your version of Project: Red. oc:gui.Chat.WarningRecipes=There were errors loading one or more recipes. Some items may be uncraftable. Please check your log file for more information. oc:gui.Chat.WarningSimpleComponent=An addon (yours?) using the §aSimpleComponent§f interface did §esomething wrong§f. Component logic could not be injected. Please check your log file for more information. +oc:gui.Drive.Managed=Managed +oc:gui.Drive.Unmanaged=Unmanaged +oc:gui.Drive.Warning=§lWarning§r: switching modes will result in a loss of all data currently stored on the disk! oc:gui.Error.ComponentOverflow=Too many components connected to the computer. oc:gui.Error.InternalError=Internal error, please see the log file. This is probably a bug. oc:gui.Error.NoCPU=No CPU is installed in the computer. @@ -272,6 +275,9 @@ oc:tooltip.Disassembler=Separates items into their original components. §lWarni oc:tooltip.Disk=Primitive medium that can be used to build persistent storage devices. oc:tooltip.DiskDrive.CC=ComputerCraft floppies are §asupported§7. oc:tooltip.DiskDrive=Allows reading and writing floppies. Can be installed in robots to allow inserting floppies later on. +oc:tooltip.DiskUsage=Disk usage: %s/%s Byte +oc:tooltip.DiskModeManaged=Mode: Managed +oc:tooltip.DiskModeUnmanaged=Mode: Unmanaged oc:tooltip.Drone=Drones are light-weight, fast reconnaissance units with limited cargo space. oc:tooltip.DroneCase=This casing is used to build Drones in the assembler. It has room for a small amount of components and provides endstone-powered levitation. oc:tooltip.EEPROM=Small, programmable storage that contains the BIOS computers use to boot. diff --git a/src/main/resources/assets/opencomputers/textures/gui/button_drive_mode.png b/src/main/resources/assets/opencomputers/textures/gui/button_drive_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..1da1bad6e89b7df82c48d4fc5322fd4d1448ead0 GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^Q-D~7gBeIhXgr<_q$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~-c6$u6Sgt$I_{P@_hWB>pEH#avA4i4__?%p)n-4-Yt;_2cT zQgQ3eHAh|s1D?YN*1Y`@e9z%W@JU92yO%Yywy(T==;w|347@4-E|kl2Q|Xi35e$N`m}?fn1=} z=KV|d14VXtx;TbZ+A&!Mg>TEAF-)+L(v4@eS91$JH*;0jsqAw) zg+8C+ChA)(J5&3dnJ?ji?4O@!ciU9$n}1;L-b#k;-{cPL-D|yLXE}5E_kYc|tNR~Z z+x?c|-QMo + Array(tier1: Int, tier2: Int, tier3: Int) + case _ => + OpenComputers.log.warn("Bad number of HDD platter counts, ignoring.") + Array(2, 4, 6) + } val floppySize = config.getInt("filesystem.floppySize") max 0 val tmpSize = config.getInt("filesystem.tmpSize") max 0 val maxHandles = config.getInt("filesystem.maxHandles") max 0 val maxReadBuffer = config.getInt("filesystem.maxReadBuffer") max 0 + val sectorSeekThreshold = config.getInt("filesystem.sectorSeekThreshold") + val sectorSeekTime = config.getDouble("filesystem.sectorSeekTime") // ----------------------------------------------------------------------- // // internet diff --git a/src/main/scala/li/cil/oc/client/GuiHandler.scala b/src/main/scala/li/cil/oc/client/GuiHandler.scala index 3169b6c9d..44190606b 100644 --- a/src/main/scala/li/cil/oc/client/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/client/GuiHandler.scala @@ -56,6 +56,8 @@ object GuiHandler extends CommonGuiHandler { } case Some(GuiType.Category.Item) => Delegator.subItem(player.getCurrentEquippedItem) match { + case Some(drive: item.traits.FileSystemLike) if id == GuiType.Drive.id => + new gui.Drive(player.inventory, () => player.getCurrentEquippedItem) case Some(database: item.UpgradeDatabase) if id == GuiType.Database.id => new gui.Database(player.inventory, new DatabaseInventory { override def tier = database.tier diff --git a/src/main/scala/li/cil/oc/client/PacketSender.scala b/src/main/scala/li/cil/oc/client/PacketSender.scala index e90ece5d9..725c296ea 100644 --- a/src/main/scala/li/cil/oc/client/PacketSender.scala +++ b/src/main/scala/li/cil/oc/client/PacketSender.scala @@ -26,6 +26,14 @@ object PacketSender { pb.sendToServer() } + def sendDriveMode(unmanaged: Boolean) { + val pb = new SimplePacketBuilder(PacketType.DriveMode) + + pb.writeBoolean(unmanaged) + + pb.sendToServer() + } + def sendDronePower(e: Drone, power: Boolean) { val pb = new SimplePacketBuilder(PacketType.DronePower) diff --git a/src/main/scala/li/cil/oc/client/Textures.scala b/src/main/scala/li/cil/oc/client/Textures.scala index 6e21bbc54..78b84868e 100644 --- a/src/main/scala/li/cil/oc/client/Textures.scala +++ b/src/main/scala/li/cil/oc/client/Textures.scala @@ -12,6 +12,7 @@ object Textures { val guiBackground = new ResourceLocation(Settings.resourceDomain, "textures/gui/background.png") val guiBar = new ResourceLocation(Settings.resourceDomain, "textures/gui/bar.png") val guiBorders = new ResourceLocation(Settings.resourceDomain, "textures/gui/borders.png") + val guiButtonDriveMode = new ResourceLocation(Settings.resourceDomain, "textures/gui/button_drive_mode.png") val guiButtonPower = new ResourceLocation(Settings.resourceDomain, "textures/gui/button_power.png") val guiButtonRange = new ResourceLocation(Settings.resourceDomain, "textures/gui/button_range.png") val guiButtonRun = new ResourceLocation(Settings.resourceDomain, "textures/gui/button_run.png") @@ -23,6 +24,7 @@ object Textures { val guiDatabase1 = new ResourceLocation(Settings.resourceDomain, "textures/gui/database1.png") val guiDatabase2 = new ResourceLocation(Settings.resourceDomain, "textures/gui/database2.png") val guiDisassembler = new ResourceLocation(Settings.resourceDomain, "textures/gui/disassembler.png") + val guiDrive = new ResourceLocation(Settings.resourceDomain, "textures/gui/drive.png") val guiDrone = new ResourceLocation(Settings.resourceDomain, "textures/gui/drone.png") val guiKeyboardMissing = new ResourceLocation(Settings.resourceDomain, "textures/gui/keyboard_missing.png") val guiManual = new ResourceLocation(Settings.resourceDomain, "textures/gui/manual.png") diff --git a/src/main/scala/li/cil/oc/client/gui/Assembler.scala b/src/main/scala/li/cil/oc/client/gui/Assembler.scala index a92a1e042..6998e5a95 100644 --- a/src/main/scala/li/cil/oc/client/gui/Assembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Assembler.scala @@ -1,7 +1,5 @@ package li.cil.oc.client.gui -import java.util - import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.gui.widget.ProgressBar @@ -36,17 +34,13 @@ class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Asse var info: Option[(Boolean, IChatComponent, Array[IChatComponent])] = None - private def assemblerContainer = inventorySlots.asInstanceOf[container.Assembler] - protected var runButton: ImageButton = _ private val progress = addWidget(new ProgressBar(28, 92)) - def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) + private def validate = AssemblerTemplates.select(inventoryContainer.getSlot(0).getStack).map(_.validate(inventoryContainer.otherInventory)) - private def validate = AssemblerTemplates.select(assemblerContainer.getSlot(0).getStack).map(_.validate(assemblerContainer.otherInventory)) - - private def canBuild = !assemblerContainer.isAssembling && validate.exists(_._1) + private def canBuild = !inventoryContainer.isAssembling && validate.exists(_._1) protected override def actionPerformed(button: GuiButton) { if (button.id == 0 && canBuild) { @@ -62,14 +56,14 @@ class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Asse override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) // Me lazy... prevents NEI render glitch. - if (!assemblerContainer.isAssembling) { + if (!inventoryContainer.isAssembling) { val message = - if (!assemblerContainer.getSlot(0).getHasStack) { + if (!inventoryContainer.getSlot(0).getHasStack) { Localization.Assembler.InsertTemplate } else info match { case Some((_, value, _)) if value != null => value.getUnformattedText - case _ if assemblerContainer.getSlot(0).getHasStack => Localization.Assembler.CollectResult + case _ if inventoryContainer.getSlot(0).getHasStack => Localization.Assembler.CollectResult case _ => "" } fontRendererObj.drawString(message, 30, 94, 0x404040) @@ -86,8 +80,8 @@ class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Asse } else if (func_146978_c(progress.x, progress.y, progress.width, progress.height, mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] - val timeRemaining = formatTime(assemblerContainer.assemblyRemainingTime) - tooltip.add(Localization.Assembler.Progress(assemblerContainer.assemblyProgress, timeRemaining)) + val timeRemaining = formatTime(inventoryContainer.assemblyRemainingTime) + tooltip.add(Localization.Assembler.Progress(inventoryContainer.assemblyProgress, timeRemaining)) copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRendererObj) } GL11.glPopAttrib() @@ -103,13 +97,11 @@ class Assembler(playerInventory: InventoryPlayer, val assembler: tileentity.Asse GL11.glColor3f(1, 1, 1) // Required under Linux. mc.renderEngine.bindTexture(Textures.guiRobotAssembler) drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) - if (assemblerContainer.isAssembling) progress.level = assemblerContainer.assemblyProgress / 100.0 + if (inventoryContainer.isAssembling) progress.level = inventoryContainer.assemblyProgress / 100.0 else progress.level = 0 drawWidgets() drawInventorySlots() } override protected def drawDisabledSlot(slot: ComponentSlot) {} - - override def doesGuiPauseGame = false } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/gui/Case.scala b/src/main/scala/li/cil/oc/client/gui/Case.scala index d1a16f6c2..244697cbb 100644 --- a/src/main/scala/li/cil/oc/client/gui/Case.scala +++ b/src/main/scala/li/cil/oc/client/gui/Case.scala @@ -1,7 +1,5 @@ package li.cil.oc.client.gui -import java.util - import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.{PacketSender => ClientPacketSender} @@ -16,8 +14,6 @@ import scala.collection.convert.WrapAsJava._ class Case(playerInventory: InventoryPlayer, val computer: tileentity.Case) extends DynamicGuiContainer(new container.Case(playerInventory, computer)) { protected var powerButton: ImageButton = _ - def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) - protected override def actionPerformed(button: GuiButton) { if (button.id == 0) { ClientPacketSender.sendComputerPower(computer, !computer.isRunning) @@ -52,6 +48,4 @@ class Case(playerInventory: InventoryPlayer, val computer: tileentity.Case) exte mc.renderEngine.bindTexture(Textures.guiComputer) drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) } - - override def doesGuiPauseGame = false } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala b/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala index fbc6c3afc..6060cdf28 100644 --- a/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/CustomGuiContainer.scala @@ -16,13 +16,17 @@ import scala.collection.convert.WrapAsScala._ // transformations that break things! Such fun. Many annoyed. And yes, this // is a common issue, have a look at EnderIO and Enchanting Plus. They have // to work around this, too. -abstract class CustomGuiContainer(container: Container) extends GuiContainer(container) with WidgetContainer { +abstract class CustomGuiContainer[C <: Container](val inventoryContainer: C) extends GuiContainer(inventoryContainer) with WidgetContainer { override def windowX = guiLeft override def windowY = guiTop override def windowZ = zLevel + override def doesGuiPauseGame = false + + protected def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) + // Pretty much Scalaified copy-pasta from base-class. override def drawHoveringText(text: util.List[_], x: Int, y: Int, font: FontRenderer) { copiedDrawHoveringText(text, x, y, font) diff --git a/src/main/scala/li/cil/oc/client/gui/Database.scala b/src/main/scala/li/cil/oc/client/gui/Database.scala index 58544a547..35d24123a 100644 --- a/src/main/scala/li/cil/oc/client/gui/Database.scala +++ b/src/main/scala/li/cil/oc/client/gui/Database.scala @@ -1,16 +1,17 @@ package li.cil.oc.client.gui import li.cil.oc.client.Textures -import li.cil.oc.common.inventory.DatabaseInventory import li.cil.oc.common.Tier import li.cil.oc.common.container +import li.cil.oc.common.inventory.DatabaseInventory import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.inventory.Slot import org.lwjgl.opengl.GL11 -class Database(playerInventory: InventoryPlayer, val databaseInventory: DatabaseInventory) extends DynamicGuiContainer(new container.Database(playerInventory, databaseInventory)) { +class Database(playerInventory: InventoryPlayer, val databaseInventory: DatabaseInventory) extends DynamicGuiContainer(new container.Database(playerInventory, databaseInventory)) with traits.LockedHotbar { ySize = 256 + override def lockedStack = databaseInventory.container + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) {} override protected def drawGuiContainerBackgroundLayer(dt: Float, mouseX: Int, mouseY: Int) { @@ -28,14 +29,4 @@ class Database(playerInventory: InventoryPlayer, val databaseInventory: Database drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) } } - - override def doesGuiPauseGame = false - - protected override def handleMouseClick(slot: Slot, slotNumber: Int, button: Int, shift: Int) { - if (slot == null || slot.getStack != databaseInventory.container) { - super.handleMouseClick(slot, slotNumber, button, shift) - } - } - - protected override def checkHotbarKeys(slot: Int) = false } diff --git a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala index 93bb8ebdd..998877220 100644 --- a/src/main/scala/li/cil/oc/client/gui/Disassembler.scala +++ b/src/main/scala/li/cil/oc/client/gui/Disassembler.scala @@ -9,8 +9,6 @@ import net.minecraft.entity.player.InventoryPlayer import org.lwjgl.opengl.GL11 class Disassembler(playerInventory: InventoryPlayer, val disassembler: tileentity.Disassembler) extends DynamicGuiContainer(new container.Disassembler(playerInventory, disassembler)) { - private def disassemblerContainer = inventorySlots.asInstanceOf[container.Disassembler] - val progress = addWidget(new ProgressBar(18, 65)) override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { @@ -23,7 +21,7 @@ class Disassembler(playerInventory: InventoryPlayer, val disassembler: tileentit GL11.glColor3f(1, 1, 1) // Required under Linux. mc.renderEngine.bindTexture(Textures.guiDisassembler) drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) - progress.level = disassemblerContainer.disassemblyProgress / 100.0 + progress.level = inventoryContainer.disassemblyProgress / 100.0 drawWidgets() } } diff --git a/src/main/scala/li/cil/oc/client/gui/Drive.scala b/src/main/scala/li/cil/oc/client/gui/Drive.scala new file mode 100644 index 000000000..ac2905131 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/gui/Drive.scala @@ -0,0 +1,47 @@ +package li.cil.oc.client.gui + +import li.cil.oc.Localization +import li.cil.oc.client.Textures +import li.cil.oc.client.{PacketSender => ClientPacketSender} +import li.cil.oc.common.item.data.DriveData +import net.minecraft.client.gui.GuiButton +import net.minecraft.client.gui.GuiScreen +import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.item.ItemStack + +class Drive(playerInventory: InventoryPlayer, val driveStack: () => ItemStack) extends GuiScreen with traits.Window { + override val windowHeight = 85 + + override def backgroundImage = Textures.guiDrive + + protected var managedButton: ImageButton = _ + protected var unmanagedButton: ImageButton = _ + + protected override def actionPerformed(button: GuiButton) { + if (button.id == 0) { + ClientPacketSender.sendDriveMode(false) + } + if (button.id == 1) { + ClientPacketSender.sendDriveMode(true) + } + } + + override def initGui(): Unit = { + super.initGui() + managedButton = new ImageButton(0, guiLeft + 11, guiTop + 11, 74, 18, Textures.guiButtonDriveMode, text = Localization.Drive.Managed, textColor = 0x608060, canToggle = true) + unmanagedButton = new ImageButton(1, guiLeft + 91, guiTop + 11, 74, 18, Textures.guiButtonDriveMode, text = Localization.Drive.Unmanaged, textColor = 0x608060, canToggle = true) + add(buttonList, managedButton) + add(buttonList, unmanagedButton) + } + + override def updateScreen(): Unit = { + unmanagedButton.toggled = new DriveData(driveStack()).isUnmanaged + managedButton.toggled = !unmanagedButton.toggled + super.updateScreen() + } + + override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { + super.drawScreen(mouseX, mouseY, dt) + fontRendererObj.drawSplitString(Localization.Drive.Warning, guiLeft + 7, guiTop + 37, xSize - 16, 0x404040) + } +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/gui/Drone.scala b/src/main/scala/li/cil/oc/client/gui/Drone.scala index 02f70d9ee..9186bfa4c 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drone.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drone.scala @@ -1,7 +1,5 @@ package li.cil.oc.client.gui -import java.util - import li.cil.oc.Localization import li.cil.oc.client.Textures import li.cil.oc.client.gui.widget.ProgressBar @@ -52,8 +50,6 @@ class Drone(playerInventory: InventoryPlayer, val drone: entity.Drone) extends D private val selectionsStates = 17 private val selectionStepV = 1 / selectionsStates.toDouble - def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) - protected override def actionPerformed(button: GuiButton) { if (button.id == 0) { ClientPacketSender.sendDronePower(drone, !drone.isRunning) @@ -62,9 +58,9 @@ class Drone(playerInventory: InventoryPlayer, val drone: entity.Drone) extends D override def drawScreen(mouseX: Int, mouseY: Int, dt: Float) { powerButton.toggled = drone.isRunning - bufferRenderer.dirty = drone.statusText.lines.zipWithIndex.map { + bufferRenderer.dirty = drone.statusText.lines.zipWithIndex.exists { case (line, i) => buffer.set(0, i, line, vertical = false) - }.contains(true) + } super.drawScreen(mouseX, mouseY, dt) } diff --git a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala index e60106ab8..7c7581b1f 100644 --- a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala @@ -21,7 +21,7 @@ import org.lwjgl.opengl.GL11 import scala.collection.convert.WrapAsScala._ -abstract class DynamicGuiContainer(container: Container) extends CustomGuiContainer(container) { +abstract class DynamicGuiContainer[C <: Container](container: C) extends CustomGuiContainer(container) { protected var hoveredSlot: Option[Slot] = None protected var hoveredStackNEI: Option[ItemStack] = None diff --git a/src/main/scala/li/cil/oc/client/gui/Manual.scala b/src/main/scala/li/cil/oc/client/gui/Manual.scala index 45a0d6392..02a3cc9eb 100644 --- a/src/main/scala/li/cil/oc/client/gui/Manual.scala +++ b/src/main/scala/li/cil/oc/client/gui/Manual.scala @@ -10,17 +10,15 @@ import li.cil.oc.client.renderer.markdown.segment.InteractiveSegment import li.cil.oc.client.renderer.markdown.segment.Segment import li.cil.oc.client.{Manual => ManualAPI} import net.minecraft.client.Minecraft -import net.minecraft.client.gui.Gui import net.minecraft.client.gui.GuiButton import net.minecraft.client.gui.GuiScreen -import net.minecraft.client.gui.ScaledResolution import org.lwjgl.input.Mouse import org.lwjgl.opengl.GL11 import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ -class Manual extends GuiScreen { +class Manual extends GuiScreen with traits.Window { final val documentMaxWidth = 230 final val documentMaxHeight = 176 final val scrollPosX = 244 @@ -33,10 +31,11 @@ class Manual extends GuiScreen { final val tabHeight = 26 final val maxTabsPerSide = 7 - var guiLeft = 0 - var guiTop = 0 - var xSize = 0 - var ySize = 0 + override val windowWidth = 256 + override val windowHeight = 192 + + override def backgroundImage = Textures.guiManual + var isDragging = false var document: Segment = null var documentHeight = 0 @@ -49,8 +48,6 @@ class Manual extends GuiScreen { def maxOffset = documentHeight - documentMaxHeight - def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) - def resolveLink(path: String, current: String): String = if (path.startsWith("/")) path else { @@ -84,8 +81,6 @@ class Manual extends GuiScreen { } } - override def doesGuiPauseGame = false - override def actionPerformed(button: GuiButton): Unit = { if (button.id >= 0 && button.id < ManualAPI.tabs.length) { api.Manual.navigate(ManualAPI.tabs(button.id).path) @@ -95,14 +90,6 @@ class Manual extends GuiScreen { override def initGui(): Unit = { super.initGui() - val screenSize = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight) - val guiSize = new ScaledResolution(mc, 256, 192) - val (midX, midY) = (screenSize.getScaledWidth / 2, screenSize.getScaledHeight / 2) - guiLeft = midX - guiSize.getScaledWidth / 2 - guiTop = midY - guiSize.getScaledHeight / 2 - xSize = guiSize.getScaledWidth - ySize = guiSize.getScaledHeight - for ((tab, i) <- ManualAPI.tabs.zipWithIndex if i < maxTabsPerSide) { val x = guiLeft + tabPosX val y = guiTop + tabPosY + i * (tabHeight - 1) @@ -116,14 +103,11 @@ class Manual extends GuiScreen { } override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { - mc.renderEngine.bindTexture(Textures.guiManual) - Gui.func_146110_a(guiLeft, guiTop, 0, 0, xSize, ySize, 256, 192) + super.drawScreen(mouseX, mouseY, dt) scrollButton.enabled = canScroll scrollButton.hoverOverride = isDragging - super.drawScreen(mouseX, mouseY, dt) - for ((tab, i) <- ManualAPI.tabs.zipWithIndex if i < maxTabsPerSide) { val button = buttonList.get(i).asInstanceOf[ImageButton] GL11.glPushMatrix() diff --git a/src/main/scala/li/cil/oc/client/gui/Printer.scala b/src/main/scala/li/cil/oc/client/gui/Printer.scala index c28bbc0a5..0a0aaeb07 100644 --- a/src/main/scala/li/cil/oc/client/gui/Printer.scala +++ b/src/main/scala/li/cil/oc/client/gui/Printer.scala @@ -35,8 +35,6 @@ class Printer(playerInventory: InventoryPlayer, val printer: tileentity.Printer) override def barTexture = Textures.guiPrinterProgress }) - private def printerContainer = inventorySlots.asInstanceOf[container.Printer] - override def initGui() { super.initGui() } @@ -49,12 +47,12 @@ class Printer(playerInventory: InventoryPlayer, val printer: tileentity.Printer) GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) // Me lazy... prevents NEI render glitch. if (func_146978_c(materialBar.x, materialBar.y, materialBar.width, materialBar.height, mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] - tooltip.add(printerContainer.amountMaterial + "/" + printer.maxAmountMaterial) + tooltip.add(inventoryContainer.amountMaterial + "/" + printer.maxAmountMaterial) copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRendererObj) } if (func_146978_c(inkBar.x, inkBar.y, inkBar.width, inkBar.height, mouseX, mouseY)) { val tooltip = new java.util.ArrayList[String] - tooltip.add(printerContainer.amountInk + "/" + printer.maxAmountInk) + tooltip.add(inventoryContainer.amountInk + "/" + printer.maxAmountInk) copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRendererObj) } GL11.glPopAttrib() @@ -64,14 +62,12 @@ class Printer(playerInventory: InventoryPlayer, val printer: tileentity.Printer) GL11.glColor3f(1, 1, 1) // Required under Linux. mc.renderEngine.bindTexture(Textures.guiPrinter) drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) - materialBar.level = printerContainer.amountMaterial / printer.maxAmountMaterial.toDouble - inkBar.level = printerContainer.amountInk / printer.maxAmountInk.toDouble - progressBar.level = printerContainer.progress + materialBar.level = inventoryContainer.amountMaterial / printer.maxAmountMaterial.toDouble + inkBar.level = inventoryContainer.amountInk / printer.maxAmountInk.toDouble + progressBar.level = inventoryContainer.progress drawWidgets() drawInventorySlots() } override protected def drawDisabledSlot(slot: ComponentSlot) {} - - override def doesGuiPauseGame = false } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index f2857b582..063865d5d 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -1,7 +1,5 @@ package li.cil.oc.client.gui -import java.util - import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.api @@ -78,8 +76,6 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten private val selectionsStates = 17 private val selectionStepV = 1 / selectionsStates.toDouble - def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) - protected override def actionPerformed(button: GuiButton) { if (button.id == 0) { ClientPacketSender.sendComputerPower(robot, !robot.isRunning) diff --git a/src/main/scala/li/cil/oc/client/gui/Server.scala b/src/main/scala/li/cil/oc/client/gui/Server.scala index 6540eddad..7895ebc24 100644 --- a/src/main/scala/li/cil/oc/client/gui/Server.scala +++ b/src/main/scala/li/cil/oc/client/gui/Server.scala @@ -5,10 +5,11 @@ import li.cil.oc.client.Textures import li.cil.oc.common.container import li.cil.oc.common.inventory.ServerInventory import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.inventory.Slot import org.lwjgl.opengl.GL11 -class Server(playerInventory: InventoryPlayer, serverInventory: ServerInventory) extends DynamicGuiContainer(new container.Server(playerInventory, serverInventory)) { +class Server(playerInventory: InventoryPlayer, serverInventory: ServerInventory) extends DynamicGuiContainer(new container.Server(playerInventory, serverInventory)) with traits.LockedHotbar { + override def lockedStack = serverInventory.container + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) { super.drawSecondaryForegroundLayer(mouseX, mouseY) fontRendererObj.drawString( @@ -21,14 +22,4 @@ class Server(playerInventory: InventoryPlayer, serverInventory: ServerInventory) mc.renderEngine.bindTexture(Textures.guiServer) drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize) } - - override def doesGuiPauseGame = false - - protected override def handleMouseClick(slot: Slot, slotNumber: Int, button: Int, shift: Int) { - if (slot == null || slot.getStack != serverInventory.container) { - super.handleMouseClick(slot, slotNumber, button, shift) - } - } - - protected override def checkHotbarKeys(slot: Int) = false } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/gui/ServerRack.scala b/src/main/scala/li/cil/oc/client/gui/ServerRack.scala index 6ea5a75e1..3f29ba2c0 100644 --- a/src/main/scala/li/cil/oc/client/gui/ServerRack.scala +++ b/src/main/scala/li/cil/oc/client/gui/ServerRack.scala @@ -1,7 +1,5 @@ package li.cil.oc.client.gui -import java.util - import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.client.Textures @@ -35,8 +33,6 @@ class ServerRack(playerInventory: InventoryPlayer, val rack: tileentity.ServerRa case _ => Localization.ServerRack.None } - def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) - protected override def actionPerformed(button: GuiButton) { if (button.id >= 0 && button.id <= 3) { ClientPacketSender.sendServerPower(rack, button.id, !rack.isRunning(button.id)) @@ -46,7 +42,7 @@ class ServerRack(playerInventory: InventoryPlayer, val rack: tileentity.ServerRa val sides = ForgeDirection.VALID_DIRECTIONS.map(Option(_)) ++ Seq(None) val currentSide = sides.indexOf(rack.sides(number)) val searchSides = sides.drop(currentSide + 1) ++ sides.take(currentSide + 1) - val nextSide = searchSides.find(side => side != Option(ForgeDirection.SOUTH) && (!rack.sides.contains(side) || side == None)) match { + val nextSide = searchSides.find(side => side != Option(ForgeDirection.SOUTH) && (!rack.sides.contains(side) || side.isEmpty)) match { case Some(side) => side case _ => None } diff --git a/src/main/scala/li/cil/oc/client/gui/Switch.scala b/src/main/scala/li/cil/oc/client/gui/Switch.scala index a60302136..ea2dc471f 100644 --- a/src/main/scala/li/cil/oc/client/gui/Switch.scala +++ b/src/main/scala/li/cil/oc/client/gui/Switch.scala @@ -8,7 +8,6 @@ import li.cil.oc.common.tileentity import net.minecraft.entity.player.InventoryPlayer class Switch(playerInventory: InventoryPlayer, val switch: tileentity.Switch) extends DynamicGuiContainer(new container.Switch(playerInventory, switch)) { - private val switchContainer = inventorySlots.asInstanceOf[container.Switch] private val format = new DecimalFormat("#.##hz") override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { @@ -28,14 +27,14 @@ class Switch(playerInventory: InventoryPlayer, val switch: tileentity.Switch) ex 14, 58, 0x404040) fontRendererObj.drawString( - format.format(20f / switchContainer.relayDelay), + format.format(20f / inventoryContainer.relayDelay), 108, 20, 0x404040) fontRendererObj.drawString( - switchContainer.packetsPerCycleAvg + " / " + switchContainer.relayAmount, - 108, 39, thresholdBasedColor(switchContainer.packetsPerCycleAvg, math.ceil(switchContainer.relayAmount / 2f).toInt, switchContainer.relayAmount)) + inventoryContainer.packetsPerCycleAvg + " / " + inventoryContainer.relayAmount, + 108, 39, thresholdBasedColor(inventoryContainer.packetsPerCycleAvg, math.ceil(inventoryContainer.relayAmount / 2f).toInt, inventoryContainer.relayAmount)) fontRendererObj.drawString( - switchContainer.queueSize + " / " + switchContainer.maxQueueSize, - 108, 58, thresholdBasedColor(switchContainer.queueSize, switchContainer.maxQueueSize / 2, switchContainer.maxQueueSize)) + inventoryContainer.queueSize + " / " + inventoryContainer.maxQueueSize, + 108, 58, thresholdBasedColor(inventoryContainer.queueSize, inventoryContainer.maxQueueSize / 2, inventoryContainer.maxQueueSize)) } private def thresholdBasedColor(value: Int, yellow: Int, red: Int) = { diff --git a/src/main/scala/li/cil/oc/client/gui/Tablet.scala b/src/main/scala/li/cil/oc/client/gui/Tablet.scala index 48698f2fc..e5137d1d2 100644 --- a/src/main/scala/li/cil/oc/client/gui/Tablet.scala +++ b/src/main/scala/li/cil/oc/client/gui/Tablet.scala @@ -4,23 +4,14 @@ import li.cil.oc.Localization import li.cil.oc.common.container import li.cil.oc.common.item.TabletWrapper import net.minecraft.entity.player.InventoryPlayer -import net.minecraft.inventory.Slot -class Tablet(playerInventory: InventoryPlayer, val tablet: TabletWrapper) extends DynamicGuiContainer(new container.Tablet(playerInventory, tablet)) { +class Tablet(playerInventory: InventoryPlayer, val tablet: TabletWrapper) extends DynamicGuiContainer(new container.Tablet(playerInventory, tablet)) with traits.LockedHotbar { + override def lockedStack = tablet.stack + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { super.drawSecondaryForegroundLayer(mouseX, mouseY) fontRendererObj.drawString( Localization.localizeImmediately(tablet.getInventoryName), 8, 6, 0x404040) } - - override def doesGuiPauseGame = false - - protected override def handleMouseClick(slot: Slot, slotNumber: Int, button: Int, shift: Int) { - if (slot == null || slot.getStack != tablet.stack) { - super.handleMouseClick(slot, slotNumber, button, shift) - } - } - - protected override def checkHotbarKeys(slot: Int) = false -} +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/gui/traits/LockedHotbar.scala b/src/main/scala/li/cil/oc/client/gui/traits/LockedHotbar.scala new file mode 100644 index 000000000..d336efca9 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/gui/traits/LockedHotbar.scala @@ -0,0 +1,17 @@ +package li.cil.oc.client.gui.traits + +import net.minecraft.client.gui.inventory.GuiContainer +import net.minecraft.inventory.Slot +import net.minecraft.item.ItemStack + +trait LockedHotbar extends GuiContainer { + def lockedStack: ItemStack + + protected override def handleMouseClick(slot: Slot, slotNumber: Int, button: Int, shift: Int) { + if (slot == null || slot.getStack != lockedStack) { + super.handleMouseClick(slot, slotNumber, button, shift) + } + } + + protected override def checkHotbarKeys(keyCode: Int) = false +} diff --git a/src/main/scala/li/cil/oc/client/gui/traits/Window.scala b/src/main/scala/li/cil/oc/client/gui/traits/Window.scala new file mode 100644 index 000000000..0cb7e2eaa --- /dev/null +++ b/src/main/scala/li/cil/oc/client/gui/traits/Window.scala @@ -0,0 +1,44 @@ +package li.cil.oc.client.gui.traits + +import java.util + +import net.minecraft.client.gui.Gui +import net.minecraft.client.gui.GuiScreen +import net.minecraft.client.gui.ScaledResolution +import net.minecraft.util.ResourceLocation + +trait Window extends GuiScreen { + var guiLeft = 0 + var guiTop = 0 + var xSize = 0 + var ySize = 0 + + val windowWidth = 176 + val windowHeight = 166 + + def backgroundImage: ResourceLocation + + protected def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) + + override def doesGuiPauseGame = false + + override def initGui(): Unit = { + super.initGui() + + val screenSize = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight) + val guiSize = new ScaledResolution(mc, windowWidth, windowHeight) + val (midX, midY) = (screenSize.getScaledWidth / 2, screenSize.getScaledHeight / 2) + guiLeft = midX - guiSize.getScaledWidth / 2 + guiTop = midY - guiSize.getScaledHeight / 2 + xSize = guiSize.getScaledWidth + ySize = guiSize.getScaledHeight + } + + override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { + mc.renderEngine.bindTexture(backgroundImage) + Gui.func_146110_a(guiLeft, guiTop, 0, 0, xSize, ySize, windowWidth, windowHeight) + + super.drawScreen(mouseX, mouseY, dt) + } + +} diff --git a/src/main/scala/li/cil/oc/common/GuiType.scala b/src/main/scala/li/cil/oc/common/GuiType.scala index 361727f03..864bdbf39 100644 --- a/src/main/scala/li/cil/oc/common/GuiType.scala +++ b/src/main/scala/li/cil/oc/common/GuiType.scala @@ -20,6 +20,7 @@ object GuiType extends ScalaEnum { val Database = new EnumVal { def name = "Database"; def subType = GuiType.Category.Item } val Disassembler = new EnumVal { def name = "Disassembler"; def subType = GuiType.Category.Block } val DiskDrive = new EnumVal { def name = "DiskDrive"; def subType = GuiType.Category.Block } + val Drive = new EnumVal { def name = "Drive"; def subType = GuiType.Category.Item } val Drone = new EnumVal { def name = "Drone"; def subType = GuiType.Category.Entity } val Manual = new EnumVal { def name = "Manual"; def subType = GuiType.Category.None } val Printer = new EnumVal { def name = "Printer"; def subType = GuiType.Category.Block } diff --git a/src/main/scala/li/cil/oc/common/PacketType.scala b/src/main/scala/li/cil/oc/common/PacketType.scala index 770e3c389..24ad97452 100644 --- a/src/main/scala/li/cil/oc/common/PacketType.scala +++ b/src/main/scala/li/cil/oc/common/PacketType.scala @@ -60,6 +60,7 @@ object PacketType extends Enumeration { // Client -> Server ComputerPower, CopyToAnalyzer, + DriveMode, DronePower, KeyDown, KeyUp, diff --git a/src/main/scala/li/cil/oc/common/SaveHandler.scala b/src/main/scala/li/cil/oc/common/SaveHandler.scala index 2fdc003a8..5277c61fe 100644 --- a/src/main/scala/li/cil/oc/common/SaveHandler.scala +++ b/src/main/scala/li/cil/oc/common/SaveHandler.scala @@ -230,7 +230,7 @@ object SaveHandler { // systems, to avoid deleting in-use folders here. System.currentTimeMillis() - file.lastModified() > TimeToHoldOntoOldSaves && { val list = file.list() - list == null || list.length == 0 + list == null || list.isEmpty } }) if (emptyDirs != null) { diff --git a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala index 4094f115d..5712678f9 100644 --- a/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala +++ b/src/main/scala/li/cil/oc/common/item/FloppyDisk.scala @@ -1,19 +1,16 @@ package li.cil.oc.common.item -import java.util - import cpw.mods.fml.relauncher.Side import cpw.mods.fml.relauncher.SideOnly import li.cil.oc.Settings import li.cil.oc.util.Color -import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack -class FloppyDisk(val parent: Delegator) extends traits.Delegate { +class FloppyDisk(val parent: Delegator) extends traits.Delegate with traits.FileSystemLike { // Necessary for anonymous subclasses used for loot disks. override def unlocalizedName = "FloppyDisk" - override protected def tooltipName = None + val kiloBytes = Settings.get.floppySize val icons = Array.fill[Icon](16)(null) @@ -24,16 +21,6 @@ class FloppyDisk(val parent: Delegator) extends traits.Delegate { else Some(icons(8)) - override def tooltipLines(stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean) = { - if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "data")) { - val nbt = stack.getTagCompound.getCompoundTag(Settings.namespace + "data") - if (nbt.hasKey(Settings.namespace + "fs.label")) { - tooltip.add(nbt.getString(Settings.namespace + "fs.label")) - } - } - super.tooltipLines(stack, player, tooltip, advanced) - } - override def registerIcons(iconRegister: IconRegister) { val baseTextureName = Settings.resourceDomain + ":" + unlocalizedName + "_" Color.dyes.zipWithIndex.foreach { diff --git a/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala b/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala index 12774f234..da7b2a227 100644 --- a/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala +++ b/src/main/scala/li/cil/oc/common/item/HardDiskDrive.scala @@ -1,36 +1,12 @@ package li.cil.oc.common.item -import java.util - import li.cil.oc.Settings -import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack -class HardDiskDrive(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier { +class HardDiskDrive(val parent: Delegator, val tier: Int) extends traits.Delegate with traits.ItemTier with traits.FileSystemLike { override val unlocalizedName = super.unlocalizedName + tier val kiloBytes = Settings.get.hddSizes(tier) - - override protected def tooltipName = None - - override def tooltipLines(stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean) = { - if (stack.hasTagCompound) { - val nbt = stack.getTagCompound - if (nbt.hasKey(Settings.namespace + "data")) { - val data = nbt.getCompoundTag(Settings.namespace + "data") - if (data.hasKey(Settings.namespace + "fs.label")) { - tooltip.add(data.getString(Settings.namespace + "fs.label")) - } - if (advanced && data.hasKey("fs")) { - val fsNbt = data.getCompoundTag("fs") - if (fsNbt.hasKey("capacity.used")) { - val used = fsNbt.getLong("capacity.used") - tooltip.add(s"Disk usage: $used/${kiloBytes * 1024} Byte") - } - } - } - } - super.tooltipLines(stack, player, tooltip, advanced) - } + val platterCount = Settings.get.hddPlatterCounts(tier) override def displayName(stack: ItemStack) = { val localizedName = parent.internalGetItemStackDisplayName(stack) diff --git a/src/main/scala/li/cil/oc/common/item/data/DriveData.scala b/src/main/scala/li/cil/oc/common/item/data/DriveData.scala new file mode 100644 index 000000000..8b81d97ea --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/data/DriveData.scala @@ -0,0 +1,22 @@ +package li.cil.oc.common.item.data + +import li.cil.oc.Settings +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound + +class DriveData extends ItemData(null) { + def this(stack: ItemStack) { + this() + load(stack) + } + + var isUnmanaged = false + + override def load(nbt: NBTTagCompound) { + isUnmanaged = nbt.getBoolean(Settings.namespace + "unmanaged") + } + + override def save(nbt: NBTTagCompound) { + nbt.setBoolean(Settings.namespace + "unmanaged", isUnmanaged) + } +} diff --git a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala new file mode 100644 index 000000000..7383f244c --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala @@ -0,0 +1,46 @@ +package li.cil.oc.common.item.traits + +import java.util + +import li.cil.oc.Localization +import li.cil.oc.OpenComputers +import li.cil.oc.Settings +import li.cil.oc.common.GuiType +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.world.World + +trait FileSystemLike extends Delegate { + override protected def tooltipName = None + + def kiloBytes: Int + + override def tooltipLines(stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean) = { + if (stack.hasTagCompound) { + val nbt = stack.getTagCompound + if (nbt.hasKey(Settings.namespace + "data")) { + val data = nbt.getCompoundTag(Settings.namespace + "data") + if (data.hasKey(Settings.namespace + "fs.label")) { + tooltip.add(data.getString(Settings.namespace + "fs.label")) + } + if (advanced && data.hasKey("fs")) { + val fsNbt = data.getCompoundTag("fs") + if (fsNbt.hasKey("capacity.used")) { + val used = fsNbt.getLong("capacity.used") + tooltip.add(Localization.Tooltip.DiskUsage(used, kiloBytes * 1024)) + } + } + } + tooltip.add(Localization.Tooltip.DiskMode(nbt.getBoolean(Settings.namespace + "unmanaged"))) + } + super.tooltipLines(stack, player, tooltip, advanced) + } + + override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ItemStack = { + if (!player.isSneaking) { + player.openGui(OpenComputers, GuiType.Drive.id, world, 0, 0, 0) + player.swingItem() + } + stack + } +} diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala index 9d8b9b966..1ef419562 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala @@ -10,6 +10,8 @@ import li.cil.oc.common.Slot import li.cil.oc.common.item.Delegator import li.cil.oc.common.item.FloppyDisk import li.cil.oc.common.item.HardDiskDrive +import li.cil.oc.common.item.data.DriveData +import li.cil.oc.server.component.Drive import li.cil.oc.server.fs.FileSystem.ItemLabel import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound @@ -23,8 +25,8 @@ object DriverFileSystem extends Item { override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = Delegator.subItem(stack) match { - case Some(hdd: HardDiskDrive) => createEnvironment(stack, hdd.kiloBytes * 1024, host, hdd.tier + 2) - case Some(disk: FloppyDisk) => createEnvironment(stack, Settings.get.floppySize * 1024, host, 1) + case Some(hdd: HardDiskDrive) => createEnvironment(stack, hdd.kiloBytes * 1024, hdd.platterCount, host, hdd.tier + 2) + case Some(disk: FloppyDisk) => createEnvironment(stack, Settings.get.floppySize * 1024, 1, host, 1) case _ => null } @@ -41,7 +43,7 @@ object DriverFileSystem extends Item { case _ => 0 } - private def createEnvironment(stack: ItemStack, capacity: Int, host: EnvironmentHost, speed: Int) = { + private def createEnvironment(stack: ItemStack, capacity: Int, platterCount: Int, host: EnvironmentHost, speed: Int) = { if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "lootFactory")) { // Loot disk, create file system using factory callback. Loot.factories.get(stack.getTagCompound.getString(Settings.namespace + "lootFactory")) match { @@ -59,9 +61,17 @@ object DriverFileSystem extends Item { // node's address as the folder name... so we generate the address here, // if necessary. No one will know, right? Right!? val address = addressFromTag(dataTag(stack)) + val label = new ReadWriteItemLabel(stack) val isFloppy = api.Items.get(stack) == api.Items.get(Constants.ItemName.Floppy) - val fs = oc.api.FileSystem.fromSaveDirectory(address, capacity, Settings.get.bufferChanges) - val environment = oc.api.FileSystem.asManagedEnvironment(fs, new ReadWriteItemLabel(stack), host, Settings.resourceDomain + ":" + (if (isFloppy) "floppy_access" else "hdd_access"), speed) + val sound = Settings.resourceDomain + ":" + (if (isFloppy) "floppy_access" else "hdd_access") + val drive = new DriveData(stack) + val environment = if (drive.isUnmanaged) { + Drive(capacity, platterCount, label, Option(host), Option(sound), speed) + } + else { + val fs = oc.api.FileSystem.fromSaveDirectory(address, capacity, Settings.get.bufferChanges) + oc.api.FileSystem.asManagedEnvironment(fs, label, host, sound, speed) + } if (environment != null && environment.node != null) { environment.node.asInstanceOf[oc.server.network.Node].address = address } diff --git a/src/main/scala/li/cil/oc/server/PacketHandler.scala b/src/main/scala/li/cil/oc/server/PacketHandler.scala index f9fae1a13..45d948126 100644 --- a/src/main/scala/li/cil/oc/server/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/server/PacketHandler.scala @@ -10,6 +10,9 @@ import li.cil.oc.common.Achievement import li.cil.oc.common.PacketType import li.cil.oc.common.component.TextBuffer import li.cil.oc.common.entity.Drone +import li.cil.oc.common.item.Delegator +import li.cil.oc.common.item.data.DriveData +import li.cil.oc.common.item.traits.FileSystemLike import li.cil.oc.common.tileentity._ import li.cil.oc.common.tileentity.traits.Computer import li.cil.oc.common.tileentity.traits.TileEntity @@ -34,6 +37,7 @@ object PacketHandler extends CommonPacketHandler { p.packetType match { case PacketType.ComputerPower => onComputerPower(p) case PacketType.CopyToAnalyzer => onCopyToAnalyzer(p) + case PacketType.DriveMode => onDriveMode(p) case PacketType.DronePower => onDronePower(p) case PacketType.KeyDown => onKeyDown(p) case PacketType.KeyUp => onKeyUp(p) @@ -77,6 +81,18 @@ object PacketHandler extends CommonPacketHandler { } } + def onDriveMode(p: PacketParser) = p.player match { + case player: EntityPlayerMP => + Delegator.subItem(player.getCurrentEquippedItem) match { + case Some(drive: FileSystemLike) => + val data = new DriveData(player.getCurrentEquippedItem) + data.isUnmanaged = p.readBoolean() + data.save(player.getCurrentEquippedItem) + case _ => // Invalid packet. + } + case _ => // Invalid packet. + } + def onDronePower(p: PacketParser) = p.readEntity[Drone]() match { case Some(drone) => p.player match { diff --git a/src/main/scala/li/cil/oc/server/component/Drive.scala b/src/main/scala/li/cil/oc/server/component/Drive.scala new file mode 100644 index 000000000..4733a1966 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/component/Drive.scala @@ -0,0 +1,268 @@ +package li.cil.oc.server.component + +import java.io +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.util.zip.GZIPInputStream +import java.util.zip.GZIPOutputStream + +import com.google.common.io.Files +import li.cil.oc.OpenComputers +import li.cil.oc.Settings +import li.cil.oc.api.Network +import li.cil.oc.api.driver.EnvironmentHost +import li.cil.oc.api.fs.Label +import li.cil.oc.api.machine.Arguments +import li.cil.oc.api.machine.Callback +import li.cil.oc.api.machine.Context +import li.cil.oc.api.network.Visibility +import li.cil.oc.api.prefab +import li.cil.oc.server.{PacketSender => ServerPacketSender} +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.DimensionManager + +class Drive(val capacity: Int, val platterCount: Int, val label: Label, host: Option[EnvironmentHost], val sound: Option[String]) extends prefab.ManagedEnvironment { + override val node = Network.newNode(this, Visibility.Network). + withComponent("drive", Visibility.Neighbors). + withConnector(). + create() + + private def savePath = new io.File(DimensionManager.getCurrentSaveRootDirectory, Settings.savePath + node.address + ".bin") + + private final val sectorSize = 512 + + private val data = new Array[Byte](capacity) + + private val sectorCount = capacity / sectorSize + + private val sectorsPerPlatter = sectorCount / platterCount + + private var headPos = 0 + + // ----------------------------------------------------------------------- // + + @Callback(direct = true, doc = """function():string -- Get the current label of the drive.""") + def getLabel(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { + if (label != null) result(label.getLabel) else null + } + + @Callback(doc = """function(value:string):string -- Sets the label of the drive. Returns the new value, which may be truncated.""") + def setLabel(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { + if (label == null) throw new Exception("drive does not support labeling") + if (args.checkAny(0) == null) label.setLabel(null) + else label.setLabel(args.checkString(0)) + result(label.getLabel) + } + + @Callback(direct = true, doc = """function():number -- Returns the total capacity of the drive, in bytes.""") + def getCapacity(context: Context, args: Arguments): Array[AnyRef] = result(capacity) + + @Callback(direct = true, doc = """function():number -- Returns the size of a single sector on the drive, in bytes.""") + def getSectorSize(context: Context, args: Arguments): Array[AnyRef] = result(sectorSize) + + @Callback(direct = true, doc = """function():number -- Returns the number of platters in the drive.""") + def getPlatterCount(context: Context, args: Arguments): Array[AnyRef] = result(platterCount) + + def readSector(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { + val sector = moveToSector(context, checkSector(args, 0)) + diskActivity() + val sectorData = new Array[Byte](sectorSize) + Array.copy(data, sectorOffset(sector), sectorData, 0, sectorSize) + result(sectorData) + } + + def writeSector(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { + val sectorData = args.checkByteArray(1) + val sector = moveToSector(context, checkSector(args, 0)) + diskActivity() + Array.copy(sectorData, 0, data, sectorOffset(sector), math.min(sectorSize, sectorData.length)) + null + } + + def readByte(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { + val offset = args.checkInteger(0) - 1 + moveToSector(context, checkSector(offset)) + diskActivity() + result(data(offset)) + } + + def writeByte(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { + val offset = args.checkInteger(0) - 1 + val value = args.checkInteger(1).toByte + moveToSector(context, checkSector(offset)) + diskActivity() + data(offset) = value + null + } + + // ----------------------------------------------------------------------- // + + override def load(nbt: NBTTagCompound) = this.synchronized { + super.load(nbt) + + if (node.address != null) try { + val path = savePath + if (path.exists()) { + val bin = new ByteArrayInputStream(Files.toByteArray(path)) + val zin = new GZIPInputStream(bin) + var offset = 0 + var read = 0 + while (read >= 0 && offset < data.length) { + read = zin.read(data, offset, data.length - offset) + offset += read + } + } + } + catch { + case t: Throwable => OpenComputers.log.warn(s"Failed loading drive contents for '${node.address}'.", t) + } + + headPos = nbt.getInteger("headPos") max 0 min sectorToHeadPos(sectorCount) + + if (label != null) { + label.load(nbt) + } + } + + override def save(nbt: NBTTagCompound) = this.synchronized { + super.save(nbt) + + if (node.address != null) try { + val path = savePath + path.getParentFile.mkdirs() + val bos = new ByteArrayOutputStream() + val zos = new GZIPOutputStream(bos) + zos.write(data) + zos.close() + Files.write(bos.toByteArray, path) + } + catch { + case t: Throwable => OpenComputers.log.warn(s"Failed saving drive contents for '${node.address}'.", t) + } + + nbt.setInteger("headPos", headPos) + + if (label != null) { + label.save(nbt) + } + } + + // ----------------------------------------------------------------------- // + + private def validateSector(sector: Int) = { + if (sector < 0 || sector >= sectorCount) + throw new IllegalArgumentException("invalid offset, not in a usable sector") + sector + } + + private def checkSector(offset: Int) = validateSector(offsetSector(offset)) + + private def checkSector(args: Arguments, n: Int) = validateSector(args.checkInteger(n) - 1) + + private def moveToSector(context: Context, sector: Int) = { + val newHeadPos = sectorToHeadPos(sector) + if (headPos != newHeadPos) { + val delta = math.abs(headPos - newHeadPos) + if (delta > Settings.get.sectorSeekThreshold) context.pause(Settings.get.sectorSeekTime) + headPos = newHeadPos + } + sector + } + + private def sectorToHeadPos(sector: Int) = sector % sectorsPerPlatter + + private def sectorOffset(sector: Int) = sector * sectorSize + + private def offsetSector(offset: Int) = offset / sectorSize + + private def diskActivity() { + (sound, host) match { + case (Some(s), Some(h)) => ServerPacketSender.sendFileSystemActivity(node, h, s) + case _ => + } + } +} + +object Drive { + // I really need to come up with a way to make the call limit dynamic... + def apply(capacity: Int, platterCount: Int, label: Label, host: Option[EnvironmentHost], sound: Option[String], speed: Int = 1): Drive = speed match { + case 6 => new Drive(capacity, platterCount, label, host, sound) { + @Callback(direct = true, limit = 60, doc = """function(sector:number):string -- Read the current contents of the specified sector.""") + override def readSector(context: Context, args: Arguments): Array[AnyRef] = super.readSector(context, args) + + @Callback(direct = true, limit = 30, doc = """function(sector:number, value:string) -- Write the specified contents to the specified sector.""") + override def writeSector(context: Context, args: Arguments): Array[AnyRef] = super.writeSector(context, args) + + @Callback(direct = true, limit = 128, doc = """function(offset:number):number -- Read a single byte at the specified offset.""") + override def readByte(context: Context, args: Arguments): Array[AnyRef] = super.readByte(context, args) + + @Callback(direct = true, limit = 64, doc = """function(offset:number, value:number) -- Write a single byte to the specified offset.""") + override def writeByte(context: Context, args: Arguments): Array[AnyRef] = super.writeByte(context, args) + } + case 5 => new Drive(capacity, platterCount, label, host, sound) { + @Callback(direct = true, limit = 50, doc = """function(sector:number):string -- Read the current contents of the specified sector.""") + override def readSector(context: Context, args: Arguments): Array[AnyRef] = super.readSector(context, args) + + @Callback(direct = true, limit = 25, doc = """function(sector:number, value:string) -- Write the specified contents to the specified sector.""") + override def writeSector(context: Context, args: Arguments): Array[AnyRef] = super.writeSector(context, args) + + @Callback(direct = true, limit = 112, doc = """function(offset:number):number -- Read a single byte at the specified offset.""") + override def readByte(context: Context, args: Arguments): Array[AnyRef] = super.readByte(context, args) + + @Callback(direct = true, limit = 56, doc = """function(offset:number, value:number) -- Write a single byte to the specified offset.""") + override def writeByte(context: Context, args: Arguments): Array[AnyRef] = super.writeByte(context, args) + } + case 4 => new Drive(capacity, platterCount, label, host, sound) { + @Callback(direct = true, limit = 40, doc = """function(sector:number):string -- Read the current contents of the specified sector.""") + override def readSector(context: Context, args: Arguments): Array[AnyRef] = super.readSector(context, args) + + @Callback(direct = true, limit = 20, doc = """function(sector:number, value:string) -- Write the specified contents to the specified sector.""") + override def writeSector(context: Context, args: Arguments): Array[AnyRef] = super.writeSector(context, args) + + @Callback(direct = true, limit = 96, doc = """function(offset:number):number -- Read a single byte at the specified offset.""") + override def readByte(context: Context, args: Arguments): Array[AnyRef] = super.readByte(context, args) + + @Callback(direct = true, limit = 48, doc = """function(offset:number, value:number) -- Write a single byte to the specified offset.""") + override def writeByte(context: Context, args: Arguments): Array[AnyRef] = super.writeByte(context, args) + } + case 3 => new Drive(capacity, platterCount, label, host, sound) { + @Callback(direct = true, limit = 30, doc = """function(sector:number):string -- Read the current contents of the specified sector.""") + override def readSector(context: Context, args: Arguments): Array[AnyRef] = super.readSector(context, args) + + @Callback(direct = true, limit = 15, doc = """function(sector:number, value:string) -- Write the specified contents to the specified sector.""") + override def writeSector(context: Context, args: Arguments): Array[AnyRef] = super.writeSector(context, args) + + @Callback(direct = true, limit = 80, doc = """function(offset:number):number -- Read a single byte at the specified offset.""") + override def readByte(context: Context, args: Arguments): Array[AnyRef] = super.readByte(context, args) + + @Callback(direct = true, limit = 40, doc = """function(offset:number, value:number) -- Write a single byte to the specified offset.""") + override def writeByte(context: Context, args: Arguments): Array[AnyRef] = super.writeByte(context, args) + } + case 2 => new Drive(capacity, platterCount, label, host, sound) { + @Callback(direct = true, limit = 20, doc = """function(sector:number):string -- Read the current contents of the specified sector.""") + override def readSector(context: Context, args: Arguments): Array[AnyRef] = super.readSector(context, args) + + @Callback(direct = true, limit = 10, doc = """function(sector:number, value:string) -- Write the specified contents to the specified sector.""") + override def writeSector(context: Context, args: Arguments): Array[AnyRef] = super.writeSector(context, args) + + @Callback(direct = true, limit = 64, doc = """function(offset:number):number -- Read a single byte at the specified offset.""") + override def readByte(context: Context, args: Arguments): Array[AnyRef] = super.readByte(context, args) + + @Callback(direct = true, limit = 32, doc = """function(offset:number, value:number) -- Write a single byte to the specified offset.""") + override def writeByte(context: Context, args: Arguments): Array[AnyRef] = super.writeByte(context, args) + } + case _ => new Drive(capacity, platterCount, label, host, sound) { + @Callback(direct = true, limit = 10, doc = """function(sector:number):string -- Read the current contents of the specified sector.""") + override def readSector(context: Context, args: Arguments): Array[AnyRef] = super.readSector(context, args) + + @Callback(direct = true, limit = 5, doc = """function(sector:number, value:string) -- Write the specified contents to the specified sector.""") + override def writeSector(context: Context, args: Arguments): Array[AnyRef] = super.writeSector(context, args) + + @Callback(direct = true, limit = 48, doc = """function(offset:number):number -- Read a single byte at the specified offset.""") + override def readByte(context: Context, args: Arguments): Array[AnyRef] = super.readByte(context, args) + + @Callback(direct = true, limit = 24, doc = """function(offset:number, value:number) -- Write a single byte to the specified offset.""") + override def writeByte(context: Context, args: Arguments): Array[AnyRef] = super.writeByte(context, args) + } + } +} diff --git a/src/main/scala/li/cil/oc/server/component/FileSystem.scala b/src/main/scala/li/cil/oc/server/component/FileSystem.scala index 10d6e3487..e584a06fa 100644 --- a/src/main/scala/li/cil/oc/server/component/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/component/FileSystem.scala @@ -33,14 +33,14 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option // ----------------------------------------------------------------------- // - @Callback(direct = true, doc = """function():string -- Get the current label of the file system.""") + @Callback(direct = true, doc = """function():string -- Get the current label of the drive.""") def getLabel(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized { if (label != null) result(label.getLabel) else null } - @Callback(doc = """function(value:string):string -- Sets the label of the file system. Returns the new value, which may be truncated.""") + @Callback(doc = """function(value:string):string -- Sets the label of the drive. Returns the new value, which may be truncated.""") def setLabel(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized { - if (label == null) throw new Exception("filesystem does not support labeling") + if (label == null) throw new Exception("drive does not support labeling") if (args.checkAny(0) == null) label.setLabel(null) else label.setLabel(args.checkString(0)) result(label.getLabel) 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 f6038e16b..1d29a032b 100644 --- a/src/main/scala/li/cil/oc/server/fs/Buffered.scala +++ b/src/main/scala/li/cil/oc/server/fs/Buffered.scala @@ -72,7 +72,7 @@ trait Buffered extends OutputStreamFileSystem { } setLastModified(path, directory.lastModified()) } - if (fileRoot.list() == null || fileRoot.list().length == 0) { + if (fileRoot.list() == null || fileRoot.list().isEmpty) { fileRoot.delete() } else recurse("", fileRoot) @@ -114,7 +114,7 @@ trait Buffered extends OutputStreamFileSystem { } directory.setLastModified(lastModified(path)) } - if (list("") == null || list("").length == 0) { + if (list("") == null || list("").isEmpty) { fileRoot.delete() } else recurse("") From 4be141a90311d40f26af3a60f860c0c25b78eca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 2 Aug 2015 21:16:30 +0200 Subject: [PATCH 5/5] Properly handle arbitrary number types in signals and return values. Closes #1345. --- src/main/scala/li/cil/oc/server/driver/Registry.scala | 1 + src/main/scala/li/cil/oc/server/machine/Machine.scala | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/scala/li/cil/oc/server/driver/Registry.scala b/src/main/scala/li/cil/oc/server/driver/Registry.scala index 7a5df84bb..07d9a4097 100644 --- a/src/main/scala/li/cil/oc/server/driver/Registry.scala +++ b/src/main/scala/li/cil/oc/server/driver/Registry.scala @@ -122,6 +122,7 @@ private[oc] object Registry extends api.detail.DriverAPI { case arg: java.lang.Long => arg case arg: java.lang.Float => arg case arg: java.lang.Double => arg + case arg: java.lang.Number => Double.box(arg.doubleValue()) case arg: java.lang.String => arg case arg: Array[Boolean] => arg diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala index 459b7eb60..30edcd439 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -303,13 +303,9 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach signals.enqueue(new Machine.Signal(name, args.map { case null | Unit | None => null case arg: java.lang.Boolean => arg - case arg: java.lang.Byte => Double.box(arg.doubleValue) case arg: java.lang.Character => Double.box(arg.toDouble) - case arg: java.lang.Short => Double.box(arg.doubleValue) - case arg: java.lang.Integer => Double.box(arg.doubleValue) - case arg: java.lang.Long => Double.box(arg.doubleValue) - case arg: java.lang.Float => Double.box(arg.doubleValue) - case arg: java.lang.Double => arg + case arg: java.lang.Long => arg + case arg: java.lang.Number => Double.box(arg.doubleValue) case arg: java.lang.String => arg case arg: Array[Byte] => arg case arg: Map[_, _] if arg.isEmpty || arg.head._1.isInstanceOf[String] && arg.head._2.isInstanceOf[String] => arg @@ -697,6 +693,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach (0 until argsLength).map("arg" + _).map(argsNbt.getTag).map { case tag: NBTTagByte if tag.func_150290_f == -1 => null case tag: NBTTagByte => Boolean.box(tag.func_150290_f == 1) + case tag: NBTTagLong => Long.box(tag.func_150291_c) case tag: NBTTagDouble => Double.box(tag.func_150286_g) case tag: NBTTagString => tag.func_150285_a_ case tag: NBTTagByteArray => tag.func_150292_c @@ -770,6 +767,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach s.args.zipWithIndex.foreach { case (null, i) => args.setByte("arg" + i, -1) case (arg: java.lang.Boolean, i) => args.setByte("arg" + i, if (arg) 1 else 0) + case (arg: java.lang.Long, i) => args.setLong("arg" + i, arg) case (arg: java.lang.Double, i) => args.setDouble("arg" + i, arg) case (arg: String, i) => args.setString("arg" + i, arg) case (arg: Array[Byte], i) => args.setByteArray("arg" + i, arg)