diff --git a/OpenMetaverse/Primitives/Primitive.cs b/OpenMetaverse/Primitives/Primitive.cs
index eb9c9230..258e8d42 100644
--- a/OpenMetaverse/Primitives/Primitive.cs
+++ b/OpenMetaverse/Primitives/Primitive.cs
@@ -1052,7 +1052,7 @@ namespace OpenMetaverse
///
public Vector3 Scale;
///
- public Quaternion Rotation;
+ public Quaternion Rotation = Quaternion.Identity;
///
public Vector3 Velocity;
///
diff --git a/OpenMetaverse/Types/Quaternion.cs b/OpenMetaverse/Types/Quaternion.cs
index 170ef8b5..e6d78e23 100644
--- a/OpenMetaverse/Types/Quaternion.cs
+++ b/OpenMetaverse/Types/Quaternion.cs
@@ -275,7 +275,7 @@ namespace OpenMetaverse
}
///
- /// Conver this quaternion to an angle around an axis
+ /// Convert this quaternion to an angle around an axis
///
/// Unit vector describing the axis
/// Angle around the axis, in radians
@@ -317,7 +317,7 @@ namespace OpenMetaverse
///
/// Returns the conjugate (spatial inverse) of a quaternion
///
- private static Quaternion Conjugate(Quaternion quaternion)
+ public static Quaternion Conjugate(Quaternion quaternion)
{
quaternion.X = -quaternion.X;
quaternion.Y = -quaternion.Y;
diff --git a/Programs/Simian.Tests/SceneManagerTests.cs b/Programs/Simian.Tests/SceneManagerTests.cs
index 7f69feab..ccfcb9f2 100644
--- a/Programs/Simian.Tests/SceneManagerTests.cs
+++ b/Programs/Simian.Tests/SceneManagerTests.cs
@@ -28,10 +28,10 @@ namespace SimianTests
sceneManager.Start(simian);
agent = CreateDummyAgent();
- simian.Agents.Add(agent.Avatar.ID, agent);
+ simian.Scene.AgentAdd(this, agent, PrimFlags.None);
observer = CreateDummyAgent();
- simian.Agents.Add(observer.Avatar.ID, observer);
+ simian.Scene.AgentAdd(this, observer, PrimFlags.None);
}
[TearDown]
@@ -74,7 +74,7 @@ namespace SimianTests
);
simian.UDP.OnOutgoingPacket += callback;
- sceneManager.AvatarAppearance(this, agent, textures, visualParams);
+ sceneManager.AgentAppearance(this, agent, textures, visualParams);
Assert.IsTrue(callbackEvent.WaitOne(1000, false), "Timed out waiting for callback");
simian.UDP.OnOutgoingPacket -= callback;
diff --git a/Programs/Simian/Extensions/AvatarManager.cs b/Programs/Simian/Extensions/AvatarManager.cs
index b6c488dd..d4084107 100644
--- a/Programs/Simian/Extensions/AvatarManager.cs
+++ b/Programs/Simian/Extensions/AvatarManager.cs
@@ -172,7 +172,7 @@ namespace Simian.Extensions
AvatarPropertiesRequestPacket request = (AvatarPropertiesRequestPacket)packet;
Agent foundAgent;
- if (server.Agents.TryGetValue(request.AgentData.AvatarID, out foundAgent))
+ if (server.Scene.TryGetAgent(request.AgentData.AvatarID, out foundAgent))
{
AvatarPropertiesReplyPacket reply = new AvatarPropertiesReplyPacket();
reply.AgentData.AgentID = agent.Avatar.ID;
@@ -348,7 +348,7 @@ namespace Simian.Extensions
for (int i = 0; i < set.VisualParam.Length; i++)
visualParams[i] = set.VisualParam[i].ParamValue;
- server.Scene.AvatarAppearance(this, agent, textureEntry, visualParams);
+ server.Scene.AgentAppearance(this, agent, textureEntry, visualParams);
}
void AgentCachedTextureHandler(Packet packet, Agent agent)
@@ -396,7 +396,7 @@ namespace Simian.Extensions
reply.UUIDNameBlock[i].ID = id;
Agent foundAgent;
- if (server.Agents.TryGetValue(id, out foundAgent))
+ if (server.Scene.TryGetAgent(id, out foundAgent))
{
reply.UUIDNameBlock[i].FirstName = Utils.StringToBytes(foundAgent.FirstName);
reply.UUIDNameBlock[i].LastName = Utils.StringToBytes(foundAgent.LastName);
@@ -413,45 +413,50 @@ namespace Simian.Extensions
void CoarseLocationTimer_Elapsed(object sender)
{
- lock (server.Agents)
- {
- foreach (Agent recipient in server.Agents.Values)
+ // Create lists containing all of the agent blocks
+ List agentDatas = new List();
+ List agentLocations = new List();
+
+ server.Scene.ForEachAgent(
+ delegate(Agent agent)
{
- int i = 0;
+ CoarseLocationUpdatePacket.AgentDataBlock dataBlock = new CoarseLocationUpdatePacket.AgentDataBlock();
+ dataBlock.AgentID = agent.Avatar.ID;
+ CoarseLocationUpdatePacket.LocationBlock locationBlock = new CoarseLocationUpdatePacket.LocationBlock();
+ locationBlock.X = (byte)agent.Avatar.Position.X;
+ locationBlock.Y = (byte)agent.Avatar.Position.Y;
+ locationBlock.Z = (byte)((int)agent.Avatar.Position.Z / 4);
+ agentDatas.Add(dataBlock);
+ agentLocations.Add(locationBlock);
+ }
+ );
+
+ // Send location updates out to each agent
+ server.Scene.ForEachAgent(
+ delegate(Agent agent)
+ {
CoarseLocationUpdatePacket update = new CoarseLocationUpdatePacket();
update.Index.Prey = -1;
update.Index.You = 0;
- update.AgentData = new CoarseLocationUpdatePacket.AgentDataBlock[server.Agents.Count];
- update.Location = new CoarseLocationUpdatePacket.LocationBlock[server.Agents.Count];
+ update.AgentData = new CoarseLocationUpdatePacket.AgentDataBlock[agentDatas.Count - 1];
+ update.Location = new CoarseLocationUpdatePacket.LocationBlock[agentDatas.Count - 1];
- // Fill in this avatar
- update.AgentData[0] = new CoarseLocationUpdatePacket.AgentDataBlock();
- update.AgentData[0].AgentID = recipient.Avatar.ID;
- update.Location[0] = new CoarseLocationUpdatePacket.LocationBlock();
- update.Location[0].X = (byte)((int)recipient.Avatar.Position.X);
- update.Location[0].Y = (byte)((int)recipient.Avatar.Position.Y);
- update.Location[0].Z = (byte)((int)recipient.Avatar.Position.Z / 4);
- ++i;
-
- foreach (Agent agent in server.Agents.Values)
+ int j = 0;
+ for (int i = 0; i < update.AgentData.Length; i++)
{
- if (agent != recipient)
+ if (agentDatas[i].AgentID != agent.Avatar.ID)
{
- update.AgentData[i] = new CoarseLocationUpdatePacket.AgentDataBlock();
- update.AgentData[i].AgentID = agent.Avatar.ID;
- update.Location[i] = new CoarseLocationUpdatePacket.LocationBlock();
- update.Location[i].X = (byte)((int)agent.Avatar.Position.X);
- update.Location[i].Y = (byte)((int)agent.Avatar.Position.Y);
- update.Location[i].Z = (byte)((int)agent.Avatar.Position.Z / 4);
- ++i;
+ update.AgentData[j] = agentDatas[i];
+ update.Location[j] = agentLocations[i];
+ ++j;
}
}
- server.UDP.SendPacket(recipient.Avatar.ID, update, PacketCategory.State);
+ server.UDP.SendPacket(agent.Avatar.ID, update, PacketCategory.State);
}
- }
+ );
}
}
}
diff --git a/Programs/Simian/Extensions/FriendManager.cs b/Programs/Simian/Extensions/FriendManager.cs
index 1511ac25..3f841896 100644
--- a/Programs/Simian/Extensions/FriendManager.cs
+++ b/Programs/Simian/Extensions/FriendManager.cs
@@ -32,75 +32,72 @@ namespace Simian.Extensions
if (dialog == InstantMessageDialog.FriendshipOffered || dialog == InstantMessageDialog.FriendshipAccepted || dialog == InstantMessageDialog.FriendshipDeclined)
{
- lock (server.Agents)
+ // HACK: Only works for agents currently online
+ Agent recipient;
+ if (server.Scene.TryGetAgent(im.MessageBlock.ToAgentID, out recipient))
{
- // HACK: Only works for agents currently online
- Agent recipient;
- if (server.Agents.TryGetValue(im.MessageBlock.ToAgentID, out recipient))
+ ImprovedInstantMessagePacket sendIM = new ImprovedInstantMessagePacket();
+ sendIM.MessageBlock.RegionID = UUID.Random(); //FIXME
+ sendIM.MessageBlock.ParentEstateID = 1;
+ sendIM.MessageBlock.FromGroup = false;
+ sendIM.MessageBlock.FromAgentName = Utils.StringToBytes(agent.Avatar.Name);
+ sendIM.MessageBlock.ToAgentID = im.MessageBlock.ToAgentID;
+ sendIM.MessageBlock.Dialog = im.MessageBlock.Dialog;
+ sendIM.MessageBlock.Offline = (byte)InstantMessageOnline.Online;
+ sendIM.MessageBlock.ID = agent.Avatar.ID;
+ sendIM.MessageBlock.Message = im.MessageBlock.Message;
+ sendIM.MessageBlock.BinaryBucket = new byte[0];
+ sendIM.MessageBlock.Timestamp = 0;
+ sendIM.MessageBlock.Position = agent.Avatar.Position;
+
+ sendIM.AgentData.AgentID = agent.Avatar.ID;
+
+ server.UDP.SendPacket(recipient.Avatar.ID, sendIM, PacketCategory.Transaction);
+
+ if (dialog == InstantMessageDialog.FriendshipAccepted)
{
- ImprovedInstantMessagePacket sendIM = new ImprovedInstantMessagePacket();
- sendIM.MessageBlock.RegionID = UUID.Random(); //FIXME
- sendIM.MessageBlock.ParentEstateID = 1;
- sendIM.MessageBlock.FromGroup = false;
- sendIM.MessageBlock.FromAgentName = Utils.StringToBytes(agent.Avatar.Name);
- sendIM.MessageBlock.ToAgentID = im.MessageBlock.ToAgentID;
- sendIM.MessageBlock.Dialog = im.MessageBlock.Dialog;
- sendIM.MessageBlock.Offline = (byte)InstantMessageOnline.Online;
- sendIM.MessageBlock.ID = agent.Avatar.ID;
- sendIM.MessageBlock.Message = im.MessageBlock.Message;
- sendIM.MessageBlock.BinaryBucket = new byte[0];
- sendIM.MessageBlock.Timestamp = 0;
- sendIM.MessageBlock.Position = agent.Avatar.Position;
+ bool receiverOnline = server.Scene.ContainsAgent(agent.Avatar.ID);
+ bool senderOnline = server.Scene.ContainsAgent(recipient.Avatar.ID);
- sendIM.AgentData.AgentID = agent.Avatar.ID;
-
- server.UDP.SendPacket(recipient.Avatar.ID, sendIM, PacketCategory.Transaction);
-
- if (dialog == InstantMessageDialog.FriendshipAccepted)
+ if (receiverOnline)
{
- bool receiverOnline = server.Agents.ContainsKey(agent.Avatar.ID);
- bool senderOnline = server.Agents.ContainsKey(recipient.Avatar.ID);
-
- if (receiverOnline)
- {
- if (senderOnline)
- {
- OnlineNotificationPacket notify = new OnlineNotificationPacket();
- notify.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[0];
- notify.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock();
- notify.AgentBlock[0].AgentID = agent.Avatar.ID;
- server.UDP.SendPacket(recipient.Avatar.ID, notify, PacketCategory.State);
- }
- else
- {
- OfflineNotificationPacket notify = new OfflineNotificationPacket();
- notify.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[0];
- notify.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock();
- notify.AgentBlock[0].AgentID = agent.Avatar.ID;
- server.UDP.SendPacket(recipient.Avatar.ID, notify, PacketCategory.State);
- }
- }
-
if (senderOnline)
{
- if (receiverOnline)
- {
- OnlineNotificationPacket notify = new OnlineNotificationPacket();
- notify.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[0];
- notify.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock();
- notify.AgentBlock[0].AgentID = recipient.Avatar.ID;
- server.UDP.SendPacket(agent.Avatar.ID, notify, PacketCategory.State);
- }
- else
- {
- OfflineNotificationPacket notify = new OfflineNotificationPacket();
- notify.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[0];
- notify.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock();
- notify.AgentBlock[0].AgentID = recipient.Avatar.ID;
- server.UDP.SendPacket(agent.Avatar.ID, notify, PacketCategory.State);
- }
+ OnlineNotificationPacket notify = new OnlineNotificationPacket();
+ notify.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[0];
+ notify.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock();
+ notify.AgentBlock[0].AgentID = agent.Avatar.ID;
+ server.UDP.SendPacket(recipient.Avatar.ID, notify, PacketCategory.State);
}
- }
+ else
+ {
+ OfflineNotificationPacket notify = new OfflineNotificationPacket();
+ notify.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[0];
+ notify.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock();
+ notify.AgentBlock[0].AgentID = agent.Avatar.ID;
+ server.UDP.SendPacket(recipient.Avatar.ID, notify, PacketCategory.State);
+ }
+ }
+
+ if (senderOnline)
+ {
+ if (receiverOnline)
+ {
+ OnlineNotificationPacket notify = new OnlineNotificationPacket();
+ notify.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[0];
+ notify.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock();
+ notify.AgentBlock[0].AgentID = recipient.Avatar.ID;
+ server.UDP.SendPacket(agent.Avatar.ID, notify, PacketCategory.State);
+ }
+ else
+ {
+ OfflineNotificationPacket notify = new OfflineNotificationPacket();
+ notify.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[0];
+ notify.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock();
+ notify.AgentBlock[0].AgentID = recipient.Avatar.ID;
+ server.UDP.SendPacket(agent.Avatar.ID, notify, PacketCategory.State);
+ }
+ }
}
}
}
diff --git a/Programs/Simian/Extensions/LindenLogin.cs b/Programs/Simian/Extensions/LindenLogin.cs
new file mode 100644
index 00000000..e9e49bbe
--- /dev/null
+++ b/Programs/Simian/Extensions/LindenLogin.cs
@@ -0,0 +1,252 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Xml;
+using ExtensionLoader;
+using HttpServer;
+using OpenMetaverse;
+
+namespace Simian.Extensions
+{
+ public class LindenLogin : IExtension
+ {
+ Simian server;
+
+ public LindenLogin()
+ {
+ }
+
+ public void Start(Simian server)
+ {
+ this.server = server;
+
+ // Login webpage HEAD request, used to check if the login webpage is alive
+ server.HttpServer.AddHandler("head", null, "^/$", LoginWebpageHeadHandler);
+
+ // Login webpage GET request, gets the login webpage data (purely aesthetic)
+ server.HttpServer.AddHandler("get", null, @"^/(\?.*)?$", LoginWebpageGetHandler);
+
+ // Client XML-RPC login
+ server.HttpServer.AddHandler("post", "text/xml", "^/$", LoginXmlRpcPostHandler);
+
+ // Client LLSD login
+ server.HttpServer.AddHandler("post", "application/xml", "^/$", LoginLLSDPostHandler);
+
+ }
+
+ public void Stop()
+ {
+ }
+
+ bool LoginWebpageHeadHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
+ {
+ return true;
+ }
+
+ bool LoginWebpageGetHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
+ {
+ string pageContent = "Simian
Welcome to Simian
";
+ byte[] pageData = Encoding.UTF8.GetBytes(pageContent);
+ response.Body.Write(pageData, 0, pageData.Length);
+ response.Body.Flush();
+ return true;
+ }
+
+ bool LoginXmlRpcPostHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
+ {
+ 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(request.Body);
+
+ 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;
+ case "platform":
+ case "mac":
+ case "id0":
+ case "options":
+ // Ignored values
+ reader.ReadInnerXml();
+ break;
+ default:
+ if (reader.Name == "string")
+ Console.WriteLine(String.Format("Ignore login xml value: name={0}", 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);
+
+ if (responseData.Success)
+ responseData.InventorySkeleton = server.Inventory.CreateInventorySkeleton(responseData.AgentID);
+
+ XmlWriter writer = XmlWriter.Create(response.Body);
+ responseData.ToXmlRpc(writer);
+ writer.Flush();
+ writer.Close();
+ }
+ catch (Exception ex)
+ {
+ Logger.Log("XmlRpc login error: " + ex.Message, Helpers.LogLevel.Error, ex);
+ }
+
+ return true;
+ }
+
+ bool LoginLLSDPostHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
+ {
+ string body = String.Empty;
+
+ using (StreamReader reader = new StreamReader(request.Body, request.ContentEncoding))
+ {
+ body = reader.ReadToEnd();
+ }
+
+ Logger.DebugLog("LLSD login is not implemented:\n" + body);
+ return true;
+ }
+
+ LoginResponseData HandleLogin(string firstName, string lastName, string password, string start, string version, string channel)
+ {
+ LoginResponseData response = new LoginResponseData();
+ Agent agent;
+
+ UUID agentID = server.Authentication.Authenticate(firstName, lastName, password);
+ if (agentID != UUID.Zero)
+ {
+ // Authentication successful, create a login instance of this agent
+ agent = server.Accounts.CreateInstance(agentID);
+
+ if (agent != null)
+ {
+ // Assign a circuit code and insert the agent into the unassociatedAgents dictionary
+ agent.CircuitCode = server.UDP.CreateCircuit(agent);
+
+ agent.TickLastPacketReceived = Environment.TickCount;
+ agent.LastLoginTime = Utils.DateTimeToUnixTime(DateTime.Now);
+
+ // Get this machine's IP address
+ IPHostEntry addresses = Dns.GetHostByName(Dns.GetHostName());
+ IPAddress simIP = addresses.AddressList.Length > 0 ?
+ addresses.AddressList[addresses.AddressList.Length - 1] : IPAddress.Loopback;
+
+ response.AgentID = agent.Avatar.ID;
+ response.SecureSessionID = agent.SecureSessionID;
+ response.SessionID = agent.SessionID;
+ response.CircuitCode = agent.CircuitCode;
+ response.AgentAccess = agent.AccessLevel;
+ response.BuddyList = null; // FIXME:
+ response.FirstName = agent.FirstName;
+ response.HomeLookAt = agent.HomeLookAt;
+ response.HomePosition = agent.HomePosition;
+ response.HomeRegion = agent.HomeRegionHandle;
+ response.InventoryRoot = agent.InventoryRoot;
+ response.InventorySkeleton = null; // FIXME:
+ response.LastName = agent.LastName;
+ response.LibraryOwner = agent.InventoryLibraryOwner;
+ response.LibraryRoot = agent.InventoryLibraryRoot;
+ response.LibrarySkeleton = null; // FIXME:
+ response.LookAt = agent.CurrentLookAt;
+ response.Message = "Welcome to Simian";
+ response.Reason = String.Empty;
+
+ uint regionX, regionY;
+ Utils.LongToUInts(agent.CurrentRegionHandle, out regionX, out regionY);
+ 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, server.HttpPort);
+ response.SimIP = simIP;
+ response.SimPort = (ushort)server.UDPPort;
+ response.StartLocation = "last"; // FIXME:
+ response.Success = true;
+ }
+ else
+ {
+ // Something went wrong creating an agent instance, return a fail response
+ response.AgentID = agentID;
+ response.FirstName = firstName;
+ response.LastName = lastName;
+ response.Message = "Failed to create an account instance";
+ response.Reason = "account";
+ response.Success = false;
+ }
+ }
+ else
+ {
+ // Authentication failed, return a fail response
+ response.AgentID = agentID;
+ response.FirstName = firstName;
+ response.LastName = lastName;
+ response.Message = "Authentication failed";
+ response.Reason = "key";
+ response.Success = false;
+ }
+
+ return response;
+ }
+ }
+}
diff --git a/Programs/Simian/Extensions/Messaging.cs b/Programs/Simian/Extensions/Messaging.cs
index 4410198d..5ae10253 100644
--- a/Programs/Simian/Extensions/Messaging.cs
+++ b/Programs/Simian/Extensions/Messaging.cs
@@ -56,30 +56,27 @@ namespace Simian.Extensions
if (dialog == InstantMessageDialog.MessageFromAgent)
{
- lock (server.Agents)
+ // HACK: Only works for agents currently online
+ Agent recipient;
+ if (server.Scene.TryGetAgent(im.MessageBlock.ToAgentID, out recipient))
{
- // HACK: Only works for agents currently online
- Agent recipient;
- if (server.Agents.TryGetValue(im.MessageBlock.ToAgentID, out recipient))
- {
- ImprovedInstantMessagePacket sendIM = new ImprovedInstantMessagePacket();
- sendIM.MessageBlock.RegionID = UUID.Random(); //FIXME
- sendIM.MessageBlock.ParentEstateID = 1;
- sendIM.MessageBlock.FromGroup = false;
- sendIM.MessageBlock.FromAgentName = Utils.StringToBytes(agent.Avatar.Name);
- sendIM.MessageBlock.ToAgentID = im.MessageBlock.ToAgentID;
- sendIM.MessageBlock.Dialog = im.MessageBlock.Dialog;
- sendIM.MessageBlock.Offline = (byte)InstantMessageOnline.Online;
- sendIM.MessageBlock.ID = agent.Avatar.ID;
- sendIM.MessageBlock.Message = im.MessageBlock.Message;
- sendIM.MessageBlock.BinaryBucket = new byte[0];
- sendIM.MessageBlock.Timestamp = 0;
- sendIM.MessageBlock.Position = agent.Avatar.Position;
+ ImprovedInstantMessagePacket sendIM = new ImprovedInstantMessagePacket();
+ sendIM.MessageBlock.RegionID = UUID.Random(); //FIXME
+ sendIM.MessageBlock.ParentEstateID = 1;
+ sendIM.MessageBlock.FromGroup = false;
+ sendIM.MessageBlock.FromAgentName = Utils.StringToBytes(agent.Avatar.Name);
+ sendIM.MessageBlock.ToAgentID = im.MessageBlock.ToAgentID;
+ sendIM.MessageBlock.Dialog = im.MessageBlock.Dialog;
+ sendIM.MessageBlock.Offline = (byte)InstantMessageOnline.Online;
+ sendIM.MessageBlock.ID = agent.Avatar.ID;
+ sendIM.MessageBlock.Message = im.MessageBlock.Message;
+ sendIM.MessageBlock.BinaryBucket = new byte[0];
+ sendIM.MessageBlock.Timestamp = 0;
+ sendIM.MessageBlock.Position = agent.Avatar.Position;
- sendIM.AgentData.AgentID = agent.Avatar.ID;
+ sendIM.AgentData.AgentID = agent.Avatar.ID;
- server.UDP.SendPacket(recipient.Avatar.ID, sendIM, PacketCategory.Transaction);
- }
+ server.UDP.SendPacket(recipient.Avatar.ID, sendIM, PacketCategory.Transaction);
}
}
}
diff --git a/Programs/Simian/Extensions/Money.cs b/Programs/Simian/Extensions/Money.cs
index fd72d0b0..56a4b2fd 100644
--- a/Programs/Simian/Extensions/Money.cs
+++ b/Programs/Simian/Extensions/Money.cs
@@ -52,18 +52,15 @@ namespace Simian.Extensions
if (request.MoneyData.Amount < 0 || request.MoneyData.Amount > agent.Balance)
return;
- lock (server.Agents)
+ // HACK: Only works for sending money to someone who is online
+ Agent recipient;
+ if (server.Scene.TryGetAgent(request.MoneyData.DestID, out recipient))
{
- // HACK: Only works for sending money to someone who is online
- Agent recipient;
- if (server.Agents.TryGetValue(request.MoneyData.DestID, out recipient))
- {
- agent.Balance -= request.MoneyData.Amount;
- recipient.Balance += request.MoneyData.Amount;
+ agent.Balance -= request.MoneyData.Amount;
+ recipient.Balance += request.MoneyData.Amount;
- SendBalance(agent, UUID.Zero, String.Format("You paid L${0} to {1}.", request.MoneyData.Amount, recipient.Avatar.Name));
- SendBalance(agent, UUID.Zero, String.Format("{1} paid you L${0}.", request.MoneyData.Amount, agent.Avatar.Name));
- }
+ SendBalance(agent, UUID.Zero, String.Format("You paid L${0} to {1}.", request.MoneyData.Amount, recipient.Avatar.Name));
+ SendBalance(agent, UUID.Zero, String.Format("{1} paid you L${0}.", request.MoneyData.Amount, agent.Avatar.Name));
}
}
diff --git a/Programs/Simian/Extensions/Movement.cs b/Programs/Simian/Extensions/Movement.cs
index eef0690d..9c8c285c 100644
--- a/Programs/Simian/Extensions/Movement.cs
+++ b/Programs/Simian/Extensions/Movement.cs
@@ -69,9 +69,8 @@ namespace Simian.Extensions
float seconds = (float)((tick - LastTick) / 1000f);
LastTick = tick;
- lock (server.Agents)
- {
- foreach (Agent agent in server.Agents.Values)
+ server.Scene.ForEachAgent(
+ delegate(Agent agent)
{
bool animsChanged = false;
@@ -118,7 +117,7 @@ namespace Simian.Extensions
// least possible distance from avatar to the ground
// TODO: calculate to get rid of "bot squat"
- float lowerLimit = newFloor + agent.Avatar.Scale.Z / 2;
+ float lowerLimit = newFloor + agent.Avatar.Scale.Z / 2;
// Z acceleration resulting from gravity
float gravity = 0f;
@@ -231,7 +230,7 @@ namespace Simian.Extensions
//friction
agent.Avatar.Acceleration *= 0.2f;
- agent.Avatar.Velocity *= 0.2f;
+ agent.Avatar.Velocity *= 0.2f;
agent.Avatar.Position.Z = lowerLimit;
@@ -243,7 +242,7 @@ namespace Simian.Extensions
if (server.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP))
animsChanged = true;
- agent.TickJump = Environment.TickCount;
+ agent.TickJump = Environment.TickCount;
}
else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000)
{ //start actual jump
@@ -339,7 +338,7 @@ namespace Simian.Extensions
if (agent.Avatar.Position.Z < lowerLimit) agent.Avatar.Position.Z = lowerLimit;
}
- }
+ );
}
void AgentUpdateHandler(Packet packet, Agent agent)
diff --git a/Programs/Simian/Extensions/Periscope.cs b/Programs/Simian/Extensions/Periscope.cs
index 130040db..abc9eccd 100644
--- a/Programs/Simian/Extensions/Periscope.cs
+++ b/Programs/Simian/Extensions/Periscope.cs
@@ -90,12 +90,8 @@ namespace Simian.Extensions
agent.CurrentRegionHandle = server.RegionHandle;
agent.FirstName = avatar.FirstName;
agent.LastName = avatar.LastName;
-
- lock (server.Agents)
- server.Agents[agent.Avatar.ID] = agent;
- SimulationObject simObj = new SimulationObject(avatar, server);
- server.Scene.ObjectAdd(this, simObj, avatar.Flags);
+ server.Scene.AgentAdd(this, agent, avatar.Flags);
}
void Objects_OnObjectUpdated(Simulator simulator, ObjectUpdate update, ulong regionHandle, ushort timeDilation)
@@ -126,7 +122,7 @@ namespace Simian.Extensions
Primitive.TextureEntryFace[] faceTextures, List visualParams)
{
Agent agent;
- if (server.Agents.TryGetValue(avatarID, out agent))
+ if (server.Scene.TryGetAgent(avatarID, out agent))
{
Primitive.TextureEntry te = new Primitive.TextureEntry(defaultTexture);
te.FaceTextures = faceTextures;
@@ -135,10 +131,10 @@ namespace Simian.Extensions
Logger.Log("[Periscope] Updating foreign avatar appearance for " + agent.FirstName + " " + agent.LastName, Helpers.LogLevel.Info);
- server.Scene.AvatarAppearance(this, agent, te, vp);
+ server.Scene.AgentAppearance(this, agent, te, vp);
if (agent.Avatar.ID == client.Self.AgentID)
- server.Scene.AvatarAppearance(this, MasterAgent, te, vp);
+ server.Scene.AgentAppearance(this, MasterAgent, te, vp);
}
else
{
@@ -177,14 +173,27 @@ namespace Simian.Extensions
{
if (status == AgentManager.TeleportStatus.Finished)
{
- // Kill off any prims from the previous sim
- IDictionary scene = server.Scene.GetSceneCopy();
+ server.Scene.ForEachObject(
+ delegate(SimulationObject obj)
+ {
+ if (obj.Prim.RegionHandle != client.Network.CurrentSim.Handle)
+ server.Scene.ObjectRemove(this, obj.Prim.ID);
+ }
+ );
- foreach (SimulationObject obj in scene.Values)
- {
- if (obj.Prim.RegionHandle != client.Network.CurrentSim.Handle)
- server.Scene.ObjectRemove(this, obj.Prim.ID);
- }
+ // FIXME: Use the scene region handle when it has one
+ ulong regionHandle = Utils.UIntsToLong(Simian.REGION_X, Simian.REGION_Y);
+
+ server.Scene.ForEachAgent(
+ delegate(Agent agent)
+ {
+ if (agent.Avatar.RegionHandle != regionHandle &&
+ agent.Avatar.RegionHandle != client.Network.CurrentSim.Handle)
+ {
+ server.Scene.AgentRemove(this, agent.Avatar.ID);
+ }
+ }
+ );
}
}
@@ -199,7 +208,7 @@ namespace Simian.Extensions
AvatarAnimationPacket animations = (AvatarAnimationPacket)packet;
Agent agent;
- if (server.Agents.TryGetValue(animations.Sender.ID, out agent))
+ if (server.Scene.TryGetAgent(animations.Sender.ID, out agent))
{
agent.Animations.Clear();
@@ -275,12 +284,9 @@ namespace Simian.Extensions
imageDelivery.Pipeline.CurrentCount, imageDelivery.Pipeline.QueuedCount));
return;
case "/nudemod":
- int count = 0;
- Dictionary agents;
- lock (server.Agents)
- agents = new Dictionary(server.Agents);
-
- foreach (Agent curAgent in agents.Values)
+ //int count = 0;
+ // FIXME: AvatarAppearance locks the agents dictionary. Need to be able to copy the agents dictionary?
+ /*foreach (Agent curAgent in agents.Values)
{
if (curAgent != agent && curAgent.VisualParams != null)
{
@@ -302,7 +308,7 @@ namespace Simian.Extensions
}
}
- server.Avatars.SendAlert(agent, String.Format("Modified appearances for {0} avatar(s)", count));
+ server.Avatars.SendAlert(agent, String.Format("Modified appearances for {0} avatar(s)", count));*/
return;
}
}
@@ -331,13 +337,13 @@ namespace Simian.Extensions
lock (loginLock)
{
// Double-checked locking to avoid hitting the loginLock each time
- if (MasterAgent == null &&
- server.Agents.TryGetValue(update.AgentData.AgentID, out MasterAgent))
+ if (MasterAgent == null && server.Scene.TryGetAgent(update.AgentData.AgentID, out MasterAgent))
{
Logger.Log(String.Format("[Periscope] {0} {1} is the controlling agent",
MasterAgent.FirstName, MasterAgent.LastName), Helpers.LogLevel.Info);
- LoginParams login = client.Network.DefaultLoginParams(agent.FirstName, agent.LastName, agent.PasswordHash, "Simian Periscope", "1.0.0");
+ LoginParams login = client.Network.DefaultLoginParams(agent.FirstName, agent.LastName, agent.PasswordHash,
+ "Simian Periscope", "1.0.0");
login.Start = "last";
client.Network.Login(login);
diff --git a/Programs/Simian/Extensions/PeriscopeMovement.cs b/Programs/Simian/Extensions/PeriscopeMovement.cs
index 1848503d..027009ff 100644
--- a/Programs/Simian/Extensions/PeriscopeMovement.cs
+++ b/Programs/Simian/Extensions/PeriscopeMovement.cs
@@ -66,9 +66,8 @@ namespace Simian.Extensions
float seconds = (float)((tick - LastTick) / 1000f);
LastTick = tick;
- lock (server.Agents)
- {
- foreach (Agent agent in server.Agents.Values)
+ server.Scene.ForEachAgent(
+ delegate(Agent agent)
{
// Don't handle movement for the master agent or foreign agents
if (agent != periscope.MasterAgent && agent.SessionID != UUID.Zero)
@@ -310,7 +309,7 @@ namespace Simian.Extensions
if (agent.Avatar.Position.Z < lowerLimit) agent.Avatar.Position.Z = lowerLimit;
}
}
- }
+ );
}
void AgentUpdateHandler(Packet packet, Agent agent)
diff --git a/Programs/Simian/Extensions/PeriscopeTransferManager.cs b/Programs/Simian/Extensions/PeriscopeTransferManager.cs
index 1ff8ec9e..40e96065 100644
--- a/Programs/Simian/Extensions/PeriscopeTransferManager.cs
+++ b/Programs/Simian/Extensions/PeriscopeTransferManager.cs
@@ -324,7 +324,7 @@ namespace Simian.Extensions
{
currentDownloads.Remove(transfer.ID);
- if (server.Agents.TryGetValue(kvp.Key, out agent))
+ if (server.Scene.TryGetAgent(kvp.Key, out agent))
{
if (transfer.Success)
{
diff --git a/Programs/Simian/Extensions/SceneManager.cs b/Programs/Simian/Extensions/SceneManager.cs
index 23acc7a7..948827a8 100644
--- a/Programs/Simian/Extensions/SceneManager.cs
+++ b/Programs/Simian/Extensions/SceneManager.cs
@@ -16,6 +16,7 @@ namespace Simian.Extensions
{
Simian server;
DoubleDictionary sceneObjects = new DoubleDictionary();
+ DoubleDictionary sceneAgents = new DoubleDictionary();
int currentLocalID = 1;
float[] heightmap = new float[256 * 256];
@@ -25,7 +26,9 @@ namespace Simian.Extensions
public event ObjectFlagsCallback OnObjectFlags;
public event ObjectImageCallback OnObjectImage;
public event ObjectModifyCallback OnObjectModify;
- public event AvatarAppearanceCallback OnAvatarAppearance;
+ public event AgentAddCallback OnAgentAdd;
+ public event AgentRemoveCallback OnAgentRemove;
+ public event AgentAppearanceCallback OnAgentAppearance;
public event TerrainUpdatedCallback OnTerrainUpdated;
public float[] Heightmap
@@ -75,7 +78,7 @@ namespace Simian.Extensions
// Add the object to the scene dictionary
sceneObjects.Add(obj.Prim.LocalID, obj.Prim.ID, obj);
- if (server.Agents.ContainsKey(obj.Prim.OwnerID))
+ if (sceneAgents.ContainsKey(obj.Prim.OwnerID))
{
// Send an update out to the creator
ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(obj.Prim, server.RegionHandle,
@@ -86,14 +89,13 @@ namespace Simian.Extensions
// Send an update out to everyone else
ObjectUpdatePacket updateToOthers = SimulationObject.BuildFullUpdate(obj.Prim, server.RegionHandle,
obj.Prim.Flags);
- lock (server.Agents)
- {
- foreach (Agent recipient in server.Agents.Values)
+ server.Scene.ForEachAgent(
+ delegate(Agent recipient)
{
if (recipient.Avatar.ID != obj.Prim.OwnerID)
server.UDP.SendPacket(recipient.Avatar.ID, updateToOthers, PacketCategory.State);
}
- }
+ );
return true;
}
@@ -217,11 +219,116 @@ namespace Simian.Extensions
}
}
- public void AvatarAppearance(object sender, Agent agent, Primitive.TextureEntry textures, byte[] visualParams)
+ public bool ContainsObject(uint localID)
{
- if (OnAvatarAppearance != null)
+ return sceneObjects.ContainsKey(localID);
+ }
+
+ public bool ContainsObject(UUID id)
+ {
+ return sceneObjects.ContainsKey(id);
+ }
+
+ public bool TryGetObject(uint localID, out SimulationObject obj)
+ {
+ return sceneObjects.TryGetValue(localID, out obj);
+ }
+
+ public bool TryGetObject(UUID id, out SimulationObject obj)
+ {
+ return sceneObjects.TryGetValue(id, out obj);
+ }
+
+ public bool AgentAdd(object sender, Agent agent, PrimFlags creatorFlags)
+ {
+ // Check if the agent already exists in the scene
+ if (sceneAgents.ContainsKey(agent.Avatar.ID))
+ sceneAgents.Remove(agent.Avatar.LocalID, agent.Avatar.ID);
+
+ if (agent.Avatar.LocalID == 0)
{
- OnAvatarAppearance(sender, agent, textures, visualParams);
+ // Assign a unique LocalID to this agent
+ agent.Avatar.LocalID = (uint)Interlocked.Increment(ref currentLocalID);
+ }
+
+ if (OnAgentAdd != null)
+ OnAgentAdd(sender, agent, creatorFlags);
+
+ // Add the agent to the scene dictionary
+ sceneAgents.Add(agent.Avatar.LocalID, agent.Avatar.ID, agent);
+
+ // Send an update out to the agent
+ ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(agent.Avatar, server.RegionHandle,
+ agent.Avatar.Flags | creatorFlags);
+ server.UDP.SendPacket(agent.Avatar.ID, updateToOwner, PacketCategory.State);
+
+ // Send an update out to everyone else
+ ObjectUpdatePacket updateToOthers = SimulationObject.BuildFullUpdate(agent.Avatar, server.RegionHandle,
+ agent.Avatar.Flags);
+ server.Scene.ForEachAgent(
+ delegate(Agent recipient)
+ {
+ if (recipient.Avatar.ID != agent.Avatar.ID)
+ server.UDP.SendPacket(recipient.Avatar.ID, updateToOthers, PacketCategory.State);
+ }
+ );
+
+ return true;
+ }
+
+ public bool AgentRemove(object sender, uint localID)
+ {
+ Agent agent;
+ if (sceneAgents.TryGetValue(localID, out agent))
+ {
+ if (OnAgentRemove != null)
+ OnAgentRemove(sender, agent);
+
+ sceneAgents.Remove(agent.Avatar.LocalID, agent.Avatar.ID);
+
+ KillObjectPacket kill = new KillObjectPacket();
+ kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
+ kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
+ kill.ObjectData[0].ID = agent.Avatar.LocalID;
+
+ server.UDP.BroadcastPacket(kill, PacketCategory.State);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public bool AgentRemove(object sender, UUID id)
+ {
+ Agent agent;
+ if (sceneAgents.TryGetValue(id, out agent))
+ {
+ if (OnAgentRemove != null)
+ OnAgentRemove(sender, agent);
+
+ sceneAgents.Remove(agent.Avatar.LocalID, agent.Avatar.ID);
+
+ KillObjectPacket kill = new KillObjectPacket();
+ kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
+ kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
+ kill.ObjectData[0].ID = agent.Avatar.LocalID;
+
+ server.UDP.BroadcastPacket(kill, PacketCategory.State);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public void AgentAppearance(object sender, Agent agent, Primitive.TextureEntry textures, byte[] visualParams)
+ {
+ if (OnAgentAppearance != null)
+ {
+ OnAgentAppearance(sender, agent, textures, visualParams);
}
// Broadcast an object update for this avatar
@@ -239,37 +346,44 @@ namespace Simian.Extensions
{
// Send the appearance packet to all other clients
AvatarAppearancePacket appearance = BuildAppearancePacket(agent);
- lock (server.Agents)
- {
- foreach (Agent recipient in server.Agents.Values)
+ sceneAgents.ForEach(
+ delegate(Agent recipient)
{
if (recipient != agent)
server.UDP.SendPacket(recipient.Avatar.ID, appearance, PacketCategory.State);
}
- }
+ );
}
}
- public bool TryGetObject(uint localID, out SimulationObject obj)
+ public void ForEachObject(Action action)
{
- return sceneObjects.TryGetValue(localID, out obj);
+ sceneObjects.ForEach(action);
}
- public bool TryGetObject(UUID id, out SimulationObject obj)
+ public bool ContainsAgent(uint localID)
{
- return sceneObjects.TryGetValue(id, out obj);
+ return sceneAgents.ContainsKey(localID);
}
- public IDictionary GetSceneCopy()
+ public bool ContainsAgent(UUID id)
{
- IDictionary scene = new Dictionary(sceneObjects.Count);
+ return sceneAgents.ContainsKey(id);
+ }
- sceneObjects.ForEach(
- delegate(SimulationObject obj)
- { scene.Add(obj.Prim.LocalID, obj); }
- );
+ public bool TryGetAgent(uint localID, out Agent agent)
+ {
+ return sceneAgents.TryGetValue(localID, out agent);
+ }
- return scene;
+ public bool TryGetAgent(UUID id, out Agent agent)
+ {
+ return sceneAgents.TryGetValue(id, out agent);
+ }
+
+ public void ForEachAgent(Action action)
+ {
+ sceneAgents.ForEach(action);
}
void BroadcastObjectUpdate(SimulationObject obj)
@@ -359,9 +473,8 @@ namespace Simian.Extensions
});
// Send appearances for all avatars
- lock (server.Agents)
- {
- foreach (Agent otherAgent in server.Agents.Values)
+ sceneAgents.ForEach(
+ delegate(Agent otherAgent)
{
if (otherAgent != agent)
{
@@ -370,7 +483,7 @@ namespace Simian.Extensions
server.UDP.SendPacket(agent.Avatar.ID, appearance, PacketCategory.State);
}
}
- }
+ );
// Send terrain data
SendLayerData(agent);
diff --git a/Programs/Simian/Extensions/UDPManager.cs b/Programs/Simian/Extensions/UDPManager.cs
index ebecc543..ad08a93e 100644
--- a/Programs/Simian/Extensions/UDPManager.cs
+++ b/Programs/Simian/Extensions/UDPManager.cs
@@ -172,7 +172,6 @@ namespace Simian
if (clients.TryGetValue(agent.Avatar.ID, out client))
{
client.Shutdown();
- lock (server.Agents) server.Agents.Remove(agent.Avatar.ID);
return clients.Remove(agent.Avatar.ID, client.Address);
}
else
@@ -601,7 +600,7 @@ namespace Simian
if (unassociatedAgents.TryGetValue(circuitCode, out agent))
{
unassociatedAgents.Remove(circuitCode);
- lock (server.Agents) server.Agents[agent.Avatar.ID] = agent;
+ server.Scene.AgentAdd(this, agent, PrimFlags.None);
return true;
}
else
diff --git a/Programs/Simian/Interfaces/ISceneProvider.cs b/Programs/Simian/Interfaces/ISceneProvider.cs
index 13a50f86..7f4fffb1 100644
--- a/Programs/Simian/Interfaces/ISceneProvider.cs
+++ b/Programs/Simian/Interfaces/ISceneProvider.cs
@@ -6,16 +6,16 @@ namespace Simian
{
public delegate void ObjectAddCallback(object sender, SimulationObject obj, PrimFlags creatorFlags);
public delegate void ObjectRemoveCallback(object sender, SimulationObject obj);
- public delegate void ObjectTransformCallback(object sender, SimulationObject obj,
- Vector3 position, Quaternion rotation, Vector3 velocity, Vector3 acceleration,
- Vector3 angularVelocity);
+ public delegate void ObjectTransformCallback(object sender, SimulationObject obj, Vector3 position,
+ Quaternion rotation, Vector3 velocity, Vector3 acceleration, Vector3 angularVelocity);
public delegate void ObjectFlagsCallback(object sender, SimulationObject obj, PrimFlags flags);
public delegate void ObjectImageCallback(object sender, SimulationObject obj,
string mediaURL, Primitive.TextureEntry textureEntry);
- public delegate void ObjectModifyCallback(object sender, SimulationObject obj,
- Primitive.ConstructionData data);
- public delegate void AvatarAppearanceCallback(object sender, Agent agent,
- Primitive.TextureEntry textures, byte[] visualParams);
+ public delegate void ObjectModifyCallback(object sender, SimulationObject obj, Primitive.ConstructionData data);
+ public delegate void AgentAddCallback(object sender, Agent agent, PrimFlags creatorFlags);
+ public delegate void AgentRemoveCallback(object sender, Agent agent);
+ public delegate void AgentAppearanceCallback(object sender, Agent agent, Primitive.TextureEntry textures,
+ byte[] visualParams);
// TODO: Convert terrain to a patch-based system
public delegate void TerrainUpdatedCallback(object sender);
@@ -26,7 +26,9 @@ namespace Simian
event ObjectTransformCallback OnObjectTransform;
event ObjectFlagsCallback OnObjectFlags;
event ObjectModifyCallback OnObjectModify;
- event AvatarAppearanceCallback OnAvatarAppearance;
+ event AgentAddCallback OnAgentAdd;
+ event AgentRemoveCallback OnAgentRemove;
+ event AgentAppearanceCallback OnAgentAppearance;
event TerrainUpdatedCallback OnTerrainUpdated;
// TODO: Convert to a patch-based system, and expose terrain editing
@@ -42,12 +44,20 @@ namespace Simian
void ObjectFlags(object sender, SimulationObject obj, PrimFlags flags);
void ObjectImage(object sender, SimulationObject obj, string mediaURL, Primitive.TextureEntry textureEntry);
void ObjectModify(object sender, uint localID, Primitive.ConstructionData data);
-
- void AvatarAppearance(object sender, Agent agent, Primitive.TextureEntry textures, byte[] visualParams);
-
+ bool ContainsObject(uint localID);
+ bool ContainsObject(UUID id);
bool TryGetObject(uint localID, out SimulationObject obj);
bool TryGetObject(UUID id, out SimulationObject obj);
+ void ForEachObject(Action obj);
- IDictionary GetSceneCopy();
+ bool AgentAdd(object sender, Agent agent, PrimFlags creatorFlags);
+ bool AgentRemove(object sender, uint localID);
+ bool AgentRemove(object sender, UUID id);
+ void AgentAppearance(object sender, Agent agent, Primitive.TextureEntry textures, byte[] visualParams);
+ bool ContainsAgent(uint localID);
+ bool ContainsAgent(UUID id);
+ bool TryGetAgent(uint localID, out Agent agent);
+ bool TryGetAgent(UUID id, out Agent agent);
+ void ForEachAgent(Action action);
}
}
diff --git a/Programs/Simian/Simian.cs b/Programs/Simian/Simian.cs
index 82d6f3a4..18457cdb 100644
--- a/Programs/Simian/Simian.cs
+++ b/Programs/Simian/Simian.cs
@@ -45,9 +45,6 @@ namespace Simian
// Persistent extensions
public List PersistentExtensions = new List();
- /// All of the agents currently connected to this UDP server
- public Dictionary Agents = new Dictionary();
-
public Simian()
{
}
@@ -75,21 +72,25 @@ namespace Simian
try
{
- // Load all of the extensions
+ // Create a list of references for .cs extensions that are compiled at runtime
List references = new List();
references.Add("OpenMetaverseTypes.dll");
references.Add("OpenMetaverse.dll");
references.Add("Simian.exe");
+ // Search the Simian class for member variables that are interfaces
List assignables = ExtensionLoader.GetInterfaces(this);
+ // Load extensions from the current executing assembly, Simian.*.dll assemblies on disk, and
+ // Simian.*.cs source files on disk. Automatically assign extensions that implement interfaces
+ // to the list of interface variables in "assignables"
ExtensionLoader.LoadAllExtensions(Assembly.GetExecutingAssembly(),
AppDomain.CurrentDomain.BaseDirectory, extensionList, references,
"Simian.*.dll", "Simian.*.cs", this, assignables);
}
catch (ExtensionException ex)
{
- Logger.Log("Interface loading failed, shutting down: " + ex.Message, Helpers.LogLevel.Error);
+ Logger.Log("Extension loading failed, shutting down: " + ex.Message, Helpers.LogLevel.Error);
Stop();
return false;
}
@@ -134,229 +135,7 @@ namespace Simian
{
HttpServer = new WebServer(IPAddress.Any, port);
- // Login webpage HEAD request, used to check if the login webpage is alive
- HttpServer.AddHandler("head", null, "^/$", LoginWebpageHeadHandler);
-
- // Login webpage GET request, gets the login webpage data (purely aesthetic)
- HttpServer.AddHandler("get", null, @"^/(\?.*)?$", LoginWebpageGetHandler);
-
- // Client XML-RPC login
- HttpServer.AddHandler("post", "text/xml", "^/$", LoginXmlRpcPostHandler);
-
- // Client LLSD login
- HttpServer.AddHandler("post", "application/xml", "^/$", LoginLLSDPostHandler);
-
HttpServer.Start();
}
-
- bool LoginWebpageHeadHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
- {
- return true;
- }
-
- bool LoginWebpageGetHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
- {
- string pageContent = "Simian
Welcome to Simian
";
- byte[] pageData = Encoding.UTF8.GetBytes(pageContent);
- response.Body.Write(pageData, 0, pageData.Length);
- response.Body.Flush();
- return true;
- }
-
- bool LoginXmlRpcPostHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
- {
- 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(request.Body);
-
- 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;
- case "platform":
- case "mac":
- case "id0":
- case "options":
- // Ignored values
- reader.ReadInnerXml();
- break;
- default:
- if (reader.Name == "string")
- Console.WriteLine(String.Format("Ignore login xml value: name={0}", 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);
-
- if (responseData.Success)
- responseData.InventorySkeleton = Inventory.CreateInventorySkeleton(responseData.AgentID);
-
- XmlWriter writer = XmlWriter.Create(response.Body);
- responseData.ToXmlRpc(writer);
- writer.Flush();
- writer.Close();
- }
- catch (Exception ex)
- {
- Logger.Log("XmlRpc login error: " + ex.Message, Helpers.LogLevel.Error, ex);
- }
-
- return true;
- }
-
- bool LoginLLSDPostHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
- {
- string body = String.Empty;
-
- using (StreamReader reader = new StreamReader(request.Body, request.ContentEncoding))
- {
- body = reader.ReadToEnd();
- }
-
- Logger.DebugLog("LLSD login is not implemented:\n" + body);
- return true;
- }
-
- LoginResponseData HandleLogin(string firstName, string lastName, string password, string start, string version, string channel)
- {
- LoginResponseData response = new LoginResponseData();
- Agent agent;
-
- UUID agentID = Authentication.Authenticate(firstName, lastName, password);
- if (agentID != UUID.Zero)
- {
- // Authentication successful, create a login instance of this agent
- agent = Accounts.CreateInstance(agentID);
-
- if (agent != null)
- {
- // Assign a circuit code and insert the agent into the unassociatedAgents dictionary
- agent.CircuitCode = UDP.CreateCircuit(agent);
-
- agent.TickLastPacketReceived = Environment.TickCount;
- agent.LastLoginTime = Utils.DateTimeToUnixTime(DateTime.Now);
-
- // Get this machine's IP address
- IPHostEntry addresses = Dns.GetHostByName(Dns.GetHostName());
- IPAddress simIP = addresses.AddressList.Length > 0 ?
- addresses.AddressList[addresses.AddressList.Length - 1] :IPAddress.Loopback;
-
- response.AgentID = agent.Avatar.ID;
- response.SecureSessionID = agent.SecureSessionID;
- response.SessionID = agent.SessionID;
- response.CircuitCode = agent.CircuitCode;
- response.AgentAccess = agent.AccessLevel;
- response.BuddyList = null; // FIXME:
- response.FirstName = agent.FirstName;
- response.HomeLookAt = agent.HomeLookAt;
- response.HomePosition = agent.HomePosition;
- response.HomeRegion = agent.HomeRegionHandle;
- response.InventoryRoot = agent.InventoryRoot;
- response.InventorySkeleton = null; // FIXME:
- response.LastName = agent.LastName;
- response.LibraryOwner = agent.InventoryLibraryOwner;
- response.LibraryRoot = agent.InventoryLibraryRoot;
- response.LibrarySkeleton = null; // FIXME:
- response.LookAt = agent.CurrentLookAt;
- response.Message = "Welcome to Simian";
- response.Reason = String.Empty;
-
- uint regionX, regionY;
- Utils.LongToUInts(agent.CurrentRegionHandle, out regionX, out regionY);
- 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, HttpPort);
- response.SimIP = simIP;
- response.SimPort = (ushort)UDPPort;
- response.StartLocation = "last"; // FIXME:
- response.Success = true;
- }
- else
- {
- // Something went wrong creating an agent instance, return a fail response
- response.AgentID = agentID;
- response.FirstName = firstName;
- response.LastName = lastName;
- response.Message = "Failed to create an account instance";
- response.Reason = "account";
- response.Success = false;
- }
- }
- else
- {
- // Authentication failed, return a fail response
- response.AgentID = agentID;
- response.FirstName = firstName;
- response.LastName = lastName;
- response.Message = "Authentication failed";
- response.Reason = "key";
- response.Success = false;
- }
-
- return response;
- }
}
}
diff --git a/bin/Simian.ini b/bin/Simian.ini
index 0c5f535b..0c156556 100644
--- a/bin/Simian.ini
+++ b/bin/Simian.ini
@@ -12,6 +12,9 @@ ConnectionManagement
; no form of authentication or authorization is done
AuthFreeForAll
+; Implements the login server for Linden-based clients directly into Simian
+LindenLogin
+
;
; ---Local Simulator Stores---
;
diff --git a/prebuild.xml b/prebuild.xml
index f03e3b0f..9b0dbf81 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -775,6 +775,7 @@
../../../bin/
+