diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs
index 8d503bd7e6..c7f4c20d67 100644
--- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs
@@ -111,6 +111,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
#region IAvatarFactoryModule
+ ///
+ ///
+ ///
+ ///
+ public void SetAppearance(IScenePresence sp, AvatarAppearance appearance)
+ {
+ SetAppearance(sp, appearance.Texture, appearance.VisualParams);
+ }
+
///
/// Set appearance data (texture asset IDs and slider settings)
///
@@ -156,14 +165,23 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
changed = sp.Appearance.SetTextureEntries(textureEntry) || changed;
// WriteBakedTexturesReport(sp, m_log.DebugFormat);
- if (!ValidateBakedTextureCache(sp))
+
+ // If bake textures are missing and this is not an NPC, request a rebake from client
+ if (!ValidateBakedTextureCache(sp) && (((ScenePresence)sp).PresenceType != PresenceType.Npc))
RequestRebake(sp, true);
// This appears to be set only in the final stage of the appearance
// update transaction. In theory, we should be able to do an immediate
// appearance send and save here.
}
-
+
+ // NPC should send to clients immediately and skip saving appearance
+ if (((ScenePresence)sp).PresenceType == PresenceType.Npc)
+ {
+ SendAppearance((ScenePresence)sp);
+ return;
+ }
+
// save only if there were changes, send no matter what (doesn't hurt to send twice)
if (changed)
QueueAppearanceSave(sp.ControllingClient.AgentId);
@@ -174,6 +192,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
// m_log.WarnFormat("[AVFACTORY]: complete SetAppearance for {0}:\n{1}",client.AgentId,sp.Appearance.ToString());
}
+ private void SendAppearance(ScenePresence sp)
+ {
+ // Send the appearance to everyone in the scene
+ sp.SendAppearanceToAllOtherAgents();
+
+ // Send animations back to the avatar as well
+ sp.Animator.SendAnimPack();
+ }
+
public bool SendAppearance(UUID agentId)
{
// m_log.DebugFormat("[AVFACTORY]: Sending appearance for {0}", agentId);
@@ -185,12 +212,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
return false;
}
- // Send the appearance to everyone in the scene
- sp.SendAppearanceToAllOtherAgents();
-
- // Send animations back to the avatar as well
- sp.Animator.SendAnimPack();
-
+ SendAppearance(sp);
return true;
}
@@ -626,4 +648,4 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
outputAction("{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "corrupt");
}
}
-}
\ No newline at end of file
+}
diff --git a/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs b/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs
index 39a760cbf4..34aca33349 100644
--- a/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs
+++ b/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs
@@ -35,6 +35,7 @@ namespace OpenSim.Region.Framework.Interfaces
public interface IAvatarFactoryModule
{
+ void SetAppearance(IScenePresence sp, AvatarAppearance appearance);
void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams);
///
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs
index 42008dae43..130513d4c0 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs
@@ -30,6 +30,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
+using System.Threading;
using Nwc.XmlRpc;
@@ -167,6 +168,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
private bool m_debugEnabled = false;
+ private Dictionary m_pendingRequests = new Dictionary();
+
private ExpiringCache m_memoryCache;
private int m_cacheTimeout = 30;
@@ -1348,6 +1351,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
// Immediately forward the request if the cache is disabled.
if (m_cacheTimeout == 0)
{
+ m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: cache is disabled");
return WebUtil.PostToService(m_groupsServerURI, requestArgs);
}
@@ -1355,6 +1359,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
if (requestArgs["RequestMethod"] == "RemoveGeneric"
|| requestArgs["RequestMethod"] == "AddGeneric")
{
+ m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: clearing generics cache");
+
// Any and all updates cause the cache to clear
m_memoryCache.Clear();
@@ -1366,18 +1372,67 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
// Create the cache key for the request and see if we have it cached
string CacheKey = WebUtil.BuildQueryString(requestArgs);
- OSDMap response = null;
- if (!m_memoryCache.TryGetValue(CacheKey, out response))
- {
- // if it wasn't in the cache, pass the request to the Simian Grid Services
- response = WebUtil.PostToService(m_groupsServerURI, requestArgs);
- // and cache the response
- m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout));
+ // This code uses a leader/follower pattern. On a cache miss, the request is added
+ // to a queue; the first thread to add it to the queue completes the request while
+ // follow on threads busy wait for the results, this situation seems to happen
+ // often when checking permissions
+ while (true)
+ {
+ OSDMap response = null;
+ bool firstRequest = false;
+
+ lock (m_memoryCache)
+ {
+ if (m_memoryCache.TryGetValue(CacheKey, out response))
+ return response;
+
+ if (! m_pendingRequests.ContainsKey(CacheKey))
+ {
+ m_pendingRequests.Add(CacheKey,true);
+ firstRequest = true;
+ }
+ }
+
+ if (firstRequest)
+ {
+ // if it wasn't in the cache, pass the request to the Simian Grid Services
+ try
+ {
+ response = WebUtil.PostToService(m_groupsServerURI, requestArgs);
+ }
+ catch (Exception e)
+ {
+ m_log.InfoFormat("[SIMIAN GROUPS CONNECTOR] request failed {0}",CacheKey);
+ }
+
+ // and cache the response
+ lock (m_memoryCache)
+ {
+ m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout));
+ m_pendingRequests.Remove(CacheKey);
+ }
+
+ return response;
+ }
+
+ Thread.Sleep(50); // waiting for a web request to complete, 50msecs is reasonable
}
- // return cached response
- return response;
+ // if (!m_memoryCache.TryGetValue(CacheKey, out response))
+ // {
+ // m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: query not in the cache");
+ // Util.PrintCallStack();
+
+ // // if it wasn't in the cache, pass the request to the Simian Grid Services
+ // response = WebUtil.PostToService(m_groupsServerURI, requestArgs);
+
+ // // and cache the response
+ // m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout));
+ // }
+
+ // // return cached response
+ // return response;
}
#endregion
diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
index dc6eefcb16..53593545ef 100644
--- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
+++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
@@ -96,15 +96,15 @@ namespace OpenSim.Region.OptionalModules.World.NPC
if (!m_avatars.ContainsKey(agentId))
return false;
+ // Delete existing sp attachments
scene.AttachmentsModule.DeleteAttachmentsFromScene(sp, false);
- AvatarAppearance npcAppearance = new AvatarAppearance(appearance, true);
- sp.Appearance = npcAppearance;
+ // Set new sp appearance. Also sends to clients.
+ scene.RequestModuleInterface().SetAppearance(sp, new AvatarAppearance(appearance, true));
+
+ // Rez needed sp attachments
scene.AttachmentsModule.RezAttachments(sp);
-
- IAvatarFactoryModule module = scene.RequestModuleInterface();
- module.SendAppearance(sp.UUID);
-
+
return true;
}