LLImageManager.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  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 OpenSim 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 C5;
  30. using OpenMetaverse;
  31. using OpenMetaverse.Imaging;
  32. using OpenSim.Framework;
  33. using OpenSim.Region.Framework.Interfaces;
  34. using log4net;
  35. using System.Reflection;
  36. namespace OpenSim.Region.ClientStack.LindenUDP
  37. {
  38. /// <summary>
  39. /// Client image priority + discardlevel sender/manager
  40. /// </summary>
  41. public class LLImageManager
  42. {
  43. /// <summary>
  44. /// Priority Queue for images. Contains lots of data
  45. /// </summary>
  46. private readonly IPriorityQueue<Prio<J2KImage>> pq = new IntervalHeap<Prio<J2KImage>>();
  47. /// <summary>
  48. /// Dictionary of PriorityQueue handles by AssetId
  49. /// </summary>
  50. private readonly Dictionary<UUID, IPriorityQueueHandle<Prio<J2KImage>>> PQHandles =
  51. new Dictionary<UUID, IPriorityQueueHandle<Prio<J2KImage>>>();
  52. private LLClientView m_client;
  53. private readonly IAssetCache m_assetCache;
  54. private bool m_shuttingdown = false;
  55. private readonly IJ2KDecoder m_j2kDecodeModule;
  56. private readonly AssetBase MissingSubstitute;
  57. /// <summary>
  58. /// Client image priority + discardlevel sender/manager
  59. /// </summary>
  60. /// <param name="client">LLClientView of client</param>
  61. /// <param name="pAssetCache">The Asset retrieval system</param>
  62. /// <param name="pJ2kDecodeModule">The Jpeg2000 Decoder</param>
  63. public LLImageManager(LLClientView client, IAssetCache pAssetCache, IJ2KDecoder pJ2kDecodeModule)
  64. {
  65. m_client = client;
  66. m_assetCache = pAssetCache;
  67. if (pAssetCache != null)
  68. MissingSubstitute = pAssetCache.GetAsset(UUID.Parse("5748decc-f629-461c-9a36-a35a221fe21f"), true);
  69. m_j2kDecodeModule = pJ2kDecodeModule;
  70. }
  71. /// <summary>
  72. /// Enqueues a texture request
  73. /// </summary>
  74. /// <param name="req">Request from the client to get a texture</param>
  75. public void EnqueueReq(TextureRequestArgs req)
  76. {
  77. if (m_shuttingdown)
  78. return;
  79. //if (req.RequestType == 1) // avatar body texture!
  80. // return;
  81. AddQueueItem(req.RequestedAssetID, (int)req.Priority + 100000);
  82. //if (pq[PQHandles[req.RequestedAssetID]].data.Missing)
  83. //{
  84. // pq[PQHandles[req.RequestedAssetID]] -= 900000;
  85. //}
  86. //
  87. //if (pq[PQHandles[req.RequestedAssetID]].data.HasData && pq[PQHandles[req.RequestedAssetID]].data.Layers.Length > 0)
  88. //{
  89. //}
  90. pq[PQHandles[req.RequestedAssetID]].data.requestedUUID = req.RequestedAssetID;
  91. pq[PQHandles[req.RequestedAssetID]].data.Priority = (int)req.Priority;
  92. lock (pq[PQHandles[req.RequestedAssetID]].data)
  93. pq[PQHandles[req.RequestedAssetID]].data.Update(req.DiscardLevel, (int)req.PacketNumber);
  94. }
  95. /// <summary>
  96. /// Callback for the asset system
  97. /// </summary>
  98. /// <param name="assetID">UUID of the asset that we have received</param>
  99. /// <param name="asset">AssetBase of the asset that we've received</param>
  100. public void AssetDataCallback(UUID assetID, AssetBase asset)
  101. {
  102. if (m_shuttingdown)
  103. return;
  104. //m_log.Debug("AssetCallback for assetId" + assetID);
  105. if (asset == null || asset.Data == null)
  106. {
  107. lock (pq)
  108. {
  109. //pq[PQHandles[assetID]].data.Missing = true;
  110. pq[PQHandles[assetID]].data.asset = MissingSubstitute;
  111. pq[PQHandles[assetID]].data.Missing = false;
  112. }
  113. }
  114. //else
  115. pq[PQHandles[assetID]].data.asset = asset;
  116. //lock (pq[PQHandles[assetID]].data)
  117. pq[PQHandles[assetID]].data.Update((int)pq[PQHandles[assetID]].data.Priority, pq[PQHandles[assetID]].data.CurrentPacket);
  118. }
  119. /// <summary>
  120. /// Processes the image queue. Pops count elements off and processes them
  121. /// </summary>
  122. /// <param name="count">number of images to peek off the queue</param>
  123. public void ProcessImageQueue(int count)
  124. {
  125. if (m_shuttingdown)
  126. return;
  127. IPriorityQueueHandle<Prio<J2KImage>> h = null;
  128. for (int j = 0; j < count; j++)
  129. {
  130. lock (pq)
  131. {
  132. if (!pq.IsEmpty)
  133. {
  134. //peek off the top
  135. Prio<J2KImage> process = pq.FindMax(out h);
  136. // Do we have the Asset Data?
  137. if (!process.data.HasData)
  138. {
  139. // Did we request the asset data?
  140. if (!process.data.dataRequested)
  141. {
  142. m_assetCache.GetAsset(process.data.requestedUUID, AssetDataCallback, true);
  143. pq[h].data.dataRequested = true;
  144. }
  145. // Is the asset missing?
  146. if (process.data.Missing)
  147. {
  148. //m_client.sendtextur
  149. pq[h] -= 90000;
  150. /*
  151. {
  152. OpenMetaverse.Packets.ImageNotInDatabasePacket imdback =
  153. new OpenMetaverse.Packets.ImageNotInDatabasePacket();
  154. imdback.ImageID =
  155. new OpenMetaverse.Packets.ImageNotInDatabasePacket.ImageIDBlock();
  156. imdback.ImageID.ID = process.data.requestedUUID;
  157. m_client.OutPacket(imdback, ThrottleOutPacketType.Texture);
  158. }
  159. */
  160. // Substitute a blank image
  161. process.data.asset = MissingSubstitute;
  162. process.data.Missing = false;
  163. // If the priority is less then -4billion, the client has forgotten about it.
  164. if (pq[h] < -400000000)
  165. {
  166. RemoveItemFromQueue(pq[h].data.requestedUUID);
  167. continue;
  168. }
  169. }
  170. // Lower the priority to give the next image a chance
  171. pq[h] -= 100000;
  172. }
  173. else if (process.data.HasData)
  174. {
  175. // okay, we've got the data
  176. lock (process.data)
  177. {
  178. if (!process.data.J2KDecode && !process.data.J2KDecodeWaiting)
  179. {
  180. process.data.J2KDecodeWaiting = true;
  181. // Do we have a jpeg decoder?
  182. if (m_j2kDecodeModule != null)
  183. {
  184. // Send it off to the jpeg decoder
  185. m_j2kDecodeModule.decode(process.data.requestedUUID, process.data.Data,
  186. j2kDecodedCallback);
  187. }
  188. else
  189. {
  190. // no module, no layers, full resolution only
  191. j2kDecodedCallback(process.data.AssetId, new OpenJPEG.J2KLayerInfo[0]);
  192. }
  193. } // Are we waiting?
  194. else if (!process.data.J2KDecodeWaiting)
  195. {
  196. // Send more data at a time for higher discard levels
  197. bool done = false;
  198. for (int i = 0; i < (2*(process.data.DiscardLevel) + 1)*2; i++)
  199. if (!process.data.SendPacket(m_client))
  200. {
  201. done = true;
  202. pq[h] -= (500000*i);
  203. break;
  204. }
  205. if (!done)
  206. {
  207. for (int i = 0; i < (2 * (5- process.data.DiscardLevel) + 1) * 2; i++)
  208. if (!process.data.SendPacket(m_client))
  209. {
  210. done = true;
  211. pq[h] -= (500000 * i);
  212. break;
  213. }
  214. }
  215. }
  216. // If the priority is less then -4 billion, the client has forgotten about it, pop it off
  217. if (pq[h] < -400000000)
  218. {
  219. RemoveItemFromQueue(pq[h].data.requestedUUID);
  220. continue;
  221. }
  222. }
  223. //pq[h] = process;
  224. }
  225. // uncomment the following line to see the upper most asset and the priority
  226. //m_log.Debug(process.ToString());
  227. // Lower priority to give the next image a chance to bubble up
  228. pq[h] -= 50000;
  229. }
  230. }
  231. }
  232. }
  233. /// <summary>
  234. /// Callback for when the image has been decoded
  235. /// </summary>
  236. /// <param name="AssetId">The UUID of the Asset</param>
  237. /// <param name="layers">The Jpeg2000 discard level Layer start and end byte offsets Array. 0 elements for failed or no decoder</param>
  238. public void j2kDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers)
  239. {
  240. // are we shutting down? if so, end.
  241. if (m_shuttingdown)
  242. return;
  243. lock (PQHandles)
  244. {
  245. // Update our asset data
  246. if (PQHandles.ContainsKey(AssetId))
  247. {
  248. pq[PQHandles[AssetId]].data.Layers = layers;
  249. pq[PQHandles[AssetId]].data.J2KDecode = true;
  250. pq[PQHandles[AssetId]].data.J2KDecodeWaiting = false;
  251. //lock (pq[PQHandles[AssetId]].data)
  252. pq[PQHandles[AssetId]].data.Update((int)pq[PQHandles[AssetId]].data.Priority, (int)pq[PQHandles[AssetId]].data.CurrentPacket);
  253. // Send the first packet
  254. pq[PQHandles[AssetId]].data.SendPacket(m_client);
  255. }
  256. }
  257. }
  258. /// <summary>
  259. /// This image has had a good life. It's now expired. Remove it off the queue
  260. /// </summary>
  261. /// <param name="AssetId">UUID of asset to remove off the queue</param>
  262. private void RemoveItemFromQueue(UUID AssetId)
  263. {
  264. lock (PQHandles)
  265. {
  266. if (PQHandles.ContainsKey(AssetId))
  267. {
  268. IPriorityQueueHandle<Prio<J2KImage>> h = PQHandles[AssetId];
  269. PQHandles.Remove(AssetId);
  270. pq.Delete(h);
  271. }
  272. }
  273. }
  274. /// <summary>
  275. /// Adds an image to the queue and update priority
  276. /// if the item is already in the queue, just update the priority
  277. /// </summary>
  278. /// <param name="AssetId">UUID of the asset</param>
  279. /// <param name="priority">Priority to set</param>
  280. private void AddQueueItem(UUID AssetId, int priority)
  281. {
  282. IPriorityQueueHandle<Prio<J2KImage>> h = null;
  283. lock (PQHandles)
  284. {
  285. if (PQHandles.ContainsKey(AssetId))
  286. {
  287. h = PQHandles[AssetId];
  288. pq[h] = pq[h].SetPriority(priority);
  289. }
  290. else
  291. {
  292. J2KImage newreq = new J2KImage();
  293. newreq.requestedUUID = AssetId;
  294. pq.Add(ref h, new Prio<J2KImage>(newreq, priority));
  295. PQHandles.Add(AssetId, h);
  296. }
  297. }
  298. }
  299. /// <summary>
  300. /// Okay, we're ending. Clean up on isle 9
  301. /// </summary>
  302. public void Close()
  303. {
  304. m_shuttingdown = true;
  305. lock (pq)
  306. {
  307. while (!pq.IsEmpty)
  308. {
  309. pq.DeleteMin();
  310. }
  311. }
  312. lock (PQHandles)
  313. PQHandles.Clear();
  314. m_client = null;
  315. }
  316. }
  317. /// <summary>
  318. /// Image Data for this send
  319. /// Encapsulates the image sending data and method
  320. /// </summary>
  321. public class J2KImage
  322. {
  323. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  324. private AssetBase m_asset_ref = null;
  325. public volatile int LastPacketNum = 0;
  326. public volatile int DiscardLimit = 0;
  327. public volatile bool dataRequested = false;
  328. public OpenJPEG.J2KLayerInfo[] Layers = new OpenJPEG.J2KLayerInfo[0];
  329. public const int FIRST_IMAGE_PACKET_SIZE = 600;
  330. public const int IMAGE_PACKET_SIZE = 1000;
  331. public volatile int DiscardLevel;
  332. public float Priority;
  333. public volatile int CurrentPacket = 1;
  334. public volatile int StopPacket;
  335. public bool Missing = false;
  336. public bool J2KDecode = false;
  337. public bool J2KDecodeWaiting = false;
  338. private volatile bool sendFirstPacket = true;
  339. // Having this *AND* the AssetId allows us to remap asset data to AssetIds as necessary.
  340. public UUID requestedUUID = UUID.Zero;
  341. public J2KImage(AssetBase asset)
  342. {
  343. m_asset_ref = asset;
  344. }
  345. public J2KImage()
  346. {
  347. }
  348. public AssetBase asset
  349. {
  350. set { m_asset_ref = value; }
  351. }
  352. // We make the asset a reference so that we don't duplicate the byte[]
  353. // it's read only anyway, so no worries here
  354. // we want to avoid duplicating the byte[] for the images at all costs to avoid memory bloat! :)
  355. /// <summary>
  356. /// ID of the AssetBase
  357. /// </summary>
  358. public UUID AssetId
  359. {
  360. get { return m_asset_ref.FullID; }
  361. }
  362. /// <summary>
  363. /// Asset Data
  364. /// </summary>
  365. public byte[] Data
  366. {
  367. get { return m_asset_ref.Data; }
  368. }
  369. /// <summary>
  370. /// Returns true if we have the asset
  371. /// </summary>
  372. public bool HasData
  373. {
  374. get { return !(m_asset_ref == null); }
  375. }
  376. /// <summary>
  377. /// Called from the PriorityQueue handle .ToString(). Prints data on this asset
  378. /// </summary>
  379. /// <returns></returns>
  380. public override string ToString()
  381. {
  382. return string.Format("ID:{0}, RD:{1}, CP:{2}", requestedUUID, HasData, CurrentPacket);
  383. }
  384. /// <summary>
  385. /// Returns the total number of packets needed to transfer this texture,
  386. /// including the first packet of size FIRST_IMAGE_PACKET_SIZE
  387. /// </summary>
  388. /// <returns>Total number of packets needed to transfer this texture</returns>
  389. public int TexturePacketCount()
  390. {
  391. if (!HasData)
  392. return 0;
  393. return ((m_asset_ref.Data.Length - FIRST_IMAGE_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1;
  394. }
  395. /// <summary>
  396. /// Returns the current byte offset for this transfer, calculated from
  397. /// the CurrentPacket
  398. /// </summary>
  399. /// <returns>Current byte offset for this transfer</returns>
  400. public int CurrentBytePosition()
  401. {
  402. if (CurrentPacket == 0)
  403. return 0;
  404. if (CurrentPacket == 1)
  405. return FIRST_IMAGE_PACKET_SIZE;
  406. int result = FIRST_IMAGE_PACKET_SIZE + (CurrentPacket - 2) * IMAGE_PACKET_SIZE;
  407. if (result < 0)
  408. {
  409. result = FIRST_IMAGE_PACKET_SIZE;
  410. }
  411. return result;
  412. }
  413. /// <summary>
  414. /// Returns the size, in bytes, of the last packet. This will be somewhere
  415. /// between 1 and IMAGE_PACKET_SIZE bytes
  416. /// </summary>
  417. /// <returns>Size of the last packet in the transfer</returns>
  418. public int LastPacketSize()
  419. {
  420. if (CurrentPacket == 1)
  421. return m_asset_ref.Data.Length;
  422. return (m_asset_ref.Data.Length - FIRST_IMAGE_PACKET_SIZE) % IMAGE_PACKET_SIZE; // m_asset_ref.Data.Length - (FIRST_IMAGE_PACKET_SIZE + ((TexturePacketCount() - 1) * IMAGE_PACKET_SIZE));
  423. }
  424. /// <summary>
  425. /// Find the packet number that contains a given byte position
  426. /// </summary>
  427. /// <param name="bytePosition">Byte position</param>
  428. /// <returns>Packet number that contains the given byte position</returns>
  429. int GetPacketForBytePosition(int bytePosition)
  430. {
  431. return ((bytePosition - FIRST_IMAGE_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1;
  432. }
  433. /// <summary>
  434. /// Updates the Image sending limits based on the discard
  435. /// If we don't have any Layers, Send the full texture
  436. /// </summary>
  437. /// <param name="discardLevel">jpeg2000 discard level. 5-0</param>
  438. /// <param name="packet">Which packet to start from</param>
  439. public void Update(int discardLevel, int packet)
  440. {
  441. //Requests for 0 means that the client wants us to resend the whole image
  442. //Requests for -1 mean 'update priority but don't change discard level'
  443. if (packet == 0 || packet == -1)
  444. return;
  445. // Check if we've got layers
  446. if (Layers.Length > 0)
  447. {
  448. DiscardLevel = Util.Clamp<int>(discardLevel, 0, Layers.Length - 1);
  449. StopPacket = GetPacketForBytePosition(Layers[(Layers.Length - 1) - DiscardLevel].End);
  450. CurrentPacket = Util.Clamp<int>(packet, 1, TexturePacketCount() - 1);
  451. // sendFirstPacket = true;
  452. }
  453. else
  454. {
  455. // No layers, send full image
  456. DiscardLevel = 0;
  457. StopPacket = TexturePacketCount();
  458. CurrentPacket = Util.Clamp<int>(packet, 1, TexturePacketCount() - 1);
  459. }
  460. }
  461. /// <summary>
  462. /// Sends a texture packet to the client.
  463. /// </summary>
  464. /// <param name="client">Client to send texture to</param>
  465. /// <returns>true if a packet was sent, false if not</returns>
  466. public bool SendPacket(LLClientView client)
  467. {
  468. // If we've hit the end of the send or if the client set -1, return false.
  469. if (CurrentPacket > StopPacket || StopPacket == -1)
  470. return false;
  471. // The first packet contains up to 600 bytes and the details of the image. Number of packets, image size in bytes, etc.
  472. // This packet only gets sent once unless we're restarting the transfer from 0!
  473. if (sendFirstPacket)
  474. {
  475. sendFirstPacket = false;
  476. // Do we have less then 1 packet's worth of data?
  477. if (m_asset_ref.Data.Length <= FIRST_IMAGE_PACKET_SIZE)
  478. {
  479. // Send only 1 packet
  480. client.SendImageFirstPart(1, requestedUUID , (uint)m_asset_ref.Data.Length, m_asset_ref.Data, 2);
  481. CurrentPacket = 2; // Makes it so we don't come back to SendPacket and error trying to send a second packet
  482. return true;
  483. }
  484. else
  485. {
  486. // Send first packet
  487. byte[] firstImageData = new byte[FIRST_IMAGE_PACKET_SIZE];
  488. try { Buffer.BlockCopy(m_asset_ref.Data, 0, firstImageData, 0, FIRST_IMAGE_PACKET_SIZE); }
  489. catch (Exception)
  490. {
  491. m_log.Error(String.Format("Err: srcLen:{0}, BytePos:{1}, desLen:{2}, pktsize{3}", m_asset_ref.Data.Length, CurrentBytePosition(), firstImageData.Length, FIRST_IMAGE_PACKET_SIZE));
  492. //m_log.Error("Texture data copy failed on first packet for " + m_asset_ref.FullID.ToString());
  493. //m_cancel = true;
  494. //m_sending = false;
  495. return false;
  496. }
  497. client.SendImageFirstPart((ushort)TexturePacketCount(), requestedUUID, (uint)m_asset_ref.Data.Length, firstImageData, 2);
  498. ++CurrentPacket; // sets CurrentPacket to 1
  499. }
  500. }
  501. // figure out if we're on the last packet, if so, use the last packet size. If not, use 1000.
  502. // we know that the total image size is greater then 1000 if we're here
  503. int imagePacketSize = (CurrentPacket == (TexturePacketCount() ) ) ? LastPacketSize() : IMAGE_PACKET_SIZE;
  504. //if (imagePacketSize > 0)
  505. // imagePacketSize = IMAGE_PACKET_SIZE;
  506. //if (imagePacketSize != 1000)
  507. // m_log.Debug("ENdPacket");
  508. //m_log.Debug(String.Format("srcLen:{0}, BytePos:{1}, desLen:{2}, pktsize{3}", m_asset_ref.Data.Length, CurrentBytePosition(),0, imagePacketSize));
  509. bool atEnd = false;
  510. // edge case
  511. if ((CurrentBytePosition() + IMAGE_PACKET_SIZE) > m_asset_ref.Data.Length)
  512. {
  513. imagePacketSize = LastPacketSize();
  514. atEnd = true;
  515. // edge case 2!
  516. if ((CurrentBytePosition() + imagePacketSize) > m_asset_ref.Data.Length)
  517. {
  518. imagePacketSize = m_asset_ref.Data.Length - CurrentBytePosition();
  519. atEnd = true;
  520. }
  521. }
  522. byte[] imageData = new byte[imagePacketSize];
  523. try { Buffer.BlockCopy(m_asset_ref.Data, CurrentBytePosition(), imageData, 0, imagePacketSize); }
  524. catch (Exception e)
  525. {
  526. m_log.Error(String.Format("Err: srcLen:{0}, BytePos:{1}, desLen:{2}, pktsize:{3}, currpak:{4}, stoppak:{5}, totalpak:{6}", m_asset_ref.Data.Length, CurrentBytePosition(),
  527. imageData.Length, imagePacketSize, CurrentPacket, StopPacket, TexturePacketCount()));
  528. m_log.Error(e.ToString());
  529. //m_log.Error("Texture data copy failed for " + m_asset_ref.FullID.ToString());
  530. //m_cancel = true;
  531. //m_sending = false;
  532. return false;
  533. }
  534. // Send next packet to the client
  535. client.SendImageNextPart((ushort)(CurrentPacket - 1), requestedUUID, imageData);
  536. ++CurrentPacket;
  537. if (atEnd)
  538. CurrentPacket = StopPacket + 1;
  539. return true;
  540. }
  541. }
  542. /// <summary>
  543. /// Generic Priority Queue element
  544. /// Contains a Priority and a Reference type Data Element
  545. /// </summary>
  546. /// <typeparam name="D">Reference type data element</typeparam>
  547. struct Prio<D> : IComparable<Prio<D>> where D : class
  548. {
  549. public D data;
  550. private int priority;
  551. public Prio(D data, int priority)
  552. {
  553. this.data = data;
  554. this.priority = priority;
  555. }
  556. public int CompareTo(Prio<D> that)
  557. {
  558. return priority.CompareTo(that.priority);
  559. }
  560. public bool Equals(Prio<D> that)
  561. {
  562. return priority == that.priority;
  563. }
  564. public static Prio<D> operator +(Prio<D> tp, int delta)
  565. {
  566. return new Prio<D>(tp.data, tp.priority + delta);
  567. }
  568. public static bool operator <(Prio<D> tp, int check)
  569. {
  570. return (tp.priority < check);
  571. }
  572. public static bool operator >(Prio<D> tp, int check)
  573. {
  574. return (tp.priority > check);
  575. }
  576. public static Prio<D> operator -(Prio<D> tp, int delta)
  577. {
  578. if (tp.priority - delta < 0)
  579. return new Prio<D>(tp.data, tp.priority - delta);
  580. else
  581. return new Prio<D>(tp.data, 0);
  582. }
  583. public override String ToString()
  584. {
  585. return String.Format("{0}[{1}]", data, priority);
  586. }
  587. internal Prio<D> SetPriority(int pPriority)
  588. {
  589. return new Prio<D>(this.data, pPriority);
  590. }
  591. }
  592. }