From e69157a4afbc62d94dca5a01b1a1f2cc1725fbca Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Mon, 16 Mar 2009 18:02:48 +0000 Subject: [PATCH] [Simian] * Sim to sim caps for establishing child agents * Still working out a bug in the libomv event queue, committing what is done for now git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2485 52acb1d6-8a22-11de-b505-999d5b087335 --- OpenMetaverse.Http/CapsClient.cs | 4 +- OpenMetaverse/NetworkManager.cs | 2 +- Programs/Simian/Agent.cs | 1 + Programs/Simian/Extensions/AuthFreeForAll.cs | 6 + Programs/Simian/Extensions/GridLocal.cs | 74 ++++- Programs/Simian/Extensions/LindenLogin.cs | 20 +- Programs/Simian/Extensions/SceneManager.cs | 283 +++++++++++++++++- Programs/Simian/Interfaces/IGridProvider.cs | 51 +--- Programs/Simian/Interfaces/ISceneProvider.cs | 6 +- Programs/Simian/Interfaces/IUDPProvider.cs | 9 + Programs/Simian/SceneExtensions/UDPManager.cs | 49 ++- Programs/Simian/Simian.cs | 31 +- bin/SimianData/Simian.ini | 6 +- bin/SimianData/grid_message_template.msg | 31 ++ 14 files changed, 472 insertions(+), 101 deletions(-) create mode 100644 bin/SimianData/grid_message_template.msg diff --git a/OpenMetaverse.Http/CapsClient.cs b/OpenMetaverse.Http/CapsClient.cs index d13102a7..8e24a2b7 100644 --- a/OpenMetaverse.Http/CapsClient.cs +++ b/OpenMetaverse.Http/CapsClient.cs @@ -37,8 +37,8 @@ namespace OpenMetaverse.Http long totalBytesToReceive, long totalBytesToSend); public delegate void CompleteCallback(CapsClient client, OSD result, Exception error); - public ProgressCallback OnProgress; - public CompleteCallback OnComplete; + public event ProgressCallback OnProgress; + public event CompleteCallback OnComplete; public IWebProxy Proxy; public object UserData; diff --git a/OpenMetaverse/NetworkManager.cs b/OpenMetaverse/NetworkManager.cs index 6ccb3b73..c0bfbaa1 100644 --- a/OpenMetaverse/NetworkManager.cs +++ b/OpenMetaverse/NetworkManager.cs @@ -1130,7 +1130,7 @@ namespace OpenMetaverse // don't reconnect if we're already connected or attempting to connect if (FindSimulator(endPoint) != null) return; - if (Connect(ip, port, rh, false, LoginSeedCapability) == null) + if (Connect(ip, port, rh, false, null) == null) { Logger.Log("Unabled to connect to new sim " + ip + ":" + port, Helpers.LogLevel.Error, Client); diff --git a/Programs/Simian/Agent.cs b/Programs/Simian/Agent.cs index 99f8baf5..44fe9e85 100644 --- a/Programs/Simian/Agent.cs +++ b/Programs/Simian/Agent.cs @@ -80,6 +80,7 @@ namespace Simian public Vector3 CurrentLookAt; public UUID RequestedSitTarget; public Vector3 RequestedSitOffset; + public bool[] NeighborConnections = new bool[8]; public UUID ID { diff --git a/Programs/Simian/Extensions/AuthFreeForAll.cs b/Programs/Simian/Extensions/AuthFreeForAll.cs index 7de961c9..60e8d944 100644 --- a/Programs/Simian/Extensions/AuthFreeForAll.cs +++ b/Programs/Simian/Extensions/AuthFreeForAll.cs @@ -100,9 +100,15 @@ namespace Simian } if (password == agentInfo.PasswordHash) + { + Logger.Log("Authenticated account for " + fullName, Helpers.LogLevel.Info); return agentInfo.ID; + } else + { + Logger.Log("Authentication failed for " + fullName, Helpers.LogLevel.Warning); return UUID.Zero; + } } } } diff --git a/Programs/Simian/Extensions/GridLocal.cs b/Programs/Simian/Extensions/GridLocal.cs index 746be501..830e92ce 100644 --- a/Programs/Simian/Extensions/GridLocal.cs +++ b/Programs/Simian/Extensions/GridLocal.cs @@ -8,8 +8,11 @@ namespace Simian { public class GridLocal : IExtension, IGridProvider { + public event RegionUpdateCallback OnRegionUpdate; + Simian server; DoubleDictionary grid = new DoubleDictionary(); + object syncRoot = new object(); public GridLocal() { @@ -29,33 +32,78 @@ namespace Simian { // No need to check the certificate since the requests are all local - // Check the coordinates - if (!grid.ContainsKey(regionInfo.Handle)) + lock (syncRoot) { - regionID = UUID.Random(); - grid.Add(regionInfo.Handle, regionID, regionInfo); - return true; - } - else - { - regionID = UUID.Zero; - return false; + // Check the coordinates + if (!grid.ContainsKey(regionInfo.Handle)) + { + regionID = UUID.Random(); + grid.Add(regionInfo.Handle, regionID, regionInfo); + return true; + } + else + { + regionID = UUID.Zero; + return false; + } } } - public bool TryRegisterAnyGridSpace(RegionInfo region, X509Certificate2 regionCert, bool isolated, out UUID regionID) + public bool TryRegisterAnyGridSpace(RegionInfo regionInfo, X509Certificate2 regionCert, bool isolated, out UUID regionID) { regionID = UUID.Zero; + regionInfo.Online = false; return false; } public bool UnregisterGridSpace(UUID regionID, X509Certificate2 regionCert) { - return grid.Remove(regionID); + lock (syncRoot) + { + RegionInfo regionInfo; + if (grid.TryGetValue(regionID, out regionInfo)) + { + regionInfo.Online = false; + grid.Remove(regionID); + + if (OnRegionUpdate != null) + { + OnRegionUpdate(regionInfo); + } + + return true; + } + else + { + return false; + } + } } - public void RegionUpdate(UUID regionID, X509Certificate2 regionCert) + public void RegionUpdate(RegionInfo regionInfo, X509Certificate2 regionCert) { + lock (syncRoot) + { + RegionInfo oldRegionInfo; + if (grid.TryGetValue(regionInfo.ID, out oldRegionInfo)) + { + // TODO: Handle requests to move the region + //oldRegionInfo.Handle + + oldRegionInfo.HttpServer = regionInfo.HttpServer; + oldRegionInfo.IPAndPort = regionInfo.IPAndPort; + oldRegionInfo.MapTextureID = regionInfo.MapTextureID; + oldRegionInfo.Name = regionInfo.Name; + oldRegionInfo.Owner = regionInfo.Owner; + oldRegionInfo.Online = regionInfo.Online; + oldRegionInfo.EnableClientCap = regionInfo.EnableClientCap; + + if (OnRegionUpdate != null) + { + OnRegionUpdate(oldRegionInfo); + } + } + } } public void RegionHeartbeat(UUID regionID, X509Certificate2 regionCert) diff --git a/Programs/Simian/Extensions/LindenLogin.cs b/Programs/Simian/Extensions/LindenLogin.cs index 200f858d..874f81f3 100644 --- a/Programs/Simian/Extensions/LindenLogin.cs +++ b/Programs/Simian/Extensions/LindenLogin.cs @@ -199,17 +199,21 @@ namespace Simian // Create a seed capability for this agent agent.SeedCapability = server.Capabilities.CreateCapability(scene.SeedCapabilityHandler, false, agentID); - - // Assign a circuit code and insert the agent into the unassociatedAgents dictionary - agent.CircuitCode = scene.UDP.CreateCircuit(agent); agent.TickLastPacketReceived = Environment.TickCount; agent.Info.LastLoginTime = Utils.DateTimeToUnixTime(DateTime.Now); - // Get this machine's IP address - IPHostEntry entry = Dns.GetHostEntry(System.Environment.MachineName); - IPAddress simIP = entry.AddressList.Length > 0 ? - entry.AddressList[entry.AddressList.Length - 1] : IPAddress.Loopback; + // Assign a circuit code and track the agent as an unassociated agent (no UDP connection yet) + agent.CircuitCode = scene.UDP.CreateCircuit(agent); + + // Get the IP address of the sim (IPAndPort may be storing IPAdress.Any, aka 0.0.0.0) + IPAddress simIP = scene.IPAndPort.Address; + if (simIP == IPAddress.Any) + { + // Get this machine's IP address + IPHostEntry entry = Dns.GetHostEntry(server.HttpUri.DnsSafeHost); + simIP = entry.AddressList.Length > 0 ? entry.AddressList[entry.AddressList.Length - 1] : IPAddress.Loopback; + } response.AgentID = agent.ID; response.SecureSessionID = agent.SecureSessionID; @@ -240,6 +244,8 @@ namespace Simian response.SimPort = (ushort)scene.IPAndPort.Port; response.StartLocation = "last"; // FIXME: response.Success = true; + + Logger.DebugLog("Sending a login success response with circuit_code " + response.CircuitCode); } else { diff --git a/Programs/Simian/Extensions/SceneManager.cs b/Programs/Simian/Extensions/SceneManager.cs index 403c6de9..8e4485d9 100644 --- a/Programs/Simian/Extensions/SceneManager.cs +++ b/Programs/Simian/Extensions/SceneManager.cs @@ -6,6 +6,7 @@ using System.IO; using System.Net; using System.Reflection; using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; using System.Threading; using ExtensionLoader; using HttpServer; @@ -92,9 +93,7 @@ namespace Simian public IPEndPoint IPAndPort { get { return endpoint; } set { endpoint = value; } } public Vector3 DefaultPosition { get { return defaultPosition; } } public Vector3 DefaultLookAt { get { return defaultLookAt; } } - public float WaterHeight { get { return 20f; } } - public uint TerrainPatchWidth { get { return 16; } } public uint TerrainPatchHeight { get { return 16; } } public uint TerrainPatchCountWidth { get { return 16; } } @@ -119,22 +118,27 @@ namespace Simian string regionName; Vector3 defaultPosition = new Vector3(128f, 128f, 30f); Vector3 defaultLookAt = Vector3.UnitX; + /// Track the eight neighboring tiles around us + RegionInfo[] neighbors = new RegionInfo[8]; + /// List of callback URIs for pending client connections. When a new client connection + /// is established for a client in this dictionary, an enable_client_complete message will be + /// sent to the associated URI + Dictionary enableClientCompleteCallbacks = new Dictionary(); public SceneManager() { } - public bool Start(Simian server, string name, IPEndPoint endpoint, UUID regionID, uint regionX, uint regionY, - string defaultTerrainFile, int staticObjects, int physicalObjects) + public bool Start(Simian server, RegionInfo regionInfo, X509Certificate2 regionCert, string defaultTerrainFile, int staticObjects, int physicalObjects) { this.server = server; - this.regionName = name; - this.endpoint = endpoint; - this.regionID = regionID; + this.regionName = regionInfo.Name; + this.endpoint = regionInfo.IPAndPort; + this.regionID = regionInfo.ID; // Set the properties because this will automatically update the regionHandle - RegionX = regionX; - RegionY = regionY; + RegionX = regionInfo.X; + RegionY = regionInfo.Y; #region ISceneProvider Extension Loading @@ -159,7 +163,10 @@ namespace Simian // Start all of the extensions foreach (IExtension extension in extensions.Extensions) { - Logger.Log("Starting Scene extension " + extension.GetType().Name, Helpers.LogLevel.Info); + // Only print the extension names if this is the first loaded scene + if (server.Scenes.Count == 0) + Logger.Log("Starting Scene extension " + extension.GetType().Name, Helpers.LogLevel.Info); + extension.Start(this); } } @@ -172,16 +179,33 @@ namespace Simian #endregion ISceneProvider Extension Loading - udp.RegisterPacketCallback(PacketType.CompleteAgentMovement, new PacketCallback(CompleteAgentMovementHandler)); + // Callback registration + server.Grid.OnRegionUpdate += Grid_OnRegionUpdate; + udp.OnAgentConnection += udp_OnAgentConnection; + udp.RegisterPacketCallback(PacketType.CompleteAgentMovement, CompleteAgentMovementHandler); + udp.RegisterPacketCallback(PacketType.ChatFromViewer, ChatHandler); if (!String.IsNullOrEmpty(defaultTerrainFile)) LoadTerrain(Simian.DATA_DIR + defaultTerrainFile); - Logger.Log(String.Format("Region {0} online at ({1},{2}) listening on {3}", name, regionX, regionY, endpoint), + Logger.Log(String.Format("Region {0} online at ({1},{2}) listening on {3}", regionName, regionX, regionY, endpoint), Helpers.LogLevel.Info); + + // Tell the grid that this region is online + regionInfo.Online = true; + server.Grid.RegionUpdate(regionInfo, regionCert); + return true; } + void ChatHandler(Packet packet, Agent agent) + { + ChatFromViewerPacket chat = (ChatFromViewerPacket)packet; + string message = Utils.BytesToString(chat.ChatData.Message); + if (message == "lol") + SendEvent(agent, "lol", new OSDMap()); + } + public void Stop() { Logger.Log("Stopping region " + regionName, Helpers.LogLevel.Info); @@ -665,6 +689,7 @@ namespace Simian lock (sceneAgents) sceneAgents[agent.ID] = agent; Logger.Log("Added agent " + agent.FullName + " to the scene", Helpers.LogLevel.Info); + return true; } @@ -942,6 +967,10 @@ namespace Simian } } + #endregion Capabilities Interfaces + + #region Callback Handlers + public bool SeedCapabilityHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state) { UUID agentID = (UUID)state; @@ -987,9 +1016,105 @@ namespace Simian return true; } - #endregion Capabilities Interfaces + public bool EnableClientCapHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state) + { + OSDMap map = OSDParser.DeserializeLLSDXml(request.Body) as OSDMap; + OSDMap osdResponse = new OSDMap(); - #region Callback Handlers + if (map != null) + { + UUID agentID = map["agent_id"].AsUUID(); + UUID sessionID = map["session_id"].AsUUID(); + UUID secureSessionID = map["secure_session_id"].AsUUID(); + // TODO: Send an identity url and token instead so we can pull down all of the information + string firstName = map["first_name"].AsString(); + string lastName = map["last_name"].AsString(); + Uri callbackUri = map["callback_uri"].AsUri(); + + Logger.Log(String.Format( + "enable_client request. agent_id: {0}, session_id: {1}, secureSessionID: {2}, " + + "first_name: {3}, last_name: {4}, callback_uri: {5}", agentID, sessionID, secureSessionID, + firstName, lastName, callbackUri), Helpers.LogLevel.Info); + + if (agentID != UUID.Zero && sessionID != UUID.Zero && secureSessionID != UUID.Zero && + !String.IsNullOrEmpty(firstName) && !String.IsNullOrEmpty(lastName)) + { + AgentInfo info = new AgentInfo(); + info.AccessLevel = "M"; + info.FirstName = firstName; + info.Height = 1.9f; + info.HomeLookAt = Vector3.UnitX; + info.HomePosition = new Vector3(128f, 128f, 25f); + info.HomeRegionHandle = regionHandle; + info.ID = agentID; + info.LastName = lastName; + info.PasswordHash = String.Empty; + + Agent agent = new Agent(new SimulationObject(new Avatar(), this), info); + + // Set the avatar ID + agent.Avatar.Prim.ID = agentID; + + // Random session IDs + agent.SessionID = sessionID; + agent.SecureSessionID = secureSessionID; + + // Create a seed capability for this agent + agent.SeedCapability = server.Capabilities.CreateCapability(SeedCapabilityHandler, false, agentID); + + agent.TickLastPacketReceived = Environment.TickCount; + agent.Info.LastLoginTime = Utils.DateTimeToUnixTime(DateTime.Now); + + // Add the callback URI to the list of pending enable_client_complete callbacks + lock (enableClientCompleteCallbacks) + enableClientCompleteCallbacks[agentID] = callbackUri; + + // Assign a circuit code and track the agent as an unassociated agent (no UDP connection yet) + agent.CircuitCode = udp.CreateCircuit(agent); + + osdResponse["success"] = OSD.FromBoolean(true); + } + else + { + osdResponse["success"] = OSD.FromBoolean(false); + osdResponse["message"] = OSD.FromString("missing required fields for enable_client"); + } + } + else + { + osdResponse["success"] = OSD.FromBoolean(false); + osdResponse["message"] = OSD.FromString("failed to parse enable_client message"); + } + + byte[] responseData = OSDParser.SerializeLLSDXmlBytes(osdResponse); + response.ContentLength = responseData.Length; + response.Body.Write(responseData, 0, responseData.Length); + response.Body.Flush(); + + return true; + } + + public bool EnableClientCompleteCapHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state) + { + OSDMap map = OSDParser.DeserializeLLSDXml(request.Body) as OSDMap; + OSDMap osdResponse = new OSDMap(); + + if (map != null) + { + UUID agentID = map["agent_id"].AsUUID(); + Uri seedCapability = map["seed_capability"].AsUri(); + + Logger.Log(String.Format("enable_client_complete response. agent_id: {0}, seed_capability: {1}", + agentID, seedCapability), Helpers.LogLevel.Info); + + if (enableClientCompleteCallbacks.Remove(agentID)) + { + // FIXME: Finish this + } + } + + return true; + } bool EventQueueHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state) { @@ -997,6 +1122,70 @@ namespace Simian return eqServer.EventQueueHandler(context, request, response); } + void Grid_OnRegionUpdate(RegionInfo regionInfo) + { + // TODO: Detect regions coming online so we can call + // InformClientOfNeighbors(agent); + // for every agent + + // Check if the sim was a neighbor + if (regionInfo.Handle == Utils.UIntsToLong(256 * (regionX - 1), 256 * (regionY + 1))) + neighbors[0] = regionInfo; + else if (regionInfo.Handle == Utils.UIntsToLong(256 * regionX, 256 * (regionY + 1))) + neighbors[1] = regionInfo; + else if (regionInfo.Handle == Utils.UIntsToLong(256 * (regionX + 1), 256 * (regionY + 1))) + neighbors[2] = regionInfo; + else if (regionInfo.Handle == Utils.UIntsToLong(256 * (regionX - 1), 256 * regionY)) + neighbors[3] = regionInfo; + else if (regionInfo.Handle == Utils.UIntsToLong(256 * (regionX + 1), 256 * regionY)) + neighbors[4] = regionInfo; + else if (regionInfo.Handle == Utils.UIntsToLong(256 * (regionX - 1), 256 * (regionY - 1))) + neighbors[5] = regionInfo; + else if (regionInfo.Handle == Utils.UIntsToLong(256 * regionX, 256 * (regionY - 1))) + neighbors[6] = regionInfo; + else if (regionInfo.Handle == Utils.UIntsToLong(256 * (regionX + 1), 256 * (regionY - 1))) + neighbors[7] = regionInfo; + } + + void udp_OnAgentConnection(Agent agent, uint circuitCode) + { + Uri callbackUri; + if (enableClientCompleteCallbacks.TryGetValue(agent.ID, out callbackUri)) + { + lock (enableClientCompleteCallbacks) + enableClientCompleteCallbacks.Remove(agent.ID); + + Logger.Log("Sending enable_client_complete callback to " + callbackUri.ToString(), Helpers.LogLevel.Info); + + OSDMap map = new OSDMap(1); + map["agent_id"] = OSD.FromUUID(agent.ID); + + AutoResetEvent waitEvent = new AutoResetEvent(false); + + CapsClient request = new CapsClient(callbackUri); + request.OnComplete += + delegate(CapsClient client, OSD result, Exception error) + { + OSDMap response = result as OSDMap; + if (response != null) + { + bool success = response["success"].AsBoolean(); + Logger.Log("enable_client_complete response: " + success, Helpers.LogLevel.Info); + + if (success) + { + Uri seedCapability = response["seed_capability"].AsUri(); + } + } + waitEvent.Set(); + }; + request.StartRequest(map); + + if (!waitEvent.WaitOne(30 * 1000, false)) + Logger.Log("enable_client_complete request timed out", Helpers.LogLevel.Warning); + } + } + void CompleteAgentMovementHandler(Packet packet, Agent agent) { // Add this avatar as an object in the scene @@ -1006,7 +1195,7 @@ namespace Simian AgentMovementCompletePacket complete = new AgentMovementCompletePacket(); complete.AgentData.AgentID = agent.ID; complete.AgentData.SessionID = agent.SessionID; - complete.Data.LookAt = Vector3.UnitX; + complete.Data.LookAt = Vector3.UnitX; // TODO: Properly implement LookAt someday complete.Data.Position = agent.Avatar.Prim.Position; complete.Data.RegionHandle = regionHandle; complete.Data.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now); @@ -1023,10 +1212,72 @@ namespace Simian online.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock(); online.AgentBlock[0].AgentID = agent.ID; udp.BroadcastPacket(online, PacketCategory.State); + + // Initiate the connection process for this agent to neighboring regions + InformClientOfNeighbors(agent); } #endregion Callback Handlers + void InformClientOfNeighbors(Agent agent) + { + for (int i = 0; i < 8; i++) + { + if (!agent.NeighborConnections[i] && neighbors[i].Online) + { + Logger.Log("Sending enable_client for " + agent.FullName + " to neighbor " + neighbors[i].Name, Helpers.LogLevel.Info); + + // Create a callback for enable_client_complete + Uri callbackUri = server.Capabilities.CreateCapability(EnableClientCompleteCapHandler, false, null); + + OSDMap map = new OSDMap(); + map["agent_id"] = OSD.FromUUID(agent.ID); + map["session_id"] = OSD.FromUUID(agent.SessionID); + map["secure_session_id"] = OSD.FromUUID(agent.SecureSessionID); + map["first_name"] = OSD.FromString(agent.Info.FirstName); + map["last_name"] = OSD.FromString(agent.Info.LastName); + map["callback_uri"] = OSD.FromUri(callbackUri); + + AutoResetEvent waitEvent = new AutoResetEvent(false); + + CapsClient request = new CapsClient(neighbors[i].EnableClientCap); + request.OnComplete += + delegate(CapsClient client, OSD result, Exception error) + { + OSDMap response = result as OSDMap; + if (response != null) + { + bool success = response["success"].AsBoolean(); + Logger.Log("enable_client response: " + success, Helpers.LogLevel.Info); + + if (success) + { + // Send the EnableSimulator capability to clients + OSDMap llsdSimInfo = new OSDMap(3); + + llsdSimInfo.Add("Handle", OSD.FromULong(neighbors[i].Handle)); + llsdSimInfo.Add("IP", OSD.FromBinary(neighbors[i].IPAndPort.Address.GetAddressBytes())); + llsdSimInfo.Add("Port", OSD.FromInteger(neighbors[i].IPAndPort.Port)); + + OSDArray arr = new OSDArray(1); + arr.Add(llsdSimInfo); + + OSDMap llsdBody = new OSDMap(1); + llsdBody.Add("SimulatorInfo", arr); + + SendEvent(agent, "EnableSimulator", llsdBody); + } + } + waitEvent.Set(); + }; + request.StartRequest(map); + + if (!waitEvent.WaitOne(30 * 1000, false)) + Logger.Log("enable_client request timed out", Helpers.LogLevel.Warning); + } + } + } + // HACK: The reduction provider will deprecate this at some point void SynchronizeStateTo(Agent agent) { @@ -1452,7 +1703,7 @@ namespace Simian { #region ImprovedTerseObjectUpdate - Logger.DebugLog("Sending ImprovedTerseObjectUpdate"); + //Logger.DebugLog("Sending ImprovedTerseObjectUpdate"); int pos = 0; byte[] data = new byte[(obj.Prim is Avatar ? 60 : 44)]; diff --git a/Programs/Simian/Interfaces/IGridProvider.cs b/Programs/Simian/Interfaces/IGridProvider.cs index a5da1ff0..a553e735 100644 --- a/Programs/Simian/Interfaces/IGridProvider.cs +++ b/Programs/Simian/Interfaces/IGridProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Net; using System.Security.Cryptography.X509Certificates; using OpenMetaverse; @@ -6,7 +7,7 @@ using OpenMetaverse.StructuredData; namespace Simian { - public class RegionInfo + public struct RegionInfo { public string Name; public UUID ID; @@ -16,6 +17,7 @@ namespace Simian public Uri HttpServer; public UUID MapTextureID; public Uri Owner; + public Uri EnableClientCap; public uint X { @@ -23,7 +25,7 @@ namespace Simian { uint x, y; OpenMetaverse.Utils.LongToUInts(Handle, out x, out y); - return x; + return x / 256; } set @@ -40,7 +42,7 @@ namespace Simian { uint x, y; OpenMetaverse.Utils.LongToUInts(Handle, out x, out y); - return y; + return y / 256; } set @@ -50,51 +52,20 @@ namespace Simian Handle = OpenMetaverse.Utils.UIntsToLong(x, value); } } - - public OSDMap SerializeToOSD() - { - OSDMap osdata = new OSDMap(); - - osdata["handle"] = OSD.FromULong(Handle); - osdata["id"] = OSD.FromUUID(ID); - osdata["map_texture_id"] = OSD.FromUUID(MapTextureID); - osdata["name"] = OSD.FromString(Name); - osdata["owner"] = OSD.FromUri(Owner); - osdata["ipaddr"] = OSD.FromString(IPAndPort.Address.ToString()); - osdata["port"] = OSD.FromInteger(IPAndPort.Port); - - return osdata; - } - - public void Deserialize(OSD osdata) - { - if (osdata.Type == OSDType.Map) - { - OSDMap map = (OSDMap)osdata; - - Handle = map["handle"].AsULong(); - ID = map["id"].AsUUID(); - MapTextureID = map["map_texture_id"].AsUUID(); - Name = map["name"].AsString(); - Owner = map["owner"].AsUri(); - - IPAddress address; - if (IPAddress.TryParse(map["ipaddr"].AsString(), out address)) - IPAndPort = new IPEndPoint(address, map["port"].AsInteger()); - } - } } - public delegate void NeighborSimNotice(RegionInfo neighbor, bool online); + public delegate void RegionUpdateCallback(RegionInfo regionInfo); public interface IGridProvider { + event RegionUpdateCallback OnRegionUpdate; + bool TryRegisterGridSpace(RegionInfo regionInfo, X509Certificate2 regionCert, out UUID regionID); /// /// Attempts to register any available space closest to the given grid /// coordinates /// - /// Information about the region to be registered. + /// Information about the region to be registered. /// The X, Y, and Handle values may be modified if the exact grid /// coordinate requested is not available /// SSL client certificate file for the region. @@ -104,10 +75,10 @@ namespace Simian /// The unique identifier of the registered /// region upon success. This will also be assigned to region.ID /// True if the registration was successful, otherwise false - bool TryRegisterAnyGridSpace(RegionInfo region, X509Certificate2 regionCert, bool isolated, out UUID regionID); + bool TryRegisterAnyGridSpace(RegionInfo regionInfo, X509Certificate2 regionCert, bool isolated, out UUID regionID); bool UnregisterGridSpace(UUID regionID, X509Certificate2 regionCert); - void RegionUpdate(UUID regionID, X509Certificate2 regionCert); + void RegionUpdate(RegionInfo regionInfo, X509Certificate2 regionCert); void RegionHeartbeat(UUID regionID, X509Certificate2 regionCert); bool TryGetRegion(UUID regionID, X509Certificate2 regionCert, out RegionInfo region); diff --git a/Programs/Simian/Interfaces/ISceneProvider.cs b/Programs/Simian/Interfaces/ISceneProvider.cs index 205afe91..ce46dbaf 100644 --- a/Programs/Simian/Interfaces/ISceneProvider.cs +++ b/Programs/Simian/Interfaces/ISceneProvider.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Net; +using System.Security.Cryptography.X509Certificates; using HttpServer; using OpenMetaverse; using OpenMetaverse.StructuredData; @@ -149,7 +150,7 @@ namespace Simian uint TerrainPatchCountWidth { get; } uint TerrainPatchCountHeight { get; } - bool Start(Simian server, string name, IPEndPoint endpoint, UUID regionID, uint regionX, uint regionY, string defaultTerrainFile, int staticObjects, int physicalObjects); + bool Start(Simian server, RegionInfo regionInfo, X509Certificate2 regionCert, string defaultTerrainFile, int staticObjects, int physicalObjects); void Stop(); void ObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags updateFlags); @@ -194,5 +195,8 @@ namespace Simian void SendEvent(Agent agent, string name, OSDMap body); bool HasRunningEventQueue(Agent agent); bool SeedCapabilityHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state); + + bool EnableClientCapHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state); + bool EnableClientCompleteCapHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state); } } diff --git a/Programs/Simian/Interfaces/IUDPProvider.cs b/Programs/Simian/Interfaces/IUDPProvider.cs index ce6d145d..e05b9567 100644 --- a/Programs/Simian/Interfaces/IUDPProvider.cs +++ b/Programs/Simian/Interfaces/IUDPProvider.cs @@ -44,14 +44,23 @@ namespace Simian /// The specified category of the outgoing packet /// True to continue sending this packet, otherwise false public delegate bool OutgoingPacketCallback(Packet packet, UUID agentID, PacketCategory category); + /// + /// Triggered when an agent establishes a UDP connection + /// + /// Agent that now has a UDP connection + /// Circuit code that was used to identify the agent + /// during connection establishment + public delegate void AgentConnectionCallback(Agent agent, uint circuitCode); public interface IUDPProvider { event OutgoingPacketCallback OnOutgoingPacket; + event AgentConnectionCallback OnAgentConnection; void AddClient(Agent agent, IPEndPoint endpoint); bool RemoveClient(Agent agent); uint CreateCircuit(Agent agent); + void CreateCircuit(Agent agent, uint circuitCode); void SendPacket(UUID agentID, Packet packet, PacketCategory category); void BroadcastPacket(Packet packet, PacketCategory category); diff --git a/Programs/Simian/SceneExtensions/UDPManager.cs b/Programs/Simian/SceneExtensions/UDPManager.cs index fb21a8fb..ad2b911a 100644 --- a/Programs/Simian/SceneExtensions/UDPManager.cs +++ b/Programs/Simian/SceneExtensions/UDPManager.cs @@ -73,6 +73,8 @@ namespace Simian public class UDPManager : IExtension, IUDPProvider { + public event AgentConnectionCallback OnAgentConnection; + ISceneProvider scene; UDPServer udpServer; @@ -85,7 +87,7 @@ namespace Simian public bool Start(ISceneProvider scene) { this.scene = scene; - udpServer = new UDPServer(scene.IPAndPort, scene); + udpServer = new UDPServer(scene.IPAndPort, scene, this); return true; } @@ -110,6 +112,11 @@ namespace Simian return udpServer.CreateCircuit(agent); } + public void CreateCircuit(Agent agent, uint circuitCode) + { + udpServer.CreateCircuit(agent, circuitCode); + } + public void SendPacket(UUID agentID, Packet packet, PacketCategory category) { if (OnOutgoingPacket == null || OnOutgoingPacket(packet, agentID, category)) @@ -126,6 +133,14 @@ namespace Simian { udpServer.RegisterPacketCallback(type, callback); } + + internal void TriggerAgentConnectionCallback(Agent agent, uint circuitCode) + { + if (OnAgentConnection != null) + { + OnAgentConnection(agent, circuitCode); + } + } } public class UDPServer : UDPBase @@ -141,14 +156,17 @@ namespace Simian DoubleDictionary clients = new DoubleDictionary(); /// Dictionary unassociatedAgents = new Dictionary(); - /// - int currentCircuitCode = 0; + /// Generates new circuit codes + Random circuitCodeGenerator = new Random(); + /// Reference to the UDPManager for triggering functions + UDPManager manager; // FIXME: Upgrade UDPBase to be able to listen on different endpoints - public UDPServer(IPEndPoint endpoint, ISceneProvider scene) + public UDPServer(IPEndPoint endpoint, ISceneProvider scene, UDPManager manager) : base(endpoint.Port) { this.scene = scene; + this.manager = manager; Start(); @@ -182,15 +200,28 @@ namespace Simian public uint CreateCircuit(Agent agent) { - uint circuitCode = (uint)Interlocked.Increment(ref currentCircuitCode); + uint circuitCode = 0; - // Put this client in the list of clients that have not been associated with an IPEndPoint yet + lock (unassociatedAgents) + { + // Generate a random circuit code that is not currently in use + do { circuitCode = (uint)circuitCodeGenerator.Next(); } + while (unassociatedAgents.ContainsKey(circuitCode)); + + // Put this client in the list of clients that have not been associated with an IPEndPoint yet + unassociatedAgents[circuitCode] = agent; + } + + Logger.Log("Created circuit " + circuitCode + " for " + agent.FullName, Helpers.LogLevel.Info); + return circuitCode; + } + + public void CreateCircuit(Agent agent, uint circuitCode) + { lock (unassociatedAgents) unassociatedAgents[circuitCode] = agent; Logger.Log("Created circuit " + circuitCode + " for " + agent.FullName, Helpers.LogLevel.Info); - - return circuitCode; } public void BroadcastPacket(Packet packet, PacketCategory category) @@ -625,6 +656,8 @@ namespace Simian { unassociatedAgents.Remove(circuitCode); scene.AgentAdd(this, agent, PrimFlags.None); + + manager.TriggerAgentConnectionCallback(agent, circuitCode); return true; } else diff --git a/Programs/Simian/Simian.cs b/Programs/Simian/Simian.cs index 3cb2e2fd..08a2e088 100644 --- a/Programs/Simian/Simian.cs +++ b/Programs/Simian/Simian.cs @@ -59,6 +59,8 @@ namespace Simian IPAddress address; IConfig httpConfig; + #region Config Parsing + try { // Load the extension list (and ordering) from our config file @@ -73,6 +75,8 @@ namespace Simian return false; } + #endregion Config Parsing + #region HTTP Server int port = httpConfig.GetInt("ListenPort"); @@ -177,6 +181,11 @@ namespace Simian for (int i = 0; i < configFiles.Length; i++) { + // TODO: Support non-SceneManager scenes? + ISceneProvider scene = new SceneManager(); + + #region Config Parsing + IniConfigSource source = new IniConfigSource(configFiles[i]); IConfig regionConfig = source.Configs["Region"]; @@ -196,6 +205,8 @@ namespace Simian continue; } + #endregion Config Parsing + #region IPEndPoint Assignment IPEndPoint endpoint; @@ -241,14 +252,16 @@ namespace Simian continue; } - RegionInfo info = new RegionInfo(); - info.Handle = Utils.UIntsToLong(256 * regionX, 256 * regionY); - info.HttpServer = HttpUri; - info.IPAndPort = endpoint; - info.Name = name; - info.Online = true; + RegionInfo regionInfo = new RegionInfo(); + regionInfo.Handle = Utils.UIntsToLong(256 * regionX, 256 * regionY); + regionInfo.HttpServer = HttpUri; + regionInfo.IPAndPort = endpoint; + regionInfo.Name = name; + regionInfo.Online = true; + // Create a capability for other regions to initiate a client connection to this region + regionInfo.EnableClientCap = Capabilities.CreateCapability(scene.EnableClientCapHandler, false, null); - if (!Grid.TryRegisterGridSpace(info, regionCert, out info.ID)) + if (!Grid.TryRegisterGridSpace(regionInfo, regionCert, out regionInfo.ID)) { Logger.Log("Failed to register grid space for region " + name, Helpers.LogLevel.Error); continue; @@ -256,9 +269,7 @@ namespace Simian #endregion Grid Registration - // TODO: Support non-SceneManager scenes? - ISceneProvider scene = new SceneManager(); - scene.Start(this, name, endpoint, info.ID, regionX, regionY, defaultTerrain, staticObjectLimit, physicalObjectLimit); + scene.Start(this, regionInfo, regionCert, defaultTerrain, staticObjectLimit, physicalObjectLimit); Scenes.Add(scene); } } diff --git a/bin/SimianData/Simian.ini b/bin/SimianData/Simian.ini index 925525e1..61b0d3f6 100644 --- a/bin/SimianData/Simian.ini +++ b/bin/SimianData/Simian.ini @@ -103,12 +103,12 @@ XScriptEngine ScriptConsole ; Texture downloads -;ImageDelivery +ImageDelivery ; A simple physics engine for avatar movement. Supports walking, flying, and ; swimming as well as avatar-avatar collisions. Does not support avatar-prim ; or prim-prim collisions. -;Movement +Movement ; Periscope allows you to proxy a foreign grid simulator into the local Simian ; using a libOpenMetaverse bot. The first person to login to Simian will become @@ -117,7 +117,7 @@ ScriptConsole ; agent is seeing through the periscope. If you enable this extension, disable ; ImageDelivery and Movement as Periscope has its own implementations of those ; extensions -Periscope +;Periscope ; ; ---Linden Client Extensions--- diff --git a/bin/SimianData/grid_message_template.msg b/bin/SimianData/grid_message_template.msg new file mode 100644 index 00000000..99aad306 --- /dev/null +++ b/bin/SimianData/grid_message_template.msg @@ -0,0 +1,31 @@ +%%enable_client + +-> +{ + agent_id: uuid, + session_id: uuid, + secure_session_id: uuid, + first_name: string, + last_name: string, + callback_uri: uri +} + +<- +{ + success: boolean, + message: string +} + +%%enable_client_complete + +-> +{ + agent_id: uuid, + seed_capability: uri +} + +<- +{ + success: boolean, + message: string +}