diff --git a/OpenSim/Framework/Console/LocalConsole.cs b/OpenSim/Framework/Console/LocalConsole.cs index 5f2f4041b5..eda41b8d5a 100644 --- a/OpenSim/Framework/Console/LocalConsole.cs +++ b/OpenSim/Framework/Console/LocalConsole.cs @@ -473,9 +473,11 @@ namespace OpenSim.Framework.Console y = -1; } + string commandLine = cmdline.ToString(); + if (isCommand) { - string[] cmd = Commands.Resolve(Parser.Parse(cmdline.ToString())); + string[] cmd = Commands.Resolve(Parser.Parse(commandLine)); if (cmd.Length != 0) { @@ -491,8 +493,11 @@ namespace OpenSim.Framework.Console } } - //AddToHistory(cmdline.ToString()); - return cmdline.ToString(); + // If we're not echoing to screen (e.g. a password) then we probably don't want it in history + if (echo && commandLine != "") + AddToHistory(commandLine); + + return commandLine; default: break; } diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index f1d622ef48..6bee4bed3c 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -451,6 +451,14 @@ namespace OpenSim.Framework return (x + y - (min >> 1) - (min >> 2) + (min >> 4)); } + /// + /// Are the co-ordinates of the new region visible from the old region? + /// + /// Old region x-coord + /// New region x-coord + /// Old region y-coord + /// New region y-coord + /// public static bool IsOutsideView(uint oldx, uint newx, uint oldy, uint newy) { // Eventually this will be a function of the draw distance / camera position too. diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 5969be0c8b..58ece345b8 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -30,6 +30,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Reflection; +using System.Text; using System.Timers; using log4net; using Nini.Config; @@ -285,16 +286,15 @@ namespace OpenSim m_console.Commands.AddCommand("region", false, "show users", "show users [full]", - "Show user data", HandleShow); + "Show user data for users currently on the region", + "Without the 'full' option, only users actually on the region are shown." + + " With the 'full' option child agents of users in neighbouring regions are also shown.", + HandleShow); m_console.Commands.AddCommand("region", false, "show connections", "show connections", "Show connection data", HandleShow); - m_console.Commands.AddCommand("region", false, "show users full", - "show users full", - String.Empty, HandleShow); - m_console.Commands.AddCommand("region", false, "show modules", "show modules", "Show module data", HandleShow); @@ -304,8 +304,12 @@ namespace OpenSim "Show region data", HandleShow); m_console.Commands.AddCommand("region", false, "show queues", - "show queues", - "Show queue data", HandleShow); + "show queues [full]", + "Show queue data for each client", + "Without the 'full' option, only users actually on the region are shown." + + " With the 'full' option child agents of users in neighbouring regions are also shown.", + HandleShow); + m_console.Commands.AddCommand("region", false, "show ratings", "show ratings", "Show rating data", HandleShow); @@ -876,7 +880,7 @@ namespace OpenSim { agents = m_sceneManager.GetCurrentSceneAvatars(); } - + MainConsole.Instance.Output(String.Format("\nAgents connected: {0}\n", agents.Count)); MainConsole.Instance.Output( @@ -953,7 +957,7 @@ namespace OpenSim break; case "queues": - Notice(GetQueuesReport()); + Notice(GetQueuesReport(showParams)); break; case "ratings": @@ -983,43 +987,91 @@ namespace OpenSim } /// - /// print UDP Queue data for each client + /// Generate UDP Queue data report for each client /// + /// /// - private string GetQueuesReport() + private string GetQueuesReport(string[] showParams) { - string report = String.Empty; + bool showChildren = false; + + if (showParams.Length > 1 && showParams[1] == "full") + showChildren = true; + + StringBuilder report = new StringBuilder(); + + int columnPadding = 2; + int maxNameLength = 18; + int maxRegionNameLength = 14; + int maxTypeLength = 4; + int totalInfoFieldsLength = maxNameLength + columnPadding + maxRegionNameLength + columnPadding + maxTypeLength + columnPadding; + + report.AppendFormat("{0,-" + maxNameLength + "}{1,-" + columnPadding + "}", "User", ""); + report.AppendFormat("{0,-" + maxRegionNameLength + "}{1,-" + columnPadding + "}", "Region", ""); + report.AppendFormat("{0,-" + maxTypeLength + "}{1,-" + columnPadding + "}", "Type", ""); + + report.AppendFormat( + "{0,9} {1,9} {2,9} {3,8} {4,7} {5,7} {6,7} {7,7} {8,9} {9,7} {10,7}\n", + "Packets", + "Packets", + "Bytes", + "Bytes", + "Bytes", + "Bytes", + "Bytes", + "Bytes", + "Bytes", + "Bytes", + "Bytes"); + + report.AppendFormat("{0,-" + totalInfoFieldsLength + "}", ""); + report.AppendFormat( + "{0,9} {1,9} {2,9} {3,8} {4,7} {5,7} {6,7} {7,7} {8,9} {9,7} {10,7}\n", + "Out", + "In", + "Unacked", + "Resend", + "Land", + "Wind", + "Cloud", + "Task", + "Texture", + "Asset", + "State"); + + m_sceneManager.ForEachScene( + delegate(Scene scene) + { + scene.ForEachClient( + delegate(IClientAPI client) + { + if (client is IStatsCollector) + { + bool isChild = scene.PresenceChildStatus(client.AgentId); + if (isChild && !showChildren) + return; + + string name = client.Name; + string regionName = scene.RegionInfo.RegionName; + + report.AppendFormat( + "{0,-" + maxNameLength + "}{1,-" + columnPadding + "}", + name.Length > maxNameLength ? name.Substring(0, maxNameLength) : name, ""); + report.AppendFormat( + "{0,-" + maxRegionNameLength + "}{1,-" + columnPadding + "}", + regionName.Length > maxRegionNameLength ? regionName.Substring(0, maxRegionNameLength) : regionName, ""); + report.AppendFormat( + "{0,-" + maxTypeLength + "}{1,-" + columnPadding + "}", + isChild ? "Cd" : "Rt", ""); - m_sceneManager.ForEachScene(delegate(Scene scene) - { - scene.ForEachClient(delegate(IClientAPI client) - { - if (client is IStatsCollector) - { - report = report + client.FirstName + - " " + client.LastName; + IStatsCollector stats = (IStatsCollector)client; + + report.AppendLine(stats.Report()); + } + }); + }); - IStatsCollector stats = - (IStatsCollector) client; - - report = report + string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}\n", - "Send", - "In", - "Out", - "Resend", - "Land", - "Wind", - "Cloud", - "Task", - "Texture", - "Asset"); - report = report + stats.Report() + - "\n"; - } - }); - }); - - return report; + return report.ToString(); } /// diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index a4738ff3ec..ed0e60d9eb 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -247,11 +247,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP throw new NotImplementedException(); } + /// + /// Return statistics information about client packet queues. + /// + /// + /// FIXME: This should really be done in a more sensible manner rather than sending back a formatted string. + /// + /// public string GetStats() { - // TODO: ??? - return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + return string.Format( + "{0,9} {1,9} {2,9} {3,8} {4,7} {5,7} {6,7} {7,7} {8,9} {9,7} {10,7}", + PacketsSent, + PacketsReceived, + UnackedBytes, + m_throttleCategories[(int)ThrottleOutPacketType.Resend].Content, + m_throttleCategories[(int)ThrottleOutPacketType.Land].Content, + m_throttleCategories[(int)ThrottleOutPacketType.Wind].Content, + m_throttleCategories[(int)ThrottleOutPacketType.Cloud].Content, + m_throttleCategories[(int)ThrottleOutPacketType.Task].Content, + m_throttleCategories[(int)ThrottleOutPacketType.Texture].Content, + m_throttleCategories[(int)ThrottleOutPacketType.Asset].Content, + m_throttleCategories[(int)ThrottleOutPacketType.State].Content); } public void SendPacketStats() diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs index 2dd444d523..0df4585eec 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs @@ -55,6 +55,8 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory private Dictionary m_savequeue = new Dictionary(); private Dictionary m_sendqueue = new Dictionary(); + private object m_setAppearanceLock = new object(); + #region RegionModule Members public void Initialise(Scene scene, IConfigSource config) @@ -69,6 +71,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory { m_savetime = Convert.ToInt32(sconfig.GetString("DelayBeforeAppearanceSave",Convert.ToString(m_savetime))); m_sendtime = Convert.ToInt32(sconfig.GetString("DelayBeforeAppearanceSend",Convert.ToString(m_sendtime))); + // m_log.InfoFormat("[AVFACTORY] configured for {0} save and {1} send",m_savetime,m_sendtime); } } @@ -117,26 +120,28 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory ScenePresence sp = m_scene.GetScenePresence(client.AgentId); if (sp == null) { - m_log.WarnFormat("[AVATAR FACTORY MODULE]: SetAppearance unable to find presence for {0}", client.AgentId); + m_log.WarnFormat("[AVFACTORY]: SetAppearance unable to find presence for {0}", client.AgentId); return false; } - bool cached = true; + bool defonly = true; // are we only using default textures // Process the texture entry for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++) { int idx = AvatarAppearance.BAKE_INDICES[i]; Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx]; - if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) - if (! CheckBakedTextureAsset(client,face.TextureID,idx)) - { - sp.Appearance.Texture.FaceTextures[idx] = null; - cached = false; - } + if (face == null || face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) + continue; + + defonly = false; // found a non-default texture reference + + if (! CheckBakedTextureAsset(client,face.TextureID,idx)) + return false; } - return cached; + // If we only found default textures, then the appearance is not cached + return (defonly ? false : true); } /// @@ -146,44 +151,59 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory /// public void SetAppearance(IClientAPI client, Primitive.TextureEntry textureEntry, byte[] visualParams) { -// m_log.WarnFormat("[AVATAR FACTORY MODULE]: SetAppearance for {0}",client.AgentId); - ScenePresence sp = m_scene.GetScenePresence(client.AgentId); if (sp == null) { - m_log.WarnFormat("[AVATAR FACTORY MODULE]: SetAppearance unable to find presence for {0}",client.AgentId); + m_log.WarnFormat("[AVFACTORY]: SetAppearance unable to find presence for {0}",client.AgentId); return; } + // m_log.WarnFormat("[AVFACTORY]: Start SetAppearance for {0}",client.AgentId); + bool changed = false; - // Process the texture entry - if (textureEntry != null) + // Process the texture entry transactionally, this doesn't guarantee that Appearance is + // going to be handled correctly but it does serialize the updates to the appearance + lock (m_setAppearanceLock) { - changed = sp.Appearance.SetTextureEntries(textureEntry); - - for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++) + if (textureEntry != null) { - int idx = AvatarAppearance.BAKE_INDICES[i]; - Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx]; - if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) - Util.FireAndForget(delegate(object o) { - if (! CheckBakedTextureAsset(client,face.TextureID,idx)) - client.SendRebakeAvatarTextures(face.TextureID); - }); - } - } + changed = sp.Appearance.SetTextureEntries(textureEntry); - // Process the visual params, this may change height as well - if (visualParams != null) - { - if (sp.Appearance.SetVisualParams(visualParams)) - { - changed = true; - if (sp.Appearance.AvatarHeight > 0) - sp.SetHeight(sp.Appearance.AvatarHeight); + // m_log.WarnFormat("[AVFACTORY]: Prepare to check textures for {0}",client.AgentId); + + for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++) + { + int idx = AvatarAppearance.BAKE_INDICES[i]; + Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx]; + if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) + Util.FireAndForget(delegate(object o) { + if (! CheckBakedTextureAsset(client,face.TextureID,idx)) + client.SendRebakeAvatarTextures(face.TextureID); + }); + } + + // m_log.WarnFormat("[AVFACTORY]: Complete texture check for {0}",client.AgentId); } + + // Process the visual params, this may change height as well + if (visualParams != null) + { + if (sp.Appearance.SetVisualParams(visualParams)) + { + changed = true; + if (sp.Appearance.AvatarHeight > 0) + sp.SetHeight(sp.Appearance.AvatarHeight); + } + } + + // Send the appearance back to the avatar, not clear that this is needed + sp.ControllingClient.SendAvatarDataImmediate(sp); + // AvatarAppearance avp = sp.Appearance; + // sp.ControllingClient.SendAppearance(avp.Owner,avp.VisualParams,avp.Texture.GetBytes()); + } + // If something changed in the appearance then queue an appearance save if (changed) @@ -192,10 +212,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory // And always queue up an appearance update to send out QueueAppearanceSend(client.AgentId); - // Send the appearance back to the avatar - // AvatarAppearance avp = sp.Appearance; - // sp.ControllingClient.SendAvatarDataImmediate(sp); - // sp.ControllingClient.SendAppearance(avp.Owner,avp.VisualParams,avp.Texture.GetBytes()); + // m_log.WarnFormat("[AVFACTORY]: Complete SetAppearance for {0}:\n{1}",client.AgentId,sp.Appearance.ToString()); } /// @@ -209,7 +226,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory { if (m_scene.AssetService.Get(textureID.ToString()) == null) { - m_log.WarnFormat("[AVATAR FACTORY MODULE]: Missing baked texture {0} ({1}) for avatar {2}", + m_log.WarnFormat("[AVFACTORY]: Missing baked texture {0} ({1}) for avatar {2}", textureID, idx, client.Name); return false; } @@ -220,10 +237,10 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory public void QueueAppearanceSend(UUID agentid) { -// m_log.WarnFormat("[AVATAR FACTORY MODULE]: Queue appearance send for {0}", agentid); + // m_log.WarnFormat("[AVFACTORY]: Queue appearance send for {0}", agentid); - // 100 nanoseconds (ticks) we should wait - long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_sendtime * 10000000); + // 10000 ticks per millisecond, 1000 milliseconds per second + long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_sendtime * 1000 * 10000); lock (m_sendqueue) { m_sendqueue[agentid] = timestamp; @@ -233,10 +250,10 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory public void QueueAppearanceSave(UUID agentid) { -// m_log.WarnFormat("[AVATAR FACTORY MODULE]: Queue appearance save for {0}", agentid); + // m_log.WarnFormat("[AVFACTORY]: Queue appearance save for {0}", agentid); - // 100 nanoseconds (ticks) we should wait - long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 10000000); + // 10000 ticks per millisecond, 1000 milliseconds per second + long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000); lock (m_savequeue) { m_savequeue[agentid] = timestamp; @@ -249,15 +266,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory ScenePresence sp = m_scene.GetScenePresence(agentid); if (sp == null) { - m_log.WarnFormat("[AVATAR FACTORY MODULE]: Agent {0} no longer in the scene", agentid); + m_log.WarnFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentid); return; } -// m_log.WarnFormat("[AVATAR FACTORY MODULE]: Handle appearance send for {0}", agentid); + // m_log.WarnFormat("[AVFACTORY]: Handle appearance send for {0}", agentid); // Send the appearance to everyone in the scene sp.SendAppearanceToAllOtherAgents(); - sp.ControllingClient.SendAvatarDataImmediate(sp); + // sp.ControllingClient.SendAvatarDataImmediate(sp); // Send the appearance back to the avatar // AvatarAppearance avp = sp.Appearance; @@ -279,10 +296,12 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory ScenePresence sp = m_scene.GetScenePresence(agentid); if (sp == null) { - m_log.WarnFormat("[AVATAR FACTORY MODULE]: Agent {0} no longer in the scene", agentid); + m_log.WarnFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentid); return; } + // m_log.WarnFormat("[AVFACTORY] avatar {0} save appearance",agentid); + m_scene.AvatarService.SetAppearance(agentid, sp.Appearance); } @@ -330,11 +349,11 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory ScenePresence sp = m_scene.GetScenePresence(client.AgentId); if (sp == null) { - m_log.WarnFormat("[AVATAR FACTORY MODULE]: SendWearables unable to find presence for {0}", client.AgentId); + m_log.WarnFormat("[AVFACTORY]: SendWearables unable to find presence for {0}", client.AgentId); return; } -// m_log.WarnFormat("[AVATAR FACTORY MODULE]: Received request for wearables of {0}", client.AgentId); + // m_log.WarnFormat("[AVFACTORY]: Received request for wearables of {0}", client.AgentId); client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++); } @@ -349,11 +368,11 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory ScenePresence sp = m_scene.GetScenePresence(client.AgentId); if (sp == null) { - m_log.WarnFormat("[AVATAR FACTORY MODULE]: AvatarIsWearing unable to find presence for {0}", client.AgentId); + m_log.WarnFormat("[AVFACTORY]: AvatarIsWearing unable to find presence for {0}", client.AgentId); return; } -// m_log.WarnFormat("[AVATAR FACTORY MODULE]: AvatarIsWearing called for {0}", client.AgentId); + // m_log.WarnFormat("[AVFACTORY]: AvatarIsWearing called for {0}", client.AgentId); AvatarAppearance avatAppearance = new AvatarAppearance(sp.Appearance, false); @@ -368,6 +387,8 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory // This could take awhile since it needs to pull inventory SetAppearanceAssets(sp.UUID, ref avatAppearance); + // could get fancier with the locks here, but in the spirit of "last write wins" + // this should work correctly sp.Appearance = avatAppearance; m_scene.AvatarService.SetAppearance(client.AgentId, sp.Appearance); } @@ -398,7 +419,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory else { m_log.ErrorFormat( - "[AVATAR FACTORY MODULE]: Can't find inventory item {0} for {1}, setting to default", + "[AVFACTORY]: Can't find inventory item {0} for {1}, setting to default", appearance.Wearables[i][j].ItemID, (WearableType)i); appearance.Wearables[i].RemoveItem(appearance.Wearables[i][j].ItemID); @@ -408,7 +429,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory } else { - m_log.WarnFormat("[AVATAR FACTORY MODULE]: user {0} has no inventory, appearance isn't going to work", userID); + m_log.WarnFormat("[AVFACTORY]: user {0} has no inventory, appearance isn't going to work", userID); } } } diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 3c7638d539..81d6f70f36 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -1255,11 +1255,16 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer } m_log.Debug("[ENTITY TRANSFER MODULE]: Completed inform client about neighbour " + endPoint.ToString()); - } - } + /// + /// Return the list of regions that are considered to be neighbours to the given scene. + /// + /// + /// + /// + /// protected List RequestNeighbours(Scene pScene, uint pRegionLocX, uint pRegionLocY) { RegionInfo m_regionInfo = pScene.RegionInfo;