AssetCache.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. * Copyright (c) OpenSim project, http://sim.opensecondlife.org/
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. * * Redistributions of source code must retain the above copyright
  7. * notice, this list of conditions and the following disclaimer.
  8. * * Redistributions in binary form must reproduce the above copyright
  9. * notice, this list of conditions and the following disclaimer in the
  10. * documentation and/or other materials provided with the distribution.
  11. * * Neither the name of the <organization> nor the
  12. * names of its contributors may be used to endorse or promote products
  13. * derived from this software without specific prior written permission.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
  16. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  17. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
  19. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  20. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  21. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  22. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  24. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. *
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Threading;
  30. using libsecondlife;
  31. using libsecondlife.Packets;
  32. using OpenSim;
  33. using OpenSim.GridServers;
  34. namespace OpenSim.Assets
  35. {
  36. /// <summary>
  37. /// Manages local cache of assets and their sending to viewers.
  38. /// </summary>
  39. public class AssetCache : IAssetReceiver
  40. {
  41. public Dictionary<libsecondlife.LLUUID, AssetInfo> Assets;
  42. public Dictionary<libsecondlife.LLUUID, TextureImage> Textures;
  43. public List<AssetRequest> AssetRequests = new List<AssetRequest>(); //assets ready to be sent to viewers
  44. public List<AssetRequest> TextureRequests = new List<AssetRequest>(); //textures ready to be sent
  45. public Dictionary<LLUUID, AssetRequest> RequestedAssets = new Dictionary<LLUUID, AssetRequest>(); //Assets requested from the asset server
  46. public Dictionary<LLUUID, AssetRequest> RequestedTextures = new Dictionary<LLUUID, AssetRequest>(); //Textures requested from the asset server
  47. private IAssetServer _assetServer;
  48. private Thread _assetCacheThread;
  49. /// <summary>
  50. ///
  51. /// </summary>
  52. public AssetCache( IAssetServer assetServer)
  53. {
  54. _assetServer = assetServer;
  55. _assetServer.SetReceiver(this);
  56. this._assetCacheThread = new Thread( new ThreadStart(RunAssetManager));
  57. this._assetCacheThread.IsBackground = true;
  58. this._assetCacheThread.Start();
  59. }
  60. /// <summary>
  61. ///
  62. /// </summary>
  63. private void RunAssetManager()
  64. {
  65. //should be running in its own thread
  66. this.ProcessAssetQueue();
  67. this.ProcessTextureQueue();
  68. Thread.Sleep(100);
  69. }
  70. /// <summary>
  71. ///
  72. /// </summary>
  73. private void ProcessTextureQueue()
  74. {
  75. if(this.TextureRequests.Count == 0)
  76. {
  77. //no requests waiting
  78. return;
  79. }
  80. int num;
  81. if(this.TextureRequests.Count < 5)
  82. {
  83. //lower than 5 so do all of them
  84. num = this.TextureRequests.Count;
  85. }
  86. else
  87. {
  88. num=5;
  89. }
  90. AssetRequest req;
  91. for(int i = 0; i < num; i++)
  92. {
  93. req=(AssetRequest)this.TextureRequests[i];
  94. if(req.PacketCounter == 0)
  95. {
  96. //first time for this request so send imagedata packet
  97. if(req.NumPackets == 1)
  98. {
  99. //only one packet so send whole file
  100. ImageDataPacket im = new ImageDataPacket();
  101. im.ImageID.Packets = 1;
  102. im.ImageID.ID = req.ImageInfo.FullID;
  103. im.ImageID.Size = (uint)req.ImageInfo.Data.Length;
  104. im.ImageData.Data = req.ImageInfo.Data;
  105. im.ImageID.Codec = 2;
  106. req.RequestUser.OutPacket(im);
  107. req.PacketCounter++;
  108. //req.ImageInfo.l= time;
  109. //System.Console.WriteLine("sent texture: "+req.image_info.FullID);
  110. }
  111. else
  112. {
  113. //more than one packet so split file up
  114. ImageDataPacket im = new ImageDataPacket();
  115. im.ImageID.Packets = (ushort)req.NumPackets;
  116. im.ImageID.ID = req.ImageInfo.FullID;
  117. im.ImageID.Size = (uint)req.ImageInfo.Data.Length;
  118. im.ImageData.Data = new byte[600];
  119. Array.Copy(req.ImageInfo.Data, 0, im.ImageData.Data, 0, 600);
  120. im.ImageID.Codec = 2;
  121. req.RequestUser.OutPacket(im);
  122. req.PacketCounter++;
  123. //req.ImageInfo.last_used = time;
  124. //System.Console.WriteLine("sent first packet of texture:
  125. }
  126. }
  127. else
  128. {
  129. //send imagepacket
  130. //more than one packet so split file up
  131. ImagePacketPacket im = new ImagePacketPacket();
  132. im.ImageID.Packet = (ushort)req.PacketCounter;
  133. im.ImageID.ID = req.ImageInfo.FullID;
  134. int size = req.ImageInfo.Data.Length - 600 - 1000*(req.PacketCounter - 1);
  135. if(size > 1000) size = 1000;
  136. im.ImageData.Data = new byte[size];
  137. Array.Copy(req.ImageInfo.Data, 600 + 1000*(req.PacketCounter - 1), im.ImageData.Data, 0, size);
  138. req.RequestUser.OutPacket(im);
  139. req.PacketCounter++;
  140. //req.ImageInfo.last_used = time;
  141. //System.Console.WriteLine("sent a packet of texture: "+req.image_info.FullID);
  142. }
  143. }
  144. //remove requests that have been completed
  145. for(int i = 0; i < num; i++)
  146. {
  147. req=(AssetRequest)this.TextureRequests[i];
  148. if(req.PacketCounter == req.NumPackets)
  149. {
  150. this.TextureRequests.Remove(req);
  151. }
  152. }
  153. }
  154. public void AssetReceived(AssetBase asset, bool IsTexture)
  155. {
  156. //check if it is a texture or not
  157. //then add to the correct cache list
  158. //then check for waiting requests for this asset/texture (in the Requested lists)
  159. //and move those requests into the Requests list.
  160. if(IsTexture)
  161. {
  162. TextureImage image = new TextureImage(asset);
  163. this.Textures.Add(image.FullID, image);
  164. if(this.RequestedTextures.ContainsKey(image.FullID))
  165. {
  166. AssetRequest req = this.RequestedTextures[image.FullID];
  167. req.ImageInfo = image;
  168. this.RequestedTextures.Remove(image.FullID);
  169. this.TextureRequests.Add(req);
  170. }
  171. }
  172. else
  173. {
  174. AssetInfo assetInf = new AssetInfo(asset);
  175. this.Assets.Add(assetInf.FullID, assetInf);
  176. if(this.RequestedAssets.ContainsKey(assetInf.FullID))
  177. {
  178. AssetRequest req = this.RequestedAssets[assetInf.FullID];
  179. req.AssetInf = assetInf;
  180. this.RequestedAssets.Remove(assetInf.FullID);
  181. this.AssetRequests.Add(req);
  182. }
  183. }
  184. }
  185. public void AssetNotFound(AssetBase asset)
  186. {
  187. //the asset server had no knowledge of requested asset
  188. }
  189. #region Assets
  190. /// <summary>
  191. ///
  192. /// </summary>
  193. /// <param name="userInfo"></param>
  194. /// <param name="transferRequest"></param>
  195. public void AddAssetRequest(OpenSimClient userInfo, TransferRequestPacket transferRequest)
  196. {
  197. LLUUID requestID = new LLUUID(transferRequest.TransferInfo.Params, 0);
  198. //check to see if asset is in local cache, if not we need to request it from asset server.
  199. if(!this.Assets.ContainsKey(requestID))
  200. {
  201. //not found asset
  202. // so request from asset server
  203. AssetRequest request = new AssetRequest();
  204. request.RequestUser = userInfo;
  205. request.RequestAssetID = requestID;
  206. request.TransferRequestID = transferRequest.TransferInfo.TransferID;
  207. this.RequestedAssets.Add(requestID,request);
  208. this._assetServer.RequestAsset(requestID, false);
  209. return;
  210. }
  211. //it is in our cache
  212. AssetInfo asset = this.Assets[requestID];
  213. //work out how many packets it should be sent in
  214. // and add to the AssetRequests list
  215. AssetRequest req = new AssetRequest();
  216. req.RequestUser = userInfo;
  217. req.RequestAssetID = requestID;
  218. req.TransferRequestID = transferRequest.TransferInfo.TransferID;
  219. req.AssetInf = asset;
  220. if(asset.Data.LongLength>600)
  221. {
  222. //over 600 bytes so split up file
  223. req.NumPackets = 1 + (int)(asset.Data.Length-600+999)/1000;
  224. }
  225. else
  226. {
  227. req.NumPackets = 1;
  228. }
  229. this.AssetRequests.Add(req);
  230. }
  231. /// <summary>
  232. ///
  233. /// </summary>
  234. private void ProcessAssetQueue()
  235. {
  236. if(this.AssetRequests.Count == 0)
  237. {
  238. //no requests waiting
  239. return;
  240. }
  241. int num;
  242. if(this.AssetRequests.Count < 5)
  243. {
  244. //lower than 5 so do all of them
  245. num = this.AssetRequests.Count;
  246. }
  247. else
  248. {
  249. num=5;
  250. }
  251. AssetRequest req;
  252. for(int i = 0; i < num; i++)
  253. {
  254. req=(AssetRequest)this.AssetRequests[i];
  255. TransferInfoPacket Transfer = new TransferInfoPacket();
  256. Transfer.TransferInfo.ChannelType = 2;
  257. Transfer.TransferInfo.Status = 0;
  258. Transfer.TransferInfo.TargetType = 0;
  259. Transfer.TransferInfo.Params = req.RequestAssetID.GetBytes();
  260. Transfer.TransferInfo.Size = (int)req.AssetInf.Data.Length;
  261. Transfer.TransferInfo.TransferID = req.TransferRequestID;
  262. req.RequestUser.OutPacket(Transfer);
  263. if(req.NumPackets == 1)
  264. {
  265. TransferPacketPacket TransferPacket = new TransferPacketPacket();
  266. TransferPacket.TransferData.Packet = 0;
  267. TransferPacket.TransferData.ChannelType = 2;
  268. TransferPacket.TransferData.TransferID=req.TransferRequestID;
  269. TransferPacket.TransferData.Data = req.AssetInf.Data;
  270. TransferPacket.TransferData.Status = 1;
  271. req.RequestUser.OutPacket(TransferPacket);
  272. }
  273. else
  274. {
  275. //more than one packet so split file up , for now it can't be bigger than 2000 bytes
  276. TransferPacketPacket TransferPacket = new TransferPacketPacket();
  277. TransferPacket.TransferData.Packet = 0;
  278. TransferPacket.TransferData.ChannelType = 2;
  279. TransferPacket.TransferData.TransferID=req.TransferRequestID;
  280. byte[] chunk = new byte[1000];
  281. Array.Copy(req.AssetInf.Data,chunk,1000);
  282. TransferPacket.TransferData.Data = chunk;
  283. TransferPacket.TransferData.Status = 0;
  284. req.RequestUser.OutPacket(TransferPacket);
  285. TransferPacket = new TransferPacketPacket();
  286. TransferPacket.TransferData.Packet = 1;
  287. TransferPacket.TransferData.ChannelType = 2;
  288. TransferPacket.TransferData.TransferID = req.TransferRequestID;
  289. byte[] chunk1 = new byte[(req.AssetInf.Data.Length-1000)];
  290. Array.Copy(req.AssetInf.Data, 1000, chunk1, 0, chunk1.Length);
  291. TransferPacket.TransferData.Data = chunk1;
  292. TransferPacket.TransferData.Status = 1;
  293. req.RequestUser.OutPacket(TransferPacket);
  294. }
  295. }
  296. //remove requests that have been completed
  297. for(int i = 0; i < num; i++)
  298. {
  299. this.AssetRequests.RemoveAt(i);
  300. }
  301. }
  302. #endregion
  303. #region Textures
  304. /// <summary>
  305. ///
  306. /// </summary>
  307. /// <param name="userInfo"></param>
  308. /// <param name="imageID"></param>
  309. public void AddTextureRequest(OpenSimClient userInfo, LLUUID imageID)
  310. {
  311. //check to see if texture is in local cache, if not request from asset server
  312. if(!this.Textures.ContainsKey(imageID))
  313. {
  314. //not is cache so request from asset server
  315. AssetRequest request = new AssetRequest();
  316. request.RequestUser = userInfo;
  317. request.RequestAssetID = imageID;
  318. request.IsTextureRequest = true;
  319. this.RequestedTextures.Add(imageID, request);
  320. this._assetServer.RequestAsset(imageID, true);
  321. return;
  322. }
  323. TextureImage imag = this.Textures[imageID];
  324. AssetRequest req = new AssetRequest();
  325. req.RequestUser = userInfo;
  326. req.RequestAssetID = imageID;
  327. req.IsTextureRequest = true;
  328. req.ImageInfo = imag;
  329. if(imag.Data.LongLength>600)
  330. {
  331. //over 600 bytes so split up file
  332. req.NumPackets = 1 + (int)(imag.Data.Length-600+999)/1000;
  333. }
  334. else
  335. {
  336. req.NumPackets = 1;
  337. }
  338. this.TextureRequests.Add(req);
  339. }
  340. #endregion
  341. }
  342. public class AssetRequest
  343. {
  344. public OpenSimClient RequestUser;
  345. public LLUUID RequestAssetID;
  346. public AssetInfo AssetInf;
  347. public TextureImage ImageInfo;
  348. public LLUUID TransferRequestID;
  349. public long DataPointer = 0;
  350. public int NumPackets = 0;
  351. public int PacketCounter = 0;
  352. public bool IsTextureRequest;
  353. //public bool AssetInCache;
  354. //public int TimeRequested;
  355. public AssetRequest()
  356. {
  357. }
  358. }
  359. public class AssetBase
  360. {
  361. public byte[] Data;
  362. public LLUUID FullID;
  363. public sbyte Type;
  364. public sbyte InvType;
  365. public string Name;
  366. public string Description;
  367. public AssetBase()
  368. {
  369. }
  370. }
  371. public class AssetInfo : AssetBase
  372. {
  373. public AssetInfo()
  374. {
  375. }
  376. public AssetInfo(AssetBase aBase)
  377. {
  378. Data= aBase.Data;
  379. FullID = aBase.FullID;
  380. Type = aBase.Type;
  381. InvType = aBase.InvType;
  382. Name= aBase.Name;
  383. Description = aBase.Description;
  384. }
  385. }
  386. public class TextureImage : AssetBase
  387. {
  388. public TextureImage()
  389. {
  390. }
  391. public TextureImage(AssetBase aBase)
  392. {
  393. Data= aBase.Data;
  394. FullID = aBase.FullID;
  395. Type = aBase.Type;
  396. InvType = aBase.InvType;
  397. Name= aBase.Name;
  398. Description = aBase.Description;
  399. }
  400. }
  401. }