diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index c1796df77..0a8d6c874 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -201,6 +201,8 @@ oc:gui.Chat.WarningRecipes=There were errors loading one or more recipes. Some i 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.ReadOnlyLock=Lock +oc:gui.Drive.ReadOnlyLockWarning=§lRead Only§r lock. Cannot be removed unless the drive is wiped. 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. @@ -283,6 +285,7 @@ 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.diskdrivemountable=Provides the same functionality as a normal disk drive, but must be installed in a rack. oc:tooltip.diskusage=Disk usage: %s/%s Byte +oc:tooltip.disklocked=Locked by: %s oc:tooltip.diskmodemanaged=Mode: Managed oc:tooltip.diskmodeunmanaged=Mode: Unmanaged oc:tooltip.drone=Drones are light-weight, fast reconnaissance units with limited cargo space. diff --git a/src/main/resources/assets/opencomputers/textures/gui/drive.png b/src/main/resources/assets/opencomputers/textures/gui/drive.png index 124e3e294..1f86630b7 100644 Binary files a/src/main/resources/assets/opencomputers/textures/gui/drive.png and b/src/main/resources/assets/opencomputers/textures/gui/drive.png differ diff --git a/src/main/scala/li/cil/oc/Localization.scala b/src/main/scala/li/cil/oc/Localization.scala index 0bdd2a18d..0c3ff9947 100644 --- a/src/main/scala/li/cil/oc/Localization.scala +++ b/src/main/scala/li/cil/oc/Localization.scala @@ -117,6 +117,10 @@ object Localization { def Unmanaged: String = localizeImmediately("gui.Drive.Unmanaged") def Warning: String = localizeImmediately("gui.Drive.Warning") + + def ReadOnlyLock: String = localizeImmediately("gui.Drive.ReadOnlyLock") + + def LockWarning: String = localizeImmediately("gui.Drive.ReadOnlyLockWarning") } object Raid { @@ -162,6 +166,8 @@ object Localization { def Materials: String = localizeImmediately("tooltip.materials") + def DiskLock(lockInfo: String): String = if (lockInfo.isEmpty) "" else localizeImmediately("tooltip.disklocked", lockInfo) + def Tier(tier: Int): String = localizeImmediately("tooltip.tier", tier.toString) def PrintBeaconBase: String = localizeImmediately("tooltip.print.BeaconBase") diff --git a/src/main/scala/li/cil/oc/client/PacketSender.scala b/src/main/scala/li/cil/oc/client/PacketSender.scala index 61cfe68bb..d740a982d 100644 --- a/src/main/scala/li/cil/oc/client/PacketSender.scala +++ b/src/main/scala/li/cil/oc/client/PacketSender.scala @@ -35,6 +35,12 @@ object PacketSender { pb.sendToServer() } + def sendDriveLock(): Unit = { + val pb = new SimplePacketBuilder(PacketType.DriveLock) + + pb.sendToServer() + } + def sendDronePower(e: Drone, power: Boolean) { val pb = new SimplePacketBuilder(PacketType.DronePower) diff --git a/src/main/scala/li/cil/oc/client/gui/Drive.scala b/src/main/scala/li/cil/oc/client/gui/Drive.scala index 539d2ee0c..800d898f3 100644 --- a/src/main/scala/li/cil/oc/client/gui/Drive.scala +++ b/src/main/scala/li/cil/oc/client/gui/Drive.scala @@ -10,19 +10,21 @@ 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 val windowHeight = 120 override def backgroundImage = Textures.GUI.Drive protected var managedButton: ImageButton = _ protected var unmanagedButton: ImageButton = _ + protected var lockedButton: ImageButton = _ protected override def actionPerformed(button: GuiButton) { if (button.id == 0) { ClientPacketSender.sendDriveMode(false) - } - if (button.id == 1) { + } else if (button.id == 1) { ClientPacketSender.sendDriveMode(true) + } else if (button.id == 2) { + ClientPacketSender.sendDriveLock() } } @@ -30,18 +32,24 @@ class Drive(playerInventory: InventoryPlayer, val driveStack: () => ItemStack) e super.initGui() managedButton = new ImageButton(0, guiLeft + 11, guiTop + 11, 74, 18, Textures.GUI.ButtonDriveMode, text = Localization.Drive.Managed, textColor = 0x608060, canToggle = true) unmanagedButton = new ImageButton(1, guiLeft + 91, guiTop + 11, 74, 18, Textures.GUI.ButtonDriveMode, text = Localization.Drive.Unmanaged, textColor = 0x608060, canToggle = true) + lockedButton = new ImageButton(2, guiLeft + 11, guiTop + windowHeight - 42, 44, 18, Textures.GUI.ButtonDriveMode, text = Localization.Drive.ReadOnlyLock, textColor = 0x608060, canToggle = true) add(buttonList, managedButton) add(buttonList, unmanagedButton) + add(buttonList, lockedButton) } override def updateScreen(): Unit = { - unmanagedButton.toggled = new DriveData(driveStack()).isUnmanaged + val data = new DriveData(driveStack()) + unmanagedButton.toggled = data.isUnmanaged managedButton.toggled = !unmanagedButton.toggled + lockedButton.toggled = data.isLocked + lockedButton.enabled = !data.isLocked super.updateScreen() } override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { super.drawScreen(mouseX, mouseY, dt) - fontRenderer.drawSplitString(Localization.Drive.Warning, guiLeft + 7, guiTop + 37, xSize - 16, 0x404040) + fontRenderer.drawSplitString(Localization.Drive.Warning, guiLeft + 11, guiTop + 37, xSize - 20, 0x404040) + fontRenderer.drawSplitString(Localization.Drive.LockWarning, guiLeft + 61, guiTop + windowHeight - 48, xSize - 68, 0x404040) } } diff --git a/src/main/scala/li/cil/oc/common/PacketType.scala b/src/main/scala/li/cil/oc/common/PacketType.scala index 9ea852788..662aaeb29 100644 --- a/src/main/scala/li/cil/oc/common/PacketType.scala +++ b/src/main/scala/li/cil/oc/common/PacketType.scala @@ -73,6 +73,7 @@ object PacketType extends Enumeration { // Client -> Server ComputerPower, CopyToAnalyzer, + DriveLock, DriveMode, DronePower, KeyDown, 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 index c4cbc59ed..68e10b5e2 100644 --- a/src/main/scala/li/cil/oc/common/item/data/DriveData.scala +++ b/src/main/scala/li/cil/oc/common/item/data/DriveData.scala @@ -11,14 +11,24 @@ class DriveData extends ItemData(null) { } var isUnmanaged = false + var lockInfo: String = "" + + def isLocked: Boolean = { + lockInfo != null && !lockInfo.isEmpty + } private final val UnmanagedTag = Settings.namespace + "unmanaged" + private val LockTag = Settings.namespace + "lock" override def load(nbt: NBTTagCompound) { isUnmanaged = nbt.getBoolean(UnmanagedTag) + lockInfo = if (nbt.hasKey(LockTag)) { + nbt.getString(LockTag) + } else "" } override def save(nbt: NBTTagCompound) { nbt.setBoolean(UnmanagedTag, isUnmanaged) + nbt.setString(LockTag, lockInfo) } } 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 index baf54b544..8af4dacb1 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/FileSystemLike.scala @@ -6,6 +6,7 @@ import li.cil.oc.Localization import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.common.GuiType +import li.cil.oc.common.item.data.DriveData import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult @@ -34,7 +35,9 @@ trait FileSystemLike extends Delegate { } } } - tooltip.add(Localization.Tooltip.DiskMode(nbt.getBoolean(Settings.namespace + "unmanaged"))) + val data = new DriveData(stack) + tooltip.add(Localization.Tooltip.DiskMode(data.isUnmanaged)) + tooltip.add(Localization.Tooltip.DiskLock(data.lockInfo)) } super.tooltipLines(stack, player, tooltip, advanced) } 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 ad4b03409..9c431ecc6 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala @@ -72,10 +72,13 @@ object DriverFileSystem extends Item { val sound = Settings.resourceDomain + ":" + (if (isFloppy) "floppy_access" else "hdd_access") val drive = new DriveData(stack) val environment = if (drive.isUnmanaged) { - new Drive(capacity max 0, platterCount, label, Option(host), Option(sound), speed) + new Drive(capacity max 0, platterCount, label, Option(host), Option(sound), speed, drive.isLocked) } else { - val fs = oc.api.FileSystem.fromSaveDirectory(address, capacity max 0, Settings.get.bufferChanges) + var fs = oc.api.FileSystem.fromSaveDirectory(address, capacity max 0, Settings.get.bufferChanges) + if (drive.isLocked) { + fs = oc.api.FileSystem.asReadOnly(fs) + } oc.api.FileSystem.asManagedEnvironment(fs, label, host, sound, speed) } if (environment != null && environment.node != null) { diff --git a/src/main/scala/li/cil/oc/server/PacketHandler.scala b/src/main/scala/li/cil/oc/server/PacketHandler.scala index d1574025c..57e5d51b2 100644 --- a/src/main/scala/li/cil/oc/server/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/server/PacketHandler.scala @@ -35,6 +35,7 @@ object PacketHandler extends CommonPacketHandler { p.packetType match { case PacketType.ComputerPower => onComputerPower(p) case PacketType.CopyToAnalyzer => onCopyToAnalyzer(p) + case PacketType.DriveLock => onDriveLock(p) case PacketType.DriveMode => onDriveMode(p) case PacketType.DronePower => onDronePower(p) case PacketType.KeyDown => onKeyDown(p) @@ -85,13 +86,37 @@ object PacketHandler extends CommonPacketHandler { } } + def onDriveLock(p: PacketParser): Unit = p.player match { + case player: EntityPlayerMP => + val heldItem = player.getHeldItem(EnumHand.MAIN_HAND) + Delegator.subItem(heldItem) match { + case Some(drive: FileSystemLike) => + val data = new DriveData(heldItem) + if (!data.isLocked) { + data.lockInfo = player.getName match { + case name: String if name != null && !name.isEmpty => name + case _ => "notch" // meaning: "unknown" + } + data.save(heldItem) + } + case _ => // Invalid packet + } + case _ => // Invalid Packet + } + def onDriveMode(p: PacketParser): Unit = p.player match { case player: EntityPlayerMP => - Delegator.subItem(player.getHeldItem(EnumHand.MAIN_HAND)) match { + val heldItem = player.getHeldItem(EnumHand.MAIN_HAND) + Delegator.subItem(heldItem) match { case Some(drive: FileSystemLike) => - val data = new DriveData(player.getHeldItem(EnumHand.MAIN_HAND)) - data.isUnmanaged = p.readBoolean() - data.save(player.getHeldItem(EnumHand.MAIN_HAND)) + val data = new DriveData(heldItem) + val newIsUnmanaged = p.readBoolean() + if (data.isUnmanaged != newIsUnmanaged) { + fs.FileSystem.removeAddress(heldItem) + data.lockInfo = "" + } + data.isUnmanaged = newIsUnmanaged + data.save(heldItem) case _ => // Invalid packet. } case _ => // Invalid packet. diff --git a/src/main/scala/li/cil/oc/server/component/Drive.scala b/src/main/scala/li/cil/oc/server/component/Drive.scala index d0e3ab33d..b62bb350b 100644 --- a/src/main/scala/li/cil/oc/server/component/Drive.scala +++ b/src/main/scala/li/cil/oc/server/component/Drive.scala @@ -29,7 +29,7 @@ import net.minecraftforge.common.DimensionManager import scala.collection.convert.WrapAsJava._ -class Drive(val capacity: Int, val platterCount: Int, val label: Label, host: Option[EnvironmentHost], val sound: Option[String], val speed: Int) extends AbstractManagedEnvironment with DeviceInfo { +class Drive(val capacity: Int, val platterCount: Int, val label: Label, host: Option[EnvironmentHost], val sound: Option[String], val speed: Int, val isLocked: Boolean) extends AbstractManagedEnvironment with DeviceInfo { override val node = Network.newNode(this, Visibility.Network). withComponent("drive", Visibility.Neighbors). withConnector(). @@ -75,6 +75,7 @@ class Drive(val capacity: Int, val platterCount: Int, val label: Label, host: Op @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 (isLocked) throw new Exception("drive is read only") 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)) @@ -102,6 +103,7 @@ class Drive(val capacity: Int, val platterCount: Int, val label: Label, host: Op @Callback(direct = true, doc = """function(sector:number, value:string) -- Write the specified contents to the specified sector.""") def writeSector(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { + if (isLocked) throw new Exception("drive is read only") context.consumeCallBudget(writeSectorCosts(speed)) val sectorData = args.checkByteArray(1) val sector = moveToSector(context, checkSector(args, 0)) @@ -121,6 +123,7 @@ class Drive(val capacity: Int, val platterCount: Int, val label: Label, host: Op @Callback(direct = true, doc = """function(offset:number, value:number) -- Write a single byte to the specified offset.""") def writeByte(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { + if (isLocked) throw new Exception("drive is read only") context.consumeCallBudget(writeByteCosts(speed)) val offset = args.checkInteger(0) - 1 val value = args.checkInteger(1).toByte diff --git a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala index ddd1ea2fc..8e50dd179 100755 --- a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala @@ -11,6 +11,8 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.fs.Label import li.cil.oc.api.network.EnvironmentHost +import li.cil.oc.common.item.Delegator +import li.cil.oc.common.item.traits.FileSystemLike import li.cil.oc.server.component import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound @@ -110,6 +112,23 @@ object FileSystem extends api.detail.FileSystemAPI { else null } + def removeAddress(fsStack: ItemStack): Boolean = { + Delegator.subItem(fsStack) match { + case Some(drive: FileSystemLike) => { + val data = li.cil.oc.integration.opencomputers.Item.dataTag(fsStack) + if (data.hasKey("node")) { + val nodeData = data.getCompoundTag("node") + if (nodeData.hasKey("address")) { + nodeData.removeTag("address") + return true + } + } + } + case _ => + } + false + } + def fromMemory(capacity: Long): api.fs.FileSystem = new RamFileSystem(capacity) override def asReadOnly(fileSystem: api.fs.FileSystem) =