AvatarFactoryModule.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782
  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. using System;
  28. using System.Collections.Generic;
  29. using System.Reflection;
  30. using System.Threading;
  31. using System.Text;
  32. using System.Timers;
  33. using log4net;
  34. using Nini.Config;
  35. using OpenMetaverse;
  36. using OpenSim.Framework;
  37. using OpenSim.Region.Framework.Interfaces;
  38. using OpenSim.Region.Framework.Scenes;
  39. using OpenSim.Services.Interfaces;
  40. using Mono.Addins;
  41. using PermissionMask = OpenSim.Framework.PermissionMask;
  42. namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
  43. {
  44. [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AvatarFactoryModule")]
  45. public class AvatarFactoryModule : IAvatarFactoryModule, INonSharedRegionModule
  46. {
  47. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  48. public const string BAKED_TEXTURES_REPORT_FORMAT = "{0,-9} {1}";
  49. private Scene m_scene = null;
  50. private int m_savetime = 5; // seconds to wait before saving changed appearance
  51. private int m_sendtime = 2; // seconds to wait before sending changed appearance
  52. private bool m_reusetextures = false;
  53. private int m_checkTime = 500; // milliseconds to wait between checks for appearance updates
  54. private System.Timers.Timer m_updateTimer = new System.Timers.Timer();
  55. private Dictionary<UUID,long> m_savequeue = new Dictionary<UUID,long>();
  56. private Dictionary<UUID,long> m_sendqueue = new Dictionary<UUID,long>();
  57. private object m_setAppearanceLock = new object();
  58. #region Region Module interface
  59. public void Initialise(IConfigSource config)
  60. {
  61. IConfig appearanceConfig = config.Configs["Appearance"];
  62. if (appearanceConfig != null)
  63. {
  64. m_savetime = Convert.ToInt32(appearanceConfig.GetString("DelayBeforeAppearanceSave",Convert.ToString(m_savetime)));
  65. m_sendtime = Convert.ToInt32(appearanceConfig.GetString("DelayBeforeAppearanceSend",Convert.ToString(m_sendtime)));
  66. m_reusetextures = appearanceConfig.GetBoolean("ReuseTextures",m_reusetextures);
  67. // m_log.InfoFormat("[AVFACTORY] configured for {0} save and {1} send",m_savetime,m_sendtime);
  68. }
  69. }
  70. public void AddRegion(Scene scene)
  71. {
  72. if (m_scene == null)
  73. m_scene = scene;
  74. scene.RegisterModuleInterface<IAvatarFactoryModule>(this);
  75. scene.EventManager.OnNewClient += SubscribeToClientEvents;
  76. }
  77. public void RemoveRegion(Scene scene)
  78. {
  79. if (scene == m_scene)
  80. {
  81. scene.UnregisterModuleInterface<IAvatarFactoryModule>(this);
  82. scene.EventManager.OnNewClient -= SubscribeToClientEvents;
  83. }
  84. m_scene = null;
  85. }
  86. public void RegionLoaded(Scene scene)
  87. {
  88. m_updateTimer.Enabled = false;
  89. m_updateTimer.AutoReset = true;
  90. m_updateTimer.Interval = m_checkTime; // 500 milliseconds wait to start async ops
  91. m_updateTimer.Elapsed += new ElapsedEventHandler(HandleAppearanceUpdateTimer);
  92. }
  93. public void Close()
  94. {
  95. }
  96. public string Name
  97. {
  98. get { return "Default Avatar Factory"; }
  99. }
  100. public bool IsSharedModule
  101. {
  102. get { return false; }
  103. }
  104. public Type ReplaceableInterface
  105. {
  106. get { return null; }
  107. }
  108. private void SubscribeToClientEvents(IClientAPI client)
  109. {
  110. client.OnRequestWearables += Client_OnRequestWearables;
  111. client.OnSetAppearance += Client_OnSetAppearance;
  112. client.OnAvatarNowWearing += Client_OnAvatarNowWearing;
  113. client.OnCachedTextureRequest += Client_OnCachedTextureRequest;
  114. }
  115. #endregion
  116. #region IAvatarFactoryModule
  117. /// </summary>
  118. /// <param name="sp"></param>
  119. /// <param name="texture"></param>
  120. /// <param name="visualParam"></param>
  121. public void SetAppearance(IScenePresence sp, AvatarAppearance appearance)
  122. {
  123. DoSetAppearance(sp, appearance.Texture, appearance.VisualParams, new List<CachedTextureRequestArg>());
  124. }
  125. /// <summary>
  126. /// Set appearance data (texture asset IDs and slider settings)
  127. /// </summary>
  128. /// <param name="sp"></param>
  129. /// <param name="texture"></param>
  130. /// <param name="visualParam"></param>
  131. public void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams)
  132. {
  133. DoSetAppearance(sp, textureEntry, visualParams, new List<CachedTextureRequestArg>());
  134. }
  135. /// <summary>
  136. /// Set appearance data (texture asset IDs and slider settings)
  137. /// </summary>
  138. /// <param name="sp"></param>
  139. /// <param name="texture"></param>
  140. /// <param name="visualParam"></param>
  141. protected void DoSetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, List<CachedTextureRequestArg> hashes)
  142. {
  143. // m_log.DebugFormat(
  144. // "[AVFACTORY]: start SetAppearance for {0}, te {1}, visualParams {2}",
  145. // sp.Name, textureEntry, visualParams);
  146. // TODO: This is probably not necessary any longer, just assume the
  147. // textureEntry set implies that the appearance transaction is complete
  148. bool changed = false;
  149. // Process the texture entry transactionally, this doesn't guarantee that Appearance is
  150. // going to be handled correctly but it does serialize the updates to the appearance
  151. lock (m_setAppearanceLock)
  152. {
  153. // Process the visual params, this may change height as well
  154. if (visualParams != null)
  155. {
  156. // string[] visualParamsStrings = new string[visualParams.Length];
  157. // for (int i = 0; i < visualParams.Length; i++)
  158. // visualParamsStrings[i] = visualParams[i].ToString();
  159. // m_log.DebugFormat(
  160. // "[AVFACTORY]: Setting visual params for {0} to {1}",
  161. // client.Name, string.Join(", ", visualParamsStrings));
  162. float oldHeight = sp.Appearance.AvatarHeight;
  163. changed = sp.Appearance.SetVisualParams(visualParams);
  164. if (sp.Appearance.AvatarHeight != oldHeight && sp.Appearance.AvatarHeight > 0)
  165. ((ScenePresence)sp).SetHeight(sp.Appearance.AvatarHeight);
  166. }
  167. // Process the baked texture array
  168. if (textureEntry != null)
  169. {
  170. // m_log.DebugFormat("[AVFACTORY]: Received texture update for {0} {1}", sp.Name, sp.UUID);
  171. // WriteBakedTexturesReport(sp, m_log.DebugFormat);
  172. changed = sp.Appearance.SetTextureEntries(textureEntry) || changed;
  173. // WriteBakedTexturesReport(sp, m_log.DebugFormat);
  174. // If bake textures are missing and this is not an NPC, request a rebake from client
  175. if (!ValidateBakedTextureCache(sp) && (((ScenePresence)sp).PresenceType != PresenceType.Npc))
  176. RequestRebake(sp, true);
  177. // Save the wearble hashes in the appearance
  178. sp.Appearance.ResetTextureHashes();
  179. if (m_reusetextures)
  180. {
  181. foreach (CachedTextureRequestArg arg in hashes)
  182. sp.Appearance.SetTextureHash(arg.BakedTextureIndex,arg.WearableHashID);
  183. }
  184. // This appears to be set only in the final stage of the appearance
  185. // update transaction. In theory, we should be able to do an immediate
  186. // appearance send and save here.
  187. }
  188. // NPC should send to clients immediately and skip saving appearance
  189. if (((ScenePresence)sp).PresenceType == PresenceType.Npc)
  190. {
  191. SendAppearance((ScenePresence)sp);
  192. return;
  193. }
  194. // save only if there were changes, send no matter what (doesn't hurt to send twice)
  195. if (changed)
  196. QueueAppearanceSave(sp.ControllingClient.AgentId);
  197. QueueAppearanceSend(sp.ControllingClient.AgentId);
  198. }
  199. // m_log.WarnFormat("[AVFACTORY]: complete SetAppearance for {0}:\n{1}",client.AgentId,sp.Appearance.ToString());
  200. }
  201. private void SendAppearance(ScenePresence sp)
  202. {
  203. // Send the appearance to everyone in the scene
  204. sp.SendAppearanceToAllOtherAgents();
  205. // Send animations back to the avatar as well
  206. sp.Animator.SendAnimPack();
  207. }
  208. public bool SendAppearance(UUID agentId)
  209. {
  210. // m_log.DebugFormat("[AVFACTORY]: Sending appearance for {0}", agentId);
  211. ScenePresence sp = m_scene.GetScenePresence(agentId);
  212. if (sp == null)
  213. {
  214. // This is expected if the user has gone away.
  215. // m_log.DebugFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentId);
  216. return false;
  217. }
  218. SendAppearance(sp);
  219. return true;
  220. }
  221. public Dictionary<BakeType, Primitive.TextureEntryFace> GetBakedTextureFaces(UUID agentId)
  222. {
  223. ScenePresence sp = m_scene.GetScenePresence(agentId);
  224. if (sp == null)
  225. return new Dictionary<BakeType, Primitive.TextureEntryFace>();
  226. return GetBakedTextureFaces(sp);
  227. }
  228. public bool SaveBakedTextures(UUID agentId)
  229. {
  230. ScenePresence sp = m_scene.GetScenePresence(agentId);
  231. if (sp == null)
  232. return false;
  233. m_log.DebugFormat(
  234. "[AV FACTORY]: Permanently saving baked textures for {0} in {1}",
  235. sp.Name, m_scene.RegionInfo.RegionName);
  236. Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = GetBakedTextureFaces(sp);
  237. if (bakedTextures.Count == 0)
  238. return false;
  239. foreach (BakeType bakeType in bakedTextures.Keys)
  240. {
  241. Primitive.TextureEntryFace bakedTextureFace = bakedTextures[bakeType];
  242. if (bakedTextureFace == null)
  243. {
  244. // This can happen legitimately, since some baked textures might not exist
  245. //m_log.WarnFormat(
  246. // "[AV FACTORY]: No texture ID set for {0} for {1} in {2} not found when trying to save permanently",
  247. // bakeType, sp.Name, m_scene.RegionInfo.RegionName);
  248. continue;
  249. }
  250. AssetBase asset = m_scene.AssetService.Get(bakedTextureFace.TextureID.ToString());
  251. if (asset != null)
  252. {
  253. // Replace an HG ID with the simple asset ID so that we can persist textures for foreign HG avatars
  254. asset.ID = asset.FullID.ToString();
  255. asset.Temporary = false;
  256. asset.Local = false;
  257. m_scene.AssetService.Store(asset);
  258. }
  259. else
  260. {
  261. m_log.WarnFormat(
  262. "[AV FACTORY]: Baked texture id {0} not found for bake {1} for avatar {2} in {3} when trying to save permanently",
  263. bakedTextureFace.TextureID, bakeType, sp.Name, m_scene.RegionInfo.RegionName);
  264. }
  265. }
  266. return true;
  267. }
  268. /// <summary>
  269. /// Queue up a request to send appearance.
  270. /// </summary>
  271. /// <remarks>
  272. /// Makes it possible to accumulate changes without sending out each one separately.
  273. /// </remarks>
  274. /// <param name="agentId"></param>
  275. public void QueueAppearanceSend(UUID agentid)
  276. {
  277. // m_log.DebugFormat("[AVFACTORY]: Queue appearance send for {0}", agentid);
  278. // 10000 ticks per millisecond, 1000 milliseconds per second
  279. long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_sendtime * 1000 * 10000);
  280. lock (m_sendqueue)
  281. {
  282. m_sendqueue[agentid] = timestamp;
  283. m_updateTimer.Start();
  284. }
  285. }
  286. public void QueueAppearanceSave(UUID agentid)
  287. {
  288. // m_log.DebugFormat("[AVFACTORY]: Queueing appearance save for {0}", agentid);
  289. // 10000 ticks per millisecond, 1000 milliseconds per second
  290. long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000);
  291. lock (m_savequeue)
  292. {
  293. m_savequeue[agentid] = timestamp;
  294. m_updateTimer.Start();
  295. }
  296. }
  297. public bool ValidateBakedTextureCache(IScenePresence sp)
  298. {
  299. bool defonly = true; // are we only using default textures
  300. // Process the texture entry
  301. for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
  302. {
  303. int idx = AvatarAppearance.BAKE_INDICES[i];
  304. Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
  305. // if there is no texture entry, skip it
  306. if (face == null)
  307. continue;
  308. // m_log.DebugFormat(
  309. // "[AVFACTORY]: Looking for texture {0}, id {1} for {2} {3}",
  310. // face.TextureID, idx, client.Name, client.AgentId);
  311. // if the texture is one of the "defaults" then skip it
  312. // this should probably be more intelligent (skirt texture doesnt matter
  313. // if the avatar isnt wearing a skirt) but if any of the main baked
  314. // textures is default then the rest should be as well
  315. if (face.TextureID == UUID.Zero || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
  316. continue;
  317. defonly = false; // found a non-default texture reference
  318. if (m_scene.AssetService.Get(face.TextureID.ToString()) == null)
  319. return false;
  320. }
  321. // m_log.DebugFormat("[AVFACTORY]: Completed texture check for {0} {1}", sp.Name, sp.UUID);
  322. // If we only found default textures, then the appearance is not cached
  323. return (defonly ? false : true);
  324. }
  325. public int RequestRebake(IScenePresence sp, bool missingTexturesOnly)
  326. {
  327. int texturesRebaked = 0;
  328. for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
  329. {
  330. int idx = AvatarAppearance.BAKE_INDICES[i];
  331. Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
  332. // if there is no texture entry, skip it
  333. if (face == null)
  334. continue;
  335. // m_log.DebugFormat(
  336. // "[AVFACTORY]: Looking for texture {0}, id {1} for {2} {3}",
  337. // face.TextureID, idx, client.Name, client.AgentId);
  338. // if the texture is one of the "defaults" then skip it
  339. // this should probably be more intelligent (skirt texture doesnt matter
  340. // if the avatar isnt wearing a skirt) but if any of the main baked
  341. // textures is default then the rest should be as well
  342. if (face.TextureID == UUID.Zero || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
  343. continue;
  344. if (missingTexturesOnly)
  345. {
  346. if (m_scene.AssetService.Get(face.TextureID.ToString()) != null)
  347. {
  348. continue;
  349. }
  350. else
  351. {
  352. // On inter-simulator teleports, this occurs if baked textures are not being stored by the
  353. // grid asset service (which means that they are not available to the new region and so have
  354. // to be re-requested from the client).
  355. //
  356. // The only available core OpenSimulator behaviour right now
  357. // is not to store these textures, temporarily or otherwise.
  358. m_log.DebugFormat(
  359. "[AVFACTORY]: Missing baked texture {0} ({1}) for {2}, requesting rebake.",
  360. face.TextureID, idx, sp.Name);
  361. }
  362. }
  363. else
  364. {
  365. m_log.DebugFormat(
  366. "[AVFACTORY]: Requesting rebake of {0} ({1}) for {2}.",
  367. face.TextureID, idx, sp.Name);
  368. }
  369. texturesRebaked++;
  370. sp.ControllingClient.SendRebakeAvatarTextures(face.TextureID);
  371. }
  372. return texturesRebaked;
  373. }
  374. #endregion
  375. #region AvatarFactoryModule private methods
  376. private Dictionary<BakeType, Primitive.TextureEntryFace> GetBakedTextureFaces(ScenePresence sp)
  377. {
  378. if (sp.IsChildAgent)
  379. return new Dictionary<BakeType, Primitive.TextureEntryFace>();
  380. Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures
  381. = new Dictionary<BakeType, Primitive.TextureEntryFace>();
  382. AvatarAppearance appearance = sp.Appearance;
  383. Primitive.TextureEntryFace[] faceTextures = appearance.Texture.FaceTextures;
  384. foreach (int i in Enum.GetValues(typeof(BakeType)))
  385. {
  386. BakeType bakeType = (BakeType)i;
  387. if (bakeType == BakeType.Unknown)
  388. continue;
  389. // m_log.DebugFormat(
  390. // "[AVFACTORY]: NPC avatar {0} has texture id {1} : {2}",
  391. // acd.AgentID, i, acd.Appearance.Texture.FaceTextures[i]);
  392. int ftIndex = (int)AppearanceManager.BakeTypeToAgentTextureIndex(bakeType);
  393. Primitive.TextureEntryFace texture = faceTextures[ftIndex]; // this will be null if there's no such baked texture
  394. bakedTextures[bakeType] = texture;
  395. }
  396. return bakedTextures;
  397. }
  398. private void HandleAppearanceUpdateTimer(object sender, EventArgs ea)
  399. {
  400. long now = DateTime.Now.Ticks;
  401. lock (m_sendqueue)
  402. {
  403. Dictionary<UUID, long> sends = new Dictionary<UUID, long>(m_sendqueue);
  404. foreach (KeyValuePair<UUID, long> kvp in sends)
  405. {
  406. // We have to load the key and value into local parameters to avoid a race condition if we loop
  407. // around and load kvp with a different value before FireAndForget has launched its thread.
  408. UUID avatarID = kvp.Key;
  409. long sendTime = kvp.Value;
  410. // m_log.DebugFormat("[AVFACTORY]: Handling queued appearance updates for {0}, update delta to now is {1}", avatarID, sendTime - now);
  411. if (sendTime < now)
  412. {
  413. Util.FireAndForget(o => SendAppearance(avatarID));
  414. m_sendqueue.Remove(avatarID);
  415. }
  416. }
  417. }
  418. lock (m_savequeue)
  419. {
  420. Dictionary<UUID, long> saves = new Dictionary<UUID, long>(m_savequeue);
  421. foreach (KeyValuePair<UUID, long> kvp in saves)
  422. {
  423. // We have to load the key and value into local parameters to avoid a race condition if we loop
  424. // around and load kvp with a different value before FireAndForget has launched its thread.
  425. UUID avatarID = kvp.Key;
  426. long sendTime = kvp.Value;
  427. if (sendTime < now)
  428. {
  429. Util.FireAndForget(o => SaveAppearance(avatarID));
  430. m_savequeue.Remove(avatarID);
  431. }
  432. }
  433. // We must lock both queues here so that QueueAppearanceSave() or *Send() don't m_updateTimer.Start() on
  434. // another thread inbetween the first count calls and m_updateTimer.Stop() on this thread.
  435. lock (m_sendqueue)
  436. if (m_savequeue.Count == 0 && m_sendqueue.Count == 0)
  437. m_updateTimer.Stop();
  438. }
  439. }
  440. private void SaveAppearance(UUID agentid)
  441. {
  442. // We must set appearance parameters in the en_US culture in order to avoid issues where values are saved
  443. // in a culture where decimal points are commas and then reloaded in a culture which just treats them as
  444. // number seperators.
  445. Culture.SetCurrentCulture();
  446. ScenePresence sp = m_scene.GetScenePresence(agentid);
  447. if (sp == null)
  448. {
  449. // This is expected if the user has gone away.
  450. // m_log.DebugFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentid);
  451. return;
  452. }
  453. // m_log.DebugFormat("[AVFACTORY]: Saving appearance for avatar {0}", agentid);
  454. // This could take awhile since it needs to pull inventory
  455. // 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
  456. // assets and item asset id changes to complete.
  457. // I don't think we need to worry about doing this within m_setAppearanceLock since the queueing avoids
  458. // multiple save requests.
  459. SetAppearanceAssets(sp.UUID, sp.Appearance);
  460. // List<AvatarAttachment> attachments = sp.Appearance.GetAttachments();
  461. // foreach (AvatarAttachment att in attachments)
  462. // {
  463. // m_log.DebugFormat(
  464. // "[AVFACTORY]: For {0} saving attachment {1} at point {2}",
  465. // sp.Name, att.ItemID, att.AttachPoint);
  466. // }
  467. m_scene.AvatarService.SetAppearance(agentid, sp.Appearance);
  468. // Trigger this here because it's the final step in the set/queue/save process for appearance setting.
  469. // Everything has been updated and stored. Ensures bakes have been persisted (if option is set to persist bakes).
  470. m_scene.EventManager.TriggerAvatarAppearanceChanged(sp);
  471. }
  472. private void SetAppearanceAssets(UUID userID, AvatarAppearance appearance)
  473. {
  474. IInventoryService invService = m_scene.InventoryService;
  475. if (invService.GetRootFolder(userID) != null)
  476. {
  477. for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++)
  478. {
  479. for (int j = 0; j < appearance.Wearables[i].Count; j++)
  480. {
  481. if (appearance.Wearables[i][j].ItemID == UUID.Zero)
  482. continue;
  483. // Ignore ruth's assets
  484. if (appearance.Wearables[i][j].ItemID == AvatarWearable.DefaultWearables[i][0].ItemID)
  485. continue;
  486. InventoryItemBase baseItem = new InventoryItemBase(appearance.Wearables[i][j].ItemID, userID);
  487. baseItem = invService.GetItem(baseItem);
  488. if (baseItem != null)
  489. {
  490. appearance.Wearables[i].Add(appearance.Wearables[i][j].ItemID, baseItem.AssetID);
  491. }
  492. else
  493. {
  494. m_log.ErrorFormat(
  495. "[AVFACTORY]: Can't find inventory item {0} for {1}, setting to default",
  496. appearance.Wearables[i][j].ItemID, (WearableType)i);
  497. appearance.Wearables[i].RemoveItem(appearance.Wearables[i][j].ItemID);
  498. }
  499. }
  500. }
  501. }
  502. else
  503. {
  504. m_log.WarnFormat("[AVFACTORY]: user {0} has no inventory, appearance isn't going to work", userID);
  505. }
  506. }
  507. #endregion
  508. #region Client Event Handlers
  509. /// <summary>
  510. /// Tell the client for this scene presence what items it should be wearing now
  511. /// </summary>
  512. /// <param name="client"></param>
  513. private void Client_OnRequestWearables(IClientAPI client)
  514. {
  515. // m_log.DebugFormat("[AVFACTORY]: Client_OnRequestWearables called for {0} ({1})", client.Name, client.AgentId);
  516. ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
  517. if (sp != null)
  518. client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++);
  519. else
  520. m_log.WarnFormat("[AVFACTORY]: Client_OnRequestWearables unable to find presence for {0}", client.AgentId);
  521. }
  522. /// <summary>
  523. /// Set appearance data (texture asset IDs and slider settings) received from a client
  524. /// </summary>
  525. /// <param name="client"></param>
  526. /// <param name="texture"></param>
  527. /// <param name="visualParam"></param>
  528. private void Client_OnSetAppearance(IClientAPI client, Primitive.TextureEntry textureEntry, byte[] visualParams, List<CachedTextureRequestArg> hashes)
  529. {
  530. // m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance called for {0} ({1})", client.Name, client.AgentId);
  531. ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
  532. if (sp != null)
  533. DoSetAppearance(sp, textureEntry, visualParams, hashes);
  534. else
  535. m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance unable to find presence for {0}", client.AgentId);
  536. }
  537. /// <summary>
  538. /// Update what the avatar is wearing using an item from their inventory.
  539. /// </summary>
  540. /// <param name="client"></param>
  541. /// <param name="e"></param>
  542. private void Client_OnAvatarNowWearing(IClientAPI client, AvatarWearingArgs e)
  543. {
  544. // m_log.WarnFormat("[AVFACTORY]: Client_OnAvatarNowWearing called for {0} ({1})", client.Name, client.AgentId);
  545. ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
  546. if (sp == null)
  547. {
  548. m_log.WarnFormat("[AVFACTORY]: Client_OnAvatarNowWearing unable to find presence for {0}", client.AgentId);
  549. return;
  550. }
  551. // we need to clean out the existing textures
  552. sp.Appearance.ResetAppearance();
  553. // operate on a copy of the appearance so we don't have to lock anything yet
  554. AvatarAppearance avatAppearance = new AvatarAppearance(sp.Appearance, false);
  555. foreach (AvatarWearingArgs.Wearable wear in e.NowWearing)
  556. {
  557. if (wear.Type < AvatarWearable.MAX_WEARABLES)
  558. avatAppearance.Wearables[wear.Type].Add(wear.ItemID, UUID.Zero);
  559. }
  560. avatAppearance.GetAssetsFrom(sp.Appearance);
  561. lock (m_setAppearanceLock)
  562. {
  563. // Update only those fields that we have changed. This is important because the viewer
  564. // often sends AvatarIsWearing and SetAppearance packets at once, and AvatarIsWearing
  565. // shouldn't overwrite the changes made in SetAppearance.
  566. sp.Appearance.Wearables = avatAppearance.Wearables;
  567. sp.Appearance.Texture = avatAppearance.Texture;
  568. // We don't need to send the appearance here since the "iswearing" will trigger a new set
  569. // of visual param and baked texture changes. When those complete, the new appearance will be sent
  570. QueueAppearanceSave(client.AgentId);
  571. }
  572. }
  573. /// <summary>
  574. /// Respond to the cached textures request from the client
  575. /// </summary>
  576. /// <param name="client"></param>
  577. /// <param name="serial"></param>
  578. /// <param name="cachedTextureRequest"></param>
  579. private void Client_OnCachedTextureRequest(IClientAPI client, int serial, List<CachedTextureRequestArg> cachedTextureRequest)
  580. {
  581. // m_log.DebugFormat("[AVFACTORY]: Client_OnCachedTextureRequest called for {0} ({1})", client.Name, client.AgentId);
  582. ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
  583. List<CachedTextureResponseArg> cachedTextureResponse = new List<CachedTextureResponseArg>();
  584. foreach (CachedTextureRequestArg request in cachedTextureRequest)
  585. {
  586. UUID texture = UUID.Zero;
  587. int index = request.BakedTextureIndex;
  588. if (m_reusetextures)
  589. {
  590. if (sp.Appearance.GetTextureHash(index) == request.WearableHashID)
  591. {
  592. Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[index];
  593. if (face != null)
  594. texture = face.TextureID;
  595. }
  596. else
  597. {
  598. // We know that that hash is wrong, null it out
  599. // and wait for the setappearance call
  600. sp.Appearance.SetTextureHash(index,UUID.Zero);
  601. }
  602. // m_log.WarnFormat("[AVFACTORY]: use texture {0} for index {1}; hash={2}",texture,index,request.WearableHashID);
  603. }
  604. CachedTextureResponseArg response = new CachedTextureResponseArg();
  605. response.BakedTextureIndex = index;
  606. response.BakedTextureID = texture;
  607. response.HostName = null;
  608. cachedTextureResponse.Add(response);
  609. }
  610. // m_log.WarnFormat("[AVFACTORY]: serial is {0}",serial);
  611. // The serial number appears to be used to match requests and responses
  612. // in the texture transaction. We just send back the serial number
  613. // that was provided in the request. The viewer bumps this for us.
  614. client.SendCachedTextureResponse(sp, serial, cachedTextureResponse);
  615. }
  616. #endregion
  617. public void WriteBakedTexturesReport(IScenePresence sp, ReportOutputAction outputAction)
  618. {
  619. outputAction("For {0} in {1}", sp.Name, m_scene.RegionInfo.RegionName);
  620. outputAction(BAKED_TEXTURES_REPORT_FORMAT, "Bake Type", "UUID");
  621. Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = GetBakedTextureFaces(sp.UUID);
  622. foreach (BakeType bt in bakedTextures.Keys)
  623. {
  624. string rawTextureID;
  625. if (bakedTextures[bt] == null)
  626. {
  627. rawTextureID = "not set";
  628. }
  629. else
  630. {
  631. rawTextureID = bakedTextures[bt].TextureID.ToString();
  632. if (m_scene.AssetService.Get(rawTextureID) == null)
  633. rawTextureID += " (not found)";
  634. else
  635. rawTextureID += " (uploaded)";
  636. }
  637. outputAction(BAKED_TEXTURES_REPORT_FORMAT, bt, rawTextureID);
  638. }
  639. bool bakedTextureValid = m_scene.AvatarFactory.ValidateBakedTextureCache(sp);
  640. outputAction("{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "incomplete");
  641. }
  642. }
  643. }