Fatpack message on agent transfers: 1 message only (UpdateAgent) containing the agent and all attachments. Preserves backwards compatibility -- older sims get passed attachments one by one. Meaning that I finally introduced versioning in the simulation service.

bulletsim
Diva Canto 2011-04-28 20:19:54 -07:00
parent 35f190cc92
commit 9892e115cc
13 changed files with 157 additions and 156 deletions

View File

@ -62,7 +62,7 @@ namespace OpenSim.Framework
UUID AgentID { get; set; }
OSDMap Pack();
void Unpack(OSDMap map);
void Unpack(OSDMap map, IScene scene);
}
/// <summary>
@ -122,7 +122,7 @@ namespace OpenSim.Framework
return args;
}
public void Unpack(OSDMap args)
public void Unpack(OSDMap args, IScene scene)
{
if (args.ContainsKey("region_handle"))
UInt64.TryParse(args["region_handle"].AsString(), out RegionHandle);
@ -329,6 +329,10 @@ namespace OpenSim.Framework
public string CallbackURI;
// These two must have the same Count
public List<ISceneObject> AttachmentObjects;
public List<string> AttachmentObjectStates;
public virtual OSDMap Pack()
{
m_log.InfoFormat("[CHILDAGENTDATAUPDATE] Pack data");
@ -441,7 +445,30 @@ namespace OpenSim.Framework
if ((CallbackURI != null) && (!CallbackURI.Equals("")))
args["callback_uri"] = OSD.FromString(CallbackURI);
// Attachment objects for fatpack messages
if (AttachmentObjects != null)
{
int i = 0;
OSDArray attObjs = new OSDArray(AttachmentObjects.Count);
foreach (ISceneObject so in AttachmentObjects)
{
OSDMap info = new OSDMap(4);
info["sog"] = OSD.FromString(so.ToXml2());
info["extra"] = OSD.FromString(so.ExtraToXmlString());
info["modified"] = OSD.FromBoolean(so.HasGroupChanged);
try
{
info["state"] = OSD.FromString(AttachmentObjectStates[i++]);
}
catch (IndexOutOfRangeException e)
{
m_log.WarnFormat("[CHILD AGENT DATA]: scrtips list is shorter than object list.");
}
attObjs.Add(info);
}
args["attach_objects"] = attObjs;
}
return args;
}
@ -450,7 +477,7 @@ namespace OpenSim.Framework
/// Avoiding reflection makes it painful to write, but that's the price!
/// </summary>
/// <param name="hash"></param>
public virtual void Unpack(OSDMap args)
public virtual void Unpack(OSDMap args, IScene scene)
{
m_log.InfoFormat("[CHILDAGENTDATAUPDATE] Unpack data");
@ -628,6 +655,26 @@ namespace OpenSim.Framework
if (args["callback_uri"] != null)
CallbackURI = args["callback_uri"].AsString();
// Attachment objects
if (args["attach_objects"] != null && args["attach_objects"].Type == OSDType.Array)
{
OSDArray attObjs = (OSDArray)(args["attach_objects"]);
AttachmentObjects = new List<ISceneObject>();
AttachmentObjectStates = new List<string>();
foreach (OSD o in attObjs)
{
if (o.Type == OSDType.Map)
{
OSDMap info = (OSDMap)o;
ISceneObject so = scene.DeserializeObject(info["sog"].AsString());
so.ExtraFromXmlString(info["extra"].AsString());
so.HasGroupChanged = info["modified"].AsBoolean();
AttachmentObjects.Add(so);
AttachmentObjectStates.Add(info["state"].AsString());
}
}
}
}
public AgentData()
@ -655,9 +702,9 @@ namespace OpenSim.Framework
return base.Pack();
}
public override void Unpack(OSDMap map)
public override void Unpack(OSDMap map, IScene scene)
{
base.Unpack(map);
base.Unpack(map, scene);
}
}
}

View File

@ -115,7 +115,7 @@ namespace OpenSim.Framework.Tests
position2 = new AgentPosition();
Assert.IsFalse(position2.AgentID == position1.AgentID, "Test Error, position2 should be a blank uninitialized AgentPosition");
position2.Unpack(position1.Pack());
position2.Unpack(position1.Pack(), null);
Assert.IsTrue(position2.AgentID == position1.AgentID, "Agent ID didn't unpack the same way it packed");
Assert.IsTrue(position2.Position == position1.Position, "Position didn't unpack the same way it packed");

View File

@ -562,14 +562,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
/// <param name="sp"></param>
/// <param name="so"></param>
/// <param name="attachmentpoint"></param>
/// <param name="AttachOffset"></param>
/// <param name="attachOffset"></param>
/// <param name="silent"></param>
protected void AttachToAgent(ScenePresence avatar, SceneObjectGroup so, uint attachmentpoint, Vector3 AttachOffset, bool silent)
protected void AttachToAgent(ScenePresence avatar, SceneObjectGroup so, uint attachmentpoint, Vector3 attachOffset, bool silent)
{
// don't attach attachments to child agents
if (avatar.IsChildAgent) return;
// m_log.DebugFormat("[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1}", Name, avatar.Name);
m_log.DebugFormat("[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} in pt {2} pos {3} {4}", Name, avatar.Name,
attachmentpoint, attachOffset, so.RootPart.AttachedPos);
so.DetachFromBackup();
@ -590,8 +589,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
so.RootPart.PhysActor = null;
}
so.AbsolutePosition = AttachOffset;
so.RootPart.AttachedPos = AttachOffset;
so.AbsolutePosition = attachOffset;
so.RootPart.AttachedPos = attachOffset;
so.RootPart.IsAttachment = true;
so.RootPart.SetParentLocalId(avatar.LocalId);

View File

@ -285,11 +285,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
string reason;
if (!m_aScene.SimulationService.QueryAccess(finalDestination, sp.ControllingClient.AgentId, Vector3.Zero, out reason))
string version;
if (!m_aScene.SimulationService.QueryAccess(finalDestination, sp.ControllingClient.AgentId, Vector3.Zero, out version, out reason))
{
sp.ControllingClient.SendTeleportFailed("Teleport failed: " + reason);
return;
}
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Destination is running version {0}", version);
sp.ControllingClient.SendTeleportStart(teleportFlags);
@ -371,20 +373,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
}
// Expect avatar crossing is a heavy-duty function at the destination.
// That is where MakeRoot is called, which fetches appearance and inventory.
// Plus triggers OnMakeRoot, which spawns a series of asynchronous updates.
//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<ulong> lst = new List<ulong>();
// lst.Add(reg.RegionHandle);
// SendCloseChildAgentAsync(avatar.UUID, lst);
// return;
//}
SetInTransit(sp.UUID);
@ -426,7 +414,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// 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 send the attachments and close things here.
// that the client contacted the destination before we close things here.
if (!WaitForCallback(sp.UUID))
{
m_log.WarnFormat(
@ -437,14 +425,20 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return;
}
// For backwards compatibility
if (version == "Unknown" || version == string.Empty)
{
// CrossAttachmentsIntoNewRegion is a synchronous call. We shouldn't need to wait after it
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Old simulator, sending attachments one by one...");
CrossAttachmentsIntoNewRegion(finalDestination, sp, true);
}
// May need to logout or other cleanup
AgentHasMovedAway(sp, logout);
// Well, this is it. The agent is over there.
KillEntity(sp.Scene, sp.LocalId);
// May need to logout or other cleanup
AgentHasMovedAway(sp.ControllingClient.SessionId, logout);
// Now let's make it officially a child agent
sp.MakeChildAgent();
@ -513,8 +507,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
protected virtual void AgentHasMovedAway(UUID sessionID, bool logout)
protected virtual void AgentHasMovedAway(ScenePresence sp, bool logout)
{
foreach (SceneObjectGroup sop in sp.Attachments)
{
sop.Scene.DeleteSceneObject(sop, true);
}
sp.Attachments.Clear();
}
protected void KillEntity(Scene scene, uint localID)
@ -784,7 +783,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
GridRegion neighbourRegion = scene.GridService.GetRegionByPosition(scene.RegionInfo.ScopeID, (int)x, (int)y);
string reason;
if (!scene.SimulationService.QueryAccess(neighbourRegion, agent.ControllingClient.AgentId, newpos, out reason))
string version;
if (!scene.SimulationService.QueryAccess(neighbourRegion, agent.ControllingClient.AgentId, newpos, out version, out reason))
{
agent.ControllingClient.SendAlertMessage("Cannot region cross into banned parcel");
if (r == null)
@ -804,7 +804,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
agent.InTransit();
CrossAgentToNewRegionDelegate d = CrossAgentToNewRegionAsync;
d.BeginInvoke(agent, newpos, neighbourx, neighboury, neighbourRegion, isFlying, CrossAgentToNewRegionCompleted, d);
d.BeginInvoke(agent, newpos, neighbourx, neighboury, neighbourRegion, isFlying, version, CrossAgentToNewRegionCompleted, d);
return true;
}
@ -861,17 +861,17 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
icon.EndInvoke(iar);
}
public delegate ScenePresence CrossAgentToNewRegionDelegate(ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, GridRegion neighbourRegion, bool isFlying);
public delegate ScenePresence CrossAgentToNewRegionDelegate(ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, GridRegion neighbourRegion, bool isFlying, string version);
/// <summary>
/// This Closes child agents on neighbouring regions
/// Calls an asynchronous method to do so.. so it doesn't lag the sim.
/// </summary>
protected ScenePresence CrossAgentToNewRegionAsync(ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, GridRegion neighbourRegion, bool isFlying)
protected ScenePresence CrossAgentToNewRegionAsync(ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, GridRegion neighbourRegion, bool isFlying, string version)
{
ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize));
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Crossing agent {0} {1} to {2}-{3}", agent.Firstname, agent.Lastname, neighbourx, neighboury);
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Crossing agent {0} {1} to {2}-{3} running version {4}", agent.Firstname, agent.Lastname, neighbourx, neighboury, version);
Scene m_scene = agent.Scene;
@ -945,7 +945,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
agent.SendOtherAgentsAvatarDataToMe();
agent.SendOtherAgentsAppearanceToMe();
// Backwards compatibility
if (version == "Unknown" || version == string.Empty)
{
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Old neighbor, passing attachments one by one...");
CrossAttachmentsIntoNewRegion(neighbourRegion, agent, true);
}
AgentHasMovedAway(agent, false);
// the user may change their profile information in other region,
// so the userinfo in UserProfileCache is not reliable any more, delete it

View File

@ -142,11 +142,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return false;
}
protected override void AgentHasMovedAway(UUID sessionID, bool logout)
protected override void AgentHasMovedAway(ScenePresence sp, bool logout)
{
base.AgentHasMovedAway(sp, logout);
if (logout)
// Log them out of this grid
m_aScene.PresenceService.LogoutAgent(sessionID);
m_aScene.PresenceService.LogoutAgent(sp.ControllingClient.SessionId);
}
protected override bool CreateAgent(ScenePresence sp, GridRegion reg, GridRegion finalDestination, AgentCircuitData agentCircuit, uint teleportFlags, out string reason, out bool logout)

View File

@ -41,6 +41,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation
public class LocalSimulationConnectorModule : ISharedRegionModule, ISimulationService
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
// Version of this service
private const string m_Version = "SIMULATION/0.1";
private List<Scene> m_sceneList = new List<Scene>();
private IEntityTransferModule m_AgentTransferModule;
@ -257,9 +260,10 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation
return false;
}
public bool QueryAccess(GridRegion destination, UUID id, Vector3 position, out string reason)
public bool QueryAccess(GridRegion destination, UUID id, Vector3 position, out string version, out string reason)
{
reason = "Communications failure";
version = m_Version;
if (destination == null)
return false;

View File

@ -229,19 +229,20 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation
}
public bool QueryAccess(GridRegion destination, UUID id, Vector3 position, out string reason)
public bool QueryAccess(GridRegion destination, UUID id, Vector3 position, out string version, out string reason)
{
reason = "Communications failure";
version = "Unknown";
if (destination == null)
return false;
// Try local first
if (m_localBackend.QueryAccess(destination, id, position, out reason))
if (m_localBackend.QueryAccess(destination, id, position, out version, out reason))
return true;
// else do the remote thing
if (!m_localBackend.IsLocalRegion(destination.RegionHandle))
return m_remoteConnector.QueryAccess(destination, id, position, out reason);
return m_remoteConnector.QueryAccess(destination, id, position, out version, out reason);
return false;

View File

@ -2316,7 +2316,9 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns></returns>
public bool IncomingCreateObject(ISceneObject sog)
{
//m_log.Debug(" >>> IncomingCreateObject(sog) <<< " + ((SceneObjectGroup)sog).AbsolutePosition + " deleted? " + ((SceneObjectGroup)sog).IsDeleted);
//m_log.DebugFormat(" >>> IncomingCreateObject(sog) <<< {0} deleted? {1} isAttach? {2}", ((SceneObjectGroup)sog).AbsolutePosition,
// ((SceneObjectGroup)sog).IsDeleted, ((SceneObjectGroup)sog).RootPart.IsAttachment);
SceneObjectGroup newObject;
try
{

View File

@ -3361,6 +3361,8 @@ namespace OpenSim.Region.Framework.Scenes
{
SceneObjectGroup sog = Copy(false);
sog.m_isDeleted = false;
sog.RootPart.IsAttachment = false;
sog.RootPart.GroupPosition = sog.RootPart.AttachedPos;
return sog;
}

View File

@ -3078,54 +3078,6 @@ namespace OpenSim.Region.Framework.Scenes
cAgent.Appearance = new AvatarAppearance(m_appearance);
/*
try
{
// We might not pass the Wearables in all cases...
// They're only needed so that persistent changes to the appearance
// are preserved in the new region where the user is moving to.
// But in Hypergrid we might not let this happen.
int i = 0;
UUID[] wears = new UUID[m_appearance.Wearables.Length * 2];
foreach (AvatarWearable aw in m_appearance.Wearables)
{
if (aw != null)
{
wears[i++] = aw.ItemID;
wears[i++] = aw.AssetID;
}
else
{
wears[i++] = UUID.Zero;
wears[i++] = UUID.Zero;
}
}
cAgent.Wearables = wears;
cAgent.VisualParams = m_appearance.VisualParams;
if (m_appearance.Texture != null)
cAgent.AgentTextures = m_appearance.Texture.GetBytes();
}
catch (Exception e)
{
m_log.Warn("[SCENE PRESENCE]: exception in CopyTo " + e.Message);
}
//Attachments
List<int> attPoints = m_appearance.GetAttachedPoints();
if (attPoints != null)
{
//m_log.DebugFormat("[SCENE PRESENCE]: attachments {0}", attPoints.Count);
int i = 0;
AvatarAttachment[] attachs = new AvatarAttachment[attPoints.Count];
foreach (int point in attPoints)
{
attachs[i++] = new AvatarAttachment(point, m_appearance.GetAttachedItem(point), m_appearance.GetAttachedAsset(point));
}
cAgent.Attachments = attachs;
}
*/
lock (scriptedcontrols)
{
ControllerData[] controls = new ControllerData[scriptedcontrols.Count];
@ -3145,9 +3097,21 @@ namespace OpenSim.Region.Framework.Scenes
}
catch { }
// cAgent.GroupID = ??
// Groups???
// Attachment objects
if (m_attachments != null && m_attachments.Count > 0)
{
cAgent.AttachmentObjects = new List<ISceneObject>();
cAgent.AttachmentObjectStates = new List<string>();
IScriptModule se = m_scene.RequestModuleInterface<IScriptModule>();
foreach (SceneObjectGroup sog in m_attachments)
{
// We need to make a copy and pass that copy
// because of transfers withn the same sim
ISceneObject clone = sog.CloneForNewScene();
cAgent.AttachmentObjects.Add(clone);
cAgent.AttachmentObjectStates.Add(sog.GetStateSnapshot());
}
}
}
public void CopyFrom(AgentData cAgent)
@ -3188,50 +3152,6 @@ namespace OpenSim.Region.Framework.Scenes
AddToPhysicalScene(isFlying);
}
/*
uint i = 0;
try
{
if (cAgent.Wearables == null)
cAgent.Wearables = new UUID[0];
AvatarWearable[] wears = new AvatarWearable[cAgent.Wearables.Length / 2];
for (uint n = 0; n < cAgent.Wearables.Length; n += 2)
{
UUID itemId = cAgent.Wearables[n];
UUID assetId = cAgent.Wearables[n + 1];
wears[i++] = new AvatarWearable(itemId, assetId);
}
// m_appearance.Wearables = wears;
Primitive.TextureEntry textures = null;
if (cAgent.AgentTextures != null && cAgent.AgentTextures.Length > 1)
textures = new Primitive.TextureEntry(cAgent.AgentTextures, 0, cAgent.AgentTextures.Length);
byte[] visuals = null;
if ((cAgent.VisualParams != null) && (cAgent.VisualParams.Length < AvatarAppearance.VISUALPARAM_COUNT))
visuals = (byte[])cAgent.VisualParams.Clone();
m_appearance = new AvatarAppearance(cAgent.AgentID,wears,textures,visuals);
}
catch (Exception e)
{
m_log.Warn("[SCENE PRESENCE]: exception in CopyFrom " + e.Message);
}
// Attachments
try
{
if (cAgent.Attachments != null)
{
m_appearance.ClearAttachments();
foreach (AvatarAttachment att in cAgent.Attachments)
{
m_appearance.SetAttachment(att.AttachPoint, att.ItemID, att.AssetID);
}
}
}
catch { }
*/
try
{
lock (scriptedcontrols)
@ -3261,8 +3181,18 @@ namespace OpenSim.Region.Framework.Scenes
}
catch { }
//cAgent.GroupID = ??
//Groups???
if (cAgent.AttachmentObjects != null && cAgent.AttachmentObjects.Count > 0)
{
m_attachments = new List<SceneObjectGroup>();
int i = 0;
foreach (ISceneObject so in cAgent.AttachmentObjects)
{
((SceneObjectGroup)so).LocalId = 0;
((SceneObjectGroup)so).RootPart.UpdateFlag = 0;
so.SetState(cAgent.AttachmentObjectStates[i++], m_scene);
m_scene.IncomingCreateObject(so);
}
}
}
public bool CopyAgent(out IAgentData agent)

View File

@ -50,6 +50,7 @@ namespace OpenSim.Server.Handlers.Simulation
public class AgentHandler
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private ISimulationService m_SimulationService;
protected bool m_Proxy = false;
@ -275,7 +276,7 @@ namespace OpenSim.Server.Handlers.Simulation
AgentData agent = new AgentData();
try
{
agent.Unpack(args);
agent.Unpack(args, m_SimulationService.GetScene(destination.RegionHandle));
}
catch (Exception ex)
{
@ -295,7 +296,7 @@ namespace OpenSim.Server.Handlers.Simulation
AgentPosition agent = new AgentPosition();
try
{
agent.Unpack(args);
agent.Unpack(args, m_SimulationService.GetScene(destination.RegionHandle));
}
catch (Exception ex)
{
@ -342,7 +343,8 @@ namespace OpenSim.Server.Handlers.Simulation
destination.RegionID = regionID;
string reason;
bool result = m_SimulationService.QueryAccess(destination, id, position, out reason);
string version;
bool result = m_SimulationService.QueryAccess(destination, id, position, out version, out reason);
responsedata["int_response_code"] = HttpStatusCode.OK;
@ -350,6 +352,7 @@ namespace OpenSim.Server.Handlers.Simulation
resp["success"] = OSD.FromBoolean(result);
resp["reason"] = OSD.FromString(reason);
resp["version"] = OSD.FromString(version);
responsedata["str_response_string"] = OSDParser.SerializeJsonString(resp);
}

View File

@ -241,7 +241,7 @@ namespace OpenSim.Services.Connectors.Simulation
if (args != null)
{
agent = new CompleteAgentData();
agent.Unpack(args);
agent.Unpack(args, null);
return true;
}
}
@ -256,9 +256,10 @@ namespace OpenSim.Services.Connectors.Simulation
/// <summary>
/// </summary>
public bool QueryAccess(GridRegion destination, UUID id, Vector3 position, out string reason)
public bool QueryAccess(GridRegion destination, UUID id, Vector3 position, out string version, out string reason)
{
reason = "Failed to contact destination";
version = "Unknown";
// m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: QueryAccess start, position={0}", position);
@ -274,23 +275,27 @@ namespace OpenSim.Services.Connectors.Simulation
try
{
OSDMap result = WebUtil.ServiceOSDRequest(uri, request, "QUERYACCESS", 10000);
bool success = result["success"].AsBoolean();
reason = result["reason"].AsString();
OSDMap data = (OSDMap)result["_Result"];
//m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: QueryAccess to {0} returned {1}", uri, success);
bool success = result["success"].AsBoolean();
reason = data["reason"].AsString();
if (data["version"] != null)
version = data["version"].AsString();
m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: QueryAccess to {0} returned {1} version {2} ({3})", uri, success, version, data["version"].AsString());
if (!success)
{
if (result.ContainsKey("Message"))
if (data.ContainsKey("Message"))
{
string message = result["Message"].AsString();
string message = data["Message"].AsString();
if (message == "Service request failed: [MethodNotAllowed] MethodNotAllowed") // Old style region
{
m_log.Info("[REMOTE SIMULATION CONNECTOR]: The above web util error was caused by a TP to a sim that doesn't support QUERYACCESS and can be ignored");
return true;
}
reason = result["Message"];
reason = data["Message"];
}
else
{

View File

@ -67,7 +67,7 @@ namespace OpenSim.Services.Interfaces
bool RetrieveAgent(GridRegion destination, UUID id, out IAgentData agent);
bool QueryAccess(GridRegion destination, UUID id, Vector3 position, out string reason);
bool QueryAccess(GridRegion destination, UUID id, Vector3 position, out string version, out string reason);
/// <summary>
/// Message from receiving region to departing region, telling it got contacted by the client.