Files
libremetaverse/Programs/Simian/Extensions/MapLocal.cs
John Hurliman 06404ab4e4 * Fixed endianness for OSD handling of uint, long, and ulong (big endian byte arrays are used)
* Fixed CapsServer to handle all content types (old SL clients were sending application/xml, new ones send application/llsd+xml)
* Start the EventQueue ID at 1 (seems to help)
* HyperGrid teleporting is now working in Simian

git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2428 52acb1d6-8a22-11de-b505-999d5b087335
2009-02-05 03:12:41 +00:00

591 lines
28 KiB
C#

using System;
using System.IO;
using System.Net;
using System.Xml;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenMetaverse.StructuredData;
namespace Simian.Extensions
{
class HyperGridLink
{
public string RegionName;
public ulong RegionHandle;
public UUID RegionID;
public UUID RegionImage;
public int UDPPort;
public int RemotingPort;
}
public class MapLocal : IExtension<Simian>
{
Simian server;
public MapLocal()
{
}
public void Start(Simian server)
{
this.server = server;
server.UDP.RegisterPacketCallback(PacketType.MapLayerRequest, MapLayerRequestHandler);
server.UDP.RegisterPacketCallback(PacketType.MapBlockRequest, MapBlockRequestHandler);
server.UDP.RegisterPacketCallback(PacketType.TeleportRequest, TeleportRequestHandler);
server.UDP.RegisterPacketCallback(PacketType.TeleportLocationRequest, TeleportLocationRequestHandler);
}
public void Stop()
{
}
void MapLayerRequestHandler(Packet packet, Agent agent)
{
MapLayerRequestPacket request = (MapLayerRequestPacket)packet;
GridLayerType type = (GridLayerType)request.AgentData.Flags;
MapLayerReplyPacket reply = new MapLayerReplyPacket();
reply.AgentData.AgentID = agent.Avatar.ID;
reply.AgentData.Flags = (uint)type;
reply.LayerData = new MapLayerReplyPacket.LayerDataBlock[1];
reply.LayerData[0] = new MapLayerReplyPacket.LayerDataBlock();
reply.LayerData[0].Bottom = 0;
reply.LayerData[0].Left = 0;
reply.LayerData[0].Top = UInt16.MaxValue;
reply.LayerData[0].Right = UInt16.MaxValue;
reply.LayerData[0].ImageID = new UUID("89556747-24cb-43ed-920b-47caed15465f");
server.UDP.SendPacket(agent.Avatar.ID, reply, PacketCategory.Transaction);
}
void MapBlockRequestHandler(Packet packet, Agent agent)
{
MapBlockRequestPacket request = (MapBlockRequestPacket)packet;
GridLayerType type = (GridLayerType)request.AgentData.Flags;
MapBlockReplyPacket reply = new MapBlockReplyPacket();
reply.AgentData.AgentID = agent.Avatar.ID;
reply.AgentData.Flags = (uint)type;
reply.Data = new MapBlockReplyPacket.DataBlock[2];
reply.Data[0] = new MapBlockReplyPacket.DataBlock();
reply.Data[0].Access = (byte)SimAccess.Min;
reply.Data[0].Agents = (byte)server.Scene.AgentCount();
reply.Data[0].MapImageID = new UUID("89556747-24cb-43ed-920b-47caed15465f");
reply.Data[0].Name = Utils.StringToBytes(server.Scene.RegionName);
reply.Data[0].RegionFlags = (uint)server.Scene.RegionFlags;
reply.Data[0].WaterHeight = (byte)server.Scene.WaterHeight;
reply.Data[0].X = (ushort)server.Scene.RegionX;
reply.Data[0].Y = (ushort)server.Scene.RegionY;
reply.Data[1] = new MapBlockReplyPacket.DataBlock();
reply.Data[1].Access = (byte)SimAccess.Min;
reply.Data[1].Agents = 0;
reply.Data[1].MapImageID = new UUID("89556747-24cb-43ed-920b-47caed15465f");
reply.Data[1].Name = Utils.StringToBytes("HyperGrid Portal to OSGrid");
reply.Data[1].RegionFlags = (uint)server.Scene.RegionFlags;
reply.Data[1].WaterHeight = (byte)server.Scene.WaterHeight;
reply.Data[1].X = (ushort)(server.Scene.RegionX + 1);
reply.Data[1].Y = (ushort)server.Scene.RegionY;
server.UDP.SendPacket(agent.Avatar.ID, reply, PacketCategory.Transaction);
}
void TeleportRequestHandler(Packet packet, Agent agent)
{
TeleportRequestPacket request = (TeleportRequestPacket)packet;
// TODO: Stand the avatar up first
if (request.Info.RegionID == server.Scene.RegionID)
{
// Local teleport
agent.Avatar.Position = request.Info.Position;
agent.CurrentLookAt = request.Info.LookAt;
TeleportLocalPacket reply = new TeleportLocalPacket();
reply.Info.AgentID = agent.Avatar.ID;
reply.Info.LocationID = 0; // Unused by the client
reply.Info.LookAt = agent.CurrentLookAt;
reply.Info.Position = agent.Avatar.Position;
// TODO: Need a "Flying" boolean for Agent
reply.Info.TeleportFlags = (uint)TeleportFlags.ViaRegionID;
server.UDP.SendPacket(agent.Avatar.ID, reply, PacketCategory.Transaction);
}
else
{
TeleportFailedPacket reply = new TeleportFailedPacket();
reply.Info.AgentID = agent.Avatar.ID;
reply.Info.Reason = Utils.StringToBytes("Unknown region");
server.UDP.SendPacket(agent.Avatar.ID, reply, PacketCategory.Transaction);
}
}
void TeleportLocationRequestHandler(Packet packet, Agent agent)
{
TeleportLocationRequestPacket request = (TeleportLocationRequestPacket)packet;
// TODO: Stand the avatar up first
if (request.Info.RegionHandle == server.Scene.RegionHandle)
{
// Local teleport
agent.Avatar.Position = request.Info.Position;
agent.CurrentLookAt = request.Info.LookAt;
TeleportLocalPacket reply = new TeleportLocalPacket();
reply.Info.AgentID = agent.Avatar.ID;
reply.Info.LocationID = 0; // Unused by the client
reply.Info.LookAt = agent.CurrentLookAt;
reply.Info.Position = agent.Avatar.Position;
// TODO: Need a "Flying" boolean for Agent
reply.Info.TeleportFlags = (uint)TeleportFlags.ViaLocation;
server.UDP.SendPacket(agent.Avatar.ID, reply, PacketCategory.Transaction);
}
else if (request.Info.RegionHandle == Utils.UIntsToLong((server.Scene.RegionX + 1) * 256, server.Scene.RegionY * 256))
{
// Special case: adjacent simulator is the HyperGrid portal
HyperGridTeleport(agent, new Uri("http://osl2.nac.uci.edu:9006/"), request.Info.Position);
}
else
{
TeleportFailedPacket reply = new TeleportFailedPacket();
reply.Info.AgentID = agent.Avatar.ID;
reply.Info.Reason = Utils.StringToBytes("Unknown region");
server.UDP.SendPacket(agent.Avatar.ID, reply, PacketCategory.Transaction);
}
}
bool HyperGridTeleport(Agent agent, Uri destination, Vector3 destPos)
{
HyperGridLink link;
TeleportProgress(agent, "Resolving destination IP address", TeleportFlags.ViaLocation);
IPHostEntry entry = Dns.GetHostEntry(destination.DnsSafeHost);
if (entry.AddressList != null && entry.AddressList.Length >= 1)
{
TeleportProgress(agent, "Retrieving destination details", TeleportFlags.ViaLocation);
if (LinkRegion(destination, out link))
{
TeleportProgress(agent, "Creating foreign agent", TeleportFlags.ViaLocation);
// This is a crufty part of the HyperGrid protocol. We need to generate a fragment of a UUID
// (missing the last four digits) and send that as the caps_path variable. Locally, we expand
// that out to http://foreignsim:httpport/CAPS/fragment0000/ and use it as the seed caps path
// that is sent to the client
UUID seedID = UUID.Random();
string seedCapFragment = seedID.ToString().Substring(0, 32);
Uri seedCap = new Uri(destination, "/CAPS/" + seedCapFragment + "0000/");
if (ExpectHyperGridUser(agent, destination, destPos, link, seedCap))
{
TeleportProgress(agent, "Establishing foreign agent presence", TeleportFlags.ViaLocation);
if (CreateChildAgent(agent, destination, destPos, link, seedCapFragment))
{
// Send the final teleport message to the client
if (server.Scene.HasRunningEventQueue(agent))
{
uint x, y;
Utils.LongToUInts(link.RegionHandle, out x, out y);
x /= 256;
y /= 256;
Logger.Log(String.Format("HyperGrid teleporting to {0} ({1}, {2}) @ {3}",
link.RegionName, x, y, destination), Helpers.LogLevel.Info);
OSDMap info = new OSDMap();
info.Add("AgentID", OSD.FromUUID(agent.Avatar.ID));
info.Add("LocationID", OSD.FromInteger(4)); // Unused by the client
info.Add("RegionHandle", OSD.FromULong(link.RegionHandle));
info.Add("SeedCapability", OSD.FromUri(seedCap));
info.Add("SimAccess", OSD.FromInteger((byte)SimAccess.Min));
info.Add("SimIP", OSD.FromBinary(entry.AddressList[0].GetAddressBytes()));
info.Add("SimPort", OSD.FromInteger(link.UDPPort));
info.Add("TeleportFlags", OSD.FromUInteger((uint)TeleportFlags.ViaLocation));
OSDArray infoArray = new OSDArray(1);
infoArray.Add(info);
OSDMap teleport = new OSDMap();
teleport.Add("Info", infoArray);
server.Scene.SendEvent(agent, "TeleportFinish", teleport);
}
else
{
Logger.Log("No running EventQueue for " + agent.FullName + ", sending TeleportFinish over UDP",
Helpers.LogLevel.Warning);
TeleportFinishPacket teleport = new TeleportFinishPacket();
teleport.Info.AgentID = agent.Avatar.ID;
teleport.Info.LocationID = 0; // Unused by the client
teleport.Info.RegionHandle = link.RegionHandle;
teleport.Info.SeedCapability = Utils.StringToBytes(seedCap.ToString());
teleport.Info.SimAccess = (byte)SimAccess.Min;
teleport.Info.SimIP = Utils.BytesToUInt(entry.AddressList[0].GetAddressBytes());
teleport.Info.SimPort = (ushort)link.UDPPort;
teleport.Info.TeleportFlags = (uint)TeleportFlags.ViaLocation;
server.UDP.SendPacket(agent.Avatar.ID, teleport, PacketCategory.Transaction);
}
// Remove the agent from the local scene (will also tear down the UDP connection)
//server.Scene.ObjectRemove(this, agent.Avatar.ID);
return true;
}
}
}
}
return false;
}
bool LinkRegion(Uri destination, out HyperGridLink link)
{
try
{
#region Build Request
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(destination);
request.Method = "POST";
request.ContentType = "text/xml";
MemoryStream memoryStream = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(memoryStream))
{
writer.WriteStartElement("methodCall");
{
writer.WriteElementString("methodName", "link_region");
writer.WriteStartElement("params");
writer.WriteStartElement("param");
writer.WriteStartElement("value");
writer.WriteStartElement("struct");
{
WriteStringMember(writer, "region_name", String.Empty);
}
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.Flush();
}
request.ContentLength = memoryStream.Length;
using (Stream writeStream = request.GetRequestStream())
{
writeStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
#endregion Build Request
#region Parse Response
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
using (XmlReader reader = XmlReader.Create(response.GetResponseStream(), settings))
{
link = new HyperGridLink();
reader.ReadStartElement("methodResponse");
{
reader.ReadStartElement("params");
reader.ReadStartElement("param");
reader.ReadStartElement("value");
reader.ReadStartElement("struct");
{
while (reader.Name == "member")
{
reader.ReadStartElement("member");
{
string name = reader.ReadElementContentAsString("name", String.Empty);
reader.ReadStartElement("value");
{
switch (name)
{
case "region_name":
link.RegionName = reader.ReadElementContentAsString("string", String.Empty);
break;
case "handle":
string handle = reader.ReadElementContentAsString("string", String.Empty);
link.RegionHandle = UInt64.Parse(handle);
break;
case "uuid":
string uuid = reader.ReadElementContentAsString("string", String.Empty);
link.RegionID = UUID.Parse(uuid);
break;
case "internal_port":
link.UDPPort = reader.ReadElementContentAsInt("string", String.Empty);
break;
case "region_image":
string imageuuid = reader.ReadElementContentAsString("string", String.Empty);
link.RegionImage = UUID.Parse(imageuuid);
break;
case "remoting_port":
link.RemotingPort = reader.ReadElementContentAsInt("string", String.Empty);
break;
default:
Logger.Log("[HyperGrid] Unrecognized response XML chunk: " + reader.ReadInnerXml(),
Helpers.LogLevel.Warning);
break;
}
}
reader.ReadEndElement();
}
reader.ReadEndElement();
}
}
reader.ReadEndElement();
reader.ReadEndElement();
reader.ReadEndElement();
reader.ReadEndElement();
}
reader.ReadEndElement();
return true;
}
#endregion Parse Response
}
catch (Exception ex)
{
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
}
link = null;
return false;
}
bool ExpectHyperGridUser(Agent agent, Uri destination, Vector3 destPos, HyperGridLink link, Uri seedCap)
{
try
{
#region Build Request
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(destination);
request.Method = "POST";
request.ContentType = "text/xml";
MemoryStream memoryStream = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(memoryStream))
{
writer.WriteStartElement("methodCall");
{
writer.WriteElementString("methodName", "expect_hg_user");
writer.WriteStartElement("params");
writer.WriteStartElement("param");
writer.WriteStartElement("value");
writer.WriteStartElement("struct");
{
WriteStringMember(writer, "session_id", agent.SessionID.ToString());
WriteStringMember(writer, "secure_session_id", agent.SecureSessionID.ToString());
WriteStringMember(writer, "firstname", agent.FirstName);
WriteStringMember(writer, "lastname", agent.LastName);
WriteStringMember(writer, "agent_id", agent.Avatar.ID.ToString());
WriteStringMember(writer, "circuit_code", agent.CircuitCode.ToString());
WriteStringMember(writer, "startpos_x", destPos.X.ToString(Utils.EnUsCulture));
WriteStringMember(writer, "startpos_y", destPos.Y.ToString(Utils.EnUsCulture));
WriteStringMember(writer, "startpos_z", destPos.Z.ToString(Utils.EnUsCulture));
WriteStringMember(writer, "caps_path", seedCap.ToString());
WriteStringMember(writer, "region_uuid", link.RegionID.ToString());
//WriteStringMember(writer, "userserver_id", "");
//WriteStringMember(writer, "assetserver_id", "");
//WriteStringMember(writer, "inventoryserver_id", "");
WriteStringMember(writer, "root_folder_id", agent.InventoryRoot.ToString());
WriteStringMember(writer, "internal_port", server.HttpPort.ToString());
WriteStringMember(writer, "regionhandle", server.Scene.RegionHandle.ToString());
WriteStringMember(writer, "home_address", IPAddress.Loopback.ToString());
WriteStringMember(writer, "home_port", server.HttpPort.ToString());
WriteStringMember(writer, "home_remoting", server.HttpPort.ToString());
}
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.Flush();
}
request.ContentLength = memoryStream.Length;
using (Stream writeStream = request.GetRequestStream())
{
writeStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
#endregion Build Request
#region Parse Response
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
using (XmlReader reader = XmlReader.Create(response.GetResponseStream(), settings))
{
bool success = false;
string reason = String.Empty;
reader.ReadStartElement("methodResponse");
{
reader.ReadStartElement("params");
reader.ReadStartElement("param");
reader.ReadStartElement("value");
reader.ReadStartElement("struct");
{
while (reader.Name == "member")
{
reader.ReadStartElement("member");
{
string name = reader.ReadElementContentAsString("name", String.Empty);
reader.ReadStartElement("value");
{
switch (name)
{
case "success":
success = (reader.ReadElementContentAsString("string", String.Empty).ToUpper() == "TRUE");
break;
case "reason":
reason = reader.ReadElementContentAsString("string", String.Empty);
break;
default:
Logger.Log("[HyperGrid] Unrecognized response XML chunk: " + reader.ReadInnerXml(),
Helpers.LogLevel.Warning);
break;
}
}
reader.ReadEndElement();
}
reader.ReadEndElement();
}
}
reader.ReadEndElement();
reader.ReadEndElement();
reader.ReadEndElement();
reader.ReadEndElement();
}
reader.ReadEndElement();
if (!success)
Logger.Log("[HyperGrid] Teleport failed, reason: " + reason, Helpers.LogLevel.Warning);
return success;
}
#endregion Parse Response
}
catch (Exception ex)
{
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
}
return false;
}
bool CreateChildAgent(Agent agent, Uri destination, Vector3 destPos, HyperGridLink link, string seedCapFragment)
{
try
{
destination = new Uri(destination, "/agent/" + agent.Avatar.ID.ToString() + "/");
OSDMap args = new OSDMap();
args["agent_id"] = OSD.FromUUID(agent.Avatar.ID);
args["base_folder"] = OSD.FromUUID(UUID.Zero);
args["caps_path"] = OSD.FromString(seedCapFragment);
args["children_seeds"] = OSD.FromBoolean(false);
args["child"] = OSD.FromBoolean(false);
args["circuit_code"] = OSD.FromString(agent.CircuitCode.ToString());
args["first_name"] = OSD.FromString(agent.FirstName);
args["last_name"] = OSD.FromString(agent.LastName);
args["inventory_folder"] = OSD.FromUUID(agent.InventoryRoot);
args["secure_session_id"] = OSD.FromUUID(agent.SecureSessionID);
args["session_id"] = OSD.FromUUID(agent.SessionID);
args["start_pos"] = OSD.FromString(destPos.ToString());
args["destination_handle"] = OSD.FromString(link.RegionHandle.ToString());
LitJson.JsonData jsonData = OSDParser.SerializeJson(args);
byte[] data = System.Text.Encoding.UTF8.GetBytes(jsonData.ToJson());
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(destination);
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = data.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
requestStream.Flush();
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
bool success = false;
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
Boolean.TryParse(reader.ReadToEnd(), out success);
}
return success;
}
catch (Exception ex)
{
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
}
return false;
}
void TeleportProgress(Agent agent, string message, TeleportFlags flags)
{
TeleportProgressPacket progress = new TeleportProgressPacket();
progress.AgentData.AgentID = agent.Avatar.ID;
progress.Info.Message = Utils.StringToBytes(message);
progress.Info.TeleportFlags = (uint)flags;
server.UDP.SendPacket(agent.Avatar.ID, progress, PacketCategory.Transaction);
}
static void WriteStringMember(XmlWriter writer, string name, string value)
{
writer.WriteStartElement("member");
{
writer.WriteElementString("name", name);
writer.WriteStartElement("value");
{
writer.WriteElementString("string", value);
}
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
}