corrade-vassal – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 1 | using System; |
2 | using System.Collections.Generic; |
||
3 | using System.Reflection; |
||
4 | using System.Xml; |
||
5 | using System.Threading; |
||
6 | using OpenMetaverse; |
||
7 | using OpenMetaverse.Packets; |
||
8 | |||
9 | namespace OpenMetaverse.TestClient |
||
10 | { |
||
11 | public class LoginDetails |
||
12 | { |
||
13 | public string FirstName; |
||
14 | public string LastName; |
||
15 | public string Password; |
||
16 | public string StartLocation; |
||
17 | public bool GroupCommands; |
||
18 | public string MasterName; |
||
19 | public UUID MasterKey; |
||
20 | public string URI; |
||
21 | } |
||
22 | |||
23 | public class StartPosition |
||
24 | { |
||
25 | public string sim; |
||
26 | public int x; |
||
27 | public int y; |
||
28 | public int z; |
||
29 | |||
30 | public StartPosition() |
||
31 | { |
||
32 | this.sim = null; |
||
33 | this.x = 0; |
||
34 | this.y = 0; |
||
35 | this.z = 0; |
||
36 | } |
||
37 | } |
||
38 | |||
39 | public sealed class ClientManager |
||
40 | { |
||
41 | const string VERSION = "1.0.0"; |
||
42 | |||
43 | class Singleton { internal static readonly ClientManager Instance = new ClientManager(); } |
||
44 | public static ClientManager Instance { get { return Singleton.Instance; } } |
||
45 | |||
46 | public Dictionary<UUID, TestClient> Clients = new Dictionary<UUID, TestClient>(); |
||
47 | public Dictionary<Simulator, Dictionary<uint, Primitive>> SimPrims = new Dictionary<Simulator, Dictionary<uint, Primitive>>(); |
||
48 | |||
49 | public bool Running = true; |
||
50 | public bool GetTextures = false; |
||
51 | public volatile int PendingLogins = 0; |
||
52 | public string onlyAvatar = String.Empty; |
||
53 | |||
54 | ClientManager() |
||
55 | { |
||
56 | } |
||
57 | |||
58 | public void Start(List<LoginDetails> accounts, bool getTextures) |
||
59 | { |
||
60 | GetTextures = getTextures; |
||
61 | |||
62 | foreach (LoginDetails account in accounts) |
||
63 | Login(account); |
||
64 | } |
||
65 | |||
66 | public TestClient Login(string[] args) |
||
67 | { |
||
68 | if (args.Length < 3) |
||
69 | { |
||
70 | Console.WriteLine("Usage: login firstname lastname password [simname] [login server url]"); |
||
71 | return null; |
||
72 | } |
||
73 | LoginDetails account = new LoginDetails(); |
||
74 | account.FirstName = args[0]; |
||
75 | account.LastName = args[1]; |
||
76 | account.Password = args[2]; |
||
77 | |||
78 | if (args.Length > 3) |
||
79 | { |
||
80 | // If it looks like a full starting position was specified, parse it |
||
81 | if (args[3].StartsWith("http")) |
||
82 | { |
||
83 | account.URI = args[3]; |
||
84 | } |
||
85 | else |
||
86 | { |
||
87 | if (args[3].IndexOf('/') >= 0) |
||
88 | { |
||
89 | char sep = '/'; |
||
90 | string[] startbits = args[3].Split(sep); |
||
91 | try |
||
92 | { |
||
93 | account.StartLocation = NetworkManager.StartLocation(startbits[0], Int32.Parse(startbits[1]), |
||
94 | Int32.Parse(startbits[2]), Int32.Parse(startbits[3])); |
||
95 | } |
||
96 | catch (FormatException) { } |
||
97 | } |
||
98 | |||
99 | // Otherwise, use the center of the named region |
||
100 | if (account.StartLocation == null) |
||
101 | account.StartLocation = NetworkManager.StartLocation(args[3], 128, 128, 40); |
||
102 | } |
||
103 | } |
||
104 | |||
105 | if (args.Length > 4) |
||
106 | if (args[4].StartsWith("http")) |
||
107 | account.URI = args[4]; |
||
108 | |||
109 | if (string.IsNullOrEmpty(account.URI)) |
||
110 | account.URI = Program.LoginURI; |
||
111 | Logger.Log("Using login URI " + account.URI, Helpers.LogLevel.Info); |
||
112 | |||
113 | return Login(account); |
||
114 | } |
||
115 | |||
116 | public TestClient Login(LoginDetails account) |
||
117 | { |
||
118 | // Check if this client is already logged in |
||
119 | foreach (TestClient c in Clients.Values) |
||
120 | { |
||
121 | if (c.Self.FirstName == account.FirstName && c.Self.LastName == account.LastName) |
||
122 | { |
||
123 | Logout(c); |
||
124 | break; |
||
125 | } |
||
126 | } |
||
127 | |||
128 | ++PendingLogins; |
||
129 | |||
130 | TestClient client = new TestClient(this); |
||
131 | client.Network.LoginProgress += |
||
132 | delegate(object sender, LoginProgressEventArgs e) |
||
133 | { |
||
134 | Logger.Log(String.Format("Login {0}: {1}", e.Status, e.Message), Helpers.LogLevel.Info, client); |
||
135 | |||
136 | if (e.Status == LoginStatus.Success) |
||
137 | { |
||
138 | Clients[client.Self.AgentID] = client; |
||
139 | |||
140 | if (client.MasterKey == UUID.Zero) |
||
141 | { |
||
142 | UUID query = UUID.Zero; |
||
143 | EventHandler<DirPeopleReplyEventArgs> peopleDirCallback = |
||
144 | delegate(object sender2, DirPeopleReplyEventArgs dpe) |
||
145 | { |
||
146 | if (dpe.QueryID == query) |
||
147 | { |
||
148 | if (dpe.MatchedPeople.Count != 1) |
||
149 | { |
||
150 | Logger.Log("Unable to resolve master key from " + client.MasterName, Helpers.LogLevel.Warning); |
||
151 | } |
||
152 | else |
||
153 | { |
||
154 | client.MasterKey = dpe.MatchedPeople[0].AgentID; |
||
155 | Logger.Log("Master key resolved to " + client.MasterKey, Helpers.LogLevel.Info); |
||
156 | } |
||
157 | } |
||
158 | }; |
||
159 | |||
160 | client.Directory.DirPeopleReply += peopleDirCallback; |
||
161 | query = client.Directory.StartPeopleSearch(client.MasterName, 0); |
||
162 | } |
||
163 | |||
164 | Logger.Log("Logged in " + client.ToString(), Helpers.LogLevel.Info); |
||
165 | --PendingLogins; |
||
166 | } |
||
167 | else if (e.Status == LoginStatus.Failed) |
||
168 | { |
||
169 | Logger.Log("Failed to login " + account.FirstName + " " + account.LastName + ": " + |
||
170 | client.Network.LoginMessage, Helpers.LogLevel.Warning); |
||
171 | --PendingLogins; |
||
172 | } |
||
173 | }; |
||
174 | |||
175 | // Optimize the throttle |
||
176 | client.Throttle.Wind = 0; |
||
177 | client.Throttle.Cloud = 0; |
||
178 | client.Throttle.Land = 1000000; |
||
179 | client.Throttle.Task = 1000000; |
||
180 | |||
181 | client.GroupCommands = account.GroupCommands; |
||
182 | client.MasterName = account.MasterName; |
||
183 | client.MasterKey = account.MasterKey; |
||
184 | client.AllowObjectMaster = client.MasterKey != UUID.Zero; // Require UUID for object master. |
||
185 | |||
186 | LoginParams loginParams = client.Network.DefaultLoginParams( |
||
187 | account.FirstName, account.LastName, account.Password, "TestClient", VERSION); |
||
188 | |||
189 | if (!String.IsNullOrEmpty(account.StartLocation)) |
||
190 | loginParams.Start = account.StartLocation; |
||
191 | |||
192 | if (!String.IsNullOrEmpty(account.URI)) |
||
193 | loginParams.URI = account.URI; |
||
194 | |||
195 | client.Network.BeginLogin(loginParams); |
||
196 | return client; |
||
197 | } |
||
198 | |||
199 | /// <summary> |
||
200 | /// |
||
201 | /// </summary> |
||
202 | public void Run(bool noGUI) |
||
203 | { |
||
204 | if (noGUI) |
||
205 | { |
||
206 | while (Running) |
||
207 | { |
||
208 | Thread.Sleep(2 * 1000); |
||
209 | } |
||
210 | } |
||
211 | else { |
||
212 | Console.WriteLine("Type quit to exit. Type help for a command list."); |
||
213 | |||
214 | while (Running) |
||
215 | { |
||
216 | PrintPrompt(); |
||
217 | string input = Console.ReadLine(); |
||
218 | DoCommandAll(input, UUID.Zero); |
||
219 | } |
||
220 | } |
||
221 | |||
222 | foreach (GridClient client in Clients.Values) |
||
223 | { |
||
224 | if (client.Network.Connected) |
||
225 | client.Network.Logout(); |
||
226 | } |
||
227 | } |
||
228 | |||
229 | private void PrintPrompt() |
||
230 | { |
||
231 | int online = 0; |
||
232 | |||
233 | foreach (GridClient client in Clients.Values) |
||
234 | { |
||
235 | if (client.Network.Connected) online++; |
||
236 | } |
||
237 | |||
238 | Console.Write(online + " avatars online> "); |
||
239 | } |
||
240 | |||
241 | /// <summary> |
||
242 | /// |
||
243 | /// </summary> |
||
244 | /// <param name="cmd"></param> |
||
245 | /// <param name="fromAgentID"></param> |
||
246 | /// <param name="imSessionID"></param> |
||
247 | public void DoCommandAll(string cmd, UUID fromAgentID) |
||
248 | { |
||
249 | string[] tokens = cmd.Trim().Split(new char[] { ' ', '\t' }); |
||
250 | if (tokens.Length == 0) |
||
251 | return; |
||
252 | |||
253 | string firstToken = tokens[0].ToLower(); |
||
254 | if (String.IsNullOrEmpty(firstToken)) |
||
255 | return; |
||
256 | |||
257 | // Allow for comments when cmdline begins with ';' or '#' |
||
258 | if (firstToken[0] == ';' || firstToken[0] == '#') |
||
259 | return; |
||
260 | |||
261 | if ('@' == firstToken[0]) { |
||
262 | onlyAvatar = String.Empty; |
||
263 | if (tokens.Length == 3) { |
||
264 | bool found = false; |
||
265 | onlyAvatar = tokens[1]+" "+tokens[2]; |
||
266 | foreach (TestClient client in Clients.Values) { |
||
267 | if ((client.ToString() == onlyAvatar) && (client.Network.Connected)) { |
||
268 | found = true; |
||
269 | break; |
||
270 | } |
||
271 | } |
||
272 | if (found) { |
||
273 | Logger.Log("Commanding only "+onlyAvatar+" now", Helpers.LogLevel.Info); |
||
274 | } else { |
||
275 | Logger.Log("Commanding nobody now. Avatar "+onlyAvatar+" is offline", Helpers.LogLevel.Info); |
||
276 | } |
||
277 | } else { |
||
278 | Logger.Log("Commanding all avatars now", Helpers.LogLevel.Info); |
||
279 | } |
||
280 | return; |
||
281 | } |
||
282 | |||
283 | string[] args = new string[tokens.Length - 1]; |
||
284 | if (args.Length > 0) |
||
285 | Array.Copy(tokens, 1, args, 0, args.Length); |
||
286 | |||
287 | if (firstToken == "login") |
||
288 | { |
||
289 | Login(args); |
||
290 | } |
||
291 | else if (firstToken == "quit") |
||
292 | { |
||
293 | Quit(); |
||
294 | Logger.Log("All clients logged out and program finished running.", Helpers.LogLevel.Info); |
||
295 | } |
||
296 | else if (firstToken == "help") |
||
297 | { |
||
298 | if (Clients.Count > 0) |
||
299 | { |
||
300 | foreach (TestClient client in Clients.Values) |
||
301 | { |
||
302 | Console.WriteLine(client.Commands["help"].Execute(args, UUID.Zero)); |
||
303 | break; |
||
304 | } |
||
305 | } |
||
306 | else |
||
307 | { |
||
308 | Console.WriteLine("You must login at least one bot to use the help command"); |
||
309 | } |
||
310 | } |
||
311 | else if (firstToken == "script") |
||
312 | { |
||
313 | // No reason to pass this to all bots, and we also want to allow it when there are no bots |
||
314 | ScriptCommand command = new ScriptCommand(null); |
||
315 | Logger.Log(command.Execute(args, UUID.Zero), Helpers.LogLevel.Info); |
||
316 | } |
||
317 | else if (firstToken == "waitforlogin") |
||
318 | { |
||
319 | // Special exception to allow this to run before any bots have logged in |
||
320 | if (ClientManager.Instance.PendingLogins > 0) |
||
321 | { |
||
322 | WaitForLoginCommand command = new WaitForLoginCommand(null); |
||
323 | Logger.Log(command.Execute(args, UUID.Zero), Helpers.LogLevel.Info); |
||
324 | } |
||
325 | else |
||
326 | { |
||
327 | Logger.Log("No pending logins", Helpers.LogLevel.Info); |
||
328 | } |
||
329 | } |
||
330 | else |
||
331 | { |
||
332 | // Make an immutable copy of the Clients dictionary to safely iterate over |
||
333 | Dictionary<UUID, TestClient> clientsCopy = new Dictionary<UUID, TestClient>(Clients); |
||
334 | |||
335 | int completed = 0; |
||
336 | |||
337 | foreach (TestClient client in clientsCopy.Values) |
||
338 | { |
||
339 | ThreadPool.QueueUserWorkItem((WaitCallback) |
||
340 | delegate(object state) |
||
341 | { |
||
342 | TestClient testClient = (TestClient)state; |
||
343 | if ((String.Empty == onlyAvatar) || (testClient.ToString() == onlyAvatar)) { |
||
344 | if (testClient.Commands.ContainsKey(firstToken)) { |
||
345 | string result; |
||
346 | try { |
||
347 | result = testClient.Commands[firstToken].Execute(args, fromAgentID); |
||
348 | Logger.Log(result, Helpers.LogLevel.Info, testClient); |
||
349 | } catch(Exception e) { |
||
350 | Logger.Log(String.Format("{0} raised exception {1}", firstToken, e), |
||
351 | Helpers.LogLevel.Error, |
||
352 | testClient); |
||
353 | } |
||
354 | } else |
||
355 | Logger.Log("Unknown command " + firstToken, Helpers.LogLevel.Warning); |
||
356 | } |
||
357 | |||
358 | ++completed; |
||
359 | }, |
||
360 | client); |
||
361 | } |
||
362 | |||
363 | while (completed < clientsCopy.Count) |
||
364 | Thread.Sleep(50); |
||
365 | } |
||
366 | } |
||
367 | |||
368 | /// <summary> |
||
369 | /// |
||
370 | /// </summary> |
||
371 | /// <param name="client"></param> |
||
372 | public void Logout(TestClient client) |
||
373 | { |
||
374 | Clients.Remove(client.Self.AgentID); |
||
375 | client.Network.Logout(); |
||
376 | } |
||
377 | |||
378 | /// <summary> |
||
379 | /// |
||
380 | /// </summary> |
||
381 | public void Quit() |
||
382 | { |
||
383 | Running = false; |
||
384 | // TODO: It would be really nice if we could figure out a way to abort the ReadLine here in so that Run() will exit. |
||
385 | } |
||
386 | } |
||
387 | } |