SceneObjectPartInventory.cs 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Xml;
  29. using System.IO;
  30. using System.Collections.Generic;
  31. using System.Collections;
  32. using System.Reflection;
  33. using System.Threading;
  34. using OpenMetaverse;
  35. using log4net;
  36. using OpenSim.Framework;
  37. using OpenSim.Region.Framework.Interfaces;
  38. using OpenSim.Region.Framework.Scenes.Scripting;
  39. using OpenSim.Region.Framework.Scenes.Serialization;
  40. namespace OpenSim.Region.Framework.Scenes
  41. {
  42. public class SceneObjectPartInventory : IEntityInventory
  43. {
  44. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  45. private string m_inventoryFileName = String.Empty;
  46. private byte[] m_inventoryFileData = new byte[0];
  47. private uint m_inventoryFileNameSerial = 0;
  48. /// <value>
  49. /// The part to which the inventory belongs.
  50. /// </value>
  51. private SceneObjectPart m_part;
  52. /// <summary>
  53. /// Serial count for inventory file , used to tell if inventory has changed
  54. /// no need for this to be part of Database backup
  55. /// </summary>
  56. protected uint m_inventorySerial = 0;
  57. /// <summary>
  58. /// Holds in memory prim inventory
  59. /// </summary>
  60. protected TaskInventoryDictionary m_items = new TaskInventoryDictionary();
  61. /// <summary>
  62. /// Tracks whether inventory has changed since the last persistent backup
  63. /// </summary>
  64. internal bool HasInventoryChanged;
  65. /// <value>
  66. /// Inventory serial number
  67. /// </value>
  68. protected internal uint Serial
  69. {
  70. get { return m_inventorySerial; }
  71. set { m_inventorySerial = value; }
  72. }
  73. /// <value>
  74. /// Raw inventory data
  75. /// </value>
  76. protected internal TaskInventoryDictionary Items
  77. {
  78. get { return m_items; }
  79. set
  80. {
  81. m_items = value;
  82. m_inventorySerial++;
  83. }
  84. }
  85. /// <summary>
  86. /// Constructor
  87. /// </summary>
  88. /// <param name="part">
  89. /// A <see cref="SceneObjectPart"/>
  90. /// </param>
  91. public SceneObjectPartInventory(SceneObjectPart part)
  92. {
  93. m_part = part;
  94. }
  95. /// <summary>
  96. /// Force the task inventory of this prim to persist at the next update sweep
  97. /// </summary>
  98. public void ForceInventoryPersistence()
  99. {
  100. HasInventoryChanged = true;
  101. }
  102. /// <summary>
  103. /// Reset UUIDs for all the items in the prim's inventory.
  104. /// </summary>
  105. /// <remarks>
  106. /// This involves either generating
  107. /// new ones or setting existing UUIDs to the correct parent UUIDs.
  108. ///
  109. /// If this method is called and there are inventory items, then we regard the inventory as having changed.
  110. /// </remarks>
  111. public void ResetInventoryIDs()
  112. {
  113. if (null == m_part)
  114. return;
  115. lock (m_items)
  116. {
  117. if (0 == m_items.Count)
  118. return;
  119. IList<TaskInventoryItem> items = GetInventoryItems();
  120. m_items.Clear();
  121. foreach (TaskInventoryItem item in items)
  122. {
  123. item.ResetIDs(m_part.UUID);
  124. m_items.Add(item.ItemID, item);
  125. }
  126. }
  127. }
  128. public void ResetObjectID()
  129. {
  130. lock (Items)
  131. {
  132. IList<TaskInventoryItem> items = new List<TaskInventoryItem>(Items.Values);
  133. Items.Clear();
  134. foreach (TaskInventoryItem item in items)
  135. {
  136. item.ParentPartID = m_part.UUID;
  137. item.ParentID = m_part.UUID;
  138. Items.Add(item.ItemID, item);
  139. }
  140. }
  141. }
  142. /// <summary>
  143. /// Change every item in this inventory to a new owner.
  144. /// </summary>
  145. /// <param name="ownerId"></param>
  146. public void ChangeInventoryOwner(UUID ownerId)
  147. {
  148. lock (Items)
  149. {
  150. if (0 == Items.Count)
  151. {
  152. return;
  153. }
  154. }
  155. HasInventoryChanged = true;
  156. m_part.ParentGroup.HasGroupChanged = true;
  157. List<TaskInventoryItem> items = GetInventoryItems();
  158. foreach (TaskInventoryItem item in items)
  159. {
  160. if (ownerId != item.OwnerID)
  161. item.LastOwnerID = item.OwnerID;
  162. item.OwnerID = ownerId;
  163. item.PermsMask = 0;
  164. item.PermsGranter = UUID.Zero;
  165. item.OwnerChanged = true;
  166. }
  167. }
  168. /// <summary>
  169. /// Change every item in this inventory to a new group.
  170. /// </summary>
  171. /// <param name="groupID"></param>
  172. public void ChangeInventoryGroup(UUID groupID)
  173. {
  174. lock (Items)
  175. {
  176. if (0 == Items.Count)
  177. {
  178. return;
  179. }
  180. }
  181. // Don't let this set the HasGroupChanged flag for attachments
  182. // as this happens during rez and we don't want a new asset
  183. // for each attachment each time
  184. if (!m_part.ParentGroup.IsAttachment)
  185. {
  186. HasInventoryChanged = true;
  187. m_part.ParentGroup.HasGroupChanged = true;
  188. }
  189. List<TaskInventoryItem> items = GetInventoryItems();
  190. foreach (TaskInventoryItem item in items)
  191. {
  192. if (groupID != item.GroupID)
  193. item.GroupID = groupID;
  194. }
  195. }
  196. public int CreateScriptInstances(int startParam, bool postOnRez, string engine, int stateSource)
  197. {
  198. int scriptsValidForStarting = 0;
  199. List<TaskInventoryItem> scripts = GetInventoryItems(InventoryType.LSL);
  200. foreach (TaskInventoryItem item in scripts)
  201. if (CreateScriptInstance(item, startParam, postOnRez, engine, stateSource))
  202. scriptsValidForStarting++;
  203. return scriptsValidForStarting;
  204. }
  205. public ArrayList GetScriptErrors(UUID itemID)
  206. {
  207. ArrayList ret = new ArrayList();
  208. IScriptModule[] engines = m_part.ParentGroup.Scene.RequestModuleInterfaces<IScriptModule>();
  209. foreach (IScriptModule e in engines)
  210. {
  211. if (e != null)
  212. {
  213. ArrayList errors = e.GetScriptErrors(itemID);
  214. foreach (Object line in errors)
  215. ret.Add(line);
  216. }
  217. }
  218. return ret;
  219. }
  220. /// <summary>
  221. /// Stop and remove all the scripts in this prim.
  222. /// </summary>
  223. /// <param name="sceneObjectBeingDeleted">
  224. /// Should be true if these scripts are being removed because the scene
  225. /// object is being deleted. This will prevent spurious updates to the client.
  226. /// </param>
  227. public void RemoveScriptInstances(bool sceneObjectBeingDeleted)
  228. {
  229. List<TaskInventoryItem> scripts = GetInventoryItems(InventoryType.LSL);
  230. foreach (TaskInventoryItem item in scripts)
  231. RemoveScriptInstance(item.ItemID, sceneObjectBeingDeleted);
  232. }
  233. /// <summary>
  234. /// Stop all the scripts in this prim.
  235. /// </summary>
  236. public void StopScriptInstances()
  237. {
  238. GetInventoryItems(InventoryType.LSL).ForEach(i => StopScriptInstance(i));
  239. }
  240. /// <summary>
  241. /// Start a script which is in this prim's inventory.
  242. /// </summary>
  243. /// <param name="item"></param>
  244. /// <returns>true if the script instance was created, false otherwise</returns>
  245. public bool CreateScriptInstance(TaskInventoryItem item, int startParam, bool postOnRez, string engine, int stateSource)
  246. {
  247. // m_log.DebugFormat("[PRIM INVENTORY]: Starting script {0} {1} in prim {2} {3} in {4}",
  248. // item.Name, item.ItemID, m_part.Name, m_part.UUID, m_part.ParentGroup.Scene.RegionInfo.RegionName);
  249. if (!m_part.ParentGroup.Scene.Permissions.CanRunScript(item.ItemID, m_part.UUID, item.OwnerID))
  250. return false;
  251. m_part.AddFlag(PrimFlags.Scripted);
  252. if (m_part.ParentGroup.Scene.RegionInfo.RegionSettings.DisableScripts)
  253. return false;
  254. if (stateSource == 2 && // Prim crossing
  255. m_part.ParentGroup.Scene.m_trustBinaries)
  256. {
  257. lock (m_items)
  258. {
  259. m_items[item.ItemID].PermsMask = 0;
  260. m_items[item.ItemID].PermsGranter = UUID.Zero;
  261. }
  262. m_part.ParentGroup.Scene.EventManager.TriggerRezScript(
  263. m_part.LocalId, item.ItemID, String.Empty, startParam, postOnRez, engine, stateSource);
  264. m_part.ParentGroup.AddActiveScriptCount(1);
  265. m_part.ScheduleFullUpdate();
  266. return true;
  267. }
  268. AssetBase asset = m_part.ParentGroup.Scene.AssetService.Get(item.AssetID.ToString());
  269. if (null == asset)
  270. {
  271. m_log.ErrorFormat(
  272. "[PRIM INVENTORY]: Couldn't start script {0}, {1} at {2} in {3} since asset ID {4} could not be found",
  273. item.Name, item.ItemID, m_part.AbsolutePosition,
  274. m_part.ParentGroup.Scene.RegionInfo.RegionName, item.AssetID);
  275. return false;
  276. }
  277. else
  278. {
  279. if (m_part.ParentGroup.m_savedScriptState != null)
  280. item.OldItemID = RestoreSavedScriptState(item.LoadedItemID, item.OldItemID, item.ItemID);
  281. lock (m_items)
  282. {
  283. m_items[item.ItemID].OldItemID = item.OldItemID;
  284. m_items[item.ItemID].PermsMask = 0;
  285. m_items[item.ItemID].PermsGranter = UUID.Zero;
  286. }
  287. string script = Utils.BytesToString(asset.Data);
  288. m_part.ParentGroup.Scene.EventManager.TriggerRezScript(
  289. m_part.LocalId, item.ItemID, script, startParam, postOnRez, engine, stateSource);
  290. m_part.ParentGroup.AddActiveScriptCount(1);
  291. m_part.ScheduleFullUpdate();
  292. return true;
  293. }
  294. }
  295. private UUID RestoreSavedScriptState(UUID loadedID, UUID oldID, UUID newID)
  296. {
  297. IScriptModule[] engines = m_part.ParentGroup.Scene.RequestModuleInterfaces<IScriptModule>();
  298. if (engines.Length == 0) // No engine at all
  299. return oldID;
  300. UUID stateID = oldID;
  301. if (!m_part.ParentGroup.m_savedScriptState.ContainsKey(oldID))
  302. stateID = loadedID;
  303. if (m_part.ParentGroup.m_savedScriptState.ContainsKey(stateID))
  304. {
  305. XmlDocument doc = new XmlDocument();
  306. doc.LoadXml(m_part.ParentGroup.m_savedScriptState[stateID]);
  307. ////////// CRUFT WARNING ///////////////////////////////////
  308. //
  309. // Old objects will have <ScriptState><State> ...
  310. // This format is XEngine ONLY
  311. //
  312. // New objects have <State Engine="...." ...><ScriptState>...
  313. // This can be passed to any engine
  314. //
  315. XmlNode n = doc.SelectSingleNode("ScriptState");
  316. if (n != null) // Old format data
  317. {
  318. XmlDocument newDoc = new XmlDocument();
  319. XmlElement rootN = newDoc.CreateElement("", "State", "");
  320. XmlAttribute uuidA = newDoc.CreateAttribute("", "UUID", "");
  321. uuidA.Value = stateID.ToString();
  322. rootN.Attributes.Append(uuidA);
  323. XmlAttribute engineA = newDoc.CreateAttribute("", "Engine", "");
  324. engineA.Value = "XEngine";
  325. rootN.Attributes.Append(engineA);
  326. newDoc.AppendChild(rootN);
  327. XmlNode stateN = newDoc.ImportNode(n, true);
  328. rootN.AppendChild(stateN);
  329. // This created document has only the minimun data
  330. // necessary for XEngine to parse it successfully
  331. m_part.ParentGroup.m_savedScriptState[stateID] = newDoc.OuterXml;
  332. }
  333. foreach (IScriptModule e in engines)
  334. {
  335. if (e != null)
  336. {
  337. if (e.SetXMLState(newID, m_part.ParentGroup.m_savedScriptState[stateID]))
  338. break;
  339. }
  340. }
  341. m_part.ParentGroup.m_savedScriptState.Remove(stateID);
  342. }
  343. return stateID;
  344. }
  345. public bool CreateScriptInstance(UUID itemId, int startParam, bool postOnRez, string engine, int stateSource)
  346. {
  347. TaskInventoryItem item = GetInventoryItem(itemId);
  348. if (item != null)
  349. {
  350. return CreateScriptInstance(item, startParam, postOnRez, engine, stateSource);
  351. }
  352. else
  353. {
  354. m_log.ErrorFormat(
  355. "[PRIM INVENTORY]: Couldn't start script with ID {0} since it couldn't be found for prim {1}, {2} at {3} in {4}",
  356. itemId, m_part.Name, m_part.UUID,
  357. m_part.AbsolutePosition, m_part.ParentGroup.Scene.RegionInfo.RegionName);
  358. return false;
  359. }
  360. }
  361. /// <summary>
  362. /// Stop and remove a script which is in this prim's inventory.
  363. /// </summary>
  364. /// <param name="itemId"></param>
  365. /// <param name="sceneObjectBeingDeleted">
  366. /// Should be true if this script is being removed because the scene
  367. /// object is being deleted. This will prevent spurious updates to the client.
  368. /// </param>
  369. public void RemoveScriptInstance(UUID itemId, bool sceneObjectBeingDeleted)
  370. {
  371. bool scriptPresent = false;
  372. lock (m_items)
  373. {
  374. if (m_items.ContainsKey(itemId))
  375. scriptPresent = true;
  376. }
  377. if (scriptPresent)
  378. {
  379. if (!sceneObjectBeingDeleted)
  380. m_part.RemoveScriptEvents(itemId);
  381. m_part.ParentGroup.Scene.EventManager.TriggerRemoveScript(m_part.LocalId, itemId);
  382. m_part.ParentGroup.AddActiveScriptCount(-1);
  383. }
  384. else
  385. {
  386. m_log.WarnFormat(
  387. "[PRIM INVENTORY]: " +
  388. "Couldn't stop script with ID {0} since it couldn't be found for prim {1}, {2} at {3} in {4}",
  389. itemId, m_part.Name, m_part.UUID,
  390. m_part.AbsolutePosition, m_part.ParentGroup.Scene.RegionInfo.RegionName);
  391. }
  392. }
  393. /// <summary>
  394. /// Stop a script which is in this prim's inventory.
  395. /// </summary>
  396. /// <param name="itemId"></param>
  397. /// <param name="sceneObjectBeingDeleted">
  398. /// Should be true if this script is being removed because the scene
  399. /// object is being deleted. This will prevent spurious updates to the client.
  400. /// </param>
  401. public void StopScriptInstance(UUID itemId)
  402. {
  403. TaskInventoryItem scriptItem;
  404. lock (m_items)
  405. m_items.TryGetValue(itemId, out scriptItem);
  406. if (scriptItem != null)
  407. {
  408. StopScriptInstance(scriptItem);
  409. }
  410. else
  411. {
  412. m_log.WarnFormat(
  413. "[PRIM INVENTORY]: " +
  414. "Couldn't stop script with ID {0} since it couldn't be found for prim {1}, {2} at {3} in {4}",
  415. itemId, m_part.Name, m_part.UUID,
  416. m_part.AbsolutePosition, m_part.ParentGroup.Scene.RegionInfo.RegionName);
  417. }
  418. }
  419. /// <summary>
  420. /// Stop a script which is in this prim's inventory.
  421. /// </summary>
  422. /// <param name="itemId"></param>
  423. /// <param name="sceneObjectBeingDeleted">
  424. /// Should be true if this script is being removed because the scene
  425. /// object is being deleted. This will prevent spurious updates to the client.
  426. /// </param>
  427. public void StopScriptInstance(TaskInventoryItem item)
  428. {
  429. m_part.ParentGroup.Scene.EventManager.TriggerStopScript(m_part.LocalId, item.ItemID);
  430. // At the moment, even stopped scripts are counted as active, which is probably wrong.
  431. // m_part.ParentGroup.AddActiveScriptCount(-1);
  432. }
  433. /// <summary>
  434. /// Check if the inventory holds an item with a given name.
  435. /// </summary>
  436. /// <param name="name"></param>
  437. /// <returns></returns>
  438. private bool InventoryContainsName(string name)
  439. {
  440. lock (m_items)
  441. {
  442. foreach (TaskInventoryItem item in m_items.Values)
  443. {
  444. if (item.Name == name)
  445. return true;
  446. }
  447. }
  448. return false;
  449. }
  450. /// <summary>
  451. /// For a given item name, return that name if it is available. Otherwise, return the next available
  452. /// similar name (which is currently the original name with the next available numeric suffix).
  453. /// </summary>
  454. /// <param name="name"></param>
  455. /// <returns></returns>
  456. private string FindAvailableInventoryName(string name)
  457. {
  458. if (!InventoryContainsName(name))
  459. return name;
  460. int suffix=1;
  461. while (suffix < 256)
  462. {
  463. string tryName=String.Format("{0} {1}", name, suffix);
  464. if (!InventoryContainsName(tryName))
  465. return tryName;
  466. suffix++;
  467. }
  468. return String.Empty;
  469. }
  470. /// <summary>
  471. /// Add an item to this prim's inventory. If an item with the same name already exists, then an alternative
  472. /// name is chosen.
  473. /// </summary>
  474. /// <param name="item"></param>
  475. public void AddInventoryItem(TaskInventoryItem item, bool allowedDrop)
  476. {
  477. AddInventoryItem(item.Name, item, allowedDrop);
  478. }
  479. /// <summary>
  480. /// Add an item to this prim's inventory. If an item with the same name already exists, it is replaced.
  481. /// </summary>
  482. /// <param name="item"></param>
  483. public void AddInventoryItemExclusive(TaskInventoryItem item, bool allowedDrop)
  484. {
  485. List<TaskInventoryItem> il = GetInventoryItems();
  486. foreach (TaskInventoryItem i in il)
  487. {
  488. if (i.Name == item.Name)
  489. {
  490. if (i.InvType == (int)InventoryType.LSL)
  491. RemoveScriptInstance(i.ItemID, false);
  492. RemoveInventoryItem(i.ItemID);
  493. break;
  494. }
  495. }
  496. AddInventoryItem(item.Name, item, allowedDrop);
  497. }
  498. /// <summary>
  499. /// Add an item to this prim's inventory.
  500. /// </summary>
  501. /// <param name="name">The name that the new item should have.</param>
  502. /// <param name="item">
  503. /// The item itself. The name within this structure is ignored in favour of the name
  504. /// given in this method's arguments
  505. /// </param>
  506. /// <param name="allowedDrop">
  507. /// Item was only added to inventory because AllowedDrop is set
  508. /// </param>
  509. protected void AddInventoryItem(string name, TaskInventoryItem item, bool allowedDrop)
  510. {
  511. name = FindAvailableInventoryName(name);
  512. if (name == String.Empty)
  513. return;
  514. item.ParentID = m_part.UUID;
  515. item.ParentPartID = m_part.UUID;
  516. item.Name = name;
  517. item.GroupID = m_part.GroupID;
  518. lock (m_items)
  519. m_items.Add(item.ItemID, item);
  520. if (allowedDrop)
  521. m_part.TriggerScriptChangedEvent(Changed.ALLOWED_DROP);
  522. else
  523. m_part.TriggerScriptChangedEvent(Changed.INVENTORY);
  524. m_inventorySerial++;
  525. //m_inventorySerial += 2;
  526. HasInventoryChanged = true;
  527. m_part.ParentGroup.HasGroupChanged = true;
  528. }
  529. /// <summary>
  530. /// Restore a whole collection of items to the prim's inventory at once.
  531. /// We assume that the items already have all their fields correctly filled out.
  532. /// The items are not flagged for persistence to the database, since they are being restored
  533. /// from persistence rather than being newly added.
  534. /// </summary>
  535. /// <param name="items"></param>
  536. public void RestoreInventoryItems(ICollection<TaskInventoryItem> items)
  537. {
  538. lock (m_items)
  539. {
  540. foreach (TaskInventoryItem item in items)
  541. {
  542. m_items.Add(item.ItemID, item);
  543. // m_part.TriggerScriptChangedEvent(Changed.INVENTORY);
  544. }
  545. m_inventorySerial++;
  546. }
  547. }
  548. /// <summary>
  549. /// Returns an existing inventory item. Returns the original, so any changes will be live.
  550. /// </summary>
  551. /// <param name="itemID"></param>
  552. /// <returns>null if the item does not exist</returns>
  553. public TaskInventoryItem GetInventoryItem(UUID itemId)
  554. {
  555. TaskInventoryItem item;
  556. lock (m_items)
  557. m_items.TryGetValue(itemId, out item);
  558. return item;
  559. }
  560. public TaskInventoryItem GetInventoryItem(string name)
  561. {
  562. lock (m_items)
  563. {
  564. foreach (TaskInventoryItem item in m_items.Values)
  565. {
  566. if (item.Name == name)
  567. return item;
  568. }
  569. }
  570. return null;
  571. }
  572. public List<TaskInventoryItem> GetInventoryItems(string name)
  573. {
  574. List<TaskInventoryItem> items = new List<TaskInventoryItem>();
  575. lock (m_items)
  576. {
  577. foreach (TaskInventoryItem item in m_items.Values)
  578. {
  579. if (item.Name == name)
  580. items.Add(item);
  581. }
  582. }
  583. return items;
  584. }
  585. public SceneObjectGroup GetRezReadySceneObject(TaskInventoryItem item)
  586. {
  587. AssetBase rezAsset = m_part.ParentGroup.Scene.AssetService.Get(item.AssetID.ToString());
  588. if (null == rezAsset)
  589. {
  590. m_log.WarnFormat(
  591. "[PRIM INVENTORY]: Could not find asset {0} for inventory item {1} in {2}",
  592. item.AssetID, item.Name, m_part.Name);
  593. return null;
  594. }
  595. string xmlData = Utils.BytesToString(rezAsset.Data);
  596. SceneObjectGroup group = SceneObjectSerializer.FromOriginalXmlFormat(xmlData);
  597. group.ResetIDs();
  598. SceneObjectPart rootPart = group.GetChildPart(group.UUID);
  599. // Since renaming the item in the inventory does not affect the name stored
  600. // in the serialization, transfer the correct name from the inventory to the
  601. // object itself before we rez.
  602. rootPart.Name = item.Name;
  603. rootPart.Description = item.Description;
  604. SceneObjectPart[] partList = group.Parts;
  605. group.SetGroup(m_part.GroupID, null);
  606. // TODO: Remove magic number badness
  607. if ((rootPart.OwnerID != item.OwnerID) || (item.CurrentPermissions & 16) != 0 || (item.Flags & (uint)InventoryItemFlags.ObjectSlamPerm) != 0) // Magic number
  608. {
  609. if (m_part.ParentGroup.Scene.Permissions.PropagatePermissions())
  610. {
  611. foreach (SceneObjectPart part in partList)
  612. {
  613. if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteEveryone) != 0)
  614. part.EveryoneMask = item.EveryonePermissions;
  615. if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteNextOwner) != 0)
  616. part.NextOwnerMask = item.NextPermissions;
  617. if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteGroup) != 0)
  618. part.GroupMask = item.GroupPermissions;
  619. }
  620. group.ApplyNextOwnerPermissions();
  621. }
  622. }
  623. foreach (SceneObjectPart part in partList)
  624. {
  625. // TODO: Remove magic number badness
  626. if ((part.OwnerID != item.OwnerID) || (item.CurrentPermissions & 16) != 0 || (item.Flags & (uint)InventoryItemFlags.ObjectSlamPerm) != 0) // Magic number
  627. {
  628. part.LastOwnerID = part.OwnerID;
  629. part.OwnerID = item.OwnerID;
  630. part.Inventory.ChangeInventoryOwner(item.OwnerID);
  631. }
  632. if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteEveryone) != 0)
  633. part.EveryoneMask = item.EveryonePermissions;
  634. if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteNextOwner) != 0)
  635. part.NextOwnerMask = item.NextPermissions;
  636. if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteGroup) != 0)
  637. part.GroupMask = item.GroupPermissions;
  638. }
  639. rootPart.TrimPermissions();
  640. return group;
  641. }
  642. /// <summary>
  643. /// Update an existing inventory item.
  644. /// </summary>
  645. /// <param name="item">The updated item. An item with the same id must already exist
  646. /// in this prim's inventory.</param>
  647. /// <returns>false if the item did not exist, true if the update occurred successfully</returns>
  648. public bool UpdateInventoryItem(TaskInventoryItem item)
  649. {
  650. return UpdateInventoryItem(item, true, true);
  651. }
  652. public bool UpdateInventoryItem(TaskInventoryItem item, bool fireScriptEvents)
  653. {
  654. return UpdateInventoryItem(item, fireScriptEvents, true);
  655. }
  656. public bool UpdateInventoryItem(TaskInventoryItem item, bool fireScriptEvents, bool considerChanged)
  657. {
  658. TaskInventoryItem it = GetInventoryItem(item.ItemID);
  659. if (it != null)
  660. {
  661. // m_log.DebugFormat("[PRIM INVENTORY]: Updating item {0} in {1}", item.Name, m_part.Name);
  662. item.ParentID = m_part.UUID;
  663. item.ParentPartID = m_part.UUID;
  664. // If group permissions have been set on, check that the groupID is up to date in case it has
  665. // changed since permissions were last set.
  666. if (item.GroupPermissions != (uint)PermissionMask.None)
  667. item.GroupID = m_part.GroupID;
  668. if (item.AssetID == UUID.Zero)
  669. item.AssetID = it.AssetID;
  670. lock (m_items)
  671. {
  672. m_items[item.ItemID] = item;
  673. m_inventorySerial++;
  674. }
  675. if (fireScriptEvents)
  676. m_part.TriggerScriptChangedEvent(Changed.INVENTORY);
  677. if (considerChanged)
  678. {
  679. HasInventoryChanged = true;
  680. m_part.ParentGroup.HasGroupChanged = true;
  681. }
  682. return true;
  683. }
  684. else
  685. {
  686. m_log.ErrorFormat(
  687. "[PRIM INVENTORY]: " +
  688. "Tried to retrieve item ID {0} from prim {1}, {2} at {3} in {4} but the item does not exist in this inventory",
  689. item.ItemID, m_part.Name, m_part.UUID,
  690. m_part.AbsolutePosition, m_part.ParentGroup.Scene.RegionInfo.RegionName);
  691. }
  692. return false;
  693. }
  694. /// <summary>
  695. /// Remove an item from this prim's inventory
  696. /// </summary>
  697. /// <param name="itemID"></param>
  698. /// <returns>Numeric asset type of the item removed. Returns -1 if the item did not exist
  699. /// in this prim's inventory.</returns>
  700. public int RemoveInventoryItem(UUID itemID)
  701. {
  702. TaskInventoryItem item = GetInventoryItem(itemID);
  703. if (item != null)
  704. {
  705. int type = m_items[itemID].InvType;
  706. if (type == 10) // Script
  707. {
  708. m_part.RemoveScriptEvents(itemID);
  709. m_part.ParentGroup.Scene.EventManager.TriggerRemoveScript(m_part.LocalId, itemID);
  710. }
  711. m_items.Remove(itemID);
  712. m_inventorySerial++;
  713. m_part.TriggerScriptChangedEvent(Changed.INVENTORY);
  714. HasInventoryChanged = true;
  715. m_part.ParentGroup.HasGroupChanged = true;
  716. if (!ContainsScripts())
  717. m_part.RemFlag(PrimFlags.Scripted);
  718. m_part.ScheduleFullUpdate();
  719. return type;
  720. }
  721. else
  722. {
  723. m_log.ErrorFormat(
  724. "[PRIM INVENTORY]: " +
  725. "Tried to remove item ID {0} from prim {1}, {2} at {3} in {4} but the item does not exist in this inventory",
  726. itemID, m_part.Name, m_part.UUID,
  727. m_part.AbsolutePosition, m_part.ParentGroup.Scene.RegionInfo.RegionName);
  728. }
  729. return -1;
  730. }
  731. private bool CreateInventoryFile()
  732. {
  733. // m_log.DebugFormat(
  734. // "[PRIM INVENTORY]: Creating inventory file for {0} {1} {2}, serial {3}",
  735. // m_part.Name, m_part.UUID, m_part.LocalId, m_inventorySerial);
  736. if (m_inventoryFileName == String.Empty ||
  737. m_inventoryFileNameSerial < m_inventorySerial)
  738. {
  739. // Something changed, we need to create a new file
  740. m_inventoryFileName = "inventory_" + UUID.Random().ToString() + ".tmp";
  741. m_inventoryFileNameSerial = m_inventorySerial;
  742. InventoryStringBuilder invString = new InventoryStringBuilder(m_part.UUID, UUID.Zero);
  743. lock (m_items)
  744. {
  745. foreach (TaskInventoryItem item in m_items.Values)
  746. {
  747. // m_log.DebugFormat(
  748. // "[PRIM INVENTORY]: Adding item {0} {1} for serial {2} on prim {3} {4} {5}",
  749. // item.Name, item.ItemID, m_inventorySerial, m_part.Name, m_part.UUID, m_part.LocalId);
  750. UUID ownerID = item.OwnerID;
  751. uint everyoneMask = 0;
  752. uint baseMask = item.BasePermissions;
  753. uint ownerMask = item.CurrentPermissions;
  754. uint groupMask = item.GroupPermissions;
  755. invString.AddItemStart();
  756. invString.AddNameValueLine("item_id", item.ItemID.ToString());
  757. invString.AddNameValueLine("parent_id", m_part.UUID.ToString());
  758. invString.AddPermissionsStart();
  759. invString.AddNameValueLine("base_mask", Utils.UIntToHexString(baseMask));
  760. invString.AddNameValueLine("owner_mask", Utils.UIntToHexString(ownerMask));
  761. invString.AddNameValueLine("group_mask", Utils.UIntToHexString(groupMask));
  762. invString.AddNameValueLine("everyone_mask", Utils.UIntToHexString(everyoneMask));
  763. invString.AddNameValueLine("next_owner_mask", Utils.UIntToHexString(item.NextPermissions));
  764. invString.AddNameValueLine("creator_id", item.CreatorID.ToString());
  765. invString.AddNameValueLine("owner_id", ownerID.ToString());
  766. invString.AddNameValueLine("last_owner_id", item.LastOwnerID.ToString());
  767. invString.AddNameValueLine("group_id", item.GroupID.ToString());
  768. invString.AddSectionEnd();
  769. invString.AddNameValueLine("asset_id", item.AssetID.ToString());
  770. invString.AddNameValueLine("type", Utils.AssetTypeToString((AssetType)item.Type));
  771. invString.AddNameValueLine("inv_type", Utils.InventoryTypeToString((InventoryType)item.InvType));
  772. invString.AddNameValueLine("flags", Utils.UIntToHexString(item.Flags));
  773. invString.AddSaleStart();
  774. invString.AddNameValueLine("sale_type", "not");
  775. invString.AddNameValueLine("sale_price", "0");
  776. invString.AddSectionEnd();
  777. invString.AddNameValueLine("name", item.Name + "|");
  778. invString.AddNameValueLine("desc", item.Description + "|");
  779. invString.AddNameValueLine("creation_date", item.CreationDate.ToString());
  780. invString.AddSectionEnd();
  781. }
  782. }
  783. m_inventoryFileData = Utils.StringToBytes(invString.BuildString);
  784. return true;
  785. }
  786. // No need to recreate, the existing file is fine
  787. return false;
  788. }
  789. /// <summary>
  790. /// Serialize all the metadata for the items in this prim's inventory ready for sending to the client
  791. /// </summary>
  792. /// <param name="xferManager"></param>
  793. public void RequestInventoryFile(IClientAPI client, IXfer xferManager)
  794. {
  795. lock (m_items)
  796. {
  797. // Don't send a inventory xfer name if there are no items. Doing so causes viewer 3 to crash when rezzing
  798. // a new script if any previous deletion has left the prim inventory empty.
  799. if (m_items.Count == 0) // No inventory
  800. {
  801. // m_log.DebugFormat(
  802. // "[PRIM INVENTORY]: Not sending inventory data for part {0} {1} {2} for {3} since no items",
  803. // m_part.Name, m_part.LocalId, m_part.UUID, client.Name);
  804. client.SendTaskInventory(m_part.UUID, 0, new byte[0]);
  805. return;
  806. }
  807. CreateInventoryFile();
  808. // In principle, we should only do the rest if the inventory changed;
  809. // by sending m_inventorySerial to the client, it ought to know
  810. // that nothing changed and that it doesn't need to request the file.
  811. // Unfortunately, it doesn't look like the client optimizes this;
  812. // the client seems to always come back and request the Xfer,
  813. // no matter what value m_inventorySerial has.
  814. // FIXME: Could probably be > 0 here rather than > 2
  815. if (m_inventoryFileData.Length > 2)
  816. {
  817. // Add the file for Xfer
  818. // m_log.DebugFormat(
  819. // "[PRIM INVENTORY]: Adding inventory file {0} (length {1}) for transfer on {2} {3} {4}",
  820. // m_inventoryFileName, m_inventoryFileData.Length, m_part.Name, m_part.UUID, m_part.LocalId);
  821. xferManager.AddNewFile(m_inventoryFileName, m_inventoryFileData);
  822. }
  823. // Tell the client we're ready to Xfer the file
  824. client.SendTaskInventory(m_part.UUID, (short)m_inventorySerial,
  825. Util.StringToBytes256(m_inventoryFileName));
  826. }
  827. }
  828. /// <summary>
  829. /// Process inventory backup
  830. /// </summary>
  831. /// <param name="datastore"></param>
  832. public void ProcessInventoryBackup(ISimulationDataService datastore)
  833. {
  834. if (HasInventoryChanged)
  835. {
  836. HasInventoryChanged = false;
  837. List<TaskInventoryItem> items = GetInventoryItems();
  838. datastore.StorePrimInventory(m_part.UUID, items);
  839. }
  840. }
  841. public class InventoryStringBuilder
  842. {
  843. public string BuildString = String.Empty;
  844. public InventoryStringBuilder(UUID folderID, UUID parentID)
  845. {
  846. BuildString += "\tinv_object\t0\n\t{\n";
  847. AddNameValueLine("obj_id", folderID.ToString());
  848. AddNameValueLine("parent_id", parentID.ToString());
  849. AddNameValueLine("type", "category");
  850. AddNameValueLine("name", "Contents|");
  851. AddSectionEnd();
  852. }
  853. public void AddItemStart()
  854. {
  855. BuildString += "\tinv_item\t0\n";
  856. AddSectionStart();
  857. }
  858. public void AddPermissionsStart()
  859. {
  860. BuildString += "\tpermissions 0\n";
  861. AddSectionStart();
  862. }
  863. public void AddSaleStart()
  864. {
  865. BuildString += "\tsale_info\t0\n";
  866. AddSectionStart();
  867. }
  868. protected void AddSectionStart()
  869. {
  870. BuildString += "\t{\n";
  871. }
  872. public void AddSectionEnd()
  873. {
  874. BuildString += "\t}\n";
  875. }
  876. public void AddLine(string addLine)
  877. {
  878. BuildString += addLine;
  879. }
  880. public void AddNameValueLine(string name, string value)
  881. {
  882. BuildString += "\t\t";
  883. BuildString += name + "\t";
  884. BuildString += value + "\n";
  885. }
  886. public void Close()
  887. {
  888. }
  889. }
  890. public uint MaskEffectivePermissions()
  891. {
  892. uint mask=0x7fffffff;
  893. lock (m_items)
  894. {
  895. foreach (TaskInventoryItem item in m_items.Values)
  896. {
  897. if ((item.CurrentPermissions & item.NextPermissions & (uint)PermissionMask.Copy) == 0)
  898. mask &= ~((uint)PermissionMask.Copy >> 13);
  899. if ((item.CurrentPermissions & item.NextPermissions & (uint)PermissionMask.Transfer) == 0)
  900. mask &= ~((uint)PermissionMask.Transfer >> 13);
  901. if ((item.CurrentPermissions & item.NextPermissions & (uint)PermissionMask.Modify) == 0)
  902. mask &= ~((uint)PermissionMask.Modify >> 13);
  903. if (item.InvType != (int)InventoryType.Object)
  904. {
  905. if ((item.CurrentPermissions & item.NextPermissions & (uint)PermissionMask.Copy) == 0)
  906. mask &= ~((uint)PermissionMask.Copy >> 13);
  907. if ((item.CurrentPermissions & item.NextPermissions & (uint)PermissionMask.Transfer) == 0)
  908. mask &= ~((uint)PermissionMask.Transfer >> 13);
  909. if ((item.CurrentPermissions & item.NextPermissions & (uint)PermissionMask.Modify) == 0)
  910. mask &= ~((uint)PermissionMask.Modify >> 13);
  911. }
  912. else
  913. {
  914. if ((item.CurrentPermissions & ((uint)PermissionMask.Copy >> 13)) == 0)
  915. mask &= ~((uint)PermissionMask.Copy >> 13);
  916. if ((item.CurrentPermissions & ((uint)PermissionMask.Transfer >> 13)) == 0)
  917. mask &= ~((uint)PermissionMask.Transfer >> 13);
  918. if ((item.CurrentPermissions & ((uint)PermissionMask.Modify >> 13)) == 0)
  919. mask &= ~((uint)PermissionMask.Modify >> 13);
  920. }
  921. if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0)
  922. mask &= ~(uint)PermissionMask.Copy;
  923. if ((item.CurrentPermissions & (uint)PermissionMask.Transfer) == 0)
  924. mask &= ~(uint)PermissionMask.Transfer;
  925. if ((item.CurrentPermissions & (uint)PermissionMask.Modify) == 0)
  926. mask &= ~(uint)PermissionMask.Modify;
  927. }
  928. }
  929. return mask;
  930. }
  931. public void ApplyNextOwnerPermissions()
  932. {
  933. lock (m_items)
  934. {
  935. foreach (TaskInventoryItem item in m_items.Values)
  936. {
  937. // m_log.DebugFormat (
  938. // "[SCENE OBJECT PART INVENTORY]: Applying next permissions {0} to {1} in {2} with current {3}, base {4}, everyone {5}",
  939. // item.NextPermissions, item.Name, m_part.Name, item.CurrentPermissions, item.BasePermissions, item.EveryonePermissions);
  940. if (item.InvType == (int)InventoryType.Object && (item.CurrentPermissions & 7) != 0)
  941. {
  942. if ((item.CurrentPermissions & ((uint)PermissionMask.Copy >> 13)) == 0)
  943. item.CurrentPermissions &= ~(uint)PermissionMask.Copy;
  944. if ((item.CurrentPermissions & ((uint)PermissionMask.Transfer >> 13)) == 0)
  945. item.CurrentPermissions &= ~(uint)PermissionMask.Transfer;
  946. if ((item.CurrentPermissions & ((uint)PermissionMask.Modify >> 13)) == 0)
  947. item.CurrentPermissions &= ~(uint)PermissionMask.Modify;
  948. }
  949. item.CurrentPermissions &= item.NextPermissions;
  950. item.BasePermissions &= item.NextPermissions;
  951. item.EveryonePermissions &= item.NextPermissions;
  952. item.OwnerChanged = true;
  953. item.PermsMask = 0;
  954. item.PermsGranter = UUID.Zero;
  955. }
  956. }
  957. }
  958. public void ApplyGodPermissions(uint perms)
  959. {
  960. lock (m_items)
  961. {
  962. foreach (TaskInventoryItem item in m_items.Values)
  963. {
  964. item.CurrentPermissions = perms;
  965. item.BasePermissions = perms;
  966. }
  967. }
  968. m_inventorySerial++;
  969. HasInventoryChanged = true;
  970. }
  971. /// <summary>
  972. /// Returns true if this part inventory contains any scripts. False otherwise.
  973. /// </summary>
  974. /// <returns></returns>
  975. public bool ContainsScripts()
  976. {
  977. lock (m_items)
  978. {
  979. foreach (TaskInventoryItem item in m_items.Values)
  980. {
  981. if (item.InvType == (int)InventoryType.LSL)
  982. {
  983. return true;
  984. }
  985. }
  986. }
  987. return false;
  988. }
  989. public List<UUID> GetInventoryList()
  990. {
  991. List<UUID> ret = new List<UUID>();
  992. lock (m_items)
  993. {
  994. foreach (TaskInventoryItem item in m_items.Values)
  995. ret.Add(item.ItemID);
  996. }
  997. return ret;
  998. }
  999. public List<TaskInventoryItem> GetInventoryItems()
  1000. {
  1001. List<TaskInventoryItem> ret = new List<TaskInventoryItem>();
  1002. lock (m_items)
  1003. ret = new List<TaskInventoryItem>(m_items.Values);
  1004. return ret;
  1005. }
  1006. public List<TaskInventoryItem> GetInventoryItems(InventoryType type)
  1007. {
  1008. List<TaskInventoryItem> ret = new List<TaskInventoryItem>();
  1009. lock (m_items)
  1010. {
  1011. foreach (TaskInventoryItem item in m_items.Values)
  1012. if (item.InvType == (int)type)
  1013. ret.Add(item);
  1014. }
  1015. return ret;
  1016. }
  1017. public Dictionary<UUID, string> GetScriptStates()
  1018. {
  1019. Dictionary<UUID, string> ret = new Dictionary<UUID, string>();
  1020. if (m_part.ParentGroup.Scene == null) // Group not in a scene
  1021. return ret;
  1022. IScriptModule[] engines = m_part.ParentGroup.Scene.RequestModuleInterfaces<IScriptModule>();
  1023. if (engines.Length == 0) // No engine at all
  1024. return ret;
  1025. List<TaskInventoryItem> scripts = GetInventoryItems(InventoryType.LSL);
  1026. foreach (TaskInventoryItem item in scripts)
  1027. {
  1028. foreach (IScriptModule e in engines)
  1029. {
  1030. if (e != null)
  1031. {
  1032. // m_log.DebugFormat(
  1033. // "[PRIM INVENTORY]: Getting script state from engine {0} for {1} in part {2} in group {3} in {4}",
  1034. // e.Name, item.Name, m_part.Name, m_part.ParentGroup.Name, m_part.ParentGroup.Scene.Name);
  1035. string n = e.GetXMLState(item.ItemID);
  1036. if (n != String.Empty)
  1037. {
  1038. if (!ret.ContainsKey(item.ItemID))
  1039. ret[item.ItemID] = n;
  1040. break;
  1041. }
  1042. }
  1043. }
  1044. }
  1045. return ret;
  1046. }
  1047. public void ResumeScripts()
  1048. {
  1049. IScriptModule[] engines = m_part.ParentGroup.Scene.RequestModuleInterfaces<IScriptModule>();
  1050. if (engines.Length == 0)
  1051. return;
  1052. List<TaskInventoryItem> scripts = GetInventoryItems(InventoryType.LSL);
  1053. foreach (TaskInventoryItem item in scripts)
  1054. {
  1055. foreach (IScriptModule engine in engines)
  1056. {
  1057. if (engine != null)
  1058. {
  1059. if (item.OwnerChanged)
  1060. engine.PostScriptEvent(item.ItemID, "changed", new Object[] { (int)Changed.OWNER });
  1061. item.OwnerChanged = false;
  1062. engine.ResumeScript(item.ItemID);
  1063. }
  1064. }
  1065. }
  1066. }
  1067. }
  1068. }