From 88eae3bfe5b90a5cf7ced211f99f40082840a2d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sat, 7 Nov 2015 18:17:18 +0100 Subject: [PATCH] Two-way forwarding of network messages for mountable sub-nodes. Avoid errors when calling load on machines multiple times. --- .../scala/li/cil/oc/client/gui/Rack.scala | 7 - .../event/FileSystemAccessHandler.scala | 1 + .../li/cil/oc/common/tileentity/Rack.scala | 173 ++++++++++++++---- .../li/cil/oc/server/component/Server.scala | 19 +- .../li/cil/oc/server/machine/Machine.scala | 5 +- .../scala/li/cil/oc/server/network/Node.scala | 2 +- 6 files changed, 144 insertions(+), 63 deletions(-) diff --git a/src/main/scala/li/cil/oc/client/gui/Rack.scala b/src/main/scala/li/cil/oc/client/gui/Rack.scala index b8cbbea8e..9c6fc9b4d 100644 --- a/src/main/scala/li/cil/oc/client/gui/Rack.scala +++ b/src/main/scala/li/cil/oc/client/gui/Rack.scala @@ -164,13 +164,6 @@ class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends Localization.localizeImmediately(rack.getInventoryName), 8, 6, 0x404040) - // for (i <- 0 to 3 if powerButtons(i).func_146115_a) { - // val tooltip = new java.util.ArrayList[String] - // tooltip.addAll(asJavaCollection(if (rack.isRunning(i)) Localization.Computer.TurnOff.lines.toIterable else Localization.Computer.TurnOn.lines.toIterable)) - // copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRendererObj) - // } - // - GL11.glColor3f(1, 1, 1) mc.renderEngine.bindTexture(Textures.guiRack) diff --git a/src/main/scala/li/cil/oc/common/event/FileSystemAccessHandler.scala b/src/main/scala/li/cil/oc/common/event/FileSystemAccessHandler.scala index cd941722a..733954c8a 100644 --- a/src/main/scala/li/cil/oc/common/event/FileSystemAccessHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/FileSystemAccessHandler.scala @@ -22,6 +22,7 @@ object FileSystemAccessHandler { server.lastAccess = System.currentTimeMillis() t.markChanged(slot) } + case _ => } } case _ => diff --git a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala index 2bab1c6f6..447446230 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala @@ -16,18 +16,23 @@ import li.cil.oc.api.network.ComponentHost import li.cil.oc.api.network.Connector import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.api.network.ManagedEnvironment +import li.cil.oc.api.network.Message 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.common.Slot import li.cil.oc.integration.Mods import li.cil.oc.integration.opencomputers.DriverRedstoneCard import li.cil.oc.integration.stargatetech2.DriverAbstractBusCard import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedInventory._ +import li.cil.oc.util.ExtendedNBT._ import net.minecraft.entity.player.EntityPlayer import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.NBTTagIntArray +import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.common.util.ForgeDirection import scala.collection.convert.WrapAsJava._ @@ -36,7 +41,7 @@ import scala.collection.convert.WrapAsScala._ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalancer with traits.ComponentInventory with traits.Rotatable with traits.BundledRedstoneAware with traits.AbstractBusAware with Analyzable with internal.Rack with traits.StateAware { var isRelayEnabled = true val lastData = new Array[NBTTagCompound](getSizeInventory) - val hasChanged = new Array[Boolean](getSizeInventory) + val hasChanged = Array.fill(getSizeInventory)(true) // Map node connections for each installed mountable. Each mountable may // have up to four outgoing connections, with the first one always being @@ -46,33 +51,81 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance // messages. // mountable -> connectable -> side val nodeMapping = Array.fill(getSizeInventory)(Array.fill[Option[ForgeDirection]](4)(None)) + val snifferNodes = Array.fill(getSizeInventory)(Array.fill(3)(api.Network.newNode(this, Visibility.Neighbors).create())) - def connect(mountableIndex: Int, nodeIndex: Int, side: Option[ForgeDirection]): Unit = { + def connect(slot: Int, connectableIndex: Int, side: Option[ForgeDirection]): Unit = { val newSide = side match { - case Some(direction) if direction != ForgeDirection.UNKNOWN => Option(direction) + case Some(direction) if direction != ForgeDirection.UNKNOWN && direction != ForgeDirection.SOUTH => Option(direction) case _ => None } - val oldSide = nodeMapping(mountableIndex)(nodeIndex) + val oldSide = nodeMapping(slot)(connectableIndex) if (oldSide == newSide) return - // If it's the primary node cut the direct connection. - if (nodeIndex == 0 && oldSide.isDefined) { - val node = getMountable(mountableIndex).node() - val plug = sidedNode(toGlobal(oldSide.get)) - if (node != null && plug != null) { - node.disconnect(plug) + // Cut connection / remove sniffer node. + val mountable = getMountable(slot) + if (mountable != null && oldSide.isDefined) { + if (connectableIndex == 0) { + val node = mountable.node + val plug = sidedNode(toGlobal(oldSide.get)) + if (node != null && plug != null) { + node.disconnect(plug) + } + } + else { + snifferNodes(slot)(connectableIndex).remove() } } - nodeMapping(mountableIndex)(nodeIndex) = newSide + nodeMapping(slot)(connectableIndex) = newSide - // If it's the primary node establish a direct connection. - if (nodeIndex == 0 && newSide.isDefined) { - val node = getMountable(mountableIndex).node() - val plug = sidedNode(toGlobal(newSide.get)) - if (node != null && plug != null) { - node.connect(plug) + // Establish connection / add sniffer node. + if (mountable != null && newSide.isDefined) { + if (connectableIndex == 0) { + val node = mountable.node + val plug = sidedNode(toGlobal(newSide.get)) + if (node != null && plug != null) { + node.connect(plug) + } + } + else if (connectableIndex < mountable.getConnectableCount) { + val connectable = mountable.getConnectableAt(connectableIndex) + if (connectable != null && connectable.node != null) { + if (connectable.node.network == null) { + api.Network.joinNewNetwork(connectable.node) + } + connectable.node.connect(snifferNodes(slot)(connectableIndex)) + } + } + } + } + + private def reconnect(plugSide: ForgeDirection): Unit = { + for (slot <- 0 until getSizeInventory) { + val mapping = nodeMapping(slot) + mapping(0) match { + case Some(side) if toGlobal(side) == plugSide => + val mountable = getMountable(slot) + if (mountable != null && mountable.node != null && node != mountable.node) { + mountable.node.connect(sidedNode(plugSide)) + } + case _ => // Not connected to this side. + } + for (connectableIndex <- 0 until 3) { + mapping(connectableIndex) match { + case Some(side) if toGlobal(side) == plugSide => + val mountable = getMountable(slot) + if (mountable != null && connectableIndex < mountable.getConnectableCount) { + val connectable = mountable.getConnectableAt(connectableIndex) + if (connectable != null && connectable.node != null) { + if (connectable.node.network == null) { + api.Network.joinNewNetwork(connectable.node) + } + connectable.node.connect(snifferNodes(slot)(connectableIndex)) + } + } + case _ => // Not connected to this side. + } } } } @@ -86,14 +139,14 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance // When a message arrives on a bus, also send it to all secondary nodes // connected to it. Only deliver it to that very node, if it's not the // sender, to avoid loops. - for (mountableIndex <- 0 until getSizeInventory) { - val mapping = nodeMapping(mountableIndex) - for (nodeIndex <- 0 until 3) { - mapping(nodeIndex + 1) match { + for (slot <- 0 until getSizeInventory) { + val mapping = nodeMapping(slot) + for (connectableIndex <- 0 until 3) { + mapping(connectableIndex + 1) match { case Some(side) if sourceSide.contains(toGlobal(side)) => - val mountable = getMountable(mountableIndex) - if (mountable != null) { - val connectable = mountable.getConnectableAt(nodeIndex) + val mountable = getMountable(slot) + if (mountable != null && connectableIndex < mountable.getConnectableCount) { + val connectable = mountable.getConnectableAt(connectableIndex) if (connectable != null) { connectable.receivePacket(packet) } @@ -104,6 +157,40 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance } } + override protected def onPlugConnect(plug: Plug, node: Node): Unit = { + super.onPlugConnect(plug, node) + connectComponents() + reconnect(plug.side) + } + + // ----------------------------------------------------------------------- // + // Environment + + override def onMessage(message: Message): Unit = { + super.onMessage(message) + if (message.name == "network.message") message.data match { + case Array(packet: Packet) => + for (slot <- 0 until getSizeInventory) { + val mapping = nodeMapping(slot) + for (connectableIndex <- 0 until 3) { + mapping(connectableIndex + 1) match { + case Some(side) => + val mountable = getMountable(slot) + if (mountable != null && connectableIndex < mountable.getConnectableCount) { + val connectable = mountable.getConnectableAt(connectableIndex) + if (connectable != null && connectable.node == message.source) { + sidedNode(toGlobal(side)).sendToReachable("network.message", packet) + return + } + } + case _ => // Not connected to a bus. + } + } + } + case _ => + } + } + // ----------------------------------------------------------------------- // // SidedEnvironment @@ -223,19 +310,23 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance // ComponentInventory override protected def onItemAdded(slot: Int, stack: ItemStack): Unit = { - for (connectable <- 0 until 4) { - nodeMapping(slot)(connectable) = None + if (isServer) { + for (connectable <- 0 until 4) { + nodeMapping(slot)(connectable) = None + } + lastData(slot) = null + hasChanged(slot) = true } - lastData(slot) = null - hasChanged(slot) = true super.onItemAdded(slot, stack) } override protected def onItemRemoved(slot: Int, stack: ItemStack): Unit = { - for (connectable <- 0 until 4) { - nodeMapping(slot)(connectable) = None + if (isServer) { + for (connectable <- 0 until 4) { + nodeMapping(slot)(connectable) = None + } + lastData(slot) = null } - lastData(slot) = null super.onItemRemoved(slot, stack) } @@ -272,7 +363,11 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance override def readFromNBTForServer(nbt: NBTTagCompound): Unit = { super.readFromNBTForServer(nbt) - // TODO read lastState and nodeMapping + + isRelayEnabled = nbt.getBoolean(Settings.namespace + "isRelayEnabled") + nbt.getTagList(Settings.namespace + "nodeMapping", NBT.TAG_INT_ARRAY).map((buses: NBTTagIntArray) => + buses.func_150302_c().map(id => if (id < 0 || id == ForgeDirection.UNKNOWN.ordinal() || id == ForgeDirection.SOUTH.ordinal()) None else Option(ForgeDirection.getOrientation(id)))). + copyToArray(nodeMapping) // Kickstart initialization. _isOutputEnabled = hasRedstoneCard @@ -281,18 +376,26 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance override def writeToNBTForServer(nbt: NBTTagCompound): Unit = { super.writeToNBTForServer(nbt) - // TODO store lastState and nodeMapping + + nbt.setBoolean(Settings.namespace + "isRelayEnabled", isRelayEnabled) + nbt.setNewTagList(Settings.namespace + "nodeMapping", nodeMapping.map(buses => + toNbt(buses.map(side => side.map(_.ordinal()).getOrElse(-1))))) } @SideOnly(Side.CLIENT) override def readFromNBTForClient(nbt: NBTTagCompound): Unit = { super.readFromNBTForClient(nbt) - // TODO read lastState + + val data = nbt.getTagList(Settings.namespace + "lastData", NBT.TAG_COMPOUND). + toArray[NBTTagCompound] + data.copyToArray(lastData) } override def writeToNBTForClient(nbt: NBTTagCompound): Unit = { super.writeToNBTForClient(nbt) - // TODO store lastState + + val data = lastData.map(tag => if (tag == null) new NBTTagCompound() else tag) + nbt.setNewTagList(Settings.namespace + "lastData", data) } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/server/component/Server.scala b/src/main/scala/li/cil/oc/server/component/Server.scala index a6b413e4b..192fd1419 100644 --- a/src/main/scala/li/cil/oc/server/component/Server.scala +++ b/src/main/scala/li/cil/oc/server/component/Server.scala @@ -56,21 +56,6 @@ class Server(val rack: tileentity.Rack, val slot: Int) extends Environment with } override def onMessage(message: Message) { - // If we're internal mode and this server is not connected to any side, we - // must manually propagate network messages to other servers in the rack. - // Ensure the message originated in our local network, to avoid infinite - // recursion if two unconnected servers are in one server rack. - // if (rack.internalSwitch && message.name == "network.message" && - // rack.sides(this.slot).isEmpty && // Only if we're in internal mode. - // message.source != machine.node && // In this case it was relayed from another internal machine. - // node.network.node(message.source.address) != null) { - // for (slot <- rack.servers.indices) { - // rack.servers(slot) match { - // case Some(server) if server != this => server.machine.node.sendToNeighbors(message.name, message.data: _*) - // case _ => - // } - // } - // } } override def load(nbt: NBTTagCompound) { @@ -151,9 +136,9 @@ class Server(val rack: tileentity.Rack, val slot: Int) extends Environment with override def getData: NBTTagCompound = { val nbt = new NBTTagCompound() - nbt.setBoolean("isRunning", machine.isRunning) + nbt.setBoolean("isRunning", wasRunning) + nbt.setBoolean("hasErrored", hadErrored) nbt.setLong("lastAccess", lastAccess) - nbt.setBoolean("hasErrored", machine.lastError() != null) nbt } 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 5ab98a8c4..164736c11 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -660,9 +660,8 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach // ----------------------------------------------------------------------- // override def load(nbt: NBTTagCompound) = Machine.this.synchronized(state.synchronized { - assert(state.top == Machine.State.Stopped) - assert(_users.isEmpty) - assert(signals.isEmpty) + assert(state.top == Machine.State.Stopped || state.top == Machine.State.Paused) + close() state.clear() super.load(nbt) diff --git a/src/main/scala/li/cil/oc/server/network/Node.scala b/src/main/scala/li/cil/oc/server/network/Node.scala index e9e98da6c..de8100c0b 100644 --- a/src/main/scala/li/cil/oc/server/network/Node.scala +++ b/src/main/scala/li/cil/oc/server/network/Node.scala @@ -42,7 +42,7 @@ trait Node extends ImmutableNode { def remove() = if (network != null) network.remove(this) - private def isInSameNetwork(other: ImmutableNode) = network != null && network == other.network + private def isInSameNetwork(other: ImmutableNode) = network != null && other != null && network == other.network // ----------------------------------------------------------------------- //