diff --git a/assets/opencomputers/lang/de_DE.lang b/assets/opencomputers/lang/de_DE.lang index 42bfe2c60..895a8edaf 100644 --- a/assets/opencomputers/lang/de_DE.lang +++ b/assets/opencomputers/lang/de_DE.lang @@ -54,6 +54,7 @@ oc:item.PrintedCircuitBoard.name=Gedruckte Leiterplatte (PCB) oc:item.RawCircuitBoard.name=Leiterplattenrohling oc:item.RedstoneCard.name=Redstonekarte oc:item.Server.name=Server +oc:item.Terminal.name=Fernbedienung oc:item.Transistor.name=Transistor oc:item.UpgradeCrafting.name=Fertigungs-Upgrade oc:item.UpgradeGenerator.name=Generator-Upgrade @@ -81,6 +82,8 @@ oc:gui.ServerRack.Bottom=Unten oc:gui.ServerRack.Left=Links oc:gui.ServerRack.Right=Rechts oc:gui.ServerRack.Top=Oben +oc:gui.Terminal.InvalidKey=Ungültiger Schlüssel, vermutlich wurde eine andere Fernbedienung an den Server gebunden. +oc:gui.Terminal.OutOfRange=Kein Signal. # Containers oc:container.Adapter=Adapter @@ -134,6 +137,7 @@ oc:tooltip.Screen=Zeigt Text an, gesteuert von Grafikkarten in Computern.[nl] H oc:tooltip.Server=Ein Server kann wie gewöhnlich Computergehäuse mit Komponenten verbessert werden. Um den Server zu starten muss er in einem Servergehäuse installiert werden. oc:tooltip.Server.Components=Installierte Komponenten: oc:tooltip.ServerRack=Erlaubt die Installation von bis zu vier Servern. +oc:tooltip.Terminal=Erlaubt es einen Server aus der Ferne zu steuern, so lange man sich in Reichweite des Servers befindet. Verhält sich wie Bildschirm und Tastatur in einem. oc:tooltip.TooLong=Shift gedrückt halten für mehr Infos. oc:tooltip.Transistor=Elementarer Baustein der meisten Computerkomponenten. Nicht zu verwechseln mit Steinelementaren. oc:tooltip.UpgradeCrafting=Ermöglicht Robotern, in dem oberen linken Bereich ihres Inventars Dinge zu fertigen. Gegenstände müssen so angeordnet sein, wie sie es in einer Werkbank wären. diff --git a/assets/opencomputers/lang/en_US.lang b/assets/opencomputers/lang/en_US.lang index 5c99c5611..01ddda684 100644 --- a/assets/opencomputers/lang/en_US.lang +++ b/assets/opencomputers/lang/en_US.lang @@ -54,6 +54,7 @@ oc:item.PrintedCircuitBoard.name=Printed Circuit Board (PCB) oc:item.RawCircuitBoard.name=Raw Circuit Board oc:item.RedstoneCard.name=Redstone Card oc:item.Server.name=Server +oc:item.Terminal.name=Remote Terminal oc:item.Transistor.name=Transistor oc:item.UpgradeCrafting.name=Crafting Upgrade oc:item.UpgradeGenerator.name=Generator Upgrade @@ -81,6 +82,8 @@ oc:gui.ServerRack.Bottom=Bottom oc:gui.ServerRack.Left=Left oc:gui.ServerRack.Right=Right oc:gui.ServerRack.Top=Top +oc:gui.Terminal.InvalidKey=Invalid key, most likely another terminal has been bound to the server. +oc:gui.Terminal.OutOfRange=No signal. # Containers oc:container.Adapter=Adapter @@ -134,6 +137,7 @@ oc:tooltip.Screen=Display text, controlled by a Graphics Card in a Case.[nl] Max oc:tooltip.Server=This is a server, there are many like it, but this one can be upgraded with components much like a computer case can be. It can be run by inserting it into a server rack. oc:tooltip.Server.Components=Installed components: oc:tooltip.ServerRack=Allows the installation of up to four servers. +oc:tooltip.Terminal=Allows controlling a server remotely, as long as you are in range of it. Acts like a portable screen and keyboard. oc:tooltip.TooLong=Hold shift for a detailed tooltip. oc:tooltip.Transistor=A basic element in most other computer parts. It's a bit twisted, but it does the job. oc:tooltip.UpgradeCrafting=Enables robots to use the top left area of their inventory for crafting objects. Items have to be aligned as they would be in a crafting table. diff --git a/li/cil/oc/Blocks.scala b/li/cil/oc/Blocks.scala index 699abc7c0..e4254f1a7 100644 --- a/li/cil/oc/Blocks.scala +++ b/li/cil/oc/Blocks.scala @@ -13,7 +13,6 @@ object Blocks { var blockSpecial: SpecialDelegator = _ var blockSpecialWithRedstone: SpecialDelegator = _ - var accessPoint: AccessPoint = _ var adapter: Adapter = _ var cable: Cable = _ var capacitor: Capacitor = _ @@ -41,7 +40,6 @@ object Blocks { GameRegistry.registerBlock(blockSpecial, classOf[Item], Settings.namespace + "special") GameRegistry.registerBlock(blockSpecialWithRedstone, classOf[Item], Settings.namespace + "special_redstone") - GameRegistry.registerTileEntity(classOf[tileentity.AccessPoint], Settings.namespace + "accessPoint") GameRegistry.registerTileEntity(classOf[tileentity.Adapter], Settings.namespace + "adapter") GameRegistry.registerTileEntity(classOf[tileentity.Cable], Settings.namespace + "cable") GameRegistry.registerTileEntity(classOf[tileentity.Capacitor], Settings.namespace + "capacitor") @@ -88,7 +86,6 @@ object Blocks { // v1.2.0 serverRack = new Rack(blockSpecialWithRedstone) - accessPoint = new AccessPoint(blockSimple) register("oc:craftingCable", cable.createItemStack()) register("oc:craftingCapacitor", capacitor.createItemStack()) diff --git a/li/cil/oc/Recipes.scala b/li/cil/oc/Recipes.scala index f7f778c4c..b9c422582 100644 --- a/li/cil/oc/Recipes.scala +++ b/li/cil/oc/Recipes.scala @@ -110,8 +110,7 @@ object Recipes { addRecipe(Items.card.createItemStack(), recipes, "card") // Try to keep this in the same order as the fields in the Blocks class - // to make it easier to match them and check if anything is missing. - addRecipe(Blocks.accessPoint.createItemStack(), recipes, "accessPoint") + // to make it easier to match them and check if anything is missing.Point") addRecipe(Blocks.adapter.createItemStack(), recipes, "adapter") addRecipe(Blocks.cable.createItemStack(), recipes, "cable") addRecipe(Blocks.capacitor.createItemStack(), recipes, "capacitor") @@ -326,12 +325,6 @@ object Recipes { Item.itemsList(index) } - private def cartesianProduct[T](xss: List[List[T]]): List[List[T]] = xss match { - case Nil => List(Nil) - case h :: t => for (xh <- h; - xt <- cartesianProduct(t)) yield xh :: xt - } - private class RecipeException(message: String) extends RuntimeException(message) } diff --git a/li/cil/oc/client/GuiHandler.scala b/li/cil/oc/client/GuiHandler.scala index 320b1091f..0e2300e0c 100644 --- a/li/cil/oc/client/GuiHandler.scala +++ b/li/cil/oc/client/GuiHandler.scala @@ -6,9 +6,11 @@ import li.cil.oc.{Settings, Items} import net.minecraft.entity.player.EntityPlayer import net.minecraft.world.World import scala.collection.convert.WrapAsScala._ +import net.minecraft.util.StatCollector +import net.minecraft.client.Minecraft object GuiHandler extends CommonGuiHandler { - override def getClientGuiElement(id: Int, player: EntityPlayer, world: World, x: Int, y: Int, z: Int) = + override def getClientGuiElement(id: Int, player: EntityPlayer, world: World, x: Int, y: Int, z: Int): AnyRef = world.getBlockTileEntity(x, y, z) match { case adapter: tileentity.Adapter if id == GuiType.Adapter.id => new gui.Adapter(player.inventory, adapter) @@ -33,24 +35,39 @@ object GuiHandler extends CommonGuiHandler { val stack = player.getCurrentEquippedItem if (stack.hasTagCompound) { val address = stack.getTagCompound.getString(Settings.namespace + "server") - if (address != null && !address.isEmpty) { + val key = stack.getTagCompound.getString(Settings.namespace + "key") + if (key != null && !key.isEmpty && address != null && !address.isEmpty) { // Check if bound to server is loaded. TODO optimize this? world.loadedTileEntityList.flatMap { case rack: tileentity.Rack => rack.terminals case _ => Iterable.empty - } find (_.rack.isPresent.exists { + } find (term => term.rack.isPresent(term.number) match { case Some(value) => value == address case _ => false }) match { case Some(term) => // TODO check reachability - new gui.Screen(term.buffer, true, () => true) + def inRange = player.isEntityAlive && term.rack.getDistanceFrom(player.posX, player.posY, player.posZ) < term.rack.range + if (inRange) { + if (term.key.isDefined && term.key.get == key) return new gui.Screen(term.buffer, true, () => { + // Check if someone else bound a term to our server. + if (stack.getTagCompound.getString(Settings.namespace + "key") != key) { + Minecraft.getMinecraft.displayGuiScreen(null) + } + // Check whether we're still in range. + if (!inRange) { + Minecraft.getMinecraft.displayGuiScreen(null) + } + true + }) + else player.addChatMessage(StatCollector.translateToLocal(Settings.namespace + "gui.Terminal.InvalidKey")) + } + else player.addChatMessage(StatCollector.translateToLocal(Settings.namespace + "gui.Terminal.OutOfRange")) case _ => null } } - else null } - else null + null case _ => null } } diff --git a/li/cil/oc/client/PacketHandler.scala b/li/cil/oc/client/PacketHandler.scala index 948858fbc..babc47389 100644 --- a/li/cil/oc/client/PacketHandler.scala +++ b/li/cil/oc/client/PacketHandler.scala @@ -86,6 +86,10 @@ class PacketHandler extends CommonPacketHandler { val number = p.readInt() t.setRunning(number, p.readBoolean()) t.sides(number) = p.readDirection() + val key = p.readUTF() + if (key != "") { + t.terminals(number).key = Option(key) + } case _ => // Invalid packet. } diff --git a/li/cil/oc/common/block/AccessPoint.scala b/li/cil/oc/common/block/AccessPoint.scala deleted file mode 100644 index 2f701b0bc..000000000 --- a/li/cil/oc/common/block/AccessPoint.scala +++ /dev/null @@ -1,5 +0,0 @@ -package li.cil.oc.common.block - -class AccessPoint(val parent: SimpleDelegator) extends SimpleDelegate { - val unlocalizedName = "AccessPoint" -} diff --git a/li/cil/oc/common/component/Terminal.scala b/li/cil/oc/common/component/Terminal.scala index bcd5d699e..da9af8d3a 100644 --- a/li/cil/oc/common/component/Terminal.scala +++ b/li/cil/oc/common/component/Terminal.scala @@ -1,12 +1,14 @@ package li.cil.oc.common.component +import cpw.mods.fml.relauncher.{Side, SideOnly} import li.cil.oc.api.network.{Node, Visibility} import li.cil.oc.common.tileentity +import li.cil.oc.common.item import li.cil.oc.server.component import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.PackedColor.Depth -import li.cil.oc.{Settings, common} +import li.cil.oc.{Items, Settings, common} import net.minecraft.entity.player.EntityPlayer import net.minecraft.nbt.NBTTagCompound @@ -15,11 +17,20 @@ class Terminal(val rack: tileentity.Rack, val number: Int) extends Buffer.Owner val keyboard = if (buffer.node != null) { buffer.node.setVisibility(Visibility.Neighbors) new component.Keyboard { - override def isUseableByPlayer(p: EntityPlayer) = true // TODO if player has bound terminal + override def isUseableByPlayer(p: EntityPlayer) = { + val stack = p.getCurrentEquippedItem + Items.multi.subItem(stack) match { + case Some(t: item.Terminal) if key.isDefined && stack.hasTagCompound => + key.get == stack.getTagCompound.getString(Settings.namespace + "key") + case _ => false + } + } } } else null + var key: Option[String] = None + def isServer = rack.isServer def connect(node: Node) { @@ -35,11 +46,34 @@ class Terminal(val rack: tileentity.Rack, val number: Int) extends Buffer.Owner def load(nbt: NBTTagCompound) { buffer.load(nbt.getCompoundTag(Settings.namespace + "buffer")) keyboard.load(nbt.getCompoundTag(Settings.namespace + "keyboard")) + if (nbt.hasKey(Settings.namespace + "key")) { + key = Option(nbt.getString(Settings.namespace + "key")) + } } def save(nbt: NBTTagCompound) { nbt.setNewCompoundTag(Settings.namespace + "buffer", buffer.save) nbt.setNewCompoundTag(Settings.namespace + "keyboard", keyboard.save) + key match { + case Some(value) => nbt.setString(Settings.namespace + "key", value) + case _ => + } + } + + @SideOnly(Side.CLIENT) + def readFromNBTForClient(nbt: NBTTagCompound) { + buffer.buffer.load(nbt) + if (nbt.hasKey("key")) { + key = Option(nbt.getString("key")) + } + } + + def writeToNBTForClient(nbt: NBTTagCompound) { + buffer.buffer.save(nbt) + key match { + case Some(value) => nbt.setString("key", value) + case _ => + } } // ----------------------------------------------------------------------- // diff --git a/li/cil/oc/common/item/Terminal.scala b/li/cil/oc/common/item/Terminal.scala index 841b382b3..70d71b52a 100644 --- a/li/cil/oc/common/item/Terminal.scala +++ b/li/cil/oc/common/item/Terminal.scala @@ -2,8 +2,10 @@ package li.cil.oc.common.item import cpw.mods.fml.relauncher.{SideOnly, Side} import java.util +import java.util.UUID import li.cil.oc.common.{GuiType, tileentity} import li.cil.oc.util.Tooltip +import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.{OpenComputers, Settings} import net.minecraft.client.renderer.texture.IconRegister import net.minecraft.entity.player.EntityPlayer @@ -35,14 +37,18 @@ class Terminal(val parent: Delegator) extends Delegate { case rack: tileentity.Rack if side == rack.facing.ordinal() => val l = 2 / 16.0 val h = 14 / 16.0 - val s = (((1 - hitY) - l) / (h - l) * 4).toInt - if (s >= 0 && s <= 3 && rack.items(s).isDefined) { + val slot = (((1 - hitY) - l) / (h - l) * 4).toInt + if (slot >= 0 && slot <= 3 && rack.items(slot).isDefined) { if (!world.isRemote) { - rack.servers(s) match { + rack.servers(slot) match { case Some(server) => if (!stack.hasTagCompound) { stack.setTagCompound(new NBTTagCompound("tag")) } + val key = UUID.randomUUID().toString + rack.terminals(slot).key = Some(key) + ServerPacketSender.sendServerState(rack, slot) + stack.getTagCompound.setString(Settings.namespace + "key", key) stack.getTagCompound.setString(Settings.namespace + "server", server.machine.address) player.inventory.onInventoryChanged() case _ => // Huh? @@ -57,8 +63,9 @@ class Terminal(val parent: Delegator) extends Delegate { override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer) = { if (!player.isSneaking && stack.hasTagCompound) { - val address = stack.getTagCompound.getString(Settings.namespace + "server") - if (address != null && !address.isEmpty) { + val key = stack.getTagCompound.getString(Settings.namespace + "key") + val server = stack.getTagCompound.getString(Settings.namespace + "server") + if (key != null && !key.isEmpty && server != null && !server.isEmpty) { if (world.isRemote) { player.openGui(OpenComputers, GuiType.Terminal.id, world, 0, 0, 0) } diff --git a/li/cil/oc/common/tileentity/AccessPoint.scala b/li/cil/oc/common/tileentity/AccessPoint.scala deleted file mode 100644 index c0d773eb7..000000000 --- a/li/cil/oc/common/tileentity/AccessPoint.scala +++ /dev/null @@ -1,8 +0,0 @@ -package li.cil.oc.common.tileentity - -import li.cil.oc.api -import li.cil.oc.api.network.Visibility - -class AccessPoint extends Environment { - val node = api.Network.newNode(this, Visibility.Network).create() -} diff --git a/li/cil/oc/common/tileentity/Rack.scala b/li/cil/oc/common/tileentity/Rack.scala index a97bc19ba..cb082d05f 100644 --- a/li/cil/oc/common/tileentity/Rack.scala +++ b/li/cil/oc/common/tileentity/Rack.scala @@ -22,6 +22,8 @@ class Rack extends Hub with PowerBalancer with Inventory with Rotatable with Bun val terminals = (0 until servers.length).map(new common.component.Terminal(this, _)).toArray + var range = 16 * 16 + // For client side, where we don't create the component. private val _isRunning = new Array[Boolean](getSizeInventory) @@ -102,15 +104,16 @@ class Rack extends Hub with PowerBalancer with Inventory with Rotatable with Bun // ----------------------------------------------------------------------- // def onAnalyze(stats: NBTTagCompound, player: EntityPlayer, side: Int, hitX: Float, hitY: Float, hitZ: Float) = { - servers.collect { - case Some(server) => server.machine.lastError match { - case Some(value) => - // TODO check if already in, expand value string with additional messages - stats.setString(Settings.namespace + "gui.Analyzer.LastError", value) - case _ => + if (side == facing.ordinal) { + val l = 2 / 16.0 + val h = 14 / 16.0 + val slot = ((hitY - l) / (h - l) * 4).toInt + if (slot >= 0 && slot <= 3 && servers(slot).isDefined) { + servers(slot).get.machine.node } + else null } - null + else sidedNode(ForgeDirection.getOrientation(side)) } // ----------------------------------------------------------------------- // @@ -168,6 +171,7 @@ class Rack extends Hub with PowerBalancer with Inventory with Rotatable with Bun for (i <- 0 until math.min(terminals.length, terminalsNbt.length)) { terminals(i).load(terminalsNbt(i)) } + range = nbt.getInteger(Settings.namespace + "range") } override def writeToNBT(nbt: NBTTagCompound) { @@ -185,6 +189,7 @@ class Rack extends Hub with PowerBalancer with Inventory with Rotatable with Bun t.save(terminalNbt) terminalNbt })) + nbt.setInteger(Settings.namespace + "range", range) } @SideOnly(Side.CLIENT) @@ -198,8 +203,9 @@ class Rack extends Hub with PowerBalancer with Inventory with Rotatable with Bun Array.copy(sidesNbt, 0, sides, 0, math.min(sidesNbt.length, sides.length)) val terminalsNbt = nbt.getTagList("terminals").iterator[NBTTagCompound].toArray for (i <- 0 until math.min(terminals.length, terminalsNbt.length)) { - terminals(i).buffer.buffer.load(terminalsNbt(i)) + terminals(i).readFromNBTForClient(terminalsNbt(i)) } + range = nbt.getInteger("range") } override def writeToNBTForClient(nbt: NBTTagCompound) { @@ -209,9 +215,10 @@ class Rack extends Hub with PowerBalancer with Inventory with Rotatable with Bun nbt.setByteArray("sides", sides.map(_.ordinal.toByte)) nbt.setNewTagList("terminals", terminals.map(t => { val terminalNbt = new NBTTagCompound() - t.buffer.buffer.save(terminalNbt) + t.writeToNBTForClient(terminalNbt) terminalNbt })) + nbt.setInteger("range", range) } // ----------------------------------------------------------------------- // diff --git a/li/cil/oc/server/PacketSender.scala b/li/cil/oc/server/PacketSender.scala index f1f6ba318..59a638dd5 100644 --- a/li/cil/oc/server/PacketSender.scala +++ b/li/cil/oc/server/PacketSender.scala @@ -309,6 +309,7 @@ object PacketSender { pb.writeInt(number) pb.writeBoolean(t.isRunning(number)) pb.writeDirection(t.sides(number)) + pb.writeUTF(t.terminals(number).key.getOrElse("")) pb.sendToNearbyPlayers(t) }