diff --git a/OpenSim/Framework/AvatarAppearance.cs b/OpenSim/Framework/AvatarAppearance.cs index 494ae5e7a6..157feb552f 100644 --- a/OpenSim/Framework/AvatarAppearance.cs +++ b/OpenSim/Framework/AvatarAppearance.cs @@ -53,6 +53,7 @@ namespace OpenSim.Framework protected AvatarWearable[] m_wearables; protected Dictionary> m_attachments; protected float m_avatarHeight = 0; + protected UUID[] m_texturehashes; public virtual int Serial { @@ -98,6 +99,8 @@ namespace OpenSim.Framework SetDefaultParams(); SetHeight(); m_attachments = new Dictionary>(); + + ResetTextureHashes(); } public AvatarAppearance(OSDMap map) @@ -108,32 +111,6 @@ namespace OpenSim.Framework SetHeight(); } - public AvatarAppearance(AvatarWearable[] wearables, Primitive.TextureEntry textureEntry, byte[] visualParams) - { -// m_log.WarnFormat("[AVATAR APPEARANCE] create initialized appearance"); - - m_serial = 0; - - if (wearables != null) - m_wearables = wearables; - else - SetDefaultWearables(); - - if (textureEntry != null) - m_texture = textureEntry; - else - SetDefaultTexture(); - - if (visualParams != null) - m_visualparams = visualParams; - else - SetDefaultParams(); - - SetHeight(); - - m_attachments = new Dictionary>(); - } - public AvatarAppearance(AvatarAppearance appearance) : this(appearance, true) { } @@ -151,6 +128,8 @@ namespace OpenSim.Framework SetHeight(); m_attachments = new Dictionary>(); + ResetTextureHashes(); + return; } @@ -166,6 +145,10 @@ namespace OpenSim.Framework SetWearable(i,appearance.Wearables[i]); } + m_texturehashes = new UUID[AvatarAppearance.TEXTURE_COUNT]; + for (int i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++) + m_texturehashes[i] = new UUID(appearance.m_texturehashes[i]); + m_texture = null; if (appearance.Texture != null) { @@ -200,6 +183,37 @@ namespace OpenSim.Framework } } + public void ResetTextureHashes() + { + m_texturehashes = new UUID[AvatarAppearance.TEXTURE_COUNT]; + for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++) + m_texturehashes[i] = UUID.Zero; + } + + public UUID GetTextureHash(int textureIndex) + { + return m_texturehashes[NormalizeBakedTextureIndex(textureIndex)]; + } + + public void SetTextureHash(int textureIndex, UUID textureHash) + { + m_texturehashes[NormalizeBakedTextureIndex(textureIndex)] = new UUID(textureHash); + } + + /// + /// Normalizes the texture index to the actual bake index, this is done to + /// accommodate older viewers that send the BAKE_INDICES index rather than + /// the actual texture index + /// + private int NormalizeBakedTextureIndex(int textureIndex) + { + // Earlier viewer send the index into the baked index array, just trying to be careful here + if (textureIndex < BAKE_INDICES.Length) + return BAKE_INDICES[textureIndex]; + + return textureIndex; + } + public void ClearWearables() { m_wearables = new AvatarWearable[AvatarWearable.MAX_WEARABLES]; @@ -223,12 +237,7 @@ namespace OpenSim.Framework m_serial = 0; SetDefaultTexture(); - - //for (int i = 0; i < BAKE_INDICES.Length; i++) - // { - // int idx = BAKE_INDICES[i]; - // m_texture.FaceTextures[idx].TextureID = UUID.Zero; - // } + ResetTextureHashes(); } protected virtual void SetDefaultParams() @@ -598,6 +607,12 @@ namespace OpenSim.Framework data["serial"] = OSD.FromInteger(m_serial); data["height"] = OSD.FromReal(m_avatarHeight); + // Hashes + OSDArray hashes = new OSDArray(AvatarAppearance.TEXTURE_COUNT); + for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++) + hashes.Add(OSD.FromUUID(m_texturehashes[i])); + data["hashes"] = hashes; + // Wearables OSDArray wears = new OSDArray(AvatarWearable.MAX_WEARABLES); for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) @@ -642,6 +657,25 @@ namespace OpenSim.Framework try { + // Hashes + m_texturehashes = new UUID[AvatarAppearance.TEXTURE_COUNT]; + if ((data != null) && (data["hashes"] != null) && (data["hashes"]).Type == OSDType.Array) + { + OSDArray hashes = (OSDArray)(data["hashes"]); + for (int i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++) + { + UUID hashID = UUID.Zero; + if (i < hashes.Count && hashes[i] != null) + hashID = hashes[i].AsUUID(); + m_texturehashes[i] = hashID; + } + } + else + { + for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++) + m_texturehashes[i] = UUID.Zero; + } + // Wearables SetDefaultWearables(); if ((data != null) && (data["wearables"] != null) && (data["wearables"]).Type == OSDType.Array) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 59ce2c44de..1fffeffc3b 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -66,7 +66,7 @@ namespace OpenSim.Framework public delegate void CachedTextureRequest(IClientAPI remoteClient, int serial, List cachedTextureRequest); - public delegate void SetAppearance(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams); + public delegate void SetAppearance(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams, List cachedTextureData); public delegate void StartAnim(IClientAPI remoteClient, UUID animID); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index e01447120e..dd8ef9c6cd 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -6214,7 +6214,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (appear.ObjectData.TextureEntry.Length > 1) te = new Primitive.TextureEntry(appear.ObjectData.TextureEntry, 0, appear.ObjectData.TextureEntry.Length); - handlerSetAppearance(sender, te, visualparams); + List hashes = new List(); + for (int i = 0; i < appear.WearableData.Length; i++) + { + CachedTextureRequestArg arg = new CachedTextureRequestArg(); + arg.BakedTextureIndex = appear.WearableData[i].TextureIndex; + arg.WearableHashID = appear.WearableData[i].CacheID; + hashes.Add(arg); + } + + handlerSetAppearance(sender, te, visualparams, hashes); } catch (Exception e) { @@ -11487,12 +11496,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP requestArgs.Add(arg); } - CachedTextureRequest handlerCachedTextureRequest = OnCachedTextureRequest; - if (handlerCachedTextureRequest != null) + try { - handlerCachedTextureRequest(simclient,cachedtex.AgentData.SerialNum,requestArgs); + CachedTextureRequest handlerCachedTextureRequest = OnCachedTextureRequest; + if (handlerCachedTextureRequest != null) + { + handlerCachedTextureRequest(simclient,cachedtex.AgentData.SerialNum,requestArgs); + } } - + catch (Exception e) + { + m_log.ErrorFormat("[CLIENT VIEW]: AgentTextureCached packet handler threw an exception, {0}", e); + return false; + } + return true; } diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs index b640b48869..482bf6fd6f 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs @@ -147,7 +147,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory /// public void SetAppearance(IScenePresence sp, AvatarAppearance appearance) { - SetAppearance(sp, appearance.Texture, appearance.VisualParams); + DoSetAppearance(sp, appearance.Texture, appearance.VisualParams, new List()); } /// @@ -158,9 +158,20 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory /// public void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams) { -// m_log.DebugFormat( -// "[AVFACTORY]: start SetAppearance for {0}, te {1}, visualParams {2}", -// sp.Name, textureEntry, visualParams); + DoSetAppearance(sp, textureEntry, visualParams, new List()); + } + + /// + /// Set appearance data (texture asset IDs and slider settings) + /// + /// + /// + /// + protected void DoSetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, List hashes) + { + // m_log.DebugFormat( + // "[AVFACTORY]: start SetAppearance for {0}, te {1}, visualParams {2}", + // sp.Name, textureEntry, visualParams); // TODO: This is probably not necessary any longer, just assume the // textureEntry set implies that the appearance transaction is complete @@ -190,18 +201,22 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory // Process the baked texture array if (textureEntry != null) { -// m_log.DebugFormat("[AVFACTORY]: Received texture update for {0} {1}", sp.Name, sp.UUID); - -// WriteBakedTexturesReport(sp, m_log.DebugFormat); + // m_log.DebugFormat("[AVFACTORY]: Received texture update for {0} {1}", sp.Name, sp.UUID); + // WriteBakedTexturesReport(sp, m_log.DebugFormat); changed = sp.Appearance.SetTextureEntries(textureEntry) || changed; -// WriteBakedTexturesReport(sp, m_log.DebugFormat); + // WriteBakedTexturesReport(sp, m_log.DebugFormat); // 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); + // Save the wearble hashes in the appearance + sp.Appearance.ResetTextureHashes(); + foreach (CachedTextureRequestArg arg in hashes) + sp.Appearance.SetTextureHash(arg.BakedTextureIndex,arg.WearableHashID); + // 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. @@ -235,13 +250,13 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory public bool SendAppearance(UUID agentId) { -// m_log.DebugFormat("[AVFACTORY]: Sending appearance for {0}", 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); + // m_log.DebugFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentId); return false; } @@ -318,7 +333,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory /// public void QueueAppearanceSend(UUID agentid) { -// m_log.DebugFormat("[AVFACTORY]: Queue appearance send for {0}", agentid); + // m_log.DebugFormat("[AVFACTORY]: Queue appearance send for {0}", agentid); // 10000 ticks per millisecond, 1000 milliseconds per second long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_sendtime * 1000 * 10000); @@ -331,7 +346,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory public void QueueAppearanceSave(UUID agentid) { -// m_log.DebugFormat("[AVFACTORY]: Queueing appearance save for {0}", agentid); + // m_log.DebugFormat("[AVFACTORY]: Queueing appearance save for {0}", agentid); // 10000 ticks per millisecond, 1000 milliseconds per second long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000); @@ -356,9 +371,9 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory if (face == null) continue; -// m_log.DebugFormat( -// "[AVFACTORY]: Looking for texture {0}, id {1} for {2} {3}", -// face.TextureID, idx, client.Name, client.AgentId); + // m_log.DebugFormat( + // "[AVFACTORY]: Looking for texture {0}, id {1} for {2} {3}", + // face.TextureID, idx, client.Name, client.AgentId); // if the texture is one of the "defaults" then skip it // this should probably be more intelligent (skirt texture doesnt matter @@ -373,7 +388,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory return false; } -// m_log.DebugFormat("[AVFACTORY]: Completed texture check for {0} {1}", sp.Name, sp.UUID); + // m_log.DebugFormat("[AVFACTORY]: Completed texture check for {0} {1}", sp.Name, sp.UUID); // If we only found default textures, then the appearance is not cached return (defonly ? false : true); @@ -392,9 +407,9 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory if (face == null) continue; -// m_log.DebugFormat( -// "[AVFACTORY]: Looking for texture {0}, id {1} for {2} {3}", -// face.TextureID, idx, client.Name, client.AgentId); + // m_log.DebugFormat( + // "[AVFACTORY]: Looking for texture {0}, id {1} for {2} {3}", + // face.TextureID, idx, client.Name, client.AgentId); // if the texture is one of the "defaults" then skip it // this should probably be more intelligent (skirt texture doesnt matter @@ -458,9 +473,9 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory 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 @@ -484,7 +499,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory UUID avatarID = kvp.Key; long sendTime = kvp.Value; -// m_log.DebugFormat("[AVFACTORY]: Handling queued appearance updates for {0}, update delta to now is {1}", avatarID, sendTime - now); + // m_log.DebugFormat("[AVFACTORY]: Handling queued appearance updates for {0}, update delta to now is {1}", avatarID, sendTime - now); if (sendTime < now) { @@ -530,11 +545,11 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory if (sp == null) { // This is expected if the user has gone away. -// m_log.DebugFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentid); + // m_log.DebugFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentid); return; } -// m_log.DebugFormat("[AVFACTORY]: Saving appearance for avatar {0}", agentid); + // 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 @@ -622,12 +637,12 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory /// /// /// - private void Client_OnSetAppearance(IClientAPI client, Primitive.TextureEntry textureEntry, byte[] visualParams) + private void Client_OnSetAppearance(IClientAPI client, Primitive.TextureEntry textureEntry, byte[] visualParams, List hashes) { // 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); + DoSetAppearance(sp, textureEntry, visualParams, hashes); else m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance unable to find presence for {0}", client.AgentId); } @@ -684,7 +699,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory /// private void Client_OnCachedTextureRequest(IClientAPI client, int serial, List cachedTextureRequest) { - // m_log.WarnFormat("[AVFACTORY]: Client_OnCachedTextureRequest called for {0} ({1})", client.Name, client.AgentId); + // m_log.DebugFormat("[AVFACTORY]: Client_OnCachedTextureRequest called for {0} ({1})", client.Name, client.AgentId); ScenePresence sp = m_scene.GetScenePresence(client.AgentId); List cachedTextureResponse = new List(); @@ -695,23 +710,20 @@ 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. + if (sp.Appearance.GetTextureHash(index) == request.WearableHashID) + { + Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[index]; + if (face != null) + texture = face.TextureID; + } + else + { + // We know that that hash is wrong, null it out + // and wait for the setappearance call + sp.Appearance.SetTextureHash(index,UUID.Zero); + } - 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); + // m_log.WarnFormat("[AVFACTORY]: use texture {0} for index {1}; hash={2}",texture,index,request.WearableHashID); } CachedTextureResponseArg response = new CachedTextureResponseArg(); diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 384eb1fd1b..118635deb2 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -907,7 +907,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server // Mimicking LLClientView which gets always set appearance from client. AvatarAppearance appearance; m_scene.GetAvatarAppearance(this, out appearance); - OnSetAppearance(this, appearance.Texture, (byte[])appearance.VisualParams.Clone()); + OnSetAppearance(this, appearance.Texture, (byte[])appearance.VisualParams.Clone(), new List()); } public void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args)