OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
AppearanceInfoModule.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.Collections.Generic;
30 using System.Linq;
31 using System.Reflection;
32 using System.Text;
33 using log4net;
34 using Mono.Addins;
35 using Nini.Config;
36 using OpenMetaverse;
37 using OpenSim.Framework;
38 using OpenSim.Framework.Console;
39 using OpenSim.Framework.Monitoring;
40 using OpenSim.Region.ClientStack.LindenUDP;
41 using OpenSim.Region.Framework.Interfaces;
42 using OpenSim.Region.Framework.Scenes;
43 
44 namespace OpenSim.Region.OptionalModules.Avatar.Appearance
45 {
49  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AppearanceInfoModule")]
51  {
52 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
53 
54  private List<Scene> m_scenes = new List<Scene>();
55 
56 // private IAvatarFactoryModule m_avatarFactory;
57 
58  public string Name { get { return "Appearance Information Module"; } }
59 
60  public Type ReplaceableInterface { get { return null; } }
61 
62  public void Initialise(IConfigSource source)
63  {
64 // m_log.DebugFormat("[APPEARANCE INFO MODULE]: INITIALIZED MODULE");
65  }
66 
67  public void PostInitialise()
68  {
69 // m_log.DebugFormat("[APPEARANCE INFO MODULE]: POST INITIALIZED MODULE");
70  }
71 
72  public void Close()
73  {
74 // m_log.DebugFormat("[APPEARANCE INFO MODULE]: CLOSED MODULE");
75  }
76 
77  public void AddRegion(Scene scene)
78  {
79 // m_log.DebugFormat("[APPEARANCE INFO MODULE]: REGION {0} ADDED", scene.RegionInfo.RegionName);
80  }
81 
82  public void RemoveRegion(Scene scene)
83  {
84 // m_log.DebugFormat("[APPEARANCE INFO MODULE]: REGION {0} REMOVED", scene.RegionInfo.RegionName);
85 
86  lock (m_scenes)
87  m_scenes.Remove(scene);
88  }
89 
90  public void RegionLoaded(Scene scene)
91  {
92 // m_log.DebugFormat("[APPEARANCE INFO MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName);
93 
94  lock (m_scenes)
95  m_scenes.Add(scene);
96 
97  scene.AddCommand(
98  "Users", this, "show appearance",
99  "show appearance [<first-name> <last-name>]",
100  "Synonym for 'appearance show'",
101  HandleShowAppearanceCommand);
102 
103  scene.AddCommand(
104  "Users", this, "appearance show",
105  "appearance show [<first-name> <last-name>]",
106  "Show appearance information for avatars.",
107  "This command checks whether the simulator has all the baked textures required to display an avatar to other viewers. "
108  + "\nIf not, then appearance is 'corrupt' and other avatars will continue to see it as a cloud."
109  + "\nOptionally, you can view just a particular avatar's appearance information."
110  + "\nIn this case, the texture UUID for each bake type is also shown and whether the simulator can find the referenced texture.",
111  HandleShowAppearanceCommand);
112 
113  scene.AddCommand(
114  "Users", this, "appearance send",
115  "appearance send [<first-name> <last-name>]",
116  "Send appearance data for each avatar in the simulator to other viewers.",
117  "Optionally, you can specify that only a particular avatar's appearance data is sent.",
118  HandleSendAppearanceCommand);
119 
120  scene.AddCommand(
121  "Users", this, "appearance rebake",
122  "appearance rebake <first-name> <last-name>",
123  "Send a request to the user's viewer for it to rebake and reupload its appearance textures.",
124  "This is currently done for all baked texture references previously received, whether the simulator can find the asset or not."
125  + "\nThis will only work for texture ids that the viewer has already uploaded."
126  + "\nIf the viewer has not yet sent the server any texture ids then nothing will happen"
127  + "\nsince requests can only be made for ids that the client has already sent us",
128  HandleRebakeAppearanceCommand);
129 
130  scene.AddCommand(
131  "Users", this, "appearance find",
132  "appearance find <uuid-or-start-of-uuid>",
133  "Find out which avatar uses the given asset as a baked texture, if any.",
134  "You can specify just the beginning of the uuid, e.g. 2008a8d. A longer UUID must be in dashed format.",
135  HandleFindAppearanceCommand);
136 
137  scene.AddCommand(
138  "Users", this, "wearables show",
139  "wearables show [<first-name> <last-name>]",
140  "Show information about wearables for avatars.",
141  "If no avatar name is given then a general summary for all avatars in the scene is shown.\n"
142  + "If an avatar name is given then specific information about current wearables is shown.",
143  HandleShowWearablesCommand);
144 
145  scene.AddCommand(
146  "Users", this, "wearables check",
147  "wearables check <first-name> <last-name>",
148  "Check that the wearables of a given avatar in the scene are valid.",
149  "This currently checks that the wearable assets themselves and any assets referenced by them exist.",
150  HandleCheckWearablesCommand);
151  }
152 
153  private void HandleSendAppearanceCommand(string module, string[] cmd)
154  {
155  if (cmd.Length != 2 && cmd.Length < 4)
156  {
157  MainConsole.Instance.OutputFormat("Usage: appearance send [<first-name> <last-name>]");
158  return;
159  }
160 
161  bool targetNameSupplied = false;
162  string optionalTargetFirstName = null;
163  string optionalTargetLastName = null;
164 
165  if (cmd.Length >= 4)
166  {
167  targetNameSupplied = true;
168  optionalTargetFirstName = cmd[2];
169  optionalTargetLastName = cmd[3];
170  }
171 
172  lock (m_scenes)
173  {
174  foreach (Scene scene in m_scenes)
175  {
176  if (targetNameSupplied)
177  {
178  ScenePresence sp = scene.GetScenePresence(optionalTargetFirstName, optionalTargetLastName);
179  if (sp != null && !sp.IsChildAgent)
180  {
181  MainConsole.Instance.OutputFormat(
182  "Sending appearance information for {0} to all other avatars in {1}",
183  sp.Name, scene.RegionInfo.RegionName);
184 
185  scene.AvatarFactory.SendAppearance(sp.UUID);
186  }
187  }
188  else
189  {
190  scene.ForEachRootScenePresence(
191  sp =>
192  {
193  MainConsole.Instance.OutputFormat(
194  "Sending appearance information for {0} to all other avatars in {1}",
195  sp.Name, scene.RegionInfo.RegionName);
196 
197  scene.AvatarFactory.SendAppearance(sp.UUID);
198  }
199  );
200  }
201  }
202  }
203  }
204 
205  private void HandleShowAppearanceCommand(string module, string[] cmd)
206  {
207  if (cmd.Length != 2 && cmd.Length < 4)
208  {
209  MainConsole.Instance.OutputFormat("Usage: appearance show [<first-name> <last-name>]");
210  return;
211  }
212 
213  bool targetNameSupplied = false;
214  string optionalTargetFirstName = null;
215  string optionalTargetLastName = null;
216 
217  if (cmd.Length >= 4)
218  {
219  targetNameSupplied = true;
220  optionalTargetFirstName = cmd[2];
221  optionalTargetLastName = cmd[3];
222  }
223 
224  lock (m_scenes)
225  {
226  foreach (Scene scene in m_scenes)
227  {
228  if (targetNameSupplied)
229  {
230  ScenePresence sp = scene.GetScenePresence(optionalTargetFirstName, optionalTargetLastName);
231  if (sp != null && !sp.IsChildAgent)
232  scene.AvatarFactory.WriteBakedTexturesReport(sp, MainConsole.Instance.OutputFormat);
233  }
234  else
235  {
236  scene.ForEachRootScenePresence(
237  sp =>
238  {
239  bool bakedTextureValid = scene.AvatarFactory.ValidateBakedTextureCache(sp);
240  MainConsole.Instance.OutputFormat(
241  "{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "incomplete");
242  }
243  );
244  }
245  }
246  }
247  }
248 
249  private void HandleRebakeAppearanceCommand(string module, string[] cmd)
250  {
251  if (cmd.Length != 4)
252  {
253  MainConsole.Instance.OutputFormat("Usage: appearance rebake <first-name> <last-name>");
254  return;
255  }
256 
257  string firstname = cmd[2];
258  string lastname = cmd[3];
259 
260  lock (m_scenes)
261  {
262  foreach (Scene scene in m_scenes)
263  {
264  ScenePresence sp = scene.GetScenePresence(firstname, lastname);
265  if (sp != null && !sp.IsChildAgent)
266  {
267  int rebakesRequested = scene.AvatarFactory.RequestRebake(sp, false);
268 
269  if (rebakesRequested > 0)
270  MainConsole.Instance.OutputFormat(
271  "Requesting rebake of {0} uploaded textures for {1} in {2}",
272  rebakesRequested, sp.Name, scene.RegionInfo.RegionName);
273  else
274  MainConsole.Instance.OutputFormat(
275  "No texture IDs available for rebake request for {0} in {1}",
276  sp.Name, scene.RegionInfo.RegionName);
277  }
278  }
279  }
280  }
281 
282  private void HandleFindAppearanceCommand(string module, string[] cmd)
283  {
284  if (cmd.Length != 3)
285  {
286  MainConsole.Instance.OutputFormat("Usage: appearance find <uuid-or-start-of-uuid>");
287  return;
288  }
289 
290  string rawUuid = cmd[2];
291 
292  HashSet<ScenePresence> matchedAvatars = new HashSet<ScenePresence>();
293 
294  lock (m_scenes)
295  {
296  foreach (Scene scene in m_scenes)
297  {
298  scene.ForEachRootScenePresence(
299  sp =>
300  {
301  Dictionary<BakeType, Primitive.TextureEntryFace> bakedFaces = scene.AvatarFactory.GetBakedTextureFaces(sp.UUID);
302  foreach (Primitive.TextureEntryFace face in bakedFaces.Values)
303  {
304  if (face != null && face.TextureID.ToString().StartsWith(rawUuid))
305  matchedAvatars.Add(sp);
306  }
307  });
308  }
309  }
310 
311  if (matchedAvatars.Count == 0)
312  {
313  MainConsole.Instance.OutputFormat("{0} did not match any baked avatar textures in use", rawUuid);
314  }
315  else
316  {
317  MainConsole.Instance.OutputFormat(
318  "{0} matched {1}",
319  rawUuid,
320  string.Join(", ", matchedAvatars.ToList().ConvertAll<string>(sp => sp.Name).ToArray()));
321  }
322  }
323 
324  protected void HandleShowWearablesCommand(string module, string[] cmd)
325  {
326  if (cmd.Length != 2 && cmd.Length < 4)
327  {
328  MainConsole.Instance.OutputFormat("Usage: wearables show [<first-name> <last-name>]");
329  return;
330  }
331 
332  bool targetNameSupplied = false;
333  string optionalTargetFirstName = null;
334  string optionalTargetLastName = null;
335 
336  if (cmd.Length >= 4)
337  {
338  targetNameSupplied = true;
339  optionalTargetFirstName = cmd[2];
340  optionalTargetLastName = cmd[3];
341  }
342 
343  StringBuilder sb = new StringBuilder();
344 
345  if (targetNameSupplied)
346  {
347  lock (m_scenes)
348  {
349  foreach (Scene scene in m_scenes)
350  {
351  ScenePresence sp = scene.GetScenePresence(optionalTargetFirstName, optionalTargetLastName);
352  if (sp != null && !sp.IsChildAgent)
353  AppendWearablesDetailReport(sp, sb);
354  }
355  }
356  }
357  else
358  {
360  cdt.AddColumn("Name", ConsoleDisplayUtil.UserNameSize);
361  cdt.AddColumn("Wearables", 2);
362 
363  lock (m_scenes)
364  {
365  foreach (Scene scene in m_scenes)
366  {
367  scene.ForEachRootScenePresence(
368  sp =>
369  {
370  int count = 0;
371 
372  for (int i = (int)WearableType.Shape; i < (int)WearableType.Physics; i++)
373  count += sp.Appearance.Wearables[i].Count;
374 
375  cdt.AddRow(sp.Name, count);
376  }
377  );
378  }
379  }
380 
381  sb.Append(cdt.ToString());
382  }
383 
384  MainConsole.Instance.Output(sb.ToString());
385  }
386 
387  private void HandleCheckWearablesCommand(string module, string[] cmd)
388  {
389  if (cmd.Length != 4)
390  {
391  MainConsole.Instance.OutputFormat("Usage: wearables check <first-name> <last-name>");
392  return;
393  }
394 
395  string firstname = cmd[2];
396  string lastname = cmd[3];
397 
398  StringBuilder sb = new StringBuilder();
399  UuidGatherer uuidGatherer = new UuidGatherer(m_scenes[0].AssetService);
400 
401  lock (m_scenes)
402  {
403  foreach (Scene scene in m_scenes)
404  {
405  ScenePresence sp = scene.GetScenePresence(firstname, lastname);
406  if (sp != null && !sp.IsChildAgent)
407  {
408  sb.AppendFormat("Wearables checks for {0}\n\n", sp.Name);
409 
410  for (int i = (int)WearableType.Shape; i < (int)WearableType.Physics; i++)
411  {
412  AvatarWearable aw = sp.Appearance.Wearables[i];
413 
414  if (aw.Count > 0)
415  {
416  sb.Append(Enum.GetName(typeof(WearableType), i));
417  sb.Append("\n");
418 
419  for (int j = 0; j < aw.Count; j++)
420  {
421  WearableItem wi = aw[j];
422 
424  cdl.Indent = 2;
425  cdl.AddRow("Item UUID", wi.ItemID);
426  cdl.AddRow("Assets", "");
427  sb.Append(cdl.ToString());
428 
429  uuidGatherer.AddForInspection(wi.AssetID);
430  uuidGatherer.GatherAll();
431  string[] assetStrings
432  = Array.ConvertAll<UUID, string>(uuidGatherer.GatheredUuids.Keys.ToArray(), u => u.ToString());
433 
434  bool[] existChecks = scene.AssetService.AssetsExist(assetStrings);
435 
437  cdt.Indent = 4;
438  cdt.AddColumn("Type", 10);
439  cdt.AddColumn("UUID", ConsoleDisplayUtil.UuidSize);
440  cdt.AddColumn("Found", 5);
441 
442  for (int k = 0; k < existChecks.Length; k++)
443  cdt.AddRow(
444  (AssetType)uuidGatherer.GatheredUuids[new UUID(assetStrings[k])],
445  assetStrings[k], existChecks[k] ? "yes" : "no");
446 
447  sb.Append(cdt.ToString());
448  sb.Append("\n");
449  }
450  }
451  }
452  }
453  }
454  }
455 
456  MainConsole.Instance.Output(sb.ToString());
457  }
458 
459  private void AppendWearablesDetailReport(ScenePresence sp, StringBuilder sb)
460  {
461  sb.AppendFormat("\nWearables for {0}\n", sp.Name);
462 
464  cdt.AddColumn("Type", 10);
465  cdt.AddColumn("Item UUID", ConsoleDisplayUtil.UuidSize);
466  cdt.AddColumn("Asset UUID", ConsoleDisplayUtil.UuidSize);
467 
468  for (int i = (int)WearableType.Shape; i < (int)WearableType.Physics; i++)
469  {
470  AvatarWearable aw = sp.Appearance.Wearables[i];
471 
472  for (int j = 0; j < aw.Count; j++)
473  {
474  WearableItem wi = aw[j];
475  cdt.AddRow(Enum.GetName(typeof(WearableType), i), wi.ItemID, wi.AssetID);
476  }
477  }
478 
479  sb.Append(cdt.ToString());
480  }
481  }
482 }
Used to generated a formatted table for the console.
Gather uuids for a given entity.
Definition: UuidGatherer.cs:54
Used to generated a formatted table for the console.
void Initialise(IConfigSource source)
This is called to initialize the region module. For shared modules, this is called exactly once...
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
A module that just holds commands for inspecting avatar appearance.
IDictionary< UUID, sbyte > GatheredUuids
The dictionary of UUIDs gathered so far. If Complete == true then this is all the reachable UUIDs...
Definition: UuidGatherer.cs:67
Interactive OpenSim region server
Definition: OpenSim.cs:55
void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...