diff --git a/build.gradle b/build.gradle index f7ea8a62d..6c40c253d 100644 --- a/build.gradle +++ b/build.gradle @@ -45,12 +45,13 @@ def getGitRef() { if (System.getenv("BUILD_NUMBER") != null) version += ".${System.getenv("BUILD_NUMBER")}" -else - version += "-" + getGitRef() if (config.oc.subversion != null && config.oc.subversion != "") version += "-${config.oc.subversion}" +if (System.getenv("BUILD_NUMBER") == null) + version += "+" + getGitRef() + ext.simpleVersion = version version = "MC${config.minecraft.version}-${project.version}" diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/accessPoint.md b/src/main/resources/assets/opencomputers/doc/en_US/block/accessPoint.md index 5dde0da28..78bacc26c 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/block/accessPoint.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/accessPoint.md @@ -2,6 +2,8 @@ ![AAA](oredict:oc:accessPoint) +*This block is deprecated and will be removed in a future version.* Craft it into a [relay](relay.md) to avoid losing it. + The access point is the wireless version of the [switch](switch.md). It can be used to separate subnetworks so that machines in them will not see [components](../general/computer.md) in other networks, while still allowing to send network messages to the machines in other networks. In addition to that, this block can act as a repeater: it can re-send wired messages as wired messages to other devices; or wireless messages as wired or wireless messages. diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/index.md b/src/main/resources/assets/opencomputers/doc/en_US/block/index.md index a33f40557..26a47e734 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/block/index.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/index.md @@ -36,10 +36,9 @@ Keep in mind that some of these may not be available, depending on the recipe se * [Disassembler](disassembler.md) ## Networking -* [Access Point](accessPoint.md) * [Cable](cable.md) * [Net Splitter](netSplitter.md) -* [Switch](switch.md) +* [Relay](relay.md) ## Power management * [Capacitor](capacitor.md) diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/netSplitter.md b/src/main/resources/assets/opencomputers/doc/en_US/block/netSplitter.md index 230e4484f..f6c02a752 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/block/netSplitter.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/netSplitter.md @@ -2,6 +2,6 @@ ![*.net *.split](oredict:oc:netSplitter) -The net splitter is a device that allows controlling connectivity between subnetworks. Unlike the [switch](switch.md) or [power converter](powerConverter.md) it directly connects adjacent subnetworks, i.e. components can be accessed. Each side's connectivity can be toggled using a wrench (e.g. the [scrench](../item/wrench.md)). When a redstone signal is applied to the net splitter, all sides' connectivity is inverted. +The net splitter is a device that allows controlling connectivity between subnetworks. Unlike the [relay](relay.md) or [power converter](powerConverter.md) it directly connects adjacent subnetworks, i.e. components can be accessed. Each side's connectivity can be toggled using a wrench (e.g. the [scrench](../item/wrench.md)). When a redstone signal is applied to the net splitter, all sides' connectivity is inverted. This block can therefore be used to toggle connectivity to certain parts of a component network. Use a [redstone I/O block](redstone.md) or [redstone cards](../item/redstoneCard1.md) to automate the net splitter. diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/relay.md b/src/main/resources/assets/opencomputers/doc/en_US/block/relay.md new file mode 100644 index 000000000..212369838 --- /dev/null +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/relay.md @@ -0,0 +1,13 @@ +# Relay + +![Building bridges.](oredict:oc:relay) + +The relay can be used to allow different subnetworks to send network messages to each other, without exposing components to [computers](../general/computer.md) in other networks. Keeping components local is usually a good idea, to avoid [computers](../general/computer.md) using the wrong [screen](screen1.md) or to avoid component overflows to happen (causing [computers](../general/computer.md) to crash and refuse to boot up). + +The relay can be upgraded by inserting a [wireless network card](../item/wlanCard.md) to also relay messages wirelessly. Wireless messages can be received and relayed by other relays with a wireless network card, or by [computers](../general/computer.md) with a wireless network card. + +Alternatively the relay can be upgraded using [linked cards](../item/linkedCard.md). In this case it will forward messages through the tunnel provided by the linked card, too; at the usual cost, so make sure the relay is sufficiently powered. + +Relays do *not* keep track of which packets they forwarded recently, so avoid cycles in your network or you may receive the same packet multiple times. Due to the limited buffer size of relays, sending messages too frequently will result in packet loss. You can upgrade your relays to increase the speed with which they relay messages, as well as their internal message queue size. + +Packets are only re-sent a certain number of times, so chaining an arbitrary number of relays is not possible. By default, a packet will be re-sent up to five times. diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/serverRack.md b/src/main/resources/assets/opencomputers/doc/en_US/block/serverRack.md index 49f1df835..5630023b4 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/block/serverRack.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/serverRack.md @@ -6,4 +6,4 @@ A server rack houses up to four [servers](../item/server1.md). A [server](../ite Each [server](../item/server1.md) in a server rack can only communicate with one "face" of the server rack at a time - or none at all. Which side each [server](../item/server1.md) is connected to can be configured in the server rack's GUI. Beware that the sides are from the point of view of the server rack, i.e. if you are looking at the front of the server rack, `sides.right` will be to your left and vice versa. -Server racks act as [switch](switch.md) and [power distributor](powerDistributor.md) in one. The switch mode of the server rack can be configured in its GUI, with the two options being internal and external. In external mode the server rack will behave like a normal [switch](switch.md). In internal mode, messages are only passed to the [servers](../item/server1.md) in the rack, and will not be automatically relayed to the other faces of the rack. [Servers](../item/server1.md) will still be able to send messages to each other. This allows using server racks as advanced [switches](switch.md) that can perform filter and mapping operations, for example. +Server racks act as [relay](relay.md) and [power distributor](powerDistributor.md) in one. The switch mode of the server rack can be configured in its GUI, with the two options being internal and external. In external mode the server rack will behave like a normal [relay](relay.md). In internal mode, messages are only passed to the [servers](../item/server1.md) in the rack, and will not be automatically relayed to the other faces of the rack. [Servers](../item/server1.md) will still be able to send messages to each other. This allows using server racks as advanced [relays](relay.md) that can perform filter and mapping operations, for example. diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/switch.md b/src/main/resources/assets/opencomputers/doc/en_US/block/switch.md index a607e0cfd..35ad59fd4 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/block/switch.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/switch.md @@ -2,6 +2,8 @@ ![Building bridges.](oredict:oc:switch) +*This block is deprecated and will be removed in a future version.* Craft it into a [relay](relay.md) to avoid losing it. + The switch can be used to allow different subnetworks to send network messages to each other, without exposing components to [computers](../general/computer.md) in other networks. Keeping components local is usually a good idea, to avoid [computers](../general/computer.md) using the wrong [screen](screen1.md) or to avoid component overflows to happen (causing [computers](../general/computer.md) to crash and refuse to boot up). There is also a wireless variation of this block, called the [access point](accessPoint.md), which will also relay messages wirelessly. Wireless messages can be received and relayed by other [access points](accessPoint.md), or by [computers](../general/computer.md) with a [wireless network card](../item/wlanCard.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_US/item/lanCard.md b/src/main/resources/assets/opencomputers/doc/en_US/item/lanCard.md index 3fbd8a6c4..fc730e035 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/item/lanCard.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/item/lanCard.md @@ -2,4 +2,4 @@ ![Enter the network.](oredict:oc:lanCard) -The network card allows [computers](../general/computer.md) to send and receive network messages. Messages (or packets) can be broadcasted to all receiving nodes in a subnetwork, or sent to a specific node with a specified address. [Switches](../block/switch.md) and [access points](../block/accessPoint.md) can be used to bridge multiple subnetworks by relaying messages between the subnetworks they are connected to. It is also possible to send a targeted message if the receiver is in another subnetwork, if the networks are connected via one or more [switches](../block/switch.md). +The network card allows [computers](../general/computer.md) to send and receive network messages. Messages (or packets) can be broadcasted to all receiving nodes in a subnetwork, or sent to a specific node with a specified address. [Relays](../block/relay.md) can be used to bridge multiple subnetworks by relaying messages between the subnetworks they are connected to. It is also possible to send a targeted message if the receiver is in another subnetwork, if the networks are connected via one or more [relays](../block/relay.md). diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index 7091603c1..05f7f1c18 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -3,7 +3,7 @@ # Use [nl] to for a line break. # Blocks -tile.oc.accessPoint.name=Access Point +tile.oc.accessPoint.name=§cAccess Point§7 tile.oc.adapter.name=Adapter tile.oc.assembler.name=Electronics Assembler tile.oc.cable.name=Cable @@ -29,13 +29,14 @@ tile.oc.print.name=3D Print tile.oc.printer.name=3D Printer tile.oc.raid.name=Raid tile.oc.redstone.name=Redstone I/O +tile.oc.relay.name=Relay tile.oc.robot.name=Robot tile.oc.robotAfterimage.name=Robot tile.oc.screen1.name=Screen (Tier 1) tile.oc.screen2.name=Screen (Tier 2) tile.oc.screen3.name=Screen (Tier 3) tile.oc.serverRack.name=Server Rack -tile.oc.switch.name=Switch +tile.oc.switch.name=§cSwitch§7 tile.oc.netSplitter.name=Net Splitter tile.oc.waypoint.name=Waypoint @@ -235,6 +236,7 @@ oc:container.Disassembler=Disassembler oc:container.DiskDrive=Disk Drive oc:container.Printer=Printer oc:container.Raid=Raid +oc:container.Relay=Relay oc:container.Server=Server oc:container.ServerRack=Server Rack oc:container.Switch=Switch @@ -328,6 +330,7 @@ oc:tooltip.RedstoneCard.RedNet=§fRedNet§7 is §asupported§7. oc:tooltip.RedstoneCard.WirelessCBE=§fWireless Redstone (ChickenBones)§7 is §asupported§7. oc:tooltip.RedstoneCard.WirelessSV=§fWireless Redstone (SlimeVoid)§7 is §asupported§7. oc:tooltip.RedstoneCard=Allows reading and emitting redstone signals around the computer or robot. +oc:tooltip.Relay=Allows connecting different networks to each other. Only network messages will be passed along, components will not be visible through this. Use this to separate networks while still allowing communication using Network Cards, for example. oc:tooltip.Robot=Unlike computers, robots can move around and interact with the world much like a player can.[nl] §cCan not connect to external components.§7 # The underscore makes sure this isn't hidden with the rest of the tooltip. oc:tooltip.Robot_Level=§fLevel§7: §a%s§7 diff --git a/src/main/resources/assets/opencomputers/models/block/relay.json b/src/main/resources/assets/opencomputers/models/block/relay.json new file mode 100644 index 000000000..0af961954 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/block/relay.json @@ -0,0 +1,8 @@ +{ + "parent": "block/cube_bottom_top", + "textures": { + "top": "opencomputers:blocks/switch_top", + "bottom": "opencomputers:blocks/generic_top", + "side": "opencomputers:blocks/switch_side" + } +} diff --git a/src/main/resources/assets/opencomputers/models/item/relay.json b/src/main/resources/assets/opencomputers/models/item/relay.json new file mode 100644 index 000000000..9283695ae --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/relay.json @@ -0,0 +1,10 @@ +{ + "parent": "opencomputers:block/relay", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.375, 0.375, 0.375 ] + } + } +} diff --git a/src/main/resources/assets/opencomputers/recipes/default.recipes b/src/main/resources/assets/opencomputers/recipes/default.recipes index 456f87c33..bfbb73159 100644 --- a/src/main/resources/assets/opencomputers/recipes/default.recipes +++ b/src/main/resources/assets/opencomputers/recipes/default.recipes @@ -504,11 +504,6 @@ interweb { [string, string, string]] } -accessPoint { - input: [[ingotIron, "oc:wlanCard", ingotIron] - ["oc:cable", "oc:lanCard", "oc:cable"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} adapter { input: [[ingotIron, "oc:cable", ingotIron] ["oc:cable", "oc:circuitChip1", "oc:cable"] @@ -607,7 +602,7 @@ powerDistributor { serverRack { input: [["oc:circuitChip2", "oc:wlanCard", "oc:circuitChip2"] [fenceIron, chest, fenceIron] - ["oc:switch", "oc:materialCircuitBoardPrinted", "oc:powerDistributor"]] + ["oc:relay", "oc:materialCircuitBoardPrinted", "oc:powerDistributor"]] } raid { input: [[nuggetIron, "oc:cpu3", nuggetIron] @@ -619,7 +614,7 @@ redstone { [blockRedstone, "oc:redstoneCard1", blockRedstone] [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] } -switch { +relay { input: [[ingotIron, "oc:cable", ingotIron] ["oc:cable", "oc:lanCard", "oc:cable"] [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] diff --git a/src/main/resources/assets/opencomputers/recipes/gregtech.recipes b/src/main/resources/assets/opencomputers/recipes/gregtech.recipes index 4c95c8eb8..f2b3768c7 100644 --- a/src/main/resources/assets/opencomputers/recipes/gregtech.recipes +++ b/src/main/resources/assets/opencomputers/recipes/gregtech.recipes @@ -318,7 +318,7 @@ powerDistributor { serverRack { input: [[craftingToolScrewdriver, "oc:wlanCard", craftingToolWrench] ["ic2.reactorVentDiamond", chest, "ic2.reactorVentDiamond"] - ["oc:switch", "oc:materialCircuitBoardPrinted","oc:powerDistributor"]] + ["oc:relay", "oc:materialCircuitBoardPrinted","oc:powerDistributor"]] } redstone { # 32731 = Activity Detector @@ -326,7 +326,7 @@ redstone { [{item="gt.metaitem.01", subID=32731}, {block="gt.blockcasings", subID=2}, "oc:redstoneCard1"] ["oc:circuitChip2", "oc:materialCircuitBoardPrinted", "oc:circuitChip2"]] } -switch { +relay { input: [["", "oc:lanCard", ""] ["oc:cable", {block="gt.blockcasings", subID=2}, "oc:cable"] ["oc:materialCircuitBoardPrinted", craftingToolWrench, "oc:materialCircuitBoardPrinted"]] diff --git a/src/main/resources/assets/opencomputers/recipes/hardmode.recipes b/src/main/resources/assets/opencomputers/recipes/hardmode.recipes index ed930d4f3..fe0614746 100644 --- a/src/main/resources/assets/opencomputers/recipes/hardmode.recipes +++ b/src/main/resources/assets/opencomputers/recipes/hardmode.recipes @@ -363,14 +363,14 @@ powerDistributor { serverRack { input: [["oc:circuitChip3", "oc:wlanCard", "oc:circuitChip3"] [fenceIron, chest, fenceIron] - ["oc:switch", "oc:materialCircuitBoardPrinted","oc:powerDistributor"]] + ["oc:relay", "oc:materialCircuitBoardPrinted","oc:powerDistributor"]] } redstone { input: [[ingotIron, "oc:circuitChip3", ingotIron] [blockRedstone, "oc:redstoneCard1", blockRedstone] [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] } -switch { +relay { input: [[ingotIron, "oc:cable", ingotIron] ["oc:cable", "oc:lanCard", "oc:cable"] [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] diff --git a/src/main/resources/assets/opencomputers/textures/gui/upgrade_tab.png b/src/main/resources/assets/opencomputers/textures/gui/upgrade_tab.png new file mode 100644 index 000000000..2ca3b14c7 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/gui/upgrade_tab.png differ diff --git a/src/main/scala/li/cil/oc/Constants.scala b/src/main/scala/li/cil/oc/Constants.scala index addb367c6..4b13d04c8 100644 --- a/src/main/scala/li/cil/oc/Constants.scala +++ b/src/main/scala/li/cil/oc/Constants.scala @@ -31,6 +31,7 @@ object Constants { final val Printer = "printer" final val Raid = "raid" final val Redstone = "redstone" + final val Relay = "relay" final val Robot = "robot" final val RobotAfterimage = "robotAfterimage" final val ScreenTier1 = "screen1" diff --git a/src/main/scala/li/cil/oc/client/GuiHandler.scala b/src/main/scala/li/cil/oc/client/GuiHandler.scala index 48d5ea80a..a886e372e 100644 --- a/src/main/scala/li/cil/oc/client/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/client/GuiHandler.scala @@ -36,12 +36,14 @@ object GuiHandler extends CommonGuiHandler { new gui.DiskDrive(player.inventory, t) case t: tileentity.Printer if id == GuiType.Printer.id => new gui.Printer(player.inventory, t) - case t: tileentity.Raid if id == GuiType.Raid.id => - new gui.Raid(player.inventory, t) - case t: tileentity.RobotProxy if id == GuiType.Robot.id => - new gui.Robot(player.inventory, t.robot) case t: tileentity.ServerRack if id == GuiType.Rack.id => new gui.ServerRack(player.inventory, t) + case t: tileentity.Raid if id == GuiType.Raid.id => + new gui.Raid(player.inventory, t) + case t: tileentity.Relay if id == GuiType.Relay.id => + new gui.Relay(player.inventory, t) + case t: tileentity.RobotProxy if id == GuiType.Robot.id => + new gui.Robot(player.inventory, t.robot) case t: tileentity.Screen if id == GuiType.Screen.id => new gui.Screen(t.origin.buffer, t.tier > 0, () => t.origin.hasKeyboard, () => t.origin.buffer.isRenderingEnabled) case t: tileentity.Switch if id == GuiType.Switch.id => diff --git a/src/main/scala/li/cil/oc/client/PacketHandler.scala b/src/main/scala/li/cil/oc/client/PacketHandler.scala index bb4cb64d5..751730bda 100644 --- a/src/main/scala/li/cil/oc/client/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/client/PacketHandler.scala @@ -420,7 +420,7 @@ object PacketHandler extends CommonPacketHandler { } def onSwitchActivity(p: PacketParser) = - p.readTileEntity[Switch]() match { + p.readTileEntity[traits.SwitchLike]() match { case Some(t) => t.lastMessage = System.currentTimeMillis() case _ => // Invalid packet. } diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index 6f33385aa..b5d4587be 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -69,6 +69,7 @@ private[oc] class Proxy extends CommonProxy { ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.ServerRack], ServerRackRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Switch], SwitchRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.AccessPoint], SwitchRenderer) + ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Relay], SwitchRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.RobotProxy], RobotRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Screen], ScreenRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.NetSplitter], NetSplitterRenderer) diff --git a/src/main/scala/li/cil/oc/client/Textures.scala b/src/main/scala/li/cil/oc/client/Textures.scala index 39505d661..cb421249f 100644 --- a/src/main/scala/li/cil/oc/client/Textures.scala +++ b/src/main/scala/li/cil/oc/client/Textures.scala @@ -59,6 +59,7 @@ object Textures { val RobotSelection = L("robot_selection") val Server = L("server") val Slot = L("slot") + val UpgradeTab = L("upgrade_tab") val Waypoint = L("waypoint") override protected def basePath = "textures/gui/%s.png" diff --git a/src/main/scala/li/cil/oc/client/gui/Relay.scala b/src/main/scala/li/cil/oc/client/gui/Relay.scala new file mode 100644 index 000000000..c8cb3432b --- /dev/null +++ b/src/main/scala/li/cil/oc/client/gui/Relay.scala @@ -0,0 +1,123 @@ +package li.cil.oc.client.gui + +import java.lang.Iterable +import java.text.DecimalFormat +import java.util + +import codechicken.nei.VisiblityData +import codechicken.nei.api.INEIGuiHandler +import codechicken.nei.api.TaggedInventoryArea +import li.cil.oc.Localization +import li.cil.oc.client.Textures +import li.cil.oc.common.container +import li.cil.oc.common.tileentity +import li.cil.oc.integration.Mods +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.inventory.GuiContainer +import net.minecraft.client.renderer.Tessellator +import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.Optional +import org.lwjgl.opengl.GL11 +import org.lwjgl.util.Rectangle + +@Optional.Interface(iface = "codechicken.nei.api.INEIGuiHandler", modid = Mods.IDs.NotEnoughItems) +class Relay(playerInventory: InventoryPlayer, val relay: tileentity.Relay) extends DynamicGuiContainer(new container.Relay(playerInventory, relay)) with INEIGuiHandler { + private val format = new DecimalFormat("#.##hz") + + private val tabPosition = new Rectangle(xSize, 10, 23, 26) + + override protected def drawSecondaryBackgroundLayer(): Unit = { + super.drawSecondaryBackgroundLayer() + + // Tab background. + GL11.glColor4f(1, 1, 1, 1) + Minecraft.getMinecraft.getTextureManager.bindTexture(Textures.GUI.UpgradeTab) + val x = windowX + tabPosition.getX + val y = windowY + tabPosition.getY + val w = tabPosition.getWidth + val h = tabPosition.getHeight + val t = Tessellator.getInstance + val r = t.getWorldRenderer + r.startDrawingQuads() + r.addVertexWithUV(x, y + h, zLevel, 0, 1) + r.addVertexWithUV(x + w, y + h, zLevel, 1, 1) + r.addVertexWithUV(x + w, y, zLevel, 1, 0) + r.addVertexWithUV(x, y, zLevel, 0, 0) + t.draw() + } + + override def mouseClicked(mouseX: Int, mouseY: Int, button: Int): Unit = { + // So MC doesn't throw away the item in the upgrade slot when we're trying to pick it up... + val originalWidth = xSize + try { + xSize += tabPosition.getWidth + super.mouseClicked(mouseX, mouseY, button) + } + finally { + xSize = originalWidth + } + } + + override def mouseReleased(mouseX: Int, mouseY: Int, button: Int): Unit = { + // So MC doesn't throw away the item in the upgrade slot when we're trying to pick it up... + val originalWidth = xSize + try { + xSize += tabPosition.getWidth + super.mouseReleased(mouseX, mouseY, button) + } + finally { + xSize = originalWidth + } + } + + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(mouseX, mouseY) + fontRendererObj.drawString( + Localization.localizeImmediately(relay.getName), + 8, 6, 0x404040) + + fontRendererObj.drawString( + Localization.Switch.TransferRate, + 14, 20, 0x404040) + fontRendererObj.drawString( + Localization.Switch.PacketsPerCycle, + 14, 39, 0x404040) + fontRendererObj.drawString( + Localization.Switch.QueueSize, + 14, 58, 0x404040) + + fontRendererObj.drawString( + format.format(20f / inventoryContainer.relayDelay), + 108, 20, 0x404040) + fontRendererObj.drawString( + inventoryContainer.packetsPerCycleAvg + " / " + inventoryContainer.relayAmount, + 108, 39, thresholdBasedColor(inventoryContainer.packetsPerCycleAvg, math.ceil(inventoryContainer.relayAmount / 2f).toInt, inventoryContainer.relayAmount)) + fontRendererObj.drawString( + inventoryContainer.queueSize + " / " + inventoryContainer.maxQueueSize, + 108, 58, thresholdBasedColor(inventoryContainer.queueSize, inventoryContainer.maxQueueSize / 2, inventoryContainer.maxQueueSize)) + } + + private def thresholdBasedColor(value: Int, yellow: Int, red: Int) = { + if (value < yellow) 0x009900 + else if (value < red) 0x999900 + else 0x990000 + } + + @Optional.Method(modid = Mods.IDs.NotEnoughItems) + override def modifyVisiblity(gui: GuiContainer, currentVisibility: VisiblityData): VisiblityData = null + + @Optional.Method(modid = Mods.IDs.NotEnoughItems) + override def getItemSpawnSlots(gui: GuiContainer, stack: ItemStack): Iterable[Integer] = null + + @Optional.Method(modid = Mods.IDs.NotEnoughItems) + override def getInventoryAreas(gui: GuiContainer): util.List[TaggedInventoryArea] = null + + @Optional.Method(modid = Mods.IDs.NotEnoughItems) + override def handleDragNDrop(gui: GuiContainer, mouseX: Int, mouseY: Int, stack: ItemStack, button: Int): Boolean = false + + @Optional.Method(modid = Mods.IDs.NotEnoughItems) + override def hideItemPanelSlot(gui: GuiContainer, x: Int, y: Int, w: Int, h: Int): Boolean = { + new Rectangle(x - windowX, y - windowY, w, h).intersects(tabPosition) + } +} 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 aefe609ed..22096def3 100644 --- a/src/main/scala/li/cil/oc/client/gui/Switch.scala +++ b/src/main/scala/li/cil/oc/client/gui/Switch.scala @@ -7,6 +7,7 @@ import li.cil.oc.common.container import li.cil.oc.common.tileentity import net.minecraft.entity.player.InventoryPlayer +// TODO Remove in 1.7 class Switch(playerInventory: InventoryPlayer, val switch: tileentity.Switch) extends DynamicGuiContainer(new container.Switch(playerInventory, switch)) { private val format = new DecimalFormat("#.##hz") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/SwitchRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/SwitchRenderer.scala index 7041bcf2c..2849a780a 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/SwitchRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/SwitchRenderer.scala @@ -12,7 +12,7 @@ object SwitchRenderer extends TileEntitySpecialRenderer { override def renderTileEntityAt(tileEntity: TileEntity, x: Double, y: Double, z: Double, f: Float, damage: Int) { RenderState.checkError(getClass.getName + ".renderTileEntityAt: entering (aka: wasntme)") - val switch = tileEntity.asInstanceOf[tileentity.Switch] + val switch = tileEntity.asInstanceOf[tileentity.traits.SwitchLike] val activity = math.max(0, 1 - (System.currentTimeMillis() - switch.lastMessage) / 1000.0) if (activity > 0) { RenderState.pushAttrib() diff --git a/src/main/scala/li/cil/oc/common/GuiHandler.scala b/src/main/scala/li/cil/oc/common/GuiHandler.scala index a0a7aaa5d..7c80029f9 100644 --- a/src/main/scala/li/cil/oc/common/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/common/GuiHandler.scala @@ -30,6 +30,8 @@ abstract class GuiHandler extends IGuiHandler { new container.Printer(player.inventory, t) case t: tileentity.Raid if id == GuiType.Raid.id => new container.Raid(player.inventory, t) + case t: tileentity.Relay if id == GuiType.Relay.id => + new container.Relay(player.inventory, t) case t: tileentity.RobotProxy if id == GuiType.Robot.id => new container.Robot(player.inventory, t.robot) case t: tileentity.ServerRack if id == GuiType.Rack.id => diff --git a/src/main/scala/li/cil/oc/common/GuiType.scala b/src/main/scala/li/cil/oc/common/GuiType.scala index 11a6dbb5b..bf02db237 100644 --- a/src/main/scala/li/cil/oc/common/GuiType.scala +++ b/src/main/scala/li/cil/oc/common/GuiType.scala @@ -28,6 +28,7 @@ object GuiType extends ScalaEnum { val Printer = new EnumVal { def name = "Printer"; def subType = GuiType.Category.Block } val Rack = new EnumVal { def name = "Rack"; def subType = GuiType.Category.Block } val Raid = new EnumVal { def name = "Raid"; def subType = GuiType.Category.Block } + val Relay = new EnumVal { def name = "Relay"; def subType = GuiType.Category.Block } val Robot = new EnumVal { def name = "Robot"; def subType = GuiType.Category.Block } val Screen = new EnumVal { def name = "Screen"; def subType = GuiType.Category.Block } val Server = new EnumVal { def name = "Server"; def subType = GuiType.Category.Item } diff --git a/src/main/scala/li/cil/oc/common/InventorySlots.scala b/src/main/scala/li/cil/oc/common/InventorySlots.scala index 6961b5bd4..1ac7ef962 100644 --- a/src/main/scala/li/cil/oc/common/InventorySlots.scala +++ b/src/main/scala/li/cil/oc/common/InventorySlots.scala @@ -120,6 +120,13 @@ object InventorySlots { ) ) + val relay = Array( + InventorySlot(Slot.CPU, Tier.Three), + InventorySlot(Slot.Memory, Tier.Three), + InventorySlot(Slot.HDD, Tier.Three), + InventorySlot(Slot.Card, Tier.Three) + ) + val switch = Array( InventorySlot(Slot.CPU, Tier.Three), InventorySlot(Slot.Memory, Tier.Three), diff --git a/src/main/scala/li/cil/oc/common/block/AccessPoint.scala b/src/main/scala/li/cil/oc/common/block/AccessPoint.scala index be8320dff..463d21341 100644 --- a/src/main/scala/li/cil/oc/common/block/AccessPoint.scala +++ b/src/main/scala/li/cil/oc/common/block/AccessPoint.scala @@ -4,6 +4,7 @@ import li.cil.oc.Settings import li.cil.oc.common.tileentity import net.minecraft.world.World +// TODO Remove in 1.7 class AccessPoint extends Switch with traits.PowerAcceptor { override def energyThroughput = Settings.get.accessPointRate diff --git a/src/main/scala/li/cil/oc/common/block/Relay.scala b/src/main/scala/li/cil/oc/common/block/Relay.scala new file mode 100644 index 000000000..8f1356566 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/block/Relay.scala @@ -0,0 +1,17 @@ +package li.cil.oc.common.block + +import li.cil.oc.Settings +import li.cil.oc.common.GuiType +import li.cil.oc.common.tileentity +import net.minecraft.block.state.IBlockState +import net.minecraft.world.World + +class Relay extends SimpleBlock with traits.GUI with traits.PowerAcceptor { + override def guiType = GuiType.Relay + + override def energyThroughput = Settings.get.accessPointRate + + override def hasTileEntity(state: IBlockState) = true + + override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Relay() +} diff --git a/src/main/scala/li/cil/oc/common/block/Switch.scala b/src/main/scala/li/cil/oc/common/block/Switch.scala index ab8c82e1d..f61016f14 100644 --- a/src/main/scala/li/cil/oc/common/block/Switch.scala +++ b/src/main/scala/li/cil/oc/common/block/Switch.scala @@ -5,6 +5,7 @@ import li.cil.oc.common.tileentity import net.minecraft.block.state.IBlockState import net.minecraft.world.World +// TODO Remove in 1.7 class Switch extends SimpleBlock with traits.GUI { override def guiType = GuiType.Switch diff --git a/src/main/scala/li/cil/oc/common/container/Relay.scala b/src/main/scala/li/cil/oc/common/container/Relay.scala new file mode 100644 index 000000000..d9ee4a54f --- /dev/null +++ b/src/main/scala/li/cil/oc/common/container/Relay.scala @@ -0,0 +1,33 @@ +package li.cil.oc.common.container + +import li.cil.oc.common.Slot +import li.cil.oc.common.tileentity +import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.nbt.NBTTagCompound + +class Relay(playerInventory: InventoryPlayer, relay: tileentity.Relay) extends Player(playerInventory, relay) { + addSlotToContainer(151, 15, Slot.CPU) + addSlotToContainer(151, 34, Slot.Memory) + addSlotToContainer(151, 53, Slot.HDD) + addSlotToContainer(178, 15, Slot.Card) + addPlayerInventorySlots(8, 84) + + def relayDelay = synchronizedData.getInteger("relayDelay") + + def relayAmount = synchronizedData.getInteger("relayAmount") + + def maxQueueSize = synchronizedData.getInteger("maxQueueSize") + + def packetsPerCycleAvg = synchronizedData.getInteger("packetsPerCycleAvg") + + def queueSize = synchronizedData.getInteger("queueSize") + + override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { + synchronizedData.setInteger("relayDelay", relay.relayDelay) + synchronizedData.setInteger("relayAmount", relay.relayAmount) + synchronizedData.setInteger("maxQueueSize", relay.maxQueueSize) + synchronizedData.setInteger("packetsPerCycleAvg", relay.packetsPerCycleAvg()) + synchronizedData.setInteger("queueSize", relay.queue.size) + super.detectCustomDataChanges(nbt) + } +} diff --git a/src/main/scala/li/cil/oc/common/container/Switch.scala b/src/main/scala/li/cil/oc/common/container/Switch.scala index ae6c16254..78d884368 100644 --- a/src/main/scala/li/cil/oc/common/container/Switch.scala +++ b/src/main/scala/li/cil/oc/common/container/Switch.scala @@ -5,6 +5,7 @@ import li.cil.oc.common.tileentity import net.minecraft.entity.player.InventoryPlayer import net.minecraft.nbt.NBTTagCompound +// TODO Remove in 1.7 class Switch(playerInventory: InventoryPlayer, switch: tileentity.Switch) extends Player(playerInventory, switch) { addSlotToContainer(151, 15, Slot.CPU) addSlotToContainer(151, 34, Slot.Memory) diff --git a/src/main/scala/li/cil/oc/common/init/Blocks.scala b/src/main/scala/li/cil/oc/common/init/Blocks.scala index 1d3a6b21d..822235ca2 100644 --- a/src/main/scala/li/cil/oc/common/init/Blocks.scala +++ b/src/main/scala/li/cil/oc/common/init/Blocks.scala @@ -30,6 +30,7 @@ object Blocks { GameRegistry.registerTileEntity(classOf[tileentity.Printer], Settings.namespace + "printer") GameRegistry.registerTileEntity(classOf[tileentity.Raid], Settings.namespace + "raid") GameRegistry.registerTileEntity(classOf[tileentity.Redstone], Settings.namespace + "redstone") + GameRegistry.registerTileEntity(classOf[tileentity.Relay], Settings.namespace + "relay") GameRegistry.registerTileEntity(classOf[tileentity.RobotProxy], Settings.namespace + "robot") GameRegistry.registerTileEntity(classOf[tileentity.Switch], Settings.namespace + "switch") GameRegistry.registerTileEntity(classOf[tileentity.Screen], Settings.namespace + "screen") @@ -37,7 +38,7 @@ object Blocks { GameRegistry.registerTileEntity(classOf[tileentity.NetSplitter], Settings.namespace + "netSplitter") GameRegistry.registerTileEntity(classOf[tileentity.Waypoint], Settings.namespace + "waypoint") - Recipes.addBlock(new AccessPoint(), Constants.BlockName.AccessPoint, "oc:accessPoint") + Items.registerBlock(new AccessPoint(), Constants.BlockName.AccessPoint) Recipes.addBlock(new Adapter(), Constants.BlockName.Adapter, "oc:adapter") Recipes.addBlock(new Assembler(), Constants.BlockName.Assembler, "oc:assembler") Recipes.addBlock(new Cable(), Constants.BlockName.Cable, "oc:cable") @@ -59,11 +60,12 @@ object Blocks { Recipes.addBlock(new Printer(), Constants.BlockName.Printer, "oc:printer") Recipes.addBlock(new Raid(), Constants.BlockName.Raid, "oc:raid") Recipes.addBlock(new Redstone(), Constants.BlockName.Redstone, "oc:redstone") + Recipes.addBlock(new Relay(), Constants.BlockName.Relay, "oc:relay") Recipes.addBlock(new Screen(Tier.One), Constants.BlockName.ScreenTier1, "oc:screen1") Recipes.addBlock(new Screen(Tier.Three), Constants.BlockName.ScreenTier3, "oc:screen3") Recipes.addBlock(new Screen(Tier.Two), Constants.BlockName.ScreenTier2, "oc:screen2") Recipes.addBlock(new ServerRack(), Constants.BlockName.ServerRack, "oc:serverRack") - Recipes.addBlock(new Switch(), Constants.BlockName.Switch, "oc:switch") + Items.registerBlock(new Switch(), Constants.BlockName.Switch) Recipes.addBlock(new Waypoint(), Constants.BlockName.Waypoint, "oc:waypoint") Items.registerBlock(new Case(Tier.Four), Constants.BlockName.CaseCreative) diff --git a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala b/src/main/scala/li/cil/oc/common/recipe/Recipes.scala index 34c19bd6b..5e4125310 100644 --- a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala +++ b/src/main/scala/li/cil/oc/common/recipe/Recipes.scala @@ -326,6 +326,12 @@ object Recipes { GameRegistry.addRecipe(new ExtendedShapelessOreRecipe( lightPrint, print.createItemStack(1), new ItemStack(net.minecraft.init.Blocks.glowstone))) + + // Switch/AccessPoint -> Relay conversion + GameRegistry.addShapelessRecipe(api.Items.get(Constants.BlockName.Relay).createItemStack(1), + api.Items.get(Constants.BlockName.AccessPoint).createItemStack(1)) + GameRegistry.addShapelessRecipe(api.Items.get(Constants.BlockName.Relay).createItemStack(1), + api.Items.get(Constants.BlockName.Switch).createItemStack(1)) } catch { case e: Throwable => OpenComputers.log.error("Error parsing recipes, you may not be able to craft any items from this mod!", e) diff --git a/src/main/scala/li/cil/oc/common/tileentity/AccessPoint.scala b/src/main/scala/li/cil/oc/common/tileentity/AccessPoint.scala index 35b2746bc..63a195f23 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/AccessPoint.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/AccessPoint.scala @@ -15,6 +15,7 @@ import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly +// TODO Remove in 1.7 class AccessPoint extends Switch with WirelessEndpoint with traits.PowerAcceptor { var strength = Settings.get.maxWirelessRange @@ -24,6 +25,8 @@ class AccessPoint extends Switch with WirelessEndpoint with traits.PowerAcceptor withComponent("access_point"). create()) + override def isWirelessEnabled = true + // ----------------------------------------------------------------------- // @SideOnly(Side.CLIENT) @@ -71,7 +74,7 @@ class AccessPoint extends Switch with WirelessEndpoint with traits.PowerAcceptor override protected def relayPacket(sourceSide: Option[EnumFacing], packet: Packet) { super.relayPacket(sourceSide, packet) - if (strength > 0 && (sourceSide != None || isRepeater)) { + if (strength > 0 && (sourceSide.isDefined || isRepeater)) { val cost = Settings.get.wirelessCostPerRange val tryChangeBuffer = sourceSide match { case Some(side) => diff --git a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala new file mode 100644 index 000000000..c43ef60b6 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala @@ -0,0 +1,282 @@ +package li.cil.oc.common.tileentity + +import li.cil.oc.Constants +import li.cil.oc.Localization +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.api.Driver +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.Analyzable +import li.cil.oc.api.network.Connector +import li.cil.oc.api.network.Node +import li.cil.oc.api.network.Packet +import li.cil.oc.api.network.Visibility +import li.cil.oc.api.network.WirelessEndpoint +import li.cil.oc.common.InventorySlots +import li.cil.oc.common.Slot +import li.cil.oc.common.item +import li.cil.oc.common.item.Delegator +import li.cil.oc.integration.Mods +import li.cil.oc.integration.opencomputers.DriverLinkedCard +import li.cil.oc.server.network.QuantumNetwork +import li.cil.oc.util.ExtendedNBT._ +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraftforge.common.util.Constants.NBT +import net.minecraftforge.fml.relauncher.Side +import net.minecraftforge.fml.relauncher.SideOnly + +class Relay extends traits.SwitchLike with traits.ComponentInventory with traits.PowerAcceptor with Analyzable with WirelessEndpoint with QuantumNetwork.QuantumNode { + lazy final val WirelessNetworkCard = api.Items.get(Constants.ItemName.WirelessNetworkCard) + lazy final val LinkedCard = api.Items.get(Constants.ItemName.LinkedCard) + + var strength = Settings.get.maxWirelessRange + + var isRepeater = true + + var isWirelessEnabled = false + + var isLinkedEnabled = false + + var tunnel = "creative" + + val componentNodes = Array.fill(6)(api.Network.newNode(this, Visibility.Network). + withComponent("access_point"). + create()) + + override def canUpdate = isServer + + // ----------------------------------------------------------------------- // + + @SideOnly(Side.CLIENT) + override protected def hasConnector(side: EnumFacing) = true + + override protected def connector(side: EnumFacing) = sidedNode(side) match { + case connector: Connector => Option(connector) + case _ => None + } + + override def energyThroughput = Settings.get.accessPointRate + + // ----------------------------------------------------------------------- // + + override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { + if (isWirelessEnabled) { + player.addChatMessage(Localization.Analyzer.WirelessStrength(strength)) + Array(componentNodes(side.getIndex)) + } + else null + } + + // ----------------------------------------------------------------------- // + + @Callback(direct = true, doc = """function():number -- Get the signal strength (range) used when relaying messages.""") + def getStrength(context: Context, args: Arguments): Array[AnyRef] = synchronized(result(strength)) + + @Callback(doc = """function(strength:number):number -- Set the signal strength (range) used when relaying messages.""") + def setStrength(context: Context, args: Arguments): Array[AnyRef] = synchronized { + strength = math.max(args.checkDouble(0), math.min(0, Settings.get.maxWirelessRange)) + result(strength) + } + + @Callback(direct = true, doc = """function():boolean -- Get whether the access point currently acts as a repeater (resend received wireless packets wirelessly).""") + def isRepeater(context: Context, args: Arguments): Array[AnyRef] = synchronized(result(isRepeater)) + + @Callback(doc = """function(enabled:boolean):boolean -- Set whether the access point should act as a repeater.""") + def setRepeater(context: Context, args: Arguments): Array[AnyRef] = synchronized { + isRepeater = args.checkBoolean(0) + result(isRepeater) + } + + // ----------------------------------------------------------------------- // + + protected def queueMessage(source: String, destination: String, port: Int, answerPort: Int, args: Array[AnyRef]) { + /* TODO ComputerCraft + for (computer <- computers.map(_.asInstanceOf[IComputerAccess])) { + val address = s"cc${computer.getID}_${computer.getAttachmentName}" + if (source != address && Option(destination).forall(_ == address) && openPorts(computer).contains(port)) + computer.queueEvent("modem_message", Array(Seq(computer.getAttachmentName, Int.box(port), Int.box(answerPort)) ++ args.map { + case x: Array[Byte] => new String(x, Charsets.UTF_8) + case x => x + }: _*)) + } + */ + } + + // ----------------------------------------------------------------------- // + + override def receivePacket(packet: Packet, source: WirelessEndpoint): Unit = { + if (isWirelessEnabled) { + tryEnqueuePacket(None, packet) + } + } + + override def receivePacket(packet: Packet): Unit = { + if (isLinkedEnabled) { + tryEnqueuePacket(None, packet) + } + } + + override def tryEnqueuePacket(sourceSide: Option[EnumFacing], packet: Packet): Boolean = { + if (Mods.ComputerCraft.isAvailable) { + packet.data.headOption match { + case Some(answerPort: java.lang.Double) => queueMessage(packet.source, packet.destination, packet.port, answerPort.toInt, packet.data.drop(1)) + case _ => queueMessage(packet.source, packet.destination, packet.port, -1, packet.data) + } + } + super.tryEnqueuePacket(sourceSide, packet) + } + + override protected def relayPacket(sourceSide: Option[EnumFacing], packet: Packet): Unit = { + super.relayPacket(sourceSide, packet) + + val tryChangeBuffer = sourceSide match { + case Some(side) => + (amount: Double) => plugs(side.ordinal).node.asInstanceOf[Connector].tryChangeBuffer(amount) + case _ => + (amount: Double) => plugs.exists(_.node.asInstanceOf[Connector].tryChangeBuffer(amount)) + } + + if (isWirelessEnabled && strength > 0 && (sourceSide.isDefined || isRepeater)) { + val cost = Settings.get.wirelessCostPerRange + if (tryChangeBuffer(-strength * cost)) { + api.Network.sendWirelessPacket(this, strength, packet) + } + } + + if (isLinkedEnabled && (sourceSide.isDefined || isRepeater)) { + val cost = packet.size / 32.0 + Settings.get.wirelessCostPerRange * Settings.get.maxWirelessRange * 5 + if (tryChangeBuffer(-cost)) { + val endpoints = QuantumNetwork.getEndpoints(tunnel).filter(_ != this) + for (endpoint <- endpoints) { + endpoint.receivePacket(packet) + } + } + } + + onSwitchActivity() + } + + // ----------------------------------------------------------------------- // + + override protected def createNode(plug: Plug) = api.Network.newNode(plug, Visibility.Network). + withConnector(math.round(Settings.get.bufferAccessPoint)). + create() + + override protected def onPlugConnect(plug: Plug, node: Node) { + super.onPlugConnect(plug, node) + if (node == plug.node) { + api.Network.joinWirelessNetwork(this) + } + if (plug.isPrimary) + plug.node.connect(componentNodes(plug.side.ordinal())) + else + componentNodes(plug.side.ordinal).remove() + } + + override protected def onPlugDisconnect(plug: Plug, node: Node) { + super.onPlugDisconnect(plug, node) + if (node == plug.node) { + api.Network.leaveWirelessNetwork(this) + } + if (plug.isPrimary && node != plug.node) + plug.node.connect(componentNodes(plug.side.ordinal())) + else + componentNodes(plug.side.ordinal).remove() + } + + // ----------------------------------------------------------------------- // + + override protected def onItemAdded(slot: Int, stack: ItemStack) { + super.onItemAdded(slot, stack) + updateLimits(slot, stack) + } + + private def updateLimits(slot: Int, stack: ItemStack) { + Option(Driver.driverFor(stack, getClass)) match { + case Some(driver) if driver.slot(stack) == Slot.CPU => + relayDelay = math.max(1, relayBaseDelay - ((driver.tier(stack) + 1) * relayDelayPerUpgrade)) + case Some(driver) if driver.slot(stack) == Slot.Memory => + relayAmount = math.max(1, relayBaseAmount + (Delegator.subItem(stack) match { + case Some(ram: item.Memory) => (ram.tier + 1) * relayAmountPerUpgrade + case _ => (driver.tier(stack) + 1) * (relayAmountPerUpgrade * 2) + })) + case Some(driver) if driver.slot(stack) == Slot.HDD => + maxQueueSize = math.max(1, queueBaseSize + (driver.tier(stack) + 1) * queueSizePerUpgrade) + case Some(driver) if driver.slot(stack) == Slot.Card => + val descriptor = api.Items.get(stack) + if (descriptor == WirelessNetworkCard) { + isWirelessEnabled = true + } + if (descriptor == LinkedCard) { + val data = DriverLinkedCard.dataTag(stack) + if (data.hasKey(Settings.namespace + "tunnel")) { + tunnel = data.getString(Settings.namespace + "tunnel") + isLinkedEnabled = true + QuantumNetwork.add(this) + } + } + case _ => // Dafuq u doin. + } + } + + override protected def onItemRemoved(slot: Int, stack: ItemStack) { + super.onItemRemoved(slot, stack) + Driver.driverFor(stack, getClass) match { + case driver if driver.slot(stack) == Slot.CPU => relayDelay = relayBaseDelay + case driver if driver.slot(stack) == Slot.Memory => relayAmount = relayBaseAmount + case driver if driver.slot(stack) == Slot.HDD => maxQueueSize = queueBaseSize + case driver if driver.slot(stack) == Slot.Card => + isWirelessEnabled = false + isLinkedEnabled = false + QuantumNetwork.remove(this) + } + } + + override def getSizeInventory = InventorySlots.relay.length + + override def isItemValidForSlot(slot: Int, stack: ItemStack) = + Option(Driver.driverFor(stack, getClass)).fold(false)(driver => { + val provided = InventorySlots.relay(slot) + val tierSatisfied = driver.slot(stack) == provided.slot && driver.tier(stack) <= provided.tier + val cardTypeSatisfied = if (provided.slot == Slot.Card) api.Items.get(stack) == WirelessNetworkCard || api.Items.get(stack) == LinkedCard else true + tierSatisfied && cardTypeSatisfied + }) + + // ----------------------------------------------------------------------- // + + override def readFromNBTForServer(nbt: NBTTagCompound) { + super.readFromNBTForServer(nbt) + for (slot <- items.indices) items(slot) collect { + case stack => updateLimits(slot, stack) + } + + if (nbt.hasKey(Settings.namespace + "strength")) { + strength = nbt.getDouble(Settings.namespace + "strength") max 0 min Settings.get.maxWirelessRange + } + if (nbt.hasKey(Settings.namespace + "isRepeater")) { + isRepeater = nbt.getBoolean(Settings.namespace + "isRepeater") + } + nbt.getTagList(Settings.namespace + "componentNodes", NBT.TAG_COMPOUND).toArray[NBTTagCompound]. + zipWithIndex.foreach { + case (tag, index) => componentNodes(index).load(tag) + } + } + + override def writeToNBTForServer(nbt: NBTTagCompound) = { + super.writeToNBTForServer(nbt) + nbt.setDouble(Settings.namespace + "strength", strength) + nbt.setBoolean(Settings.namespace + "isRepeater", isRepeater) + nbt.setNewTagList(Settings.namespace + "componentNodes", componentNodes.map { + case node: Node => + val tag = new NBTTagCompound() + node.save(tag) + tag + case _ => new NBTTagCompound() + }) + } +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/Switch.scala b/src/main/scala/li/cil/oc/common/tileentity/Switch.scala index 1fae1a4b7..1db46a684 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Switch.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Switch.scala @@ -6,25 +6,49 @@ import li.cil.oc.common.InventorySlots import li.cil.oc.common.Slot import li.cil.oc.common.item import li.cil.oc.common.item.Delegator -import li.cil.oc.server.PacketSender +import li.cil.oc.integration.Mods import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.EnumFacing -class Switch extends traits.Hub with traits.NotAnalyzable with traits.ComponentInventory { - var lastMessage = 0L +// TODO Remove in 1.7 +class Switch extends traits.SwitchLike with traits.NotAnalyzable with traits.ComponentInventory { + override def isWirelessEnabled = false + + override def isLinkedEnabled = false override def canUpdate = isServer // ----------------------------------------------------------------------- // + protected def queueMessage(source: String, destination: String, port: Int, answerPort: Int, args: Array[AnyRef]) { + /* TODO ComputerCraft + for (computer <- computers.map(_.asInstanceOf[IComputerAccess])) { + val address = s"cc${computer.getID}_${computer.getAttachmentName}" + if (source != address && Option(destination).forall(_ == address) && openPorts(computer).contains(port)) + computer.queueEvent("modem_message", Array(Seq(computer.getAttachmentName, Int.box(port), Int.box(answerPort)) ++ args.map { + case x: Array[Byte] => new String(x, Charsets.UTF_8) + case x => x + }: _*)) + } + */ + } + + // ----------------------------------------------------------------------- // + + override def tryEnqueuePacket(sourceSide: Option[EnumFacing], packet: Packet): Boolean = { + if (Mods.ComputerCraft.isAvailable) { + packet.data.headOption match { + case Some(answerPort: java.lang.Double) => queueMessage(packet.source, packet.destination, packet.port, answerPort.toInt, packet.data.drop(1)) + case _ => queueMessage(packet.source, packet.destination, packet.port, -1, packet.data) + } + } + super.tryEnqueuePacket(sourceSide, packet) + } + override protected def relayPacket(sourceSide: Option[EnumFacing], packet: Packet) { super.relayPacket(sourceSide, packet) - val now = System.currentTimeMillis() - if (now - lastMessage >= (relayDelay - 1) * 50) { - lastMessage = now - PacketSender.sendSwitchActivity(this) - } + onSwitchActivity() } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/SwitchLike.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/SwitchLike.scala new file mode 100644 index 000000000..9b14c128a --- /dev/null +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/SwitchLike.scala @@ -0,0 +1,27 @@ +package li.cil.oc.common.tileentity.traits + +import li.cil.oc.server.PacketSender + +import scala.collection.mutable + +trait SwitchLike extends Hub { + def relayDelay: Int + + def isWirelessEnabled: Boolean + + def isLinkedEnabled: Boolean + + val computers = mutable.Buffer.empty[AnyRef] + + val openPorts = mutable.Map.empty[AnyRef, mutable.Set[Int]] + + var lastMessage = 0L + + def onSwitchActivity(): Unit = { + val now = System.currentTimeMillis() + if (now - lastMessage >= (relayDelay - 1) * 50) { + lastMessage = now + PacketSender.sendSwitchActivity(this) + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala b/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala index b2e54e940..5f17cf9df 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala @@ -2,7 +2,7 @@ package li.cil.oc.integration.computercraft import dan200.computercraft.api.ComputerCraftAPI import dan200.computercraft.api.peripheral.IPeripheralProvider -import li.cil.oc.common.tileentity.Switch +import li.cil.oc.common.tileentity.traits.SwitchLike import net.minecraft.world.World object PeripheralProvider extends IPeripheralProvider { @@ -11,7 +11,7 @@ object PeripheralProvider extends IPeripheralProvider { } override def getPeripheral(world: World, x: Int, y: Int, z: Int, side: Int) = world.getTileEntity(x, y, z) match { - case switch: Switch => new SwitchPeripheral(switch) + case switch: SwitchLike => new SwitchPeripheral(switch) case _ => null } } diff --git a/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala b/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala index 804b6bb47..234e93e4b 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala @@ -8,8 +8,7 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.machine.Context import li.cil.oc.api.network.Component -import li.cil.oc.common.tileentity.AccessPoint -import li.cil.oc.common.tileentity.Switch +import li.cil.oc.common.tileentity.traits.SwitchLike import li.cil.oc.util.ResultWrapper._ import net.minecraftforge.common.util.ForgeDirection @@ -17,7 +16,7 @@ import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ import scala.collection.mutable -class SwitchPeripheral(val switch: Switch) extends IPeripheral { +class SwitchPeripheral(val switch: SwitchLike) extends IPeripheral { private val methods = Map[String, (IComputerAccess, ILuaContext, Array[AnyRef]) => Array[AnyRef]]( // Generic modem methods. "open" -> ((computer, context, arguments) => { @@ -86,7 +85,10 @@ class SwitchPeripheral(val switch: Switch) extends IPeripheral { // OC specific. "isAccessPoint" -> ((computer, context, arguments) => { - result(switch.isInstanceOf[AccessPoint]) + result(switch.isWirelessEnabled) + }), + "isTunnel" -> ((computer, context, arguments) => { + result(switch.isLinkedEnabled) }), "maxPacketSize" -> ((computer, context, arguments) => { result(Settings.get.maxNetworkPacketSize) diff --git a/src/main/scala/li/cil/oc/server/PacketSender.scala b/src/main/scala/li/cil/oc/server/PacketSender.scala index d710bcd5f..553a83131 100644 --- a/src/main/scala/li/cil/oc/server/PacketSender.scala +++ b/src/main/scala/li/cil/oc/server/PacketSender.scala @@ -385,7 +385,7 @@ object PacketSender { pb.sendToPlayersNearTileEntity(t) } - def sendSwitchActivity(t: tileentity.Switch) { + def sendSwitchActivity(t: tileentity.traits.SwitchLike) { val pb = new SimplePacketBuilder(PacketType.SwitchActivity) pb.writeTileEntity(t) diff --git a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala index 3d3c27ad5..980609515 100644 --- a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala @@ -12,7 +12,7 @@ import net.minecraft.nbt.NBTTagCompound import scala.collection.convert.WrapAsScala._ -class LinkedCard extends prefab.ManagedEnvironment { +class LinkedCard extends prefab.ManagedEnvironment with QuantumNetwork.QuantumNode { override val node = Network.newNode(this, Visibility.Network). withComponent("tunnel", Visibility.Neighbors). withConnector(). @@ -28,8 +28,8 @@ class LinkedCard extends prefab.ManagedEnvironment { // Cast to iterable to use Scala's toArray instead of the Arguments' one (which converts byte arrays to Strings). val packet = Network.newPacket(node.address, null, 0, args.asInstanceOf[java.lang.Iterable[AnyRef]].toArray) if (node.tryChangeBuffer(-(packet.size / 32.0 + Settings.get.wirelessCostPerRange * Settings.get.maxWirelessRange * 5))) { - for (card <- endpoints) { - card.receivePacket(packet) + for (endpoint <- endpoints) { + endpoint.receivePacket(packet) } result(true) } diff --git a/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala b/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala index ce56d5f01..ca6284836 100644 --- a/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala +++ b/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala @@ -1,20 +1,27 @@ package li.cil.oc.server.network -import li.cil.oc.server.component.LinkedCard +import li.cil.oc.api.network.Packet import scala.collection.mutable // Just because the name if so fancy! object QuantumNetwork { - val tunnels = mutable.Map.empty[String, mutable.WeakHashMap[LinkedCard, Unit]] + val tunnels = mutable.Map.empty[String, mutable.WeakHashMap[QuantumNode, Unit]] - def add(card: LinkedCard) { + def add(card: QuantumNode) { tunnels.getOrElseUpdate(card.tunnel, mutable.WeakHashMap.empty).put(card, Unit) } - def remove(card: LinkedCard) { + def remove(card: QuantumNode) { tunnels.get(card.tunnel).foreach(_.remove(card)) } - def getEndpoints(tunnel: String) = tunnels.get(tunnel).fold(Iterable.empty[LinkedCard])(_.keys) + def getEndpoints(tunnel: String) = tunnels.get(tunnel).fold(Iterable.empty[QuantumNode])(_.keys) + + trait QuantumNode { + def tunnel: String + + def receivePacket(packet: Packet): Unit + } + }