New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)

In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
TeleportWork
Diva Canto 2013-07-22 11:54:35 -07:00
parent aae29c0ee2
commit 3891a8946b
6 changed files with 311 additions and 74 deletions

View File

@ -287,7 +287,7 @@ namespace OpenSim.Framework
public Vector3 AtAxis;
public Vector3 LeftAxis;
public Vector3 UpAxis;
public bool ChangedGrid;
public bool SenderWantsToWaitForRoot;
public float Far;
public float Aspect;
@ -356,8 +356,9 @@ namespace OpenSim.Framework
args["left_axis"] = OSD.FromString(LeftAxis.ToString());
args["up_axis"] = OSD.FromString(UpAxis.ToString());
args["changed_grid"] = OSD.FromBoolean(ChangedGrid);
//backwards compatibility
args["changed_grid"] = OSD.FromBoolean(SenderWantsToWaitForRoot);
args["wait_for_root"] = OSD.FromBoolean(SenderWantsToWaitForRoot);
args["far"] = OSD.FromReal(Far);
args["aspect"] = OSD.FromReal(Aspect);
@ -526,8 +527,8 @@ namespace OpenSim.Framework
if (args["up_axis"] != null)
Vector3.TryParse(args["up_axis"].AsString(), out AtAxis);
if (args["changed_grid"] != null)
ChangedGrid = args["changed_grid"].AsBoolean();
if (args.ContainsKey("wait_for_root") && args["wait_for_root"] != null)
SenderWantsToWaitForRoot = args["wait_for_root"].AsBoolean();
if (args["far"] != null)
Far = (float)(args["far"].AsReal());

View File

@ -1257,6 +1257,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// UseCircuitCode handling
if (packet.Type == PacketType.UseCircuitCode)
{
m_log.DebugFormat("[ZZZ]: In the dungeon: UseCircuitCode");
// We need to copy the endpoint so that it doesn't get changed when another thread reuses the
// buffer.
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
@ -1265,6 +1266,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return;
}
else if (packet.Type == PacketType.CompleteAgentMovement)
{
// Send ack straight away to let the viewer know that we got it.
SendAckImmediate(endPoint, packet.Header.Sequence);
m_log.DebugFormat("[ZZZ]: In the dungeon: CompleteAgentMovement");
// We need to copy the endpoint so that it doesn't get changed when another thread reuses the
// buffer.
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
Util.FireAndForget(HandleCompleteMovementIntoRegion, array);
return;
}
// Determine which agent this packet came from
IClientAPI client;
@ -1604,6 +1619,56 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
}
private void HandleCompleteMovementIntoRegion(object o)
{
IPEndPoint endPoint = null;
IClientAPI client = null;
try
{
object[] array = (object[])o;
endPoint = (IPEndPoint)array[0];
CompleteAgentMovementPacket packet = (CompleteAgentMovementPacket)array[1];
// Determine which agent this packet came from
int count = 10;
while (!m_scene.TryGetClient(endPoint, out client) && count-- > 0)
{
m_log.Debug("[LLUDPSERVER]: Received a CompleteMovementIntoRegion in " + m_scene.RegionInfo.RegionName + " (not ready yet)");
Thread.Sleep(200);
}
if (client == null)
return;
IncomingPacket incomingPacket1;
// Inbox insertion
if (UsePools)
{
incomingPacket1 = m_incomingPacketPool.GetObject();
incomingPacket1.Client = (LLClientView)client;
incomingPacket1.Packet = packet;
}
else
{
incomingPacket1 = new IncomingPacket((LLClientView)client, packet);
}
packetInbox.Enqueue(incomingPacket1);
}
catch (Exception e)
{
m_log.ErrorFormat(
"[LLUDPSERVER]: CompleteMovementIntoRegion handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}",
endPoint != null ? endPoint.ToString() : "n/a",
client != null ? client.Name : "unknown",
client != null ? client.AgentId.ToString() : "unknown",
e.Message,
e.StackTrace);
}
}
/// <summary>
/// Send an ack immediately to the given endpoint.
/// </summary>
@ -1999,6 +2064,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Packet packet = incomingPacket.Packet;
LLClientView client = incomingPacket.Client;
if (packet is CompleteAgentMovementPacket)
m_log.DebugFormat("[ZZZ]: Received CompleteAgentMovementPacket");
if (client.IsActive)
{
m_currentIncomingClient = client;

View File

@ -684,7 +684,18 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath();
}
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Contacting destination...");
if (version.Equals("SIMULATION/0.2"))
TransferAgent_V2(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, version, out reason);
else
TransferAgent_V1(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, version, out reason);
}
private void TransferAgent_V1(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination,
IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, string version, out string reason)
{
ulong destinationHandle = finalDestination.RegionHandle;
AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
// Let's create an agent there if one doesn't exist yet.
// NOTE: logout will always be false for a non-HG teleport.
@ -707,7 +718,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
m_interRegionTeleportCancels.Value++;
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request",
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request",
sp.Name, finalDestination.RegionName, sp.Scene.Name);
return;
@ -725,14 +736,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// Past this point we have to attempt clean up if the teleport fails, so update transfer state.
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring);
#region old protocol
IClientIPEndpoint ipepClient;
IClientIPEndpoint ipepClient;
string capsPath = String.Empty;
if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY))
{
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for incoming agent {3} from {4}",
"[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for incoming agent {3} from {4}",
finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name);
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent...");
@ -745,30 +755,30 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
#endregion
capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
//if (m_eqModule != null)
//{
// // The EnableSimulator message makes the client establish a connection with the destination
// // simulator by sending the initial UseCircuitCode UDP packet to the destination containing the
// // correct circuit code.
// m_eqModule.EnableSimulator(destinationHandle, endPoint, sp.UUID);
if (m_eqModule != null)
{
// The EnableSimulator message makes the client establish a connection with the destination
// simulator by sending the initial UseCircuitCode UDP packet to the destination containing the
// correct circuit code.
m_eqModule.EnableSimulator(destinationHandle, endPoint, sp.UUID);
// // XXX: Is this wait necessary? We will always end up waiting on UpdateAgent for the destination
// // simulator to confirm that it has established communication with the viewer.
// Thread.Sleep(200);
// XXX: Is this wait necessary? We will always end up waiting on UpdateAgent for the destination
// simulator to confirm that it has established communication with the viewer.
Thread.Sleep(200);
// // At least on LL 3.3.4 for teleports between different regions on the same simulator this appears
// // unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly
// // only on TeleportFinish). This is untested for region teleport between different simulators
// // though this probably also works.
// m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath);
//}
//else
//{
// // XXX: This is a little misleading since we're information the client of its avatar destination,
// // which may or may not be a neighbour region of the source region. This path is probably little
// // used anyway (with EQ being the one used). But it is currently being used for test code.
// sp.ControllingClient.InformClientOfNeighbour(destinationHandle, endPoint);
//}
// At least on LL 3.3.4 for teleports between different regions on the same simulator this appears
// unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly
// only on TeleportFinish). This is untested for region teleport between different simulators
// though this probably also works.
m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath);
}
else
{
// XXX: This is a little misleading since we're information the client of its avatar destination,
// which may or may not be a neighbour region of the source region. This path is probably little
// used anyway (with EQ being the one used). But it is currently being used for test code.
sp.ControllingClient.InformClientOfNeighbour(destinationHandle, endPoint);
}
}
else
{
@ -776,15 +786,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
}
#endregion old protocol
// Let's send a full update of the agent. This is a synchronous call.
AgentData agent = new AgentData();
sp.CopyTo(agent);
agent.Position = position;
//SetCallbackURL(agent, sp.Scene.RegionInfo);
agent.Position = agentCircuit.startpos;
SetCallbackURL(agent, sp.Scene.RegionInfo);
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Updating agent...");
// We will check for an abort before UpdateAgent since UpdateAgent will require an active viewer to
// establish th econnection to the destination which makes it return true.
@ -814,7 +821,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
sp.ControllingClient.SendRegionTeleport(destinationHandle, 13, endPoint, 4,
teleportFlags, capsPath);
}
// A common teleport failure occurs when we can send CreateAgent to the
// destination region but the viewer cannot establish the connection (e.g. due to network issues between
// the viewer and the destination). In this case, UpdateAgent timesout after 10 seconds, although then
@ -835,7 +842,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
m_log.WarnFormat(
"[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Keeping avatar in source region.",
sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established.");
return;
}
@ -845,7 +852,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
m_interRegionTeleportCancels.Value++;
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request",
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request",
sp.Name, finalDestination.RegionName, sp.Scene.Name);
CleanupFailedInterRegionTeleport(sp, currentAgentCircuit.SessionID.ToString(), finalDestination);
@ -857,30 +864,30 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
"[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}",
capsPath, sp.Scene.RegionInfo.RegionName, sp.Name);
//// TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which
//// trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation
//// that the client contacted the destination before we close things here.
//if (!m_entityTransferStateMachine.WaitForAgentArrivedAtDestination(sp.UUID))
//{
// if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
// {
// m_interRegionTeleportAborts.Value++;
// TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which
// trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation
// that the client contacted the destination before we close things here.
if (!m_entityTransferStateMachine.WaitForAgentArrivedAtDestination(sp.UUID))
{
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
{
m_interRegionTeleportAborts.Value++;
// m_log.DebugFormat(
// "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after WaitForAgentArrivedAtDestination due to previous client close.",
// sp.Name, finalDestination.RegionName, sp.Scene.Name);
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after WaitForAgentArrivedAtDestination due to previous client close.",
sp.Name, finalDestination.RegionName, sp.Scene.Name);
// return;
// }
return;
}
// m_log.WarnFormat(
// "[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.",
// sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
// Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Destination region did not signal teleport completion.");
m_log.WarnFormat(
"[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.",
sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
// return;
//}
Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Destination region did not signal teleport completion.");
return;
}
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
@ -914,7 +921,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
//
// This sleep can be increased if necessary. However, whilst it's active,
// an agent cannot teleport back to this region if it has teleported away.
Thread.Sleep(15000);
Thread.Sleep(2000);
sp.Scene.IncomingCloseAgent(sp.UUID, false);
}
@ -925,6 +932,126 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
}
private void TransferAgent_V2(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination,
IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, string version, out string reason)
{
ulong destinationHandle = finalDestination.RegionHandle;
AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
// Let's create an agent there if one doesn't exist yet.
// NOTE: logout will always be false for a non-HG teleport.
bool logout = false;
if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout))
{
m_interRegionTeleportFailures.Value++;
sp.ControllingClient.SendTeleportFailed(String.Format("Teleport refused: {0}", reason));
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}",
sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason);
return;
}
// Past this point we have to attempt clean up if the teleport fails, so update transfer state.
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring);
IClientIPEndpoint ipepClient;
string capsPath = String.Empty;
if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY))
{
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for agent {3} from {4}",
finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name);
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent...");
#region IP Translation for NAT
// Uses ipepClient above
if (sp.ClientView.TryGet(out ipepClient))
{
endPoint.Address = NetworkUtil.GetIPFor(ipepClient.EndPoint, endPoint.Address);
}
#endregion
capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
}
else
{
agentCircuit.CapsPath = sp.Scene.CapsModule.GetChildSeed(sp.UUID, reg.RegionHandle);
capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
}
// We need to set this here to avoid an unlikely race condition when teleporting to a neighbour simulator,
// where that neighbour simulator could otherwise request a child agent create on the source which then
// closes our existing agent which is still signalled as root.
//sp.IsChildAgent = true;
// New protocol: send TP Finish directly, without prior ES or EAC. That's what happens in the Linden grid
if (m_eqModule != null)
m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID);
else
sp.ControllingClient.SendRegionTeleport(destinationHandle, 13, endPoint, 4,
teleportFlags, capsPath);
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}",
capsPath, sp.Scene.RegionInfo.RegionName, sp.Name);
// Let's send a full update of the agent.
AgentData agent = new AgentData();
sp.CopyTo(agent);
agent.Position = agentCircuit.startpos;
agent.SenderWantsToWaitForRoot = true;
//SetCallbackURL(agent, sp.Scene.RegionInfo);
// Send the Update. If this returns true, we know the client has contacted the destination
// via CompleteMovementIntoRegion, so we can let go.
// If it returns false, something went wrong, and we need to abort.
m_log.DebugFormat("[ZZZ]: Sending Update");
if (!UpdateAgent(reg, finalDestination, agent, sp))
{
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
{
m_interRegionTeleportAborts.Value++;
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.",
sp.Name, finalDestination.RegionName, sp.Scene.Name);
return;
}
m_log.WarnFormat(
"[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Keeping avatar in source region.",
sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established.");
return;
}
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
// May need to logout or other cleanup
AgentHasMovedAway(sp, logout);
// Well, this is it. The agent is over there.
KillEntity(sp.Scene, sp.LocalId);
// Now let's make it officially a child agent
sp.MakeChildAgent();
// OK, it got this agent. Let's close some child agents
sp.CloseChildAgents(newRegionX, newRegionY);
// Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone
if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg))
sp.Scene.IncomingCloseAgent(sp.UUID, false);
else
// now we have a child agent in this region.
sp.Reset();
}
/// <summary>
/// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation.
/// </summary>
@ -938,11 +1065,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
sp.IsChildAgent = false;
ReInstantiateScripts(sp);
EnableChildAgents(sp);
if (sp.IsChildAgent) // We had set it to child before attempted TP (V1)
{
sp.IsChildAgent = false;
ReInstantiateScripts(sp);
EnableChildAgents(sp);
}
// Finally, kill the agent we just created at the destination.
// XXX: Possibly this should be done asynchronously.
Scene.SimulationService.CloseAgent(finalDestination, sp.UUID, auth_token);

View File

@ -48,7 +48,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation
/// <summary>
/// Version of this service
/// </summary>
private const string m_Version = "SIMULATION/0.1";
private const string m_Version = "SIMULATION/0.2";
/// <summary>
/// Map region ID to scene.

View File

@ -4221,6 +4221,20 @@ namespace OpenSim.Region.Framework.Scenes
}
childAgentUpdate.ChildAgentDataUpdate(cAgentData);
int ntimes = 20;
if (cAgentData.SenderWantsToWaitForRoot)
{
while (childAgentUpdate.IsChildAgent && ntimes-- > 0)
Thread.Sleep(500);
m_log.DebugFormat(
"[SCENE PRESENCE]: Found presence {0} {1} {2} in {3} after {4} waits",
childAgentUpdate.Name, childAgentUpdate.UUID, childAgentUpdate.IsChildAgent ? "child" : "root", RegionInfo.RegionName, 20 - ntimes);
if (childAgentUpdate.IsChildAgent)
return false;
}
return true;
}
return false;
@ -4278,10 +4292,6 @@ namespace OpenSim.Region.Framework.Scenes
m_log.WarnFormat(
"[SCENE PRESENCE]: Did not find presence with id {0} in {1} before timeout",
agentID, RegionInfo.RegionName);
// else
// m_log.DebugFormat(
// "[SCENE PRESENCE]: Found presence {0} {1} {2} in {3} after {4} waits",
// sp.Name, sp.UUID, sp.IsChildAgent ? "child" : "root", RegionInfo.RegionName, 10 - ntimes);
return sp;
}

View File

@ -29,7 +29,9 @@ using System;
using System.Xml;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Timers;
using Timer = System.Timers.Timer;
using OpenMetaverse;
using log4net;
using Nini.Config;
@ -1311,6 +1313,26 @@ namespace OpenSim.Region.Framework.Scenes
PhysicsActor.Size = new Vector3(0.45f, 0.6f, height);
}
private bool WaitForUpdateAgent(IClientAPI client)
{
// Before UpdateAgent, m_originRegionID is UUID.Zero; after, it's non-Zero
int count = 20;
while (m_originRegionID.Equals(UUID.Zero) && count-- > 0)
{
m_log.DebugFormat("[SCENE PRESENCE]: Agent {0} waiting for update in {1}", client.Name, Scene.RegionInfo.RegionName);
Thread.Sleep(200);
}
if (m_originRegionID.Equals(UUID.Zero))
{
// Movement into region will fail
m_log.WarnFormat("[SCENE PRESENCE]: Update agent {0} never arrived", client.Name);
return false;
}
return true;
}
/// <summary>
/// Complete Avatar's movement into the region.
/// </summary>
@ -1328,6 +1350,12 @@ namespace OpenSim.Region.Framework.Scenes
"[SCENE PRESENCE]: Completing movement of {0} into region {1} in position {2}",
client.Name, Scene.RegionInfo.RegionName, AbsolutePosition);
if (m_teleportFlags != TeleportFlags.ViaLogin)
// Let's wait until UpdateAgent (called by departing region) is done
if (!WaitForUpdateAgent(client))
// The sending region never sent the UpdateAgent data, we have to refuse
return;
Vector3 look = Velocity;
if ((look.X == 0) && (look.Y == 0) && (look.Z == 0))
@ -1348,10 +1376,11 @@ namespace OpenSim.Region.Framework.Scenes
bool flying = ((m_AgentControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0);
MakeRootAgent(AbsolutePosition, flying);
// Tell the client that we're totally ready
ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look);
// Remember in HandleUseCircuitCode, we delayed this to here
// This will also send the initial data to clients when TP to a neighboring region.
// Not ideal, but until we know we're TP-ing from a neighboring region, there's not much we can do
if (m_teleportFlags > 0)
SendInitialDataToMe();