AssetCache.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  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 System.Reflection;
  31. using libsecondlife;
  32. using libsecondlife.Packets;
  33. using OpenSim;
  34. using OpenSim.Framework.Interfaces;
  35. using OpenSim.Framework.Types;
  36. using OpenSim.Framework.Utilities;
  37. namespace OpenSim.Assets
  38. {
  39. /// <summary>
  40. /// Manages local cache of assets and their sending to viewers.
  41. /// </summary>
  42. public class AssetCache : IAssetReceiver
  43. {
  44. public Dictionary<libsecondlife.LLUUID, AssetInfo> Assets;
  45. public Dictionary<libsecondlife.LLUUID, TextureImage> Textures;
  46. public List<AssetRequest> AssetRequests = new List<AssetRequest>(); //assets ready to be sent to viewers
  47. public List<AssetRequest> TextureRequests = new List<AssetRequest>(); //textures ready to be sent
  48. public Dictionary<LLUUID, AssetRequest> RequestedAssets = new Dictionary<LLUUID, AssetRequest>(); //Assets requested from the asset server
  49. public Dictionary<LLUUID, AssetRequest> RequestedTextures = new Dictionary<LLUUID, AssetRequest>(); //Textures requested from the asset server
  50. private IAssetServer _assetServer;
  51. private Thread _assetCacheThread;
  52. private LLUUID[] textureList = new LLUUID[5];
  53. /// <summary>
  54. ///
  55. /// </summary>
  56. public AssetCache(IAssetServer assetServer)
  57. {
  58. Console.WriteLine("Creating Asset cache");
  59. _assetServer = assetServer;
  60. _assetServer.SetReceiver(this);
  61. Assets = new Dictionary<libsecondlife.LLUUID, AssetInfo>();
  62. Textures = new Dictionary<libsecondlife.LLUUID, TextureImage>();
  63. this._assetCacheThread = new Thread(new ThreadStart(RunAssetManager));
  64. this._assetCacheThread.IsBackground = true;
  65. this._assetCacheThread.Start();
  66. }
  67. public AssetCache(string assetServerDLLName, string assetServerURL, string assetServerKey)
  68. {
  69. Console.WriteLine("Creating Asset cache");
  70. _assetServer = this.LoadAssetDll(assetServerDLLName);
  71. _assetServer.SetServerInfo(assetServerURL, assetServerKey);
  72. _assetServer.SetReceiver(this);
  73. Assets = new Dictionary<libsecondlife.LLUUID, AssetInfo>();
  74. Textures = new Dictionary<libsecondlife.LLUUID, TextureImage>();
  75. this._assetCacheThread = new Thread(new ThreadStart(RunAssetManager));
  76. this._assetCacheThread.IsBackground = true;
  77. this._assetCacheThread.Start();
  78. }
  79. /// <summary>
  80. ///
  81. /// </summary>
  82. public void RunAssetManager()
  83. {
  84. while (true)
  85. {
  86. try
  87. {
  88. //Console.WriteLine("Asset cache loop");
  89. this.ProcessAssetQueue();
  90. this.ProcessTextureQueue();
  91. Thread.Sleep(500);
  92. }
  93. catch (Exception e)
  94. {
  95. Console.WriteLine(e.Message);
  96. }
  97. }
  98. }
  99. public void LoadDefaultTextureSet()
  100. {
  101. //hack: so we can give each user a set of textures
  102. textureList[0] = new LLUUID("00000000-0000-0000-9999-000000000001");
  103. textureList[1] = new LLUUID("00000000-0000-0000-9999-000000000002");
  104. textureList[2] = new LLUUID("00000000-0000-0000-9999-000000000003");
  105. textureList[3] = new LLUUID("00000000-0000-0000-9999-000000000004");
  106. textureList[4] = new LLUUID("00000000-0000-0000-9999-000000000005");
  107. for (int i = 0; i < textureList.Length; i++)
  108. {
  109. this._assetServer.RequestAsset(textureList[i], true);
  110. }
  111. }
  112. public AssetBase[] CreateNewInventorySet(LLUUID agentID)
  113. {
  114. AssetBase[] inventorySet = new AssetBase[this.textureList.Length];
  115. for (int i = 0; i < textureList.Length; i++)
  116. {
  117. if (this.Textures.ContainsKey(textureList[i]))
  118. {
  119. inventorySet[i] = this.CloneImage(agentID, this.Textures[textureList[i]]);
  120. TextureImage image = new TextureImage(inventorySet[i]);
  121. this.Textures.Add(image.FullID, image);
  122. this._assetServer.UploadNewAsset(image); //save the asset to the asset server
  123. }
  124. }
  125. return inventorySet;
  126. }
  127. public AssetBase GetAsset(LLUUID assetID)
  128. {
  129. AssetBase asset = null;
  130. if(this.Textures.ContainsKey(assetID))
  131. {
  132. asset = this.Textures[assetID];
  133. }
  134. else if (this.Assets.ContainsKey(assetID))
  135. {
  136. asset = this.Assets[assetID];
  137. }
  138. return asset;
  139. }
  140. public void AddAsset(AssetBase asset)
  141. {
  142. if (asset.Type == 0)
  143. {
  144. if (!this.Textures.ContainsKey(asset.FullID))
  145. { //texture
  146. TextureImage textur = new TextureImage(asset);
  147. this.Textures.Add(textur.FullID, textur);
  148. this._assetServer.UploadNewAsset(asset);
  149. }
  150. }
  151. else
  152. {
  153. if (!this.Assets.ContainsKey(asset.FullID))
  154. {
  155. AssetInfo assetInf = new AssetInfo(asset);
  156. this.Assets.Add(assetInf.FullID, assetInf);
  157. this._assetServer.UploadNewAsset(asset);
  158. }
  159. }
  160. }
  161. /// <summary>
  162. ///
  163. /// </summary>
  164. private void ProcessTextureQueue()
  165. {
  166. if (this.TextureRequests.Count == 0)
  167. {
  168. //no requests waiting
  169. return;
  170. }
  171. int num;
  172. if (this.TextureRequests.Count < 5)
  173. {
  174. //lower than 5 so do all of them
  175. num = this.TextureRequests.Count;
  176. }
  177. else
  178. {
  179. num = 5;
  180. }
  181. AssetRequest req;
  182. for (int i = 0; i < num; i++)
  183. {
  184. req = (AssetRequest)this.TextureRequests[i];
  185. if (req.PacketCounter != req.NumPackets)
  186. {
  187. // if (req.ImageInfo.FullID == new LLUUID("00000000-0000-0000-5005-000000000005"))
  188. if (req.PacketCounter == 0)
  189. {
  190. //first time for this request so send imagedata packet
  191. if (req.NumPackets == 1)
  192. {
  193. //only one packet so send whole file
  194. ImageDataPacket im = new ImageDataPacket();
  195. im.ImageID.Packets = 1;
  196. im.ImageID.ID = req.ImageInfo.FullID;
  197. im.ImageID.Size = (uint)req.ImageInfo.Data.Length;
  198. im.ImageData.Data = req.ImageInfo.Data;
  199. im.ImageID.Codec = 2;
  200. req.RequestUser.OutPacket(im);
  201. req.PacketCounter++;
  202. //req.ImageInfo.l= time;
  203. //System.Console.WriteLine("sent texture: "+req.image_info.FullID);
  204. }
  205. else
  206. {
  207. //more than one packet so split file up
  208. ImageDataPacket im = new ImageDataPacket();
  209. im.ImageID.Packets = (ushort)req.NumPackets;
  210. im.ImageID.ID = req.ImageInfo.FullID;
  211. im.ImageID.Size = (uint)req.ImageInfo.Data.Length;
  212. im.ImageData.Data = new byte[600];
  213. Array.Copy(req.ImageInfo.Data, 0, im.ImageData.Data, 0, 600);
  214. im.ImageID.Codec = 2;
  215. req.RequestUser.OutPacket(im);
  216. req.PacketCounter++;
  217. //req.ImageInfo.last_used = time;
  218. //System.Console.WriteLine("sent first packet of texture:
  219. }
  220. }
  221. else
  222. {
  223. //send imagepacket
  224. //more than one packet so split file up
  225. ImagePacketPacket im = new ImagePacketPacket();
  226. im.ImageID.Packet = (ushort)req.PacketCounter;
  227. im.ImageID.ID = req.ImageInfo.FullID;
  228. int size = req.ImageInfo.Data.Length - 600 - 1000 * (req.PacketCounter - 1);
  229. if (size > 1000) size = 1000;
  230. im.ImageData.Data = new byte[size];
  231. Array.Copy(req.ImageInfo.Data, 600 + 1000 * (req.PacketCounter - 1), im.ImageData.Data, 0, size);
  232. req.RequestUser.OutPacket(im);
  233. req.PacketCounter++;
  234. //req.ImageInfo.last_used = time;
  235. //System.Console.WriteLine("sent a packet of texture: "+req.image_info.FullID);
  236. }
  237. }
  238. }
  239. //remove requests that have been completed
  240. int count = 0;
  241. for (int i = 0; i < num; i++)
  242. {
  243. if (this.TextureRequests.Count > count)
  244. {
  245. req = (AssetRequest)this.TextureRequests[count];
  246. if (req.PacketCounter == req.NumPackets)
  247. {
  248. this.TextureRequests.Remove(req);
  249. }
  250. else
  251. {
  252. count++;
  253. }
  254. }
  255. }
  256. }
  257. public void AssetReceived(AssetBase asset, bool IsTexture)
  258. {
  259. if (asset.FullID != LLUUID.Zero) // if it is set to zero then the asset wasn't found by the server
  260. {
  261. //check if it is a texture or not
  262. //then add to the correct cache list
  263. //then check for waiting requests for this asset/texture (in the Requested lists)
  264. //and move those requests into the Requests list.
  265. if (IsTexture)
  266. {
  267. TextureImage image = new TextureImage(asset);
  268. this.Textures.Add(image.FullID, image);
  269. if (this.RequestedTextures.ContainsKey(image.FullID))
  270. {
  271. AssetRequest req = this.RequestedTextures[image.FullID];
  272. req.ImageInfo = image;
  273. if (image.Data.LongLength > 600)
  274. {
  275. //over 600 bytes so split up file
  276. req.NumPackets = 1 + (int)(image.Data.Length - 600 + 999) / 1000;
  277. }
  278. else
  279. {
  280. req.NumPackets = 1;
  281. }
  282. this.RequestedTextures.Remove(image.FullID);
  283. this.TextureRequests.Add(req);
  284. }
  285. }
  286. else
  287. {
  288. AssetInfo assetInf = new AssetInfo(asset);
  289. this.Assets.Add(assetInf.FullID, assetInf);
  290. if (this.RequestedAssets.ContainsKey(assetInf.FullID))
  291. {
  292. AssetRequest req = this.RequestedAssets[assetInf.FullID];
  293. req.AssetInf = assetInf;
  294. if (assetInf.Data.LongLength > 600)
  295. {
  296. //over 600 bytes so split up file
  297. req.NumPackets = 1 + (int)(assetInf.Data.Length - 600 + 999) / 1000;
  298. }
  299. else
  300. {
  301. req.NumPackets = 1;
  302. }
  303. this.RequestedAssets.Remove(assetInf.FullID);
  304. this.AssetRequests.Add(req);
  305. }
  306. }
  307. }
  308. }
  309. public void AssetNotFound(AssetBase asset)
  310. {
  311. //the asset server had no knowledge of requested asset
  312. }
  313. #region Assets
  314. /// <summary>
  315. ///
  316. /// </summary>
  317. /// <param name="userInfo"></param>
  318. /// <param name="transferRequest"></param>
  319. public void AddAssetRequest(ClientView userInfo, TransferRequestPacket transferRequest)
  320. {
  321. LLUUID requestID = new LLUUID(transferRequest.TransferInfo.Params, 0);
  322. //check to see if asset is in local cache, if not we need to request it from asset server.
  323. if (!this.Assets.ContainsKey(requestID))
  324. {
  325. //not found asset
  326. // so request from asset server
  327. if (!this.RequestedAssets.ContainsKey(requestID))
  328. {
  329. AssetRequest request = new AssetRequest();
  330. request.RequestUser = userInfo;
  331. request.RequestAssetID = requestID;
  332. request.TransferRequestID = transferRequest.TransferInfo.TransferID;
  333. this.RequestedAssets.Add(requestID, request);
  334. this._assetServer.RequestAsset(requestID, false);
  335. }
  336. return;
  337. }
  338. //it is in our cache
  339. AssetInfo asset = this.Assets[requestID];
  340. //work out how many packets it should be sent in
  341. // and add to the AssetRequests list
  342. AssetRequest req = new AssetRequest();
  343. req.RequestUser = userInfo;
  344. req.RequestAssetID = requestID;
  345. req.TransferRequestID = transferRequest.TransferInfo.TransferID;
  346. req.AssetInf = asset;
  347. if (asset.Data.LongLength > 600)
  348. {
  349. //over 600 bytes so split up file
  350. req.NumPackets = 1 + (int)(asset.Data.Length - 600 + 999) / 1000;
  351. }
  352. else
  353. {
  354. req.NumPackets = 1;
  355. }
  356. this.AssetRequests.Add(req);
  357. }
  358. /// <summary>
  359. ///
  360. /// </summary>
  361. private void ProcessAssetQueue()
  362. {
  363. if (this.AssetRequests.Count == 0)
  364. {
  365. //no requests waiting
  366. return;
  367. }
  368. int num;
  369. if (this.AssetRequests.Count < 5)
  370. {
  371. //lower than 5 so do all of them
  372. num = this.AssetRequests.Count;
  373. }
  374. else
  375. {
  376. num = 5;
  377. }
  378. AssetRequest req;
  379. for (int i = 0; i < num; i++)
  380. {
  381. req = (AssetRequest)this.AssetRequests[i];
  382. TransferInfoPacket Transfer = new TransferInfoPacket();
  383. Transfer.TransferInfo.ChannelType = 2;
  384. Transfer.TransferInfo.Status = 0;
  385. Transfer.TransferInfo.TargetType = 0;
  386. Transfer.TransferInfo.Params = req.RequestAssetID.GetBytes();
  387. Transfer.TransferInfo.Size = (int)req.AssetInf.Data.Length;
  388. Transfer.TransferInfo.TransferID = req.TransferRequestID;
  389. req.RequestUser.OutPacket(Transfer);
  390. if (req.NumPackets == 1)
  391. {
  392. TransferPacketPacket TransferPacket = new TransferPacketPacket();
  393. TransferPacket.TransferData.Packet = 0;
  394. TransferPacket.TransferData.ChannelType = 2;
  395. TransferPacket.TransferData.TransferID = req.TransferRequestID;
  396. TransferPacket.TransferData.Data = req.AssetInf.Data;
  397. TransferPacket.TransferData.Status = 1;
  398. req.RequestUser.OutPacket(TransferPacket);
  399. }
  400. else
  401. {
  402. //more than one packet so split file up , for now it can't be bigger than 2000 bytes
  403. TransferPacketPacket TransferPacket = new TransferPacketPacket();
  404. TransferPacket.TransferData.Packet = 0;
  405. TransferPacket.TransferData.ChannelType = 2;
  406. TransferPacket.TransferData.TransferID = req.TransferRequestID;
  407. byte[] chunk = new byte[1000];
  408. Array.Copy(req.AssetInf.Data, chunk, 1000);
  409. TransferPacket.TransferData.Data = chunk;
  410. TransferPacket.TransferData.Status = 0;
  411. req.RequestUser.OutPacket(TransferPacket);
  412. TransferPacket = new TransferPacketPacket();
  413. TransferPacket.TransferData.Packet = 1;
  414. TransferPacket.TransferData.ChannelType = 2;
  415. TransferPacket.TransferData.TransferID = req.TransferRequestID;
  416. byte[] chunk1 = new byte[(req.AssetInf.Data.Length - 1000)];
  417. Array.Copy(req.AssetInf.Data, 1000, chunk1, 0, chunk1.Length);
  418. TransferPacket.TransferData.Data = chunk1;
  419. TransferPacket.TransferData.Status = 1;
  420. req.RequestUser.OutPacket(TransferPacket);
  421. }
  422. }
  423. //remove requests that have been completed
  424. for (int i = 0; i < num; i++)
  425. {
  426. this.AssetRequests.RemoveAt(0);
  427. }
  428. }
  429. public AssetInfo CloneAsset(LLUUID newOwner, AssetInfo sourceAsset)
  430. {
  431. AssetInfo newAsset = new AssetInfo();
  432. newAsset.Data = new byte[sourceAsset.Data.Length];
  433. Array.Copy(sourceAsset.Data, newAsset.Data, sourceAsset.Data.Length);
  434. newAsset.FullID = LLUUID.Random();
  435. newAsset.Type = sourceAsset.Type;
  436. newAsset.InvType = sourceAsset.InvType;
  437. return (newAsset);
  438. }
  439. #endregion
  440. #region Textures
  441. /// <summary>
  442. ///
  443. /// </summary>
  444. /// <param name="userInfo"></param>
  445. /// <param name="imageID"></param>
  446. public void AddTextureRequest(ClientView userInfo, LLUUID imageID)
  447. {
  448. //check to see if texture is in local cache, if not request from asset server
  449. if (!this.Textures.ContainsKey(imageID))
  450. {
  451. if (!this.RequestedTextures.ContainsKey(imageID))
  452. {
  453. //not is cache so request from asset server
  454. AssetRequest request = new AssetRequest();
  455. request.RequestUser = userInfo;
  456. request.RequestAssetID = imageID;
  457. request.IsTextureRequest = true;
  458. this.RequestedTextures.Add(imageID, request);
  459. this._assetServer.RequestAsset(imageID, true);
  460. }
  461. return;
  462. }
  463. TextureImage imag = this.Textures[imageID];
  464. AssetRequest req = new AssetRequest();
  465. req.RequestUser = userInfo;
  466. req.RequestAssetID = imageID;
  467. req.IsTextureRequest = true;
  468. req.ImageInfo = imag;
  469. if (imag.Data.LongLength > 600)
  470. {
  471. //over 600 bytes so split up file
  472. req.NumPackets = 1 + (int)(imag.Data.Length - 600 + 999) / 1000;
  473. }
  474. else
  475. {
  476. req.NumPackets = 1;
  477. }
  478. this.TextureRequests.Add(req);
  479. }
  480. public TextureImage CloneImage(LLUUID newOwner, TextureImage source)
  481. {
  482. TextureImage newImage = new TextureImage();
  483. newImage.Data = new byte[source.Data.Length];
  484. Array.Copy(source.Data, newImage.Data, source.Data.Length);
  485. //newImage.filename = source.filename;
  486. newImage.FullID = LLUUID.Random();
  487. newImage.Name = source.Name;
  488. return (newImage);
  489. }
  490. #endregion
  491. private IAssetServer LoadAssetDll(string dllName)
  492. {
  493. Assembly pluginAssembly = Assembly.LoadFrom(dllName);
  494. IAssetServer server = null;
  495. foreach (Type pluginType in pluginAssembly.GetTypes())
  496. {
  497. if (pluginType.IsPublic)
  498. {
  499. if (!pluginType.IsAbstract)
  500. {
  501. Type typeInterface = pluginType.GetInterface("IAssetPlugin", true);
  502. if (typeInterface != null)
  503. {
  504. IAssetPlugin plug = (IAssetPlugin)Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString()));
  505. server = plug.GetAssetServer();
  506. break;
  507. }
  508. typeInterface = null;
  509. }
  510. }
  511. }
  512. pluginAssembly = null;
  513. return server;
  514. }
  515. }
  516. public class AssetRequest
  517. {
  518. public ClientView RequestUser;
  519. public LLUUID RequestAssetID;
  520. public AssetInfo AssetInf;
  521. public TextureImage ImageInfo;
  522. public LLUUID TransferRequestID;
  523. public long DataPointer = 0;
  524. public int NumPackets = 0;
  525. public int PacketCounter = 0;
  526. public bool IsTextureRequest;
  527. //public bool AssetInCache;
  528. //public int TimeRequested;
  529. public AssetRequest()
  530. {
  531. }
  532. }
  533. public class AssetInfo : AssetBase
  534. {
  535. public AssetInfo()
  536. {
  537. }
  538. public AssetInfo(AssetBase aBase)
  539. {
  540. Data = aBase.Data;
  541. FullID = aBase.FullID;
  542. Type = aBase.Type;
  543. InvType = aBase.InvType;
  544. Name = aBase.Name;
  545. Description = aBase.Description;
  546. }
  547. }
  548. public class TextureImage : AssetBase
  549. {
  550. public TextureImage()
  551. {
  552. }
  553. public TextureImage(AssetBase aBase)
  554. {
  555. Data = aBase.Data;
  556. FullID = aBase.FullID;
  557. Type = aBase.Type;
  558. InvType = aBase.InvType;
  559. Name = aBase.Name;
  560. Description = aBase.Description;
  561. }
  562. }
  563. }