clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Collections.Generic;
30 using System.Text;
31 using System.IO;
32 using System.Reflection;
33 using System.Threading;
34 using System.Timers;
35 using log4net;
36 using OpenMetaverse;
37 using OpenMetaverse.Assets;
38 using Nini.Config;
39 using OpenSim.Framework;
40 using OpenSim.Framework.Console;
41 using pCampBot.Interfaces;
42 using Timer = System.Timers.Timer;
43 using PermissionMask = OpenSim.Framework.PermissionMask;
44  
45 namespace pCampBot
46 {
47 public enum ConnectionState
48 {
49 Disconnected,
50 Connecting,
51 Connected,
52 Disconnecting
53 }
54  
55 public class Bot
56 {
57 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
58  
59 public delegate void AnEvent(Bot callbot, EventType someevent); // event delegate for bot events
60  
61 /// <summary>
62 /// Controls whether bots request textures for the object information they receive
63 /// </summary>
64 public bool RequestObjectTextures { get; set; }
65  
66 /// <summary>
67 /// Bot manager.
68 /// </summary>
69 public BotManager Manager { get; private set; }
70  
71 /// <summary>
72 /// Behaviours implemented by this bot.
73 /// </summary>
74 /// <remarks>
75 /// Indexed by abbreviated name. There can only be one instance of a particular behaviour.
76 /// Lock this structure before manipulating it.
77 /// </remarks>
78 public Dictionary<string, IBehaviour> Behaviours { get; private set; }
79  
80 /// <summary>
81 /// Objects that the bot has discovered.
82 /// </summary>
83 /// <remarks>
84 /// Returns a list copy. Inserting new objects manually will have no effect.
85 /// </remarks>
86 public Dictionary<UUID, Primitive> Objects
87 {
88 get
89 {
90 lock (m_objects)
91 return new Dictionary<UUID, Primitive>(m_objects);
92 }
93 }
94 private Dictionary<UUID, Primitive> m_objects = new Dictionary<UUID, Primitive>();
95  
96 /// <summary>
97 /// Is this bot connected to the grid?
98 /// </summary>
99 public ConnectionState ConnectionState { get; private set; }
100  
101 public List<Simulator> Simulators
102 {
103 get
104 {
105 lock (Client.Network.Simulators)
106 return new List<Simulator>(Client.Network.Simulators);
107 }
108 }
109  
110 /// <summary>
111 /// The number of connections that this bot has to different simulators.
112 /// </summary>
113 /// <value>Includes both root and child connections.</value>
114 public int SimulatorsCount
115 {
116 get
117 {
118 lock (Client.Network.Simulators)
119 return Client.Network.Simulators.Count;
120 }
121 }
122  
123 public string FirstName { get; private set; }
124 public string LastName { get; private set; }
125 public string Name { get; private set; }
126 public string Password { get; private set; }
127 public string LoginUri { get; private set; }
128 public string StartLocation { get; private set; }
129  
130 public string saveDir;
131 public string wear;
132  
133 public event AnEvent OnConnected;
134 public event AnEvent OnDisconnected;
135  
136 /// <summary>
137 /// Keep a track of the continuously acting thread so that we can abort it.
138 /// </summary>
139 private Thread m_actionThread;
140  
141 protected List<uint> objectIDs = new List<uint>();
142  
143 /// <summary>
144 /// Random number generator.
145 /// </summary>
146 public Random Random { get; private set; }
147  
148 /// <summary>
149 /// New instance of a SecondLife client
150 /// </summary>
151 public GridClient Client { get; private set; }
152  
153 /// <summary>
154 /// Constructor
155 /// </summary>
156 /// <param name="bm"></param>
157 /// <param name="behaviours">Behaviours for this bot to perform</param>
158 /// <param name="firstName"></param>
159 /// <param name="lastName"></param>
160 /// <param name="password"></param>
161 /// <param name="loginUri"></param>
162 /// <param name="behaviours"></param>
163 public Bot(
164 BotManager bm, List<IBehaviour> behaviours,
165 string firstName, string lastName, string password, string startLocation, string loginUri)
166 {
167 ConnectionState = ConnectionState.Disconnected;
168  
169 Random = new Random(bm.Rng.Next());
170 FirstName = firstName;
171 LastName = lastName;
172 Name = string.Format("{0} {1}", FirstName, LastName);
173 Password = password;
174 LoginUri = loginUri;
175 StartLocation = startLocation;
176  
177 Manager = bm;
178  
179 Behaviours = new Dictionary<string, IBehaviour>();
180 foreach (IBehaviour behaviour in behaviours)
181 AddBehaviour(behaviour);
182  
183 // Only calling for use as a template.
184 CreateLibOmvClient();
185 }
186  
187 public bool TryGetBehaviour(string abbreviatedName, out IBehaviour behaviour)
188 {
189 lock (Behaviours)
190 return Behaviours.TryGetValue(abbreviatedName, out behaviour);
191 }
192  
193 public bool AddBehaviour(IBehaviour behaviour)
194 {
195 Dictionary<string, IBehaviour> updatedBehaviours = new Dictionary<string, IBehaviour>(Behaviours);
196  
197 if (!updatedBehaviours.ContainsKey(behaviour.AbbreviatedName))
198 {
199 behaviour.Initialize(this);
200 updatedBehaviours.Add(behaviour.AbbreviatedName, behaviour);
201 Behaviours = updatedBehaviours;
202  
203 return true;
204 }
205  
206 return false;
207 }
208  
209 public bool RemoveBehaviour(string abbreviatedName)
210 {
211 if (Behaviours.Count <= 0)
212 return false;
213  
214 Dictionary<string, IBehaviour> updatedBehaviours = new Dictionary<string, IBehaviour>(Behaviours);
215 IBehaviour behaviour;
216  
217 if (!updatedBehaviours.TryGetValue(abbreviatedName, out behaviour))
218 return false;
219  
220 updatedBehaviours.Remove(abbreviatedName);
221 Behaviours = updatedBehaviours;
222  
223 behaviour.Close();
224  
225 return true;
226 }
227  
228 private void CreateLibOmvClient()
229 {
230 GridClient newClient = new GridClient();
231  
232 if (Client != null)
233 {
234 newClient.Settings.LOGIN_SERVER = Client.Settings.LOGIN_SERVER;
235 newClient.Settings.ALWAYS_DECODE_OBJECTS = Client.Settings.ALWAYS_DECODE_OBJECTS;
236 newClient.Settings.AVATAR_TRACKING = Client.Settings.AVATAR_TRACKING;
237 newClient.Settings.OBJECT_TRACKING = Client.Settings.OBJECT_TRACKING;
238 newClient.Settings.SEND_AGENT_THROTTLE = Client.Settings.SEND_AGENT_THROTTLE;
239 newClient.Settings.SEND_AGENT_UPDATES = Client.Settings.SEND_AGENT_UPDATES;
240 newClient.Settings.SEND_PINGS = Client.Settings.SEND_PINGS;
241 newClient.Settings.STORE_LAND_PATCHES = Client.Settings.STORE_LAND_PATCHES;
242 newClient.Settings.USE_ASSET_CACHE = Client.Settings.USE_ASSET_CACHE;
243 newClient.Settings.MULTIPLE_SIMS = Client.Settings.MULTIPLE_SIMS;
244 newClient.Throttle.Asset = Client.Throttle.Asset;
245 newClient.Throttle.Land = Client.Throttle.Land;
246 newClient.Throttle.Task = Client.Throttle.Task;
247 newClient.Throttle.Texture = Client.Throttle.Texture;
248 newClient.Throttle.Wind = Client.Throttle.Wind;
249 newClient.Throttle.Total = Client.Throttle.Total;
250 }
251 else
252 {
253 newClient.Settings.LOGIN_SERVER = LoginUri;
254 newClient.Settings.ALWAYS_DECODE_OBJECTS = false;
255 newClient.Settings.AVATAR_TRACKING = false;
256 newClient.Settings.OBJECT_TRACKING = false;
257 newClient.Settings.SEND_AGENT_THROTTLE = true;
258 newClient.Settings.SEND_PINGS = true;
259 newClient.Settings.STORE_LAND_PATCHES = false;
260 newClient.Settings.USE_ASSET_CACHE = false;
261 newClient.Settings.MULTIPLE_SIMS = true;
262 newClient.Throttle.Asset = 100000;
263 newClient.Throttle.Land = 100000;
264 newClient.Throttle.Task = 100000;
265 newClient.Throttle.Texture = 100000;
266 newClient.Throttle.Wind = 100000;
267 newClient.Throttle.Total = 400000;
268 }
269  
270 newClient.Network.LoginProgress += Network_LoginProgress;
271 newClient.Network.SimConnected += Network_SimConnected;
272 newClient.Network.SimDisconnected += Network_SimDisconnected;
273 newClient.Network.Disconnected += Network_OnDisconnected;
274 newClient.Objects.ObjectUpdate += Objects_NewPrim;
275  
276 Client = newClient;
277 }
278  
279 //We do our actions here. This is where one would
280 //add additional steps and/or things the bot should do
281 private void Action()
282 {
283 while (ConnectionState == ConnectionState.Connected)
284 {
285 foreach (IBehaviour behaviour in Behaviours.Values)
286 {
287 // Thread.Sleep(Random.Next(3000, 10000));
288  
289 // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType());
290 behaviour.Action();
291 }
292 }
293  
294 foreach (IBehaviour b in Behaviours.Values)
295 b.Close();
296 }
297  
298 /// <summary>
299 /// Tells LibSecondLife to logout and disconnect. Raises the disconnect events once it finishes.
300 /// </summary>
301 public void Disconnect()
302 {
303 ConnectionState = ConnectionState.Disconnecting;
304  
305 foreach (IBehaviour behaviour in Behaviours.Values)
306 behaviour.Close();
307  
308 Client.Network.Logout();
309 }
310  
311 public void Connect()
312 {
313 Thread connectThread = new Thread(ConnectInternal);
314 connectThread.Name = Name;
315 connectThread.IsBackground = true;
316  
317 connectThread.Start();
318 }
319  
320 /// <summary>
321 /// This is the bot startup loop.
322 /// </summary>
323 private void ConnectInternal()
324 {
325 ConnectionState = ConnectionState.Connecting;
326  
327 // Current create a new client on each connect. libomv doesn't seem to process new sim
328 // information (e.g. EstablishAgentCommunication events) if connecting after a disceonnect with the same
329 // client
330 CreateLibOmvClient();
331  
332 if (Client.Network.Login(FirstName, LastName, Password, "pCampBot", StartLocation, "pCampBot"))
333 {
334 ConnectionState = ConnectionState.Connected;
335  
336 Thread.Sleep(Random.Next(1000, 10000));
337 m_actionThread = new Thread(Action);
338 m_actionThread.Start();
339  
340 // OnConnected(this, EventType.CONNECTED);
341 if (wear == "save")
342 {
343 SaveDefaultAppearance();
344 }
345 else if (wear != "no")
346 {
347 MakeDefaultAppearance(wear);
348 }
349  
350 // Extract nearby region information.
351 Client.Grid.GridRegion += Manager.Grid_GridRegion;
352 uint xUint, yUint;
353 Utils.LongToUInts(Client.Network.CurrentSim.Handle, out xUint, out yUint);
354 ushort minX, minY, maxX, maxY;
355 minX = (ushort)Math.Min(0, xUint - 5);
356 minY = (ushort)Math.Min(0, yUint - 5);
357 maxX = (ushort)(xUint + 5);
358 maxY = (ushort)(yUint + 5);
359 Client.Grid.RequestMapBlocks(GridLayerType.Terrain, minX, minY, maxX, maxY, false);
360 }
361 else
362 {
363 ConnectionState = ConnectionState.Disconnected;
364  
365 m_log.ErrorFormat(
366 "{0} {1} cannot login: {2}", FirstName, LastName, Client.Network.LoginMessage);
367  
368 if (OnDisconnected != null)
369 {
370 OnDisconnected(this, EventType.DISCONNECTED);
371 }
372 }
373 }
374  
375 /// <summary>
376 /// Sit this bot on the ground.
377 /// </summary>
378 public void SitOnGround()
379 {
380 if (ConnectionState == ConnectionState.Connected)
381 Client.Self.SitOnGround();
382 }
383  
384 /// <summary>
385 /// Stand this bot
386 /// </summary>
387 public void Stand()
388 {
389 if (ConnectionState == ConnectionState.Connected)
390 {
391 // Unlike sit on ground, here libomv checks whether we have SEND_AGENT_UPDATES enabled.
392 bool prevUpdatesSetting = Client.Settings.SEND_AGENT_UPDATES;
393 Client.Settings.SEND_AGENT_UPDATES = true;
394 Client.Self.Stand();
395 Client.Settings.SEND_AGENT_UPDATES = prevUpdatesSetting;
396 }
397 }
398  
399 public void SaveDefaultAppearance()
400 {
401 saveDir = "MyAppearance/" + FirstName + "_" + LastName;
402 if (!Directory.Exists(saveDir))
403 {
404 Directory.CreateDirectory(saveDir);
405 }
406  
407 Array wtypes = Enum.GetValues(typeof(WearableType));
408 foreach (WearableType wtype in wtypes)
409 {
410 UUID wearable = Client.Appearance.GetWearableAsset(wtype);
411 if (wearable != UUID.Zero)
412 {
413 Client.Assets.RequestAsset(wearable, AssetType.Clothing, false, Asset_ReceivedCallback);
414 Client.Assets.RequestAsset(wearable, AssetType.Bodypart, false, Asset_ReceivedCallback);
415 }
416 }
417 }
418  
419 public void SaveAsset(AssetWearable asset)
420 {
421 if (asset != null)
422 {
423 try
424 {
425 if (asset.Decode())
426 {
427 File.WriteAllBytes(Path.Combine(saveDir, String.Format("{1}.{0}",
428 asset.AssetType.ToString().ToLower(),
429 asset.WearableType)), asset.AssetData);
430 }
431 else
432 {
433 m_log.WarnFormat("Failed to decode {0} asset {1}", asset.AssetType, asset.AssetID);
434 }
435 }
436 catch (Exception e)
437 {
438 m_log.ErrorFormat("Exception: {0}{1}", e.Message, e.StackTrace);
439 }
440 }
441 }
442  
443 public WearableType GetWearableType(string path)
444 {
445 string type = ((((path.Split('/'))[2]).Split('.'))[0]).Trim();
446 switch (type)
447 {
448 case "Eyes":
449 return WearableType.Eyes;
450 case "Hair":
451 return WearableType.Hair;
452 case "Pants":
453 return WearableType.Pants;
454 case "Shape":
455 return WearableType.Shape;
456 case "Shirt":
457 return WearableType.Shirt;
458 case "Skin":
459 return WearableType.Skin;
460 default:
461 return WearableType.Shape;
462 }
463 }
464  
465 public void MakeDefaultAppearance(string wear)
466 {
467 try
468 {
469 if (wear == "yes")
470 {
471 //TODO: Implement random outfit picking
472 m_log.DebugFormat("Picks a random outfit. Not yet implemented.");
473 }
474 else if (wear != "save")
475 saveDir = "MyAppearance/" + wear;
476 saveDir = saveDir + "/";
477  
478 string[] clothing = Directory.GetFiles(saveDir, "*.clothing", SearchOption.TopDirectoryOnly);
479 string[] bodyparts = Directory.GetFiles(saveDir, "*.bodypart", SearchOption.TopDirectoryOnly);
480 InventoryFolder clothfolder = FindClothingFolder();
481 UUID transid = UUID.Random();
482 List<InventoryBase> listwearables = new List<InventoryBase>();
483  
484 for (int i = 0; i < clothing.Length; i++)
485 {
486 UUID assetID = UUID.Random();
487 AssetClothing asset = new AssetClothing(assetID, File.ReadAllBytes(clothing[i]));
488 asset.Decode();
489 asset.Owner = Client.Self.AgentID;
490 asset.WearableType = GetWearableType(clothing[i]);
491 asset.Encode();
492 transid = Client.Assets.RequestUpload(asset,true);
493 Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyClothing" + i.ToString(), "MyClothing", AssetType.Clothing,
494 transid, InventoryType.Wearable, asset.WearableType, (OpenMetaverse.PermissionMask)PermissionMask.All, delegate(bool success, InventoryItem item)
495 {
496 if (success)
497 {
498 listwearables.Add(item);
499 }
500 else
501 {
502 m_log.WarnFormat("Failed to create item {0}", item.Name);
503 }
504 }
505 );
506 }
507  
508 for (int i = 0; i < bodyparts.Length; i++)
509 {
510 UUID assetID = UUID.Random();
511 AssetBodypart asset = new AssetBodypart(assetID, File.ReadAllBytes(bodyparts[i]));
512 asset.Decode();
513 asset.Owner = Client.Self.AgentID;
514 asset.WearableType = GetWearableType(bodyparts[i]);
515 asset.Encode();
516 transid = Client.Assets.RequestUpload(asset,true);
517 Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyBodyPart" + i.ToString(), "MyBodyPart", AssetType.Bodypart,
518 transid, InventoryType.Wearable, asset.WearableType, (OpenMetaverse.PermissionMask)PermissionMask.All, delegate(bool success, InventoryItem item)
519 {
520 if (success)
521 {
522 listwearables.Add(item);
523 }
524 else
525 {
526 m_log.WarnFormat("Failed to create item {0}", item.Name);
527 }
528 }
529 );
530 }
531  
532 Thread.Sleep(1000);
533  
534 if (listwearables == null || listwearables.Count == 0)
535 {
536 m_log.DebugFormat("Nothing to send on this folder!");
537 }
538 else
539 {
540 m_log.DebugFormat("Sending {0} wearables...", listwearables.Count);
541 Client.Appearance.WearOutfit(listwearables, false);
542 }
543 }
544 catch (Exception ex)
545 {
546 Console.WriteLine(ex.ToString());
547 }
548 }
549  
550 public InventoryFolder FindClothingFolder()
551 {
552 UUID rootfolder = Client.Inventory.Store.RootFolder.UUID;
553 List<InventoryBase> listfolders = Client.Inventory.Store.GetContents(rootfolder);
554 InventoryFolder clothfolder = new InventoryFolder(UUID.Random());
555 foreach (InventoryBase folder in listfolders)
556 {
557 if (folder.Name == "Clothing")
558 {
559 clothfolder = (InventoryFolder)folder;
560 break;
561 }
562 }
563 return clothfolder;
564 }
565  
566 public void Network_LoginProgress(object sender, LoginProgressEventArgs args)
567 {
568 m_log.DebugFormat("[BOT]: Bot {0} {1} in Network_LoginProcess", Name, args.Status);
569  
570 if (args.Status == LoginStatus.Success)
571 {
572 if (OnConnected != null)
573 {
574 OnConnected(this, EventType.CONNECTED);
575 }
576 }
577 }
578  
579 public void Network_SimConnected(object sender, SimConnectedEventArgs args)
580 {
581 m_log.DebugFormat(
582 "[BOT]: Bot {0} connected to region {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint);
583 }
584  
585 public void Network_SimDisconnected(object sender, SimDisconnectedEventArgs args)
586 {
587 m_log.DebugFormat(
588 "[BOT]: Bot {0} disconnected from region {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint);
589 }
590  
591 public void Network_OnDisconnected(object sender, DisconnectedEventArgs args)
592 {
593 ConnectionState = ConnectionState.Disconnected;
594  
595 m_log.DebugFormat(
596 "[BOT]: Bot {0} disconnected from grid, reason {1}, message {2}", Name, args.Reason, args.Message);
597  
598 // m_log.ErrorFormat("Fired Network_OnDisconnected");
599  
600 // if (
601 // (args.Reason == NetworkManager.DisconnectType.SimShutdown
602 // || args.Reason == NetworkManager.DisconnectType.NetworkTimeout)
603 // && OnDisconnected != null)
604  
605  
606  
607 if (
608 (args.Reason == NetworkManager.DisconnectType.ClientInitiated
609 || args.Reason == NetworkManager.DisconnectType.ServerInitiated
610 || args.Reason == NetworkManager.DisconnectType.NetworkTimeout)
611 && OnDisconnected != null)
612 // if (OnDisconnected != null)
613 {
614 OnDisconnected(this, EventType.DISCONNECTED);
615 }
616 }
617  
618 public void Objects_NewPrim(object sender, PrimEventArgs args)
619 {
620 if (!RequestObjectTextures)
621 return;
622  
623 Primitive prim = args.Prim;
624  
625 if (prim != null)
626 {
627 lock (m_objects)
628 m_objects[prim.ID] = prim;
629  
630 if (prim.Textures != null)
631 {
632 if (prim.Textures.DefaultTexture.TextureID != UUID.Zero)
633 {
634 GetTextureOrMesh(prim.Textures.DefaultTexture.TextureID, true);
635 }
636  
637 for (int i = 0; i < prim.Textures.FaceTextures.Length; i++)
638 {
639 Primitive.TextureEntryFace face = prim.Textures.FaceTextures[i];
640  
641 if (face != null)
642 {
643 UUID textureID = prim.Textures.FaceTextures[i].TextureID;
644  
645 if (textureID != UUID.Zero)
646 GetTextureOrMesh(textureID, true);
647 }
648 }
649 }
650  
651 if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)
652 {
653 bool mesh = (prim.Sculpt.Type == SculptType.Mesh);
654 GetTextureOrMesh(prim.Sculpt.SculptTexture, !mesh);
655 }
656 }
657 }
658  
659 private void GetTextureOrMesh(UUID assetID, bool texture)
660 {
661 lock (Manager.AssetsReceived)
662 {
663 // Don't request assets more than once.
664 if (Manager.AssetsReceived.ContainsKey(assetID))
665 return;
666  
667 Manager.AssetsReceived[assetID] = false;
668 }
669  
670 try
671 {
672 if (texture)
673 Client.Assets.RequestImage(assetID, ImageType.Normal, Asset_TextureCallback_Texture);
674 else
675 Client.Assets.RequestMesh(assetID, Asset_MeshCallback);
676 }
677 catch (Exception e)
678 {
679 m_log.Warn(string.Format("Error requesting {0} {1}", texture ? "texture" : "mesh", assetID), e);
680 }
681 }
682  
683 public void Asset_TextureCallback_Texture(TextureRequestState state, AssetTexture assetTexture)
684 {
685 if (state == TextureRequestState.Finished)
686 {
687 lock (Manager.AssetsReceived)
688 Manager.AssetsReceived[assetTexture.AssetID] = true;
689 }
690 }
691  
692 private void Asset_MeshCallback(bool success, AssetMesh assetMesh)
693 {
694 lock (Manager.AssetsReceived)
695 Manager.AssetsReceived[assetMesh.AssetID] = success;
696 }
697  
698 public void Asset_ReceivedCallback(AssetDownload transfer, Asset asset)
699 {
700 lock (Manager.AssetsReceived)
701 Manager.AssetsReceived[asset.AssetID] = true;
702  
703 // if (wear == "save")
704 // {
705 // SaveAsset((AssetWearable) asset);
706 // }
707 }
708 }
709 }