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/ +