PageRenderTime 57ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/OpenMetaverse/Modules/AppearanceManager.cs

https://bitbucket.org/VirtualReality/3rdparty-addon-modules
C# | 2342 lines | 1523 code | 316 blank | 503 comment | 258 complexity | bd3eb7a2090604a507f5d95d74e59da0 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Copyright (c) 2006-2008, openmetaverse.org
  3. * All rights reserved.
  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. *
  8. * - Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. * - Neither the name of the openmetaverse.org nor the names
  11. * of its contributors may be used to endorse or promote products derived from
  12. * this software without specific prior written permission.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  15. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  18. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  19. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  20. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  21. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  22. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  23. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  24. * POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. using System;
  27. using System.Collections.Generic;
  28. using System.Threading;
  29. using System.Drawing;
  30. using OpenMetaverse;
  31. using OpenMetaverse.Packets;
  32. using OpenMetaverse.Imaging;
  33. using OpenMetaverse.Assets;
  34. namespace OpenMetaverse
  35. {
  36. #region Enums
  37. /// <summary>
  38. /// Index of TextureEntry slots for avatar appearances
  39. /// </summary>
  40. public enum AvatarTextureIndex
  41. {
  42. Unknown = -1,
  43. HeadBodypaint = 0,
  44. UpperShirt,
  45. LowerPants,
  46. EyesIris,
  47. Hair,
  48. UpperBodypaint,
  49. LowerBodypaint,
  50. LowerShoes,
  51. HeadBaked,
  52. UpperBaked,
  53. LowerBaked,
  54. EyesBaked,
  55. LowerSocks,
  56. UpperJacket,
  57. LowerJacket,
  58. UpperGloves,
  59. UpperUndershirt,
  60. LowerUnderpants,
  61. Skirt,
  62. SkirtBaked,
  63. HairBaked,
  64. LowerAlpha,
  65. UpperAlpha,
  66. HeadAlpha,
  67. EyesAlpha,
  68. HairAlpha,
  69. HeadTattoo,
  70. UpperTattoo,
  71. LowerTattoo,
  72. NumberOfEntries
  73. }
  74. /// <summary>
  75. /// Bake layers for avatar appearance
  76. /// </summary>
  77. public enum BakeType
  78. {
  79. Unknown = -1,
  80. Head = 0,
  81. UpperBody = 1,
  82. LowerBody = 2,
  83. Eyes = 3,
  84. Skirt = 4,
  85. Hair = 5
  86. }
  87. #endregion Enums
  88. public class AppearanceManager
  89. {
  90. #region Constants
  91. /// <summary>Mask for multiple attachments</summary>
  92. public static readonly byte ATTACHMENT_ADD = 0x80;
  93. /// <summary>Mapping between BakeType and AvatarTextureIndex</summary>
  94. public static readonly byte[] BakeIndexToTextureIndex = new byte[BAKED_TEXTURE_COUNT] { 8, 9, 10, 11, 19, 20 };
  95. /// <summary>Maximum number of concurrent downloads for wearable assets and textures</summary>
  96. const int MAX_CONCURRENT_DOWNLOADS = 5;
  97. /// <summary>Maximum number of concurrent uploads for baked textures</summary>
  98. const int MAX_CONCURRENT_UPLOADS = 6;
  99. /// <summary>Timeout for fetching inventory listings</summary>
  100. const int INVENTORY_TIMEOUT = 1000 * 30;
  101. /// <summary>Timeout for fetching a single wearable, or receiving a single packet response</summary>
  102. const int WEARABLE_TIMEOUT = 1000 * 30;
  103. /// <summary>Timeout for fetching a single texture</summary>
  104. const int TEXTURE_TIMEOUT = 1000 * 120;
  105. /// <summary>Timeout for uploading a single baked texture</summary>
  106. const int UPLOAD_TIMEOUT = 1000 * 90;
  107. /// <summary>Number of times to retry bake upload</summary>
  108. const int UPLOAD_RETRIES = 2;
  109. /// <summary>When changing outfit, kick off rebake after
  110. /// 20 seconds has passed since the last change</summary>
  111. const int REBAKE_DELAY = 1000 * 20;
  112. /// <summary>Total number of wearables for each avatar</summary>
  113. public const int WEARABLE_COUNT = 16;
  114. /// <summary>Total number of baked textures on each avatar</summary>
  115. public const int BAKED_TEXTURE_COUNT = 6;
  116. /// <summary>Total number of wearables per bake layer</summary>
  117. public const int WEARABLES_PER_LAYER = 9;
  118. /// <summary>Map of what wearables are included in each bake</summary>
  119. public static readonly WearableType[][] WEARABLE_BAKE_MAP = new WearableType[][]
  120. {
  121. new WearableType[] { WearableType.Shape, WearableType.Skin, WearableType.Tattoo, WearableType.Hair, WearableType.Alpha, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid },
  122. new WearableType[] { WearableType.Shape, WearableType.Skin, WearableType.Tattoo, WearableType.Shirt, WearableType.Jacket, WearableType.Gloves, WearableType.Undershirt, WearableType.Alpha, WearableType.Invalid },
  123. new WearableType[] { WearableType.Shape, WearableType.Skin, WearableType.Tattoo, WearableType.Pants, WearableType.Shoes, WearableType.Socks, WearableType.Jacket, WearableType.Underpants, WearableType.Alpha },
  124. new WearableType[] { WearableType.Eyes, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid },
  125. new WearableType[] { WearableType.Skirt, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid },
  126. new WearableType[] { WearableType.Hair, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid, WearableType.Invalid }
  127. };
  128. /// <summary>Magic values to finalize the cache check hashes for each
  129. /// bake</summary>
  130. public static readonly UUID[] BAKED_TEXTURE_HASH = new UUID[]
  131. {
  132. new UUID("18ded8d6-bcfc-e415-8539-944c0f5ea7a6"),
  133. new UUID("338c29e3-3024-4dbb-998d-7c04cf4fa88f"),
  134. new UUID("91b4a2c7-1b1a-ba16-9a16-1f8f8dcc1c3f"),
  135. new UUID("b2cf28af-b840-1071-3c6a-78085d8128b5"),
  136. new UUID("ea800387-ea1a-14e0-56cb-24f2022f969a"),
  137. new UUID("0af1ef7c-ad24-11dd-8790-001f5bf833e8")
  138. };
  139. /// <summary>Default avatar texture, used to detect when a custom
  140. /// texture is not set for a face</summary>
  141. public static readonly UUID DEFAULT_AVATAR_TEXTURE = new UUID("c228d1cf-4b5d-4ba8-84f4-899a0796aa97");
  142. #endregion Constants
  143. #region Structs / Classes
  144. /// <summary>
  145. /// Contains information about a wearable inventory item
  146. /// </summary>
  147. public class WearableData
  148. {
  149. /// <summary>Inventory ItemID of the wearable</summary>
  150. public UUID ItemID;
  151. /// <summary>AssetID of the wearable asset</summary>
  152. public UUID AssetID;
  153. /// <summary>WearableType of the wearable</summary>
  154. public WearableType WearableType;
  155. /// <summary>AssetType of the wearable</summary>
  156. public AssetType AssetType;
  157. /// <summary>Asset data for the wearable</summary>
  158. public AssetWearable Asset;
  159. public override string ToString()
  160. {
  161. return String.Format("ItemID: {0}, AssetID: {1}, WearableType: {2}, AssetType: {3}, Asset: {4}",
  162. ItemID, AssetID, WearableType, AssetType, Asset != null ? Asset.Name : "(null)");
  163. }
  164. }
  165. /// <summary>
  166. /// Data collected from visual params for each wearable
  167. /// needed for the calculation of the color
  168. /// </summary>
  169. private struct ColorParamInfo
  170. {
  171. public VisualParam VisualParam;
  172. public VisualColorParam VisualColorParam;
  173. public float Value;
  174. public WearableType WearableType;
  175. }
  176. /// <summary>
  177. /// Holds a texture assetID and the data needed to bake this layer into
  178. /// an outfit texture. Used to keep track of currently worn textures
  179. /// and baking data
  180. /// </summary>
  181. public struct TextureData
  182. {
  183. /// <summary>A texture AssetID</summary>
  184. public UUID TextureID;
  185. /// <summary>Asset data for the texture</summary>
  186. public AssetTexture Texture;
  187. /// <summary>Collection of alpha masks that needs applying</summary>
  188. public Dictionary<VisualAlphaParam, float> AlphaMasks;
  189. /// <summary>Tint that should be applied to the texture</summary>
  190. public Color4 Color;
  191. /// <summary>Where on avatar does this texture belong</summary>
  192. public AvatarTextureIndex TextureIndex;
  193. public override string ToString()
  194. {
  195. return String.Format("TextureID: {0}, Texture: {1}",
  196. TextureID, Texture != null ? Texture.AssetData.Length + " bytes" : "(null)");
  197. }
  198. }
  199. #endregion Structs / Classes
  200. #region Event delegates, Raise Events
  201. /// <summary>The event subscribers. null if no subcribers</summary>
  202. private EventHandler<AgentWearablesReplyEventArgs> m_AgentWearablesReply;
  203. /// <summary>Raises the AgentWearablesReply event</summary>
  204. /// <param name="e">An AgentWearablesReplyEventArgs object containing the
  205. /// data returned from the data server</param>
  206. protected virtual void OnAgentWearables(AgentWearablesReplyEventArgs e)
  207. {
  208. EventHandler<AgentWearablesReplyEventArgs> handler = m_AgentWearablesReply;
  209. if (handler != null)
  210. handler(this, e);
  211. }
  212. /// <summary>Thread sync lock object</summary>
  213. private readonly object m_AgentWearablesLock = new object();
  214. /// <summary>Triggered when an AgentWearablesUpdate packet is received,
  215. /// telling us what our avatar is currently wearing
  216. /// <see cref="RequestAgentWearables"/> request.</summary>
  217. public event EventHandler<AgentWearablesReplyEventArgs> AgentWearablesReply
  218. {
  219. add { lock (m_AgentWearablesLock) { m_AgentWearablesReply += value; } }
  220. remove { lock (m_AgentWearablesLock) { m_AgentWearablesReply -= value; } }
  221. }
  222. /// <summary>The event subscribers. null if no subcribers</summary>
  223. private EventHandler<AgentCachedBakesReplyEventArgs> m_AgentCachedBakesReply;
  224. /// <summary>Raises the CachedBakesReply event</summary>
  225. /// <param name="e">An AgentCachedBakesReplyEventArgs object containing the
  226. /// data returned from the data server AgentCachedTextureResponse</param>
  227. protected virtual void OnAgentCachedBakes(AgentCachedBakesReplyEventArgs e)
  228. {
  229. EventHandler<AgentCachedBakesReplyEventArgs> handler = m_AgentCachedBakesReply;
  230. if (handler != null)
  231. handler(this, e);
  232. }
  233. /// <summary>Thread sync lock object</summary>
  234. private readonly object m_AgentCachedBakesLock = new object();
  235. /// <summary>Raised when an AgentCachedTextureResponse packet is
  236. /// received, giving a list of cached bakes that were found on the
  237. /// simulator
  238. /// <seealso cref="RequestCachedBakes"/> request.</summary>
  239. public event EventHandler<AgentCachedBakesReplyEventArgs> CachedBakesReply
  240. {
  241. add { lock (m_AgentCachedBakesLock) { m_AgentCachedBakesReply += value; } }
  242. remove { lock (m_AgentCachedBakesLock) { m_AgentCachedBakesReply -= value; } }
  243. }
  244. /// <summary>The event subscribers. null if no subcribers</summary>
  245. private EventHandler<AppearanceSetEventArgs> m_AppearanceSet;
  246. /// <summary>Raises the AppearanceSet event</summary>
  247. /// <param name="e">An AppearanceSetEventArgs object indicating if the operatin was successfull</param>
  248. protected virtual void OnAppearanceSet(AppearanceSetEventArgs e)
  249. {
  250. EventHandler<AppearanceSetEventArgs> handler = m_AppearanceSet;
  251. if (handler != null)
  252. handler(this, e);
  253. }
  254. /// <summary>Thread sync lock object</summary>
  255. private readonly object m_AppearanceSetLock = new object();
  256. /// <summary>
  257. /// Raised when appearance data is sent to the simulator, also indicates
  258. /// the main appearance thread is finished.
  259. /// </summary>
  260. /// <seealso cref="RequestAgentSetAppearance"/> request.
  261. public event EventHandler<AppearanceSetEventArgs> AppearanceSet
  262. {
  263. add { lock (m_AppearanceSetLock) { m_AppearanceSet += value; } }
  264. remove { lock (m_AppearanceSetLock) { m_AppearanceSet -= value; } }
  265. }
  266. /// <summary>The event subscribers. null if no subcribers</summary>
  267. private EventHandler<RebakeAvatarTexturesEventArgs> m_RebakeAvatarReply;
  268. /// <summary>Raises the RebakeAvatarRequested event</summary>
  269. /// <param name="e">An RebakeAvatarTexturesEventArgs object containing the
  270. /// data returned from the data server</param>
  271. protected virtual void OnRebakeAvatar(RebakeAvatarTexturesEventArgs e)
  272. {
  273. EventHandler<RebakeAvatarTexturesEventArgs> handler = m_RebakeAvatarReply;
  274. if (handler != null)
  275. handler(this, e);
  276. }
  277. /// <summary>Thread sync lock object</summary>
  278. private readonly object m_RebakeAvatarLock = new object();
  279. /// <summary>
  280. /// Triggered when the simulator requests the agent rebake its appearance.
  281. /// </summary>
  282. /// <seealso cref="RebakeAvatarRequest"/>
  283. public event EventHandler<RebakeAvatarTexturesEventArgs> RebakeAvatarRequested
  284. {
  285. add { lock (m_RebakeAvatarLock) { m_RebakeAvatarReply += value; } }
  286. remove { lock (m_RebakeAvatarLock) { m_RebakeAvatarReply -= value; } }
  287. }
  288. #endregion
  289. #region Properties and public fields
  290. /// <summary>
  291. /// Returns true if AppearanceManager is busy and trying to set or change appearance will fail
  292. /// </summary>
  293. public bool ManagerBusy
  294. {
  295. get
  296. {
  297. return AppearanceThreadRunning != 0;
  298. }
  299. }
  300. /// <summary>Visual parameters last sent to the sim</summary>
  301. public byte[] MyVisualParameters = null;
  302. /// <summary>Textures about this client sent to the sim</summary>
  303. public Primitive.TextureEntry MyTextures = null;
  304. #endregion Properties
  305. #region Private Members
  306. /// <summary>A cache of wearables currently being worn</summary>
  307. private Dictionary<WearableType, WearableData> Wearables = new Dictionary<WearableType, WearableData>();
  308. /// <summary>A cache of textures currently being worn</summary>
  309. private TextureData[] Textures = new TextureData[(int)AvatarTextureIndex.NumberOfEntries];
  310. /// <summary>Incrementing serial number for AgentCachedTexture packets</summary>
  311. private int CacheCheckSerialNum = -1;
  312. /// <summary>Incrementing serial number for AgentSetAppearance packets</summary>
  313. private int SetAppearanceSerialNum = 0;
  314. /// <summary>Indicates whether or not the appearance thread is currently
  315. /// running, to prevent multiple appearance threads from running
  316. /// simultaneously</summary>
  317. private int AppearanceThreadRunning = 0;
  318. /// <summary>Reference to our agent</summary>
  319. private GridClient Client;
  320. /// <summary>
  321. /// Timer used for delaying rebake on changing outfit
  322. /// </summary>
  323. private Timer RebakeScheduleTimer;
  324. /// <summary>
  325. /// Main appearance thread
  326. /// </summary>
  327. private Thread AppearanceThread;
  328. #endregion Private Members
  329. /// <summary>
  330. /// Default constructor
  331. /// </summary>
  332. /// <param name="client">A reference to our agent</param>
  333. public AppearanceManager(GridClient client)
  334. {
  335. Client = client;
  336. Client.Network.RegisterCallback(PacketType.AgentWearablesUpdate, AgentWearablesUpdateHandler);
  337. Client.Network.RegisterCallback(PacketType.AgentCachedTextureResponse, AgentCachedTextureResponseHandler);
  338. Client.Network.RegisterCallback(PacketType.RebakeAvatarTextures, RebakeAvatarTexturesHandler);
  339. Client.Network.EventQueueRunning += Network_OnEventQueueRunning;
  340. Client.Network.Disconnected += Network_OnDisconnected;
  341. }
  342. #region Publics Methods
  343. /// <summary>
  344. /// Obsolete method for setting appearance. This function no longer does anything.
  345. /// Use RequestSetAppearance() to manually start the appearance thread
  346. /// </summary>
  347. [Obsolete("Appearance is now handled automatically")]
  348. public void SetPreviousAppearance()
  349. {
  350. }
  351. /// <summary>
  352. /// Obsolete method for setting appearance. This function no longer does anything.
  353. /// Use RequestSetAppearance() to manually start the appearance thread
  354. /// </summary>
  355. /// <param name="allowBake">Unused parameter</param>
  356. [Obsolete("Appearance is now handled automatically")]
  357. public void SetPreviousAppearance(bool allowBake)
  358. {
  359. }
  360. /// <summary>
  361. /// Starts the appearance setting thread
  362. /// </summary>
  363. public void RequestSetAppearance()
  364. {
  365. RequestSetAppearance(false);
  366. }
  367. /// <summary>
  368. /// Starts the appearance setting thread
  369. /// </summary>
  370. /// <param name="forceRebake">True to force rebaking, otherwise false</param>
  371. public void RequestSetAppearance(bool forceRebake)
  372. {
  373. if (Interlocked.CompareExchange(ref AppearanceThreadRunning, 1, 0) != 0)
  374. {
  375. Logger.Log("Appearance thread is already running, skipping", Helpers.LogLevel.Warning);
  376. return;
  377. }
  378. // If we have an active delayed scheduled appearance bake, we dispose of it
  379. if (RebakeScheduleTimer != null)
  380. {
  381. RebakeScheduleTimer.Dispose();
  382. RebakeScheduleTimer = null;
  383. }
  384. // This is the first time setting appearance, run through the entire sequence
  385. AppearanceThread = new Thread(
  386. delegate()
  387. {
  388. bool success = true;
  389. try
  390. {
  391. if (forceRebake)
  392. {
  393. // Set all of the baked textures to UUID.Zero to force rebaking
  394. for (int bakedIndex = 0; bakedIndex < BAKED_TEXTURE_COUNT; bakedIndex++)
  395. Textures[(int)BakeTypeToAgentTextureIndex((BakeType)bakedIndex)].TextureID = UUID.Zero;
  396. }
  397. if (SetAppearanceSerialNum == 0)
  398. {
  399. // Fetch a list of the current agent wearables
  400. if (!GetAgentWearables())
  401. {
  402. Logger.Log("Failed to retrieve a list of current agent wearables, appearance cannot be set",
  403. Helpers.LogLevel.Error, Client);
  404. throw new Exception("Failed to retrieve a list of current agent wearables, appearance cannot be set");
  405. }
  406. }
  407. // Download and parse all of the agent wearables
  408. if (!DownloadWearables())
  409. {
  410. success = false;
  411. Logger.Log("One or more agent wearables failed to download, appearance will be incomplete",
  412. Helpers.LogLevel.Warning, Client);
  413. }
  414. // If this is the first time setting appearance and we're not forcing rebakes, check the server
  415. // for cached bakes
  416. if (SetAppearanceSerialNum == 0 && !forceRebake)
  417. {
  418. // Compute hashes for each bake layer and compare against what the simulator currently has
  419. if (!GetCachedBakes())
  420. {
  421. Logger.Log("Failed to get a list of cached bakes from the simulator, appearance will be rebaked",
  422. Helpers.LogLevel.Warning, Client);
  423. }
  424. }
  425. // Download textures, compute bakes, and upload for any cache misses
  426. if (!CreateBakes())
  427. {
  428. success = false;
  429. Logger.Log("Failed to create or upload one or more bakes, appearance will be incomplete",
  430. Helpers.LogLevel.Warning, Client);
  431. }
  432. // Send the appearance packet
  433. RequestAgentSetAppearance();
  434. }
  435. catch (Exception)
  436. {
  437. success = false;
  438. }
  439. finally
  440. {
  441. AppearanceThreadRunning = 0;
  442. OnAppearanceSet(new AppearanceSetEventArgs(success));
  443. }
  444. }
  445. );
  446. AppearanceThread.Name = "Appearance";
  447. AppearanceThread.IsBackground = true;
  448. AppearanceThread.Start();
  449. }
  450. /// <summary>
  451. /// Ask the server what textures our agent is currently wearing
  452. /// </summary>
  453. public void RequestAgentWearables()
  454. {
  455. AgentWearablesRequestPacket request = new AgentWearablesRequestPacket();
  456. request.AgentData.AgentID = Client.Self.AgentID;
  457. request.AgentData.SessionID = Client.Self.SessionID;
  458. Client.Network.SendPacket(request);
  459. }
  460. /// <summary>
  461. /// Build hashes out of the texture assetIDs for each baking layer to
  462. /// ask the simulator whether it has cached copies of each baked texture
  463. /// </summary>
  464. public void RequestCachedBakes()
  465. {
  466. List<AgentCachedTexturePacket.WearableDataBlock> hashes = new List<AgentCachedTexturePacket.WearableDataBlock>();
  467. // Build hashes for each of the bake layers from the individual components
  468. lock (Wearables)
  469. {
  470. for (int bakedIndex = 0; bakedIndex < BAKED_TEXTURE_COUNT; bakedIndex++)
  471. {
  472. // Don't do a cache request for a skirt bake if we're not wearing a skirt
  473. if (bakedIndex == (int)BakeType.Skirt && !Wearables.ContainsKey(WearableType.Skirt))
  474. continue;
  475. // Build a hash of all the texture asset IDs in this baking layer
  476. UUID hash = UUID.Zero;
  477. for (int wearableIndex = 0; wearableIndex < WEARABLES_PER_LAYER; wearableIndex++)
  478. {
  479. WearableType type = WEARABLE_BAKE_MAP[bakedIndex][wearableIndex];
  480. WearableData wearable;
  481. if (type != WearableType.Invalid && Wearables.TryGetValue(type, out wearable))
  482. hash ^= wearable.AssetID;
  483. }
  484. if (hash != UUID.Zero)
  485. {
  486. // Hash with our secret value for this baked layer
  487. hash ^= BAKED_TEXTURE_HASH[bakedIndex];
  488. // Add this to the list of hashes to send out
  489. AgentCachedTexturePacket.WearableDataBlock block = new AgentCachedTexturePacket.WearableDataBlock();
  490. block.ID = hash;
  491. block.TextureIndex = (byte)bakedIndex;
  492. hashes.Add(block);
  493. Logger.DebugLog("Checking cache for " + (BakeType)block.TextureIndex + ", hash=" + block.ID, Client);
  494. }
  495. }
  496. }
  497. // Only send the packet out if there's something to check
  498. if (hashes.Count > 0)
  499. {
  500. AgentCachedTexturePacket cache = new AgentCachedTexturePacket();
  501. cache.AgentData.AgentID = Client.Self.AgentID;
  502. cache.AgentData.SessionID = Client.Self.SessionID;
  503. cache.AgentData.SerialNum = Interlocked.Increment(ref CacheCheckSerialNum);
  504. cache.WearableData = hashes.ToArray();
  505. Client.Network.SendPacket(cache);
  506. }
  507. }
  508. /// <summary>
  509. /// Returns the AssetID of the asset that is currently being worn in a
  510. /// given WearableType slot
  511. /// </summary>
  512. /// <param name="type">WearableType slot to get the AssetID for</param>
  513. /// <returns>The UUID of the asset being worn in the given slot, or
  514. /// UUID.Zero if no wearable is attached to the given slot or wearables
  515. /// have not been downloaded yet</returns>
  516. public UUID GetWearableAsset(WearableType type)
  517. {
  518. WearableData wearable;
  519. if (Wearables.TryGetValue(type, out wearable))
  520. return wearable.AssetID;
  521. else
  522. return UUID.Zero;
  523. }
  524. /// <summary>
  525. /// Add a wearable to the current outfit and set appearance
  526. /// </summary>
  527. /// <param name="wearableItem">Wearable to be added to the outfit</param>
  528. public void AddToOutfit(InventoryItem wearableItem)
  529. {
  530. List<InventoryItem> wearableItems = new List<InventoryItem> { wearableItem };
  531. AddToOutfit(wearableItems);
  532. }
  533. /// <summary>
  534. /// Add a list of wearables to the current outfit and set appearance
  535. /// </summary>
  536. /// <param name="wearableItems">List of wearable inventory items to
  537. /// be added to the outfit</param>
  538. public void AddToOutfit(List<InventoryItem> wearableItems)
  539. {
  540. List<InventoryWearable> wearables = new List<InventoryWearable>();
  541. List<InventoryItem> attachments = new List<InventoryItem>();
  542. for (int i = 0; i < wearableItems.Count; i++)
  543. {
  544. InventoryItem item = wearableItems[i];
  545. if (item is InventoryWearable)
  546. wearables.Add((InventoryWearable)item);
  547. else if (item is InventoryAttachment || item is InventoryObject)
  548. attachments.Add(item);
  549. }
  550. lock (Wearables)
  551. {
  552. // Add the given wearables to the wearables collection
  553. for (int i = 0; i < wearables.Count; i++)
  554. {
  555. InventoryWearable wearableItem = wearables[i];
  556. WearableData wd = new WearableData();
  557. wd.AssetID = wearableItem.AssetUUID;
  558. wd.AssetType = wearableItem.AssetType;
  559. wd.ItemID = wearableItem.UUID;
  560. wd.WearableType = wearableItem.WearableType;
  561. Wearables[wearableItem.WearableType] = wd;
  562. }
  563. }
  564. if (attachments.Count > 0)
  565. {
  566. AddAttachments(attachments, false, false);
  567. }
  568. if (wearables.Count > 0)
  569. {
  570. SendAgentIsNowWearing();
  571. DelayedRequestSetAppearance();
  572. }
  573. }
  574. /// <summary>
  575. /// Remove a wearable from the current outfit and set appearance
  576. /// </summary>
  577. /// <param name="wearableItem">Wearable to be removed from the outfit</param>
  578. public void RemoveFromOutfit(InventoryItem wearableItem)
  579. {
  580. List<InventoryItem> wearableItems = new List<InventoryItem>();
  581. wearableItems.Add(wearableItem);
  582. RemoveFromOutfit(wearableItems);
  583. }
  584. /// <summary>
  585. /// Removes a list of wearables from the current outfit and set appearance
  586. /// </summary>
  587. /// <param name="wearableItems">List of wearable inventory items to
  588. /// be removed from the outfit</param>
  589. public void RemoveFromOutfit(List<InventoryItem> wearableItems)
  590. {
  591. List<InventoryWearable> wearables = new List<InventoryWearable>();
  592. List<InventoryItem> attachments = new List<InventoryItem>();
  593. for (int i = 0; i < wearableItems.Count; i++)
  594. {
  595. InventoryItem item = wearableItems[i];
  596. if (item is InventoryWearable)
  597. wearables.Add((InventoryWearable)item);
  598. else if (item is InventoryAttachment || item is InventoryObject)
  599. attachments.Add(item);
  600. }
  601. bool needSetAppearance = false;
  602. lock (Wearables)
  603. {
  604. // Remove the given wearables from the wearables collection
  605. for (int i = 0; i < wearables.Count; i++)
  606. {
  607. InventoryWearable wearableItem = wearables[i];
  608. if (wearables[i].AssetType != AssetType.Bodypart // Remove if it's not a body part
  609. && Wearables.ContainsKey(wearableItem.WearableType) // And we have that wearabe type
  610. && Wearables[wearableItem.WearableType].ItemID == wearableItem.UUID // And we are wearing it
  611. )
  612. {
  613. Wearables.Remove(wearableItem.WearableType);
  614. needSetAppearance = true;
  615. }
  616. }
  617. }
  618. for (int i = 0; i < attachments.Count; i++)
  619. {
  620. Detach(attachments[i].UUID);
  621. }
  622. if (needSetAppearance)
  623. {
  624. SendAgentIsNowWearing();
  625. DelayedRequestSetAppearance();
  626. }
  627. }
  628. /// <summary>
  629. /// Replace the current outfit with a list of wearables and set appearance
  630. /// </summary>
  631. /// <param name="wearableItems">List of wearable inventory items that
  632. /// define a new outfit</param>
  633. public void ReplaceOutfit(List<InventoryItem> wearableItems)
  634. {
  635. ReplaceOutfit(wearableItems, true);
  636. }
  637. /// <summary>
  638. /// Replace the current outfit with a list of wearables and set appearance
  639. /// </summary>
  640. /// <param name="wearableItems">List of wearable inventory items that
  641. /// define a new outfit</param>
  642. /// <param name="safe">Check if we have all body parts, set this to false only
  643. /// if you know what you're doing</param>
  644. public void ReplaceOutfit(List<InventoryItem> wearableItems, bool safe)
  645. {
  646. List<InventoryWearable> wearables = new List<InventoryWearable>();
  647. List<InventoryItem> attachments = new List<InventoryItem>();
  648. for (int i = 0; i < wearableItems.Count; i++)
  649. {
  650. InventoryItem item = wearableItems[i];
  651. if (item is InventoryWearable)
  652. wearables.Add((InventoryWearable)item);
  653. else if (item is InventoryAttachment || item is InventoryObject)
  654. attachments.Add(item);
  655. }
  656. if (safe)
  657. {
  658. // If we don't already have a the current agent wearables downloaded, updating to a
  659. // new set of wearables that doesn't have all of the bodyparts can leave the avatar
  660. // in an inconsistent state. If any bodypart entries are empty, we need to fetch the
  661. // current wearables first
  662. bool needsCurrentWearables = false;
  663. lock (Wearables)
  664. {
  665. for (int i = 0; i < WEARABLE_COUNT; i++)
  666. {
  667. WearableType wearableType = (WearableType)i;
  668. if (WearableTypeToAssetType(wearableType) == AssetType.Bodypart && !Wearables.ContainsKey(wearableType))
  669. {
  670. needsCurrentWearables = true;
  671. break;
  672. }
  673. }
  674. }
  675. if (needsCurrentWearables && !GetAgentWearables())
  676. {
  677. Logger.Log("Failed to fetch the current agent wearables, cannot safely replace outfit",
  678. Helpers.LogLevel.Error);
  679. return;
  680. }
  681. }
  682. // Replace our local Wearables collection, send the packet(s) to update our
  683. // attachments, tell sim what we are wearing now, and start the baking process
  684. if (!safe)
  685. {
  686. SetAppearanceSerialNum++;
  687. }
  688. ReplaceOutfit(wearables);
  689. AddAttachments(attachments, true, false);
  690. SendAgentIsNowWearing();
  691. DelayedRequestSetAppearance();
  692. }
  693. /// <summary>
  694. /// Checks if an inventory item is currently being worn
  695. /// </summary>
  696. /// <param name="item">The inventory item to check against the agent
  697. /// wearables</param>
  698. /// <returns>The WearableType slot that the item is being worn in,
  699. /// or WearbleType.Invalid if it is not currently being worn</returns>
  700. public WearableType IsItemWorn(InventoryItem item)
  701. {
  702. lock (Wearables)
  703. {
  704. foreach (KeyValuePair<WearableType, WearableData> entry in Wearables)
  705. {
  706. if (entry.Value.ItemID == item.UUID)
  707. return entry.Key;
  708. }
  709. }
  710. return WearableType.Invalid;
  711. }
  712. /// <summary>
  713. /// Returns a copy of the agents currently worn wearables
  714. /// </summary>
  715. /// <returns>A copy of the agents currently worn wearables</returns>
  716. /// <remarks>Avoid calling this function multiple times as it will make
  717. /// a copy of all of the wearable data each time</remarks>
  718. public Dictionary<WearableType, WearableData> GetWearables()
  719. {
  720. lock (Wearables)
  721. return new Dictionary<WearableType, WearableData>(Wearables);
  722. }
  723. /// <summary>
  724. /// Calls either <seealso cref="ReplaceOutfit"/> or
  725. /// <seealso cref="AddToOutfit"/> depending on the value of
  726. /// replaceItems
  727. /// </summary>
  728. /// <param name="wearables">List of wearable inventory items to add
  729. /// to the outfit or become a new outfit</param>
  730. /// <param name="replaceItems">True to replace existing items with the
  731. /// new list of items, false to add these items to the existing outfit</param>
  732. public void WearOutfit(List<InventoryBase> wearables, bool replaceItems)
  733. {
  734. List<InventoryItem> wearableItems = new List<InventoryItem>(wearables.Count);
  735. for (int i = 0; i < wearables.Count; i++)
  736. {
  737. if (wearables[i] is InventoryItem)
  738. wearableItems.Add((InventoryItem)wearables[i]);
  739. }
  740. if (replaceItems)
  741. ReplaceOutfit(wearableItems);
  742. else
  743. AddToOutfit(wearableItems);
  744. }
  745. #endregion Publics Methods
  746. #region Attachments
  747. /// <summary>
  748. /// Adds a list of attachments to our agent
  749. /// </summary>
  750. /// <param name="attachments">A List containing the attachments to add</param>
  751. /// <param name="removeExistingFirst">If true, tells simulator to remove existing attachment
  752. /// first</param>
  753. public void AddAttachments(List<InventoryItem> attachments, bool removeExistingFirst)
  754. {
  755. AddAttachments(attachments, removeExistingFirst, true);
  756. }
  757. /// <summary>
  758. /// Adds a list of attachments to our agent
  759. /// </summary>
  760. /// <param name="attachments">A List containing the attachments to add</param>
  761. /// <param name="removeExistingFirst">If true, tells simulator to remove existing attachment
  762. /// <param name="replace">If true replace existing attachment on this attachment point, otherwise add to it (multi-attachments)</param>
  763. /// first</param>
  764. public void AddAttachments(List<InventoryItem> attachments, bool removeExistingFirst, bool replace)
  765. {
  766. // Use RezMultipleAttachmentsFromInv to clear out current attachments, and attach new ones
  767. RezMultipleAttachmentsFromInvPacket attachmentsPacket = new RezMultipleAttachmentsFromInvPacket();
  768. attachmentsPacket.AgentData.AgentID = Client.Self.AgentID;
  769. attachmentsPacket.AgentData.SessionID = Client.Self.SessionID;
  770. attachmentsPacket.HeaderData.CompoundMsgID = UUID.Random();
  771. attachmentsPacket.HeaderData.FirstDetachAll = removeExistingFirst;
  772. attachmentsPacket.HeaderData.TotalObjects = (byte)attachments.Count;
  773. attachmentsPacket.ObjectData = new RezMultipleAttachmentsFromInvPacket.ObjectDataBlock[attachments.Count];
  774. for (int i = 0; i < attachments.Count; i++)
  775. {
  776. if (attachments[i] is InventoryAttachment)
  777. {
  778. InventoryAttachment attachment = (InventoryAttachment)attachments[i];
  779. attachmentsPacket.ObjectData[i] = new RezMultipleAttachmentsFromInvPacket.ObjectDataBlock();
  780. attachmentsPacket.ObjectData[i].AttachmentPt = replace ? (byte)attachment.AttachmentPoint : (byte)(ATTACHMENT_ADD | (byte)attachment.AttachmentPoint);
  781. attachmentsPacket.ObjectData[i].EveryoneMask = (uint)attachment.Permissions.EveryoneMask;
  782. attachmentsPacket.ObjectData[i].GroupMask = (uint)attachment.Permissions.GroupMask;
  783. attachmentsPacket.ObjectData[i].ItemFlags = (uint)attachment.Flags;
  784. attachmentsPacket.ObjectData[i].ItemID = attachment.UUID;
  785. attachmentsPacket.ObjectData[i].Name = Utils.StringToBytes(attachment.Name);
  786. attachmentsPacket.ObjectData[i].Description = Utils.StringToBytes(attachment.Description);
  787. attachmentsPacket.ObjectData[i].NextOwnerMask = (uint)attachment.Permissions.NextOwnerMask;
  788. attachmentsPacket.ObjectData[i].OwnerID = attachment.OwnerID;
  789. }
  790. else if (attachments[i] is InventoryObject)
  791. {
  792. InventoryObject attachment = (InventoryObject)attachments[i];
  793. attachmentsPacket.ObjectData[i] = new RezMultipleAttachmentsFromInvPacket.ObjectDataBlock();
  794. attachmentsPacket.ObjectData[i].AttachmentPt = replace ? (byte)0 : ATTACHMENT_ADD;
  795. attachmentsPacket.ObjectData[i].EveryoneMask = (uint)attachment.Permissions.EveryoneMask;
  796. attachmentsPacket.ObjectData[i].GroupMask = (uint)attachment.Permissions.GroupMask;
  797. attachmentsPacket.ObjectData[i].ItemFlags = (uint)attachment.Flags;
  798. attachmentsPacket.ObjectData[i].ItemID = attachment.UUID;
  799. attachmentsPacket.ObjectData[i].Name = Utils.StringToBytes(attachment.Name);
  800. attachmentsPacket.ObjectData[i].Description = Utils.StringToBytes(attachment.Description);
  801. attachmentsPacket.ObjectData[i].NextOwnerMask = (uint)attachment.Permissions.NextOwnerMask;
  802. attachmentsPacket.ObjectData[i].OwnerID = attachment.OwnerID;
  803. }
  804. else
  805. {
  806. Logger.Log("Cannot attach inventory item " + attachments[i].Name, Helpers.LogLevel.Warning, Client);
  807. }
  808. }
  809. Client.Network.SendPacket(attachmentsPacket);
  810. }
  811. /// <summary>
  812. /// Attach an item to our agent at a specific attach point
  813. /// </summary>
  814. /// <param name="item">A <seealso cref="OpenMetaverse.InventoryItem"/> to attach</param>
  815. /// <param name="attachPoint">the <seealso cref="OpenMetaverse.AttachmentPoint"/> on the avatar
  816. /// to attach the item to</param>
  817. public void Attach(InventoryItem item, AttachmentPoint attachPoint)
  818. {
  819. Attach(item, attachPoint, true);
  820. }
  821. /// <summary>
  822. /// Attach an item to our agent at a specific attach point
  823. /// </summary>
  824. /// <param name="item">A <seealso cref="OpenMetaverse.InventoryItem"/> to attach</param>
  825. /// <param name="attachPoint">the <seealso cref="OpenMetaverse.AttachmentPoint"/> on the avatar
  826. /// <param name="replace">If true replace existing attachment on this attachment point, otherwise add to it (multi-attachments)</param>
  827. /// to attach the item to</param>
  828. public void Attach(InventoryItem item, AttachmentPoint attachPoint, bool replace)
  829. {
  830. Attach(item.UUID, item.OwnerID, item.Name, item.Description, item.Permissions, item.Flags,
  831. attachPoint, replace);
  832. }
  833. /// <summary>
  834. /// Attach an item to our agent specifying attachment details
  835. /// </summary>
  836. /// <param name="itemID">The <seealso cref="OpenMetaverse.UUID"/> of the item to attach</param>
  837. /// <param name="ownerID">The <seealso cref="OpenMetaverse.UUID"/> attachments owner</param>
  838. /// <param name="name">The name of the attachment</param>
  839. /// <param name="description">The description of the attahment</param>
  840. /// <param name="perms">The <seealso cref="OpenMetaverse.Permissions"/> to apply when attached</param>
  841. /// <param name="itemFlags">The <seealso cref="OpenMetaverse.InventoryItemFlags"/> of the attachment</param>
  842. /// <param name="attachPoint">The <seealso cref="OpenMetaverse.AttachmentPoint"/> on the agent
  843. /// to attach the item to</param>
  844. public void Attach(UUID itemID, UUID ownerID, string name, string description,
  845. Permissions perms, uint itemFlags, AttachmentPoint attachPoint)
  846. {
  847. Attach(itemID, ownerID, name, description, perms, itemFlags, attachPoint, true);
  848. }
  849. /// <summary>
  850. /// Attach an item to our agent specifying attachment details
  851. /// </summary>
  852. /// <param name="itemID">The <seealso cref="OpenMetaverse.UUID"/> of the item to attach</param>
  853. /// <param name="ownerID">The <seealso cref="OpenMetaverse.UUID"/> attachments owner</param>
  854. /// <param name="name">The name of the attachment</param>
  855. /// <param name="description">The description of the attahment</param>
  856. /// <param name="perms">The <seealso cref="OpenMetaverse.Permissions"/> to apply when attached</param>
  857. /// <param name="itemFlags">The <seealso cref="OpenMetaverse.InventoryItemFlags"/> of the attachment</param>
  858. /// <param name="attachPoint">The <seealso cref="OpenMetaverse.AttachmentPoint"/> on the agent
  859. /// <param name="replace">If true replace existing attachment on this attachment point, otherwise add to it (multi-attachments)</param>
  860. /// to attach the item to</param>
  861. public void Attach(UUID itemID, UUID ownerID, string name, string description,
  862. Permissions perms, uint itemFlags, AttachmentPoint attachPoint, bool replace)
  863. {
  864. // TODO: At some point it might be beneficial to have AppearanceManager track what we
  865. // are currently wearing for attachments to make enumeration and detachment easier
  866. RezSingleAttachmentFromInvPacket attach = new RezSingleAttachmentFromInvPacket();
  867. attach.AgentData.AgentID = Client.Self.AgentID;
  868. attach.AgentData.SessionID = Client.Self.SessionID;
  869. attach.ObjectData.AttachmentPt = replace ? (byte)attachPoint : (byte)(ATTACHMENT_ADD | (byte)attachPoint);
  870. attach.ObjectData.Description = Utils.StringToBytes(description);
  871. attach.ObjectData.EveryoneMask = (uint)perms.EveryoneMask;
  872. attach.ObjectData.GroupMask = (uint)perms.GroupMask;
  873. attach.ObjectData.ItemFlags = itemFlags;
  874. attach.ObjectData.ItemID = itemID;
  875. attach.ObjectData.Name = Utils.StringToBytes(name);
  876. attach.ObjectData.NextOwnerMask = (uint)perms.NextOwnerMask;
  877. attach.ObjectData.OwnerID = ownerID;
  878. Client.Network.SendPacket(attach);
  879. }
  880. /// <summary>
  881. /// Detach an item from our agent using an <seealso cref="OpenMetaverse.InventoryItem"/> object
  882. /// </summary>
  883. /// <param name="item">An <seealso cref="OpenMetaverse.InventoryItem"/> object</param>
  884. public void Detach(InventoryItem item)
  885. {
  886. Detach(item.UUID);
  887. }
  888. /// <summary>
  889. /// Detach an item from our agent
  890. /// </summary>
  891. /// <param name="itemID">The inventory itemID of the item to detach</param>
  892. public void Detach(UUID itemID)
  893. {
  894. DetachAttachmentIntoInvPacket detach = new DetachAttachmentIntoInvPacket();
  895. detach.ObjectData.AgentID = Client.Self.AgentID;
  896. detach.ObjectData.ItemID = itemID;
  897. Client.Network.SendPacket(detach);
  898. }
  899. #endregion Attachments
  900. #region Appearance Helpers
  901. /// <summary>
  902. /// Inform the sim which wearables are part of our current outfit
  903. /// </summary>
  904. private void SendAgentIsNowWearing()
  905. {
  906. AgentIsNowWearingPacket wearing = new AgentIsNowWearingPacket();
  907. wearing.AgentData.AgentID = Client.Self.AgentID;
  908. wearing.AgentData.SessionID = Client.Self.SessionID;
  909. wearing.WearableData = new AgentIsNowWearingPacket.WearableDataBlock[WEARABLE_COUNT];
  910. lock (Wearables)
  911. {
  912. for (int i = 0; i < WEARABLE_COUNT; i++)
  913. {
  914. WearableType type = (WearableType)i;
  915. wearing.WearableData[i] = new AgentIsNowWearingPacket.WearableDataBlock();
  916. wearing.WearableData[i].WearableType = (byte)i;
  917. if (Wearables.ContainsKey(type))
  918. wearing.WearableData[i].ItemID = Wearables[type].ItemID;
  919. else
  920. wearing.WearableData[i].ItemID = UUID.Zero;
  921. }
  922. }
  923. Client.Network.SendPacket(wearing);
  924. }
  925. /// <summary>
  926. /// Replaces the Wearables collection with a list of new wearable items
  927. /// </summary>
  928. /// <param name="wearableItems">Wearable items to replace the Wearables collection with</param>
  929. private void ReplaceOutfit(List<InventoryWearable> wearableItems)
  930. {
  931. Dictionary<WearableType, WearableData> newWearables = new Dictionary<WearableType, WearableData>();
  932. lock (Wearables)
  933. {
  934. // Preserve body parts from the previous set of wearables. They may be overwritten,
  935. // but cannot be missing in the new set
  936. foreach (KeyValuePair<WearableType, WearableData> entry in Wearables)
  937. {
  938. if (entry.Value.AssetType == AssetType.Bodypart)
  939. newWearables[entry.Key] = entry.Value;
  940. }
  941. // Add the given wearables to the new wearables collection
  942. for (int i = 0; i < wearableItems.Count; i++)
  943. {
  944. InventoryWearable wearableItem = wearableItems[i];
  945. WearableData wd = new WearableData();
  946. wd.AssetID = wearableItem.AssetUUID;
  947. wd.AssetType = wearableItem.AssetType;
  948. wd.ItemID = wearableItem.UUID;
  949. wd.WearableType = wearableItem.WearableType;
  950. newWearables[wearableItem.WearableType] = wd;
  951. }
  952. // Replace the Wearables collection
  953. Wearables = newWearables;
  954. }
  955. }
  956. /// <summary>
  957. /// Calculates base color/tint for a specific wearable
  958. /// based on its params
  959. /// </summary>
  960. /// <param name="param">All the color info gathered from wearable's VisualParams
  961. /// passed as list of ColorParamInfo tuples</param>
  962. /// <returns>Base color/tint for the wearable</returns>
  963. private Color4 GetColorFromParams(List<ColorParamInfo> param)
  964. {
  965. // Start off with a blank slate, black, fully transparent
  966. Color4 res = new Color4(0, 0, 0, 0);
  967. // Apply color modification from each color parameter
  968. foreach (ColorParamInfo p in param)
  969. {
  970. int n = p.VisualColorParam.Colors.Length;
  971. Color4 paramColor = new Color4(0, 0, 0, 0);
  972. if (n == 1)
  973. {

Large files files are truncated, but you can click here to view the full file