OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
AvatarFactoryModule.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.Reflection;
31 using System.Threading;
32 using System.Text;
33 using System.Timers;
34 using log4net;
35 using Nini.Config;
36 using OpenMetaverse;
37 using OpenSim.Framework;
38 using OpenSim.Region.Framework.Interfaces;
39 using OpenSim.Region.Framework.Scenes;
40 using OpenSim.Services.Interfaces;
41 
42 using Mono.Addins;
44 
45 namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
46 {
47  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AvatarFactoryModule")]
49  {
50  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51 
52  public const string BAKED_TEXTURES_REPORT_FORMAT = "{0,-9} {1}";
53 
54  private Scene m_scene = null;
55 
56  private int m_savetime = 5; // seconds to wait before saving changed appearance
57  private int m_sendtime = 2; // seconds to wait before sending changed appearance
58  private bool m_reusetextures = false;
59 
60  private int m_checkTime = 500; // milliseconds to wait between checks for appearance updates
61  private System.Timers.Timer m_updateTimer = new System.Timers.Timer();
62  private Dictionary<UUID,long> m_savequeue = new Dictionary<UUID,long>();
63  private Dictionary<UUID,long> m_sendqueue = new Dictionary<UUID,long>();
64 
65  private object m_setAppearanceLock = new object();
66 
67  #region Region Module interface
68 
69  public void Initialise(IConfigSource config)
70  {
71 
72  IConfig appearanceConfig = config.Configs["Appearance"];
73  if (appearanceConfig != null)
74  {
75  m_savetime = Convert.ToInt32(appearanceConfig.GetString("DelayBeforeAppearanceSave",Convert.ToString(m_savetime)));
76  m_sendtime = Convert.ToInt32(appearanceConfig.GetString("DelayBeforeAppearanceSend",Convert.ToString(m_sendtime)));
77  m_reusetextures = appearanceConfig.GetBoolean("ReuseTextures",m_reusetextures);
78 
79  // m_log.InfoFormat("[AVFACTORY] configured for {0} save and {1} send",m_savetime,m_sendtime);
80  }
81 
82  }
83 
84  public void AddRegion(Scene scene)
85  {
86  if (m_scene == null)
87  m_scene = scene;
88 
89  scene.RegisterModuleInterface<IAvatarFactoryModule>(this);
90  scene.EventManager.OnNewClient += SubscribeToClientEvents;
91  }
92 
93  public void RemoveRegion(Scene scene)
94  {
95  if (scene == m_scene)
96  {
97  scene.UnregisterModuleInterface<IAvatarFactoryModule>(this);
98  scene.EventManager.OnNewClient -= SubscribeToClientEvents;
99  }
100 
101  m_scene = null;
102  }
103 
104  public void RegionLoaded(Scene scene)
105  {
106  m_updateTimer.Enabled = false;
107  m_updateTimer.AutoReset = true;
108  m_updateTimer.Interval = m_checkTime; // 500 milliseconds wait to start async ops
109  m_updateTimer.Elapsed += new ElapsedEventHandler(HandleAppearanceUpdateTimer);
110  }
111 
112  public void Close()
113  {
114  }
115 
116  public string Name
117  {
118  get { return "Default Avatar Factory"; }
119  }
120 
121  public bool IsSharedModule
122  {
123  get { return false; }
124  }
125 
126  public Type ReplaceableInterface
127  {
128  get { return null; }
129  }
130 
131 
132  private void SubscribeToClientEvents(IClientAPI client)
133  {
134  client.OnRequestWearables += Client_OnRequestWearables;
135  client.OnSetAppearance += Client_OnSetAppearance;
136  client.OnAvatarNowWearing += Client_OnAvatarNowWearing;
137  client.OnCachedTextureRequest += Client_OnCachedTextureRequest;
138  }
139 
140  #endregion
141 
142  #region IAvatarFactoryModule
143 
148  public void SetAppearance(IScenePresence sp, AvatarAppearance appearance, WearableCacheItem[] cacheItems)
149  {
150  SetAppearance(sp, appearance.Texture, appearance.VisualParams, cacheItems);
151  }
152 
153 
154  public void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 avSize, WearableCacheItem[] cacheItems)
155  {
156  float oldoff = sp.Appearance.AvatarFeetOffset;
157  Vector3 oldbox = sp.Appearance.AvatarBoxSize;
158 
159  SetAppearance(sp, textureEntry, visualParams, cacheItems);
160  sp.Appearance.SetSize(avSize);
161 
162  float off = sp.Appearance.AvatarFeetOffset;
163  Vector3 box = sp.Appearance.AvatarBoxSize;
164  if (oldoff != off || oldbox != box)
165  ((ScenePresence)sp).SetSize(box, off);
166  }
167 
174  public void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, WearableCacheItem[] cacheItems)
175  {
176 // m_log.DebugFormat(
177 // "[AVFACTORY]: start SetAppearance for {0}, te {1}, visualParams {2}",
178 // sp.Name, textureEntry, visualParams);
179 
180  // TODO: This is probably not necessary any longer, just assume the
181  // textureEntry set implies that the appearance transaction is complete
182  bool changed = false;
183 
184  // Process the texture entry transactionally, this doesn't guarantee that Appearance is
185  // going to be handled correctly but it does serialize the updates to the appearance
186  lock (m_setAppearanceLock)
187  {
188  // Process the visual params, this may change height as well
189  if (visualParams != null)
190  {
191  changed = sp.Appearance.SetVisualParams(visualParams);
192  }
193 
194  // Process the baked texture array
195  if (textureEntry != null)
196  {
197  m_log.DebugFormat("[AVFACTORY]: Received texture update for {0} {1}", sp.Name, sp.UUID);
198 
199 // WriteBakedTexturesReport(sp, m_log.DebugFormat);
200 
201  changed = sp.Appearance.SetTextureEntries(textureEntry) || changed;
202 
203 // WriteBakedTexturesReport(sp, m_log.DebugFormat);
204 
205  UpdateBakedTextureCache(sp, cacheItems);
206 
207  // This appears to be set only in the final stage of the appearance
208  // update transaction. In theory, we should be able to do an immediate
209  // appearance send and save here.
210  }
211 
212  // NPC should send to clients immediately and skip saving appearance
213  if (((ScenePresence)sp).PresenceType == PresenceType.Npc)
214  {
215  SendAppearance((ScenePresence)sp);
216  return;
217  }
218 
219  // save only if there were changes, send no matter what (doesn't hurt to send twice)
220  if (changed)
221  QueueAppearanceSave(sp.ControllingClient.AgentId);
222 
223  QueueAppearanceSend(sp.ControllingClient.AgentId);
224  }
225 
226  // m_log.WarnFormat("[AVFACTORY]: complete SetAppearance for {0}:\n{1}",client.AgentId,sp.Appearance.ToString());
227  }
228 
229  private void SendAppearance(ScenePresence sp)
230  {
231  // Send the appearance to everyone in the scene
232  sp.SendAppearanceToAllOtherAgents();
233 
234  // Send animations back to the avatar as well
235  sp.Animator.SendAnimPack();
236  }
237 
238  public bool SendAppearance(UUID agentId)
239  {
240 // m_log.DebugFormat("[AVFACTORY]: Sending appearance for {0}", agentId);
241 
242  ScenePresence sp = m_scene.GetScenePresence(agentId);
243  if (sp == null)
244  {
245  // This is expected if the user has gone away.
246 // m_log.DebugFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentId);
247  return false;
248  }
249 
250  SendAppearance(sp);
251  return true;
252  }
253 
254  public Dictionary<BakeType, Primitive.TextureEntryFace> GetBakedTextureFaces(UUID agentId)
255  {
256  ScenePresence sp = m_scene.GetScenePresence(agentId);
257 
258  if (sp == null)
259  return new Dictionary<BakeType, Primitive.TextureEntryFace>();
260 
261  return GetBakedTextureFaces(sp);
262  }
263 
264  public WearableCacheItem[] GetCachedItems(UUID agentId)
265  {
266  ScenePresence sp = m_scene.GetScenePresence(agentId);
267  WearableCacheItem[] items = sp.Appearance.WearableCacheItems;
268  //foreach (WearableCacheItem item in items)
269  //{
270 
271  //}
272  return items;
273  }
274 
275  public bool SaveBakedTextures(UUID agentId)
276  {
277  ScenePresence sp = m_scene.GetScenePresence(agentId);
278 
279  if (sp == null)
280  return false;
281 
282  m_log.DebugFormat(
283  "[AV FACTORY]: Permanently saving baked textures for {0} in {1}",
284  sp.Name, m_scene.RegionInfo.RegionName);
285 
286  Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = GetBakedTextureFaces(sp);
287 
288  if (bakedTextures.Count == 0)
289  return false;
290 
291  foreach (BakeType bakeType in bakedTextures.Keys)
292  {
293  Primitive.TextureEntryFace bakedTextureFace = bakedTextures[bakeType];
294 
295  if (bakedTextureFace == null)
296  {
297  // This can happen legitimately, since some baked textures might not exist
298  //m_log.WarnFormat(
299  // "[AV FACTORY]: No texture ID set for {0} for {1} in {2} not found when trying to save permanently",
300  // bakeType, sp.Name, m_scene.RegionInfo.RegionName);
301  continue;
302  }
303 
304  AssetBase asset = m_scene.AssetService.Get(bakedTextureFace.TextureID.ToString());
305 
306  if (asset != null)
307  {
308  // Replace an HG ID with the simple asset ID so that we can persist textures for foreign HG avatars
309  asset.ID = asset.FullID.ToString();
310 
311  asset.Temporary = false;
312  asset.Local = false;
313  m_scene.AssetService.Store(asset);
314  }
315  else
316  {
317  m_log.WarnFormat(
318  "[AV FACTORY]: Baked texture id {0} not found for bake {1} for avatar {2} in {3} when trying to save permanently",
319  bakedTextureFace.TextureID, bakeType, sp.Name, m_scene.RegionInfo.RegionName);
320  }
321  }
322  return true;
323  }
324 
332  public void QueueAppearanceSend(UUID agentid)
333  {
334 // m_log.DebugFormat("[AVFACTORY]: Queue appearance send for {0}", agentid);
335 
336  // 10000 ticks per millisecond, 1000 milliseconds per second
337  long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_sendtime * 1000 * 10000);
338  lock (m_sendqueue)
339  {
340  m_sendqueue[agentid] = timestamp;
341  m_updateTimer.Start();
342  }
343  }
344 
345  public void QueueAppearanceSave(UUID agentid)
346  {
347 // m_log.DebugFormat("[AVFACTORY]: Queueing appearance save for {0}", agentid);
348 
349  // 10000 ticks per millisecond, 1000 milliseconds per second
350  long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000);
351  lock (m_savequeue)
352  {
353  m_savequeue[agentid] = timestamp;
354  m_updateTimer.Start();
355  }
356  }
357 
358  // called on textures update
360  {
361  if(cacheItems == null)
362  return false;
363 
364  // npcs dont have baked cache
365  if (((ScenePresence)sp).isNPC)
366  return true;
367 
368  // uploaded baked textures will be in assets local cache
369  IAssetService cache = m_scene.AssetService;
370  IBakedTextureModule m_BakedTextureModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
371 
372  int validDirtyBakes = 0;
373  int hits = 0;
374 
375  // our main cacheIDs mapper is p.Appearance.WearableCacheItems
376  WearableCacheItem[] wearableCache = sp.Appearance.WearableCacheItems;
377 
378  if (wearableCache == null)
379  {
380  wearableCache = WearableCacheItem.GetDefaultCacheItem();
381  }
382 
383  List<UUID> missing = new List<UUID>();
384 
385  bool haveSkirt = (wearableCache[19].TextureAsset != null);
386  bool haveNewSkirt = false;
387 
388  // Process received baked textures
389  for (int i = 0; i < cacheItems.Length; i++)
390  {
391  int idx = (int)cacheItems[i].TextureIndex;
392  Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
393 
394  // No face
395  if (face == null)
396  {
397  // for some reason viewer is cleaning this
398  if(idx != 19) // skirt is optional
399  {
400  sp.Appearance.Texture.FaceTextures[idx] = sp.Appearance.Texture.CreateFace((uint) idx);
401  sp.Appearance.Texture.FaceTextures[idx].TextureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
402  }
403  wearableCache[idx].CacheId = UUID.Zero;
404  wearableCache[idx].TextureID = UUID.Zero;
405  wearableCache[idx].TextureAsset = null;
406  continue;
407  }
408  else
409  {
410  if (face.TextureID == UUID.Zero || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
411  {
412  wearableCache[idx].CacheId = UUID.Zero;
413  wearableCache[idx].TextureID = UUID.Zero;
414  wearableCache[idx].TextureAsset = null;
415  continue;
416  }
417 
418  if(idx == 19)
419  haveNewSkirt = true;
420 /*
421  if (face.TextureID == wearableCache[idx].TextureID && m_BakedTextureModule != null)
422  {
423  if (wearableCache[idx].CacheId != cacheItems[i].CacheId)
424  {
425  wearableCache[idx].CacheId = cacheItems[i].CacheId;
426  validDirtyBakes++;
427 
428  //assuming this can only happen if asset is in cache
429  }
430  hits++;
431  continue;
432  }
433 */
434  wearableCache[idx].TextureAsset = null;
435  if (cache != null)
436  wearableCache[idx].TextureAsset = cache.GetCached(face.TextureID.ToString());
437 
438  if (wearableCache[idx].TextureAsset != null)
439  {
440  if ( wearableCache[idx].TextureID != face.TextureID ||
441  wearableCache[idx].CacheId != cacheItems[i].CacheId)
442  validDirtyBakes++;
443 
444  wearableCache[idx].TextureID = face.TextureID;
445  wearableCache[idx].CacheId = cacheItems[i].CacheId;
446  hits++;
447  }
448  else
449  {
450  wearableCache[idx].CacheId = UUID.Zero;
451  wearableCache[idx].TextureID = UUID.Zero;
452  wearableCache[idx].TextureAsset = null;
453  missing.Add(face.TextureID);
454  continue;
455  }
456  }
457  }
458 
459  // handle optional skirt case
460  if(!haveNewSkirt && haveSkirt)
461  {
462  wearableCache[19].CacheId = UUID.Zero;
463  wearableCache[19].TextureID = UUID.Zero;
464  wearableCache[19].TextureAsset = null;
465  validDirtyBakes++;
466  }
467 
468  sp.Appearance.WearableCacheItems = wearableCache;
469 
470  if (missing.Count > 0)
471  {
472  foreach (UUID id in missing)
473  sp.ControllingClient.SendRebakeAvatarTextures(id);
474  }
475 
476  if (validDirtyBakes > 0 && hits == cacheItems.Length)
477  {
478  // if we got a full set of baked textures save all in BakedTextureModule
479  if (m_BakedTextureModule != null)
480  {
481  m_log.Debug("[UpdateBakedCache] start async uploading to bakedModule cache");
482 
483  m_BakedTextureModule.Store(sp.UUID, wearableCache);
484  }
485  }
486 
487 
488  // debug
489  m_log.Debug("[UpdateBakedCache] cache hits: " + hits.ToString() + " changed entries: " + validDirtyBakes.ToString() + " rebakes " + missing.Count);
490 /*
491  for (int iter = 0; iter < AvatarAppearance.BAKE_INDICES.Length; iter++)
492  {
493  int j = AvatarAppearance.BAKE_INDICES[iter];
494  m_log.Debug("[UpdateBCache] {" + iter + "/" +
495  sp.Appearance.WearableCacheItems[j].TextureIndex + "}: c-" +
496  sp.Appearance.WearableCacheItems[j].CacheId + ", t-" +
497  sp.Appearance.WearableCacheItems[j].TextureID);
498  }
499 */
500  return (hits == cacheItems.Length);
501  }
502 
503  // called when we get a new root avatar
505  {
506  int hits = 0;
507 
508  if (((ScenePresence)sp).isNPC)
509  return true;
510 
511  lock (m_setAppearanceLock)
512  {
513  IAssetService cache = m_scene.AssetService;
514  IBakedTextureModule bakedModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
515  WearableCacheItem[] bakedModuleCache = null;
516 
517  if (cache == null)
518  return false;
519 
520  WearableCacheItem[] wearableCache = sp.Appearance.WearableCacheItems;
521 
522  // big debug
523  m_log.DebugFormat("[AVFACTORY]: ValidateBakedTextureCache start for {0} {1}", sp.Name, sp.UUID);
524 /*
525  for (int iter = 0; iter < AvatarAppearance.BAKE_INDICES.Length; iter++)
526  {
527  int j = AvatarAppearance.BAKE_INDICES[iter];
528  Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[j];
529  if (wearableCache == null)
530  {
531  if (face != null)
532  m_log.Debug("[ValidateBakedCache] {" + iter + "/" + j + " t- " + face.TextureID);
533  else
534  m_log.Debug("[ValidateBakedCache] {" + iter + "/" + j + " t- No texture");
535  }
536  else
537  {
538  if (face != null)
539  m_log.Debug("[ValidateBakedCache] {" + iter + "/" + j + " ft- " + face.TextureID +
540  "}: cc-" +
541  wearableCache[j].CacheId + ", ct-" +
542  wearableCache[j].TextureID
543  );
544  else
545  m_log.Debug("[ValidateBakedCache] {" + iter + "/" + j + " t - No texture" +
546  "}: cc-" +
547  wearableCache[j].CacheId + ", ct-" +
548  wearableCache[j].TextureID
549  );
550  }
551  }
552 */
553  bool wearableCacheValid = false;
554  if (wearableCache == null)
555  {
556  wearableCache = WearableCacheItem.GetDefaultCacheItem();
557  }
558  else
559  {
560  // we may have received a full cache
561  // check same coerence and store
562  wearableCacheValid = true;
563  for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
564  {
565  int idx = AvatarAppearance.BAKE_INDICES[i];
566  Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
567  if (face != null)
568  {
569  if (face.TextureID == wearableCache[idx].TextureID &&
570  face.TextureID != UUID.Zero)
571  {
572  if (wearableCache[idx].TextureAsset != null)
573  {
574  hits++;
575  wearableCache[idx].TextureAsset.Temporary = true;
576  wearableCache[idx].TextureAsset.Local = true;
577  cache.Store(wearableCache[idx].TextureAsset);
578  continue;
579  }
580  if (cache.GetCached((wearableCache[idx].TextureID).ToString()) != null)
581  {
582  hits++;
583  continue;
584  }
585  }
586  wearableCacheValid = false;
587  }
588  }
589 
590  wearableCacheValid = (wearableCacheValid && (hits >= AvatarAppearance.BAKE_INDICES.Length - 1));
591  if (wearableCacheValid)
592  m_log.Debug("[ValidateBakedCache] have valid local cache");
593  else
594  wearableCache[19].TextureAsset = null; // clear optional skirt
595  }
596 
597  bool checkExternal = false;
598 
599  if (!wearableCacheValid)
600  {
601  hits = 0;
602  // only use external bake module on login condition check
603 // ScenePresence ssp = null;
604 // if (sp is ScenePresence)
605  {
606 // ssp = (ScenePresence)sp;
607 // checkExternal = (((uint)ssp.TeleportFlags & (uint)TeleportFlags.ViaLogin) != 0) &&
608 // bakedModule != null;
609 
610  // or do it anytime we dont have the cache
611  checkExternal = bakedModule != null;
612  }
613  }
614 
615  if (checkExternal)
616  {
617  bool gotbacked = false;
618 
619  m_log.Debug("[ValidateBakedCache] local cache invalid, checking bakedModule");
620  try
621  {
622  bakedModuleCache = bakedModule.Get(sp.UUID);
623  }
624  catch (Exception e)
625  {
626  m_log.ErrorFormat(e.ToString());
627  bakedModuleCache = null;
628  }
629 
630  if (bakedModuleCache != null)
631  {
632  m_log.Debug("[ValidateBakedCache] got bakedModule " + bakedModuleCache.Length + " cached textures");
633 
634  for (int i = 0; i < bakedModuleCache.Length; i++)
635  {
636  int j = (int)bakedModuleCache[i].TextureIndex;
637 
638  if (bakedModuleCache[i].TextureAsset != null)
639  {
640  wearableCache[j].TextureID = bakedModuleCache[i].TextureID;
641  wearableCache[j].CacheId = bakedModuleCache[i].CacheId;
642  wearableCache[j].TextureAsset = bakedModuleCache[i].TextureAsset;
643  bakedModuleCache[i].TextureAsset.Temporary = true;
644  bakedModuleCache[i].TextureAsset.Local = true;
645  cache.Store(bakedModuleCache[i].TextureAsset);
646  }
647  }
648  gotbacked = true;
649  }
650 
651  if (gotbacked)
652  {
653  // force the ones we got
654  for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
655  {
656  int idx = AvatarAppearance.BAKE_INDICES[i];
657  if(wearableCache[idx].TextureAsset == null)
658  {
659  if(idx == 19)
660  {
661  sp.Appearance.Texture.FaceTextures[idx] = null;
662  hits++;
663  }
664  continue;
665  }
666 
667  Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
668 
669  if (face == null)
670  {
671  face = sp.Appearance.Texture.CreateFace((uint)idx);
672  sp.Appearance.Texture.FaceTextures[idx] = face;
673  }
674 
675  face.TextureID = wearableCache[idx].TextureID;
676  hits++;
677  }
678  }
679  }
680 
681  sp.Appearance.WearableCacheItems = wearableCache;
682 
683  }
684 
685  // debug
686  m_log.DebugFormat("[ValidateBakedCache]: Completed texture check for {0} {1} with {2} hits", sp.Name, sp.UUID, hits);
687 /*
688  for (int iter = 0; iter < AvatarAppearance.BAKE_INDICES.Length; iter++)
689  {
690  int j = AvatarAppearance.BAKE_INDICES[iter];
691  m_log.Debug("[ValidateBakedCache] {" + iter + "/" +
692  sp.Appearance.WearableCacheItems[j].TextureIndex + "}: c-" +
693  sp.Appearance.WearableCacheItems[j].CacheId + ", t-" +
694  sp.Appearance.WearableCacheItems[j].TextureID);
695  }
696 */
697  return (hits >= AvatarAppearance.BAKE_INDICES.Length - 1); // skirt is optional
698  }
699 
700  public int RequestRebake(IScenePresence sp, bool missingTexturesOnly)
701  {
702  if (((ScenePresence)sp).isNPC)
703  return 0;
704 
705  int texturesRebaked = 0;
706 // IImprovedAssetCache cache = m_scene.RequestModuleInterface<IImprovedAssetCache>();
707 
708  for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
709  {
710  int idx = AvatarAppearance.BAKE_INDICES[i];
711  Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
712 
713  // if there is no texture entry, skip it
714  if (face == null)
715  continue;
716 
717  if (face.TextureID == UUID.Zero || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
718  continue;
719 
720  if (missingTexturesOnly)
721  {
722  if (m_scene.AssetService.Get(face.TextureID.ToString()) != null)
723  {
724  continue;
725  }
726  else
727  {
728  // On inter-simulator teleports, this occurs if baked textures are not being stored by the
729  // grid asset service (which means that they are not available to the new region and so have
730  // to be re-requested from the client).
731  //
732  // The only available core OpenSimulator behaviour right now
733  // is not to store these textures, temporarily or otherwise.
734  m_log.DebugFormat(
735  "[AVFACTORY]: Missing baked texture {0} ({1}) for {2}, requesting rebake.",
736  face.TextureID, idx, sp.Name);
737  }
738  }
739  else
740  {
741  m_log.DebugFormat(
742  "[AVFACTORY]: Requesting rebake of {0} ({1}) for {2}.",
743  face.TextureID, idx, sp.Name);
744  }
745 
746  texturesRebaked++;
747  sp.ControllingClient.SendRebakeAvatarTextures(face.TextureID);
748  }
749 
750  return texturesRebaked;
751  }
752 
753  #endregion
754 
755  #region AvatarFactoryModule private methods
756 
757  private Dictionary<BakeType, Primitive.TextureEntryFace> GetBakedTextureFaces(ScenePresence sp)
758  {
759  if (sp.IsChildAgent)
760  return new Dictionary<BakeType, Primitive.TextureEntryFace>();
761 
762  Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures
763  = new Dictionary<BakeType, Primitive.TextureEntryFace>();
764 
765  AvatarAppearance appearance = sp.Appearance;
766  Primitive.TextureEntryFace[] faceTextures = appearance.Texture.FaceTextures;
767 
768  foreach (int i in Enum.GetValues(typeof(BakeType)))
769  {
770  BakeType bakeType = (BakeType)i;
771 
772  if (bakeType == BakeType.Unknown)
773  continue;
774 
775 // m_log.DebugFormat(
776 // "[AVFACTORY]: NPC avatar {0} has texture id {1} : {2}",
777 // acd.AgentID, i, acd.Appearance.Texture.FaceTextures[i]);
778 
779  int ftIndex = (int)AppearanceManager.BakeTypeToAgentTextureIndex(bakeType);
780  Primitive.TextureEntryFace texture = faceTextures[ftIndex]; // this will be null if there's no such baked texture
781  bakedTextures[bakeType] = texture;
782  }
783 
784  return bakedTextures;
785  }
786 
787  private void HandleAppearanceUpdateTimer(object sender, EventArgs ea)
788  {
789  long now = DateTime.Now.Ticks;
790 
791  lock (m_sendqueue)
792  {
793  Dictionary<UUID, long> sends = new Dictionary<UUID, long>(m_sendqueue);
794  foreach (KeyValuePair<UUID, long> kvp in sends)
795  {
796  // We have to load the key and value into local parameters to avoid a race condition if we loop
797  // around and load kvp with a different value before FireAndForget has launched its thread.
798  UUID avatarID = kvp.Key;
799  long sendTime = kvp.Value;
800 
801 // m_log.DebugFormat("[AVFACTORY]: Handling queued appearance updates for {0}, update delta to now is {1}", avatarID, sendTime - now);
802 
803  if (sendTime < now)
804  {
805  Util.FireAndForget(o => SendAppearance(avatarID), null, "AvatarFactoryModule.SendAppearance");
806  m_sendqueue.Remove(avatarID);
807  }
808  }
809  }
810 
811  lock (m_savequeue)
812  {
813  Dictionary<UUID, long> saves = new Dictionary<UUID, long>(m_savequeue);
814  foreach (KeyValuePair<UUID, long> kvp in saves)
815  {
816  // We have to load the key and value into local parameters to avoid a race condition if we loop
817  // around and load kvp with a different value before FireAndForget has launched its thread.
818  UUID avatarID = kvp.Key;
819  long sendTime = kvp.Value;
820 
821  if (sendTime < now)
822  {
823  Util.FireAndForget(o => SaveAppearance(avatarID), null, "AvatarFactoryModule.SaveAppearance");
824  m_savequeue.Remove(avatarID);
825  }
826  }
827 
828  // We must lock both queues here so that QueueAppearanceSave() or *Send() don't m_updateTimer.Start() on
829  // another thread inbetween the first count calls and m_updateTimer.Stop() on this thread.
830  lock (m_sendqueue)
831  if (m_savequeue.Count == 0 && m_sendqueue.Count == 0)
832  m_updateTimer.Stop();
833  }
834  }
835 
836  private void SaveAppearance(UUID agentid)
837  {
838  // We must set appearance parameters in the en_US culture in order to avoid issues where values are saved
839  // in a culture where decimal points are commas and then reloaded in a culture which just treats them as
840  // number seperators.
841  Culture.SetCurrentCulture();
842 
843  ScenePresence sp = m_scene.GetScenePresence(agentid);
844  if (sp == null)
845  {
846  // This is expected if the user has gone away.
847 // m_log.DebugFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentid);
848  return;
849  }
850 
851 // m_log.DebugFormat("[AVFACTORY]: Saving appearance for avatar {0}", agentid);
852 
853  // This could take awhile since it needs to pull inventory
854  // We need to do it at the point of save so that there is a sufficient delay for any upload of new body part/shape
855  // assets and item asset id changes to complete.
856  // I don't think we need to worry about doing this within m_setAppearanceLock since the queueing avoids
857  // multiple save requests.
858  SetAppearanceAssets(sp.UUID, sp.Appearance);
859 
860 // List<AvatarAttachment> attachments = sp.Appearance.GetAttachments();
861 // foreach (AvatarAttachment att in attachments)
862 // {
863 // m_log.DebugFormat(
864 // "[AVFACTORY]: For {0} saving attachment {1} at point {2}",
865 // sp.Name, att.ItemID, att.AttachPoint);
866 // }
867 
868  m_scene.AvatarService.SetAppearance(agentid, sp.Appearance);
869 
870  // Trigger this here because it's the final step in the set/queue/save process for appearance setting.
871  // Everything has been updated and stored. Ensures bakes have been persisted (if option is set to persist bakes).
872  m_scene.EventManager.TriggerAvatarAppearanceChanged(sp);
873  }
874 
881  private void SetAppearanceAssets(UUID userID, AvatarAppearance appearance)
882  {
883  IInventoryService invService = m_scene.InventoryService;
884 
885  if (invService.GetRootFolder(userID) != null)
886  {
887  for (int i = 0; i < appearance.Wearables.Length; i++)
888  {
889  for (int j = 0; j < appearance.Wearables[i].Count; j++)
890  {
891  if (appearance.Wearables[i][j].ItemID == UUID.Zero)
892  {
893  m_log.WarnFormat(
894  "[AVFACTORY]: Wearable item {0}:{1} for user {2} unexpectedly UUID.Zero. Ignoring.",
895  i, j, userID);
896 
897  continue;
898  }
899 
900  // Ignore ruth's assets
901  if (i < AvatarWearable.DefaultWearables.Length)
902  {
903  if (appearance.Wearables[i][j].ItemID == AvatarWearable.DefaultWearables[i][0].ItemID)
904  continue;
905  }
906 
907  InventoryItemBase baseItem = new InventoryItemBase(appearance.Wearables[i][j].ItemID, userID);
908  baseItem = invService.GetItem(baseItem);
909 
910  if (baseItem != null)
911  {
912  appearance.Wearables[i].Add(appearance.Wearables[i][j].ItemID, baseItem.AssetID);
913  }
914  else
915  {
916  m_log.WarnFormat(
917  "[AVFACTORY]: Can't find inventory item {0} for {1}, setting to default",
918  appearance.Wearables[i][j].ItemID, (WearableType)i);
919 
920  appearance.Wearables[i].RemoveItem(appearance.Wearables[i][j].ItemID);
921  }
922  }
923  }
924  }
925  else
926  {
927  m_log.WarnFormat("[AVFACTORY]: user {0} has no inventory, appearance isn't going to work", userID);
928  }
929 
930 // IInventoryService invService = m_scene.InventoryService;
931 // bool resetwearable = false;
932 // if (invService.GetRootFolder(userID) != null)
933 // {
934 // for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++)
935 // {
936 // for (int j = 0; j < appearance.Wearables[i].Count; j++)
937 // {
938 // // Check if the default wearables are not set
939 // if (appearance.Wearables[i][j].ItemID == UUID.Zero)
940 // {
941 // switch ((WearableType) i)
942 // {
943 // case WearableType.Eyes:
944 // case WearableType.Hair:
945 // case WearableType.Shape:
946 // case WearableType.Skin:
947 // //case WearableType.Underpants:
948 // TryAndRepairBrokenWearable((WearableType)i, invService, userID, appearance);
949 // resetwearable = true;
950 // m_log.Warn("[AVFACTORY]: UUID.Zero Wearables, passing fake values.");
951 // resetwearable = true;
952 // break;
953 //
954 // }
955 // continue;
956 // }
957 //
958 // // Ignore ruth's assets except for the body parts! missing body parts fail avatar appearance on V1
959 // if (appearance.Wearables[i][j].ItemID == AvatarWearable.DefaultWearables[i][0].ItemID)
960 // {
961 // switch ((WearableType)i)
962 // {
963 // case WearableType.Eyes:
964 // case WearableType.Hair:
965 // case WearableType.Shape:
966 // case WearableType.Skin:
967 // //case WearableType.Underpants:
968 // TryAndRepairBrokenWearable((WearableType)i, invService, userID, appearance);
969 //
970 // m_log.WarnFormat("[AVFACTORY]: {0} Default Wearables, passing existing values.", (WearableType)i);
971 // resetwearable = true;
972 // break;
973 //
974 // }
975 // continue;
976 // }
977 //
978 // InventoryItemBase baseItem = new InventoryItemBase(appearance.Wearables[i][j].ItemID, userID);
979 // baseItem = invService.GetItem(baseItem);
980 //
981 // if (baseItem != null)
982 // {
983 // appearance.Wearables[i].Add(appearance.Wearables[i][j].ItemID, baseItem.AssetID);
984 // int unmodifiedWearableIndexForClosure = i;
985 // m_scene.AssetService.Get(baseItem.AssetID.ToString(), this,
986 // delegate(string x, object y, AssetBase z)
987 // {
988 // if (z == null)
989 // {
990 // TryAndRepairBrokenWearable(
991 // (WearableType)unmodifiedWearableIndexForClosure, invService,
992 // userID, appearance);
993 // }
994 // });
995 // }
996 // else
997 // {
998 // m_log.ErrorFormat(
999 // "[AVFACTORY]: Can't find inventory item {0} for {1}, setting to default",
1000 // appearance.Wearables[i][j].ItemID, (WearableType)i);
1001 //
1002 // TryAndRepairBrokenWearable((WearableType)i, invService, userID, appearance);
1003 // resetwearable = true;
1004 //
1005 // }
1006 // }
1007 // }
1008 //
1009 // // I don't know why we have to test for this again... but the above switches do not capture these scenarios for some reason....
1010 // if (appearance.Wearables[(int) WearableType.Eyes] == null)
1011 // {
1012 // m_log.WarnFormat("[AVFACTORY]: {0} Eyes are Null, passing existing values.", (WearableType.Eyes));
1013 //
1014 // TryAndRepairBrokenWearable(WearableType.Eyes, invService, userID, appearance);
1015 // resetwearable = true;
1016 // }
1017 // else
1018 // {
1019 // if (appearance.Wearables[(int) WearableType.Eyes][0].ItemID == UUID.Zero)
1020 // {
1021 // m_log.WarnFormat("[AVFACTORY]: Eyes are UUID.Zero are broken, {0} {1}",
1022 // appearance.Wearables[(int) WearableType.Eyes][0].ItemID,
1023 // appearance.Wearables[(int) WearableType.Eyes][0].AssetID);
1024 // TryAndRepairBrokenWearable(WearableType.Eyes, invService, userID, appearance);
1025 // resetwearable = true;
1026 //
1027 // }
1028 //
1029 // }
1030 // // I don't know why we have to test for this again... but the above switches do not capture these scenarios for some reason....
1031 // if (appearance.Wearables[(int)WearableType.Shape] == null)
1032 // {
1033 // m_log.WarnFormat("[AVFACTORY]: {0} shape is Null, passing existing values.", (WearableType.Shape));
1034 //
1035 // TryAndRepairBrokenWearable(WearableType.Shape, invService, userID, appearance);
1036 // resetwearable = true;
1037 // }
1038 // else
1039 // {
1040 // if (appearance.Wearables[(int)WearableType.Shape][0].ItemID == UUID.Zero)
1041 // {
1042 // m_log.WarnFormat("[AVFACTORY]: Shape is UUID.Zero and broken, {0} {1}",
1043 // appearance.Wearables[(int)WearableType.Shape][0].ItemID,
1044 // appearance.Wearables[(int)WearableType.Shape][0].AssetID);
1045 // TryAndRepairBrokenWearable(WearableType.Shape, invService, userID, appearance);
1046 // resetwearable = true;
1047 //
1048 // }
1049 //
1050 // }
1051 // // I don't know why we have to test for this again... but the above switches do not capture these scenarios for some reason....
1052 // if (appearance.Wearables[(int)WearableType.Hair] == null)
1053 // {
1054 // m_log.WarnFormat("[AVFACTORY]: {0} Hair is Null, passing existing values.", (WearableType.Hair));
1055 //
1056 // TryAndRepairBrokenWearable(WearableType.Hair, invService, userID, appearance);
1057 // resetwearable = true;
1058 // }
1059 // else
1060 // {
1061 // if (appearance.Wearables[(int)WearableType.Hair][0].ItemID == UUID.Zero)
1062 // {
1063 // m_log.WarnFormat("[AVFACTORY]: Hair is UUID.Zero and broken, {0} {1}",
1064 // appearance.Wearables[(int)WearableType.Hair][0].ItemID,
1065 // appearance.Wearables[(int)WearableType.Hair][0].AssetID);
1066 // TryAndRepairBrokenWearable(WearableType.Hair, invService, userID, appearance);
1067 // resetwearable = true;
1068 //
1069 // }
1070 //
1071 // }
1072 // // I don't know why we have to test for this again... but the above switches do not capture these scenarios for some reason....
1073 // if (appearance.Wearables[(int)WearableType.Skin] == null)
1074 // {
1075 // m_log.WarnFormat("[AVFACTORY]: {0} Skin is Null, passing existing values.", (WearableType.Skin));
1076 //
1077 // TryAndRepairBrokenWearable(WearableType.Skin, invService, userID, appearance);
1078 // resetwearable = true;
1079 // }
1080 // else
1081 // {
1082 // if (appearance.Wearables[(int)WearableType.Skin][0].ItemID == UUID.Zero)
1083 // {
1084 // m_log.WarnFormat("[AVFACTORY]: Skin is UUID.Zero and broken, {0} {1}",
1085 // appearance.Wearables[(int)WearableType.Skin][0].ItemID,
1086 // appearance.Wearables[(int)WearableType.Skin][0].AssetID);
1087 // TryAndRepairBrokenWearable(WearableType.Skin, invService, userID, appearance);
1088 // resetwearable = true;
1089 //
1090 // }
1091 //
1092 // }
1093 // if (resetwearable)
1094 // {
1095 // ScenePresence presence = null;
1096 // if (m_scene.TryGetScenePresence(userID, out presence))
1097 // {
1098 // presence.ControllingClient.SendWearables(presence.Appearance.Wearables,
1099 // presence.Appearance.Serial++);
1100 // }
1101 // }
1102 //
1103 // }
1104 // else
1105 // {
1106 // m_log.WarnFormat("[AVFACTORY]: user {0} has no inventory, appearance isn't going to work", userID);
1107 // }
1108  }
1109 
1110  private void TryAndRepairBrokenWearable(WearableType type, IInventoryService invService, UUID userID,AvatarAppearance appearance)
1111  {
1112  UUID defaultwearable = GetDefaultItem(type);
1113  if (defaultwearable != UUID.Zero)
1114  {
1115  UUID newInvItem = UUID.Random();
1116  InventoryItemBase itembase = new InventoryItemBase(newInvItem, userID)
1117  {
1118  AssetID = defaultwearable,
1119  AssetType = (int)FolderType.BodyPart,
1120  CreatorId = userID.ToString(),
1121  //InvType = (int)InventoryType.Wearable,
1122  Description = "Failed Wearable Replacement",
1123  Folder = invService.GetFolderForType(userID, FolderType.BodyPart).ID,
1124  Flags = (uint) type, Name = Enum.GetName(typeof (WearableType), type),
1125  BasePermissions = (uint) PermissionMask.Copy,
1126  CurrentPermissions = (uint) PermissionMask.Copy,
1127  EveryOnePermissions = (uint) PermissionMask.Copy,
1128  GroupPermissions = (uint) PermissionMask.Copy,
1129  NextPermissions = (uint) PermissionMask.Copy
1130  };
1131  invService.AddItem(itembase);
1132  UUID LinkInvItem = UUID.Random();
1133  itembase = new InventoryItemBase(LinkInvItem, userID)
1134  {
1135  AssetID = newInvItem,
1136  AssetType = (int)AssetType.Link,
1137  CreatorId = userID.ToString(),
1138  InvType = (int) InventoryType.Wearable,
1139  Description = "Failed Wearable Replacement",
1140  Folder = invService.GetFolderForType(userID, FolderType.CurrentOutfit).ID,
1141  Flags = (uint) type,
1142  Name = Enum.GetName(typeof (WearableType), type),
1143  BasePermissions = (uint) PermissionMask.Copy,
1144  CurrentPermissions = (uint) PermissionMask.Copy,
1145  EveryOnePermissions = (uint) PermissionMask.Copy,
1146  GroupPermissions = (uint) PermissionMask.Copy,
1147  NextPermissions = (uint) PermissionMask.Copy
1148  };
1149  invService.AddItem(itembase);
1150  appearance.Wearables[(int)type] = new AvatarWearable(newInvItem, GetDefaultItem(type));
1151  ScenePresence presence = null;
1152  if (m_scene.TryGetScenePresence(userID, out presence))
1153  {
1154  m_scene.SendInventoryUpdate(presence.ControllingClient,
1155  invService.GetFolderForType(userID, FolderType.CurrentOutfit), false, true);
1156  }
1157  }
1158  }
1159 
1160  private UUID GetDefaultItem(WearableType wearable)
1161  {
1162  // These are ruth
1163  UUID ret = UUID.Zero;
1164  switch (wearable)
1165  {
1166  case WearableType.Eyes:
1167  ret = new UUID("4bb6fa4d-1cd2-498a-a84c-95c1a0e745a7");
1168  break;
1169  case WearableType.Hair:
1170  ret = new UUID("d342e6c0-b9d2-11dc-95ff-0800200c9a66");
1171  break;
1172  case WearableType.Pants:
1173  ret = new UUID("00000000-38f9-1111-024e-222222111120");
1174  break;
1175  case WearableType.Shape:
1176  ret = new UUID("66c41e39-38f9-f75a-024e-585989bfab73");
1177  break;
1178  case WearableType.Shirt:
1179  ret = new UUID("00000000-38f9-1111-024e-222222111110");
1180  break;
1181  case WearableType.Skin:
1182  ret = new UUID("77c41e39-38f9-f75a-024e-585989bbabbb");
1183  break;
1184  case WearableType.Undershirt:
1185  ret = new UUID("16499ebb-3208-ec27-2def-481881728f47");
1186  break;
1187  case WearableType.Underpants:
1188  ret = new UUID("4ac2e9c7-3671-d229-316a-67717730841d");
1189  break;
1190  }
1191 
1192  return ret;
1193  }
1194  #endregion
1195 
1196  #region Client Event Handlers
1197  private void Client_OnRequestWearables(IClientAPI client)
1202  {
1203  Util.FireAndForget(delegate(object x)
1204  {
1205  Thread.Sleep(4000);
1206 
1207  // m_log.DebugFormat("[AVFACTORY]: Client_OnRequestWearables called for {0} ({1})", client.Name, client.AgentId);
1208  ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
1209  if (sp != null)
1210  client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++);
1211  else
1212  m_log.WarnFormat("[AVFACTORY]: Client_OnRequestWearables unable to find presence for {0}", client.AgentId);
1213  }, null, "AvatarFactoryModule.OnClientRequestWearables");
1214  }
1215 
1222  private void Client_OnSetAppearance(IClientAPI client, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 avSize, WearableCacheItem[] cacheItems)
1223  {
1224  // m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance called for {0} ({1})", client.Name, client.AgentId);
1225  ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
1226  if (sp != null)
1227  SetAppearance(sp, textureEntry, visualParams,avSize, cacheItems);
1228  else
1229  m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance unable to find presence for {0}", client.AgentId);
1230  }
1231 
1237  private void Client_OnAvatarNowWearing(IClientAPI client, AvatarWearingArgs e)
1238  {
1239  // m_log.WarnFormat("[AVFACTORY]: Client_OnAvatarNowWearing called for {0} ({1})", client.Name, client.AgentId);
1240  ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
1241  if (sp == null)
1242  {
1243  m_log.WarnFormat("[AVFACTORY]: Client_OnAvatarNowWearing unable to find presence for {0}", client.AgentId);
1244  return;
1245  }
1246 
1247  // we need to clean out the existing textures
1248  sp.Appearance.ResetAppearance();
1249 
1250  // operate on a copy of the appearance so we don't have to lock anything yet
1251  AvatarAppearance avatAppearance = new AvatarAppearance(sp.Appearance, false);
1252 
1253  foreach (AvatarWearingArgs.Wearable wear in e.NowWearing)
1254  {
1255  // If the wearable type is larger than the current array, expand it
1256  if (avatAppearance.Wearables.Length <= wear.Type)
1257  {
1258  int currentLength = avatAppearance.Wearables.Length;
1259  AvatarWearable[] wears = avatAppearance.Wearables;
1260  Array.Resize(ref wears, wear.Type + 1);
1261  for (int i = currentLength ; i <= wear.Type ; i++)
1262  wears[i] = new AvatarWearable();
1263  avatAppearance.Wearables = wears;
1264  }
1265  avatAppearance.Wearables[wear.Type].Add(wear.ItemID, UUID.Zero);
1266  }
1267 
1268  avatAppearance.GetAssetsFrom(sp.Appearance);
1269 
1270  lock (m_setAppearanceLock)
1271  {
1272  // Update only those fields that we have changed. This is important because the viewer
1273  // often sends AvatarIsWearing and SetAppearance packets at once, and AvatarIsWearing
1274  // shouldn't overwrite the changes made in SetAppearance.
1275  sp.Appearance.Wearables = avatAppearance.Wearables;
1276  sp.Appearance.Texture = avatAppearance.Texture;
1277 
1278  // We don't need to send the appearance here since the "iswearing" will trigger a new set
1279  // of visual param and baked texture changes. When those complete, the new appearance will be sent
1280 
1281  QueueAppearanceSave(client.AgentId);
1282  }
1283  }
1284 
1291  private void Client_OnCachedTextureRequest(IClientAPI client, int serial, List<CachedTextureRequestArg> cachedTextureRequest)
1292  {
1293  // m_log.WarnFormat("[AVFACTORY]: Client_OnCachedTextureRequest called for {0} ({1})", client.Name, client.AgentId);
1294  ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
1295 
1296  List<CachedTextureResponseArg> cachedTextureResponse = new List<CachedTextureResponseArg>();
1297  foreach (CachedTextureRequestArg request in cachedTextureRequest)
1298  {
1299  UUID texture = UUID.Zero;
1300  int index = request.BakedTextureIndex;
1301 
1302  if (m_reusetextures)
1303  {
1304  // this is the most insanely dumb way to do this... however it seems to
1305  // actually work. if the appearance has been reset because wearables have
1306  // changed then the texture entries are zero'd out until the bakes are
1307  // uploaded. on login, if the textures exist in the cache (eg if you logged
1308  // into the simulator recently, then the appearance will pull those and send
1309  // them back in the packet and you won't have to rebake. if the textures aren't
1310  // in the cache then the intial makeroot() call in scenepresence will zero
1311  // them out.
1312  //
1313  // a better solution (though how much better is an open question) is to
1314  // store the hashes in the appearance and compare them. Thats's coming.
1315 
1316  Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[index];
1317  if (face != null)
1318  texture = face.TextureID;
1319 
1320  // m_log.WarnFormat("[AVFACTORY]: reuse texture {0} for index {1}",texture,index);
1321  }
1322 
1324  response.BakedTextureIndex = index;
1325  response.BakedTextureID = texture;
1326  response.HostName = null;
1327 
1328  cachedTextureResponse.Add(response);
1329  }
1330 
1331  // m_log.WarnFormat("[AVFACTORY]: serial is {0}",serial);
1332  // The serial number appears to be used to match requests and responses
1333  // in the texture transaction. We just send back the serial number
1334  // that was provided in the request. The viewer bumps this for us.
1335  client.SendCachedTextureResponse(sp, serial, cachedTextureResponse);
1336  }
1337 
1338 
1339  #endregion
1340 
1342  {
1343  outputAction("For {0} in {1}", sp.Name, m_scene.RegionInfo.RegionName);
1344  outputAction(BAKED_TEXTURES_REPORT_FORMAT, "Bake Type", "UUID");
1345 
1346  Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = GetBakedTextureFaces(sp.UUID);
1347 
1348  foreach (BakeType bt in bakedTextures.Keys)
1349  {
1350  string rawTextureID;
1351 
1352  if (bakedTextures[bt] == null)
1353  {
1354  rawTextureID = "not set";
1355  }
1356  else
1357  {
1358  rawTextureID = bakedTextures[bt].TextureID.ToString();
1359 
1360  if (m_scene.AssetService.Get(rawTextureID) == null)
1361  rawTextureID += " (not found)";
1362  else
1363  rawTextureID += " (uploaded)";
1364  }
1365 
1366  outputAction(BAKED_TEXTURES_REPORT_FORMAT, bt, rawTextureID);
1367  }
1368 
1369  bool bakedTextureValid = m_scene.AvatarFactory.ValidateBakedTextureCache(sp);
1370  outputAction("{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "incomplete");
1371  }
1372  }
1373 }
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
virtual Primitive.TextureEntry Texture
void WriteBakedTexturesReport(IScenePresence sp, ReportOutputAction outputAction)
Get a report about the current state of a scene presence's baked appearance textures.
void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
static readonly byte[] BAKE_INDICES
InventoryFolderBase GetRootFolder(UUID userID)
Retrieve the root inventory folder for the given user.
delegate void ReportOutputAction(string format, params object[] args)
void SetAppearance(IScenePresence sp, AvatarAppearance appearance, WearableCacheItem[] cacheItems)
Contains the Avatar's Appearance and methods to manipulate the appearance.
void QueueAppearanceSend(UUID agentid)
Queue up a request to send appearance.
OpenSim.Framework.PermissionMask PermissionMask
AssetBase GetCached(string id)
Synchronously fetches an asset from the local cache only.
bool SaveBakedTextures(UUID agentId)
Save the baked textures for the given agent permanently in the asset database.
bool UpdateBakedTextureCache(IScenePresence sp, WearableCacheItem[] cacheItems)
PresenceType
Indicate the type of ScenePresence.
Definition: PresenceType.cs:34
Asset class. All Assets are reference by this class or a class derived from this class ...
Definition: AssetBase.cs:49
static AvatarWearable[] DefaultWearables
Dictionary< BakeType, Primitive.TextureEntryFace > GetBakedTextureFaces(UUID agentId)
Return the baked texture ids of the given agent.
Inventory Item - contains all the properties associated with an individual inventory piece...
virtual AvatarWearable[] Wearables
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
Interactive OpenSim region server
Definition: OpenSim.cs:55
bool ValidateBakedTextureCache(IScenePresence sp)
Validate that OpenSim can find the baked textures need to display a given avatar
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
delegate void SetAppearance(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 AvSize, WearableCacheItem[] CacheItems)
void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 avSize, WearableCacheItem[] cacheItems)
virtual string Name
The name of this entity
Definition: EntityBase.cs:65
void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, WearableCacheItem[] cacheItems)
Set appearance data (texture asset IDs and slider settings)
int RequestRebake(IScenePresence sp, bool missingTexturesOnly)
Request a rebake of textures for an avatar.
bool SendAppearance(UUID agentId)
Send the appearance of an avatar to others in the scene.
InventoryFolderBase GetFolderForType(UUID userID, FolderType type)
Gets the user folder for the given folder-type
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...