diff --git a/OpenSim/Framework/AvatarAppearance.cs b/OpenSim/Framework/AvatarAppearance.cs index 05330c7d78..e66a1e7390 100644 --- a/OpenSim/Framework/AvatarAppearance.cs +++ b/OpenSim/Framework/AvatarAppearance.cs @@ -233,7 +233,7 @@ namespace OpenSim.Framework // DEBUG ON m_log.WarnFormat("[AVATAR APPEARANCE] create empty appearance for {0}",owner); // DEBUG OFF - m_serial = 0; + m_serial = 1; m_owner = owner; SetDefaultWearables(); @@ -289,7 +289,7 @@ namespace OpenSim.Framework // DEBUG OFF if (appearance == null) { - m_serial = 0; + m_serial = 1; m_owner = UUID.Zero; SetDefaultWearables(); @@ -467,6 +467,9 @@ namespace OpenSim.Framework public override String ToString() { String s = ""; + + s += String.Format("Serial: {0}\n",m_serial); + for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++) if (m_texture.FaceTextures[i] != null) s += String.Format("Texture: {0} --> {1}\n",i,m_texture.FaceTextures[i].TextureID); @@ -625,8 +628,8 @@ namespace OpenSim.Framework /// public void Unpack(OSDMap data) { - if ((data != null) && (data["appearance_serial"] != null)) - m_serial = data["appearance_serial"].AsInteger(); + if ((data != null) && (data["serial"] != null)) + m_serial = data["serial"].AsInteger(); if ((data != null) && (data["height"] != null)) m_avatarHeight = (float)data["height"].AsReal(); if ((data != null) && (data["hipoffset"] != null)) diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs index 5444f809b3..903e94b112 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs @@ -32,6 +32,10 @@ using Nini.Config; using OpenMetaverse; using OpenSim.Framework; +using System.Threading; +using System.Timers; +using System.Collections.Generic; + using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; @@ -44,7 +48,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory private static readonly byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 }; private Scene m_scene = null; - private bool m_startAnimationSet = false; + private static readonly int m_savetime = 5; // seconds to wait before saving changed appearance + private static readonly int m_sendtime = 2; // seconds to wait before sending changed appearance + + private static readonly int m_checkTime = 500; // milliseconds to wait between checks for appearance updates + private System.Timers.Timer m_updateTimer = new System.Timers.Timer(); + private Dictionary m_savequeue = new Dictionary(); + private Dictionary m_sendqueue = new Dictionary(); + + #region RegionModule Members public void Initialise(Scene scene, IConfigSource source) { @@ -56,6 +68,10 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory public void PostInitialise() { + m_updateTimer.Enabled = false; + m_updateTimer.AutoReset = true; + m_updateTimer.Interval = m_checkTime; // 500 milliseconds wait to start async ops + m_updateTimer.Elapsed += new ElapsedEventHandler(HandleAppearanceUpdateTimer); } public void Close() @@ -84,15 +100,8 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory // client.OnAvatarNowWearing -= AvatarIsWearing; } - public void CheckBakedTextureAssets(IClientAPI client, UUID textureID, int idx) - { - if (m_scene.AssetService.Get(textureID.ToString()) == null) - { - m_log.WarnFormat("[AVFACTORY]: Missing baked texture {0} ({1}) for avatar {2}",textureID,idx,client.Name); - client.SendRebakeAvatarTextures(textureID); - } - } - + #endregion + /// /// Set appearance data (textureentry and slider settings) received from the client /// @@ -100,6 +109,10 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory /// public void SetAppearance(IClientAPI client, Primitive.TextureEntry textureEntry, byte[] visualParams) { +// DEBUG ON + m_log.WarnFormat("[AVFACTORY] SetAppearance for {0}",client.AgentId); +// DEBUG OFF + ScenePresence sp = m_scene.GetScenePresence(client.AgentId); if (sp == null) { @@ -107,79 +120,175 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory return; } -// DEBUG ON - m_log.WarnFormat("[AVFACTORY] SetAppearance for {0}",client.AgentId); -// DEBUG OFF - -/* - if (m_physicsActor != null) - { - if (!IsChildAgent) - { - // This may seem like it's redundant, remove the avatar from the physics scene - // just to add it back again, but it saves us from having to update - // 3 variables 10 times a second. - bool flyingTemp = m_physicsActor.Flying; - RemoveFromPhysicalScene(); - //m_scene.PhysicsScene.RemoveAvatar(m_physicsActor); - - //PhysicsActor = null; - - AddToPhysicalScene(flyingTemp); - } - } -*/ - #region Bake Cache Check - bool changed = false; // Process the texture entry if (textureEntry != null) { + changed = sp.Appearance.SetTextureEntries(textureEntry); + for (int i = 0; i < BAKE_INDICES.Length; i++) { - int j = BAKE_INDICES[i]; - Primitive.TextureEntryFace face = textureEntry.FaceTextures[j]; - + int idx = BAKE_INDICES[i]; + Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx]; if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) - Util.FireAndForget(delegate(object o) { CheckBakedTextureAssets(client,face.TextureID,j); }); + Util.FireAndForget(delegate(object o) { CheckBakedTextureAssets(client,face.TextureID,idx); }); } - changed = sp.Appearance.SetTextureEntries(textureEntry); - } - #endregion Bake Cache Check - - changed = sp.Appearance.SetVisualParams(visualParams) || changed; - - // If nothing changed (this happens frequently) just return - if (changed) + // Process the visual params, this may change height as well + if (visualParams != null) { -// DEBUG ON - m_log.Warn("[AVFACTORY] Appearance changed"); -// DEBUG OFF - sp.Appearance.SetAppearance(textureEntry, visualParams); - if (sp.Appearance.AvatarHeight > 0) - sp.SetHeight(sp.Appearance.AvatarHeight); - - m_scene.AvatarService.SetAppearance(client.AgentId, sp.Appearance); + if (sp.Appearance.SetVisualParams(visualParams)) + { + changed = true; + if (sp.Appearance.AvatarHeight > 0) + sp.SetHeight(sp.Appearance.AvatarHeight); + } } -// DEBUG ON - else - m_log.Warn("[AVFACTORY] Appearance did not change"); -// DEBUG OFF + + // If something changed in the appearance then queue an appearance save + if (changed) + QueueAppearanceSave(client.AgentId); + // And always queue up an appearance update to send out + QueueAppearanceSend(client.AgentId); + + // Send the appearance back to the avatar + AvatarAppearance avp = sp.Appearance; + sp.ControllingClient.SendAvatarDataImmediate(sp); + sp.ControllingClient.SendAppearance(avp.Owner,avp.VisualParams,avp.Texture.GetBytes()); + } + + /// + /// Checks for the existance of a baked texture asset and + /// requests the viewer rebake if the asset is not found + /// + /// + /// + /// + private void CheckBakedTextureAssets(IClientAPI client, UUID textureID, int idx) + { + if (m_scene.AssetService.Get(textureID.ToString()) == null) + { + m_log.WarnFormat("[AVFACTORY]: Missing baked texture {0} ({1}) for avatar {2}", + textureID,idx,client.Name); + client.SendRebakeAvatarTextures(textureID); + } + } + + #region UpdateAppearanceTimer + + public void QueueAppearanceSend(UUID agentid) + { +// DEBUG ON + m_log.WarnFormat("[AVFACTORY] Queue appearance send for {0}",agentid); +// DEBUG OFF + + // 100 nanoseconds (ticks) we should wait + long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_sendtime * 10000000); + lock (m_sendqueue) + { + m_sendqueue[agentid] = timestamp; + m_updateTimer.Start(); + } + } + + public void QueueAppearanceSave(UUID agentid) + { +// DEBUG ON + m_log.WarnFormat("[AVFACTORY] Queue appearance save for {0}",agentid); +// DEBUG OFF + + // 100 nanoseconds (ticks) we should wait + long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 10000000); + lock (m_savequeue) + { + m_savequeue[agentid] = timestamp; + m_updateTimer.Start(); + } + } + + private void HandleAppearanceSend(UUID agentid) + { + ScenePresence sp = m_scene.GetScenePresence(agentid); + if (sp == null) + { + m_log.WarnFormat("[AVFACTORY] Agent {0} no longer in the scene",agentid); + return; + } + +// DEBUG ON + m_log.WarnFormat("[AVFACTORY] Handle appearance send for {0}\n{1}",agentid,sp.Appearance.ToString()); +// DEBUG OFF + + // Send the appearance to everyone in the scene sp.SendAppearanceToAllOtherAgents(); + + // Send the appearance back to the avatar + AvatarAppearance avp = sp.Appearance; + sp.ControllingClient.SendAvatarDataImmediate(sp); + sp.ControllingClient.SendAppearance(avp.Owner,avp.VisualParams,avp.Texture.GetBytes()); + +/* +// this needs to be fixed, the flag should be on scene presence not the region module + // Start the animations if necessary if (!m_startAnimationSet) { sp.Animator.UpdateMovementAnimations(); m_startAnimationSet = true; } - - client.SendAvatarDataImmediate(sp); - client.SendAppearance(sp.Appearance.Owner,sp.Appearance.VisualParams,sp.Appearance.Texture.GetBytes()); +*/ } + private void HandleAppearanceSave(UUID agentid) + { + ScenePresence sp = m_scene.GetScenePresence(agentid); + if (sp == null) + { + m_log.WarnFormat("[AVFACTORY] Agent {0} no longer in the scene",agentid); + return; + } + + m_scene.AvatarService.SetAppearance(agentid, sp.Appearance); + } + + private void HandleAppearanceUpdateTimer(object sender, EventArgs ea) + { + long now = DateTime.Now.Ticks; + + lock (m_sendqueue) + { + Dictionary sends = new Dictionary(m_sendqueue); + foreach (KeyValuePair kvp in sends) + { + if (kvp.Value < now) + { + Util.FireAndForget(delegate(object o) { HandleAppearanceSend(kvp.Key); }); + m_sendqueue.Remove(kvp.Key); + } + } + } + + lock (m_savequeue) + { + Dictionary saves = new Dictionary(m_savequeue); + foreach (KeyValuePair kvp in saves) + { + if (kvp.Value < now) + { + Util.FireAndForget(delegate(object o) { HandleAppearanceSave(kvp.Key); }); + m_savequeue.Remove(kvp.Key); + } + } + } + + if (m_savequeue.Count == 0 && m_sendqueue.Count == 0) + m_updateTimer.Stop(); + } + + #endregion + /// /// Tell the client for this scene presence what items it should be wearing now ///