29 using System.Collections.Generic;
30 using System.Reflection;
31 using System.Threading;
37 using OpenSim.Framework;
38 using OpenSim.Region.Framework.Interfaces;
39 using OpenSim.Region.Framework.Scenes;
40 using OpenSim.Services.Interfaces;
47 [Extension(Path =
"/OpenSim/RegionModules", NodeName =
"RegionModule", Id =
"AvatarFactoryModule")]
50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
52 public const string BAKED_TEXTURES_REPORT_FORMAT =
"{0,-9} {1}";
54 private Scene m_scene = null;
56 private int m_savetime = 5;
57 private int m_sendtime = 2;
58 private bool m_reusetextures =
false;
60 private int m_checkTime = 500;
61 private System.Timers.Timer m_updateTimer =
new System.Timers.Timer();
62 private Dictionary<UUID,long> m_savequeue =
new Dictionary<UUID,long>();
63 private Dictionary<UUID,long> m_sendqueue =
new Dictionary<UUID,long>();
65 private object m_setAppearanceLock =
new object();
67 #region Region Module interface
72 IConfig appearanceConfig = config.Configs[
"Appearance"];
73 if (appearanceConfig != null)
75 m_savetime = Convert.ToInt32(appearanceConfig.GetString(
"DelayBeforeAppearanceSave",Convert.ToString(m_savetime)));
76 m_sendtime = Convert.ToInt32(appearanceConfig.GetString(
"DelayBeforeAppearanceSend",Convert.ToString(m_sendtime)));
77 m_reusetextures = appearanceConfig.GetBoolean(
"ReuseTextures",m_reusetextures);
90 scene.EventManager.OnNewClient += SubscribeToClientEvents;
98 scene.EventManager.OnNewClient -= SubscribeToClientEvents;
106 m_updateTimer.Enabled =
false;
107 m_updateTimer.AutoReset =
true;
108 m_updateTimer.Interval = m_checkTime;
109 m_updateTimer.Elapsed +=
new ElapsedEventHandler(HandleAppearanceUpdateTimer);
118 get {
return "Default Avatar Factory"; }
121 public bool IsSharedModule
123 get {
return false; }
126 public Type ReplaceableInterface
132 private void SubscribeToClientEvents(
IClientAPI client)
134 client.OnRequestWearables += Client_OnRequestWearables;
135 client.OnSetAppearance += Client_OnSetAppearance;
136 client.OnAvatarNowWearing += Client_OnAvatarNowWearing;
137 client.OnCachedTextureRequest += Client_OnCachedTextureRequest;
142 #region IAvatarFactoryModule
156 float oldoff = sp.Appearance.AvatarFeetOffset;
157 Vector3 oldbox = sp.Appearance.AvatarBoxSize;
160 sp.Appearance.SetSize(avSize);
162 float off = sp.Appearance.AvatarFeetOffset;
163 Vector3 box = sp.Appearance.AvatarBoxSize;
164 if (oldoff != off || oldbox != box)
182 bool changed =
false;
186 lock (m_setAppearanceLock)
189 if (visualParams != null)
191 changed = sp.Appearance.SetVisualParams(visualParams);
195 if (textureEntry != null)
197 m_log.DebugFormat(
"[AVFACTORY]: Received texture update for {0} {1}", sp.Name, sp.UUID);
201 changed = sp.Appearance.SetTextureEntries(textureEntry) || changed;
205 UpdateBakedTextureCache(sp, cacheItems);
232 sp.SendAppearanceToAllOtherAgents();
235 sp.Animator.SendAnimPack();
259 return new Dictionary<BakeType, Primitive.TextureEntryFace>();
261 return GetBakedTextureFaces(sp);
283 "[AV FACTORY]: Permanently saving baked textures for {0} in {1}",
284 sp.Name, m_scene.RegionInfo.RegionName);
286 Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = GetBakedTextureFaces(sp);
288 if (bakedTextures.Count == 0)
291 foreach (BakeType bakeType
in bakedTextures.Keys)
293 Primitive.TextureEntryFace bakedTextureFace = bakedTextures[bakeType];
295 if (bakedTextureFace == null)
304 AssetBase asset = m_scene.AssetService.Get(bakedTextureFace.TextureID.ToString());
309 asset.ID = asset.FullID.ToString();
311 asset.Temporary =
false;
313 m_scene.AssetService.Store(asset);
318 "[AV FACTORY]: Baked texture id {0} not found for bake {1} for avatar {2} in {3} when trying to save permanently",
319 bakedTextureFace.TextureID, bakeType, sp.Name, m_scene.RegionInfo.RegionName);
337 long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_sendtime * 1000 * 10000);
340 m_sendqueue[agentid] = timestamp;
341 m_updateTimer.Start();
350 long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000);
353 m_savequeue[agentid] = timestamp;
354 m_updateTimer.Start();
361 if(cacheItems == null)
372 int validDirtyBakes = 0;
378 if (wearableCache == null)
380 wearableCache = WearableCacheItem.GetDefaultCacheItem();
383 List<UUID> missing =
new List<UUID>();
385 bool haveSkirt = (wearableCache[19].TextureAsset != null);
386 bool haveNewSkirt =
false;
389 for (
int i = 0; i < cacheItems.Length; i++)
391 int idx = (int)cacheItems[i].TextureIndex;
392 Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
400 sp.Appearance.Texture.FaceTextures[idx] = sp.Appearance.Texture.CreateFace((uint) idx);
401 sp.Appearance.Texture.FaceTextures[idx].TextureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
403 wearableCache[idx].CacheId = UUID.Zero;
404 wearableCache[idx].TextureID = UUID.Zero;
405 wearableCache[idx].TextureAsset = null;
410 if (face.TextureID ==
UUID.Zero || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
412 wearableCache[idx].CacheId = UUID.Zero;
413 wearableCache[idx].TextureID = UUID.Zero;
414 wearableCache[idx].TextureAsset = null;
434 wearableCache[idx].TextureAsset = null;
436 wearableCache[idx].TextureAsset = cache.GetCached(face.TextureID.ToString());
438 if (wearableCache[idx].TextureAsset != null)
440 if ( wearableCache[idx].TextureID != face.
TextureID ||
444 wearableCache[idx].TextureID = face.TextureID;
445 wearableCache[idx].CacheId = cacheItems[i].CacheId;
450 wearableCache[idx].CacheId = UUID.Zero;
451 wearableCache[idx].TextureID = UUID.Zero;
452 wearableCache[idx].TextureAsset = null;
453 missing.Add(face.TextureID);
460 if(!haveNewSkirt && haveSkirt)
462 wearableCache[19].CacheId = UUID.Zero;
463 wearableCache[19].TextureID = UUID.Zero;
464 wearableCache[19].TextureAsset = null;
468 sp.Appearance.WearableCacheItems = wearableCache;
470 if (missing.Count > 0)
472 foreach (
UUID id in missing)
473 sp.ControllingClient.SendRebakeAvatarTextures(id);
476 if (validDirtyBakes > 0 && hits == cacheItems.Length)
479 if (m_BakedTextureModule != null)
481 m_log.Debug(
"[UpdateBakedCache] start async uploading to bakedModule cache");
483 m_BakedTextureModule.Store(sp.UUID, wearableCache);
489 m_log.Debug(
"[UpdateBakedCache] cache hits: " + hits.ToString() +
" changed entries: " + validDirtyBakes.ToString() +
" rebakes " + missing.Count);
500 return (hits == cacheItems.Length);
511 lock (m_setAppearanceLock)
523 m_log.DebugFormat(
"[AVFACTORY]: ValidateBakedTextureCache start for {0} {1}", sp.Name, sp.UUID);
553 bool wearableCacheValid =
false;
554 if (wearableCache == null)
556 wearableCache = WearableCacheItem.GetDefaultCacheItem();
562 wearableCacheValid =
true;
563 for (
int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
565 int idx = AvatarAppearance.BAKE_INDICES[i];
566 Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
569 if (face.TextureID == wearableCache[idx].
TextureID &&
570 face.TextureID !=
UUID.Zero)
572 if (wearableCache[idx].TextureAsset != null)
575 wearableCache[idx].TextureAsset.Temporary =
true;
576 wearableCache[idx].TextureAsset.Local =
true;
577 cache.Store(wearableCache[idx].TextureAsset);
586 wearableCacheValid =
false;
590 wearableCacheValid = (wearableCacheValid && (hits >= AvatarAppearance.BAKE_INDICES.Length - 1));
591 if (wearableCacheValid)
592 m_log.Debug(
"[ValidateBakedCache] have valid local cache");
594 wearableCache[19].TextureAsset = null;
597 bool checkExternal =
false;
599 if (!wearableCacheValid)
611 checkExternal = bakedModule != null;
617 bool gotbacked =
false;
619 m_log.Debug(
"[ValidateBakedCache] local cache invalid, checking bakedModule");
622 bakedModuleCache = bakedModule.Get(sp.UUID);
626 m_log.ErrorFormat(e.ToString());
627 bakedModuleCache = null;
630 if (bakedModuleCache != null)
632 m_log.Debug(
"[ValidateBakedCache] got bakedModule " + bakedModuleCache.Length +
" cached textures");
634 for (
int i = 0; i < bakedModuleCache.Length; i++)
636 int j = (int)bakedModuleCache[i].TextureIndex;
638 if (bakedModuleCache[i].TextureAsset != null)
640 wearableCache[j].TextureID = bakedModuleCache[i].TextureID;
641 wearableCache[j].CacheId = bakedModuleCache[i].CacheId;
642 wearableCache[j].TextureAsset = bakedModuleCache[i].TextureAsset;
643 bakedModuleCache[i].TextureAsset.Temporary =
true;
644 bakedModuleCache[i].TextureAsset.Local =
true;
645 cache.Store(bakedModuleCache[i].TextureAsset);
654 for (
int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
656 int idx = AvatarAppearance.BAKE_INDICES[i];
657 if(wearableCache[idx].TextureAsset == null)
661 sp.Appearance.Texture.FaceTextures[idx] = null;
667 Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
671 face = sp.Appearance.Texture.CreateFace((uint)idx);
672 sp.Appearance.Texture.FaceTextures[idx] = face;
675 face.TextureID = wearableCache[idx].TextureID;
681 sp.Appearance.WearableCacheItems = wearableCache;
686 m_log.DebugFormat(
"[ValidateBakedCache]: Completed texture check for {0} {1} with {2} hits", sp.Name, sp.UUID, hits);
705 int texturesRebaked = 0;
708 for (
int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
710 int idx = AvatarAppearance.BAKE_INDICES[i];
711 Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
717 if (face.TextureID ==
UUID.Zero || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
720 if (missingTexturesOnly)
722 if (m_scene.AssetService.Get(face.TextureID.ToString()) != null)
735 "[AVFACTORY]: Missing baked texture {0} ({1}) for {2}, requesting rebake.",
736 face.TextureID, idx, sp.Name);
742 "[AVFACTORY]: Requesting rebake of {0} ({1}) for {2}.",
743 face.TextureID, idx, sp.Name);
747 sp.ControllingClient.SendRebakeAvatarTextures(face.TextureID);
750 return texturesRebaked;
755 #region AvatarFactoryModule private methods
757 private Dictionary<BakeType, Primitive.TextureEntryFace> GetBakedTextureFaces(
ScenePresence sp)
760 return new Dictionary<BakeType, Primitive.TextureEntryFace>();
762 Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures
763 =
new Dictionary<BakeType, Primitive.TextureEntryFace>();
766 Primitive.TextureEntryFace[] faceTextures = appearance.Texture.FaceTextures;
768 foreach (
int i
in Enum.GetValues(typeof(BakeType)))
770 BakeType bakeType = (BakeType)i;
772 if (bakeType == BakeType.Unknown)
779 int ftIndex = (int)AppearanceManager.BakeTypeToAgentTextureIndex(bakeType);
780 Primitive.TextureEntryFace texture = faceTextures[ftIndex];
781 bakedTextures[bakeType] = texture;
784 return bakedTextures;
787 private void HandleAppearanceUpdateTimer(
object sender, EventArgs ea)
789 long now = DateTime.Now.Ticks;
793 Dictionary<UUID, long> sends =
new Dictionary<UUID, long>(m_sendqueue);
794 foreach (KeyValuePair<UUID, long> kvp
in sends)
798 UUID avatarID = kvp.Key;
799 long sendTime = kvp.Value;
805 Util.FireAndForget(o => SendAppearance(avatarID), null,
"AvatarFactoryModule.SendAppearance");
806 m_sendqueue.Remove(avatarID);
813 Dictionary<UUID, long> saves =
new Dictionary<UUID, long>(m_savequeue);
814 foreach (KeyValuePair<UUID, long> kvp
in saves)
818 UUID avatarID = kvp.Key;
819 long sendTime = kvp.Value;
823 Util.FireAndForget(o => SaveAppearance(avatarID), null,
"AvatarFactoryModule.SaveAppearance");
824 m_savequeue.Remove(avatarID);
831 if (m_savequeue.Count == 0 && m_sendqueue.Count == 0)
832 m_updateTimer.Stop();
836 private
void SaveAppearance(UUID agentid)
841 Culture.SetCurrentCulture();
858 SetAppearanceAssets(sp.UUID, sp.Appearance);
868 m_scene.AvatarService.SetAppearance(agentid, sp.Appearance);
872 m_scene.EventManager.TriggerAvatarAppearanceChanged(sp);
887 for (
int i = 0; i < appearance.Wearables.Length; i++)
889 for (
int j = 0; j < appearance.Wearables[i].Count; j++)
894 "[AVFACTORY]: Wearable item {0}:{1} for user {2} unexpectedly UUID.Zero. Ignoring.",
908 baseItem = invService.GetItem(baseItem);
910 if (baseItem != null)
912 appearance.Wearables[i].Add(appearance.Wearables[i][j].ItemID, baseItem.AssetID);
917 "[AVFACTORY]: Can't find inventory item {0} for {1}, setting to default",
918 appearance.Wearables[i][j].ItemID, (WearableType)i);
920 appearance.Wearables[i].RemoveItem(appearance.Wearables[i][j].ItemID);
927 m_log.WarnFormat(
"[AVFACTORY]: user {0} has no inventory, appearance isn't going to work", userID);
1112 UUID defaultwearable = GetDefaultItem(type);
1113 if (defaultwearable !=
UUID.Zero)
1115 UUID newInvItem = UUID.Random();
1118 AssetID = defaultwearable,
1119 AssetType = (int)FolderType.BodyPart,
1120 CreatorId = userID.ToString(),
1122 Description =
"Failed Wearable Replacement",
1123 Folder = invService.GetFolderForType(userID, FolderType.BodyPart).ID,
1124 Flags = (uint) type, Name = Enum.GetName(typeof (WearableType), type),
1131 invService.AddItem(itembase);
1132 UUID LinkInvItem = UUID.Random();
1135 AssetID = newInvItem,
1136 AssetType = (int)AssetType.Link,
1137 CreatorId = userID.ToString(),
1138 InvType = (int) InventoryType.Wearable,
1139 Description =
"Failed Wearable Replacement",
1141 Flags = (uint) type,
1142 Name = Enum.GetName(typeof (WearableType), type),
1144 CurrentPermissions = (uint) PermissionMask.Copy,
1146 GroupPermissions = (uint) PermissionMask.Copy,
1149 invService.AddItem(itembase);
1150 appearance.Wearables[(int)type] =
new AvatarWearable(newInvItem, GetDefaultItem(type));
1152 if (m_scene.TryGetScenePresence(userID, out presence))
1154 m_scene.SendInventoryUpdate(presence.ControllingClient,
1155 invService.GetFolderForType(userID, FolderType.CurrentOutfit),
false,
true);
1160 private UUID GetDefaultItem(WearableType wearable)
1163 UUID ret = UUID.Zero;
1166 case WearableType.Eyes:
1167 ret =
new UUID(
"4bb6fa4d-1cd2-498a-a84c-95c1a0e745a7");
1169 case WearableType.Hair:
1170 ret =
new UUID(
"d342e6c0-b9d2-11dc-95ff-0800200c9a66");
1172 case WearableType.Pants:
1173 ret =
new UUID(
"00000000-38f9-1111-024e-222222111120");
1175 case WearableType.Shape:
1176 ret =
new UUID(
"66c41e39-38f9-f75a-024e-585989bfab73");
1178 case WearableType.Shirt:
1179 ret =
new UUID(
"00000000-38f9-1111-024e-222222111110");
1181 case WearableType.Skin:
1182 ret =
new UUID(
"77c41e39-38f9-f75a-024e-585989bbabbb");
1184 case WearableType.Undershirt:
1185 ret =
new UUID(
"16499ebb-3208-ec27-2def-481881728f47");
1187 case WearableType.Underpants:
1188 ret =
new UUID(
"4ac2e9c7-3671-d229-316a-67717730841d");
1196 #region Client Event Handlers
1197 private void Client_OnRequestWearables(
IClientAPI client)
1203 Util.FireAndForget(delegate(
object x)
1208 ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
1210 client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++);
1212 m_log.WarnFormat(
"[AVFACTORY]: Client_OnRequestWearables unable to find presence for {0}", client.AgentId);
1213 }, null,
"AvatarFactoryModule.OnClientRequestWearables");
1225 ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
1227 SetAppearance(sp, textureEntry, visualParams,avSize, cacheItems);
1229 m_log.WarnFormat(
"[AVFACTORY]: Client_OnSetAppearance unable to find presence for {0}", client.AgentId);
1240 ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
1243 m_log.WarnFormat(
"[AVFACTORY]: Client_OnAvatarNowWearing unable to find presence for {0}", client.AgentId);
1248 sp.Appearance.ResetAppearance();
1256 if (avatAppearance.Wearables.Length <= wear.Type)
1258 int currentLength = avatAppearance.Wearables.Length;
1260 Array.Resize(ref wears, wear.Type + 1);
1261 for (
int i = currentLength ; i <= wear.Type ; i++)
1263 avatAppearance.Wearables = wears;
1265 avatAppearance.Wearables[wear.Type].Add(wear.ItemID, UUID.Zero);
1268 avatAppearance.GetAssetsFrom(sp.Appearance);
1270 lock (m_setAppearanceLock)
1275 sp.Appearance.Wearables = avatAppearance.Wearables;
1276 sp.Appearance.Texture = avatAppearance.Texture;
1281 QueueAppearanceSave(client.
AgentId);
1291 private void Client_OnCachedTextureRequest(
IClientAPI client,
int serial, List<CachedTextureRequestArg> cachedTextureRequest)
1294 ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
1296 List<CachedTextureResponseArg> cachedTextureResponse =
new List<CachedTextureResponseArg>();
1299 UUID texture = UUID.Zero;
1300 int index = request.BakedTextureIndex;
1302 if (m_reusetextures)
1316 Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[index];
1318 texture = face.TextureID;
1324 response.BakedTextureIndex = index;
1325 response.BakedTextureID = texture;
1326 response.HostName = null;
1328 cachedTextureResponse.Add(response);
1335 client.SendCachedTextureResponse(sp, serial, cachedTextureResponse);
1343 outputAction(
"For {0} in {1}", sp.
Name, m_scene.RegionInfo.RegionName);
1344 outputAction(BAKED_TEXTURES_REPORT_FORMAT,
"Bake Type",
"UUID");
1346 Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = GetBakedTextureFaces(sp.
UUID);
1348 foreach (BakeType bt
in bakedTextures.Keys)
1350 string rawTextureID;
1352 if (bakedTextures[bt] == null)
1354 rawTextureID =
"not set";
1358 rawTextureID = bakedTextures[bt].TextureID.ToString();
1360 if (m_scene.AssetService.Get(rawTextureID) == null)
1361 rawTextureID +=
" (not found)";
1363 rawTextureID +=
" (uploaded)";
1366 outputAction(BAKED_TEXTURES_REPORT_FORMAT, bt, rawTextureID);
1369 bool bakedTextureValid = m_scene.AvatarFactory.ValidateBakedTextureCache(sp);
1370 outputAction(
"{0} baked appearance texture is {1}", sp.
Name, bakedTextureValid ?
"OK" :
"incomplete");
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
virtual Primitive.TextureEntry Texture
IClientAPI ControllingClient
void WriteBakedTexturesReport(IScenePresence sp, ReportOutputAction outputAction)
Get a report about the current state of a scene presence's baked appearance textures.
void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
static readonly byte[] BAKE_INDICES
InventoryFolderBase GetRootFolder(UUID userID)
Retrieve the root inventory folder for the given user.
delegate void ReportOutputAction(string format, params object[] args)
void SetAppearance(IScenePresence sp, AvatarAppearance appearance, WearableCacheItem[] cacheItems)
Contains the Avatar's Appearance and methods to manipulate the appearance.
AvatarAppearance Appearance
void QueueAppearanceSend(UUID agentid)
Queue up a request to send appearance.
void QueueAppearanceSave(UUID agentid)
OpenSim.Framework.PermissionMask PermissionMask
AssetBase GetCached(string id)
Synchronously fetches an asset from the local cache only.
bool SaveBakedTextures(UUID agentId)
Save the baked textures for the given agent permanently in the asset database.
bool UpdateBakedTextureCache(IScenePresence sp, WearableCacheItem[] cacheItems)
PresenceType
Indicate the type of ScenePresence.
Asset class. All Assets are reference by this class or a class derived from this class ...
List< Wearable > NowWearing
static AvatarWearable[] DefaultWearables
Dictionary< BakeType, Primitive.TextureEntryFace > GetBakedTextureFaces(UUID agentId)
Return the baked texture ids of the given agent.
Inventory Item - contains all the properties associated with an individual inventory piece...
virtual AvatarWearable[] Wearables
virtual byte[] VisualParams
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
Interactive OpenSim region server
bool ValidateBakedTextureCache(IScenePresence sp)
Validate that OpenSim can find the baked textures need to display a given avatar
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
delegate void SetAppearance(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 AvSize, WearableCacheItem[] CacheItems)
void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 avSize, WearableCacheItem[] cacheItems)
virtual string Name
The name of this entity
void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, WearableCacheItem[] cacheItems)
Set appearance data (texture asset IDs and slider settings)
int RequestRebake(IScenePresence sp, bool missingTexturesOnly)
Request a rebake of textures for an avatar.
bool SendAppearance(UUID agentId)
Send the appearance of an avatar to others in the scene.
InventoryFolderBase GetFolderForType(UUID userID, FolderType type)
Gets the user folder for the given folder-type
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
WearableCacheItem[] GetCachedItems(UUID agentId)