Added background thread to handle delayed send and save of appearance
to accommodate batching of the many updates that happen on login and teleport. Fixed handling of the serial property in appearance.viewer-2-initial-appearance
parent
9cfd3e1d5a
commit
0f28fa400d
|
@ -233,7 +233,7 @@ namespace OpenSim.Framework
|
||||||
// DEBUG ON
|
// DEBUG ON
|
||||||
m_log.WarnFormat("[AVATAR APPEARANCE] create empty appearance for {0}",owner);
|
m_log.WarnFormat("[AVATAR APPEARANCE] create empty appearance for {0}",owner);
|
||||||
// DEBUG OFF
|
// DEBUG OFF
|
||||||
m_serial = 0;
|
m_serial = 1;
|
||||||
m_owner = owner;
|
m_owner = owner;
|
||||||
|
|
||||||
SetDefaultWearables();
|
SetDefaultWearables();
|
||||||
|
@ -289,7 +289,7 @@ namespace OpenSim.Framework
|
||||||
// DEBUG OFF
|
// DEBUG OFF
|
||||||
if (appearance == null)
|
if (appearance == null)
|
||||||
{
|
{
|
||||||
m_serial = 0;
|
m_serial = 1;
|
||||||
m_owner = UUID.Zero;
|
m_owner = UUID.Zero;
|
||||||
|
|
||||||
SetDefaultWearables();
|
SetDefaultWearables();
|
||||||
|
@ -467,6 +467,9 @@ namespace OpenSim.Framework
|
||||||
public override String ToString()
|
public override String ToString()
|
||||||
{
|
{
|
||||||
String s = "";
|
String s = "";
|
||||||
|
|
||||||
|
s += String.Format("Serial: {0}\n",m_serial);
|
||||||
|
|
||||||
for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++)
|
for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++)
|
||||||
if (m_texture.FaceTextures[i] != null)
|
if (m_texture.FaceTextures[i] != null)
|
||||||
s += String.Format("Texture: {0} --> {1}\n",i,m_texture.FaceTextures[i].TextureID);
|
s += String.Format("Texture: {0} --> {1}\n",i,m_texture.FaceTextures[i].TextureID);
|
||||||
|
@ -625,8 +628,8 @@ namespace OpenSim.Framework
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Unpack(OSDMap data)
|
public void Unpack(OSDMap data)
|
||||||
{
|
{
|
||||||
if ((data != null) && (data["appearance_serial"] != null))
|
if ((data != null) && (data["serial"] != null))
|
||||||
m_serial = data["appearance_serial"].AsInteger();
|
m_serial = data["serial"].AsInteger();
|
||||||
if ((data != null) && (data["height"] != null))
|
if ((data != null) && (data["height"] != null))
|
||||||
m_avatarHeight = (float)data["height"].AsReal();
|
m_avatarHeight = (float)data["height"].AsReal();
|
||||||
if ((data != null) && (data["hipoffset"] != null))
|
if ((data != null) && (data["hipoffset"] != null))
|
||||||
|
|
|
@ -32,6 +32,10 @@ using Nini.Config;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
|
|
||||||
|
using System.Threading;
|
||||||
|
using System.Timers;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
using OpenSim.Region.Framework.Interfaces;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Services.Interfaces;
|
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 static readonly byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 };
|
||||||
private Scene m_scene = null;
|
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<UUID,long> m_savequeue = new Dictionary<UUID,long>();
|
||||||
|
private Dictionary<UUID,long> m_sendqueue = new Dictionary<UUID,long>();
|
||||||
|
|
||||||
|
#region RegionModule Members
|
||||||
|
|
||||||
public void Initialise(Scene scene, IConfigSource source)
|
public void Initialise(Scene scene, IConfigSource source)
|
||||||
{
|
{
|
||||||
|
@ -56,6 +68,10 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
||||||
|
|
||||||
public void PostInitialise()
|
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()
|
public void Close()
|
||||||
|
@ -84,14 +100,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
||||||
// client.OnAvatarNowWearing -= AvatarIsWearing;
|
// client.OnAvatarNowWearing -= AvatarIsWearing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckBakedTextureAssets(IClientAPI client, UUID textureID, int idx)
|
#endregion
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set appearance data (textureentry and slider settings) received from the client
|
/// Set appearance data (textureentry and slider settings) received from the client
|
||||||
|
@ -100,6 +109,10 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
||||||
/// <param name="visualParam"></param>
|
/// <param name="visualParam"></param>
|
||||||
public void SetAppearance(IClientAPI client, Primitive.TextureEntry textureEntry, byte[] visualParams)
|
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);
|
ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
|
||||||
if (sp == null)
|
if (sp == null)
|
||||||
{
|
{
|
||||||
|
@ -107,79 +120,175 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
||||||
return;
|
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;
|
bool changed = false;
|
||||||
|
|
||||||
// Process the texture entry
|
// Process the texture entry
|
||||||
if (textureEntry != null)
|
if (textureEntry != null)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < BAKE_INDICES.Length; i++)
|
|
||||||
{
|
|
||||||
int j = BAKE_INDICES[i];
|
|
||||||
Primitive.TextureEntryFace face = textureEntry.FaceTextures[j];
|
|
||||||
|
|
||||||
if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE)
|
|
||||||
Util.FireAndForget(delegate(object o) { CheckBakedTextureAssets(client,face.TextureID,j); });
|
|
||||||
}
|
|
||||||
changed = sp.Appearance.SetTextureEntries(textureEntry);
|
changed = sp.Appearance.SetTextureEntries(textureEntry);
|
||||||
|
|
||||||
|
for (int i = 0; i < BAKE_INDICES.Length; i++)
|
||||||
|
{
|
||||||
|
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,idx); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Bake Cache Check
|
// Process the visual params, this may change height as well
|
||||||
|
if (visualParams != null)
|
||||||
changed = sp.Appearance.SetVisualParams(visualParams) || changed;
|
|
||||||
|
|
||||||
// If nothing changed (this happens frequently) just return
|
|
||||||
if (changed)
|
|
||||||
{
|
{
|
||||||
// DEBUG ON
|
if (sp.Appearance.SetVisualParams(visualParams))
|
||||||
m_log.Warn("[AVFACTORY] Appearance changed");
|
{
|
||||||
// DEBUG OFF
|
changed = true;
|
||||||
sp.Appearance.SetAppearance(textureEntry, visualParams);
|
if (sp.Appearance.AvatarHeight > 0)
|
||||||
if (sp.Appearance.AvatarHeight > 0)
|
sp.SetHeight(sp.Appearance.AvatarHeight);
|
||||||
sp.SetHeight(sp.Appearance.AvatarHeight);
|
}
|
||||||
|
|
||||||
m_scene.AvatarService.SetAppearance(client.AgentId, sp.Appearance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks for the existance of a baked texture asset and
|
||||||
|
/// requests the viewer rebake if the asset is not found
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client"></param>
|
||||||
|
/// <param name="textureID"></param>
|
||||||
|
/// <param name="idx"></param>
|
||||||
|
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
|
// DEBUG ON
|
||||||
else
|
m_log.WarnFormat("[AVFACTORY] Queue appearance send for {0}",agentid);
|
||||||
m_log.Warn("[AVFACTORY] Appearance did not change");
|
|
||||||
// DEBUG OFF
|
// 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();
|
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)
|
if (!m_startAnimationSet)
|
||||||
{
|
{
|
||||||
sp.Animator.UpdateMovementAnimations();
|
sp.Animator.UpdateMovementAnimations();
|
||||||
m_startAnimationSet = true;
|
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<UUID,long> sends = new Dictionary<UUID,long>(m_sendqueue);
|
||||||
|
foreach (KeyValuePair<UUID,long> kvp in sends)
|
||||||
|
{
|
||||||
|
if (kvp.Value < now)
|
||||||
|
{
|
||||||
|
Util.FireAndForget(delegate(object o) { HandleAppearanceSend(kvp.Key); });
|
||||||
|
m_sendqueue.Remove(kvp.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (m_savequeue)
|
||||||
|
{
|
||||||
|
Dictionary<UUID,long> saves = new Dictionary<UUID,long>(m_savequeue);
|
||||||
|
foreach (KeyValuePair<UUID,long> 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
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tell the client for this scene presence what items it should be wearing now
|
/// Tell the client for this scene presence what items it should be wearing now
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
Loading…
Reference in New Issue