Two-way forwarding of network messages for mountable sub-nodes.

Avoid errors when calling load on machines multiple times.
This commit is contained in:
Florian Nücke 2015-11-07 18:17:18 +01:00
parent 31e4c3c53a
commit 88eae3bfe5
6 changed files with 144 additions and 63 deletions

View File

@ -164,13 +164,6 @@ class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends
Localization.localizeImmediately(rack.getInventoryName), Localization.localizeImmediately(rack.getInventoryName),
8, 6, 0x404040) 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) GL11.glColor3f(1, 1, 1)
mc.renderEngine.bindTexture(Textures.guiRack) mc.renderEngine.bindTexture(Textures.guiRack)

View File

@ -22,6 +22,7 @@ object FileSystemAccessHandler {
server.lastAccess = System.currentTimeMillis() server.lastAccess = System.currentTimeMillis()
t.markChanged(slot) t.markChanged(slot)
} }
case _ =>
} }
} }
case _ => case _ =>

View File

@ -16,18 +16,23 @@ import li.cil.oc.api.network.ComponentHost
import li.cil.oc.api.network.Connector import li.cil.oc.api.network.Connector
import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.api.network.EnvironmentHost
import li.cil.oc.api.network.ManagedEnvironment 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.Node
import li.cil.oc.api.network.Packet import li.cil.oc.api.network.Packet
import li.cil.oc.api.network.Visibility
import li.cil.oc.common.Slot import li.cil.oc.common.Slot
import li.cil.oc.integration.Mods import li.cil.oc.integration.Mods
import li.cil.oc.integration.opencomputers.DriverRedstoneCard import li.cil.oc.integration.opencomputers.DriverRedstoneCard
import li.cil.oc.integration.stargatetech2.DriverAbstractBusCard import li.cil.oc.integration.stargatetech2.DriverAbstractBusCard
import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.ExtendedInventory._ import li.cil.oc.util.ExtendedInventory._
import li.cil.oc.util.ExtendedNBT._
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory.IInventory import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import net.minecraft.nbt.NBTTagIntArray
import net.minecraftforge.common.util.Constants.NBT
import net.minecraftforge.common.util.ForgeDirection import net.minecraftforge.common.util.ForgeDirection
import scala.collection.convert.WrapAsJava._ 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 { 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 var isRelayEnabled = true
val lastData = new Array[NBTTagCompound](getSizeInventory) 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 // Map node connections for each installed mountable. Each mountable may
// have up to four outgoing connections, with the first one always being // have up to four outgoing connections, with the first one always being
@ -46,35 +51,83 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance
// messages. // messages.
// mountable -> connectable -> side // mountable -> connectable -> side
val nodeMapping = Array.fill(getSizeInventory)(Array.fill[Option[ForgeDirection]](4)(None)) 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 { 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 case _ => None
} }
val oldSide = nodeMapping(mountableIndex)(nodeIndex) val oldSide = nodeMapping(slot)(connectableIndex)
if (oldSide == newSide) return if (oldSide == newSide) return
// If it's the primary node cut the direct connection. // Cut connection / remove sniffer node.
if (nodeIndex == 0 && oldSide.isDefined) { val mountable = getMountable(slot)
val node = getMountable(mountableIndex).node() if (mountable != null && oldSide.isDefined) {
if (connectableIndex == 0) {
val node = mountable.node
val plug = sidedNode(toGlobal(oldSide.get)) val plug = sidedNode(toGlobal(oldSide.get))
if (node != null && plug != null) { if (node != null && plug != null) {
node.disconnect(plug) 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. // Establish connection / add sniffer node.
if (nodeIndex == 0 && newSide.isDefined) { if (mountable != null && newSide.isDefined) {
val node = getMountable(mountableIndex).node() if (connectableIndex == 0) {
val node = mountable.node
val plug = sidedNode(toGlobal(newSide.get)) val plug = sidedNode(toGlobal(newSide.get))
if (node != null && plug != null) { if (node != null && plug != null) {
node.connect(plug) 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 // 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 // connected to it. Only deliver it to that very node, if it's not the
// sender, to avoid loops. // sender, to avoid loops.
for (mountableIndex <- 0 until getSizeInventory) { for (slot <- 0 until getSizeInventory) {
val mapping = nodeMapping(mountableIndex) val mapping = nodeMapping(slot)
for (nodeIndex <- 0 until 3) { for (connectableIndex <- 0 until 3) {
mapping(nodeIndex + 1) match { mapping(connectableIndex + 1) match {
case Some(side) if sourceSide.contains(toGlobal(side)) => case Some(side) if sourceSide.contains(toGlobal(side)) =>
val mountable = getMountable(mountableIndex) val mountable = getMountable(slot)
if (mountable != null) { if (mountable != null && connectableIndex < mountable.getConnectableCount) {
val connectable = mountable.getConnectableAt(nodeIndex) val connectable = mountable.getConnectableAt(connectableIndex)
if (connectable != null) { if (connectable != null) {
connectable.receivePacket(packet) 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 // SidedEnvironment
@ -223,19 +310,23 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance
// ComponentInventory // ComponentInventory
override protected def onItemAdded(slot: Int, stack: ItemStack): Unit = { override protected def onItemAdded(slot: Int, stack: ItemStack): Unit = {
if (isServer) {
for (connectable <- 0 until 4) { for (connectable <- 0 until 4) {
nodeMapping(slot)(connectable) = None nodeMapping(slot)(connectable) = None
} }
lastData(slot) = null lastData(slot) = null
hasChanged(slot) = true hasChanged(slot) = true
}
super.onItemAdded(slot, stack) super.onItemAdded(slot, stack)
} }
override protected def onItemRemoved(slot: Int, stack: ItemStack): Unit = { override protected def onItemRemoved(slot: Int, stack: ItemStack): Unit = {
if (isServer) {
for (connectable <- 0 until 4) { for (connectable <- 0 until 4) {
nodeMapping(slot)(connectable) = None nodeMapping(slot)(connectable) = None
} }
lastData(slot) = null lastData(slot) = null
}
super.onItemRemoved(slot, stack) 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 = { override def readFromNBTForServer(nbt: NBTTagCompound): Unit = {
super.readFromNBTForServer(nbt) 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. // Kickstart initialization.
_isOutputEnabled = hasRedstoneCard _isOutputEnabled = hasRedstoneCard
@ -281,18 +376,26 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance
override def writeToNBTForServer(nbt: NBTTagCompound): Unit = { override def writeToNBTForServer(nbt: NBTTagCompound): Unit = {
super.writeToNBTForServer(nbt) 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 @SideOnly(Side.CLIENT) override
def readFromNBTForClient(nbt: NBTTagCompound): Unit = { def readFromNBTForClient(nbt: NBTTagCompound): Unit = {
super.readFromNBTForClient(nbt) 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 = { override def writeToNBTForClient(nbt: NBTTagCompound): Unit = {
super.writeToNBTForClient(nbt) super.writeToNBTForClient(nbt)
// TODO store lastState
val data = lastData.map(tag => if (tag == null) new NBTTagCompound() else tag)
nbt.setNewTagList(Settings.namespace + "lastData", data)
} }
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //

View File

@ -56,21 +56,6 @@ class Server(val rack: tileentity.Rack, val slot: Int) extends Environment with
} }
override def onMessage(message: Message) { 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) { 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 = { override def getData: NBTTagCompound = {
val nbt = new NBTTagCompound() val nbt = new NBTTagCompound()
nbt.setBoolean("isRunning", machine.isRunning) nbt.setBoolean("isRunning", wasRunning)
nbt.setBoolean("hasErrored", hadErrored)
nbt.setLong("lastAccess", lastAccess) nbt.setLong("lastAccess", lastAccess)
nbt.setBoolean("hasErrored", machine.lastError() != null)
nbt nbt
} }

View File

@ -660,9 +660,8 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def load(nbt: NBTTagCompound) = Machine.this.synchronized(state.synchronized { override def load(nbt: NBTTagCompound) = Machine.this.synchronized(state.synchronized {
assert(state.top == Machine.State.Stopped) assert(state.top == Machine.State.Stopped || state.top == Machine.State.Paused)
assert(_users.isEmpty) close()
assert(signals.isEmpty)
state.clear() state.clear()
super.load(nbt) super.load(nbt)

View File

@ -42,7 +42,7 @@ trait Node extends ImmutableNode {
def remove() = if (network != null) network.remove(this) 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
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //