Files
libremetaverse/Programs/Simian/Simian.cs
John Hurliman 11776ef202 libomv:
* Completed DeRezDestination enum
Simian:
* Added IInventoryProvider to allow other extensions to create inventory items
* Basic derez support for deleting items (the new inventory items in the trash won't actually work because the assetID is meaningless until we have an IAssetProvider)

git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@2175 52acb1d6-8a22-11de-b505-999d5b087335
2008-08-27 23:27:48 +00:00

387 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Text;
using System.Xml;
using System.Threading;
using OpenMetaverse;
using OpenMetaverse.Capabilities;
using OpenMetaverse.Packets;
namespace Simian
{
public partial class Simian
{
public string DataDir = "SimianData/";
public HttpServer HttpServer;
public UDPServer UDPServer;
public ulong RegionHandle;
public float[] Heightmap = new float[65536];
public float WaterHeight = 35.0f;
public Dictionary<UUID, Asset> AssetStore = new Dictionary<UUID, Asset>();
// Interfaces
public IAvatarProvider Avatars;
public IInventoryProvider Inventory;
public IMeshingProvider Mesher;
/// <summary>All of the agents currently connected to this UDP server</summary>
public Dictionary<IPEndPoint, Agent> Agents = new Dictionary<IPEndPoint, Agent>();
const uint regionX = 256000;
const uint regionY = 256000;
Dictionary<uint, Agent> unassociatedAgents;
int currentCircuitCode;
int tcpPort;
int udpPort;
public Simian()
{
unassociatedAgents = new Dictionary<uint, Agent>();
currentCircuitCode = 0;
}
public void Start(int port, bool ssl)
{
// Put UDP listening on the same port number as the HTTP server for simplicity
tcpPort = port;
udpPort = port;
InitUDPServer(udpPort);
InitHttpServer(tcpPort, ssl);
RegionHandle = Helpers.UIntsToLong(regionX, regionY);
// Load all of the extensions
ExtensionLoader.LoadAllExtensions(AppDomain.CurrentDomain.BaseDirectory, this);
foreach (ISimianExtension extension in ExtensionLoader.Extensions)
{
Logger.DebugLog("Loading extension " + extension.GetType().Name);
extension.Start();
// Assign to an interface if possible
TryAssignToInterface(extension);
}
if (!CheckInterfaces())
{
Logger.Log("Missing interfaces, shutting down", Helpers.LogLevel.Error);
Stop();
}
}
public void Stop()
{
foreach (ISimianExtension extension in ExtensionLoader.Extensions)
extension.Stop();
UDPServer.Stop();
HttpServer.Stop();
}
public bool TryGetUnassociatedAgent(uint circuitCode, out Agent agent)
{
if (unassociatedAgents.TryGetValue(circuitCode, out agent))
{
lock (unassociatedAgents)
unassociatedAgents.Remove(circuitCode);
return true;
}
else
{
return false;
}
}
void TryAssignToInterface(ISimianExtension extension)
{
if (extension is IAvatarProvider)
{
Avatars = (IAvatarProvider)extension;
}
else if (extension is IInventoryProvider)
{
Inventory = (IInventoryProvider)extension;
}
else if (extension is IMeshingProvider)
{
Mesher = (IMeshingProvider)extension;
}
}
bool CheckInterfaces()
{
if (Avatars == null)
{
Logger.Log("No IAvatarProvider interface loaded", Helpers.LogLevel.Error);
return false;
}
if (Inventory == null)
{
Logger.Log("No IInventoryProvider interface loaded", Helpers.LogLevel.Error);
return false;
}
if (Mesher == null)
{
Logger.Log("No IMeshingProvider interface loaded", Helpers.LogLevel.Error);
return false;
}
return true;
}
void InitUDPServer(int port)
{
UDPServer = new UDPServer(port, this);
}
void InitHttpServer(int port, bool ssl)
{
HttpServer = new HttpServer(tcpPort, ssl);
// Login webpage HEAD request, used to check if the login webpage is alive
HttpRequestSignature signature = new HttpRequestSignature();
signature.Method = "head";
signature.ContentType = String.Empty;
signature.Path = "/loginpage";
HttpServer.HttpRequestCallback callback = new HttpServer.HttpRequestCallback(LoginWebpageHeadHandler);
HttpServer.HttpRequestHandler handler = new HttpServer.HttpRequestHandler(signature, callback);
HttpServer.AddHandler(handler);
// Login webpage GET request, gets the login webpage data (purely aesthetic)
signature.Method = "get";
signature.ContentType = String.Empty;
signature.Path = "/loginpage";
callback = new HttpServer.HttpRequestCallback(LoginWebpageGetHandler);
handler.Signature = signature;
handler.Callback = callback;
HttpServer.AddHandler(handler);
// Client XML-RPC login
signature.Method = "post";
signature.ContentType = "text/xml";
signature.Path = "/login";
callback = new HttpServer.HttpRequestCallback(LoginXmlRpcPostHandler);
handler.Signature = signature;
handler.Callback = callback;
HttpServer.AddHandler(handler);
// Client LLSD login
signature.Method = "post";
signature.ContentType = "application/xml";
signature.Path = "/login";
callback = new HttpServer.HttpRequestCallback(LoginLLSDPostHandler);
handler.Signature = signature;
handler.Callback = callback;
HttpServer.AddHandler(handler);
HttpServer.Start();
}
void LoginWebpageHeadHandler(HttpRequestSignature signature, ref HttpListenerContext context)
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.StatusDescription = "OK";
}
void LoginWebpageGetHandler(HttpRequestSignature signature, ref HttpListenerContext context)
{
string pageContent = "<html><head><title>Simian</title></head><body><br/><h1>Welcome to Simian</h1></body></html>";
byte[] pageData = Encoding.UTF8.GetBytes(pageContent);
context.Response.OutputStream.Write(pageData, 0, pageData.Length);
context.Response.Close();
}
void LoginXmlRpcPostHandler(HttpRequestSignature signature, ref HttpListenerContext context)
{
string
firstName = String.Empty,
lastName = String.Empty,
password = String.Empty,
start = String.Empty,
version = String.Empty,
channel = String.Empty;
try
{
// Parse the incoming XML
XmlReader reader = XmlReader.Create(context.Request.InputStream);
reader.ReadStartElement("methodCall");
{
string methodName = reader.ReadElementContentAsString("methodName", String.Empty);
if (methodName == "login_to_simulator")
{
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 "first":
firstName = reader.ReadElementContentAsString("string", String.Empty);
break;
case "last":
lastName = reader.ReadElementContentAsString("string", String.Empty);
break;
case "passwd":
password = reader.ReadElementContentAsString("string", String.Empty);
break;
case "start":
start = reader.ReadElementContentAsString("string", String.Empty);
break;
case "version":
version = reader.ReadElementContentAsString("string", String.Empty);
break;
case "channel":
channel = reader.ReadElementContentAsString("string", String.Empty);
break;
default:
if (reader.Name == "string")
Console.WriteLine(String.Format("Ignore login xml value: name={0}, value={1}", name, reader.ReadInnerXml()));
else
Console.WriteLine(String.Format("Unknown login xml: name={0}, value={1}", name, reader.ReadInnerXml()));
break;
}
}
reader.ReadEndElement();
}
reader.ReadEndElement();
}
}
reader.ReadEndElement();
reader.ReadEndElement();
reader.ReadEndElement();
reader.ReadEndElement();
}
}
reader.ReadEndElement();
reader.Close();
LoginResponseData responseData = HandleLogin(firstName, lastName, password, start, version, channel);
XmlWriter writer = XmlWriter.Create(context.Response.OutputStream);
responseData.ToXmlRpc(writer);
writer.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
void LoginLLSDPostHandler(HttpRequestSignature signature, ref HttpListenerContext context)
{
string body = String.Empty;
using (StreamReader reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
{
body = reader.ReadToEnd();
}
Console.WriteLine(body);
}
LoginResponseData HandleLogin(string firstName, string lastName, string password, string start, string version, string channel)
{
uint regionX = 256000;
uint regionY = 256000;
Agent agent = new Agent(UDPServer);
agent.AgentID = UUID.Random();
agent.FirstName = firstName;
agent.LastName = lastName;
agent.SessionID = UUID.Random();
agent.SecureSessionID = UUID.Random();
agent.CircuitCode = CreateAgentCircuit(agent);
agent.InventoryRoot = UUID.Random();
agent.InventoryLibRoot = UUID.Random();
agent.Flags = PrimFlags.Physics | PrimFlags.ObjectModify | PrimFlags.ObjectCopy |
PrimFlags.ObjectAnyOwner | PrimFlags.ObjectMove | PrimFlags.InventoryEmpty |
PrimFlags.ObjectTransfer | PrimFlags.ObjectOwnerModify | PrimFlags.ObjectYouOwner;
// Setup the agent inventory
InventoryFolder rootFolder = new InventoryFolder();
rootFolder.ID = agent.InventoryRoot;
rootFolder.Name = "Inventory";
rootFolder.OwnerID = agent.AgentID;
rootFolder.PreferredType = AssetType.RootFolder;
rootFolder.Version = 1;
agent.Inventory[rootFolder.ID] = rootFolder;
// Setup the default library
InventoryFolder libRootFolder = new InventoryFolder();
libRootFolder.ID = agent.InventoryLibRoot;
libRootFolder.Name = "Library";
libRootFolder.OwnerID = agent.AgentID;
libRootFolder.PreferredType = AssetType.RootFolder;
libRootFolder.Version = 1;
agent.Library[libRootFolder.ID] = libRootFolder;
IPHostEntry addresses = Dns.GetHostByName(Dns.GetHostName());
IPAddress simIP = addresses.AddressList.Length > 0 ? addresses.AddressList[0] : IPAddress.Loopback;
// Setup default login response values
LoginResponseData response;
response.AgentID = agent.AgentID;
response.SecureSessionID = agent.SecureSessionID;
response.SessionID = agent.SessionID;
response.CircuitCode = agent.CircuitCode;
response.AgentAccess = "M";
response.BuddyList = null;
response.FirstName = agent.FirstName;
response.HomeLookAt = Vector3.UnitX;
response.HomePosition = new Vector3(128f, 128f, 25f);
response.HomeRegion = Helpers.UIntsToLong(regionX, regionY);
response.InventoryRoot = agent.InventoryRoot;
response.InventorySkeleton = null;
response.LastName = agent.LastName;
response.LibraryOwner = response.AgentID;
response.LibraryRoot = agent.InventoryLibRoot;
response.LibrarySkeleton = null;
response.LookAt = Vector3.UnitX;
response.Message = "Welcome to Simian";
response.Reason = String.Empty;
response.RegionX = regionX;
response.RegionY = regionY;
response.SecondsSinceEpoch = DateTime.Now;
// FIXME: Actually generate a seed capability
response.SeedCapability = String.Format("http://{0}:{1}/seed_caps", simIP, tcpPort);
response.SimIP = simIP;
response.SimPort = (ushort)udpPort;
response.StartLocation = "last";
response.Success = true;
return response;
}
uint CreateAgentCircuit(Agent agent)
{
uint circuitCode = (uint)Interlocked.Increment(ref currentCircuitCode);
// Put this client in the list of clients that have not been associated with an IPEndPoint yet
lock (unassociatedAgents)
unassociatedAgents[circuitCode] = agent;
Logger.Log("Created a circuit for " + agent.FirstName, Helpers.LogLevel.Info);
return circuitCode;
}
}
}