opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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 lock (Behaviours)
196 {
197 if (!Behaviours.ContainsKey(behaviour.AbbreviatedName))
198 {
199 behaviour.Initialize(this);
200 Behaviours.Add(behaviour.AbbreviatedName, behaviour);
201  
202 return true;
203 }
204 }
205  
206 return false;
207 }
208  
209 public bool RemoveBehaviour(string abbreviatedName)
210 {
211 lock (Behaviours)
212 {
213 IBehaviour behaviour;
214  
215 if (!Behaviours.TryGetValue(abbreviatedName, out behaviour))
216 return false;
217  
218 behaviour.Close();
219 Behaviours.Remove(abbreviatedName);
220  
221 return true;
222 }
223 }
224  
225 private void CreateLibOmvClient()
226 {
227 GridClient newClient = new GridClient();
228  
229 if (Client != null)
230 {
231 newClient.Settings.LOGIN_SERVER = Client.Settings.LOGIN_SERVER;
232 newClient.Settings.ALWAYS_DECODE_OBJECTS = Client.Settings.ALWAYS_DECODE_OBJECTS;
233 newClient.Settings.AVATAR_TRACKING = Client.Settings.AVATAR_TRACKING;
234 newClient.Settings.OBJECT_TRACKING = Client.Settings.OBJECT_TRACKING;
235 newClient.Settings.SEND_AGENT_THROTTLE = Client.Settings.SEND_AGENT_THROTTLE;
236 newClient.Settings.SEND_AGENT_UPDATES = Client.Settings.SEND_AGENT_UPDATES;
237 newClient.Settings.SEND_PINGS = Client.Settings.SEND_PINGS;
238 newClient.Settings.STORE_LAND_PATCHES = Client.Settings.STORE_LAND_PATCHES;
239 newClient.Settings.USE_ASSET_CACHE = Client.Settings.USE_ASSET_CACHE;
240 newClient.Settings.MULTIPLE_SIMS = Client.Settings.MULTIPLE_SIMS;
241 newClient.Throttle.Asset = Client.Throttle.Asset;
242 newClient.Throttle.Land = Client.Throttle.Land;
243 newClient.Throttle.Task = Client.Throttle.Task;
244 newClient.Throttle.Texture = Client.Throttle.Texture;
245 newClient.Throttle.Wind = Client.Throttle.Wind;
246 newClient.Throttle.Total = Client.Throttle.Total;
247 }
248 else
249 {
250 newClient.Settings.LOGIN_SERVER = LoginUri;
251 newClient.Settings.ALWAYS_DECODE_OBJECTS = false;
252 newClient.Settings.AVATAR_TRACKING = false;
253 newClient.Settings.OBJECT_TRACKING = false;
254 newClient.Settings.SEND_AGENT_THROTTLE = true;
255 newClient.Settings.SEND_PINGS = true;
256 newClient.Settings.STORE_LAND_PATCHES = false;
257 newClient.Settings.USE_ASSET_CACHE = false;
258 newClient.Settings.MULTIPLE_SIMS = true;
259 newClient.Throttle.Asset = 100000;
260 newClient.Throttle.Land = 100000;
261 newClient.Throttle.Task = 100000;
262 newClient.Throttle.Texture = 100000;
263 newClient.Throttle.Wind = 100000;
264 newClient.Throttle.Total = 400000;
265 }
266  
267 newClient.Network.LoginProgress += Network_LoginProgress;
268 newClient.Network.SimConnected += Network_SimConnected;
269 newClient.Network.SimDisconnected += Network_SimDisconnected;
270 newClient.Network.Disconnected += Network_OnDisconnected;
271 newClient.Objects.ObjectUpdate += Objects_NewPrim;
272  
273 Client = newClient;
274 }
275  
276 //We do our actions here. This is where one would
277 //add additional steps and/or things the bot should do
278 private void Action()
279 {
280 while (ConnectionState == ConnectionState.Connected)
281 {
282 lock (Behaviours)
283 {
284 foreach (IBehaviour behaviour in Behaviours.Values)
285 {
286 // Thread.Sleep(Random.Next(3000, 10000));
287  
288 // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType());
289 behaviour.Action();
290 }
291 }
292  
293 // XXX: This is a really shitty way of yielding so that behaviours can be added/removed
294 Thread.Sleep(100);
295 }
296  
297 lock (Behaviours)
298 foreach (IBehaviour b in Behaviours.Values)
299 b.Close();
300 }
301  
302 /// <summary>
303 /// Tells LibSecondLife to logout and disconnect. Raises the disconnect events once it finishes.
304 /// </summary>
305 public void Disconnect()
306 {
307 ConnectionState = ConnectionState.Disconnecting;
308  
309 // if (m_actionThread != null)
310 // m_actionThread.Abort();
311  
312 Client.Network.Logout();
313 }
314  
315 public void Connect()
316 {
317 Thread connectThread = new Thread(ConnectInternal);
318 connectThread.Name = Name;
319 connectThread.IsBackground = true;
320  
321 connectThread.Start();
322 }
323  
324 /// <summary>
325 /// This is the bot startup loop.
326 /// </summary>
327 private void ConnectInternal()
328 {
329 ConnectionState = ConnectionState.Connecting;
330  
331 // Current create a new client on each connect. libomv doesn't seem to process new sim
332 // information (e.g. EstablishAgentCommunication events) if connecting after a disceonnect with the same
333 // client
334 CreateLibOmvClient();
335  
336 if (Client.Network.Login(FirstName, LastName, Password, "pCampBot", StartLocation, "Your name"))
337 {
338 ConnectionState = ConnectionState.Connected;
339  
340 Thread.Sleep(Random.Next(1000, 10000));
341 m_actionThread = new Thread(Action);
342 m_actionThread.Start();
343  
344 // OnConnected(this, EventType.CONNECTED);
345 if (wear == "save")
346 {
347 Client.Appearance.SetPreviousAppearance();
348 SaveDefaultAppearance();
349 }
350 else if (wear != "no")
351 {
352 MakeDefaultAppearance(wear);
353 }
354  
355 // Extract nearby region information.
356 Client.Grid.GridRegion += Manager.Grid_GridRegion;
357 uint xUint, yUint;
358 Utils.LongToUInts(Client.Network.CurrentSim.Handle, out xUint, out yUint);
359 ushort minX, minY, maxX, maxY;
360 minX = (ushort)Math.Min(0, xUint - 5);
361 minY = (ushort)Math.Min(0, yUint - 5);
362 maxX = (ushort)(xUint + 5);
363 maxY = (ushort)(yUint + 5);
364 Client.Grid.RequestMapBlocks(GridLayerType.Terrain, minX, minY, maxX, maxY, false);
365 }
366 else
367 {
368 ConnectionState = ConnectionState.Disconnected;
369  
370 m_log.ErrorFormat(
371 "{0} {1} cannot login: {2}", FirstName, LastName, Client.Network.LoginMessage);
372  
373 if (OnDisconnected != null)
374 {
375 OnDisconnected(this, EventType.DISCONNECTED);
376 }
377 }
378 }
379  
380 /// <summary>
381 /// Sit this bot on the ground.
382 /// </summary>
383 public void SitOnGround()
384 {
385 if (ConnectionState == ConnectionState.Connected)
386 Client.Self.SitOnGround();
387 }
388  
389 /// <summary>
390 /// Stand this bot
391 /// </summary>
392 public void Stand()
393 {
394 if (ConnectionState == ConnectionState.Connected)
395 {
396 // Unlike sit on ground, here libomv checks whether we have SEND_AGENT_UPDATES enabled.
397 bool prevUpdatesSetting = Client.Settings.SEND_AGENT_UPDATES;
398 Client.Settings.SEND_AGENT_UPDATES = true;
399 Client.Self.Stand();
400 Client.Settings.SEND_AGENT_UPDATES = prevUpdatesSetting;
401 }
402 }
403  
404 public void SaveDefaultAppearance()
405 {
406 saveDir = "MyAppearance/" + FirstName + "_" + LastName;
407 if (!Directory.Exists(saveDir))
408 {
409 Directory.CreateDirectory(saveDir);
410 }
411  
412 Array wtypes = Enum.GetValues(typeof(WearableType));
413 foreach (WearableType wtype in wtypes)
414 {
415 UUID wearable = Client.Appearance.GetWearableAsset(wtype);
416 if (wearable != UUID.Zero)
417 {
418 Client.Assets.RequestAsset(wearable, AssetType.Clothing, false, Asset_ReceivedCallback);
419 Client.Assets.RequestAsset(wearable, AssetType.Bodypart, false, Asset_ReceivedCallback);
420 }
421 }
422 }
423  
424 public void SaveAsset(AssetWearable asset)
425 {
426 if (asset != null)
427 {
428 try
429 {
430 if (asset.Decode())
431 {
432 File.WriteAllBytes(Path.Combine(saveDir, String.Format("{1}.{0}",
433 asset.AssetType.ToString().ToLower(),
434 asset.WearableType)), asset.AssetData);
435 }
436 else
437 {
438 m_log.WarnFormat("Failed to decode {0} asset {1}", asset.AssetType, asset.AssetID);
439 }
440 }
441 catch (Exception e)
442 {
443 m_log.ErrorFormat("Exception: {0}{1}", e.Message, e.StackTrace);
444 }
445 }
446 }
447  
448 public WearableType GetWearableType(string path)
449 {
450 string type = ((((path.Split('/'))[2]).Split('.'))[0]).Trim();
451 switch (type)
452 {
453 case "Eyes":
454 return WearableType.Eyes;
455 case "Hair":
456 return WearableType.Hair;
457 case "Pants":
458 return WearableType.Pants;
459 case "Shape":
460 return WearableType.Shape;
461 case "Shirt":
462 return WearableType.Shirt;
463 case "Skin":
464 return WearableType.Skin;
465 default:
466 return WearableType.Shape;
467 }
468 }
469  
470 public void MakeDefaultAppearance(string wear)
471 {
472 try
473 {
474 if (wear == "yes")
475 {
476 //TODO: Implement random outfit picking
477 m_log.DebugFormat("Picks a random outfit. Not yet implemented.");
478 }
479 else if (wear != "save")
480 saveDir = "MyAppearance/" + wear;
481 saveDir = saveDir + "/";
482  
483 string[] clothing = Directory.GetFiles(saveDir, "*.clothing", SearchOption.TopDirectoryOnly);
484 string[] bodyparts = Directory.GetFiles(saveDir, "*.bodypart", SearchOption.TopDirectoryOnly);
485 InventoryFolder clothfolder = FindClothingFolder();
486 UUID transid = UUID.Random();
487 List<InventoryBase> listwearables = new List<InventoryBase>();
488  
489 for (int i = 0; i < clothing.Length; i++)
490 {
491 UUID assetID = UUID.Random();
492 AssetClothing asset = new AssetClothing(assetID, File.ReadAllBytes(clothing[i]));
493 asset.Decode();
494 asset.Owner = Client.Self.AgentID;
495 asset.WearableType = GetWearableType(clothing[i]);
496 asset.Encode();
497 transid = Client.Assets.RequestUpload(asset,true);
498 Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyClothing" + i.ToString(), "MyClothing", AssetType.Clothing,
499 transid, InventoryType.Wearable, asset.WearableType, (OpenMetaverse.PermissionMask)PermissionMask.All, delegate(bool success, InventoryItem item)
500 {
501 if (success)
502 {
503 listwearables.Add(item);
504 }
505 else
506 {
507 m_log.WarnFormat("Failed to create item {0}", item.Name);
508 }
509 }
510 );
511 }
512  
513 for (int i = 0; i < bodyparts.Length; i++)
514 {
515 UUID assetID = UUID.Random();
516 AssetBodypart asset = new AssetBodypart(assetID, File.ReadAllBytes(bodyparts[i]));
517 asset.Decode();
518 asset.Owner = Client.Self.AgentID;
519 asset.WearableType = GetWearableType(bodyparts[i]);
520 asset.Encode();
521 transid = Client.Assets.RequestUpload(asset,true);
522 Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyBodyPart" + i.ToString(), "MyBodyPart", AssetType.Bodypart,
523 transid, InventoryType.Wearable, asset.WearableType, (OpenMetaverse.PermissionMask)PermissionMask.All, delegate(bool success, InventoryItem item)
524 {
525 if (success)
526 {
527 listwearables.Add(item);
528 }
529 else
530 {
531 m_log.WarnFormat("Failed to create item {0}", item.Name);
532 }
533 }
534 );
535 }
536  
537 Thread.Sleep(1000);
538  
539 if (listwearables == null || listwearables.Count == 0)
540 {
541 m_log.DebugFormat("Nothing to send on this folder!");
542 }
543 else
544 {
545 m_log.DebugFormat("Sending {0} wearables...", listwearables.Count);
546 Client.Appearance.WearOutfit(listwearables, false);
547 }
548 }
549 catch (Exception ex)
550 {
551 Console.WriteLine(ex.ToString());
552 }
553 }
554  
555 public InventoryFolder FindClothingFolder()
556 {
557 UUID rootfolder = Client.Inventory.Store.RootFolder.UUID;
558 List<InventoryBase> listfolders = Client.Inventory.Store.GetContents(rootfolder);
559 InventoryFolder clothfolder = new InventoryFolder(UUID.Random());
560 foreach (InventoryBase folder in listfolders)
561 {
562 if (folder.Name == "Clothing")
563 {
564 clothfolder = (InventoryFolder)folder;
565 break;
566 }
567 }
568 return clothfolder;
569 }
570  
571 public void Network_LoginProgress(object sender, LoginProgressEventArgs args)
572 {
573 m_log.DebugFormat("[BOT]: Bot {0} {1} in Network_LoginProcess", Name, args.Status);
574  
575 if (args.Status == LoginStatus.Success)
576 {
577 if (OnConnected != null)
578 {
579 OnConnected(this, EventType.CONNECTED);
580 }
581 }
582 }
583  
584 public void Network_SimConnected(object sender, SimConnectedEventArgs args)
585 {
586 m_log.DebugFormat(
587 "[BOT]: Bot {0} connected to region {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint);
588 }
589  
590 public void Network_SimDisconnected(object sender, SimDisconnectedEventArgs args)
591 {
592 m_log.DebugFormat(
593 "[BOT]: Bot {0} disconnected from region {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint);
594 }
595  
596 public void Network_OnDisconnected(object sender, DisconnectedEventArgs args)
597 {
598 ConnectionState = ConnectionState.Disconnected;
599  
600 m_log.DebugFormat(
601 "[BOT]: Bot {0} disconnected from grid, reason {1}, message {2}", Name, args.Reason, args.Message);
602  
603 // m_log.ErrorFormat("Fired Network_OnDisconnected");
604  
605 // if (
606 // (args.Reason == NetworkManager.DisconnectType.SimShutdown
607 // || args.Reason == NetworkManager.DisconnectType.NetworkTimeout)
608 // && OnDisconnected != null)
609  
610  
611  
612 if (
613 (args.Reason == NetworkManager.DisconnectType.ClientInitiated
614 || args.Reason == NetworkManager.DisconnectType.ServerInitiated
615 || args.Reason == NetworkManager.DisconnectType.NetworkTimeout)
616 && OnDisconnected != null)
617 // if (OnDisconnected != null)
618 {
619 OnDisconnected(this, EventType.DISCONNECTED);
620 }
621 }
622  
623 public void Objects_NewPrim(object sender, PrimEventArgs args)
624 {
625 if (!RequestObjectTextures)
626 return;
627  
628 Primitive prim = args.Prim;
629  
630 if (prim != null)
631 {
632 lock (m_objects)
633 m_objects[prim.ID] = prim;
634  
635 if (prim.Textures != null)
636 {
637 if (prim.Textures.DefaultTexture.TextureID != UUID.Zero)
638 {
639 GetTexture(prim.Textures.DefaultTexture.TextureID);
640 }
641  
642 for (int i = 0; i < prim.Textures.FaceTextures.Length; i++)
643 {
644 Primitive.TextureEntryFace face = prim.Textures.FaceTextures[i];
645  
646 if (face != null)
647 {
648 UUID textureID = prim.Textures.FaceTextures[i].TextureID;
649  
650 if (textureID != UUID.Zero)
651 GetTexture(textureID);
652 }
653 }
654 }
655  
656 if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)
657 GetTexture(prim.Sculpt.SculptTexture);
658 }
659 }
660  
661 private void GetTexture(UUID textureID)
662 {
663 lock (Manager.AssetsReceived)
664 {
665 // Don't request assets more than once.
666 if (Manager.AssetsReceived.ContainsKey(textureID))
667 return;
668  
669 Manager.AssetsReceived[textureID] = false;
670 Client.Assets.RequestImage(textureID, ImageType.Normal, Asset_TextureCallback_Texture);
671 }
672 }
673  
674 public void Asset_TextureCallback_Texture(TextureRequestState state, AssetTexture assetTexture)
675 {
676 //TODO: Implement texture saving and applying
677 }
678  
679 public void Asset_ReceivedCallback(AssetDownload transfer, Asset asset)
680 {
681 lock (Manager.AssetsReceived)
682 Manager.AssetsReceived[asset.AssetID] = true;
683  
684 // if (wear == "save")
685 // {
686 // SaveAsset((AssetWearable) asset);
687 // }
688 }
689 }
690 }