浏览代码

extent supported number of avatar textures/bakes/wearables, tell viewers about it on lludp RegionHandShake; propagate agenthover; block teleports/crossings based on worn wearables and peer version;

UbitUmarov 4 年之前
父节点
当前提交
9d6c996570

+ 160 - 70
OpenSim/Framework/AvatarAppearance.cs

@@ -51,27 +51,28 @@ namespace OpenSim.Framework
 
         // this is viewer capabilities and weared things dependent
         // should be only used as initial default value ( V1 viewers )
-        public readonly static int VISUALPARAM_COUNT = 218;
+        public const int VISUALPARAM_COUNT = 218;
 
-//        public readonly static int TEXTURE_COUNT = 21
-        // 21 bad, make it be updated as libovm gets update
-        // also keeping in sync with it
-        public readonly static int TEXTURE_COUNT = Primitive.TextureEntry.MAX_FACES;
+        // regions and viewer compatibility
+        public readonly static int TEXTURE_COUNT = 45;
+        public const int TEXTURE_COUNT_PV7 = 26;
+        public const int BAKES_COUNT_PV7 = 6;
+        public const int MAXWEARABLE_PV7 = 16;
+        public const int MAXWEARABLE_LEGACY = 14;
 
-        public readonly static byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 };
+        public readonly static byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20, 40, 41, 42, 43, 44 };
 
         protected int m_serial = 0;
         protected byte[] m_visualparams;
         protected Primitive.TextureEntry m_texture;
         protected AvatarWearable[] m_wearables;
         protected Dictionary<int, List<AvatarAttachment>> m_attachments;
-        protected float m_avatarHeight = 0;
+        protected WearableCacheItem[] m_cacheitems;
         protected Vector3 m_avatarSize = new Vector3(0.45f, 0.6f, 1.9f); // sl Z cloud value
         protected Vector3 m_avatarBoxSize = new Vector3(0.45f, 0.6f, 1.9f);
+        protected float m_avatarHeight = 0;
         protected float m_avatarFeetOffset = 0;
         protected float m_avatarAnimOffset = 0;
-        protected WearableCacheItem[] m_cacheitems;
-        protected bool m_cacheItemsDirty = true;
 
         public virtual int Serial
         {
@@ -128,11 +129,7 @@ namespace OpenSim.Framework
             set { m_cacheitems = value; }
         }
 
-        public virtual bool WearableCacheItemsDirty
-        {
-            get { return m_cacheItemsDirty; }
-            set { m_cacheItemsDirty = value; }
-        }
+        public virtual float AvatarPreferencesHoverZ { get; set; }
 
         public AvatarAppearance()
         {
@@ -204,12 +201,14 @@ namespace OpenSim.Framework
                 SetDefaultParams();
 //                SetHeight();
                 SetSize(new Vector3(0.45f, 0.6f, 1.9f));
+                AvatarPreferencesHoverZ = 0;
                 m_attachments = new Dictionary<int, List<AvatarAttachment>>();
 
                 return;
             }
 
             m_serial = appearance.Serial;
+            AvatarPreferencesHoverZ = appearance.AvatarPreferencesHoverZ;
 
             if (copyWearables && (appearance.Wearables != null))
             {
@@ -228,7 +227,7 @@ namespace OpenSim.Framework
             m_texture = null;
             if (appearance.Texture != null)
             {
-                byte[] tbytes = appearance.Texture.GetBytes();
+                byte[] tbytes = appearance.Texture.GetBakesBytes();
                 m_texture = new Primitive.TextureEntry(tbytes,0,tbytes.Length);
                 if (copyBaked && appearance.m_cacheitems != null)
                     m_cacheitems = (WearableCacheItem[])appearance.m_cacheitems.Clone();
@@ -295,6 +294,7 @@ namespace OpenSim.Framework
             m_serial = 0;
 
             SetDefaultTexture();
+            AvatarPreferencesHoverZ = 0;
 
             //for (int i = 0; i < BAKE_INDICES.Length; i++)
             // {
@@ -330,9 +330,6 @@ namespace OpenSim.Framework
         protected virtual void SetDefaultTexture()
         {
             m_texture = new Primitive.TextureEntry(new UUID(AppearanceManager.DEFAULT_AVATAR_TEXTURE));
-
-            // for (uint i = 0; i < TEXTURE_COUNT; i++)
-            //     m_texture.CreateFace(i).TextureID = new UUID(AppearanceManager.DEFAULT_AVATAR_TEXTURE);
         }
 
         /// <summary>
@@ -347,31 +344,31 @@ namespace OpenSim.Framework
             if (textureEntry == null)
                 return false;
 
-            // There are much simpler versions of this copy that could be
-            // made. We determine if any of the textures actually
-            // changed to know if the appearance should be saved later
             bool changed = false;
-            for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++)
-            {
-                Primitive.TextureEntryFace newface = textureEntry.FaceTextures[i];
-                Primitive.TextureEntryFace oldface = m_texture.FaceTextures[i];
+            Primitive.TextureEntryFace newface;
+            Primitive.TextureEntryFace tmpFace;
 
-                if (newface == null)
+            //make sure textureEntry.DefaultTexture is the unused one(DEFAULT_AVATAR_TEXTURE).
+            Primitive.TextureEntry converted = new Primitive.TextureEntry(AppearanceManager.DEFAULT_AVATAR_TEXTURE);
+            for (uint i = 0; i < TEXTURE_COUNT; ++i)
+            {
+                newface = textureEntry.GetFace(i);
+                if (newface.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE)
                 {
-                    if (oldface == null)
-                        continue;
+                    tmpFace = converted.GetFace(i);
+                    tmpFace.TextureID = newface.TextureID; // we need a full high level copy, assuming all other parameters are the same.
+                    if (m_texture.FaceTextures[i] == null || newface.TextureID != m_texture.FaceTextures[i].TextureID)
+                        changed = true;
                 }
                 else
-                {
-                    if (oldface != null && oldface.TextureID == newface.TextureID)
+                {   if (m_texture.FaceTextures[i] == null)
                         continue;
+                    if(m_texture.FaceTextures[i].TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE)
+                        changed = true;
                 }
-
-                changed = true;
             }
-
-            m_texture = textureEntry;
-
+            if(changed)
+                m_texture = converted;
             return changed;
         }
 
@@ -736,42 +733,69 @@ namespace OpenSim.Framework
 
             data["serial"] = OSD.FromInteger(m_serial);
             data["height"] = OSD.FromReal(m_avatarHeight);
+            data["aphz"] = OSD.FromReal(AvatarPreferencesHoverZ);
+
+            if (m_texture == null)
+                return data;
+
+            bool sendPV8 = false;
+            if(ctx != null)
+                sendPV8 = ctx.OutboundVersion >= 0.8;
 
             // Wearables
-            //
-            // This will send as many or as few wearables as we have, unless a count
-            // is given. Used for legacy (pre 0.4) versions.
-            int count = ctx.WearablesCount;
-            if (ctx.WearablesCount == -1)
-                count = m_wearables.Length;
-            OSDArray wears = new OSDArray(count);
-            for (int i = 0; i < count; i++)
+            OSDArray wears;
+            int count;
+            if (ctx == null)
+                count = MAXWEARABLE_LEGACY;
+            else
             {
-                AvatarWearable dummyWearable = new AvatarWearable();
+                int wbcount = ctx.WearablesCount;
+                if (wbcount == -1)
+                    wbcount = m_wearables.Length;
 
-                if (i < m_wearables.Length)
-                    wears.Add(m_wearables[i].Pack());
-                else
-                    wears.Add(dummyWearable.Pack());
+                count = wbcount;
+                if(count >  MAXWEARABLE_PV7)
+                {
+                    count = MAXWEARABLE_PV7;
+                    if(sendPV8)
+                    {
+                        wears = new OSDArray(wbcount - MAXWEARABLE_PV7);
+                        for (int i = MAXWEARABLE_PV7; i < wbcount; ++i)
+                            wears.Add(m_wearables[i].Pack());
+
+                        data["wrbls8"] = wears;
+                    }
+                }
             }
+
+            wears = new OSDArray(count);
+            for (int i = 0; i < count; i++)
+                wears.Add(m_wearables[i].Pack());
             data["wearables"] = wears;
 
-            // Avatar Textures
-            OSDArray textures = new OSDArray(AvatarAppearance.TEXTURE_COUNT);
-            for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++)
+            // Avatar Textures and preferences hover
+            OSDArray textures;
+            if (sendPV8)
             {
-                if (m_texture.FaceTextures[i] != null)
-                    textures.Add(OSD.FromUUID(m_texture.FaceTextures[i].TextureID));
-                else
-                    textures.Add(OSD.FromUUID(AppearanceManager.DEFAULT_AVATAR_TEXTURE));
+                byte[] te = m_texture.GetBakesBytes();
+                data["te8"] = OSD.FromBinary(te);
+            }
+            else
+            {
+                textures = new OSDArray(TEXTURE_COUNT_PV7);
+                for (uint i = 0; i < TEXTURE_COUNT_PV7; i++)
+                    textures.Add(OSD.FromUUID(m_texture.GetFace(i).TextureID));
+                data["textures"] = textures;
             }
-            data["textures"] = textures;
 
             if (m_cacheitems != null)
             {
-                OSDArray baked = WearableCacheItem.BakedToOSD(m_cacheitems);
-                if (baked != null)
+                OSDArray baked = WearableCacheItem.BakedToOSD(m_cacheitems, 0, BAKES_COUNT_PV7);
+                if (baked != null && baked.Count > 0)
                     data["bakedcache"] = baked;
+                baked = WearableCacheItem.BakedToOSD(m_cacheitems, BAKES_COUNT_PV7, -1);
+                if (baked != null && baked.Count > 0)
+                    data["bc8"] = baked;
             }
 
             // Visual Parameters
@@ -810,48 +834,88 @@ namespace OpenSim.Framework
             OSD tmpOSD;
             if (data.TryGetValue("serial", out tmpOSD))
                 m_serial = tmpOSD.AsInteger();
+            if(data.TryGetValue("aphz", out tmpOSD))
+                AvatarPreferencesHoverZ = (float)tmpOSD.AsReal();
             if (data.TryGetValue("height", out tmpOSD))
-//                m_avatarHeight = (float)data["height"].AsReal();
-                SetSize(new Vector3(0.45f,0.6f, (float)tmpOSD.AsReal()));
+                SetSize(new Vector3(0.45f, 0.6f, (float)tmpOSD.AsReal()));
 
             try
             {
                 // Wearables
+                OSD tmpOSD8;
+                OSDArray wears8 = null;
+                int wears8Count = 0;
+
+                if (data.TryGetValue("wrbls8", out tmpOSD8) && (tmpOSD8 is OSDArray))
+                {
+                    wears8 = (OSDArray)tmpOSD;
+                    wears8Count = wears8.Count;
+                }
+
                 if (data.TryGetValue("wearables", out tmpOSD) && (tmpOSD is OSDArray))
                 {
                     OSDArray wears = (OSDArray)tmpOSD;
-                    m_wearables = new AvatarWearable[wears.Count];
+                    m_wearables = new AvatarWearable[wears.Count + wears8Count];
 
-                    for (int i = 0; i < wears.Count; i++)
+                    for (int i = 0; i < wears.Count; ++i)
                         m_wearables[i] = new AvatarWearable((OSDArray)wears[i]);
+                    if (wears8Count > 0)
+                    {
+                        for (int i = wears.Count; i < wears8Count + wears.Count; ++i)
+                            m_wearables[i] = new AvatarWearable((OSDArray)wears[i]);
+                    }
                 }
                 else
                 {
                     m_log.Warn("[AVATAR APPEARANCE]: failed to unpack wearables");
                 }
 
-                // Avatar Textures
                 if (data.TryGetValue("textures", out tmpOSD) && (tmpOSD is OSDArray))
                 {
                     OSDArray textures = (OSDArray)tmpOSD;
-                    for (int i = 0; i < AvatarAppearance.TEXTURE_COUNT && i < textures.Count; i++)
+                    for (int i = 0; i < textures.Count && i < TEXTURE_COUNT_PV7; ++i)
                     {
-                        UUID textureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
                         tmpOSD = textures[i];
                         if (tmpOSD != null)
-                            textureID = tmpOSD.AsUUID();
-                        m_texture.CreateFace((uint)i).TextureID = new UUID(textureID);
+                            m_texture.CreateFace((uint)i).TextureID = tmpOSD.AsUUID();
                     }
                 }
-                else
+                if (data.TryGetValue("te8", out tmpOSD))
                 {
-                    m_log.Warn("[AVATAR APPEARANCE]: failed to unpack textures");
+                    byte[] teb = tmpOSD.AsBinary();
+                    Primitive.TextureEntry te = new Primitive.TextureEntry(teb, 0, teb.Length);
+                    m_texture = te;
                 }
 
                 if (data.TryGetValue("bakedcache", out tmpOSD) && (tmpOSD is OSDArray))
                 {
                     OSDArray bakedOSDArray = (OSDArray)tmpOSD;
-                    m_cacheitems = WearableCacheItem.BakedFromOSD(bakedOSDArray);
+                    m_cacheitems = WearableCacheItem.GetDefaultCacheItem();
+
+                    bakedOSDArray = (OSDArray)tmpOSD;
+                    foreach (OSDMap item in bakedOSDArray)
+                    {
+                        int idx = item["textureindex"].AsInteger();
+                        if (idx < 0 || idx >= m_cacheitems.Length)
+                            continue;
+                        m_cacheitems[idx].CacheId = item["cacheid"].AsUUID();
+                        m_cacheitems[idx].TextureID = item["textureid"].AsUUID();
+                        m_cacheitems[idx].TextureAsset = null;
+                    }
+
+                    if (data.TryGetValue("bc8", out tmpOSD) && (tmpOSD is OSDArray))
+                    {
+                        bakedOSDArray = (OSDArray)tmpOSD;
+                        foreach (OSDMap item in bakedOSDArray)
+                        {
+                            int idx = item["textureindex"].AsInteger();
+                            if (idx < 0 || idx >= m_cacheitems.Length)
+                                continue;
+                            m_cacheitems[idx].CacheId = item["cacheid"].AsUUID();
+                            m_cacheitems[idx].TextureID = item["textureid"].AsUUID();
+                            m_cacheitems[idx].TextureAsset = null;
+                        }
+                    }
                 }
 
                 // Visual Parameters
@@ -888,6 +952,32 @@ namespace OpenSim.Framework
 
         #endregion
 
+        public bool CanTeleport(float version)
+        {
+            if (version >= 0.8)
+                return true;
+            if (m_wearables.Length <= MAXWEARABLE_PV7)
+                return true;
+            for(int i = MAXWEARABLE_PV7; i < m_wearables.Length; ++i)
+            {
+                if(m_wearables[i].Count > 0)
+                    return false;
+            }
+
+            // also check baked
+            for(int i = BAKES_COUNT_PV7; i < BAKE_INDICES.Length; i++)
+            {
+                int idx = BAKE_INDICES[i];
+                if (m_texture.FaceTextures[idx] == null)
+                    continue;
+                UUID tid = m_texture.FaceTextures[idx].TextureID;
+                if(tid == AppearanceManager.DEFAULT_AVATAR_TEXTURE || tid == UUID.Zero)
+                    continue;
+                return false;
+            }
+            return true;
+        }
+
         #region VPElement
 
         /// <summary>

+ 7 - 3
OpenSim/Framework/AvatarWearable.cs

@@ -67,10 +67,14 @@ namespace OpenSim.Framework
 
         public static readonly int ALPHA = 13;
         public static readonly int TATTOO = 14;
-
         public static readonly int LEGACY_VERSION_MAX_WEARABLES = 15;
-//        public static readonly int PHYSICS = 15;
-//        public static int MAX_WEARABLES = 16;
+
+        public static readonly int PHYSICS = 15;
+
+        public static int MAX_WEARABLES_PV7 = 16;
+
+        public static readonly int UNIVERSAL = 16;
+        public static int MAX_WEARABLES = 17;
 
 
         public static readonly UUID DEFAULT_BODY_ITEM = new UUID("66c41e39-38f9-f75a-024e-585989bfaba9");

+ 1 - 1
OpenSim/Framework/IClientAPI.cs

@@ -1107,7 +1107,7 @@ namespace OpenSim.Framework
         /// <param name="agentID">The id of the agent associated with the appearance</param>
         /// <param name="visualParams"></param>
         /// <param name="textureEntry"></param>
-        void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry);
+        void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry, float hoverheight);
 
         void SendCachedTextureResponse(ISceneEntity avatar, int serial, List<CachedTextureResponseArg> cachedTextures);
 

+ 0 - 6
OpenSim/Framework/Tests/AgentCircuitDataTest.cs

@@ -220,12 +220,6 @@ namespace OpenSim.Framework.Tests
 
             AvAppearance.VisualParams = VisualParams;
 
-            List<byte> wearbyte = new List<byte>();
-            for (int i = 0; i < VisualParams.Length; i++)
-            {
-                wearbyte.Add(VisualParams[i]);
-            }
-
             AvAppearance.SetAppearance(AvAppearance.Texture, (byte[])VisualParams.Clone());
         }
 

+ 2 - 2
OpenSim/Framework/VersionInfo.cs

@@ -85,8 +85,8 @@ namespace OpenSim
         ///   - this is an older teleport protocol used in OpenSimulator 0.7.5 and before.
         /// </remarks>
         public readonly static float SimulationServiceVersionAcceptedMin = 0.3f;
-        public readonly static float SimulationServiceVersionAcceptedMax = 0.7f;
+        public readonly static float SimulationServiceVersionAcceptedMax = 0.8f;
         public readonly static float SimulationServiceVersionSupportedMin = 0.3f;
-        public readonly static float SimulationServiceVersionSupportedMax = 0.7f;
+        public readonly static float SimulationServiceVersionSupportedMax = 0.8f;
     }
 }

+ 12 - 27
OpenSim/Framework/WearableCacheItem.cs

@@ -128,31 +128,27 @@ namespace OpenSim.Framework
             return arr;
         }
 
-        public static OSDArray BakedToOSD(WearableCacheItem[] pcacheItems)
+        public static OSDArray BakedToOSD(WearableCacheItem[] pcacheItems, int start, int end)
         {
-            if (pcacheItems.Length < AvatarAppearance.BAKE_INDICES[AvatarAppearance.BAKE_INDICES.Length - 1])
-                return null;
-
             OSDArray arr = new OSDArray();
+            if(start < 0)
+                start = 0;
+            if (end < 0 || end > AvatarAppearance.BAKE_INDICES.Length)
+                end = AvatarAppearance.BAKE_INDICES.Length;
+            if (start > end)
+                return null;
 
-            for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
+            for (int i = start; i < end; i++)
             {
                 int idx = AvatarAppearance.BAKE_INDICES[i];
-
+                if(idx >= pcacheItems.Length)
+                    continue;
                 WearableCacheItem item = pcacheItems[idx];
 
                 OSDMap itemmap = new OSDMap();
                 itemmap.Add("textureindex", OSD.FromUInteger(item.TextureIndex));
                 itemmap.Add("cacheid", OSD.FromUUID(item.CacheId));
                 itemmap.Add("textureid", OSD.FromUUID(item.TextureID));
-/*
-                if (item.TextureAsset != null)
-                {
-                    itemmap.Add("assetdata", OSD.FromBinary(item.TextureAsset.Data));
-                    itemmap.Add("assetcreator", OSD.FromString(item.TextureAsset.CreatorID));
-                    itemmap.Add("assetname", OSD.FromString(item.TextureAsset.Name));
-                }
- */
                 arr.Add(itemmap);
             }
             return arr;
@@ -167,22 +163,11 @@ namespace OpenSim.Framework
                 foreach (OSDMap item in itemarray)
                 {
                     int idx = item["textureindex"].AsInteger();
-                    if (idx < 0 || idx > pcache.Length)
+                    if (idx < 0 || idx >= pcache.Length)
                         continue;
                     pcache[idx].CacheId = item["cacheid"].AsUUID();
                     pcache[idx].TextureID = item["textureid"].AsUUID();
-/*
-                    if (item.ContainsKey("assetdata"))
-                    {
-                        AssetBase asset = new AssetBase(item["textureid"].AsUUID(), "BakedTexture", (sbyte)AssetType.Texture, UUID.Zero.ToString());
-                        asset.Temporary = true;
-                        asset.Local = true;
-                        asset.Data = item["assetdata"].AsBinary();
-                        pcache[idx].TextureAsset = asset;
-                    }
-                    else
- */
-                        pcache[idx].TextureAsset = null;
+                    pcache[idx].TextureAsset = null;
                 }
             }
             return pcache;

+ 12 - 7
OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs

@@ -989,11 +989,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
             //RegionInfo4 block
 
             //RegionFlagsExtended
-            zc.AddZeros(1); // we dont have this
-                //zc.AddByte(1); 
-                //zc.AddUInt64(regionFlags); // we have nothing other base flags
-                //RegionProtocols
-                //zc.AddUInt64(0); // bit 0 signals server side texture baking"
+            //zc.AddZeros(1); // if we dont have this else
+            zc.AddByte(1); 
+            zc.AddUInt64(regionFlags); // we have nothing other base flags
+            //RegionProtocols
+                // bit 0 signals server side texture baking
+                // bit 63 signals more than 6 baked textures support"
+            zc.AddUInt64(1UL << 63);
 
             buf.DataLength = zc.Finish();
             m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Unknown);
@@ -4434,7 +4436,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
                 //0xff, 0xff, 0, 1, 158 // ID 158 (low frequency bigendian) zeroencoded
                 };
 
-        public void SendAppearance(UUID targetID, byte[] visualParams, byte[] textureEntry)
+        public void SendAppearance(UUID targetID, byte[] visualParams, byte[] textureEntry, float hover)
         {
             // doing post zero encode, because odds of beeing bad are not that low
             UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint);
@@ -4469,7 +4471,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
             // no AppearanceData
             data[pos++] = 0;
             // no AppearanceHover
-            data[pos++] = 0;
+            data[pos++] = 1;
+            Utils.FloatToBytesSafepos(0, data, pos); pos += 4;
+            Utils.FloatToBytesSafepos(0, data, pos); pos += 4;
+            Utils.FloatToBytesSafepos(hover, data, pos); pos += 4;
 
             buf.DataLength = pos;
             m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority, null, false, true);

+ 200 - 266
OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs

@@ -27,6 +27,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Concurrent;
 using System.Reflection;
 using System.Threading;
 using System.Text;
@@ -35,6 +36,7 @@ using log4net;
 using Nini.Config;
 using OpenMetaverse;
 using OpenSim.Framework;
+using OpenSim.Framework.Monitoring;
 using OpenSim.Region.Framework.Interfaces;
 using OpenSim.Region.Framework.Scenes;
 using OpenSim.Services.Interfaces;
@@ -59,8 +61,10 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
 
         private int m_checkTime = 500; // milliseconds to wait between checks for appearance updates
         private System.Timers.Timer m_updateTimer = new System.Timers.Timer();
-        private Dictionary<UUID,long> m_savequeue = new Dictionary<UUID,long>();
-        private Dictionary<UUID,long> m_sendqueue = new Dictionary<UUID,long>();
+        private ConcurrentDictionary<UUID,long> m_savequeue = new ConcurrentDictionary<UUID,long>();
+        private ConcurrentDictionary<UUID,long> m_sendqueue = new ConcurrentDictionary<UUID,long>();
+        private object m_updatesLock = new object();
+        private int m_updatesbusy = 0;
 
         private object m_setAppearanceLock = new object();
 
@@ -134,7 +138,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
             client.OnRequestWearables += Client_OnRequestWearables;
             client.OnSetAppearance += Client_OnSetAppearance;
             client.OnAvatarNowWearing += Client_OnAvatarNowWearing;
-            client.OnCachedTextureRequest += Client_OnCachedTextureRequest;
+            //client.OnCachedTextureRequest += Client_OnCachedTextureRequest;
         }
 
         #endregion
@@ -232,20 +236,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
             sp.SendAppearanceToAllOtherAgents();
 
             // Send animations back to the avatar as well
-            sp.Animator.SendAnimPack();
+            if(sp.Animator != null)
+                sp.Animator.SendAnimPack();
         }
 
         public bool SendAppearance(UUID agentId)
         {
-//            m_log.DebugFormat("[AVFACTORY]: Sending appearance for {0}", agentId);
-
             ScenePresence sp = m_scene.GetScenePresence(agentId);
-            if (sp == null)
-            {
-                // This is expected if the user has gone away.
-//                m_log.DebugFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentId);
+            if (sp == null || sp.IsDeleted)
                 return false;
-            }
 
             SendAppearance(sp);
             return true;
@@ -338,11 +337,8 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
 
             // 10000 ticks per millisecond, 1000 milliseconds per second
             long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_sendtime * 1000 * 10000);
-            lock (m_sendqueue)
-            {
-                m_sendqueue[agentid] = timestamp;
-                m_updateTimer.Start();
-            }
+            m_sendqueue[agentid] = timestamp;
+            m_updateTimer.Start();
         }
 
         public void QueueAppearanceSave(UUID agentid)
@@ -351,11 +347,8 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
 
             // 10000 ticks per millisecond, 1000 milliseconds per second
             long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000);
-            lock (m_savequeue)
-            {
-                m_savequeue[agentid] = timestamp;
-                m_updateTimer.Start();
-            }
+            m_savequeue[agentid] = timestamp;
+            m_updateTimer.Start();
         }
 
         // called on textures update
@@ -370,106 +363,90 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
 
             // uploaded baked textures will be in assets local cache
             IAssetCache cache = m_scene.RequestModuleInterface<IAssetCache>();
-            IBakedTextureModule m_BakedTextureModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
 
             int validDirtyBakes = 0;
             int hits = 0;
 
             // our main cacheIDs mapper is p.Appearance.WearableCacheItems
-            WearableCacheItem[] wearableCache = sp.Appearance.WearableCacheItems;
+            bool hadSkirt = false;
 
+            WearableCacheItem[] wearableCache = sp.Appearance.WearableCacheItems;
             if (wearableCache == null)
-            {
                 wearableCache = WearableCacheItem.GetDefaultCacheItem();
+            else
+            {
+                hadSkirt = (wearableCache[19].TextureID != UUID.Zero);
             }
 
+            HashSet<uint> updatedFaces = new HashSet<uint>();
             List<UUID> missing = new List<UUID>();
 
-            bool haveSkirt = (wearableCache[19].TextureID != UUID.Zero);
-            bool haveNewSkirt = false;
-
             // Process received baked textures
             for (int i = 0; i < cacheItems.Length; i++)
             {
-                int idx = (int)cacheItems[i].TextureIndex;
+                uint idx = cacheItems[i].TextureIndex;
+                if(idx >= AvatarAppearance.TEXTURE_COUNT)
+                {
+                    hits++;
+                    continue;
+                }
+
+                updatedFaces.Add(idx);
+
+                wearableCache[idx].TextureAsset = null; // just in case
                 Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
 
-                // No face
-                if (face == null)
+                if (face == null || face.TextureID == UUID.Zero || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
                 {
-                    // for some reason viewer is cleaning this
-                    if(idx != 19) // skirt is optional
-                        {
-                        sp.Appearance.Texture.FaceTextures[idx] = sp.Appearance.Texture.CreateFace((uint) idx);
-                        sp.Appearance.Texture.FaceTextures[idx].TextureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
-                        }
                     wearableCache[idx].CacheId = UUID.Zero;
                     wearableCache[idx].TextureID = UUID.Zero;
-                    wearableCache[idx].TextureAsset = null;
-                    continue;
-                }
-                else
-                {
-                    if (face.TextureID == UUID.Zero || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
-                    {
-                        wearableCache[idx].CacheId = UUID.Zero;
-                        wearableCache[idx].TextureID = UUID.Zero;
-                        wearableCache[idx].TextureAsset = null;
-                        continue;
-                    }
-
-                    if(idx == 19)
-                        haveNewSkirt = true;
-/*
-                    if (face.TextureID == wearableCache[idx].TextureID && m_BakedTextureModule != null)
+                    if (idx == 19)
                     {
-                        if (wearableCache[idx].CacheId != cacheItems[i].CacheId)
-                        {
-                            wearableCache[idx].CacheId = cacheItems[i].CacheId;
-                            validDirtyBakes++;
-
-                            //assuming this can only happen if asset is in cache
-                        }
                         hits++;
-                        continue;
-                    }
-*/
-                    wearableCache[idx].TextureAsset = null;
-                    if (cache != null)
-                    {
-                        AssetBase asb = null;
-                        cache.Get(face.TextureID.ToString(), out asb);
-                        wearableCache[idx].TextureAsset = asb;
+                        if(hadSkirt)
+                            validDirtyBakes++;
                     }
+                    continue;
+                }
 
-                    if (wearableCache[idx].TextureAsset != null)
-                    {
-                        if ( wearableCache[idx].TextureID != face.TextureID ||
-                                wearableCache[idx].CacheId != cacheItems[i].CacheId)
-                            validDirtyBakes++;
+                if (cache != null)
+                {
+                    AssetBase asb = null;
+                    cache.Get(face.TextureID.ToString(), out asb);
+                    wearableCache[idx].TextureAsset = asb;
+                }
 
-                        wearableCache[idx].TextureID = face.TextureID;
-                        wearableCache[idx].CacheId = cacheItems[i].CacheId;
-                        hits++;
-                    }
-                    else
-                    {
-                        wearableCache[idx].CacheId = UUID.Zero;
-                        wearableCache[idx].TextureID = UUID.Zero;
-                        wearableCache[idx].TextureAsset = null;
-                        missing.Add(face.TextureID);
-                        continue;
-                    }
+                if (wearableCache[idx].TextureAsset != null)
+                {
+                    if ( wearableCache[idx].TextureID != face.TextureID ||
+                            wearableCache[idx].CacheId != cacheItems[i].CacheId)
+                        validDirtyBakes++;
+
+                    wearableCache[idx].TextureID = face.TextureID;
+                    wearableCache[idx].CacheId = cacheItems[i].CacheId;
+                    hits++;
+                }
+                else
+                {
+                    wearableCache[idx].CacheId = UUID.Zero;
+                    wearableCache[idx].TextureID = UUID.Zero;
+                    missing.Add(face.TextureID);
+                    continue;
                 }
             }
 
-            // handle optional skirt case
-            if(!haveNewSkirt && haveSkirt)
+            // this may be a current fs bug
+            for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
             {
-                wearableCache[19].CacheId = UUID.Zero;
-                wearableCache[19].TextureID = UUID.Zero;
-                wearableCache[19].TextureAsset = null;
-                validDirtyBakes++;
+                uint idx = AvatarAppearance.BAKE_INDICES[i];
+                if(updatedFaces.Contains(idx))
+                    continue;
+
+                sp.Appearance.Texture.FaceTextures[idx] = null;
+
+                 wearableCache[idx].CacheId = UUID.Zero;
+                 wearableCache[idx].TextureID = UUID.Zero;
+                 wearableCache[idx].TextureAsset = null;
             }
 
             sp.Appearance.WearableCacheItems = wearableCache;
@@ -480,15 +457,18 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
                     sp.ControllingClient.SendRebakeAvatarTextures(id);
             }
 
+            bool changed = false;
             if (validDirtyBakes > 0 && hits == cacheItems.Length)
             {
                 // if we got a full set of baked textures save all in BakedTextureModule
+                IBakedTextureModule m_BakedTextureModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
                 if (m_BakedTextureModule != null)
                 {
                     m_log.DebugFormat("[UpdateBakedCache] Uploading to Bakes Server: cache hits: {0} changed entries: {1} rebakes {2}",
                         hits.ToString(), validDirtyBakes.ToString(), missing.Count);
 
                     m_BakedTextureModule.Store(sp.UUID, wearableCache);
+                    changed = true;
                 }
             }
             else
@@ -505,26 +485,25 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
 //                                    sp.Appearance.WearableCacheItems[j].TextureID);
             }
 
-            return (hits == cacheItems.Length);
+            return changed;
         }
 
         // called when we get a new root avatar
         public bool ValidateBakedTextureCache(IScenePresence sp)
         {
-            int hits = 0;
-
             if (((ScenePresence)sp).IsNPC)
                 return true;
 
-            lock (m_setAppearanceLock)
-            {
-                IAssetCache cache = m_scene.RequestModuleInterface<IAssetCache>();
-                IBakedTextureModule bakedModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
-                WearableCacheItem[] bakedModuleCache = null;
+            int hits = 0;
 
-                if (cache == null)
-                    return false;
+            IAssetCache cache = m_scene.RequestModuleInterface<IAssetCache>();
+            if (cache == null)
+                return false;
 
+            IBakedTextureModule bakedModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
+
+            lock (m_setAppearanceLock)
+            {
                 WearableCacheItem[] wearableCache = sp.Appearance.WearableCacheItems;
 
                 // big debug
@@ -566,70 +545,47 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
                 }
                 else
                 {
-                    // we may have received a full cache
-                    // check same coerence and store
                     wearableCacheValid = true;
+                    Primitive.TextureEntryFace face;
                     for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
                     {
                         int idx = AvatarAppearance.BAKE_INDICES[i];
-                        Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
-                        if (face != null)
+                        face = sp.Appearance.Texture.FaceTextures[idx];
+
+                        if(face == null || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
                         {
-                            if (face.TextureID == wearableCache[idx].TextureID &&
-                                face.TextureID != UUID.Zero)
+                            wearableCache[idx].CacheId = UUID.Zero;
+                            wearableCache[idx].TextureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
+                            hits++;
+                            continue;
+                        }
+
+                        if (face.TextureID == wearableCache[idx].TextureID &&
+                            face.TextureID != UUID.Zero)
+                        {
+                            if (cache.Check((wearableCache[idx].TextureID).ToString()))
                             {
-                                if (wearableCache[idx].TextureAsset != null)
-                                {
-                                    hits++;
-                                    wearableCache[idx].TextureAsset.Temporary = true;
-                                    wearableCache[idx].TextureAsset.Local = true;
-                                    cache.Cache(wearableCache[idx].TextureAsset);
-                                    wearableCache[idx].TextureAsset = null;
-                                    continue;
-                                }
-                                
-                                if (cache.Check((wearableCache[idx].TextureID).ToString()))
-                                {
-                                    hits++;
-                                    continue;
-                                }
+                                hits++;
+                                continue;
                             }
-                            wearableCacheValid = false;
                         }
+                        wearableCache[idx].CacheId = UUID.Zero;
+                        wearableCache[idx].TextureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
+                        wearableCacheValid = false;
                     }
-
-                    wearableCacheValid = (wearableCacheValid && (hits >= AvatarAppearance.BAKE_INDICES.Length - 1));
-                    if (wearableCacheValid)
-                    {
-//                        m_log.Debug("[ValidateBakedCache] have valid local cache");
-                    }
-                    else
-                        wearableCache[19].TextureAsset = null; // clear optional skirt
                 }
 
                 bool checkExternal = false;
-
                 if (!wearableCacheValid)
-                {
-                    hits = 0;
-                    // only use external bake module on login condition check
-//                    ScenePresence ssp = null;
-//                    if (sp is ScenePresence)
-                    {
-//                        ssp = (ScenePresence)sp;
-//                        checkExternal = (((uint)ssp.TeleportFlags & (uint)TeleportFlags.ViaLogin) != 0) &&
-//                            bakedModule != null;
-
-                        // or do it anytime we dont have the cache
-                        checkExternal = bakedModule != null;
-                    }
-                }
+                    checkExternal = bakedModule != null;
 
                 if (checkExternal)
                 {
+                    WearableCacheItem[] bakedModuleCache = null;
                     bool gotbacked = false;
+                    hits = 0;
 
-//                    m_log.Debug("[ValidateBakedCache] local cache invalid, checking bakedModule");
+                    // m_log.Debug("[ValidateBakedCache] local cache invalid, checking bakedModule");
                     try
                     {
                         bakedModuleCache = bakedModule.Get(sp.UUID);
@@ -647,8 +603,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
                         for (int i = 0; i < bakedModuleCache.Length; i++)
                         {
                             int j = (int)bakedModuleCache[i].TextureIndex;
-
-                            if (bakedModuleCache[i].TextureAsset != null)
+                            if (j < AvatarAppearance.TEXTURE_COUNT && bakedModuleCache[i].TextureAsset != null)
                             {
                                 wearableCache[j].TextureID = bakedModuleCache[i].TextureID;
                                 wearableCache[j].CacheId = bakedModuleCache[i].CacheId;
@@ -658,33 +613,27 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
                                 cache.Cache(bakedModuleCache[i].TextureAsset);
                             }
                         }
-                        gotbacked = true;
-                    }
 
-                    if (gotbacked)
-                    {
                         // force the ones we got
                         for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
                         {
                             int idx = AvatarAppearance.BAKE_INDICES[i];
-                            if(wearableCache[idx].TextureAsset == null)
+                            if (wearableCache[idx].TextureAsset == null)
                             {
                                 if(idx == 19)
                                 {
                                     sp.Appearance.Texture.FaceTextures[idx] = null;
                                     hits++;
                                 }
+                                else if(sp.Appearance.Texture.FaceTextures[idx] == null ||
+                                    sp.Appearance.Texture.FaceTextures[idx].TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
+                                    hits++;
+                                wearableCache[idx].TextureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
+                                wearableCache[idx].CacheId = UUID.Zero;
                                 continue;
                             }
 
-                            Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
-
-                            if (face == null)
-                            {
-                                face = sp.Appearance.Texture.CreateFace((uint)idx);
-                                sp.Appearance.Texture.FaceTextures[idx] = face;
-                            }
-
+                            Primitive.TextureEntryFace face = sp.Appearance.Texture.GetFace((uint)idx);
                             face.TextureID = wearableCache[idx].TextureID;
                             hits++;
                             wearableCache[idx].TextureAsset = null;
@@ -708,7 +657,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
                                     sp.Appearance.WearableCacheItems[j].TextureID);
             }
 */
-            return (hits >= AvatarAppearance.BAKE_INDICES.Length - 1); // skirt is optional
+            return (hits >= AvatarAppearance.BAKE_INDICES.Length); // skirt is optional
         }
 
         public int RequestRebake(IScenePresence sp, bool missingTexturesOnly)
@@ -776,13 +725,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
             foreach (int i in Enum.GetValues(typeof(BakeType)))
             {
                 BakeType bakeType = (BakeType)i;
+                if (bakeType == BakeType.NumberOfEntries)
+                    break;
 
                 if (bakeType == BakeType.Unknown)
                     continue;
 
-//                m_log.DebugFormat(
-//                    "[AVFACTORY]: NPC avatar {0} has texture id {1} : {2}",
-//                    acd.AgentID, i, acd.Appearance.Texture.FaceTextures[i]);
+                //                m_log.DebugFormat(
+                //                    "[AVFACTORY]: NPC avatar {0} has texture id {1} : {2}",
+                //                    acd.AgentID, i, acd.Appearance.Texture.FaceTextures[i]);
 
                 int ftIndex = (int)AppearanceManager.BakeTypeToAgentTextureIndex(bakeType);
                 Primitive.TextureEntryFace texture = faceTextures[ftIndex];    // this will be null if there's no such baked texture
@@ -794,90 +745,78 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
 
         private void HandleAppearanceUpdateTimer(object sender, EventArgs ea)
         {
-            long now = DateTime.Now.Ticks;
-
-            lock (m_sendqueue)
+            if(Monitor.TryEnter(m_updatesLock))
             {
-                Dictionary<UUID, long> sends = new Dictionary<UUID, long>(m_sendqueue);
-                foreach (KeyValuePair<UUID, long> kvp in sends)
+                UUID id;
+                long now = DateTime.Now.Ticks;
+
+                foreach (KeyValuePair<UUID, long> kvp in m_sendqueue)
                 {
-                    // We have to load the key and value into local parameters to avoid a race condition if we loop
-                    // around and load kvp with a different value before FireAndForget has launched its thread.
-                    UUID avatarID = kvp.Key;
                     long sendTime = kvp.Value;
+                    if (sendTime > now)
+                    continue;
 
-//                    m_log.DebugFormat("[AVFACTORY]: Handling queued appearance updates for {0}, update delta to now is {1}", avatarID, sendTime - now);
-
-                    if (sendTime < now)
-                    {
-                        Util.FireAndForget(o => SendAppearance(avatarID), null, "AvatarFactoryModule.SendAppearance");
-                        m_sendqueue.Remove(avatarID);
-                    }
+                    id = kvp.Key;
+                    m_sendqueue.TryRemove(id, out sendTime);
+                    SendAppearance(id);
                 }
-            }
 
-            lock (m_savequeue)
-            {
-                Dictionary<UUID, long> saves = new Dictionary<UUID, long>(m_savequeue);
-                foreach (KeyValuePair<UUID, long> kvp in saves)
+                if(m_updatesbusy == 0)
                 {
-                    // We have to load the key and value into local parameters to avoid a race condition if we loop
-                    // around and load kvp with a different value before FireAndForget has launched its thread.
-                    UUID avatarID = kvp.Key;
-                    long sendTime = kvp.Value;
+                    m_updatesbusy = -1;
+                    List<UUID> saves = new List<UUID>(m_savequeue.Count);
+                    foreach (KeyValuePair<UUID, long> kvp in m_savequeue)
+                    {
+                        long sendTime = kvp.Value;
+                        if (sendTime > now)
+                            continue;
+
+                        id = kvp.Key;
+                        m_savequeue.TryRemove(id, out sendTime);
+                            saves.Add(id);
+                    }
 
-                    if (sendTime < now)
+                    m_updatesbusy = 0;
+                    if (saves.Count > 0)
                     {
-                        Util.FireAndForget(o => SaveAppearance(avatarID), null, "AvatarFactoryModule.SaveAppearance");
-                        m_savequeue.Remove(avatarID);
+                        ++m_updatesbusy;
+                        WorkManager.RunInThreadPool(
+                            delegate
+                            {
+                                SaveAppearance(saves);
+                                saves = null;
+                                --m_updatesbusy;
+                            }, null, string.Format("SaveAppearance ({0})", m_scene.Name));
                     }
                 }
 
-                // We must lock both queues here so that QueueAppearanceSave() or *Send() don't m_updateTimer.Start() on
-                // another thread inbetween the first count calls and m_updateTimer.Stop() on this thread.
-                lock (m_sendqueue)
-                    if (m_savequeue.Count == 0 && m_sendqueue.Count == 0)
-                        m_updateTimer.Stop();
+                if (m_savequeue.Count == 0 && m_sendqueue.Count == 0)
+                    m_updateTimer.Stop();
+
+                 Monitor.Exit(m_updatesLock);
             }
         }
 
-        private void SaveAppearance(UUID agentid)
+        private void SaveAppearance(List<UUID> ids)
         {
-            // We must set appearance parameters in the en_US culture in order to avoid issues where values are saved
-            // in a culture where decimal points are commas and then reloaded in a culture which just treats them as
-            // number seperators.
-            Culture.SetCurrentCulture();
-
-            ScenePresence sp = m_scene.GetScenePresence(agentid);
-            if (sp == null)
-            {
-                // This is expected if the user has gone away.
-//                m_log.DebugFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentid);
-                return;
-            }
-
 //            m_log.DebugFormat("[AVFACTORY]: Saving appearance for avatar {0}", agentid);
 
-            // This could take awhile since it needs to pull inventory
-            // We need to do it at the point of save so that there is a sufficient delay for any upload of new body part/shape
-            // assets and item asset id changes to complete.
-            // I don't think we need to worry about doing this within m_setAppearanceLock since the queueing avoids
-            // multiple save requests.
-            SetAppearanceAssets(sp.UUID, sp.Appearance);
-
-//            List<AvatarAttachment> attachments = sp.Appearance.GetAttachments();
-//            foreach (AvatarAttachment att in attachments)
-//            {
-//                m_log.DebugFormat(
-//                    "[AVFACTORY]: For {0} saving attachment {1} at point {2}",
-//                    sp.Name, att.ItemID, att.AttachPoint);
-//            }
+            foreach(UUID id in ids)
+            {
+                ScenePresence sp = m_scene.GetScenePresence(id);
+                if(sp == null)
+                    continue;
+                // This could take awhile since it needs to pull inventory
+                // We need to do it at the point of save so that there is a sufficient delay for any upload of new body part/shape
+                // assets and item asset id changes to complete.
+                // I don't think we need to worry about doing this within m_setAppearanceLock since the queueing avoids
+                // multiple save requests.
 
-            m_scene.AvatarService.SetAppearance(agentid, sp.Appearance);
+                SetAppearanceAssets(id, sp.Appearance);
 
-            // Trigger this here because it's the final step in the set/queue/save process for appearance setting.
-            // Everything has been updated and stored. Ensures bakes have been persisted (if option is set to persist bakes).
-            m_scene.EventManager.TriggerAvatarAppearanceChanged(sp);
+                m_scene.AvatarService.SetAppearance(id, sp.Appearance);
+                //m_scene.EventManager.TriggerAvatarAppearanceChanged(sp);
+            }
         }
 
         /// <summary>
@@ -1231,7 +1170,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
             // m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance called for {0} ({1})", client.Name, client.AgentId);
             ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
             if (sp != null)
-                SetAppearance(sp, textureEntry, visualParams,avSize, cacheItems);
+                SetAppearance(sp, textureEntry, visualParams, avSize, cacheItems);
             else
                 m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance unable to find presence for {0}", client.AgentId);
         }
@@ -1251,9 +1190,6 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
                 return;
             }
 
-            // we need to clean out the existing textures
-            sp.Appearance.ResetAppearance();
-
             // operate on a copy of the appearance so we don't have to lock anything yet
             AvatarAppearance avatAppearance = new AvatarAppearance(sp.Appearance, false);
 
@@ -1280,15 +1216,13 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
                 // often sends AvatarIsWearing and SetAppearance packets at once, and AvatarIsWearing
                 // shouldn't overwrite the changes made in SetAppearance.
                 sp.Appearance.Wearables = avatAppearance.Wearables;
-                sp.Appearance.Texture = avatAppearance.Texture;
-
                 // We don't need to send the appearance here since the "iswearing" will trigger a new set
                 // of visual param and baked texture changes. When those complete, the new appearance will be sent
-
                 QueueAppearanceSave(client.AgentId);
             }
         }
 
+/*
         /// <summary>
         /// Respond to the cached textures request from the client
         /// </summary>
@@ -1308,23 +1242,9 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
 
                 if (m_reusetextures)
                 {
-                    // this is the most insanely dumb way to do this... however it seems to
-                    // actually work. if the appearance has been reset because wearables have
-                    // changed then the texture entries are zero'd out until the bakes are
-                    // uploaded. on login, if the textures exist in the cache (eg if you logged
-                    // into the simulator recently, then the appearance will pull those and send
-                    // them back in the packet and you won't have to rebake. if the textures aren't
-                    // in the cache then the intial makeroot() call in scenepresence will zero
-                    // them out.
-                    //
-                    // a better solution (though how much better is an open question) is to
-                    // store the hashes in the appearance and compare them. Thats's coming.
-
                     Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[index];
                     if (face != null)
                         texture = face.TextureID;
-
-                    // m_log.WarnFormat("[AVFACTORY]: reuse texture {0} for index {1}",texture,index);
                 }
 
                 CachedTextureResponseArg response = new CachedTextureResponseArg();
@@ -1334,21 +1254,16 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
 
                 cachedTextureResponse.Add(response);
             }
-
-            // m_log.WarnFormat("[AVFACTORY]: serial is {0}",serial);
-            // The serial number appears to be used to match requests and responses
-            // in the texture transaction. We just send back the serial number
-            // that was provided in the request. The viewer bumps this for us.
             client.SendCachedTextureResponse(sp, serial, cachedTextureResponse);
         }
-
+*/
 
         #endregion
 
         public void WriteBakedTexturesReport(IScenePresence sp, ReportOutputAction outputAction)
         {
-            outputAction("For {0} in {1}", sp.Name, m_scene.RegionInfo.RegionName);
-            outputAction(BAKED_TEXTURES_REPORT_FORMAT, "Bake Type", "UUID");
+            outputAction("For {0} in {1}", null, sp.Name, m_scene.RegionInfo.RegionName);
+            outputAction(BAKED_TEXTURES_REPORT_FORMAT, null, "Bake Type", "UUID");
 
             Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = GetBakedTextureFaces(sp.UUID);
 
@@ -1362,19 +1277,38 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
                 }
                 else
                 {
-                    rawTextureID = bakedTextures[bt].TextureID.ToString();
-
-                    if (m_scene.AssetService.Get(rawTextureID) == null)
-                        rawTextureID += " (not found)";
+                    if(bakedTextures[bt].TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
+                        rawTextureID = "not set";
                     else
-                        rawTextureID += " (uploaded)";
+                    {
+                        rawTextureID = bakedTextures[bt].TextureID.ToString();
+
+                        if (m_scene.AssetService.Get(rawTextureID) == null)
+                            rawTextureID += " (not found)";
+                        else
+                            rawTextureID += " (uploaded)";
+                    }
                 }
 
                 outputAction(BAKED_TEXTURES_REPORT_FORMAT, null, bt, rawTextureID);
             }
 
             bool bakedTextureValid = m_scene.AvatarFactory.ValidateBakedTextureCache(sp);
-            outputAction("{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "incomplete");
+            outputAction("{0} baked appearance texture is {1}", null, sp.Name, bakedTextureValid ? "OK" : "incomplete");
+        }
+
+        public void SetPreferencesHoverZ(UUID agentId, float val)
+        {
+            ScenePresence sp = m_scene.GetScenePresence(agentId);
+            if (sp == null || sp.IsDeleted || sp.IsNPC || sp.IsInTransit)
+                return;
+            float last = sp.Appearance.AvatarPreferencesHoverZ;
+            if(val != last)
+            {
+                sp.Appearance.AvatarPreferencesHoverZ = val;
+                //sp.SendAppearanceToAgentNF(sp);
+                QueueAppearanceSend(agentId);
+            }
         }
     }
 }

+ 27 - 4
OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs

@@ -53,6 +53,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
     {
         private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
         private static readonly string LogHeader = "[ENTITY TRANSFER MODULE]";
+        private static readonly string OutfitTPError = "destination region does not support the Outfit you are wearing. Please retry with a simpler one";
 
         public const bool WaitForAgentArrivedAtDestinationDefault = true;
 
@@ -720,6 +721,17 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
                 return;
             }
 
+            if (!sp.Appearance.CanTeleport(ctx.OutboundVersion))
+            {
+                sp.ControllingClient.SendTeleportFailed(OutfitTPError);
+
+                m_log.DebugFormat(
+                    "[ENTITY TRANSFER MODULE]: {0} was stopped from teleporting from {1} to {2} because: {3}",
+                    sp.Name, sp.Scene.Name, finalDestination.RegionName, "incompatible wearable");
+
+                return;
+            }
+
             // Before this point, teleport 'failure' is due to checkable pre-conditions such as whether the target
             // simulator can be found and is explicitly prepared to allow access.  Therefore, we will not count these
             // as server attempts.
@@ -1489,6 +1501,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
                 m_bannedRegionCache.Add(destinyHandle, agentID, 30.0, 30.0);
                 return false;
             }
+            if (!agent.Appearance.CanTeleport(ctx.OutboundVersion))
+            {
+                reason = OutfitTPError;
+                m_bannedRegionCache.Add(destinyHandle, agentID, 30.0, 30.0);
+                return false;
+            }
+
             return true;
         }
 
@@ -1545,7 +1564,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
                     failureReason = "Access Denied";
                 return null;
             }
-
             return neighbourRegion;
         }
 
@@ -1599,9 +1617,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
                     agent.ControllingClient.SendAlertMessage(failureReason);
                 return agent;
             }
+            if (!agent.Appearance.CanTeleport(ctx.OutboundVersion))
+            {
+                if (agent.ControllingClient != null)
+                    agent.ControllingClient.SendAlertMessage(OutfitTPError);
+                return agent;
+            }
 
-//            agent.IsInTransit = true;
-
+            //            agent.IsInTransit = true;
             CrossAgentToNewRegionAsync(agent, newpos, neighbourRegion, isFlying, ctx);
             agent.IsInTransit = false;
             return agent;
@@ -2601,7 +2624,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
                 string reason = String.Empty;
 
                 EntityTransferContext ctx = new EntityTransferContext();
-                bool regionAccepted = scene.SimulationService.CreateAgent(reg, reg, agentCircData, (uint)TeleportFlags.Default, ctx, out reason);
+                bool regionAccepted = scene.SimulationService.CreateAgent(reg, reg, agentCircData, (uint)TeleportFlags.Default, null, out reason);
 
                 if (regionAccepted)
                 {

+ 1 - 1
OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs

@@ -37,7 +37,7 @@ namespace OpenSim.Region.Framework.Interfaces
     {
         void SetAppearance(IScenePresence sp, AvatarAppearance appearance, WearableCacheItem[] cacheItems);
         void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, WearableCacheItem[] cacheItems);
-
+        void SetPreferencesHoverZ(UUID agentId, float val);
         /// <summary>
         /// Send the appearance of an avatar to others in the scene.
         /// </summary>

+ 1 - 10
OpenSim/Region/Framework/Scenes/ScenePresence.cs

@@ -4358,16 +4358,7 @@ namespace OpenSim.Region.Framework.Scenes
 
         public void SendAppearanceToAgentNF(ScenePresence avatar)
         {
-            if(avatar.UUID == UUID)
-            {
-                avatar.ControllingClient.SendAppearance(
-                    UUID, Appearance.VisualParams, Appearance.Texture.GetBytes());
-            }
-            else
-            {
-                avatar.ControllingClient.SendAppearance(
-                    UUID, Appearance.VisualParams, Appearance.Texture.GetBakesBytes());
-            }
+            avatar.ControllingClient.SendAppearance(UUID, Appearance.VisualParams, Appearance.Texture.GetBakesBytes(), Appearance.AvatarPreferencesHoverZ);
         }
 
         public void SendAnimPackToAgent(ScenePresence p)

+ 1 - 1
OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs

@@ -961,7 +961,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
 
         }
 
-        public void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry)
+        public void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry, float hoverheight)
         {
 
         }

+ 4 - 3
OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs

@@ -413,13 +413,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance
                         sb.AppendFormat("Wearables checks for {0}\n\n", sp.Name);
 
                         AvatarWearable[] wearables = sp.Appearance.Wearables;
-                        if(wearables.Count() == 0)
+                        if(wearables.Length == 0)
                         {
                             MainConsole.Instance.Output("avatar has no wearables");
                             return;
                         }
                         
-                        for (int i = 0; i < wearables.Count(); i++)
+                        for (int i = 0; i < wearables.Length; i++)
                         {
                             AvatarWearable aw = wearables[i];
 
@@ -477,8 +477,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance
             cdt.AddColumn("Type", 10);
             cdt.AddColumn("Item UUID", ConsoleDisplayUtil.UuidSize);
             cdt.AddColumn("Asset UUID", ConsoleDisplayUtil.UuidSize);
+            AvatarWearable[] wearables = sp.Appearance.Wearables;
 
-            for (int i = (int)WearableType.Shape; i < (int)WearableType.Physics; i++)
+            for (int i = 0; i < wearables.Length; i++)
             {
                 AvatarWearable aw = sp.Appearance.Wearables[i];
 

+ 1 - 1
OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs

@@ -664,7 +664,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC
         {
         }
 
-        public virtual void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry)
+        public virtual void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry, float hover)
         {
         }
 

+ 1 - 1
OpenSim/Server/Handlers/AgentPreferences/AgentPreferencesServerPostHandler.cs

@@ -129,7 +129,7 @@ namespace OpenSim.Server.Handlers.AgentPreferences
 
             AgentPrefs data = new AgentPrefs(userID);
             data.AccessPrefs = request["AccessPrefs"].ToString();
-            data.HoverHeight = double.Parse(request["HoverHeight"].ToString());
+            data.HoverHeight = float.Parse(request["HoverHeight"].ToString());
             data.Language = request["Language"].ToString();
             data.LanguageIsPublic = bool.Parse(request["LanguageIsPublic"].ToString());
             data.PermEveryone = int.Parse(request["PermEveryone"].ToString());

+ 2 - 0
OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs

@@ -116,6 +116,8 @@ namespace OpenSim.Services.Connectors.Simulation
             try
             {
                 OSDMap args = aCircuit.PackAgentCircuitData(ctx);
+                if(ctx == null)
+                    ctx = new EntityTransferContext();
                 args["context"] = ctx.Pack();
                 PackData(args, source, aCircuit, destination, flags);
 

+ 3 - 3
OpenSim/Services/Interfaces/IAgentPreferencesService.cs

@@ -45,7 +45,7 @@ namespace OpenSim.Services.Interfaces
             if (kvp.ContainsKey("AccessPrefs"))
                 AccessPrefs = kvp["AccessPrefs"];
             if (kvp.ContainsKey("HoverHeight"))
-                HoverHeight = double.Parse(kvp["HoverHeight"]);
+                HoverHeight = float.Parse(kvp["HoverHeight"]);
             if (kvp.ContainsKey("Language"))
                 Language = kvp["Language"];
             if (kvp.ContainsKey("LanguageIsPublic"))
@@ -65,7 +65,7 @@ namespace OpenSim.Services.Interfaces
             if (kvp.ContainsKey("AccessPrefs"))
                 AccessPrefs = kvp["AccessPrefs"].ToString();
             if (kvp.ContainsKey("HoverHeight"))
-                HoverHeight = double.Parse(kvp["HoverHeight"].ToString());
+                HoverHeight = float.Parse(kvp["HoverHeight"].ToString());
             if (kvp.ContainsKey("Language"))
                 Language = kvp["Language"].ToString();
             if (kvp.ContainsKey("LanguageIsPublic"))
@@ -95,7 +95,7 @@ namespace OpenSim.Services.Interfaces
         public UUID PrincipalID = UUID.Zero;
         public string AccessPrefs = "M";
         //public int GodLevel; // *TODO: Implement GodLevel (Unused by the viewer, afaict - 6/11/2015)
-        public double HoverHeight = 0.0;
+        public float HoverHeight = 0.0f;
         public string Language = "en-us";
         public bool LanguageIsPublic = true;
         // DefaultObjectPermMasks

+ 1 - 1
OpenSim/Tests/Common/Mock/TestClient.cs

@@ -570,7 +570,7 @@ namespace OpenSim.Tests.Common
         {
         }
 
-        public virtual void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry)
+        public virtual void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry, float hover)
         {
         }
 

二进制
bin/OpenMetaverse.Rendering.Meshmerizer.dll


二进制
bin/OpenMetaverse.StructuredData.dll


二进制
bin/OpenMetaverse.dll


二进制
bin/OpenMetaverseTypes.dll