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