aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2011-08-31 16:29:51 +0100
committerJustin Clark-Casey (justincc)2011-08-31 16:29:51 +0100
commit32444d98cb13423fdf8c874e4fbb7ea17670d7c5 (patch)
tree4c384f277afc6138a050706eaaf839caeed22d1d /OpenSim
parentremove pointless ToArray() call in AttachmentsModule.SaveChangedAttachments() (diff)
downloadopensim-SC-32444d98cb13423fdf8c874e4fbb7ea17670d7c5.zip
opensim-SC-32444d98cb13423fdf8c874e4fbb7ea17670d7c5.tar.gz
opensim-SC-32444d98cb13423fdf8c874e4fbb7ea17670d7c5.tar.bz2
opensim-SC-32444d98cb13423fdf8c874e4fbb7ea17670d7c5.tar.xz
Make SP.Attachments available as sp.GetAttachments() instead.
The approach here, as in other parts of OpenSim, is to return a copy of the list rather than the attachments list itself This prevents callers from forgetting to lock the list when they read it, as was happening in various parts of the codebase. It also improves liveness. This might improve attachment anomolies when performing region crossings.
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs19
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs10
-rw-r--r--OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs61
-rw-r--r--OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs12
-rw-r--r--OpenSim/Region/Framework/Interfaces/IScenePresence.cs7
-rw-r--r--OpenSim/Region/Framework/Scenes/ScenePresence.cs51
-rw-r--r--OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs10
7 files changed, 98 insertions, 72 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
index f4bc495..9e5ce8f 100644
--- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
@@ -136,10 +136,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
136 136
137 public void SaveChangedAttachments(IScenePresence sp) 137 public void SaveChangedAttachments(IScenePresence sp)
138 { 138 {
139 // Need to copy this list because DetachToInventoryPrep mods it 139 foreach (SceneObjectGroup grp in sp.GetAttachments())
140 List<SceneObjectGroup> attachments = new List<SceneObjectGroup>(sp.Attachments);
141
142 foreach (SceneObjectGroup grp in attachments)
143 { 140 {
144 if (grp.HasGroupChanged) // Resizer scripts? 141 if (grp.HasGroupChanged) // Resizer scripts?
145 { 142 {
@@ -273,14 +270,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
273 270
274 // Remove any previous attachments 271 // Remove any previous attachments
275 UUID itemID = UUID.Zero; 272 UUID itemID = UUID.Zero;
276 foreach (SceneObjectGroup grp in sp.Attachments) 273
277 { 274 List<SceneObjectGroup> attachments = sp.GetAttachments(attachmentPt);
278 if (grp.AttachmentPoint == attachmentPt) 275
279 { 276 // At the moment we can only deal with a single attachment
280 itemID = grp.GetFromItemID(); 277 if (attachments.Count != 0)
281 break; 278 itemID = attachments[0].GetFromItemID();
282 }
283 }
284 279
285 if (itemID != UUID.Zero) 280 if (itemID != UUID.Zero)
286 DetachSingleAttachmentToInv(itemID, sp); 281 DetachSingleAttachmentToInv(itemID, sp);
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
index b0146a1..363e258 100644
--- a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
@@ -106,7 +106,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
106 106
107 // Check status on scene presence 107 // Check status on scene presence
108 Assert.That(m_presence.HasAttachments(), Is.True); 108 Assert.That(m_presence.HasAttachments(), Is.True);
109 List<SceneObjectGroup> attachments = m_presence.Attachments; 109 List<SceneObjectGroup> attachments = m_presence.GetAttachments();
110 Assert.That(attachments.Count, Is.EqualTo(1)); 110 Assert.That(attachments.Count, Is.EqualTo(1));
111 SceneObjectGroup attSo = attachments[0]; 111 SceneObjectGroup attSo = attachments[0];
112 Assert.That(attSo.Name, Is.EqualTo(attName)); 112 Assert.That(attSo.Name, Is.EqualTo(attName));
@@ -140,7 +140,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
140 140
141 // Check scene presence status 141 // Check scene presence status
142 Assert.That(m_presence.HasAttachments(), Is.True); 142 Assert.That(m_presence.HasAttachments(), Is.True);
143 List<SceneObjectGroup> attachments = m_presence.Attachments; 143 List<SceneObjectGroup> attachments = m_presence.GetAttachments();
144 Assert.That(attachments.Count, Is.EqualTo(1)); 144 Assert.That(attachments.Count, Is.EqualTo(1));
145 SceneObjectGroup attSo = attachments[0]; 145 SceneObjectGroup attSo = attachments[0];
146 Assert.That(attSo.Name, Is.EqualTo(attName)); 146 Assert.That(attSo.Name, Is.EqualTo(attName));
@@ -174,7 +174,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
174 174
175 // Check scene presence status 175 // Check scene presence status
176 Assert.That(m_presence.HasAttachments(), Is.False); 176 Assert.That(m_presence.HasAttachments(), Is.False);
177 List<SceneObjectGroup> attachments = m_presence.Attachments; 177 List<SceneObjectGroup> attachments = m_presence.GetAttachments();
178 Assert.That(attachments.Count, Is.EqualTo(0)); 178 Assert.That(attachments.Count, Is.EqualTo(0));
179 179
180 // Check appearance status 180 // Check appearance status
@@ -208,7 +208,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
208 208
209 // Check status on scene presence 209 // Check status on scene presence
210 Assert.That(m_presence.HasAttachments(), Is.False); 210 Assert.That(m_presence.HasAttachments(), Is.False);
211 List<SceneObjectGroup> attachments = m_presence.Attachments; 211 List<SceneObjectGroup> attachments = m_presence.GetAttachments();
212 Assert.That(attachments.Count, Is.EqualTo(0)); 212 Assert.That(attachments.Count, Is.EqualTo(0));
213 213
214 // Check item status 214 // Check item status
@@ -237,7 +237,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
237 ScenePresence presence = SceneHelpers.AddScenePresence(scene, acd); 237 ScenePresence presence = SceneHelpers.AddScenePresence(scene, acd);
238 238
239 Assert.That(presence.HasAttachments(), Is.True); 239 Assert.That(presence.HasAttachments(), Is.True);
240 List<SceneObjectGroup> attachments = presence.Attachments; 240 List<SceneObjectGroup> attachments = presence.GetAttachments();
241 241
242 Assert.That(attachments.Count, Is.EqualTo(1)); 242 Assert.That(attachments.Count, Is.EqualTo(1));
243 SceneObjectGroup attSo = attachments[0]; 243 SceneObjectGroup attSo = attachments[0];
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
index 7963e53..c24cc17 100644
--- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
@@ -208,7 +208,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
208 sp.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags); 208 sp.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags);
209 sp.Teleport(position); 209 sp.Teleport(position);
210 210
211 foreach (SceneObjectGroup grp in sp.Attachments) 211 foreach (SceneObjectGroup grp in sp.GetAttachments())
212 sp.Scene.EventManager.TriggerOnScriptChangedEvent(grp.LocalId, (uint)Changed.TELEPORT); 212 sp.Scene.EventManager.TriggerOnScriptChangedEvent(grp.LocalId, (uint)Changed.TELEPORT);
213 } 213 }
214 else // Another region possibly in another simulator 214 else // Another region possibly in another simulator
@@ -559,11 +559,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
559 559
560 protected virtual void AgentHasMovedAway(ScenePresence sp, bool logout) 560 protected virtual void AgentHasMovedAway(ScenePresence sp, bool logout)
561 { 561 {
562 foreach (SceneObjectGroup sop in sp.Attachments) 562 foreach (SceneObjectGroup sop in sp.GetAttachments())
563 { 563 {
564 sop.Scene.DeleteSceneObject(sop, true); 564 sop.Scene.DeleteSceneObject(sop, true);
565 } 565 }
566 sp.Attachments.Clear(); 566 sp.ClearAttachments();
567 } 567 }
568 568
569 protected void KillEntity(Scene scene, uint localID) 569 protected void KillEntity(Scene scene, uint localID)
@@ -1764,34 +1764,33 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
1764 1764
1765 protected bool CrossAttachmentsIntoNewRegion(GridRegion destination, ScenePresence sp, bool silent) 1765 protected bool CrossAttachmentsIntoNewRegion(GridRegion destination, ScenePresence sp, bool silent)
1766 { 1766 {
1767 List<SceneObjectGroup> m_attachments = sp.Attachments; 1767 List<SceneObjectGroup> m_attachments = sp.GetAttachments();
1768 lock (m_attachments) 1768
1769 // Validate
1770 foreach (SceneObjectGroup gobj in m_attachments)
1769 { 1771 {
1770 // Validate 1772 if (gobj == null || gobj.IsDeleted)
1771 foreach (SceneObjectGroup gobj in m_attachments) 1773 return false;
1772 { 1774 }
1773 if (gobj == null || gobj.IsDeleted)
1774 return false;
1775 }
1776 1775
1777 foreach (SceneObjectGroup gobj in m_attachments) 1776 foreach (SceneObjectGroup gobj in m_attachments)
1777 {
1778 // If the prim group is null then something must have happened to it!
1779 if (gobj != null && gobj.RootPart != null)
1778 { 1780 {
1779 // If the prim group is null then something must have happened to it! 1781 // Set the parent localID to 0 so it transfers over properly.
1780 if (gobj != null && gobj.RootPart != null) 1782 gobj.RootPart.SetParentLocalId(0);
1781 { 1783 gobj.AbsolutePosition = gobj.RootPart.AttachedPos;
1782 // Set the parent localID to 0 so it transfers over properly. 1784 gobj.IsAttachment = false;
1783 gobj.RootPart.SetParentLocalId(0); 1785 //gobj.RootPart.LastOwnerID = gobj.GetFromAssetID();
1784 gobj.AbsolutePosition = gobj.RootPart.AttachedPos; 1786 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Sending attachment {0} to region {1}", gobj.UUID, destination.RegionName);
1785 gobj.IsAttachment = false; 1787 CrossPrimGroupIntoNewRegion(destination, gobj, silent);
1786 //gobj.RootPart.LastOwnerID = gobj.GetFromAssetID();
1787 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Sending attachment {0} to region {1}", gobj.UUID, destination.RegionName);
1788 CrossPrimGroupIntoNewRegion(destination, gobj, silent);
1789 }
1790 } 1788 }
1791 m_attachments.Clear();
1792
1793 return true;
1794 } 1789 }
1790
1791 sp.ClearAttachments();
1792
1793 return true;
1795 } 1794 }
1796 1795
1797 #endregion 1796 #endregion
@@ -1840,7 +1839,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
1840 int i = 0; 1839 int i = 0;
1841 if (sp.InTransitScriptStates.Count > 0) 1840 if (sp.InTransitScriptStates.Count > 0)
1842 { 1841 {
1843 sp.Attachments.ForEach(delegate(SceneObjectGroup sog) 1842 List<SceneObjectGroup> attachments = sp.GetAttachments();
1843
1844 foreach (SceneObjectGroup sog in attachments)
1844 { 1845 {
1845 if (i < sp.InTransitScriptStates.Count) 1846 if (i < sp.InTransitScriptStates.Count)
1846 { 1847 {
@@ -1849,8 +1850,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
1849 sog.ResumeScripts(); 1850 sog.ResumeScripts();
1850 } 1851 }
1851 else 1852 else
1852 m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: InTransitScriptStates.Count={0} smaller than Attachments.Count={1}", sp.InTransitScriptStates.Count, sp.Attachments.Count); 1853 m_log.ErrorFormat(
1853 }); 1854 "[ENTITY TRANSFER MODULE]: InTransitScriptStates.Count={0} smaller than Attachments.Count={1}",
1855 sp.InTransitScriptStates.Count, attachments.Count);
1856 }
1854 1857
1855 sp.InTransitScriptStates.Clear(); 1858 sp.InTransitScriptStates.Clear();
1856 } 1859 }
diff --git a/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs
index 22352f5..057500c 100644
--- a/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs
@@ -283,7 +283,7 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
283 } 283 }
284 284
285 /// <summary> 285 /// <summary>
286 /// Delivers the message to. 286 /// Delivers the message to a scene entity.
287 /// </summary> 287 /// </summary>
288 /// <param name='target'> 288 /// <param name='target'>
289 /// Target. 289 /// Target.
@@ -314,17 +314,19 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
314 m_scene.SimChatBroadcast(Utils.StringToBytes(msg), ChatTypeEnum.Owner, 0, pos, name, id, false); 314 m_scene.SimChatBroadcast(Utils.StringToBytes(msg), ChatTypeEnum.Owner, 0, pos, name, id, false);
315 } 315 }
316 316
317 List<SceneObjectGroup> attachments = sp.Attachments; 317 List<SceneObjectGroup> attachments = sp.GetAttachments();
318
318 // Nothing left to do 319 // Nothing left to do
319 if (attachments == null) 320 if (attachments == null)
320 return true; 321 return true;
321 322
322 // Get uuid of attachments 323 // Get uuid of attachments
323 List<UUID> targets = new List<UUID>(); 324 List<UUID> targets = new List<UUID>();
324 foreach ( SceneObjectGroup sog in attachments ) 325 foreach (SceneObjectGroup sog in attachments)
325 { 326 {
326 targets.Add(sog.UUID); 327 targets.Add(sog.UUID);
327 } 328 }
329
328 // Need to check each attachment 330 // Need to check each attachment
329 foreach (ListenerInfo li in m_listenerManager.GetListeners(UUID.Zero, channel, name, id, msg)) 331 foreach (ListenerInfo li in m_listenerManager.GetListeners(UUID.Zero, channel, name, id, msg))
330 { 332 {
@@ -334,9 +336,10 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
334 if (m_scene.GetSceneObjectPart(li.GetHostID()) == null) 336 if (m_scene.GetSceneObjectPart(li.GetHostID()) == null)
335 continue; 337 continue;
336 338
337 if ( targets.Contains(li.GetHostID())) 339 if (targets.Contains(li.GetHostID()))
338 QueueMessage(new ListenerInfo(li, name, id, msg)); 340 QueueMessage(new ListenerInfo(li, name, id, msg));
339 } 341 }
342
340 return true; 343 return true;
341 } 344 }
342 345
@@ -363,6 +366,7 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
363 break; 366 break;
364 } 367 }
365 } 368 }
369
366 return true; 370 return true;
367 } 371 }
368 372
diff --git a/OpenSim/Region/Framework/Interfaces/IScenePresence.cs b/OpenSim/Region/Framework/Interfaces/IScenePresence.cs
index b07c821..788b36f 100644
--- a/OpenSim/Region/Framework/Interfaces/IScenePresence.cs
+++ b/OpenSim/Region/Framework/Interfaces/IScenePresence.cs
@@ -58,10 +58,13 @@ namespace OpenSim.Region.Framework.Interfaces
58 /// <summary> 58 /// <summary>
59 /// The scene objects attached to this avatar. 59 /// The scene objects attached to this avatar.
60 /// </summary> 60 /// </summary>
61 /// <returns>
62 /// A copy of the list.
63 /// </returns>
61 /// <remarks> 64 /// <remarks>
62 /// Do not change this list directly - use methods such as 65 /// Do not change this list directly - use methods such as
63 /// AddAttachment() and RemoveAttachment(). Lock this list when performing any read operations upon it. 66 /// AddAttachment() and RemoveAttachment().
64 /// </remarks> 67 /// </remarks>
65 List<SceneObjectGroup> Attachments { get; } 68 List<SceneObjectGroup> GetAttachments();
66 } 69 }
67} \ No newline at end of file 70} \ No newline at end of file
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
index 0a91989..f5c72e1 100644
--- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs
+++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
@@ -114,10 +114,13 @@ namespace OpenSim.Region.Framework.Scenes
114 } 114 }
115 protected ScenePresenceAnimator m_animator; 115 protected ScenePresenceAnimator m_animator;
116 116
117 public List<SceneObjectGroup> Attachments 117 /// <summary>
118 { 118 /// Attachments recorded on this avatar.
119 get { return m_attachments; } 119 /// </summary>
120 } 120 /// <remarks>
121 /// TODO: For some reason, we effectively have a list both here and in Appearance. Need to work out if this is
122 /// necessary.
123 /// </remarks>
121 protected List<SceneObjectGroup> m_attachments = new List<SceneObjectGroup>(); 124 protected List<SceneObjectGroup> m_attachments = new List<SceneObjectGroup>();
122 125
123 private Dictionary<UUID, ScriptControllers> scriptedcontrols = new Dictionary<UUID, ScriptControllers>(); 126 private Dictionary<UUID, ScriptControllers> scriptedcontrols = new Dictionary<UUID, ScriptControllers>();
@@ -940,15 +943,18 @@ namespace OpenSim.Region.Framework.Scenes
940 // and it has already rezzed the attachments and started their scripts. 943 // and it has already rezzed the attachments and started their scripts.
941 // We do the following only for non-login agents, because their scripts 944 // We do the following only for non-login agents, because their scripts
942 // haven't started yet. 945 // haven't started yet.
943 if (wasChild && Attachments != null && Attachments.Count > 0) 946 lock (m_attachments)
944 { 947 {
945 m_log.DebugFormat("[SCENE PRESENCE]: Restarting scripts in attachments..."); 948 if (wasChild && m_attachments != null && m_attachments.Count > 0)
946 // Resume scripts
947 Attachments.ForEach(delegate(SceneObjectGroup sog)
948 { 949 {
949 sog.RootPart.ParentGroup.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, GetStateSource()); 950 m_log.DebugFormat("[SCENE PRESENCE]: Restarting scripts in attachments...");
950 sog.ResumeScripts(); 951 // Resume scripts
951 }); 952 foreach (SceneObjectGroup sog in m_attachments)
953 {
954 sog.RootPart.ParentGroup.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, GetStateSource());
955 sog.ResumeScripts();
956 }
957 }
952 } 958 }
953 959
954 // send the animations of the other presences to me 960 // send the animations of the other presences to me
@@ -3472,9 +3478,19 @@ namespace OpenSim.Region.Framework.Scenes
3472 m_attachments.Add(gobj); 3478 m_attachments.Add(gobj);
3473 } 3479 }
3474 } 3480 }
3475 3481
3476 /// <summary> 3482 /// <summary>
3477 /// Get the scene object attached to the given point. 3483 /// Get all the presence's attachments.
3484 /// </summary>
3485 /// <returns>A copy of the list which contains the attachments.</returns>
3486 public List<SceneObjectGroup> GetAttachments()
3487 {
3488 lock (m_attachments)
3489 return new List<SceneObjectGroup>(m_attachments);
3490 }
3491
3492 /// <summary>
3493 /// Get the scene objects attached to the given point.
3478 /// </summary> 3494 /// </summary>
3479 /// <param name="attachmentPoint"></param> 3495 /// <param name="attachmentPoint"></param>
3480 /// <returns>Returns an empty list if there were no attachments at the point.</returns> 3496 /// <returns>Returns an empty list if there were no attachments at the point.</returns>
@@ -3521,6 +3537,15 @@ namespace OpenSim.Region.Framework.Scenes
3521 m_attachments.Remove(gobj); 3537 m_attachments.Remove(gobj);
3522 } 3538 }
3523 3539
3540 /// <summary>
3541 /// Clear all attachments
3542 /// </summary>
3543 public void ClearAttachments()
3544 {
3545 lock (m_attachments)
3546 m_attachments.Clear();
3547 }
3548
3524 public bool ValidateAttachments() 3549 public bool ValidateAttachments()
3525 { 3550 {
3526 lock (m_attachments) 3551 lock (m_attachments)
diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
index 79c79e4..e58dca2 100644
--- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
+++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
@@ -144,14 +144,10 @@ namespace OpenSim.Region.OptionalModules.World.NPC
144 return false; 144 return false;
145 145
146 // FIXME: An extremely bad bit of code that reaches directly into the attachments list and manipulates it 146 // FIXME: An extremely bad bit of code that reaches directly into the attachments list and manipulates it
147 List<SceneObjectGroup> attachments = sp.Attachments; 147 foreach (SceneObjectGroup att in sp.GetAttachments())
148 lock (attachments) 148 scene.DeleteSceneObject(att, false);
149 {
150 foreach (SceneObjectGroup att in attachments)
151 scene.DeleteSceneObject(att, false);
152 149
153 attachments.Clear(); 150 sp.ClearAttachments();
154 }
155 151
156 AvatarAppearance npcAppearance = new AvatarAppearance(appearance, true); 152 AvatarAppearance npcAppearance = new AvatarAppearance(appearance, true);
157 sp.Appearance = npcAppearance; 153 sp.Appearance = npcAppearance;