aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
authordiva2009-02-12 23:23:44 +0000
committerdiva2009-02-12 23:23:44 +0000
commit7a274a7e1dfa651c17cb33ca1994f321ccddc005 (patch)
tree2864e53e24a97c3d77a83f2b2d852bf1119415d9 /OpenSim
parent* Make it possible to load and save inventory archives while a user is not lo... (diff)
downloadopensim-SC-7a274a7e1dfa651c17cb33ca1994f321ccddc005.zip
opensim-SC-7a274a7e1dfa651c17cb33ca1994f321ccddc005.tar.gz
opensim-SC-7a274a7e1dfa651c17cb33ca1994f321ccddc005.tar.bz2
opensim-SC-7a274a7e1dfa651c17cb33ca1994f321ccddc005.tar.xz
Makes region crossings asynchronous. Moved the bulk of the original code out of ScenePresence and into SceneCommunicationService, where it should be (next to RequestTeleportToLocation). No changes in the crossing mechanism itself, yet. But this change opens the way to doing crossings as slowly as it needs to be, outside the simulator Update loop.
Note: weirdnesses may occur!
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.cs5
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs170
-rw-r--r--OpenSim/Region/Framework/Scenes/ScenePresence.cs164
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs14
4 files changed, 219 insertions, 134 deletions
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index e2eb89e..30b44b1 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -3179,6 +3179,11 @@ namespace OpenSim.Region.Framework.Scenes
3179 return m_sceneGridService.CrossToNeighbouringRegion(regionHandle, agentID, position, isFlying); 3179 return m_sceneGridService.CrossToNeighbouringRegion(regionHandle, agentID, position, isFlying);
3180 } 3180 }
3181 3181
3182 public void CrossAgentToNewRegion(ScenePresence agent, bool isFlying)
3183 {
3184 m_sceneGridService.CrossAgentToNewRegion(this, agent, isFlying);
3185 }
3186
3182 public void SendOutChildAgentUpdates(AgentPosition cadu, ScenePresence presence) 3187 public void SendOutChildAgentUpdates(AgentPosition cadu, ScenePresence presence)
3183 { 3188 {
3184 m_sceneGridService.SendChildAgentDataUpdate(cadu, presence); 3189 m_sceneGridService.SendChildAgentDataUpdate(cadu, presence);
diff --git a/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs b/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs
index bef57a0..98b0f97 100644
--- a/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs
@@ -35,6 +35,7 @@ using OpenMetaverse.StructuredData;
35using log4net; 35using log4net;
36using OpenSim.Framework; 36using OpenSim.Framework;
37using OpenSim.Framework.Communications; 37using OpenSim.Framework.Communications;
38using OpenSim.Framework.Communications.Cache;
38using OpenSim.Framework.Communications.Capabilities; 39using OpenSim.Framework.Communications.Capabilities;
39using OpenSim.Region.Framework.Interfaces; 40using OpenSim.Region.Framework.Interfaces;
40using OSD = OpenMetaverse.StructuredData.OSD; 41using OSD = OpenMetaverse.StructuredData.OSD;
@@ -1018,6 +1019,175 @@ namespace OpenSim.Region.Framework.Scenes
1018 return m_commsProvider.InterRegion.ExpectAvatarCrossing(regionhandle, agentID, position, isFlying); 1019 return m_commsProvider.InterRegion.ExpectAvatarCrossing(regionhandle, agentID, position, isFlying);
1019 } 1020 }
1020 1021
1022 public void CrossAgentToNewRegion(Scene scene, ScenePresence agent, bool isFlying)
1023 {
1024 Vector3 pos = agent.AbsolutePosition;
1025 Vector3 newpos = new Vector3(pos.X, pos.Y, pos.Z);
1026 uint neighbourx = m_regionInfo.RegionLocX;
1027 uint neighboury = m_regionInfo.RegionLocY;
1028
1029 // distance to edge that will trigger crossing
1030 const float boundaryDistance = 1.7f;
1031
1032 // distance into new region to place avatar
1033 const float enterDistance = 0.1f;
1034
1035 if (pos.X < boundaryDistance)
1036 {
1037 neighbourx--;
1038 newpos.X = Constants.RegionSize - enterDistance;
1039 }
1040 else if (pos.X > Constants.RegionSize - boundaryDistance)
1041 {
1042 neighbourx++;
1043 newpos.X = enterDistance;
1044 }
1045
1046 if (pos.Y < boundaryDistance)
1047 {
1048 neighboury--;
1049 newpos.Y = Constants.RegionSize - enterDistance;
1050 }
1051 else if (pos.Y > Constants.RegionSize - boundaryDistance)
1052 {
1053 neighboury++;
1054 newpos.Y = enterDistance;
1055 }
1056
1057 Vector3 vel = agent.Velocity;
1058
1059 CrossAgentToNewRegionDelegate d = CrossAgentToNewRegionAsync;
1060 d.BeginInvoke(agent, newpos, neighbourx, neighboury, isFlying, CrossAgentToNewRegionCompleted, d);
1061
1062
1063 }
1064
1065 public delegate ScenePresence CrossAgentToNewRegionDelegate(ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, bool isFlying);
1066
1067 /// <summary>
1068 /// This Closes child agents on neighboring regions
1069 /// Calls an asynchronous method to do so.. so it doesn't lag the sim.
1070 /// </summary>
1071 protected ScenePresence CrossAgentToNewRegionAsync(ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, bool isFlying)
1072 {
1073 m_log.DebugFormat("[SCENE COMM]: Crossing agent {0} {1} to {2}-{3}", agent.Firstname, agent.Lastname, neighbourx, neighboury);
1074
1075 ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize));
1076 SimpleRegionInfo neighbourRegion = RequestNeighbouringRegionInfo(neighbourHandle);
1077 if (neighbourRegion != null && agent.ValidateAttachments())
1078 {
1079 pos = pos + (agent.Velocity);
1080
1081 CachedUserInfo userInfo = m_commsProvider.UserProfileCacheService.GetUserDetails(agent.UUID);
1082 if (userInfo != null)
1083 {
1084 userInfo.DropInventory();
1085 }
1086 else
1087 {
1088 m_log.WarnFormat("[SCENE COMM]: No cached user info found for {0} {1} on leaving region {2}",
1089 agent.Name, agent.UUID, agent.Scene.RegionInfo.RegionName);
1090 }
1091
1092 bool crossingSuccessful =
1093 CrossToNeighbouringRegion(neighbourHandle, agent.ControllingClient.AgentId, pos,
1094 isFlying);
1095 if (crossingSuccessful)
1096 {
1097 // Next, let's close the child agent connections that are too far away.
1098 agent.CloseChildAgents(neighbourx, neighboury);
1099
1100 //AgentCircuitData circuitdata = m_controllingClient.RequestClientInfo();
1101 agent.ControllingClient.RequestClientInfo();
1102
1103 //Console.WriteLine("BEFORE CROSS");
1104 //Scene.DumpChildrenSeeds(UUID);
1105 //DumpKnownRegions();
1106 string agentcaps;
1107 if (!agent.KnownRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps))
1108 {
1109 m_log.ErrorFormat("[SCENE COMM]: No CAPS information for region handle {0}, exiting CrossToNewRegion.",
1110 neighbourRegion.RegionHandle);
1111 return agent;
1112 }
1113 // TODO Should construct this behind a method
1114 string capsPath =
1115 "http://" + neighbourRegion.ExternalHostName + ":" + neighbourRegion.HttpPort
1116 + "/CAPS/" + agentcaps /*circuitdata.CapsPath*/ + "0000/";
1117
1118 m_log.DebugFormat("[CAPS]: Sending new CAPS seed url {0} to client {1}", capsPath, agent.UUID);
1119
1120 IEventQueue eq = agent.Scene.RequestModuleInterface<IEventQueue>();
1121 if (eq != null)
1122 {
1123 eq.CrossRegion(neighbourHandle, pos, agent.Velocity, neighbourRegion.ExternalEndPoint,
1124 capsPath, agent.UUID, agent.ControllingClient.SessionId);
1125 }
1126 else
1127 {
1128 agent.ControllingClient.CrossRegion(neighbourHandle, pos, agent.Velocity, neighbourRegion.ExternalEndPoint,
1129 capsPath);
1130 }
1131
1132 agent.MakeChildAgent();
1133 // now we have a child agent in this region. Request all interesting data about other (root) agents
1134 agent.SendInitialFullUpdateToAllClients();
1135
1136 agent.CrossAttachmentsIntoNewRegion(neighbourHandle, true);
1137
1138 // m_scene.SendKillObject(m_localId);
1139
1140 agent.Scene.NotifyMyCoarseLocationChange();
1141 // the user may change their profile information in other region,
1142 // so the userinfo in UserProfileCache is not reliable any more, delete it
1143 if (agent.Scene.NeedSceneCacheClear(agent.UUID))
1144 {
1145 agent.Scene.CommsManager.UserProfileCacheService.RemoveUser(agent.UUID);
1146 m_log.DebugFormat(
1147 "[SCENE COMM]: User {0} is going to another region, profile cache removed", agent.UUID);
1148 }
1149 }
1150 else
1151 {
1152 //// Restore the user structures that we needed to delete before asking the receiving region
1153 //// to complete the crossing
1154 //userInfo.FetchInventory();
1155 //agent.Scene.CapsModule.AddCapsHandler(agent.UUID);
1156 }
1157 }
1158
1159 //Console.WriteLine("AFTER CROSS");
1160 //Scene.DumpChildrenSeeds(UUID);
1161 //DumpKnownRegions();
1162 return agent;
1163 }
1164
1165 private void CrossAgentToNewRegionCompleted(IAsyncResult iar)
1166 {
1167 CrossAgentToNewRegionDelegate icon = (CrossAgentToNewRegionDelegate)iar.AsyncState;
1168 ScenePresence agent = icon.EndInvoke(iar);
1169
1170 // If the cross was successful, this agent is a child agent
1171 if (agent.IsChildAgent)
1172 {
1173 // Put the child agent back at the center
1174 agent.AbsolutePosition = new Vector3(128, 128, 70);
1175 }
1176 else // Not successful
1177 {
1178 CachedUserInfo userInfo = m_commsProvider.UserProfileCacheService.GetUserDetails(agent.UUID);
1179 if (userInfo != null)
1180 {
1181 userInfo.FetchInventory();
1182 }
1183 agent.RestoreInCurrentScene();
1184 }
1185 agent.IsInTransit = false;
1186
1187 //m_log.DebugFormat("[SCENE COMM]: Crossing agent {0} {1} completed.", agent.Firstname, agent.Lastname);
1188 }
1189
1190
1021 public bool PrimCrossToNeighboringRegion(ulong regionhandle, UUID primID, string objData, int XMLMethod) 1191 public bool PrimCrossToNeighboringRegion(ulong regionhandle, UUID primID, string objData, int XMLMethod)
1022 { 1192 {
1023 return m_commsProvider.InterRegion.InformRegionOfPrimCrossing(regionhandle, primID, objData, XMLMethod); 1193 return m_commsProvider.InterRegion.InformRegionOfPrimCrossing(regionhandle, primID, objData, XMLMethod);
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
index 2dd305a..f841707 100644
--- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs
+++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
@@ -547,6 +547,13 @@ namespace OpenSim.Region.Framework.Scenes
547 get { return m_animations; } 547 get { return m_animations; }
548 } 548 }
549 549
550 private bool m_inTransit;
551 public bool IsInTransit
552 {
553 get { return m_inTransit; }
554 set { m_inTransit = value; }
555 }
556
550 #endregion 557 #endregion
551 558
552 #region Constructor(s) 559 #region Constructor(s)
@@ -850,7 +857,7 @@ namespace OpenSim.Region.Framework.Scenes
850 857
851 CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(m_uuid); 858 CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(m_uuid);
852 if (userInfo != null) 859 if (userInfo != null)
853 userInfo.FetchInventory(); 860 userInfo.FetchInventory();
854 else 861 else
855 m_log.ErrorFormat("[SCENE]: Could not find user info for {0} when making it a root agent", m_uuid); 862 m_log.ErrorFormat("[SCENE]: Could not find user info for {0} when making it a root agent", m_uuid);
856 863
@@ -2377,15 +2384,31 @@ namespace OpenSim.Region.Framework.Scenes
2377 pos2.Y = pos2.Y + (vel.Y*timeStep); 2384 pos2.Y = pos2.Y + (vel.Y*timeStep);
2378 pos2.Z = pos2.Z + (vel.Z*timeStep); 2385 pos2.Z = pos2.Z + (vel.Z*timeStep);
2379 2386
2380 if ((pos2.X < 0) || (pos2.X > Constants.RegionSize)) 2387 if (!IsInTransit)
2381 { 2388 {
2382 CrossToNewRegion(); 2389 if ((pos2.X < 0) || (pos2.X > Constants.RegionSize))
2383 } 2390 {
2391 CrossToNewRegion();
2392 }
2384 2393
2385 if ((pos2.Y < 0) || (pos2.Y > Constants.RegionSize)) 2394 if ((pos2.Y < 0) || (pos2.Y > Constants.RegionSize))
2395 {
2396 CrossToNewRegion();
2397 }
2398 }
2399 else
2386 { 2400 {
2387 CrossToNewRegion(); 2401 RemoveFromPhysicalScene();
2402 // This constant has been inferred from experimentation
2403 // I'm not sure what this value should be, so I tried a few values.
2404 timeStep = 0.04f;
2405 pos2 = AbsolutePosition;
2406 pos2.X = pos2.X + (vel.X * timeStep);
2407 pos2.Y = pos2.Y + (vel.Y * timeStep);
2408 pos2.Z = pos2.Z + (vel.Z * timeStep);
2409 m_pos = pos2;
2388 } 2410 }
2411
2389 } 2412 }
2390 2413
2391 /// <summary> 2414 /// <summary>
@@ -2396,130 +2419,13 @@ namespace OpenSim.Region.Framework.Scenes
2396 /// </summary> 2419 /// </summary>
2397 protected void CrossToNewRegion() 2420 protected void CrossToNewRegion()
2398 { 2421 {
2399 Vector3 pos = AbsolutePosition; 2422 m_inTransit = true;
2400 Vector3 newpos = new Vector3(pos.X, pos.Y, pos.Z); 2423 m_scene.CrossAgentToNewRegion(this, m_physicsActor.Flying);
2401 uint neighbourx = m_regionInfo.RegionLocX; 2424 }
2402 uint neighboury = m_regionInfo.RegionLocY;
2403
2404 // distance to edge that will trigger crossing
2405 const float boundaryDistance = 1.7f;
2406
2407 // distance into new region to place avatar
2408 const float enterDistance = 0.1f;
2409
2410 if (pos.X < boundaryDistance)
2411 {
2412 neighbourx--;
2413 newpos.X = Constants.RegionSize - enterDistance;
2414 }
2415 else if (pos.X > Constants.RegionSize - boundaryDistance)
2416 {
2417 neighbourx++;
2418 newpos.X = enterDistance;
2419 }
2420
2421 if (pos.Y < boundaryDistance)
2422 {
2423 neighboury--;
2424 newpos.Y = Constants.RegionSize - enterDistance;
2425 }
2426 else if (pos.Y > Constants.RegionSize - boundaryDistance)
2427 {
2428 neighboury++;
2429 newpos.Y = enterDistance;
2430 }
2431
2432 Vector3 vel = m_velocity;
2433 ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize));
2434 SimpleRegionInfo neighbourRegion = m_scene.RequestNeighbouringRegionInfo(neighbourHandle);
2435 if (neighbourRegion != null && ValidateAttachments())
2436 {
2437 // When the neighbour is informed of the border crossing, it will set up CAPS handlers for the avatar
2438 // This means we need to remove the current caps handler here and possibly compensate later,
2439 // in case both scenes are being hosted on the same region server. Messy
2440 //m_scene.RemoveCapsHandler(UUID);
2441 newpos = newpos + (vel);
2442
2443 CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(UUID);
2444 if (userInfo != null)
2445 {
2446 userInfo.DropInventory();
2447 }
2448 else
2449 {
2450 m_log.WarnFormat("[SCENE PRESENCE]: No cached user info found for {0} {1} on leaving region", Name, UUID);
2451 }
2452
2453 bool crossingSuccessful =
2454 m_scene.InformNeighbourOfCrossing(neighbourHandle, m_controllingClient.AgentId, newpos,
2455 m_physicsActor.Flying);
2456 if (crossingSuccessful)
2457 {
2458 // Next, let's close the child agent connections that are too far away.
2459 CloseChildAgents(neighbourx, neighboury);
2460
2461 //AgentCircuitData circuitdata = m_controllingClient.RequestClientInfo();
2462 m_controllingClient.RequestClientInfo();
2463
2464 //Console.WriteLine("BEFORE CROSS");
2465 //Scene.DumpChildrenSeeds(UUID);
2466 //DumpKnownRegions();
2467 string agentcaps;
2468 if (!m_knownChildRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps))
2469 {
2470 m_log.ErrorFormat("[SCENE PRESENCE]: No CAPS information for region handle {0}, exiting CrossToNewRegion.",
2471 neighbourRegion.RegionHandle);
2472 return;
2473 }
2474 // TODO Should construct this behind a method
2475 string capsPath =
2476 "http://" + neighbourRegion.ExternalHostName + ":" + neighbourRegion.HttpPort
2477 + "/CAPS/" + agentcaps /*circuitdata.CapsPath*/ + "0000/";
2478
2479 m_log.DebugFormat("[CAPS]: Sending new CAPS seed url {0} to client {1}", capsPath, m_uuid);
2480
2481 IEventQueue eq = m_scene.RequestModuleInterface<IEventQueue>();
2482 if (eq != null)
2483 {
2484 eq.CrossRegion(neighbourHandle, newpos, vel, neighbourRegion.ExternalEndPoint,
2485 capsPath, UUID, ControllingClient.SessionId);
2486 }
2487 else
2488 {
2489 m_controllingClient.CrossRegion(neighbourHandle, newpos, vel, neighbourRegion.ExternalEndPoint,
2490 capsPath);
2491 }
2492
2493 MakeChildAgent();
2494 // now we have a child agent in this region. Request all interesting data about other (root) agents
2495 SendInitialFullUpdateToAllClients();
2496
2497 CrossAttachmentsIntoNewRegion(neighbourHandle, true);
2498
2499 // m_scene.SendKillObject(m_localId);
2500
2501 m_scene.NotifyMyCoarseLocationChange();
2502 // the user may change their profile information in other region,
2503 // so the userinfo in UserProfileCache is not reliable any more, delete it
2504 if (m_scene.NeedSceneCacheClear(UUID))
2505 {
2506 m_scene.CommsManager.UserProfileCacheService.RemoveUser(UUID);
2507 m_log.DebugFormat(
2508 "[SCENE PRESENCE]: User {0} is going to another region, profile cache removed", UUID);
2509 }
2510 }
2511 else
2512 {
2513 // Restore the user structures that we needed to delete before asking the receiving region
2514 // to complete the crossing
2515 userInfo.FetchInventory();
2516 m_scene.CapsModule.AddCapsHandler(UUID);
2517 }
2518 }
2519 2425
2520 //Console.WriteLine("AFTER CROSS"); 2426 public void RestoreInCurrentScene()
2521 //Scene.DumpChildrenSeeds(UUID); 2427 {
2522 //DumpKnownRegions(); 2428 AddToPhysicalScene();
2523 } 2429 }
2524 2430
2525 /// <summary> 2431 /// <summary>
diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs
index db88878..2f2bc19 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs
@@ -66,11 +66,11 @@ namespace OpenSim.Region.Framework.Scenes.Tests
66 scene2 = SceneSetupHelpers.SetupScene("Neighbour x+1", UUID.Random(), 1001, 1000, cm); 66 scene2 = SceneSetupHelpers.SetupScene("Neighbour x+1", UUID.Random(), 1001, 1000, cm);
67 scene3 = SceneSetupHelpers.SetupScene("Neighbour x-1", UUID.Random(), 999, 1000, cm); 67 scene3 = SceneSetupHelpers.SetupScene("Neighbour x-1", UUID.Random(), 999, 1000, cm);
68 68
69 IRegionModule interregionComms = new RESTInterregionComms(); 69 IRegionModule interregionComms = new RESTInterregionComms();
70 interregionComms.Initialise(scene, new IniConfigSource()); 70 interregionComms.Initialise(scene, new IniConfigSource());
71 interregionComms.Initialise(scene2, new IniConfigSource()); 71 interregionComms.Initialise(scene2, new IniConfigSource());
72 interregionComms.Initialise(scene3, new IniConfigSource()); 72 interregionComms.Initialise(scene3, new IniConfigSource());
73 interregionComms.PostInitialise(); 73 interregionComms.PostInitialise();
74 SceneSetupHelpers.SetupSceneModules(scene, new IniConfigSource(), interregionComms); 74 SceneSetupHelpers.SetupSceneModules(scene, new IniConfigSource(), interregionComms);
75 SceneSetupHelpers.SetupSceneModules(scene2, new IniConfigSource(), interregionComms); 75 SceneSetupHelpers.SetupSceneModules(scene2, new IniConfigSource(), interregionComms);
76 SceneSetupHelpers.SetupSceneModules(scene3, new IniConfigSource(), interregionComms); 76 SceneSetupHelpers.SetupSceneModules(scene3, new IniConfigSource(), interregionComms);
@@ -203,6 +203,8 @@ namespace OpenSim.Region.Framework.Scenes.Tests
203 scene.RegisterRegionWithGrid(); 203 scene.RegisterRegionWithGrid();
204 scene2.RegisterRegionWithGrid(); 204 scene2.RegisterRegionWithGrid();
205 presence.Update(); 205 presence.Update();
206 // Crossings are asynchronous
207 while (presence.IsInTransit) { } ;
206 208
207 Assert.That(presence.IsChildAgent, Is.True, "Did not complete region cross as expected."); 209 Assert.That(presence.IsChildAgent, Is.True, "Did not complete region cross as expected.");
208 Assert.That(presence2.IsChildAgent, Is.False, "Did not receive root status after receiving agent."); 210 Assert.That(presence2.IsChildAgent, Is.False, "Did not receive root status after receiving agent.");
@@ -210,6 +212,8 @@ namespace OpenSim.Region.Framework.Scenes.Tests
210 // Cross Back 212 // Cross Back
211 presence2.AbsolutePosition = new Vector3(-1, 3, 100); 213 presence2.AbsolutePosition = new Vector3(-1, 3, 100);
212 presence2.Update(); 214 presence2.Update();
215 // Crossings are asynchronous
216 while (presence.IsInTransit) { };
213 217
214 Assert.That(presence2.IsChildAgent, Is.True, "Did not return from region as expected."); 218 Assert.That(presence2.IsChildAgent, Is.True, "Did not return from region as expected.");
215 Assert.That(presence.IsChildAgent, Is.False, "Presence was not made root in old region again."); 219 Assert.That(presence.IsChildAgent, Is.False, "Presence was not made root in old region again.");