Merge branch 'master-MC1.7.10' of github.com:MightyPirates/OpenComputers into OC1.5-MC1.7.10

This commit is contained in:
Florian Nücke 2015-02-20 01:42:19 +01:00
commit 3f6a4d25ed
20 changed files with 225 additions and 86 deletions

View File

@ -6,7 +6,6 @@ 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.Visibility; import li.cil.oc.api.network.Visibility;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.server.gui.IUpdatePlayerListBox;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
/** /**
@ -18,7 +17,7 @@ import net.minecraft.tileentity.TileEntity;
* network as an index structure to find other nodes connected to them. * network as an index structure to find other nodes connected to them.
*/ */
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
public abstract class TileEntityEnvironment extends TileEntity implements Environment, IUpdatePlayerListBox { public abstract class TileEntityEnvironment extends TileEntity implements Environment {
/** /**
* This must be set in subclasses to the node that is used to represent * This must be set in subclasses to the node that is used to represent
* this tile entity. * this tile entity.
@ -97,7 +96,7 @@ public abstract class TileEntityEnvironment extends TileEntity implements Enviro
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
@Override @Override
public void update() { public void updateEntity() {
// On the first update, try to add our node to nearby networks. We do // On the first update, try to add our node to nearby networks. We do
// this in the update logic, not in validate() because we need to access // this in the update logic, not in validate() because we need to access
// neighboring tile entities, which isn't possible in validate(). // neighboring tile entities, which isn't possible in validate().

View File

@ -1,9 +1,18 @@
local version = "OpenLoader 0.2EE"
local eeprom = [[ local eeprom = [[
_G._OSVERSION = "OpenLoader 0.1EE" _G._OSVERSION = "]] .. version .. [["
local component = component or require('component') local component = component or require('component')
local computer = computer or require('computer') local computer = computer or require('computer')
local unicode = unicode or require('unicode') local unicode = unicode or require('unicode')
local eeprom = component.list("eeprom")()
computer.getBootAddress = function()
return component.invoke(eeprom, "getData")
end
computer.setBootAddress = function(address)
return component.invoke(eeprom, "setData", address)
end
local gpu = component.list("gpu")() local gpu = component.list("gpu")()
local w, h local w, h
@ -147,7 +156,7 @@ say ("Do you really want to flash openloader to EEPROM("..tostring(#eeprom).." b
if options.q or options.quiet or io.read():lower() == "y" then if options.q or options.quiet or io.read():lower() == "y" then
say("Flashing... Do not reboot now!") say("Flashing... Do not reboot now!")
component.eeprom.set(eeprom) component.eeprom.set(eeprom)
component.eeprom.setLabel((type(options.label) == "string" and options.label) or "OpenLoader") component.eeprom.setLabel((type(options.label) == "string" and options.label) or version)
if options.r or options.reboot then if options.r or options.reboot then
computer.shutdown(true) computer.shutdown(true)
else else

View File

@ -1,8 +1,15 @@
_G._OSVERSION = "OpenLoader 0.1" _G._OSVERSION = "OpenLoader 0.2"
local component = component or require('component') local component = component or require('component')
local computer = computer or require('computer') local computer = computer or require('computer')
local unicode = unicode or require('unicode') local unicode = unicode or require('unicode')
local eeprom = component.list("eeprom")()
computer.getBootAddress = function()
return component.invoke(eeprom, "getData")
end
computer.setBootAddress = function(address)
return component.invoke(eeprom, "setData", address)
end
local gpu = component.list("gpu")() local gpu = component.list("gpu")()
local w, h local w, h

View File

@ -0,0 +1,33 @@
local event = require "event"
local component = require "component"
local keyboard = require "keyboard"
local interactive = io.output() == io.stdout
local color, isPal, evt
if interactive then
color, isPal = component.gpu.getForeground()
end
io.write("Press 'q' to exit\n")
pcall(function()
repeat
evt = table.pack(event.pull())
if interactive then component.gpu.setForeground(0xCC2200) end
io.write("[" .. os.date("%T") .. "] ")
if interactive then component.gpu.setForeground(0x44CC00) end
io.write(tostring(evt[1]) .. string.rep(" ", math.max(10 - #tostring(evt[1]), 0) + 1))
if interactive then component.gpu.setForeground(0xB0B00F) end
io.write(tostring(evt[2]) .. string.rep(" ", 37 - #tostring(evt[2])))
if interactive then component.gpu.setForeground(0xFFFFFF) end
if evt.n > 2 then
for i = 3, evt.n do
io.write(" " .. tostring(evt[i]))
end
end
io.write("\n")
until evt[1] == "key_down" and evt[4] == keyboard.keys.q
end)
if interactive then
component.gpu.setForeground(color, isPal)
end

View File

@ -0,0 +1,20 @@
local args = {...}
if args[1] then
local file, reason = io.open("/etc/hostname", "w")
if not file then
io.stderr:write(reason .. "\n")
else
file:write(args[1])
file:close()
os.setenv("HOSTNAME", args[1])
os.setenv("PS1", "$HOSTNAME:$PWD# ")
end
else
local file = io.open("/etc/hostname")
if file then
io.write(file:read("*l"), "\n")
file:close()
else
io.stderr:write("Hostname not set\n")
end
end

View File

@ -14,3 +14,12 @@ shell.setAlias("view", "edit -r")
shell.setAlias("help", "man") shell.setAlias("help", "man")
shell.setAlias("?", "man") shell.setAlias("?", "man")
shell.setAlias("cp", "cp -i") shell.setAlias("cp", "cp -i")
require("event").listen("init", function()
local file = io.open("/etc/hostname")
if file then
os.setenv("HOSTNAME", file:read("*l"))
os.setenv("PS1", "$HOSTNAME:$PWD# ")
file:close()
end
end)

View File

@ -0,0 +1,6 @@
NAME
dmesg - display messages(events)
SYNOPIS
dmesg

View File

@ -0,0 +1,12 @@
NAME
hostname - Display and modify hostname
SYNOPIS
hostname [NEW NAME]
EXAMPLES
hostname
Prints currently set hostname
hostname test
Sets hostname of this computer to test

View File

@ -51,6 +51,7 @@ Kilobyte # Contributor
KITT # Knight Rider KITT # Knight Rider
Kodos # Contributor Kodos # Contributor
Laire # Perry Rhodan Laire # Perry Rhodan
Loader 1340 # Borderlands 2
LordFokas # Contributor LordFokas # Contributor
Marvin # Hitchhiker's Guide to the Galaxy Marvin # Hitchhiker's Guide to the Galaxy
Michiyo # Contributor Michiyo # Contributor

View File

@ -6,6 +6,7 @@ import cpw.mods.fml.common.Optional
import cpw.mods.fml.common.eventhandler.SubscribeEvent import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.common.gameevent.PlayerEvent._ import cpw.mods.fml.common.gameevent.PlayerEvent._
import cpw.mods.fml.common.gameevent.TickEvent import cpw.mods.fml.common.gameevent.TickEvent
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent
import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent
import cpw.mods.fml.common.network.FMLNetworkEvent.ClientConnectedToServerEvent import cpw.mods.fml.common.network.FMLNetworkEvent.ClientConnectedToServerEvent
import li.cil.oc._ import li.cil.oc._
@ -97,7 +98,7 @@ object EventHandler {
} }
@SubscribeEvent @SubscribeEvent
def onTick(e: ServerTickEvent) = if (e.phase == TickEvent.Phase.START) { def onServerTick(e: ServerTickEvent) = if (e.phase == TickEvent.Phase.START) {
pending.synchronized { pending.synchronized {
val adds = pending.toArray val adds = pending.toArray
pending.clear() pending.clear()
@ -107,6 +108,10 @@ object EventHandler {
case t: Throwable => OpenComputers.log.warn("Error in scheduled tick action.", t) case t: Throwable => OpenComputers.log.warn("Error in scheduled tick action.", t)
} }
}) })
}
@SubscribeEvent
def onClientTick(e: ClientTickEvent) = if (e.phase == TickEvent.Phase.START) {
totalWorldTicks += 1 totalWorldTicks += 1
} }

View File

@ -29,7 +29,7 @@ class Raid(protected implicit val tileTag: ClassTag[tileentity.Raid]) extends Si
super.tooltipTail(metadata, stack, player, tooltip, advanced) super.tooltipTail(metadata, stack, player, tooltip, advanced)
if (KeyBindings.showExtendedTooltips) { if (KeyBindings.showExtendedTooltips) {
val data = new RaidData(stack) val data = new RaidData(stack)
for (disk <- data.disks) { for (disk <- data.disks if disk != null) {
tooltip.add("- " + disk.getDisplayName) tooltip.add("- " + disk.getDisplayName)
} }
} }

View File

@ -88,7 +88,7 @@ class RobotProxy extends RedstoneAware with traits.SpecialBlock with traits.Stat
val components = info.containers ++ info.components val components = info.containers ++ info.components
if (components.length > 0) { if (components.length > 0) {
tooltip.addAll(Tooltip.get("Server.Components")) tooltip.addAll(Tooltip.get("Server.Components"))
for (component <- components) { for (component <- components if component != null) {
tooltip.add("- " + component.getDisplayName) tooltip.add("- " + component.getDisplayName)
} }
} }

View File

@ -77,7 +77,8 @@ class Raid extends traits.Environment with traits.Inventory with traits.Rotatabl
} }
def tryCreateRaid(id: String) { def tryCreateRaid(id: String) {
if (items.count(_.isDefined) == items.length) { if (items.count(_.isDefined) == items.length && filesystem.fold(true)(fs => fs.node == null || fs.node.address != id)) {
filesystem.foreach(fs => if (fs.node != null) fs.node.remove())
val fs = api.FileSystem.asManagedEnvironment( val fs = api.FileSystem.asManagedEnvironment(
api.FileSystem.fromSaveDirectory(id, wipeDisksAndComputeSpace, Settings.get.bufferChanges), api.FileSystem.fromSaveDirectory(id, wipeDisksAndComputeSpace, Settings.get.bufferChanges),
label, this, Settings.resourceDomain + ":hdd_access"). label, this, Settings.resourceDomain + ":hdd_access").

View File

@ -447,6 +447,11 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand
swingingTool = nbt.getBoolean(Settings.namespace + "swingingTool") swingingTool = nbt.getBoolean(Settings.namespace + "swingingTool")
turnAxis = nbt.getByte(Settings.namespace + "turnAxis") turnAxis = nbt.getByte(Settings.namespace + "turnAxis")
} }
// Normally set in superclass, but that's not called directly, only in the
// robot's proxy instance.
_isOutputEnabled = hasRedstoneCard
_isAbstractBusAvailable = hasAbstractBusCard
} }
// Side check for Waila (and other mods that may call this client side). // Side check for Waila (and other mods that may call this client side).
@ -692,8 +697,8 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand
player().inventory.addItemStackToInventory(stack) player().inventory.addItemStackToInventory(stack)
spawnStackInWorld(stack, Option(facing)) spawnStackInWorld(stack, Option(facing))
} }
setSelectedSlot(oldSelected)
} // else: save is screwed and we potentially lose items. Life is hard. } // else: save is screwed and we potentially lose items. Life is hard.
setSelectedSlot(oldSelected)
} }
} }
finally { finally {

View File

@ -64,10 +64,8 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInfo
override def disconnectComponents() {} override def disconnectComponents() {}
@SideOnly(Side.CLIENT)
override def isRunning = robot.isRunning override def isRunning = robot.isRunning
@SideOnly(Side.CLIENT)
override def setRunning(value: Boolean) = robot.setRunning(value) override def setRunning(value: Boolean) = robot.setRunning(value)
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //

View File

@ -50,14 +50,15 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B
def isRunning = _isRunning def isRunning = _isRunning
@SideOnly(Side.CLIENT)
def setRunning(value: Boolean): Unit = if (value != _isRunning) { def setRunning(value: Boolean): Unit = if (value != _isRunning) {
_isRunning = value _isRunning = value
world.markBlockForUpdate(x, y, z) if (world != null) {
runSound.foreach(sound => world.markBlockForUpdate(x, y, z)
if (_isRunning) Sound.startLoop(this, sound, 0.5f, 50 + world.rand.nextInt(50)) runSound.foreach(sound =>
else Sound.stopLoop(this) if (_isRunning) Sound.startLoop(this, sound, 0.5f, 50 + world.rand.nextInt(50))
) else Sound.stopLoop(this)
)
}
} }
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
@ -137,7 +138,7 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B
// Kickstart initialization to avoid values getting overwritten by // Kickstart initialization to avoid values getting overwritten by
// readFromNBTForClient if that packet is handled after a manual // readFromNBTForClient if that packet is handled after a manual
// initialization / state change packet. // initialization / state change packet.
_isRunning = machine.isRunning setRunning(machine.isRunning)
_isOutputEnabled = hasRedstoneCard _isOutputEnabled = hasRedstoneCard
_isAbstractBusAvailable = hasAbstractBusCard _isAbstractBusAvailable = hasAbstractBusCard
} }
@ -155,7 +156,7 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
override def readFromNBTForClient(nbt: NBTTagCompound) { override def readFromNBTForClient(nbt: NBTTagCompound) {
super.readFromNBTForClient(nbt) super.readFromNBTForClient(nbt)
_isRunning = nbt.getBoolean("isRunning") setRunning(nbt.getBoolean("isRunning"))
_users.clear() _users.clear()
_users ++= nbt.getTagList("users", NBT.TAG_STRING).map((tag: NBTTagString) => tag.func_150285_a_()) _users ++= nbt.getTagList("users", NBT.TAG_STRING).map((tag: NBTTagString) => tag.func_150285_a_())
if (_isRunning) runSound.foreach(sound => Sound.startLoop(this, sound, 0.5f, 1000 + world.rand.nextInt(2000))) if (_isRunning) runSound.foreach(sound => Sound.startLoop(this, sound, 0.5f, 1000 + world.rand.nextInt(2000)))

View File

@ -10,7 +10,7 @@ import net.minecraft.item.ItemStack
object DriverMemory extends Item with driver.item.Memory { object DriverMemory extends Item with driver.item.Memory {
override def amount(stack: ItemStack) = Items.multi.subItem(stack) match { override def amount(stack: ItemStack) = Items.multi.subItem(stack) match {
case Some(memory: item.Memory) => memory.tier case Some(memory: item.Memory) => memory.tier + 1
case _ => 0.0 case _ => 0.0
} }

View File

@ -20,7 +20,6 @@ import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.util.InventoryUtils import li.cil.oc.util.InventoryUtils
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.command.ICommandSender
import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.item.Item import net.minecraft.item.Item
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
@ -33,12 +32,14 @@ import net.minecraft.world.World
import net.minecraft.world.WorldServer import net.minecraft.world.WorldServer
import net.minecraft.world.WorldSettings.GameType import net.minecraft.world.WorldSettings.GameType
import net.minecraftforge.common.DimensionManager import net.minecraftforge.common.DimensionManager
import net.minecraftforge.common.util.FakePlayer
import net.minecraftforge.common.util.FakePlayerFactory import net.minecraftforge.common.util.FakePlayerFactory
import net.minecraftforge.common.util.ForgeDirection import net.minecraftforge.common.util.ForgeDirection
import net.minecraftforge.fluids.FluidRegistry import net.minecraftforge.fluids.FluidRegistry
import net.minecraftforge.fluids.FluidStack import net.minecraftforge.fluids.FluidStack
import net.minecraftforge.fluids.IFluidHandler import net.minecraftforge.fluids.IFluidHandler
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable import scala.collection.mutable
class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment { class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment {
@ -56,6 +57,17 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment {
// Player this card is bound to (if any) to use for permissions. // Player this card is bound to (if any) to use for permissions.
var player: Option[String] = None var player: Option[String] = None
private lazy val CommandSender = {
def defaultFakePlayer = FakePlayerFactory.get(host.world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile)
new CommandSender(host, player match {
case Some(name) => Option(MinecraftServer.getServer.getConfigurationManager.func_152612_a(name)) match {
case Some(playerEntity) => playerEntity
case _ => defaultFakePlayer
}
case _ => defaultFakePlayer
})
}
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
import li.cil.oc.server.component.DebugCard.checkEnabled import li.cil.oc.server.component.DebugCard.checkEnabled
@ -99,10 +111,18 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment {
@Callback(doc = """function(command:string):number -- Runs an arbitrary command using a fake player.""") @Callback(doc = """function(command:string):number -- Runs an arbitrary command using a fake player.""")
def runCommand(context: Context, args: Arguments): Array[AnyRef] = { def runCommand(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled() checkEnabled()
val command = args.checkString(0) val commands =
val sender = new CommandSender(host, player) if (args.isTable(0)) collectionAsScalaIterable(args.checkTable(0).values())
val value = MinecraftServer.getServer.getCommandManager.executeCommand(sender, command) else Iterable (args.checkString(0))
result(value, sender.messages.orNull)
CommandSender.synchronized {
CommandSender.prepare()
var value = 0
for (command <- commands) {
value = MinecraftServer.getServer.getCommandManager.executeCommand(CommandSender, command.toString)
}
result(value, CommandSender.messages.orNull)
}
} }
@Callback(doc = """function(x:number, y:number, z:number):boolean -- Connect the debug card to the block at the specified coordinates.""") @Callback(doc = """function(x:number, y:number, z:number):boolean -- Connect the debug card to the block at the specified coordinates.""")
@ -496,31 +516,28 @@ object DebugCard {
} }
} }
class CommandSender(val host: EnvironmentHost, val playerName: Option[String]) extends ICommandSender { class CommandSender(val host: EnvironmentHost, val underlying: EntityPlayerMP) extends FakePlayer(underlying.getEntityWorld.asInstanceOf[WorldServer], underlying.getGameProfile) {
val fakePlayer = {
def defaultFakePlayer = FakePlayerFactory.get(host.world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile)
playerName match {
case Some(name) => Option(MinecraftServer.getServer.getConfigurationManager.func_152612_a(name)) match {
case Some(player) => player
case _ => defaultFakePlayer
}
case _ => defaultFakePlayer
}
}
var messages: Option[String] = None var messages: Option[String] = None
override def getCommandSenderName = fakePlayer.getCommandSenderName def prepare(): Unit = {
val blockPos = BlockPosition(host)
posX = blockPos.x
posY = blockPos.y
posZ = blockPos.z
messages = None
}
override def getCommandSenderName = underlying.getCommandSenderName
override def getEntityWorld = host.world override def getEntityWorld = host.world
override def addChatMessage(message: IChatComponent) { override def addChatMessage(message: IChatComponent) {
messages = Option(messages.getOrElse("") + message.getUnformattedText) messages = Option(messages.fold("")(_ + "\n") + message.getUnformattedText)
} }
override def canCommandSenderUseCommand(level: Int, command: String) = { override def canCommandSenderUseCommand(level: Int, command: String) = {
val profile = fakePlayer.getGameProfile val profile = underlying.getGameProfile
val server = fakePlayer.mcServer val server = underlying.mcServer
val config = server.getConfigurationManager val config = server.getConfigurationManager
server.isSinglePlayer || (config.func_152596_g(profile) && (config.func_152603_m.func_152683_b(profile) match { server.isSinglePlayer || (config.func_152596_g(profile) && (config.func_152603_m.func_152683_b(profile) match {
case entry: UserListOpsEntry => entry.func_152644_a >= level case entry: UserListOpsEntry => entry.func_152644_a >= level
@ -530,7 +547,7 @@ object DebugCard {
override def getPlayerCoordinates = BlockPosition(host).toChunkCoordinates override def getPlayerCoordinates = BlockPosition(host).toChunkCoordinates
override def func_145748_c_() = fakePlayer.func_145748_c_() override def func_145748_c_() = underlying.func_145748_c_()
} }
class TestValue extends AbstractValue { class TestValue extends AbstractValue {

View File

@ -9,7 +9,8 @@ import li.cil.oc.OpenComputers
import li.cil.oc.Settings import li.cil.oc.Settings
import li.cil.oc.api import li.cil.oc.api
import li.cil.oc.api.network import li.cil.oc.api.network
import li.cil.oc.api.network.{Node => ImmutableNode, _} import li.cil.oc.api.network._
import li.cil.oc.api.network.{Node => ImmutableNode}
import li.cil.oc.common.block.Cable import li.cil.oc.common.block.Cable
import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity
import li.cil.oc.integration.Mods import li.cil.oc.integration.Mods
@ -21,8 +22,8 @@ import net.minecraft.nbt._
import net.minecraft.tileentity.TileEntity import net.minecraft.tileentity.TileEntity
import net.minecraftforge.common.util.ForgeDirection import net.minecraftforge.common.util.ForgeDirection
import scala.collection.convert.WrapAsScala._
import scala.collection.JavaConverters._ import scala.collection.JavaConverters._
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
@ -50,13 +51,21 @@ private class Network private(private val data: mutable.Map[String, Network.Vert
node.data.network = wrapper node.data.network = wrapper
}) })
// Called by nodes when they may have changed address from loading. // Called by nodes when they want to change address from loading.
def remap(node: MutableNode) { def remap(remappedNode: MutableNode, newAddress: String) {
data.find(_._2.data == node) match { data.get(remappedNode.address) match {
case Some((address, vertex)) => case Some(node) =>
data -= address val neighbors = node.edges.map(_.other(node))
data += node.address -> vertex node.data.remove()
case _ => // Eh? node.data.address = newAddress
while (data.contains(node.data.address)) {
node.data.address = java.util.UUID.randomUUID().toString
}
if (neighbors.isEmpty)
addNew(node.data)
else
neighbors.foreach(_.data.connect(node.data))
case _ => throw new AssertionError("Node believes it belongs to a network it doesn't.")
} }
} }
@ -217,7 +226,7 @@ private class Network private(private val data: mutable.Map[String, Network.Vert
newNode newNode
} }
private def add(oldNode: Network.Vertex, addedNode: MutableNode) = { private def add(oldNode: Network.Vertex, addedNode: MutableNode): Boolean = {
// Queue onConnect calls to avoid side effects from callbacks. // Queue onConnect calls to avoid side effects from callbacks.
val connects = mutable.Buffer.empty[(ImmutableNode, Iterable[ImmutableNode])] val connects = mutable.Buffer.empty[(ImmutableNode, Iterable[ImmutableNode])]
// Check if the other node is new or if we have to merge networks. // Check if the other node is new or if we have to merge networks.
@ -248,44 +257,52 @@ private class Network private(private val data: mutable.Map[String, Network.Vert
// never happen in normal operation anyway. It *can* happen when NBT // never happen in normal operation anyway. It *can* happen when NBT
// editing stuff or using mods to clone blocks (e.g. WorldEdit). // editing stuff or using mods to clone blocks (e.g. WorldEdit).
otherNetwork.data.filter(entry => data.contains(entry._1)).toArray.foreach { otherNetwork.data.filter(entry => data.contains(entry._1)).toArray.foreach {
case (address, node: Network.Vertex) => case (_, node: Network.Vertex) =>
val neighbors = node.edges.map(_.other(node)) val neighbors = node.edges.map(_.other(node))
node.data.remove() node.data.remove()
node.data.address = java.util.UUID.randomUUID().toString do {
node.data.address = java.util.UUID.randomUUID().toString
} while (data.contains(node.data.address) || otherNetwork.data.contains(node.data.address))
if (neighbors.isEmpty) if (neighbors.isEmpty)
otherNetwork.addNew(node.data) otherNetwork.addNew(node.data)
else else
neighbors.foreach(_.data.connect(node.data)) neighbors.foreach(_.data.connect(node.data))
} }
if (addedNode.reachability == Visibility.Neighbors) // The address change can theoretically cause the node to be kicked from
connects += ((addedNode, Iterable(oldNode.data))) // its old network (via onConnect callbacks), so we make sure it's still
if (oldNode.data.reachability == Visibility.Neighbors) // in the same network. If it isn't we start over.
connects += ((oldNode.data, Iterable(addedNode))) if (addedNode.network != null && addedNode.network.asInstanceOf[Network.Wrapper].network == otherNetwork) {
if (addedNode.reachability == Visibility.Neighbors)
connects += ((addedNode, Iterable(oldNode.data)))
if (oldNode.data.reachability == Visibility.Neighbors)
connects += ((oldNode.data, Iterable(addedNode)))
val oldNodes = nodes val oldNodes = nodes
val newNodes = otherNetwork.nodes val newNodes = otherNetwork.nodes
val oldVisibleNodes = oldNodes.filter(_.reachability == Visibility.Network) val oldVisibleNodes = oldNodes.filter(_.reachability == Visibility.Network)
val newVisibleNodes = newNodes.filter(_.reachability == Visibility.Network) val newVisibleNodes = newNodes.filter(_.reachability == Visibility.Network)
newVisibleNodes.foreach(node => connects += ((node, oldNodes))) newVisibleNodes.foreach(node => connects += ((node, oldNodes)))
oldVisibleNodes.foreach(node => connects += ((node, newNodes))) oldVisibleNodes.foreach(node => connects += ((node, newNodes)))
data ++= otherNetwork.data data ++= otherNetwork.data
connectors ++= otherNetwork.connectors connectors ++= otherNetwork.connectors
globalBuffer += otherNetwork.globalBuffer globalBuffer += otherNetwork.globalBuffer
globalBufferSize += otherNetwork.globalBufferSize globalBufferSize += otherNetwork.globalBufferSize
otherNetwork.data.values.foreach(node => { otherNetwork.data.values.foreach(node => {
node.data match { node.data match {
case connector: Connector => connector.distributor = Some(wrapper) case connector: Connector => connector.distributor = Some(wrapper)
case _ => case _ =>
} }
node.data.network = wrapper node.data.network = wrapper
}) })
otherNetwork.data.clear() otherNetwork.data.clear()
otherNetwork.connectors.clear() otherNetwork.connectors.clear()
Network.Edge(oldNode, node(addedNode)) Network.Edge(oldNode, node(addedNode))
}
else add(oldNode, addedNode)
} }
for ((node, nodes) <- connects) nodes.foreach(_.asInstanceOf[MutableNode].onConnect(node)) for ((node, nodes) <- connects) nodes.foreach(_.asInstanceOf[MutableNode].onConnect(node))

View File

@ -66,11 +66,10 @@ trait Node extends ImmutableNode {
def load(nbt: NBTTagCompound) = { def load(nbt: NBTTagCompound) = {
if (nbt.hasKey("address")) { if (nbt.hasKey("address")) {
val oldAddress = address val newAddress = nbt.getString("address")
address = nbt.getString("address") if (newAddress != address) network match {
if (address != oldAddress) network match { case wrapper: Network.Wrapper => wrapper.network.remap(this, newAddress)
case wrapper: Network.Wrapper => wrapper.network.remap(this) case _ => address = newAddress
case _ =>
} }
} }
} }