From e6eb571c1d19972fe7eb4c3f7de113b1b91f5e02 Mon Sep 17 00:00:00 2001 From: Charles Krinke Date: Sun, 14 Dec 2008 02:17:12 +0000 Subject: Mantis#2725. Thank you kindly, Diva, for a patch that: Adds missing protocol pieces for EstablishAgentCommunication event which allows the client to activate CAPS and the EQ for child agents. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 1 + .../Modules/Framework/EventQueueGetModule.cs | 9 +- .../Modules/Framework/EventQueueHelper.cs | 22 +- .../Hypergrid/HGSceneCommunicationService.cs | 87 ++++--- OpenSim/Region/Environment/Scenes/Scene.cs | 81 ++++--- OpenSim/Region/Environment/Scenes/SceneBase.cs | 49 +++- .../Scenes/SceneCommunicationService.cs | 266 ++++++++++++++++----- OpenSim/Region/Environment/Scenes/SceneGraph.cs | 25 +- OpenSim/Region/Environment/Scenes/ScenePresence.cs | 129 +++++++--- 9 files changed, 492 insertions(+), 177 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 160d57e..0802952 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -1327,6 +1327,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP agentData.firstname = m_firstName; agentData.lastname = m_lastName; agentData.CapsPath = m_scene.GetCapsPath(m_agentId); + agentData.ChildrenCapSeeds = new Dictionary(m_scene.GetChildrenSeeds(m_agentId)); return agentData; } diff --git a/OpenSim/Region/Environment/Modules/Framework/EventQueueGetModule.cs b/OpenSim/Region/Environment/Modules/Framework/EventQueueGetModule.cs index 6d3391a..f1f70ff 100644 --- a/OpenSim/Region/Environment/Modules/Framework/EventQueueGetModule.cs +++ b/OpenSim/Region/Environment/Modules/Framework/EventQueueGetModule.cs @@ -202,6 +202,7 @@ namespace OpenSim.Region.Environment.Modules.Framework { m_AvatarQueueUUIDMapping.Remove(ky); m_scene.RemoveHTTPHandler("","/CAPS/EQG/" + ky.ToString() + "/"); + m_log.Debug("[EVENTQUEUE]: Removing " + "/CAPS/EQG/" + ky.ToString() + "/"); } } @@ -225,6 +226,7 @@ namespace OpenSim.Region.Environment.Modules.Framework m_QueueUUIDAvatarMapping.Remove(ky); } + m_log.DebugFormat("[EVENTQUEUE]: Client {0} deregistered in region {1}.", AgentID, m_scene.RegionInfo.RegionName); } @@ -326,13 +328,14 @@ namespace OpenSim.Region.Environment.Modules.Framework if (element == null) { - // m_log.ErrorFormat("[EVENTQUEUE]: Failed to process queue"); + //m_log.ErrorFormat("[EVENTQUEUE]: Nothing to process in " + m_scene.RegionInfo.RegionName); if (thisID == -1) // close-request { - responsedata["int_response_code"] = 404; + m_log.ErrorFormat("[EVENTQUEUE]: 404 in " + m_scene.RegionInfo.RegionName); + responsedata["int_response_code"] = 404; //501; //410; //404; responsedata["content_type"] = "text/plain"; responsedata["keepalive"] = false; - responsedata["str_response_string"] = ""; + responsedata["str_response_string"] = "Closed EQG"; return responsedata; } responsedata["int_response_code"] = 502; diff --git a/OpenSim/Region/Environment/Modules/Framework/EventQueueHelper.cs b/OpenSim/Region/Environment/Modules/Framework/EventQueueHelper.cs index 4833f27..0ffa8aa 100644 --- a/OpenSim/Region/Environment/Modules/Framework/EventQueueHelper.cs +++ b/OpenSim/Region/Environment/Modules/Framework/EventQueueHelper.cs @@ -80,15 +80,15 @@ namespace OpenSim.Region.Environment public static OSD DisableSimulator(ulong Handle) { - OSDMap llsdSimInfo = new OSDMap(1); + //OSDMap llsdSimInfo = new OSDMap(1); - llsdSimInfo.Add("Handle", new OSDBinary(regionHandleToByteArray(Handle))); + //llsdSimInfo.Add("Handle", new OSDBinary(regionHandleToByteArray(Handle))); - OSDArray arr = new OSDArray(1); - arr.Add(llsdSimInfo); + //OSDArray arr = new OSDArray(1); + //arr.Add(llsdSimInfo); - OSDMap llsdBody = new OSDMap(1); - llsdBody.Add("SimulatorInfo", arr); + OSDMap llsdBody = new OSDMap(0); + //llsdBody.Add("SimulatorInfo", arr); return buildEvent("DisableSimulator", llsdBody); } @@ -178,6 +178,16 @@ namespace OpenSim.Region.Environment return buildEvent("ScriptRunningReply", body); } + public static OSD EstablishAgentCommunication(UUID agentID, string simIpAndPort, string seedcap) + { + OSDMap body = new OSDMap(3); + body.Add("agent-id", new OSDUUID(agentID)); + body.Add("sim-ip-and-port", new OSDString(simIpAndPort)); + body.Add("seed-capability", new OSDString(seedcap)); + + return buildEvent("EstablishAgentCommunication", body); + } + public static OSD KeepAliveEvent() { return buildEvent("FAKEEVENT", new OSDMap()); diff --git a/OpenSim/Region/Environment/Scenes/Hypergrid/HGSceneCommunicationService.cs b/OpenSim/Region/Environment/Scenes/Hypergrid/HGSceneCommunicationService.cs index 7505c75..4e94c52 100644 --- a/OpenSim/Region/Environment/Scenes/Hypergrid/HGSceneCommunicationService.cs +++ b/OpenSim/Region/Environment/Scenes/Hypergrid/HGSceneCommunicationService.cs @@ -108,6 +108,12 @@ namespace OpenSim.Region.Environment.Scenes.Hypergrid RegionInfo reg = RequestNeighbouringRegionInfo(regionHandle); if (reg != null) { + + uint newRegionX = (uint)(reg.RegionHandle >> 40); + uint newRegionY = (((uint)(reg.RegionHandle)) >> 8); + uint oldRegionX = (uint)(m_regionInfo.RegionHandle >> 40); + uint oldRegionY = (((uint)(m_regionInfo.RegionHandle)) >> 8); + /// /// Hypergrid mod start /// @@ -130,11 +136,6 @@ namespace OpenSim.Region.Environment.Scenes.Hypergrid if (eq == null) avatar.ControllingClient.SendTeleportLocationStart(); - AgentCircuitData agent = avatar.ControllingClient.RequestClientInfo(); - agent.BaseFolder = UUID.Zero; - agent.InventoryFolder = UUID.Zero; - agent.startpos = position; - agent.child = true; if (reg.RemotingAddress != "" && reg.RemotingPort != 0) { @@ -166,14 +167,42 @@ namespace OpenSim.Region.Environment.Scenes.Hypergrid // Compared to ScenePresence.CrossToNewRegion(), there's no obvious code to handle a teleport // failure at this point (unlike a border crossing failure). So perhaps this can never fail // once we reach here... - avatar.Scene.RemoveCapsHandler(avatar.UUID); - agent.child = false; - m_commsProvider.InterRegion.InformRegionOfChildAgent(reg.RegionHandle, agent); + //avatar.Scene.RemoveCapsHandler(avatar.UUID); + + AgentCircuitData agent = avatar.ControllingClient.RequestClientInfo(); + agent.BaseFolder = UUID.Zero; + agent.InventoryFolder = UUID.Zero; + agent.startpos = position; + agent.child = true; + if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) + { + // brand new agent + agent.CapsPath = Util.GetRandomCapsPath(); + } + else + { + // child agent already there + agent.CapsPath = avatar.Scene.GetChildSeed(avatar.UUID, reg.RegionHandle); + } + + if (!m_commsProvider.InterRegion.InformRegionOfChildAgent(reg.RegionHandle, agent)) + { + avatar.ControllingClient.SendTeleportFailed("Destination is not accepting teleports."); + return; + } + + // TODO Should construct this behind a method + string capsPath = + "http://" + reg.ExternalHostName + ":" + reg.HttpPort + + "/CAPS/" + agent.CapsPath + "0000/"; if (eq != null) { OSD Item = EventQueueHelper.EnableSimulator(realHandle, reg.ExternalEndPoint); eq.Enqueue(Item, avatar.UUID); + + Item = EventQueueHelper.EstablishAgentCommunication(avatar.UUID, reg.ExternalEndPoint.ToString(), capsPath); + eq.Enqueue(Item, avatar.UUID); } else { @@ -181,18 +210,21 @@ namespace OpenSim.Region.Environment.Scenes.Hypergrid // TODO: make Event Queue disablable! } - m_commsProvider.InterRegion.ExpectAvatarCrossing(reg.RegionHandle, avatar.ControllingClient.AgentId, - position, false); - Thread.Sleep(2000); - AgentCircuitData circuitdata = avatar.ControllingClient.RequestClientInfo(); + if (!m_commsProvider.InterRegion.ExpectAvatarCrossing(reg.RegionHandle, avatar.ControllingClient.AgentId, + position, false)) + { + avatar.ControllingClient.SendTeleportFailed("Problem with destination."); + // We should close that agent we just created over at destination... + List lst = new List(); + lst.Add(reg.RegionHandle); + SendCloseChildAgentAsync(avatar.UUID, lst); + return; + } - // TODO Should construct this behind a method - string capsPath = - "http://" + reg.ExternalHostName + ":" + reg.HttpPort - + "/CAPS/" + circuitdata.CapsPath + "0000/"; + Thread.Sleep(2000); m_log.DebugFormat( - "[CAPS]: Sending new CAPS seed url {0} to client {1}", capsPath, avatar.UUID); + "[CAPS]: Sending new CAPS seed url {0} to client {1}", agent.CapsPath, avatar.UUID); /// @@ -215,7 +247,7 @@ namespace OpenSim.Region.Environment.Scenes.Hypergrid /// avatar.MakeChildAgent(); - Thread.Sleep(7000); + Thread.Sleep(5000); avatar.CrossAttachmentsIntoNewRegion(reg.RegionHandle, true); if (KiPrimitive != null) { @@ -223,29 +255,22 @@ namespace OpenSim.Region.Environment.Scenes.Hypergrid } - uint newRegionX = (uint)(reg.RegionHandle >> 40); - uint newRegionY = (((uint)(reg.RegionHandle)) >> 8); - uint oldRegionX = (uint)(m_regionInfo.RegionHandle >> 40); - uint oldRegionY = (((uint)(m_regionInfo.RegionHandle)) >> 8); - // Let's close some children agents if (isHyperLink) // close them all - SendCloseChildAgentConnections(avatar.UUID, avatar.GetKnownRegionList()); + SendCloseChildAgentConnections(avatar.UUID, avatar.KnownChildRegionHandles); else // close just a few avatar.CloseChildAgents(newRegionX, newRegionY); - avatar.Close(); + //avatar.Close(); // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone /// /// Hypergrid mod: extra check for isHyperLink /// - //if ((Util.fast_distance2d((int)(newRegionX - oldRegionX), (int)(newRegionY - oldRegionY)) > 1) || isHyperLink) - //if (((int)Math.Abs((int)(newRegionX - oldRegionX)) > 1) || ((int)Math.Abs((int)(newRegionY - oldRegionY)) > 1) || isHyperLink) - if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) - { - CloseConnection(avatar.UUID); - } + //if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) + //{ + // CloseConnection(avatar.UUID); + //} // if (teleport success) // seems to be always success here // the user may change their profile information in other region, // so the userinfo in UserProfileCache is not reliable any more, delete it diff --git a/OpenSim/Region/Environment/Scenes/Scene.cs b/OpenSim/Region/Environment/Scenes/Scene.cs index 4087471..6ce19c6 100644 --- a/OpenSim/Region/Environment/Scenes/Scene.cs +++ b/OpenSim/Region/Environment/Scenes/Scene.cs @@ -649,8 +649,8 @@ namespace OpenSim.Region.Environment.Scenes // Kick all ROOT agents with the message, 'The simulator is going down' ForEachScenePresence(delegate(ScenePresence avatar) { - if (avatar.KnownChildRegions.Contains(RegionInfo.RegionHandle)) - avatar.KnownChildRegions.Remove(RegionInfo.RegionHandle); + if (avatar.KnownChildRegionHandles.Contains(RegionInfo.RegionHandle)) + avatar.KnownChildRegionHandles.Remove(RegionInfo.RegionHandle); if (!avatar.IsChildAgent) avatar.ControllingClient.Kick("The simulator is going down."); @@ -2657,7 +2657,7 @@ namespace OpenSim.Region.Environment.Scenes GetAvatarAppearance(client, out appearance); ScenePresence avatar = m_sceneGraph.CreateAndAddChildScenePresence(client, appearance); - + avatar.KnownRegions = GetChildrenSeeds(avatar.UUID); return avatar; } @@ -2706,27 +2706,23 @@ namespace OpenSim.Region.Environment.Scenes "[SCENE]: Removing {0} agent {1} from region {2}", (childagentYN ? "child" : "root"), agentID, RegionInfo.RegionName); - if (avatar.IsChildAgent) - { - m_sceneGraph.removeUserCount(false); - } - else + m_sceneGraph.removeUserCount(!childagentYN); + RemoveCapsHandler(agentID); + + CommsManager.UserProfileCacheService.RemoveUser(agentID); + + if (!avatar.IsChildAgent) { - m_sceneGraph.removeUserCount(true); m_sceneGridService.LogOffUser(agentID, RegionInfo.RegionID, RegionInfo.RegionHandle, avatar.AbsolutePosition, avatar.Lookat); - List childknownRegions = new List(); - List ckn = avatar.GetKnownRegionList(); - for (int i = 0; i < ckn.Count; i++) - { - childknownRegions.Add(ckn[i]); - } - m_sceneGridService.SendCloseChildAgentConnections(agentID, childknownRegions); - - RemoveCapsHandler(agentID); + //List childknownRegions = new List(); + //List ckn = avatar.KnownChildRegionHandles; + //for (int i = 0; i < ckn.Count; i++) + //{ + // childknownRegions.Add(ckn[i]); + //} + m_sceneGridService.SendCloseChildAgentConnections(agentID, avatar.KnownChildRegionHandles); - CommsManager.UserProfileCacheService.RemoveUser(agentID); } - m_eventManager.TriggerClientClosed(agentID); } catch (NullReferenceException) @@ -2792,7 +2788,7 @@ namespace OpenSim.Region.Environment.Scenes for (int i = 0; i < regionslst.Count; i++) { - av.KnownChildRegions.Remove(regionslst[i]); + av.KnownChildRegionHandles.Remove(regionslst[i]); } } } @@ -2875,8 +2871,8 @@ namespace OpenSim.Region.Environment.Scenes /// public void NewUserConnection(AgentCircuitData agent) { - m_log.DebugFormat("[CONNECTION DEBUGGING] Adding NewUserConnection for {0} with CC of {1}", agent.AgentID, - agent.circuitcode); + m_log.DebugFormat("[CONNECTION DEBUGGING] Adding NewUserConnection for {0} in {1} with CC of {2}", agent.AgentID, + RegionInfo.RegionName, agent.circuitcode); if (m_regInfo.EstateSettings.IsBanned(agent.AgentID)) { @@ -2885,13 +2881,16 @@ namespace OpenSim.Region.Environment.Scenes agent.AgentID, RegionInfo.RegionName); } + /// Diva: Horrible stuff! capsPaths[agent.AgentID] = agent.CapsPath; + //m_log.DebugFormat("------------>child seeds in {0}: {1}", RegionInfo.RegionName, ((agent.ChildrenCapSeeds == null) ? "null" : agent.ChildrenCapSeeds.Count.ToString())); + childrenSeeds[agent.AgentID] = ((agent.ChildrenCapSeeds == null) ? new Dictionary() : agent.ChildrenCapSeeds); + + AddCapsHandler(agent.AgentID); if (!agent.child) { - AddCapsHandler(agent.AgentID); - // Honor parcel landing type and position. ILandObject land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y); if (land != null) @@ -2966,14 +2965,22 @@ namespace OpenSim.Region.Environment.Scenes { if (RegionInfo.EstateSettings.IsBanned(agentId)) return; + + String capsObjectPath = GetCapsPath(agentId); m_log.DebugFormat( - "[CAPS]: Setting up CAPS handler for root agent {0} in {1}", + "[CAPS]: Setting up CAPS handler for agent {0} in {1}", agentId, RegionInfo.RegionName); - Caps cap = - new Caps(AssetCache, m_httpListener, m_regInfo.ExternalHostName, m_httpListener.Port, + Caps cap = null; + if (m_capsHandlers.TryGetValue(agentId, out cap)) + { + m_log.DebugFormat("[CAPS] Attempt at registering twice for the same agent {0}. {1}. Ignoring.", agentId, capsObjectPath); + return; + } + + cap = new Caps(AssetCache, m_httpListener, m_regInfo.ExternalHostName, m_httpListener.Port, capsObjectPath, agentId, m_dumpAssetsToFile, RegionInfo.RegionName); cap.RegisterHandlers(); @@ -3005,6 +3012,12 @@ namespace OpenSim.Region.Environment.Scenes /// public void RemoveCapsHandler(UUID agentId) { + if (childrenSeeds.ContainsKey(agentId)) + { + //Console.WriteLine(" !!! Removing seeds for {0} in {1}", agentId, RegionInfo.RegionName); + childrenSeeds.Remove(agentId); + } + lock (m_capsHandlers) { if (m_capsHandlers.ContainsKey(agentId)) @@ -3094,7 +3107,6 @@ namespace OpenSim.Region.Environment.Scenes public bool CloseConnection(UUID agentID) { ScenePresence presence = m_sceneGraph.GetScenePresence(agentID); - if (presence != null) { // Nothing is removed here, so down count it as such @@ -3108,11 +3120,18 @@ namespace OpenSim.Region.Environment.Scenes // } // Tell a single agent to disconnect from the region. - presence.ControllingClient.SendShutdownConnectionNotice(); + IEventQueue eq = RequestModuleInterface(); + if (eq != null) + { + OSD Item = EventQueueHelper.DisableSimulator(RegionInfo.RegionHandle); + eq.Enqueue(Item, agentID); + } + else + presence.ControllingClient.SendShutdownConnectionNotice(); presence.ControllingClient.Close(true); + } - return true; } diff --git a/OpenSim/Region/Environment/Scenes/SceneBase.cs b/OpenSim/Region/Environment/Scenes/SceneBase.cs index 9528a0d..fafa933 100644 --- a/OpenSim/Region/Environment/Scenes/SceneBase.cs +++ b/OpenSim/Region/Environment/Scenes/SceneBase.cs @@ -216,8 +216,10 @@ namespace OpenSim.Region.Environment.Scenes /// /// XXX These two methods are very temporary + /// XXX Diva: this is really truly horrible; must...move...to...LL client...stack... /// protected Dictionary capsPaths = new Dictionary(); + protected Dictionary> childrenSeeds = new Dictionary>(); public string GetCapsPath(UUID agentId) { if (capsPaths.ContainsKey(agentId)) @@ -227,7 +229,52 @@ namespace OpenSim.Region.Environment.Scenes return null; } - + public Dictionary GetChildrenSeeds(UUID agentID) + { + Dictionary seeds = null; + if (childrenSeeds.TryGetValue(agentID, out seeds)) + return seeds; + return new Dictionary(); + } + + public void DropChildSeed(UUID agentID, ulong handle) + { + Dictionary seeds; + if (childrenSeeds.TryGetValue(agentID, out seeds)) + { + seeds.Remove(handle); + } + } + + public string GetChildSeed(UUID agentID, ulong handle) + { + Dictionary seeds; + if (childrenSeeds.TryGetValue(agentID, out seeds)) + { + return seeds[handle]; + } + return null; + } + + public void SetChildrenSeed(UUID agentID, Dictionary value) + { + //Console.WriteLine(" !!! Setting child seeds in {0} to {1}", RegionInfo.RegionName, value.Count); + childrenSeeds[agentID] = value; + } + + public void DumpChildrenSeeds(UUID agentID) + { + Console.WriteLine("================ ChildrenSeed {0} ================", RegionInfo.RegionName); + foreach (KeyValuePair kvp in childrenSeeds[agentID]) + { + uint x, y; + Utils.LongToUInts(kvp.Key, out x, out y); + x = x / Constants.RegionSize; + y = y / Constants.RegionSize; + Console.WriteLine(" >> {0}, {1}: {2}", x, y, kvp.Value); + } + } + /// /// Returns a new unallocated local ID /// diff --git a/OpenSim/Region/Environment/Scenes/SceneCommunicationService.cs b/OpenSim/Region/Environment/Scenes/SceneCommunicationService.cs index 2b638e3..b89f753 100644 --- a/OpenSim/Region/Environment/Scenes/SceneCommunicationService.cs +++ b/OpenSim/Region/Environment/Scenes/SceneCommunicationService.cs @@ -255,7 +255,7 @@ namespace OpenSim.Region.Environment.Scenes #region Inform Client of Neighbours private delegate void InformClientOfNeighbourDelegate( - ScenePresence avatar, AgentCircuitData a, ulong regionHandle, IPEndPoint endPoint); + ScenePresence avatar, AgentCircuitData a, SimpleRegionInfo reg, IPEndPoint endPoint); private void InformClientOfNeighbourCompleted(IAsyncResult iar) { @@ -273,28 +273,40 @@ namespace OpenSim.Region.Environment.Scenes /// /// /// - private void InformClientOfNeighbourAsync(ScenePresence avatar, AgentCircuitData a, ulong regionHandle, + private void InformClientOfNeighbourAsync(ScenePresence avatar, AgentCircuitData a, SimpleRegionInfo reg, IPEndPoint endPoint) { - m_log.Info("[INTERGRID]: Starting to inform client about neighbours"); - bool regionAccepted = m_commsProvider.InterRegion.InformRegionOfChildAgent(regionHandle, a); + uint x, y; + Utils.LongToUInts(reg.RegionHandle, out x, out y); + x = x / Constants.RegionSize; + y = y / Constants.RegionSize; + m_log.Info("[INTERGRID]: Starting to inform client about neighbour " + x + ", " + y + "(" + endPoint.ToString() + ")"); + + string capsPath = "http://" + reg.ExternalHostName + ":" + reg.HttpPort + + "/CAPS/" + a.CapsPath + "0000/"; + + bool regionAccepted = m_commsProvider.InterRegion.InformRegionOfChildAgent(reg.RegionHandle, a); if (regionAccepted) { IEventQueue eq = avatar.Scene.RequestModuleInterface(); if (eq != null) { - OSD Item = EventQueueHelper.EnableSimulator(regionHandle, endPoint); + OSD Item = EventQueueHelper.EnableSimulator(reg.RegionHandle, endPoint); eq.Enqueue(Item, avatar.UUID); + + Item = EventQueueHelper.EstablishAgentCommunication(avatar.UUID, endPoint.ToString(), capsPath); + eq.Enqueue(Item, avatar.UUID); + + m_log.DebugFormat("[CAPS]: Sending new CAPS seed url {0} to client {1} in region {2}", capsPath, avatar.UUID, avatar.Scene.RegionInfo.RegionName); } else { - avatar.ControllingClient.InformClientOfNeighbour(regionHandle, endPoint); + avatar.ControllingClient.InformClientOfNeighbour(reg.RegionHandle, endPoint); // TODO: make Event Queue disablable! } - - avatar.AddNeighbourRegion(regionHandle); - m_log.Info("[INTERGRID]: Completed inform client about neighbours"); + + m_log.Info("[INTERGRID]: Completed inform client about neighbour " + endPoint.ToString()); } } @@ -330,21 +342,72 @@ namespace OpenSim.Region.Environment.Scenes neighbours = m_commsProvider.GridService.RequestNeighbours(m_regionInfo.RegionLocX, m_regionInfo.RegionLocY); - if (neighbours != null) + m_log.Debug("[SCM]: EnableChildAgents from " + avatar.Scene.RegionInfo.RegionName); + + /// We need to find the difference between the new regions where there are no child agents + /// and the regions where there are already child agents. We only send notification to the former. + List neighbourHandles = NeighbourHandles(neighbours); // on this region + neighbourHandles.Add(avatar.Scene.RegionInfo.RegionHandle); // add this region too + List previousRegionNeighbourHandles = new List(avatar.Scene.GetChildrenSeeds(avatar.UUID).Keys); + List newRegions = NewNeighbours(neighbourHandles, previousRegionNeighbourHandles); + List oldRegions = OldNeighbours(neighbourHandles, previousRegionNeighbourHandles); + + //Dump("Current Neighbors", neighbourHandles); + //Dump("Previous Neighbours", previousRegionNeighbourHandles); + //Dump("New Neighbours", newRegions); + //Dump("Old Neighbours", oldRegions); + + /// Update the scene presence's known regions here on this region + avatar.DropOldNeighbours(oldRegions); + + /// Collect as many seeds as possible + Dictionary seeds = new Dictionary(avatar.Scene.GetChildrenSeeds(avatar.UUID)); + if (!seeds.ContainsKey(avatar.Scene.RegionInfo.RegionHandle)) + seeds.Add(avatar.Scene.RegionInfo.RegionHandle, avatar.ControllingClient.RequestClientInfo().CapsPath); + + /// Create the necessary child agents + List cagents = new List(); + foreach (SimpleRegionInfo neighbour in neighbours) { - for (int i = 0; i < neighbours.Count; i++) + AgentCircuitData agent = avatar.ControllingClient.RequestClientInfo(); + agent.BaseFolder = UUID.Zero; + agent.InventoryFolder = UUID.Zero; + agent.startpos = new Vector3(128, 128, 70); + agent.child = true; + + if (newRegions.Contains(neighbour.RegionHandle)) { - AgentCircuitData agent = avatar.ControllingClient.RequestClientInfo(); - agent.BaseFolder = UUID.Zero; - agent.InventoryFolder = UUID.Zero; - agent.startpos = new Vector3(128, 128, 70); - agent.child = true; + agent.CapsPath = Util.GetRandomCapsPath(); + avatar.AddNeighbourRegion(neighbour.RegionHandle, agent.CapsPath); + seeds.Add(neighbour.RegionHandle, agent.CapsPath); + } + else + agent.CapsPath = avatar.Scene.GetChildSeed(avatar.UUID, neighbour.RegionHandle); - InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync; + cagents.Add(agent); + } + /// Update all child agent with everyone's seeds + foreach (AgentCircuitData a in cagents) + { + a.ChildrenCapSeeds = new Dictionary(seeds); + } + // These two are the same thing! + avatar.Scene.SetChildrenSeed(avatar.UUID, seeds); + avatar.KnownRegions = seeds; + //avatar.Scene.DumpChildrenSeeds(avatar.UUID); + //avatar.DumpKnownRegions(); + + int count = 0; + foreach (SimpleRegionInfo neighbour in neighbours) + { + // Don't do it if there's already an agent in that region + if (newRegions.Contains(neighbour.RegionHandle)) + { + InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync; try { - d.BeginInvoke(avatar, agent, neighbours[i].RegionHandle, neighbours[i].ExternalEndPoint, + d.BeginInvoke(avatar, cagents[count], neighbour, neighbour.ExternalEndPoint, InformClientOfNeighbourCompleted, d); } @@ -352,10 +415,10 @@ namespace OpenSim.Region.Environment.Scenes { m_log.ErrorFormat( "[REGIONINFO]: Could not resolve external hostname {0} for region {1} ({2}, {3}). {4}", - neighbours[i].ExternalHostName, - neighbours[i].RegionHandle, - neighbours[i].RegionLocX, - neighbours[i].RegionLocY, + neighbour.ExternalHostName, + neighbour.RegionHandle, + neighbour.RegionLocX, + neighbour.RegionLocY, e); // FIXME: Okay, even though we've failed, we're still going to throw the exception on, @@ -366,6 +429,11 @@ namespace OpenSim.Region.Environment.Scenes } } + else + m_log.Debug("[SCM]: Skipping common neighbor " + neighbour.RegionLocX + ", " + neighbour.RegionLocY); + + count++; + } } @@ -373,7 +441,7 @@ namespace OpenSim.Region.Environment.Scenes /// This informs a single neighboring region about agent "avatar". /// Calls an asynchronous method to do so.. so it doesn't lag the sim. /// - public void InformNeighborChildAgent(ScenePresence avatar, RegionInfo region, List neighbours) + public void InformNeighborChildAgent(ScenePresence avatar, SimpleRegionInfo region, List neighbours) { AgentCircuitData agent = avatar.ControllingClient.RequestClientInfo(); agent.BaseFolder = UUID.Zero; @@ -382,7 +450,7 @@ namespace OpenSim.Region.Environment.Scenes agent.child = true; InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync; - d.BeginInvoke(avatar, agent, region.RegionHandle, region.ExternalEndPoint, + d.BeginInvoke(avatar, agent, region, region.ExternalEndPoint, InformClientOfNeighbourCompleted, d); } @@ -463,10 +531,10 @@ namespace OpenSim.Region.Environment.Scenes /// private void SendChildAgentDataUpdateAsync(ChildAgentDataUpdate cAgentData, ScenePresence presence) { - //m_log.Info("[INTERGRID]: Informing neighbors about my agent."); + m_log.Info("[INTERGRID]: Informing neighbors about my agent in " + presence.Scene.RegionInfo.RegionName); try { - foreach (ulong regionHandle in presence.KnownChildRegions) + foreach (ulong regionHandle in presence.KnownChildRegionHandles) { bool regionAccepted = m_commsProvider.InterRegion.ChildAgentUpdate(regionHandle, cAgentData); @@ -508,11 +576,12 @@ namespace OpenSim.Region.Environment.Scenes /// This Closes child agents on neighboring regions /// Calls an asynchronous method to do so.. so it doesn't lag the sim. /// - private void SendCloseChildAgentAsync(UUID agentID, List regionlst) + protected void SendCloseChildAgentAsync(UUID agentID, List regionlst) { foreach (ulong regionHandle in regionlst) { + m_log.Debug("[INTERGRID]: Sending close agent to " + regionHandle); bool regionAccepted = m_commsProvider.InterRegion.TellRegionToCloseChildConnection(regionHandle, agentID); if (regionAccepted) @@ -527,14 +596,14 @@ namespace OpenSim.Region.Environment.Scenes } } - // We remove the list of known regions from the agent's known region list through an event - // to scene, because, if an agent logged of, it's likely that there will be no scene presence - // by the time we get to this part of the method. - handlerRemoveKnownRegionFromAvatar = OnRemoveKnownRegionFromAvatar; - if (handlerRemoveKnownRegionFromAvatar != null) - { - handlerRemoveKnownRegionFromAvatar(agentID, regionlst); - } + //// We remove the list of known regions from the agent's known region list through an event + //// to scene, because, if an agent logged of, it's likely that there will be no scene presence + //// by the time we get to this part of the method. + //handlerRemoveKnownRegionFromAvatar = OnRemoveKnownRegionFromAvatar; + //if (handlerRemoveKnownRegionFromAvatar != null) + //{ + // handlerRemoveKnownRegionFromAvatar(agentID, regionlst); + //} } private void SendCloseChildAgentCompleted(IAsyncResult iar) @@ -643,12 +712,6 @@ namespace OpenSim.Region.Environment.Scenes if (eq == null) avatar.ControllingClient.SendTeleportLocationStart(); - AgentCircuitData agent = avatar.ControllingClient.RequestClientInfo(); - agent.BaseFolder = UUID.Zero; - agent.InventoryFolder = UUID.Zero; - agent.startpos = position; - agent.child = true; - if (reg.RemotingAddress != "" && reg.RemotingPort != 0) { // region is remote. see if it is up @@ -662,6 +725,10 @@ namespace OpenSim.Region.Environment.Scenes if (destRegionUp) { + uint newRegionX = (uint)(reg.RegionHandle >> 40); + uint newRegionY = (((uint)(reg.RegionHandle)) >> 8); + uint oldRegionX = (uint)(m_regionInfo.RegionHandle >> 40); + uint oldRegionY = (((uint)(m_regionInfo.RegionHandle)) >> 8); // Fixing a bug where teleporting while sitting results in the avatar ending up removed from // both regions @@ -675,33 +742,64 @@ namespace OpenSim.Region.Environment.Scenes // the avatar.Close below will clear the child region list. We need this below for (possibly) // closing the child agents, so save it here (we need a copy as it is Clear()-ed). - List childRegions = new List(avatar.GetKnownRegionList()); + //List childRegions = new List(avatar.GetKnownRegionList()); // Compared to ScenePresence.CrossToNewRegion(), there's no obvious code to handle a teleport // failure at this point (unlike a border crossing failure). So perhaps this can never fail // once we reach here... - avatar.Scene.RemoveCapsHandler(avatar.UUID); - agent.child = false; - m_commsProvider.InterRegion.InformRegionOfChildAgent(reg.RegionHandle, agent); + //avatar.Scene.RemoveCapsHandler(avatar.UUID); + + AgentCircuitData agent = avatar.ControllingClient.RequestClientInfo(); + agent.BaseFolder = UUID.Zero; + agent.InventoryFolder = UUID.Zero; + agent.startpos = position; + agent.child = true; + if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) + { + // brand new agent + agent.CapsPath = Util.GetRandomCapsPath(); + } + else + { + // child agent already there + agent.CapsPath = avatar.Scene.GetChildSeed(avatar.UUID, reg.RegionHandle); + } + + if (!m_commsProvider.InterRegion.InformRegionOfChildAgent(reg.RegionHandle, agent)) + { + avatar.ControllingClient.SendTeleportFailed("Destination is not accepting teleports."); + return; + } + + // TODO Should construct this behind a method + string capsPath = + "http://" + reg.ExternalHostName + ":" + reg.HttpPort + + "/CAPS/" + agent.CapsPath + "0000/"; if (eq != null) { OSD Item = EventQueueHelper.EnableSimulator(reg.RegionHandle, reg.ExternalEndPoint); eq.Enqueue(Item, avatar.UUID); + + Item = EventQueueHelper.EstablishAgentCommunication(avatar.UUID, reg.ExternalEndPoint.ToString(), capsPath); + eq.Enqueue(Item, avatar.UUID); } else { avatar.ControllingClient.InformClientOfNeighbour(reg.RegionHandle, reg.ExternalEndPoint); } - - m_commsProvider.InterRegion.ExpectAvatarCrossing(reg.RegionHandle, avatar.ControllingClient.AgentId, - position, false); - Thread.Sleep(2000); - AgentCircuitData circuitdata = avatar.ControllingClient.RequestClientInfo(); - // TODO Should construct this behind a method - string capsPath = - "http://" + reg.ExternalHostName + ":" + reg.HttpPort - + "/CAPS/" + circuitdata.CapsPath + "0000/"; + if (!m_commsProvider.InterRegion.ExpectAvatarCrossing(reg.RegionHandle, avatar.ControllingClient.AgentId, + position, false)) + { + avatar.ControllingClient.SendTeleportFailed("Problem with destination."); + // We should close that agent we just created over at destination... + List lst = new List(); + lst.Add(reg.RegionHandle); + SendCloseChildAgentAsync(avatar.UUID, lst); + return; + } + + Thread.Sleep(2000); m_log.DebugFormat( "[CAPS]: Sending new CAPS seed url {0} to client {1}", capsPath, avatar.UUID); @@ -720,26 +818,25 @@ namespace OpenSim.Region.Environment.Scenes } avatar.MakeChildAgent(); - Thread.Sleep(7000); + Thread.Sleep(5000); avatar.CrossAttachmentsIntoNewRegion(reg.RegionHandle, true); if (KiPrimitive != null) { KiPrimitive(avatar.LocalId); } - avatar.Close(); - uint newRegionX = (uint)(reg.RegionHandle >> 40); - uint newRegionY = (((uint)(reg.RegionHandle)) >> 8); - uint oldRegionX = (uint)(m_regionInfo.RegionHandle >> 40); - uint oldRegionY = (((uint)(m_regionInfo.RegionHandle)) >> 8); + // Let's close some children agents + avatar.CloseChildAgents(newRegionX, newRegionY); + // Close this ScenePresence too + //avatar.Close(); - if (Util.fast_distance2d((int)(newRegionX - oldRegionX), (int)(newRegionY - oldRegionY)) > 3) - { - //SendCloseChildAgentConnections(avatar.UUID,avatar.GetKnownRegionList()); - SendCloseChildAgentConnections(avatar.UUID, childRegions); - CloseConnection(avatar.UUID); - } + // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone + + //if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) + //{ + // CloseConnection(avatar.UUID); + //} // if (teleport success) // seems to be always success here // the user may change their profile information in other region, @@ -775,6 +872,30 @@ namespace OpenSim.Region.Environment.Scenes } } + private List NeighbourHandles(List neighbours) + { + List handles = new List(); + foreach (SimpleRegionInfo reg in neighbours) + { + handles.Add(reg.RegionHandle); + } + return handles; + } + + private List NewNeighbours(List currentNeighbours, List previousNeighbours) + { + return currentNeighbours.FindAll(delegate(ulong handle) { return !previousNeighbours.Contains(handle); }); + } + + private List CommonNeighbours(List currentNeighbours, List previousNeighbours) + { + return currentNeighbours.FindAll(delegate(ulong handle) { return previousNeighbours.Contains(handle); }); + } + + private List OldNeighbours(List currentNeighbours, List previousNeighbours) + { + return previousNeighbours.FindAll(delegate(ulong handle) { return !currentNeighbours.Contains(handle); }); + } /// /// Inform a neighbouring region that an avatar is about to cross into it. /// @@ -846,5 +967,18 @@ namespace OpenSim.Region.Environment.Scenes { return m_commsProvider.GridService.RequestNamedRegions(name, maxNumber); } + + private void Dump(string msg, List handles) + { + Console.WriteLine("-------------- HANDLE DUMP ({0}) ---------", msg); + foreach (ulong handle in handles) + { + uint x, y; + Utils.LongToUInts(handle, out x, out y); + x = x / Constants.RegionSize; + y = y / Constants.RegionSize; + Console.WriteLine("({0}, {1})", x, y); + } + } } } diff --git a/OpenSim/Region/Environment/Scenes/SceneGraph.cs b/OpenSim/Region/Environment/Scenes/SceneGraph.cs index aed01df..b373c09 100644 --- a/OpenSim/Region/Environment/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Environment/Scenes/SceneGraph.cs @@ -911,17 +911,20 @@ namespace OpenSim.Region.Environment.Scenes ScenePresence presence; if (ScenePresences.TryGetValue(avatarId, out presence)) { - if (!presence.IsChildAgent) - { - avatar = presence; - return true; - } - else - { - m_log.WarnFormat( - "[INNER SCENE]: Requested avatar {0} could not be found in scene {1} since it is only registered as a child agent!", - avatarId, m_parentScene.RegionInfo.RegionName); - } + avatar = presence; + return true; + + //if (!presence.IsChildAgent) + //{ + // avatar = presence; + // return true; + //} + //else + //{ + // m_log.WarnFormat( + // "[INNER SCENE]: Requested avatar {0} could not be found in scene {1} since it is only registered as a child agent!", + // avatarId, m_parentScene.RegionInfo.RegionName); + //} } avatar = null; diff --git a/OpenSim/Region/Environment/Scenes/ScenePresence.cs b/OpenSim/Region/Environment/Scenes/ScenePresence.cs index 3bd6be9..f139ba5 100644 --- a/OpenSim/Region/Environment/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Environment/Scenes/ScenePresence.cs @@ -188,8 +188,9 @@ namespace OpenSim.Region.Environment.Scenes protected List m_attachments = new List(); - //neighbouring regions we have enabled a child agent in - private readonly List m_knownChildRegions = new List(); + // neighbouring regions we have enabled a child agent in + // holds the seed cap for the child agent in that region + private Dictionary m_knownChildRegions = new Dictionary(); /// /// Implemented Control Flags @@ -483,9 +484,38 @@ namespace OpenSim.Region.Environment.Scenes /// /// These are the region handles known by the avatar. /// - public List KnownChildRegions + public List KnownChildRegionHandles + { + get + { + if (m_knownChildRegions.Count == 0) + return new List(); + else + return new List(m_knownChildRegions.Keys); + } + } + + public Dictionary KnownRegions { get { return m_knownChildRegions; } + set + { + //Console.WriteLine(" !! Setting known regions in {0} to {1}", Scene.RegionInfo.RegionName, value.Count); + m_knownChildRegions = value; + } + } + + public void DumpKnownRegions() + { + Console.WriteLine("================ KnownRegions {0} ================", Scene.RegionInfo.RegionName); + foreach (KeyValuePair kvp in KnownRegions) + { + uint x, y; + Utils.LongToUInts(kvp.Key, out x, out y); + x = x / Constants.RegionSize; + y = y / Constants.RegionSize; + Console.WriteLine(" >> {0}, {1}: {2}", x, y, kvp.Value); + } } public AnimationSet Animations @@ -529,6 +559,8 @@ namespace OpenSim.Region.Environment.Scenes CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(m_uuid); if (userInfo != null) userInfo.OnItemReceived += ItemReceived; + + m_log.Info("[AVATAR]: New ScenePresence in " + Scene.RegionInfo.RegionName); } public ScenePresence(IClientAPI client, Scene world, RegionInfo reginfo, byte[] visualParams, @@ -742,7 +774,9 @@ namespace OpenSim.Region.Environment.Scenes m_log.DebugFormat( "[SCENE]: Upgrading child to root agent for {0} in {1}", Name, m_scene.RegionInfo.RegionName); - + + m_log.DebugFormat("[SCENE]: known regions in {0}: {1}", Scene.RegionInfo.RegionName, KnownChildRegionHandles.Count); + IGroupsModule gm = m_scene.RequestModuleInterface(); if (gm != null) m_grouptitle = gm.GetGroupTitle(m_uuid); @@ -881,25 +915,43 @@ namespace OpenSim.Region.Environment.Scenes SendFullUpdateToAllClients(); } - public void AddNeighbourRegion(ulong regionHandle) + public void AddNeighbourRegion(ulong regionHandle, string cap) { - if (!m_knownChildRegions.Contains(regionHandle)) + lock (m_knownChildRegions) { - m_knownChildRegions.Add(regionHandle); + if (!m_knownChildRegions.ContainsKey(regionHandle)) + { + uint x, y; + Utils.LongToUInts(regionHandle, out x, out y); + m_knownChildRegions.Add(regionHandle, cap); + } } } public void RemoveNeighbourRegion(ulong regionHandle) { - if (m_knownChildRegions.Contains(regionHandle)) + lock (m_knownChildRegions) { - m_knownChildRegions.Remove(regionHandle); + if (m_knownChildRegions.ContainsKey(regionHandle)) + { + m_knownChildRegions.Remove(regionHandle); + //Console.WriteLine(" !!! removing known region {0} in {1}. Count = {2}", regionHandle, Scene.RegionInfo.RegionName, m_knownChildRegions.Count); + } + } + } + + public void DropOldNeighbours(List oldRegions) + { + foreach (ulong handle in oldRegions) + { + RemoveNeighbourRegion(handle); + Scene.DropChildSeed(UUID, handle); } } public List GetKnownRegionList() { - return m_knownChildRegions; + return new List(m_knownChildRegions.Keys); } #endregion @@ -1856,6 +1908,7 @@ namespace OpenSim.Region.Environment.Scenes #region Overridden Methods + int x = 0; public override void Update() { SendPrimUpdates(); @@ -1896,6 +1949,9 @@ namespace OpenSim.Region.Environment.Scenes CheckForBorderCrossing(); CheckForSignificantMovement(); // sends update to the modules. } + + //if ((x++ % 30) == 0) + // Console.WriteLine(" >> In {0} known regions: {0}, seeds:{1}", Scene.RegionInfo.RegionName, KnownRegions.Count, Scene.GetChildrenSeeds(UUID)); } #endregion @@ -2338,7 +2394,7 @@ namespace OpenSim.Region.Environment.Scenes // When the neighbour is informed of the border crossing, it will set up CAPS handlers for the avatar // This means we need to remove the current caps handler here and possibly compensate later, // in case both scenes are being hosted on the same region server. Messy - m_scene.RemoveCapsHandler(UUID); + //m_scene.RemoveCapsHandler(UUID); newpos = newpos + (vel); CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(UUID); @@ -2358,10 +2414,14 @@ namespace OpenSim.Region.Environment.Scenes { AgentCircuitData circuitdata = m_controllingClient.RequestClientInfo(); + //Console.WriteLine("BEFORE CROSS"); + //Scene.DumpChildrenSeeds(UUID); + //DumpKnownRegions(); + // TODO Should construct this behind a method string capsPath = "http://" + neighbourRegion.ExternalHostName + ":" + neighbourRegion.HttpPort - + "/CAPS/" + circuitdata.CapsPath + "0000/"; + + "/CAPS/" + m_knownChildRegions[neighbourRegion.RegionHandle] /*circuitdata.CapsPath*/ + "0000/"; m_log.DebugFormat("[CAPS]: Sending new CAPS seed url {0} to client {1}", capsPath, m_uuid); @@ -2386,6 +2446,8 @@ namespace OpenSim.Region.Environment.Scenes CrossAttachmentsIntoNewRegion(neighbourHandle, true); // m_scene.SendKillObject(m_localId); + // Next, let's close the child agent connections that are too far away. + CloseChildAgents(neighbourx, neighboury); m_scene.NotifyMyCoarseLocationChange(); // the user may change their profile information in other region, @@ -2401,6 +2463,11 @@ namespace OpenSim.Region.Environment.Scenes m_scene.AddCapsHandler(UUID); } } + + //Console.WriteLine("AFTER CROSS"); + //Scene.DumpChildrenSeeds(UUID); + //DumpKnownRegions(); + } /// @@ -2413,31 +2480,36 @@ namespace OpenSim.Region.Environment.Scenes public void CloseChildAgents(uint newRegionX, uint newRegionY) { List byebyeRegions = new List(); + m_log.DebugFormat("[AVATAR]: Closing child agents. Checking {0} regions in {1}", m_knownChildRegions.Keys.Count, Scene.RegionInfo.RegionName); + //DumpKnownRegions(); - foreach (ulong handle in m_knownChildRegions) + lock (m_knownChildRegions) { - uint x, y; - Utils.LongToUInts(handle, out x, out y); - x = x / Constants.RegionSize; - y = y / Constants.RegionSize; - - if (Util.IsOutsideView(x, newRegionX, y, newRegionY)) + foreach (ulong handle in m_knownChildRegions.Keys) { - Console.WriteLine("---> x: " + x + "; newx:" + newRegionX + "; Abs:" + (int)Math.Abs(x-newRegionX)); - Console.WriteLine("---> y: " + y + "; newy:" + newRegionY); - byebyeRegions.Add(handle); + uint x, y; + Utils.LongToUInts(handle, out x, out y); + x = x / Constants.RegionSize; + y = y / Constants.RegionSize; + + //Console.WriteLine("---> x: " + x + "; newx:" + newRegionX + "; Abs:" + (int)Math.Abs((int)(x - newRegionX))); + //Console.WriteLine("---> y: " + y + "; newy:" + newRegionY + "; Abs:" + (int)Math.Abs((int)(y - newRegionY))); + if (Util.IsOutsideView(x, newRegionX, y, newRegionY)) + { + byebyeRegions.Add(handle); + } } } - foreach (ulong handle in byebyeRegions) - { - RemoveNeighbourRegion(handle); - } - if (byebyeRegions.Count > 0) { m_log.Info("[AVATAR]: Closing " + byebyeRegions.Count + " child agents"); m_scene.SceneGridService.SendCloseChildAgentConnections(m_controllingClient.AgentId, byebyeRegions); } + foreach (ulong handle in byebyeRegions) + { + RemoveNeighbourRegion(handle); + } + } @@ -2940,7 +3012,8 @@ namespace OpenSim.Region.Environment.Scenes m_DrawDistance = (float)info.GetValue("m_DrawDistance", typeof(float)); m_appearance = (AvatarAppearance)info.GetValue("m_appearance", typeof(AvatarAppearance)); - m_knownChildRegions = (List)info.GetValue("m_knownChildRegions", typeof(List)); + + m_knownChildRegions = (Dictionary)info.GetValue("m_knownChildRegions", typeof(Dictionary)); posLastSignificantMove = new Vector3( -- cgit v1.1