diff options
author | Dan Lake | 2011-10-19 14:41:44 -0700 |
---|---|---|
committer | Dan Lake | 2011-10-19 14:41:44 -0700 |
commit | da794f34a56f7c88904315ae538de8f3790e6891 (patch) | |
tree | ec15f9ad1a941441ab2c0cd24f50b7636bba7bce /OpenSim/Region/CoreModules/Avatar/AvatarFactory | |
parent | Add "scripts stop" and "scripts start" console commands. (diff) | |
download | opensim-SC_OLD-da794f34a56f7c88904315ae538de8f3790e6891.zip opensim-SC_OLD-da794f34a56f7c88904315ae538de8f3790e6891.tar.gz opensim-SC_OLD-da794f34a56f7c88904315ae538de8f3790e6891.tar.bz2 opensim-SC_OLD-da794f34a56f7c88904315ae538de8f3790e6891.tar.xz |
Renamed and rearranged AvatarFactoryModule to eliminate redundant lookups of scene presence by client ID.
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar/AvatarFactory')
-rw-r--r-- | OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs | 616 | ||||
-rw-r--r-- | OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs | 12 |
2 files changed, 300 insertions, 328 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs index d4e1736..0c7c690 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs | |||
@@ -42,7 +42,7 @@ using OpenSim.Services.Interfaces; | |||
42 | 42 | ||
43 | namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | 43 | namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory |
44 | { | 44 | { |
45 | public class AvatarFactoryModule : IAvatarFactory, IRegionModule | 45 | public class AvatarFactoryModule : IAvatarFactoryModule, IRegionModule |
46 | { | 46 | { |
47 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 47 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
48 | private Scene m_scene = null; | 48 | private Scene m_scene = null; |
@@ -57,12 +57,12 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
57 | 57 | ||
58 | private object m_setAppearanceLock = new object(); | 58 | private object m_setAppearanceLock = new object(); |
59 | 59 | ||
60 | #region RegionModule Members | 60 | #region IRegionModule |
61 | 61 | ||
62 | public void Initialise(Scene scene, IConfigSource config) | 62 | public void Initialise(Scene scene, IConfigSource config) |
63 | { | 63 | { |
64 | scene.RegisterModuleInterface<IAvatarFactory>(this); | 64 | scene.RegisterModuleInterface<IAvatarFactoryModule>(this); |
65 | scene.EventManager.OnNewClient += NewClient; | 65 | scene.EventManager.OnNewClient += SubscribeToClientEvents; |
66 | 66 | ||
67 | IConfig sconfig = config.Configs["Startup"]; | 67 | IConfig sconfig = config.Configs["Startup"]; |
68 | if (sconfig != null) | 68 | if (sconfig != null) |
@@ -98,44 +98,201 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
98 | get { return false; } | 98 | get { return false; } |
99 | } | 99 | } |
100 | 100 | ||
101 | public void NewClient(IClientAPI client) | 101 | private void SubscribeToClientEvents(IClientAPI client) |
102 | { | 102 | { |
103 | client.OnRequestWearables += SendWearables; | 103 | client.OnRequestWearables += Client_OnRequestWearables; |
104 | client.OnSetAppearance += SetAppearanceFromClient; | 104 | client.OnSetAppearance += Client_OnSetAppearance; |
105 | client.OnAvatarNowWearing += AvatarIsWearing; | 105 | client.OnAvatarNowWearing += Client_OnAvatarNowWearing; |
106 | } | 106 | } |
107 | 107 | ||
108 | public void RemoveClient(IClientAPI client) | 108 | #endregion |
109 | { | 109 | |
110 | // client.OnAvatarNowWearing -= AvatarIsWearing; | 110 | #region IAvatarFactoryModule |
111 | } | 111 | |
112 | 112 | /// <summary> | |
113 | #endregion | 113 | /// Set appearance data (texture asset IDs and slider settings) |
114 | 114 | /// </summary> | |
115 | /// <param name="sp"></param> | ||
116 | /// <param name="texture"></param> | ||
117 | /// <param name="visualParam"></param> | ||
118 | public void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams) | ||
119 | { | ||
120 | // m_log.InfoFormat("[AVFACTORY]: start SetAppearance for {0}", client.AgentId); | ||
121 | |||
122 | // TODO: This is probably not necessary any longer, just assume the | ||
123 | // textureEntry set implies that the appearance transaction is complete | ||
124 | bool changed = false; | ||
125 | |||
126 | // Process the texture entry transactionally, this doesn't guarantee that Appearance is | ||
127 | // going to be handled correctly but it does serialize the updates to the appearance | ||
128 | lock (m_setAppearanceLock) | ||
129 | { | ||
130 | // Process the visual params, this may change height as well | ||
131 | if (visualParams != null) | ||
132 | { | ||
133 | // string[] visualParamsStrings = new string[visualParams.Length]; | ||
134 | // for (int i = 0; i < visualParams.Length; i++) | ||
135 | // visualParamsStrings[i] = visualParams[i].ToString(); | ||
136 | // m_log.DebugFormat( | ||
137 | // "[AVFACTORY]: Setting visual params for {0} to {1}", | ||
138 | // client.Name, string.Join(", ", visualParamsStrings)); | ||
139 | |||
140 | float oldHeight = sp.Appearance.AvatarHeight; | ||
141 | changed = sp.Appearance.SetVisualParams(visualParams); | ||
142 | |||
143 | if (sp.Appearance.AvatarHeight != oldHeight && sp.Appearance.AvatarHeight > 0) | ||
144 | ((ScenePresence)sp).SetHeight(sp.Appearance.AvatarHeight); | ||
145 | } | ||
146 | |||
147 | // Process the baked texture array | ||
148 | if (textureEntry != null) | ||
149 | { | ||
150 | changed = sp.Appearance.SetTextureEntries(textureEntry) || changed; | ||
151 | |||
152 | m_log.InfoFormat("[AVFACTORY]: received texture update for {0}", sp.UUID); | ||
153 | Util.FireAndForget(delegate(object o) { ValidateBakedTextureCache(sp, false); }); | ||
154 | |||
155 | // This appears to be set only in the final stage of the appearance | ||
156 | // update transaction. In theory, we should be able to do an immediate | ||
157 | // appearance send and save here. | ||
158 | |||
159 | } | ||
160 | // save only if there were changes, send no matter what (doesn't hurt to send twice) | ||
161 | if (changed) | ||
162 | QueueAppearanceSave(sp.ControllingClient.AgentId); | ||
163 | |||
164 | QueueAppearanceSend(sp.ControllingClient.AgentId); | ||
165 | } | ||
166 | |||
167 | // m_log.WarnFormat("[AVFACTORY]: complete SetAppearance for {0}:\n{1}",client.AgentId,sp.Appearance.ToString()); | ||
168 | } | ||
169 | |||
170 | public bool SendAppearance(UUID agentId) | ||
171 | { | ||
172 | ScenePresence sp = m_scene.GetScenePresence(agentId); | ||
173 | if (sp == null) | ||
174 | { | ||
175 | m_log.WarnFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentId); | ||
176 | return false; | ||
177 | } | ||
178 | |||
179 | // Send the appearance to everyone in the scene | ||
180 | sp.SendAppearanceToAllOtherAgents(); | ||
181 | |||
182 | // Send animations back to the avatar as well | ||
183 | sp.Animator.SendAnimPack(); | ||
184 | |||
185 | return true; | ||
186 | } | ||
187 | |||
188 | public Dictionary<BakeType, Primitive.TextureEntryFace> GetBakedTextureFaces(UUID agentId) | ||
189 | { | ||
190 | ScenePresence sp = m_scene.GetScenePresence(agentId); | ||
191 | |||
192 | if (sp == null) | ||
193 | return new Dictionary<BakeType, Primitive.TextureEntryFace>(); | ||
194 | |||
195 | return GetBakedTextureFaces(sp); | ||
196 | } | ||
197 | |||
198 | public bool SaveBakedTextures(UUID agentId) | ||
199 | { | ||
200 | ScenePresence sp = m_scene.GetScenePresence(agentId); | ||
201 | |||
202 | if (sp == null) | ||
203 | return false; | ||
204 | |||
205 | m_log.DebugFormat( | ||
206 | "[AV FACTORY]: Permanently saving baked textures for {0} in {1}", | ||
207 | sp.Name, m_scene.RegionInfo.RegionName); | ||
208 | |||
209 | Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = GetBakedTextureFaces(sp); | ||
210 | |||
211 | if (bakedTextures.Count == 0) | ||
212 | return false; | ||
213 | |||
214 | foreach (BakeType bakeType in bakedTextures.Keys) | ||
215 | { | ||
216 | Primitive.TextureEntryFace bakedTextureFace = bakedTextures[bakeType]; | ||
217 | |||
218 | if (bakedTextureFace == null) | ||
219 | { | ||
220 | m_log.WarnFormat( | ||
221 | "[AV FACTORY]: No texture ID set for {0} for {1} in {2} not found when trying to save permanently", | ||
222 | bakeType, sp.Name, m_scene.RegionInfo.RegionName); | ||
223 | |||
224 | continue; | ||
225 | } | ||
226 | |||
227 | AssetBase asset = m_scene.AssetService.Get(bakedTextureFace.TextureID.ToString()); | ||
228 | |||
229 | if (asset != null) | ||
230 | { | ||
231 | asset.Temporary = false; | ||
232 | asset.Local = false; | ||
233 | m_scene.AssetService.Store(asset); | ||
234 | } | ||
235 | else | ||
236 | { | ||
237 | m_log.WarnFormat( | ||
238 | "[AV FACTORY]: Baked texture id {0} not found for bake {1} for avatar {2} in {3} when trying to save permanently", | ||
239 | bakedTextureFace.TextureID, bakeType, sp.Name, m_scene.RegionInfo.RegionName); | ||
240 | } | ||
241 | } | ||
242 | return true; | ||
243 | } | ||
244 | |||
115 | /// <summary> | 245 | /// <summary> |
116 | /// Check for the existence of the baked texture assets. | 246 | /// Check for the existence of the baked texture assets. |
117 | /// </summary> | 247 | /// </summary> |
118 | /// <param name="client"></param> | 248 | /// <param name="sp"></param> |
119 | public bool ValidateBakedTextureCache(IClientAPI client) | 249 | public bool ValidateBakedTextureCache(IScenePresence sp) |
120 | { | 250 | { |
121 | return ValidateBakedTextureCache(client, true); | 251 | return ValidateBakedTextureCache(sp, true); |
122 | } | 252 | } |
123 | 253 | ||
254 | /// <summary> | ||
255 | /// Queue up a request to send appearance, makes it possible to | ||
256 | /// accumulate changes without sending out each one separately. | ||
257 | /// </summary> | ||
258 | public void QueueAppearanceSend(UUID agentid) | ||
259 | { | ||
260 | // m_log.WarnFormat("[AVFACTORY]: Queue appearance send for {0}", agentid); | ||
261 | |||
262 | // 10000 ticks per millisecond, 1000 milliseconds per second | ||
263 | long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_sendtime * 1000 * 10000); | ||
264 | lock (m_sendqueue) | ||
265 | { | ||
266 | m_sendqueue[agentid] = timestamp; | ||
267 | m_updateTimer.Start(); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | public void QueueAppearanceSave(UUID agentid) | ||
272 | { | ||
273 | // m_log.WarnFormat("[AVFACTORY]: Queue appearance save for {0}", agentid); | ||
274 | |||
275 | // 10000 ticks per millisecond, 1000 milliseconds per second | ||
276 | long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000); | ||
277 | lock (m_savequeue) | ||
278 | { | ||
279 | m_savequeue[agentid] = timestamp; | ||
280 | m_updateTimer.Start(); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | #endregion | ||
285 | |||
286 | #region AvatarFactoryModule private methods | ||
287 | |||
124 | /// <summary> | 288 | /// <summary> |
125 | /// Check for the existence of the baked texture assets. Request a rebake | 289 | /// Check for the existence of the baked texture assets. Request a rebake |
126 | /// unless checkonly is true. | 290 | /// unless checkonly is true. |
127 | /// </summary> | 291 | /// </summary> |
128 | /// <param name="client"></param> | 292 | /// <param name="client"></param> |
129 | /// <param name="checkonly"></param> | 293 | /// <param name="checkonly"></param> |
130 | private bool ValidateBakedTextureCache(IClientAPI client, bool checkonly) | 294 | private bool ValidateBakedTextureCache(IScenePresence sp, bool checkonly) |
131 | { | 295 | { |
132 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); | ||
133 | if (sp == null) | ||
134 | { | ||
135 | m_log.WarnFormat("[AVFACTORY]: SetAppearance unable to find presence for {0}", client.AgentId); | ||
136 | return false; | ||
137 | } | ||
138 | |||
139 | bool defonly = true; // are we only using default textures | 296 | bool defonly = true; // are we only using default textures |
140 | 297 | ||
141 | // Process the texture entry | 298 | // Process the texture entry |
@@ -161,7 +318,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
161 | 318 | ||
162 | defonly = false; // found a non-default texture reference | 319 | defonly = false; // found a non-default texture reference |
163 | 320 | ||
164 | if (!CheckBakedTextureAsset(client, face.TextureID, idx)) | 321 | if (!CheckBakedTextureAsset(sp, face.TextureID, idx)) |
165 | { | 322 | { |
166 | // the asset didn't exist if we are only checking, then we found a bad | 323 | // the asset didn't exist if we are only checking, then we found a bad |
167 | // one and we're done otherwise, ask for a rebake | 324 | // one and we're done otherwise, ask for a rebake |
@@ -170,109 +327,34 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
170 | 327 | ||
171 | m_log.InfoFormat("[AVFACTORY]: missing baked texture {0}, requesting rebake", face.TextureID); | 328 | m_log.InfoFormat("[AVFACTORY]: missing baked texture {0}, requesting rebake", face.TextureID); |
172 | 329 | ||
173 | client.SendRebakeAvatarTextures(face.TextureID); | 330 | sp.ControllingClient.SendRebakeAvatarTextures(face.TextureID); |
174 | } | 331 | } |
175 | } | 332 | } |
176 | 333 | ||
177 | m_log.DebugFormat("[AVFACTORY]: Completed texture check for {0}", client.AgentId); | 334 | m_log.DebugFormat("[AVFACTORY]: Completed texture check for {0}", sp.UUID); |
178 | 335 | ||
179 | // If we only found default textures, then the appearance is not cached | 336 | // If we only found default textures, then the appearance is not cached |
180 | return (defonly ? false : true); | 337 | return (defonly ? false : true); |
181 | } | 338 | } |
182 | 339 | ||
183 | /// <summary> | 340 | /// <summary> |
184 | /// Set appearance data (texture asset IDs and slider settings) received from the client | ||
185 | /// </summary> | ||
186 | /// <param name="client"></param> | ||
187 | /// <param name="texture"></param> | ||
188 | /// <param name="visualParam"></param> | ||
189 | public void SetAppearanceFromClient(IClientAPI client, Primitive.TextureEntry textureEntry, byte[] visualParams) | ||
190 | { | ||
191 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); | ||
192 | if (sp == null) | ||
193 | { | ||
194 | m_log.WarnFormat("[AVFACTORY]: SetAppearance unable to find presence for {0}", client.AgentId); | ||
195 | return; | ||
196 | } | ||
197 | |||
198 | // m_log.InfoFormat("[AVFACTORY]: start SetAppearance for {0}", client.AgentId); | ||
199 | |||
200 | // TODO: This is probably not necessary any longer, just assume the | ||
201 | // textureEntry set implies that the appearance transaction is complete | ||
202 | bool changed = false; | ||
203 | |||
204 | // Process the texture entry transactionally, this doesn't guarantee that Appearance is | ||
205 | // going to be handled correctly but it does serialize the updates to the appearance | ||
206 | lock (m_setAppearanceLock) | ||
207 | { | ||
208 | // Process the visual params, this may change height as well | ||
209 | if (visualParams != null) | ||
210 | { | ||
211 | // string[] visualParamsStrings = new string[visualParams.Length]; | ||
212 | // for (int i = 0; i < visualParams.Length; i++) | ||
213 | // visualParamsStrings[i] = visualParams[i].ToString(); | ||
214 | // m_log.DebugFormat( | ||
215 | // "[AVFACTORY]: Setting visual params for {0} to {1}", | ||
216 | // client.Name, string.Join(", ", visualParamsStrings)); | ||
217 | |||
218 | float oldHeight = sp.Appearance.AvatarHeight; | ||
219 | changed = sp.Appearance.SetVisualParams(visualParams); | ||
220 | |||
221 | if (sp.Appearance.AvatarHeight != oldHeight && sp.Appearance.AvatarHeight > 0) | ||
222 | sp.SetHeight(sp.Appearance.AvatarHeight); | ||
223 | } | ||
224 | |||
225 | // Process the baked texture array | ||
226 | if (textureEntry != null) | ||
227 | { | ||
228 | changed = sp.Appearance.SetTextureEntries(textureEntry) || changed; | ||
229 | |||
230 | m_log.InfoFormat("[AVFACTORY]: received texture update for {0}", client.AgentId); | ||
231 | Util.FireAndForget(delegate(object o) { ValidateBakedTextureCache(client, false); }); | ||
232 | |||
233 | // This appears to be set only in the final stage of the appearance | ||
234 | // update transaction. In theory, we should be able to do an immediate | ||
235 | // appearance send and save here. | ||
236 | |||
237 | } | ||
238 | // save only if there were changes, send no matter what (doesn't hurt to send twice) | ||
239 | if (changed) | ||
240 | QueueAppearanceSave(client.AgentId); | ||
241 | |||
242 | QueueAppearanceSend(client.AgentId); | ||
243 | } | ||
244 | |||
245 | // m_log.WarnFormat("[AVFACTORY]: complete SetAppearance for {0}:\n{1}",client.AgentId,sp.Appearance.ToString()); | ||
246 | } | ||
247 | |||
248 | /// <summary> | ||
249 | /// Checks for the existance of a baked texture asset and | 341 | /// Checks for the existance of a baked texture asset and |
250 | /// requests the viewer rebake if the asset is not found | 342 | /// requests the viewer rebake if the asset is not found |
251 | /// </summary> | 343 | /// </summary> |
252 | /// <param name="client"></param> | 344 | /// <param name="sp"></param> |
253 | /// <param name="textureID"></param> | 345 | /// <param name="textureID"></param> |
254 | /// <param name="idx"></param> | 346 | /// <param name="idx"></param> |
255 | private bool CheckBakedTextureAsset(IClientAPI client, UUID textureID, int idx) | 347 | private bool CheckBakedTextureAsset(IScenePresence sp, UUID textureID, int idx) |
256 | { | 348 | { |
257 | if (m_scene.AssetService.Get(textureID.ToString()) == null) | 349 | if (m_scene.AssetService.Get(textureID.ToString()) == null) |
258 | { | 350 | { |
259 | m_log.WarnFormat("[AVFACTORY]: Missing baked texture {0} ({1}) for avatar {2}", | 351 | m_log.WarnFormat("[AVFACTORY]: Missing baked texture {0} ({1}) for avatar {2}", |
260 | textureID, idx, client.Name); | 352 | textureID, idx, sp.Name); |
261 | return false; | 353 | return false; |
262 | } | 354 | } |
263 | return true; | 355 | return true; |
264 | } | 356 | } |
265 | 357 | ||
266 | public Dictionary<BakeType, Primitive.TextureEntryFace> GetBakedTextureFaces(UUID agentId) | ||
267 | { | ||
268 | ScenePresence sp = m_scene.GetScenePresence(agentId); | ||
269 | |||
270 | if (sp == null) | ||
271 | return new Dictionary<BakeType, Primitive.TextureEntryFace>(); | ||
272 | |||
273 | return GetBakedTextureFaces(sp); | ||
274 | } | ||
275 | |||
276 | private Dictionary<BakeType, Primitive.TextureEntryFace> GetBakedTextureFaces(ScenePresence sp) | 358 | private Dictionary<BakeType, Primitive.TextureEntryFace> GetBakedTextureFaces(ScenePresence sp) |
277 | { | 359 | { |
278 | if (sp.IsChildAgent) | 360 | if (sp.IsChildAgent) |
@@ -302,136 +384,6 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
302 | return bakedTextures; | 384 | return bakedTextures; |
303 | } | 385 | } |
304 | 386 | ||
305 | public bool SaveBakedTextures(UUID agentId) | ||
306 | { | ||
307 | ScenePresence sp = m_scene.GetScenePresence(agentId); | ||
308 | |||
309 | if (sp == null) | ||
310 | return false; | ||
311 | |||
312 | m_log.DebugFormat( | ||
313 | "[AV FACTORY]: Permanently saving baked textures for {0} in {1}", | ||
314 | sp.Name, m_scene.RegionInfo.RegionName); | ||
315 | |||
316 | Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = GetBakedTextureFaces(sp); | ||
317 | |||
318 | if (bakedTextures.Count == 0) | ||
319 | return false; | ||
320 | |||
321 | foreach (BakeType bakeType in bakedTextures.Keys) | ||
322 | { | ||
323 | Primitive.TextureEntryFace bakedTextureFace = bakedTextures[bakeType]; | ||
324 | |||
325 | if (bakedTextureFace == null) | ||
326 | { | ||
327 | m_log.WarnFormat( | ||
328 | "[AV FACTORY]: No texture ID set for {0} for {1} in {2} not found when trying to save permanently", | ||
329 | bakeType, sp.Name, m_scene.RegionInfo.RegionName); | ||
330 | |||
331 | continue; | ||
332 | } | ||
333 | |||
334 | AssetBase asset = m_scene.AssetService.Get(bakedTextureFace.TextureID.ToString()); | ||
335 | |||
336 | if (asset != null) | ||
337 | { | ||
338 | asset.Temporary = false; | ||
339 | asset.Local = false; | ||
340 | m_scene.AssetService.Store(asset); | ||
341 | } | ||
342 | else | ||
343 | { | ||
344 | m_log.WarnFormat( | ||
345 | "[AV FACTORY]: Baked texture id {0} not found for bake {1} for avatar {2} in {3} when trying to save permanently", | ||
346 | bakedTextureFace.TextureID, bakeType, sp.Name, m_scene.RegionInfo.RegionName); | ||
347 | } | ||
348 | } | ||
349 | |||
350 | // for (int i = 0; i < faceTextures.Length; i++) | ||
351 | // { | ||
352 | //// m_log.DebugFormat( | ||
353 | //// "[AVFACTORY]: NPC avatar {0} has texture id {1} : {2}", | ||
354 | //// acd.AgentID, i, acd.Appearance.Texture.FaceTextures[i]); | ||
355 | // | ||
356 | // if (faceTextures[i] == null) | ||
357 | // continue; | ||
358 | // | ||
359 | // AssetBase asset = m_scene.AssetService.Get(faceTextures[i].TextureID.ToString()); | ||
360 | // | ||
361 | // if (asset != null) | ||
362 | // { | ||
363 | // asset.Temporary = false; | ||
364 | // m_scene.AssetService.Store(asset); | ||
365 | // } | ||
366 | // else | ||
367 | // { | ||
368 | // m_log.WarnFormat( | ||
369 | // "[AV FACTORY]: Baked texture {0} for {1} in {2} not found when trying to save permanently", | ||
370 | // faceTextures[i].TextureID, sp.Name, m_scene.RegionInfo.RegionName); | ||
371 | // } | ||
372 | // } | ||
373 | |||
374 | return true; | ||
375 | } | ||
376 | |||
377 | #region UpdateAppearanceTimer | ||
378 | |||
379 | /// <summary> | ||
380 | /// Queue up a request to send appearance, makes it possible to | ||
381 | /// accumulate changes without sending out each one separately. | ||
382 | /// </summary> | ||
383 | public void QueueAppearanceSend(UUID agentid) | ||
384 | { | ||
385 | // m_log.WarnFormat("[AVFACTORY]: Queue appearance send for {0}", agentid); | ||
386 | |||
387 | // 10000 ticks per millisecond, 1000 milliseconds per second | ||
388 | long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_sendtime * 1000 * 10000); | ||
389 | lock (m_sendqueue) | ||
390 | { | ||
391 | m_sendqueue[agentid] = timestamp; | ||
392 | m_updateTimer.Start(); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | public void QueueAppearanceSave(UUID agentid) | ||
397 | { | ||
398 | // m_log.WarnFormat("[AVFACTORY]: Queue appearance save for {0}", agentid); | ||
399 | |||
400 | // 10000 ticks per millisecond, 1000 milliseconds per second | ||
401 | long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000); | ||
402 | lock (m_savequeue) | ||
403 | { | ||
404 | m_savequeue[agentid] = timestamp; | ||
405 | m_updateTimer.Start(); | ||
406 | } | ||
407 | } | ||
408 | |||
409 | private void SaveAppearance(UUID agentid) | ||
410 | { | ||
411 | // We must set appearance parameters in the en_US culture in order to avoid issues where values are saved | ||
412 | // in a culture where decimal points are commas and then reloaded in a culture which just treats them as | ||
413 | // number seperators. | ||
414 | Culture.SetCurrentCulture(); | ||
415 | |||
416 | ScenePresence sp = m_scene.GetScenePresence(agentid); | ||
417 | if (sp == null) | ||
418 | { | ||
419 | m_log.WarnFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentid); | ||
420 | return; | ||
421 | } | ||
422 | |||
423 | // m_log.WarnFormat("[AVFACTORY] avatar {0} save appearance",agentid); | ||
424 | |||
425 | // This could take awhile since it needs to pull inventory | ||
426 | // 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 | ||
427 | // assets and item asset id changes to complete. | ||
428 | // I don't think we need to worry about doing this within m_setAppearanceLock since the queueing avoids | ||
429 | // multiple save requests. | ||
430 | SetAppearanceAssets(sp.UUID, sp.Appearance); | ||
431 | |||
432 | m_scene.AvatarService.SetAppearance(agentid, sp.Appearance); | ||
433 | } | ||
434 | |||
435 | private void HandleAppearanceUpdateTimer(object sender, EventArgs ea) | 387 | private void HandleAppearanceUpdateTimer(object sender, EventArgs ea) |
436 | { | 388 | { |
437 | long now = DateTime.Now.Ticks; | 389 | long now = DateTime.Now.Ticks; |
@@ -464,43 +416,122 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
464 | 416 | ||
465 | if (m_savequeue.Count == 0 && m_sendqueue.Count == 0) | 417 | if (m_savequeue.Count == 0 && m_sendqueue.Count == 0) |
466 | m_updateTimer.Stop(); | 418 | m_updateTimer.Stop(); |
467 | } | 419 | } |
468 | 420 | ||
469 | #endregion | 421 | private void SaveAppearance(UUID agentid) |
470 | 422 | { | |
423 | // We must set appearance parameters in the en_US culture in order to avoid issues where values are saved | ||
424 | // in a culture where decimal points are commas and then reloaded in a culture which just treats them as | ||
425 | // number seperators. | ||
426 | Culture.SetCurrentCulture(); | ||
427 | |||
428 | ScenePresence sp = m_scene.GetScenePresence(agentid); | ||
429 | if (sp == null) | ||
430 | { | ||
431 | m_log.WarnFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentid); | ||
432 | return; | ||
433 | } | ||
434 | |||
435 | // m_log.WarnFormat("[AVFACTORY] avatar {0} save appearance",agentid); | ||
436 | |||
437 | // This could take awhile since it needs to pull inventory | ||
438 | // 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 | ||
439 | // assets and item asset id changes to complete. | ||
440 | // I don't think we need to worry about doing this within m_setAppearanceLock since the queueing avoids | ||
441 | // multiple save requests. | ||
442 | SetAppearanceAssets(sp.UUID, sp.Appearance); | ||
443 | |||
444 | m_scene.AvatarService.SetAppearance(agentid, sp.Appearance); | ||
445 | } | ||
446 | |||
447 | private void SetAppearanceAssets(UUID userID, AvatarAppearance appearance) | ||
448 | { | ||
449 | IInventoryService invService = m_scene.InventoryService; | ||
450 | |||
451 | if (invService.GetRootFolder(userID) != null) | ||
452 | { | ||
453 | for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) | ||
454 | { | ||
455 | for (int j = 0; j < appearance.Wearables[j].Count; j++) | ||
456 | { | ||
457 | if (appearance.Wearables[i][j].ItemID == UUID.Zero) | ||
458 | continue; | ||
459 | |||
460 | // Ignore ruth's assets | ||
461 | if (appearance.Wearables[i][j].ItemID == AvatarWearable.DefaultWearables[i][0].ItemID) | ||
462 | continue; | ||
463 | InventoryItemBase baseItem = new InventoryItemBase(appearance.Wearables[i][j].ItemID, userID); | ||
464 | baseItem = invService.GetItem(baseItem); | ||
465 | |||
466 | if (baseItem != null) | ||
467 | { | ||
468 | appearance.Wearables[i].Add(appearance.Wearables[i][j].ItemID, baseItem.AssetID); | ||
469 | } | ||
470 | else | ||
471 | { | ||
472 | m_log.ErrorFormat( | ||
473 | "[AVFACTORY]: Can't find inventory item {0} for {1}, setting to default", | ||
474 | appearance.Wearables[i][j].ItemID, (WearableType)i); | ||
475 | |||
476 | appearance.Wearables[i].RemoveItem(appearance.Wearables[i][j].ItemID); | ||
477 | } | ||
478 | } | ||
479 | } | ||
480 | } | ||
481 | else | ||
482 | { | ||
483 | m_log.WarnFormat("[AVFACTORY]: user {0} has no inventory, appearance isn't going to work", userID); | ||
484 | } | ||
485 | } | ||
486 | |||
487 | #endregion | ||
488 | |||
489 | #region Client Event Handlers | ||
471 | /// <summary> | 490 | /// <summary> |
472 | /// Tell the client for this scene presence what items it should be wearing now | 491 | /// Tell the client for this scene presence what items it should be wearing now |
473 | /// </summary> | 492 | /// </summary> |
474 | public void SendWearables(IClientAPI client) | 493 | /// <param name="client"></param> |
475 | { | 494 | private void Client_OnRequestWearables(IClientAPI client) |
495 | { | ||
496 | // m_log.DebugFormat("[AVFACTORY]: Client_OnRequestWearables called for {0} ({1})", client.Name, client.AgentId); | ||
476 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); | 497 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); |
477 | if (sp == null) | 498 | if (sp != null) |
478 | { | 499 | client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++); |
479 | m_log.WarnFormat("[AVFACTORY]: SendWearables unable to find presence for {0}", client.AgentId); | 500 | else |
480 | return; | 501 | m_log.WarnFormat("[AVFACTORY]: Client_OnRequestWearables unable to find presence for {0}", client.AgentId); |
481 | } | 502 | } |
482 | 503 | ||
483 | // m_log.DebugFormat("[AVFACTORY]: Received request for wearables of {0}", client.Name); | 504 | /// <summary> |
484 | 505 | /// Set appearance data (texture asset IDs and slider settings) received from a client | |
485 | client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++); | 506 | /// </summary> |
507 | /// <param name="client"></param> | ||
508 | /// <param name="texture"></param> | ||
509 | /// <param name="visualParam"></param> | ||
510 | private void Client_OnSetAppearance(IClientAPI client, Primitive.TextureEntry textureEntry, byte[] visualParams) | ||
511 | { | ||
512 | // m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance called for {0} ({1})", client.Name, client.AgentId); | ||
513 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); | ||
514 | if (sp != null) | ||
515 | SetAppearance(sp, textureEntry, visualParams); | ||
516 | else | ||
517 | m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance unable to find presence for {0}", client.AgentId); | ||
486 | } | 518 | } |
487 | 519 | ||
488 | /// <summary> | 520 | /// <summary> |
489 | /// Update what the avatar is wearing using an item from their inventory. | 521 | /// Update what the avatar is wearing using an item from their inventory. |
490 | /// </summary> | 522 | /// </summary> |
491 | /// <param name="sender"></param> | 523 | /// <param name="client"></param> |
492 | /// <param name="e"></param> | 524 | /// <param name="e"></param> |
493 | public void AvatarIsWearing(IClientAPI client, AvatarWearingArgs e) | 525 | private void Client_OnAvatarNowWearing(IClientAPI client, AvatarWearingArgs e) |
494 | { | 526 | { |
527 | // m_log.WarnFormat("[AVFACTORY]: Client_OnAvatarNowWearing called for {0} ({1})", client.Name, client.AgentId); | ||
495 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); | 528 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); |
496 | if (sp == null) | 529 | if (sp == null) |
497 | { | 530 | { |
498 | m_log.WarnFormat("[AVFACTORY]: AvatarIsWearing unable to find presence for {0}", client.AgentId); | 531 | m_log.WarnFormat("[AVFACTORY]: Client_OnAvatarNowWearing unable to find presence for {0}", client.AgentId); |
499 | return; | 532 | return; |
500 | } | 533 | } |
501 | 534 | ||
502 | // m_log.WarnFormat("[AVFACTORY]: AvatarIsWearing called for {0}", client.AgentId); | ||
503 | |||
504 | // we need to clean out the existing textures | 535 | // we need to clean out the existing textures |
505 | sp.Appearance.ResetAppearance(); | 536 | sp.Appearance.ResetAppearance(); |
506 | 537 | ||
@@ -527,65 +558,8 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
527 | // of visual param and baked texture changes. When those complete, the new appearance will be sent | 558 | // of visual param and baked texture changes. When those complete, the new appearance will be sent |
528 | 559 | ||
529 | QueueAppearanceSave(client.AgentId); | 560 | QueueAppearanceSave(client.AgentId); |
530 | } | 561 | } |
531 | } | 562 | } |
532 | 563 | #endregion | |
533 | public bool SendAppearance(UUID agentId) | ||
534 | { | ||
535 | ScenePresence sp = m_scene.GetScenePresence(agentId); | ||
536 | if (sp == null) | ||
537 | { | ||
538 | m_log.WarnFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentId); | ||
539 | return false; | ||
540 | } | ||
541 | |||
542 | // Send the appearance to everyone in the scene | ||
543 | sp.SendAppearanceToAllOtherAgents(); | ||
544 | |||
545 | // Send animations back to the avatar as well | ||
546 | sp.Animator.SendAnimPack(); | ||
547 | |||
548 | return true; | ||
549 | } | ||
550 | |||
551 | private void SetAppearanceAssets(UUID userID, AvatarAppearance appearance) | ||
552 | { | ||
553 | IInventoryService invService = m_scene.InventoryService; | ||
554 | |||
555 | if (invService.GetRootFolder(userID) != null) | ||
556 | { | ||
557 | for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) | ||
558 | { | ||
559 | for (int j = 0; j < appearance.Wearables[j].Count; j++) | ||
560 | { | ||
561 | if (appearance.Wearables[i][j].ItemID == UUID.Zero) | ||
562 | continue; | ||
563 | |||
564 | // Ignore ruth's assets | ||
565 | if (appearance.Wearables[i][j].ItemID == AvatarWearable.DefaultWearables[i][0].ItemID) | ||
566 | continue; | ||
567 | InventoryItemBase baseItem = new InventoryItemBase(appearance.Wearables[i][j].ItemID, userID); | ||
568 | baseItem = invService.GetItem(baseItem); | ||
569 | |||
570 | if (baseItem != null) | ||
571 | { | ||
572 | appearance.Wearables[i].Add(appearance.Wearables[i][j].ItemID, baseItem.AssetID); | ||
573 | } | ||
574 | else | ||
575 | { | ||
576 | m_log.ErrorFormat( | ||
577 | "[AVFACTORY]: Can't find inventory item {0} for {1}, setting to default", | ||
578 | appearance.Wearables[i][j].ItemID, (WearableType)i); | ||
579 | |||
580 | appearance.Wearables[i].RemoveItem(appearance.Wearables[i][j].ItemID); | ||
581 | } | ||
582 | } | ||
583 | } | ||
584 | } | ||
585 | else | ||
586 | { | ||
587 | m_log.WarnFormat("[AVFACTORY]: user {0} has no inventory, appearance isn't going to work", userID); | ||
588 | } | ||
589 | } | ||
590 | } | 564 | } |
591 | } | 565 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs index 7b2f14e..931d52f 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs | |||
@@ -55,15 +55,13 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
55 | AvatarFactoryModule afm = new AvatarFactoryModule(); | 55 | AvatarFactoryModule afm = new AvatarFactoryModule(); |
56 | TestScene scene = SceneHelpers.SetupScene(); | 56 | TestScene scene = SceneHelpers.SetupScene(); |
57 | SceneHelpers.SetupSceneModules(scene, afm); | 57 | SceneHelpers.SetupSceneModules(scene, afm); |
58 | IClientAPI tc = SceneHelpers.AddScenePresence(scene, userId).ControllingClient; | 58 | ScenePresence sp = SceneHelpers.AddScenePresence(scene, userId); |
59 | 59 | ||
60 | byte[] visualParams = new byte[AvatarAppearance.VISUALPARAM_COUNT]; | 60 | byte[] visualParams = new byte[AvatarAppearance.VISUALPARAM_COUNT]; |
61 | for (byte i = 0; i < visualParams.Length; i++) | 61 | for (byte i = 0; i < visualParams.Length; i++) |
62 | visualParams[i] = i; | 62 | visualParams[i] = i; |
63 | 63 | ||
64 | afm.SetAppearanceFromClient(tc, new Primitive.TextureEntry(TestHelpers.ParseTail(0x10)), visualParams); | 64 | afm.SetAppearance(sp, new Primitive.TextureEntry(TestHelpers.ParseTail(0x10)), visualParams); |
65 | |||
66 | ScenePresence sp = scene.GetScenePresence(userId); | ||
67 | 65 | ||
68 | // TODO: Check baked texture | 66 | // TODO: Check baked texture |
69 | Assert.AreEqual(visualParams, sp.Appearance.VisualParams); | 67 | Assert.AreEqual(visualParams, sp.Appearance.VisualParams); |
@@ -84,8 +82,8 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
84 | 82 | ||
85 | AvatarFactoryModule afm = new AvatarFactoryModule(); | 83 | AvatarFactoryModule afm = new AvatarFactoryModule(); |
86 | TestScene scene = SceneHelpers.SetupScene(assetCache); | 84 | TestScene scene = SceneHelpers.SetupScene(assetCache); |
87 | SceneHelpers.SetupSceneModules(scene, afm); | 85 | SceneHelpers.SetupSceneModules(scene, afm); |
88 | IClientAPI tc = SceneHelpers.AddScenePresence(scene, userId).ControllingClient; | 86 | ScenePresence sp = SceneHelpers.AddScenePresence(scene, userId); |
89 | 87 | ||
90 | // TODO: Use the actual BunchOfCaps functionality once we slot in the CapabilitiesModules | 88 | // TODO: Use the actual BunchOfCaps functionality once we slot in the CapabilitiesModules |
91 | AssetBase uploadedAsset; | 89 | AssetBase uploadedAsset; |
@@ -104,7 +102,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
104 | Primitive.TextureEntryFace eyesFace = bakedTextureEntry.CreateFace(eyesFaceIndex); | 102 | Primitive.TextureEntryFace eyesFace = bakedTextureEntry.CreateFace(eyesFaceIndex); |
105 | eyesFace.TextureID = eyesTextureId; | 103 | eyesFace.TextureID = eyesTextureId; |
106 | 104 | ||
107 | afm.SetAppearanceFromClient(tc, bakedTextureEntry, visualParams); | 105 | afm.SetAppearance(sp, bakedTextureEntry, visualParams); |
108 | afm.SaveBakedTextures(userId); | 106 | afm.SaveBakedTextures(userId); |
109 | // Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = afm.GetBakedTextureFaces(userId); | 107 | // Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = afm.GetBakedTextureFaces(userId); |
110 | 108 | ||