diff --git a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs index 4642b2adaf..7d4851652b 100644 --- a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs +++ b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs @@ -623,10 +623,13 @@ namespace OpenSim.Groups if (agent != null) break; } - if (agent == null) // oops - return AgentID.ToString(); + if (agent != null) + return Util.ProduceUserUniversalIdentifier(agent); + + // we don't know anything about this foreign user + // try asking the user management module, which may know more + return m_UserManagement.GetUserUUI(AgentID); - return Util.ProduceUserUniversalIdentifier(agent); } private string AgentUUIForOutside(string AgentIDStr) diff --git a/OpenSim/Data/IRegionData.cs b/OpenSim/Data/IRegionData.cs index 70e10653de..463c621859 100644 --- a/OpenSim/Data/IRegionData.cs +++ b/OpenSim/Data/IRegionData.cs @@ -81,6 +81,7 @@ namespace OpenSim.Data bool Delete(UUID regionID); List GetDefaultRegions(UUID scopeID); + List GetDefaultHypergridRegions(UUID scopeID); List GetFallbackRegions(UUID scopeID, int x, int y); List GetHyperlinks(UUID scopeID); } diff --git a/OpenSim/Data/MSSQL/MSSQLRegionData.cs b/OpenSim/Data/MSSQL/MSSQLRegionData.cs index 0d89706566..c0589dfb49 100644 --- a/OpenSim/Data/MSSQL/MSSQLRegionData.cs +++ b/OpenSim/Data/MSSQL/MSSQLRegionData.cs @@ -315,6 +315,11 @@ namespace OpenSim.Data.MSSQL return Get((int)RegionFlags.DefaultRegion, scopeID); } + public List GetDefaultHypergridRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultHGRegion, scopeID); + } + public List GetFallbackRegions(UUID scopeID, int x, int y) { List regions = Get((int)RegionFlags.FallbackRegion, scopeID); diff --git a/OpenSim/Data/MySQL/MySQLRegionData.cs b/OpenSim/Data/MySQL/MySQLRegionData.cs index a2d4ae4647..2ad7590759 100644 --- a/OpenSim/Data/MySQL/MySQLRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLRegionData.cs @@ -310,6 +310,11 @@ namespace OpenSim.Data.MySQL return Get((int)RegionFlags.DefaultRegion, scopeID); } + public List GetDefaultHypergridRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultHGRegion, scopeID); + } + public List GetFallbackRegions(UUID scopeID, int x, int y) { List regions = Get((int)RegionFlags.FallbackRegion, scopeID); diff --git a/OpenSim/Data/Null/NullRegionData.cs b/OpenSim/Data/Null/NullRegionData.cs index f707d98cd2..d28cd9933b 100644 --- a/OpenSim/Data/Null/NullRegionData.cs +++ b/OpenSim/Data/Null/NullRegionData.cs @@ -239,6 +239,11 @@ namespace OpenSim.Data.Null return Get((int)RegionFlags.DefaultRegion, scopeID); } + public List GetDefaultHypergridRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultHGRegion, scopeID); + } + public List GetFallbackRegions(UUID scopeID, int x, int y) { List regions = Get((int)RegionFlags.FallbackRegion, scopeID); diff --git a/OpenSim/Framework/RegionFlags.cs b/OpenSim/Framework/RegionFlags.cs index a3089b077d..7c6569e30c 100644 --- a/OpenSim/Framework/RegionFlags.cs +++ b/OpenSim/Framework/RegionFlags.cs @@ -48,6 +48,7 @@ namespace OpenSim.Framework NoMove = 64, // Don't allow moving this region Reservation = 128, // This is an inactive reservation Authenticate = 256, // Require authentication - Hyperlink = 512 // Record represents a HG link + Hyperlink = 512, // Record represents a HG link + DefaultHGRegion = 1024 // Record represents a default region for hypergrid teleports only. } } \ No newline at end of file diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 7398b37ab9..7db575b78a 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -130,7 +130,7 @@ namespace OpenSim.Framework private static SmartThreadPool m_ThreadPool; // Unix-epoch starts at January 1st 1970, 00:00:00 UTC. And all our times in the server are (or at least should be) in UTC. - private static readonly DateTime unixEpoch = + public static readonly DateTime UnixEpoch = DateTime.ParseExact("1970-01-01 00:00:00 +0", "yyyy-MM-dd hh:mm:ss z", DateTimeFormatInfo.InvariantInfo).ToUniversalTime(); private static readonly string rawUUIDPattern @@ -521,20 +521,18 @@ namespace OpenSim.Framework public static int ToUnixTime(DateTime stamp) { - TimeSpan t = stamp.ToUniversalTime() - unixEpoch; - return (int) t.TotalSeconds; + TimeSpan t = stamp.ToUniversalTime() - UnixEpoch; + return (int)t.TotalSeconds; } public static DateTime ToDateTime(ulong seconds) { - DateTime epoch = unixEpoch; - return epoch.AddSeconds(seconds); + return UnixEpoch.AddSeconds(seconds); } public static DateTime ToDateTime(int seconds) { - DateTime epoch = unixEpoch; - return epoch.AddSeconds(seconds); + return UnixEpoch.AddSeconds(seconds); } /// diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs index 626932f53d..b3b0b8acc0 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs @@ -76,7 +76,7 @@ namespace OpenSim.Region.ClientStack.Linden.Tests } [Test] - public void AddForClient() + public void TestAddForClient() { TestHelpers.InMethod(); // log4net.Config.XmlConfigurator.Configure(); @@ -88,7 +88,7 @@ namespace OpenSim.Region.ClientStack.Linden.Tests } [Test] - public void RemoveForClient() + public void TestRemoveForClient() { TestHelpers.InMethod(); // TestHelpers.EnableLogging(); diff --git a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs index 174642db57..917f164502 100644 --- a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs @@ -372,21 +372,24 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat UUID fromAgentID, UUID ownerID, string fromName, ChatTypeEnum type, string message, ChatSourceType src, bool ignoreDistance) { - // don't send chat to child agents - if (presence.IsChildAgent) return false; - - Vector3 fromRegionPos = fromPos + regionPos; - Vector3 toRegionPos = presence.AbsolutePosition + - new Vector3(presence.Scene.RegionInfo.RegionLocX * Constants.RegionSize, - presence.Scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); - - int dis = (int)Util.GetDistanceTo(toRegionPos, fromRegionPos); - - if (type == ChatTypeEnum.Whisper && dis > m_whisperdistance || - type == ChatTypeEnum.Say && dis > m_saydistance || - type == ChatTypeEnum.Shout && dis > m_shoutdistance) - { + if (presence.LifecycleState != ScenePresenceState.Running) return false; + + if (!ignoreDistance) + { + Vector3 fromRegionPos = fromPos + regionPos; + Vector3 toRegionPos = presence.AbsolutePosition + + new Vector3(presence.Scene.RegionInfo.RegionLocX * Constants.RegionSize, + presence.Scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); + + int dis = (int)Util.GetDistanceTo(toRegionPos, fromRegionPos); + + if (type == ChatTypeEnum.Whisper && dis > m_whisperdistance || + type == ChatTypeEnum.Say && dis > m_saydistance || + type == ChatTypeEnum.Shout && dis > m_shoutdistance) + { + return false; + } } // TODO: should change so the message is sent through the avatar rather than direct to the ClientView diff --git a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs index bf1cffb15f..56ff2bddb1 100644 --- a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs @@ -1212,7 +1212,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles json.Add("jsonrpc", OSD.FromString("2.0")); json.Add("id", OSD.FromString(jsonId)); json.Add("method", OSD.FromString(method)); - // Experiment + json.Add("params", OSD.SerializeMembers(parameters)); string jsonRequestData = OSDParser.SerializeJsonString(json); @@ -1240,8 +1240,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles } Stream rstream = webResponse.GetResponseStream(); - if (rstream.Length < 1) - return false; OSDMap mret = new OSDMap(); try @@ -1318,8 +1316,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles } Stream rstream = webResponse.GetResponseStream(); - if (rstream.Length < 1) - return false; OSDMap response = new OSDMap(); try diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 1f3e7a1c6f..ed14c125e0 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -925,6 +925,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) { + if (!sp.Scene.IncomingPreCloseAgent(sp)) + return; + // We need to delay here because Imprudence viewers, unlike v1 or v3, have a short (<200ms, <500ms) delay before // they regard the new region as the current region after receiving the AgentMovementComplete // response. If close is sent before then, it will cause the viewer to quit instead. @@ -1087,6 +1090,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) { + if (!sp.Scene.IncomingPreCloseAgent(sp)) + return; + // RED ALERT!!!! // PLEASE DO NOT DECREASE THIS WAIT TIME UNDER ANY CIRCUMSTANCES. // THE VIEWERS SEEM TO NEED SOME TIME AFTER RECEIVING MoveAgentIntoRegion @@ -1100,6 +1106,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // then this will be handled in IncomingCloseAgent under lock conditions m_log.DebugFormat( "[ENTITY TRANSFER MODULE]: Closing agent {0} in {1} after teleport", sp.Name, Scene.Name); + sp.Scene.IncomingCloseAgent(sp.UUID, false); } else diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs index 7adb203bed..8c983e68d5 100644 --- a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs +++ b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs @@ -481,14 +481,20 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement public string GetUserUUI(UUID userID) { - UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, userID); - if (account != null) - return userID.ToString(); - UserData ud; lock (m_UserCache) m_UserCache.TryGetValue(userID, out ud); + if (ud == null) // It's not in the cache + { + string[] names = new string[2]; + // This will pull the data from either UserAccounts or GridUser + // and stick it into the cache + TryGetUserNamesFromServices(userID, names); + lock (m_UserCache) + m_UserCache.TryGetValue(userID, out ud); + } + if (ud != null) { string homeURL = ud.HomeURL; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs index 3849a3fb91..31ef79baf4 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs @@ -235,6 +235,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid return m_GridService.GetDefaultRegions(scopeID); } + public List GetDefaultHypergridRegions(UUID scopeID) + { + return m_GridService.GetDefaultHypergridRegions(scopeID); + } + public List GetFallbackRegions(UUID scopeID, int x, int y) { return m_GridService.GetFallbackRegions(scopeID, x, y); diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs index b2646babcc..6a57d1f896 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs @@ -277,6 +277,26 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid return rinfo; } + public List GetDefaultHypergridRegions(UUID scopeID) + { + List rinfo = m_LocalGridService.GetDefaultHypergridRegions(scopeID); + //m_log.DebugFormat("[REMOTE GRID CONNECTOR]: Local GetDefaultHypergridRegions {0} found {1} regions", name, rinfo.Count); + List grinfo = m_RemoteGridService.GetDefaultHypergridRegions(scopeID); + + if (grinfo != null) + { + //m_log.DebugFormat("[REMOTE GRID CONNECTOR]: Remote GetDefaultHypergridRegions {0} found {1} regions", name, grinfo.Count); + foreach (GridRegion r in grinfo) + { + m_RegionInfoCache.Cache(r); + if (rinfo.Find(delegate(GridRegion gr) { return gr.RegionID == r.RegionID; }) == null) + rinfo.Add(r); + } + } + + return rinfo; + } + public List GetFallbackRegions(UUID scopeID, int x, int y) { List rinfo = m_LocalGridService.GetFallbackRegions(scopeID, x, y); diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index f864392460..18e7eb8767 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -3854,10 +3854,13 @@ namespace OpenSim.Region.Framework.Scenes // We need to ensure that we are not already removing the scene presence before we ask it not to be // closed. - if (sp != null && sp.IsChildAgent && sp.LifecycleState == ScenePresenceState.Running) + if (sp != null && sp.IsChildAgent + && (sp.LifecycleState == ScenePresenceState.Running + || sp.LifecycleState == ScenePresenceState.PreRemove)) { m_log.DebugFormat( - "[SCENE]: Reusing existing child scene presence for {0} in {1}", sp.Name, Name); + "[SCENE]: Reusing existing child scene presence for {0}, state {1} in {2}", + sp.Name, sp.LifecycleState, Name); // In the case where, for example, an A B C D region layout, an avatar may // teleport from A -> D, but then -> C before A has asked B to close its old child agent. When C @@ -3879,6 +3882,8 @@ namespace OpenSim.Region.Framework.Scenes // } // else if (EntityTransferModule.IsInTransit(sp.UUID)) + sp.LifecycleState = ScenePresenceState.Running; + if (EntityTransferModule.IsInTransit(sp.UUID)) { sp.DoNotCloseAfterTeleport = true; @@ -4018,6 +4023,19 @@ namespace OpenSim.Region.Framework.Scenes // uses down the line. sp.TeleportFlags = (TPFlags)teleportFlags; + // We must carry out a further authorization check if there's an + // attempt to make a child agent into a root agent, since SeeIntoRegion may have allowed a child + // agent to login to a region where a full avatar would not be allowed. + // + // We determine whether this is a CreateAgent for a future non-child agent by inspecting + // TeleportFlags, which will be default for a child connection. This relies on input from the source + // region. + if (sp.TeleportFlags != TPFlags.Default) + { + if (!AuthorizeUser(acd, false, out reason)) + return false; + } + if (sp.IsChildAgent) { m_log.DebugFormat( @@ -4526,10 +4544,10 @@ namespace OpenSim.Region.Framework.Scenes ScenePresence childAgentUpdate = GetScenePresence(cAgentData.AgentID); if (childAgentUpdate != null) { - if (childAgentUpdate.ControllingClient.SessionId != cAgentData.SessionID) - // Only warn for now - m_log.WarnFormat("[SCENE]: Attempt at updating position of agent {0} with invalid session id {1}. Neighbor running older version?", - childAgentUpdate.UUID, cAgentData.SessionID); +// if (childAgentUpdate.ControllingClient.SessionId != cAgentData.SessionID) +// // Only warn for now +// m_log.WarnFormat("[SCENE]: Attempt at updating position of agent {0} with invalid session id {1}. Neighbor running older version?", +// childAgentUpdate.UUID, cAgentData.SessionID); // I can't imagine *yet* why we would get an update if the agent is a root agent.. // however to avoid a race condition crossing borders.. @@ -4630,6 +4648,50 @@ namespace OpenSim.Region.Framework.Scenes return IncomingCloseAgent(agentID, true); } + /// + /// Tell a single agent to prepare to close. + /// + /// + /// This should only be called if we may close the agent but there will be some delay in so doing. Meant for + /// internal use - other callers should almost certainly called IncomingCloseAgent(). + /// + /// + /// true if pre-close state notification was successful. false if the agent + /// was not in a state where it could transition to pre-close. + public bool IncomingPreCloseAgent(ScenePresence sp) + { + lock (m_removeClientLock) + { + // We need to avoid a race condition where in, for example, an A B C D region layout, an avatar may + // teleport from A -> D, but then -> C before A has asked B to close its old child agent. We do not + // want to obey this close since C may have renewed the child agent lease on B. + if (sp.DoNotCloseAfterTeleport) + { + m_log.DebugFormat( + "[SCENE]: Not pre-closing {0} agent {1} in {2} since another simulator has re-established the child connection", + sp.IsChildAgent ? "child" : "root", sp.Name, Name); + + // Need to reset the flag so that a subsequent close after another teleport can succeed. + sp.DoNotCloseAfterTeleport = false; + + return false; + } + + if (sp.LifecycleState != ScenePresenceState.Running) + { + m_log.DebugFormat( + "[SCENE]: Called IncomingPreCloseAgent() for {0} in {1} but presence is already in state {2}", + sp.Name, Name, sp.LifecycleState); + + return false; + } + + sp.LifecycleState = ScenePresenceState.PreRemove; + + return true; + } + } + /// /// Tell a single agent to disconnect from the region. /// @@ -4649,16 +4711,16 @@ namespace OpenSim.Region.Framework.Scenes if (sp == null) { m_log.DebugFormat( - "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in {1}", + "[SCENE]: Called IncomingCloseAgent() with agent ID {0} but no such presence is in {1}", agentID, Name); return false; } - if (sp.LifecycleState != ScenePresenceState.Running) + if (sp.LifecycleState != ScenePresenceState.Running && sp.LifecycleState != ScenePresenceState.PreRemove) { m_log.DebugFormat( - "[SCENE]: Called RemoveClient() for {0} in {1} but presence is already in state {2}", + "[SCENE]: Called IncomingCloseAgent() for {0} in {1} but presence is already in state {2}", sp.Name, Name, sp.LifecycleState); return false; @@ -6025,7 +6087,7 @@ Environment.Exit(1); { if (!AuthorizeUser(aCircuit, false, out reason)) { - // m_log.DebugFormat("[SCENE]: Denying access for {0}", agentID); + //m_log.DebugFormat("[SCENE]: Denying access for {0}", agentID); return false; } } diff --git a/OpenSim/Region/Framework/Scenes/ScenePresenceStateMachine.cs b/OpenSim/Region/Framework/Scenes/ScenePresenceStateMachine.cs index dc3a212b65..cae7fe575f 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresenceStateMachine.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresenceStateMachine.cs @@ -37,7 +37,8 @@ namespace OpenSim.Region.Framework.Scenes /// This is a state machine. /// /// [Entry] => Running - /// Running => Removing + /// Running => PreRemove, Removing + /// PreRemove => Running, Removing /// Removing => Removed /// /// All other methods should only see the scene presence in running state - this is the normal operational state @@ -46,6 +47,7 @@ namespace OpenSim.Region.Framework.Scenes public enum ScenePresenceState { Running, // Normal operation state. The scene presence is available. + PreRemove, // The presence is due to be removed but can still be returning to running. Removing, // The presence is in the process of being removed from the scene via Scene.RemoveClient. Removed, // The presence has been removed from the scene and is effectively dead. // There is no exit from this state. @@ -80,8 +82,17 @@ namespace OpenSim.Region.Framework.Scenes lock (this) { - if (newState == ScenePresenceState.Removing && m_state == ScenePresenceState.Running) + if (newState == m_state) + return; + else if (newState == ScenePresenceState.Running && m_state == ScenePresenceState.PreRemove) transitionOkay = true; + else if (newState == ScenePresenceState.PreRemove && m_state == ScenePresenceState.Running) + transitionOkay = true; + else if (newState == ScenePresenceState.Removing) + { + if (m_state == ScenePresenceState.Running || m_state == ScenePresenceState.PreRemove) + transitionOkay = true; + } else if (newState == ScenePresenceState.Removed && m_state == ScenePresenceState.Removing) transitionOkay = true; } diff --git a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs index 15dea1742d..1eb0a6bc5f 100644 --- a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs +++ b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs @@ -624,9 +624,16 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden int avg_reqs = cinfo.AsyncRequests.Values.Sum() + cinfo.GenericRequests.Values.Sum() + cinfo.SyncRequests.Values.Sum(); avg_reqs = avg_reqs / ((DateTime.Now - cinfo.StartedTime).Minutes + 1); + string childAgentStatus; + + if (llClient.SceneAgent != null) + childAgentStatus = llClient.SceneAgent.IsChildAgent ? "N" : "Y"; + else + childAgentStatus = "Off!"; + m_log.InfoFormat("[INFO]: {0,-12} {1,-20} {2,-6} {3,-11} {4,-11} {5,-16}", scene.RegionInfo.RegionName, llClient.Name, - llClient.SceneAgent.IsChildAgent ? "N" : "Y", + childAgentStatus, (DateTime.Now - cinfo.StartedTime).Minutes, avg_reqs, string.Format( diff --git a/OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs b/OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs index 89cba8a7d8..c63b4093c5 100644 --- a/OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs +++ b/OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs @@ -106,6 +106,9 @@ namespace OpenSim.Server.Handlers.Grid case "get_default_regions": return GetDefaultRegions(request); + case "get_default_hypergrid_regions": + return GetDefaultHypergridRegions(request); + case "get_fallback_regions": return GetFallbackRegions(request); @@ -444,6 +447,36 @@ namespace OpenSim.Server.Handlers.Grid return Util.UTF8NoBomEncoding.GetBytes(xmlString); } + byte[] GetDefaultHypergridRegions(Dictionary request) + { + //m_log.DebugFormat("[GRID HANDLER]: GetDefaultRegions"); + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get region range"); + + List rinfos = m_GridService.GetDefaultHypergridRegions(scopeID); + + Dictionary result = new Dictionary(); + if ((rinfos == null) || ((rinfos != null) && (rinfos.Count == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (GridRegion rinfo in rinfos) + { + Dictionary rinfoDict = rinfo.ToKeyValuePairs(); + result["region" + i] = rinfoDict; + i++; + } + } + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + byte[] GetFallbackRegions(Dictionary request) { //m_log.DebugFormat("[GRID HANDLER]: GetRegionRange"); diff --git a/OpenSim/Services/Connectors/Grid/GridServicesConnector.cs b/OpenSim/Services/Connectors/Grid/GridServicesConnector.cs index f982cc1360..af91cdb12b 100644 --- a/OpenSim/Services/Connectors/Grid/GridServicesConnector.cs +++ b/OpenSim/Services/Connectors/Grid/GridServicesConnector.cs @@ -525,6 +525,57 @@ namespace OpenSim.Services.Connectors return rinfos; } + public List GetDefaultHypergridRegions(UUID scopeID) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + + sendData["METHOD"] = "get_default_hypergrid_regions"; + + List rinfos = new List(); + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData)); + + //m_log.DebugFormat("[GRID CONNECTOR]: reply was {0}", reply); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return rinfos; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + Dictionary.ValueCollection rinfosList = replyData.Values; + foreach (object r in rinfosList) + { + if (r is Dictionary) + { + GridRegion rinfo = new GridRegion((Dictionary)r); + rinfos.Add(rinfo); + } + } + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetDefaultHypergridRegions {0} received null response", + scopeID); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetDefaultHypergridRegions received null reply"); + + return rinfos; + } + public List GetFallbackRegions(UUID scopeID, int x, int y) { Dictionary sendData = new Dictionary(); diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs index 043d131128..36a51824cf 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs @@ -341,6 +341,12 @@ namespace OpenSim.Services.Connectors.SimianGrid return new List(0); } + public List GetDefaultHypergridRegions(UUID scopeID) + { + // TODO: Allow specifying the default grid location + return GetDefaultRegions(scopeID); + } + public List GetFallbackRegions(UUID scopeID, int x, int y) { GridRegion defRegion = GetNearestRegion(new Vector3d(x, y, 0.0), true); diff --git a/OpenSim/Services/GridService/GridService.cs b/OpenSim/Services/GridService/GridService.cs index a1485c864f..e72b7f98e9 100644 --- a/OpenSim/Services/GridService/GridService.cs +++ b/OpenSim/Services/GridService/GridService.cs @@ -265,8 +265,9 @@ namespace OpenSim.Services.GridService m_log.DebugFormat("[GRID SERVICE]: Database exception: {0}", e); } - m_log.DebugFormat("[GRID SERVICE]: Region {0} ({1}) registered successfully at {2}-{3}", - regionInfos.RegionName, regionInfos.RegionID, regionInfos.RegionCoordX, regionInfos.RegionCoordY); + m_log.DebugFormat("[GRID SERVICE]: Region {0} ({1}) registered successfully at {2}-{3} with flags {4}", + regionInfos.RegionName, regionInfos.RegionID, regionInfos.RegionCoordX, regionInfos.RegionCoordY, + (OpenSim.Framework.RegionFlags)flags); return String.Empty; } @@ -478,6 +479,33 @@ namespace OpenSim.Services.GridService return ret; } + public List GetDefaultHypergridRegions(UUID scopeID) + { + List ret = new List(); + + List regions = m_Database.GetDefaultHypergridRegions(scopeID); + + foreach (RegionData r in regions) + { + if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0) + ret.Add(RegionData2RegionInfo(r)); + } + + int hgDefaultRegionsFoundOnline = regions.Count; + + // For now, hypergrid default regions will always be given precedence but we will also return simple default + // regions in case no specific hypergrid regions are specified. + ret.AddRange(GetDefaultRegions(scopeID)); + + int normalDefaultRegionsFoundOnline = ret.Count - hgDefaultRegionsFoundOnline; + + m_log.DebugFormat( + "[GRID SERVICE]: GetDefaultHypergridRegions returning {0} hypergrid default and {1} normal default regions", + hgDefaultRegionsFoundOnline, normalDefaultRegionsFoundOnline); + + return ret; + } + public List GetFallbackRegions(UUID scopeID, int x, int y) { List ret = new List(); diff --git a/OpenSim/Services/GridService/HypergridLinker.cs b/OpenSim/Services/GridService/HypergridLinker.cs index 833572416e..402429597b 100644 --- a/OpenSim/Services/GridService/HypergridLinker.cs +++ b/OpenSim/Services/GridService/HypergridLinker.cs @@ -79,7 +79,7 @@ namespace OpenSim.Services.GridService { if (m_DefaultRegion == null) { - List defs = m_GridService.GetDefaultRegions(m_ScopeID); + List defs = m_GridService.GetDefaultHypergridRegions(m_ScopeID); if (defs != null && defs.Count > 0) m_DefaultRegion = defs[0]; else diff --git a/OpenSim/Services/HypergridService/GatekeeperService.cs b/OpenSim/Services/HypergridService/GatekeeperService.cs index e10c4cb5bc..f6136b5843 100644 --- a/OpenSim/Services/HypergridService/GatekeeperService.cs +++ b/OpenSim/Services/HypergridService/GatekeeperService.cs @@ -171,7 +171,7 @@ namespace OpenSim.Services.HypergridService m_log.DebugFormat("[GATEKEEPER SERVICE]: Request to link to {0}", (regionName == string.Empty)? "default region" : regionName); if (!m_AllowTeleportsToAnyRegion || regionName == string.Empty) { - List defs = m_GridService.GetDefaultRegions(m_ScopeID); + List defs = m_GridService.GetDefaultHypergridRegions(m_ScopeID); if (defs != null && defs.Count > 0) { region = defs[0]; diff --git a/OpenSim/Services/Interfaces/IGridService.cs b/OpenSim/Services/Interfaces/IGridService.cs index cecbe9ee7b..e3c70d3ea5 100644 --- a/OpenSim/Services/Interfaces/IGridService.cs +++ b/OpenSim/Services/Interfaces/IGridService.cs @@ -97,6 +97,7 @@ namespace OpenSim.Services.Interfaces List GetRegionRange(UUID scopeID, int xmin, int xmax, int ymin, int ymax); List GetDefaultRegions(UUID scopeID); + List GetDefaultHypergridRegions(UUID scopeID); List GetFallbackRegions(UUID scopeID, int x, int y); List GetHyperlinks(UUID scopeID); diff --git a/OpenSim/Services/UserAccountService/GridUserService.cs b/OpenSim/Services/UserAccountService/GridUserService.cs index b1cdd451b7..944411f2c8 100644 --- a/OpenSim/Services/UserAccountService/GridUserService.cs +++ b/OpenSim/Services/UserAccountService/GridUserService.cs @@ -47,6 +47,44 @@ namespace OpenSim.Services.UserAccountService public GridUserService(IConfigSource config) : base(config) { m_log.Debug("[GRID USER SERVICE]: Starting user grid service"); + + MainConsole.Instance.Commands.AddCommand( + "Users", false, + "show grid users online", + "show grid users online", + "Show number of grid users registered as online.", + "This number may not be accurate as a region may crash or not be cleanly shutdown and leave grid users shown as online\n." + + "For this reason, users online for more than 5 days are not currently counted", + HandleShowGridUsersOnline); + } + + protected void HandleShowGridUsersOnline(string module, string[] cmdparams) + { +// if (cmdparams.Length != 4) +// { +// MainConsole.Instance.Output("Usage: show grid users online"); +// return; +// } + +// int onlineCount; + int onlineRecentlyCount = 0; + + DateTime now = DateTime.UtcNow; + + foreach (GridUserData gu in m_Database.GetAll("")) + { + if (bool.Parse(gu.Data["Online"])) + { +// onlineCount++; + + int unixLoginTime = int.Parse(gu.Data["Login"]); + + if ((now - Util.ToDateTime(unixLoginTime)).Days < 5) + onlineRecentlyCount++; + } + } + + MainConsole.Instance.OutputFormat("Users online: {0}", onlineRecentlyCount); } public virtual GridUserInfo GetGridUserInfo(string userID) diff --git a/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs index 9a9371d050..9bc85124ac 100644 --- a/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs +++ b/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs @@ -35,6 +35,11 @@ namespace pCampBot { public class AbstractBehaviour : IBehaviour { + /// + /// Abbreviated name of this behaviour. + /// + public string AbbreviatedName { get; protected set; } + public string Name { get; protected set; } public Bot Bot { get; protected set; } @@ -45,5 +50,7 @@ namespace pCampBot { Bot = bot; } + + public virtual void Close() {} } } diff --git a/OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs index 1e01c64034..4d806fcc67 100644 --- a/OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs +++ b/OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs @@ -47,7 +47,11 @@ namespace pCampBot public const int m_regionCrossingTimeout = 1000 * 60; - public CrossBehaviour() { Name = "Cross"; } + public CrossBehaviour() + { + AbbreviatedName = "c"; + Name = "Cross"; + } public override void Action() { diff --git a/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs index 66a336a0c6..59f6244052 100644 --- a/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs +++ b/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs @@ -29,6 +29,7 @@ using OpenMetaverse; using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using pCampBot.Interfaces; namespace pCampBot @@ -41,7 +42,11 @@ namespace pCampBot /// public class GrabbingBehaviour : AbstractBehaviour { - public GrabbingBehaviour() { Name = "Grabbing"; } + public GrabbingBehaviour() + { + AbbreviatedName = "g"; + Name = "Grabbing"; + } public override void Action() { @@ -56,6 +61,8 @@ namespace pCampBot Bot.Client.Self.Grab(prim.LocalID); Bot.Client.Self.GrabUpdate(prim.ID, Vector3.Zero); Bot.Client.Self.DeGrab(prim.LocalID); + + Thread.Sleep(1000); } } } \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs index 9cf8a54594..9a3075c5c4 100644 --- a/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs +++ b/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs @@ -38,6 +38,10 @@ namespace pCampBot /// public class NoneBehaviour : AbstractBehaviour { - public NoneBehaviour() { Name = "None"; } + public NoneBehaviour() + { + AbbreviatedName = "n"; + Name = "None"; + } } } \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs index daa7485bed..6fd2b7c70b 100644 --- a/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs +++ b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs @@ -46,6 +46,7 @@ namespace pCampBot public PhysicsBehaviour() { + AbbreviatedName = "p"; Name = "Physics"; talkarray = readexcuses(); } @@ -77,6 +78,12 @@ namespace pCampBot Bot.Client.Self.Chat(randomf, 0, ChatType.Normal); } + public override void Close() + { + if (Bot.ConnectionState == ConnectionState.Connected) + Bot.Client.Self.Jump(false); + } + private string[] readexcuses() { string allexcuses = ""; diff --git a/OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs index fbb4e961d8..81f250d338 100644 --- a/OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs +++ b/OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading; using log4net; using OpenMetaverse; using pCampBot.Interfaces; @@ -42,7 +43,11 @@ namespace pCampBot { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public TeleportBehaviour() { Name = "Teleport"; } + public TeleportBehaviour() + { + AbbreviatedName = "t"; + Name = "Teleport"; + } public override void Action() { @@ -70,6 +75,8 @@ namespace pCampBot Bot.Name, sourceRegion.Name, Bot.Client.Self.SimPosition, destRegion.Name, destPosition); Bot.Client.Self.Teleport(destRegion.RegionHandle, destPosition); + + Thread.Sleep(Bot.Random.Next(3000, 10000)); } } } \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Bot.cs b/OpenSim/Tools/pCampBot/Bot.cs index d418288434..d0a4ef35e1 100644 --- a/OpenSim/Tools/pCampBot/Bot.cs +++ b/OpenSim/Tools/pCampBot/Bot.cs @@ -72,9 +72,10 @@ namespace pCampBot /// Behaviours implemented by this bot. /// /// - /// Lock this list before manipulating it. + /// Indexed by abbreviated name. There can only be one instance of a particular behaviour. + /// Lock this structure before manipulating it. /// - public List Behaviours { get; private set; } + public Dictionary Behaviours { get; private set; } /// /// Objects that the bot has discovered. @@ -165,8 +166,6 @@ namespace pCampBot { ConnectionState = ConnectionState.Disconnected; - behaviours.ForEach(b => b.Initialize(this)); - Random = new Random(Environment.TickCount);// We do stuff randomly here FirstName = firstName; LastName = lastName; @@ -176,12 +175,53 @@ namespace pCampBot StartLocation = startLocation; Manager = bm; - Behaviours = behaviours; + + Behaviours = new Dictionary(); + foreach (IBehaviour behaviour in behaviours) + AddBehaviour(behaviour); // Only calling for use as a template. CreateLibOmvClient(); } + public bool TryGetBehaviour(string abbreviatedName, out IBehaviour behaviour) + { + lock (Behaviours) + return Behaviours.TryGetValue(abbreviatedName, out behaviour); + } + + public bool AddBehaviour(IBehaviour behaviour) + { + lock (Behaviours) + { + if (!Behaviours.ContainsKey(behaviour.AbbreviatedName)) + { + behaviour.Initialize(this); + Behaviours.Add(behaviour.AbbreviatedName, behaviour); + + return true; + } + } + + return false; + } + + public bool RemoveBehaviour(string abbreviatedName) + { + lock (Behaviours) + { + IBehaviour behaviour; + + if (!Behaviours.TryGetValue(abbreviatedName, out behaviour)) + return false; + + behaviour.Close(); + Behaviours.Remove(abbreviatedName); + + return true; + } + } + private void CreateLibOmvClient() { GridClient newClient = new GridClient(); @@ -237,16 +277,25 @@ namespace pCampBot private void Action() { while (ConnectionState != ConnectionState.Disconnecting) + { lock (Behaviours) - Behaviours.ForEach( - b => - { - Thread.Sleep(Random.Next(3000, 10000)); - - // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType()); - b.Action(); - } - ); + { + foreach (IBehaviour behaviour in Behaviours.Values) + { +// Thread.Sleep(Random.Next(3000, 10000)); + + // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType()); + behaviour.Action(); + } + } + + // XXX: This is a really shitty way of yielding so that behaviours can be added/removed + Thread.Sleep(100); + } + + lock (Behaviours) + foreach (IBehaviour b in Behaviours.Values) + b.Close(); } /// diff --git a/OpenSim/Tools/pCampBot/BotManager.cs b/OpenSim/Tools/pCampBot/BotManager.cs index 5c3835bd14..3c1b11eb0c 100644 --- a/OpenSim/Tools/pCampBot/BotManager.cs +++ b/OpenSim/Tools/pCampBot/BotManager.cs @@ -140,7 +140,7 @@ namespace pCampBot /// /// Behaviour switches for bots. /// - private HashSet m_behaviourSwitches = new HashSet(); + private HashSet m_defaultBehaviourSwitches = new HashSet(); /// /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data @@ -194,6 +194,20 @@ namespace pCampBot + "If no is given, then all currently connected bots are disconnected.", HandleDisconnect); + m_console.Commands.AddCommand( + "bot", false, "add behaviour", "add behaviour []", + "Add a behaviour to a bot", + "If no bot number is specified then behaviour is added to all bots.\n" + + "Can be performed on connected or disconnected bots.", + HandleAddBehaviour); + + m_console.Commands.AddCommand( + "bot", false, "remove behaviour", "remove behaviour []", + "Remove a behaviour from a bot", + "If no bot number is specified then behaviour is added to all bots.\n" + + "Can be performed on connected or disconnected bots.", + HandleRemoveBehaviour); + m_console.Commands.AddCommand( "bot", false, "sit", "sit", "Sit all bots on the ground.", HandleSit); @@ -212,7 +226,7 @@ namespace pCampBot "bot", false, "show bots", "show bots", "Shows the status of all bots", HandleShowBotsStatus); m_console.Commands.AddCommand( - "bot", false, "show bot", "show bot ", + "bot", false, "show bot", "show bot ", "Shows the detailed status and settings of a particular bot.", HandleShowBotStatus); m_bots = new List(); @@ -235,7 +249,7 @@ namespace pCampBot m_startUri = ParseInputStartLocationToUri(startupConfig.GetString("start", "last")); Array.ForEach( - startupConfig.GetString("behaviours", "p").Split(new char[] { ',' }), b => m_behaviourSwitches.Add(b)); + startupConfig.GetString("behaviours", "p").Split(new char[] { ',' }), b => m_defaultBehaviourSwitches.Add(b)); for (int i = 0; i < botcount; i++) { @@ -243,30 +257,52 @@ namespace pCampBot { string lastName = string.Format("{0}_{1}", m_lastNameStem, i + m_fromBotNumber); - // We must give each bot its own list of instantiated behaviours since they store state. - List behaviours = new List(); - - // Hard-coded for now - if (m_behaviourSwitches.Contains("c")) - behaviours.Add(new CrossBehaviour()); - - if (m_behaviourSwitches.Contains("g")) - behaviours.Add(new GrabbingBehaviour()); - - if (m_behaviourSwitches.Contains("n")) - behaviours.Add(new NoneBehaviour()); - - if (m_behaviourSwitches.Contains("p")) - behaviours.Add(new PhysicsBehaviour()); - - if (m_behaviourSwitches.Contains("t")) - behaviours.Add(new TeleportBehaviour()); - - CreateBot(this, behaviours, m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting); + CreateBot( + this, + CreateBehavioursFromAbbreviatedNames(m_defaultBehaviourSwitches), + m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting); } } } + private List CreateBehavioursFromAbbreviatedNames(HashSet abbreviatedNames) + { + // We must give each bot its own list of instantiated behaviours since they store state. + List behaviours = new List(); + + // Hard-coded for now + foreach (string abName in abbreviatedNames) + { + IBehaviour newBehaviour = null; + + if (abName == "c") + newBehaviour = new CrossBehaviour(); + + if (abName == "g") + newBehaviour = new GrabbingBehaviour(); + + if (abName == "n") + newBehaviour = new NoneBehaviour(); + + if (abName == "p") + newBehaviour = new PhysicsBehaviour(); + + if (abName == "t") + newBehaviour = new TeleportBehaviour(); + + if (newBehaviour != null) + { + behaviours.Add(newBehaviour); + } + else + { + MainConsole.Instance.OutputFormat("No behaviour with abbreviated name {0} found", abName); + } + } + + return behaviours; + } + public void ConnectBots(int botcount) { ConnectingBots = true; @@ -453,6 +489,118 @@ namespace pCampBot } } + private void HandleAddBehaviour(string module, string[] cmd) + { + if (cmd.Length < 3 || cmd.Length > 4) + { + MainConsole.Instance.OutputFormat("Usage: add behaviour []"); + return; + } + + string rawBehaviours = cmd[2]; + + List botsToEffect = new List(); + + if (cmd.Length == 3) + { + lock (m_bots) + botsToEffect.AddRange(m_bots); + } + else + { + int botNumber; + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber)) + return; + + Bot bot = GetBotFromNumber(botNumber); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); + return; + } + + botsToEffect.Add(bot); + } + + + HashSet rawAbbreviatedSwitchesToAdd = new HashSet(); + Array.ForEach(rawBehaviours.Split(new char[] { ',' }), b => rawAbbreviatedSwitchesToAdd.Add(b)); + + foreach (Bot bot in botsToEffect) + { + List behavioursAdded = new List(); + + foreach (IBehaviour behaviour in CreateBehavioursFromAbbreviatedNames(rawAbbreviatedSwitchesToAdd)) + { + if (bot.AddBehaviour(behaviour)) + behavioursAdded.Add(behaviour); + } + + MainConsole.Instance.OutputFormat( + "Added behaviours {0} to bot {1}", + string.Join(", ", behavioursAdded.ConvertAll(b => b.Name).ToArray()), bot.Name); + } + } + + private void HandleRemoveBehaviour(string module, string[] cmd) + { + if (cmd.Length < 3 || cmd.Length > 4) + { + MainConsole.Instance.OutputFormat("Usage: remove behaviour []"); + return; + } + + string rawBehaviours = cmd[2]; + + List botsToEffect = new List(); + + if (cmd.Length == 3) + { + lock (m_bots) + botsToEffect.AddRange(m_bots); + } + else + { + int botNumber; + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber)) + return; + + Bot bot = GetBotFromNumber(botNumber); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); + return; + } + + botsToEffect.Add(bot); + } + + HashSet abbreviatedBehavioursToRemove = new HashSet(); + Array.ForEach(rawBehaviours.Split(new char[] { ',' }), b => abbreviatedBehavioursToRemove.Add(b)); + + foreach (Bot bot in botsToEffect) + { + List behavioursRemoved = new List(); + + foreach (string b in abbreviatedBehavioursToRemove) + { + IBehaviour behaviour; + + if (bot.TryGetBehaviour(b, out behaviour)) + { + bot.RemoveBehaviour(b); + behavioursRemoved.Add(behaviour); + } + } + + MainConsole.Instance.OutputFormat( + "Removed behaviours {0} to bot {1}", + string.Join(", ", behavioursRemoved.ConvertAll(b => b.Name).ToArray()), bot.Name); + } + } + private void HandleDisconnect(string module, string[] cmd) { lock (m_bots) @@ -572,10 +720,11 @@ namespace pCampBot private void HandleShowBotsStatus(string module, string[] cmd) { ConsoleDisplayTable cdt = new ConsoleDisplayTable(); - cdt.AddColumn("Name", 30); - cdt.AddColumn("Region", 30); - cdt.AddColumn("Status", 14); - cdt.AddColumn("Connections", 11); + cdt.AddColumn("Name", 24); + cdt.AddColumn("Region", 24); + cdt.AddColumn("Status", 13); + cdt.AddColumn("Conns", 5); + cdt.AddColumn("Behaviours", 20); Dictionary totals = new Dictionary(); foreach (object o in Enum.GetValues(typeof(ConnectionState))) @@ -583,13 +732,17 @@ namespace pCampBot lock (m_bots) { - foreach (Bot pb in m_bots) + foreach (Bot bot in m_bots) { - Simulator currentSim = pb.Client.Network.CurrentSim; - totals[pb.ConnectionState]++; + Simulator currentSim = bot.Client.Network.CurrentSim; + totals[bot.ConnectionState]++; cdt.AddRow( - pb.Name, currentSim != null ? currentSim.Name : "(none)", pb.ConnectionState, pb.SimulatorsCount); + bot.Name, + currentSim != null ? currentSim.Name : "(none)", + bot.ConnectionState, + bot.SimulatorsCount, + string.Join(",", bot.Behaviours.Keys.ToArray())); } } @@ -616,16 +769,11 @@ namespace pCampBot if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, cmd[2], out botNumber)) return; - string name = string.Format("{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber); - - Bot bot; - - lock (m_bots) - bot = m_bots.Find(b => b.Name == name); + Bot bot = GetBotFromNumber(botNumber); if (bot == null) { - MainConsole.Instance.Output("No bot found with name {0}", name); + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); return; } @@ -645,12 +793,39 @@ namespace pCampBot MainConsole.Instance.Output("Settings"); ConsoleDisplayList statusCdl = new ConsoleDisplayList(); + + statusCdl.AddRow( + "Behaviours", + string.Join(", ", bot.Behaviours.Values.ToList().ConvertAll(b => b.Name).ToArray())); + GridClient botClient = bot.Client; statusCdl.AddRow("SEND_AGENT_UPDATES", botClient.Settings.SEND_AGENT_UPDATES); MainConsole.Instance.Output(statusCdl.ToString()); } + /// + /// Get a specific bot from its number. + /// + /// null if no bot was found + /// + private Bot GetBotFromNumber(int botNumber) + { + string name = GenerateBotNameFromNumber(botNumber); + + Bot bot; + + lock (m_bots) + bot = m_bots.Find(b => b.Name == name); + + return bot; + } + + private string GenerateBotNameFromNumber(int botNumber) + { + return string.Format("{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber); + } + internal void Grid_GridRegion(object o, GridRegionEventArgs args) { lock (RegionsKnown) diff --git a/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs b/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs index 9c984be82e..0ed48256c1 100644 --- a/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs +++ b/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs @@ -31,6 +31,11 @@ namespace pCampBot.Interfaces { public interface IBehaviour { + /// + /// Abbreviated name of this behaviour. + /// + string AbbreviatedName { get; } + /// /// Name of this behaviour. /// @@ -45,6 +50,14 @@ namespace pCampBot.Interfaces /// void Initialize(Bot bot); + /// + /// Close down this behaviour. + /// + /// + /// This is triggered if a behaviour is removed via explicit command and when a bot is disconnected + /// + void Close(); + /// /// Action to take when this behaviour is invoked. /// diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index 466aed2a8b..8fe6d0c990 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -151,7 +151,8 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset ; * [GridService] LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" - HypergridLinker = true + + HypergridLinker = true ; Realm = "regions" ; AllowDuplicateNames = "True" @@ -168,16 +169,31 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset ;; Next, we can specify properties of regions, including default and fallback regions ;; The syntax is: Region_ = "" ;; or: Region_ = "" - ;; where can be DefaultRegion, FallbackRegion, NoDirectLogin, Persistent, LockedOut,Reservation,NoMove,Authenticate - ;; For example: + ;; where can be DefaultRegion, DefaultHGRegion, FallbackRegion, NoDirectLogin, Persistent, LockedOut, Reservation, NoMove, Authenticate + ;; + ;; DefaultRegion If a local login cannot be placed in the required region (e.g. home region does not exist, avatar is not allowed entry, etc.) + ;; then this region becomes the destination. Only the first online default region will be used. If no DefaultHGRegion + ;; is specified then this will also be used as the region for hypergrid connections that require it (commonly because they have not specified + ;; an explicit region. + ;; + ;; DefaultHGRegion If an avatar connecting via the hypergrid does not specify a region, then they are placed here. Only the first online + ;; region will be used. + ;; + ;; FallbackRegion If the DefaultRegion is not available for a local login, then any FallbackRegions are tried instead. These are tried in the + ;; order specified. This only applies to local logins at this time, not Hypergrid connections. + ;; + ;; NoDirectLogin A hypergrid user cannot directly connect to this region. This does not apply to local logins. + ;; + ;; Persistent When the simulator is shutdown, the region is signalled as offline but left registered on the grid. + ;; ; Region_Welcome_Area = "DefaultRegion, FallbackRegion" ; (replace spaces with underscore) - ;; Allow Hyperlinks to be created at the console + ;; Allow Hyperlinks to be created at the console HypergridLinker = true - ;; If you have this set under [Hypergrid], no need to set it here, leave it commented - ; GatekeeperURI = "http://127.0.0.1:8002" + ;; If you have this set under [Hypergrid], no need to set it here, leave it commented + ; GatekeeperURI = "http://127.0.0.1:8002" ; * This is the configuration for the freeswitch server in grid mode diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index da1b43ae9d..de6fc28cd8 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -132,8 +132,24 @@ MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnecto ;; Next, we can specify properties of regions, including default and fallback regions ;; The syntax is: Region_ = "" ;; or: Region_ = "" - ;; where can be DefaultRegion, FallbackRegion, NoDirectLogin, Persistent, LockedOut,Reservation,NoMove,Authenticate - ;; For example: + ;; where can be DefaultRegion, DefaultHGRegion, FallbackRegion, NoDirectLogin, Persistent, LockedOut, Reservation, NoMove, Authenticate + ;; + ;; DefaultRegion If a local login cannot be placed in the required region (e.g. home region does not exist, avatar is not allowed entry, etc.) + ;; then this region becomes the destination. Only the first online default region will be used. If no DefaultHGRegion + ;; is specified then this will also be used as the region for hypergrid connections that require it (commonly because they have not specified + ;; an explicit region. + ;; + ;; DefaultHGRegion If an avatar connecting via the hypergrid does not specify a region, then they are placed here. Only the first online + ;; region will be used. + ;; + ;; FallbackRegion If the DefaultRegion is not available for a local login, then any FallbackRegions are tried instead. These are tried in the + ;; order specified. This only applies to local logins at this time, not Hypergrid connections. + ;; + ;; NoDirectLogin A hypergrid user cannot directly connect to this region. This does not apply to local logins. + ;; + ;; Persistent When the simulator is shutdown, the region is signalled as offline but left registered on the grid. + ;; + ;; Example specification: ; Region_Welcome_Area = "DefaultRegion, FallbackRegion" ; (replace spaces with underscore) diff --git a/bin/config-include/StandaloneCommon.ini.example b/bin/config-include/StandaloneCommon.ini.example index 6b991a8027..f7545d46fb 100644 --- a/bin/config-include/StandaloneCommon.ini.example +++ b/bin/config-include/StandaloneCommon.ini.example @@ -81,11 +81,27 @@ ;; Next, we can specify properties of regions, including default and fallback regions ;; The syntax is: Region_ = "" ;; where can be DefaultRegion, FallbackRegion, NoDirectLogin, Persistent, LockedOut + ;; + ;; DefaultRegion If a local login cannot be placed in the required region (e.g. home region does not exist, avatar is not allowed entry, etc.) + ;; then this region becomes the destination. Only the first online default region will be used. If no DefaultHGRegion + ;; is specified then this will also be used as the region for hypergrid connections that require it (commonly because they have not specified + ;; an explicit region. + ;; + ;; DefaultHGRegion If an avatar connecting via the hypergrid does not specify a region, then they are placed here. Only the first online + ;; region will be used. + ;; + ;; FallbackRegion If the DefaultRegion is not available for a local login, then any FallbackRegions are tried instead. These are tried in the + ;; order specified. This only applies to local logins at this time, not Hypergrid connections. + ;; + ;; NoDirectLogin A hypergrid user cannot directly connect to this region. This does not apply to local logins. + ;; + ;; Persistent When the simulator is shutdown, the region is signalled as offline but left registered on the grid. + ;; ;; For example: Region_Welcome_Area = "DefaultRegion, FallbackRegion" ; === HG ONLY === - ;; If you have this set under [Hypergrid], no need to set it here, leave it commented + ;; If you have this set under [Hypergrid], no need to set it here, leave it commented ; GatekeeperURI="http://127.0.0.1:9000" [LibraryModule] @@ -94,7 +110,7 @@ [LoginService] WelcomeMessage = "Welcome, Avatar!" - ;; If you have Gatekeeper set under [Hypergrid], no need to set it here, leave it commented + ;; If you have Gatekeeper set under [Hypergrid], no need to set it here, leave it commented ; GatekeeperURI = "http://127.0.0.1:9000" SRV_HomeURI = "http://127.0.0.1:9000"