corrade-vassal – Blame information for rev 4

Subversion Repositories:
Rev:
Rev Author Line No. Line
2 zed 1 ///////////////////////////////////////////////////////////////////////////
2 // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
3 // Please see: http://www.gnu.org/licenses/gpl.html for legal details, //
4 // rights of fair usage, the disclaimer and warranty conditions. //
5 ///////////////////////////////////////////////////////////////////////////
6  
7 using System;
8 using System.Collections;
9 using System.Collections.Generic;
10 using System.ComponentModel;
11 using System.Data;
12 using System.Drawing;
13 using System.IO;
14 using System.Linq;
15 using System.Net;
16 using System.Reflection;
17 using System.Text;
18 using System.Text.RegularExpressions;
19 using System.Timers;
20 using System.Threading;
21 using System.Web;
22 using System.Windows.Forms;
23 using OpenMetaverse;
24 using Parallel = System.Threading.Tasks.Parallel;
25 using Timer = System.Timers.Timer;
26  
27 namespace Vassal
28 {
29 public partial class Vassal : Form
30 {
31 public static System.Timers.Timer overviewTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
32 public static System.Timers.Timer topScriptsTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
33 public static System.Timers.Timer topCollidersTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
34 public static VassalConfiguration vassalConfiguration = new VassalConfiguration();
35 public static Vassal vassalForm;
36 public static readonly object ClientInstanceTeleportLock = new object();
37  
38 ///////////////////////////////////////////////////////////////////////////
39 // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
40 ///////////////////////////////////////////////////////////////////////////
41 /// <summary>RFC1738 URL Escapes a string</summary>
42 /// <param name="data">a string to escape</param>
43 /// <returns>an RFC1738 escaped string</returns>
44 private static string wasURLEscapeDataString(string data)
45 {
46 return HttpUtility.UrlEncode(data);
47 }
48  
49 ///////////////////////////////////////////////////////////////////////////
50 // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
51 ///////////////////////////////////////////////////////////////////////////
52 /// <summary>RFC1738 URL Unescape a string</summary>
53 /// <param name="data">a string to unescape</param>
54 /// <returns>an RFC1738 unescaped string</returns>
55 private static string wasURLUnescapeDataString(string data)
56 {
57 return HttpUtility.UrlDecode(data);
58 }
59  
60 ///////////////////////////////////////////////////////////////////////////
61 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
62 ///////////////////////////////////////////////////////////////////////////
63 /// <summary>URI unescapes an RFC3986 URI escaped string</summary>
64 /// <param name="data">a string to unescape</param>
65 /// <returns>the resulting string</returns>
66 private static string wasURIUnescapeDataString(string data)
67 {
68 // Uri.UnescapeDataString can only handle 32766 characters at a time
69 return string.Join("", Enumerable.Range(0, (data.Length + 32765)/32766)
70 .Select(o => Uri.UnescapeDataString(data.Substring(o*32766, Math.Min(32766, data.Length - (o*32766)))))
71 .ToArray());
72 }
73  
74 ///////////////////////////////////////////////////////////////////////////
75 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
76 ///////////////////////////////////////////////////////////////////////////
77 /// <summary>RFC3986 URI Escapes a string</summary>
78 /// <param name="data">a string to escape</param>
79 /// <returns>an RFC3986 escaped string</returns>
80 private static string wasURIEscapeDataString(string data)
81 {
82 // Uri.EscapeDataString can only handle 32766 characters at a time
83 return string.Join("", Enumerable.Range(0, (data.Length + 32765)/32766)
84 .Select(o => Uri.EscapeDataString(data.Substring(o*32766, Math.Min(32766, data.Length - (o*32766)))))
85 .ToArray());
86 }
87  
88 ///////////////////////////////////////////////////////////////////////////
89 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
90 ///////////////////////////////////////////////////////////////////////////
91 /// <summary>
92 /// Gets an array element at a given modulo index.
93 /// </summary>
94 /// <typeparam name="T">the array type</typeparam>
95 /// <param name="index">a positive or negative index of the element</param>
96 /// <param name="data">the array</param>
97 /// <return>an array element</return>
98 public static T wasGetElementAt<T>(T[] data, int index)
99 {
100 switch (index < 0)
101 {
102 case true:
103 return data[((index%data.Length) + data.Length)%data.Length];
104 default:
105 return data[index%data.Length];
106 }
107 }
108  
109 #region KEY-VALUE DATA
110  
111 ///////////////////////////////////////////////////////////////////////////
112 // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
113 ///////////////////////////////////////////////////////////////////////////
114 /// <summary>
115 /// Returns the value of a key from a key-value data string.
116 /// </summary>
117 /// <param name="key">the key of the value</param>
118 /// <param name="data">the key-value data segment</param>
119 /// <returns>true if the key was found in data</returns>
120 private static string wasKeyValueGet(string key, string data)
121 {
122 return data.Split('&')
123 .AsParallel()
124 .Select(o => o.Split('=').ToList())
125 .Where(o => o.Count.Equals(2))
126 .Select(o => new
127 {
128 k = o.First(),
129 v = o.Last()
130 })
131 .Where(o => o.k.Equals(key))
132 .Select(o => o.v)
133 .FirstOrDefault();
134 }
135  
3 eva 136 #endregion
137  
2 zed 138 #region CRYPTOGRAPHY
139  
140 ///////////////////////////////////////////////////////////////////////////
141 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
142 ///////////////////////////////////////////////////////////////////////////
143 /// <summary>
144 /// Gets a sub-array from an array.
145 /// </summary>
146 /// <typeparam name="T">the array type</typeparam>
147 /// <param name="data">the array</param>
148 /// <param name="start">the start index</param>
149 /// <param name="stop">the stop index (-1 denotes the end)</param>
150 /// <returns>the array slice between start and stop</returns>
151 public static T[] wasGetSubArray<T>(T[] data, int start, int stop)
152 {
153 if (stop.Equals(-1))
154 stop = data.Length - 1;
155 T[] result = new T[stop - start + 1];
156 Array.Copy(data, start, result, 0, stop - start + 1);
157 return result;
158 }
159  
160 ///////////////////////////////////////////////////////////////////////////
161 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
162 ///////////////////////////////////////////////////////////////////////////
163 /// <summary>
164 /// Delete a sub-array and return the result.
165 /// </summary>
166 /// <typeparam name="T">the array type</typeparam>
167 /// <param name="data">the array</param>
168 /// <param name="start">the start index</param>
169 /// <param name="stop">the stop index (-1 denotes the end)</param>
170 /// <returns>the array without elements between start and stop</returns>
171 public static T[] wasDeleteSubArray<T>(T[] data, int start, int stop)
172 {
173 if (stop.Equals(-1))
174 stop = data.Length - 1;
175 T[] result = new T[data.Length - (stop - start) - 1];
176 Array.Copy(data, 0, result, 0, start);
177 Array.Copy(data, stop + 1, result, start, data.Length - stop - 1);
178 return result;
179 }
180  
181 ///////////////////////////////////////////////////////////////////////////
182 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
183 ///////////////////////////////////////////////////////////////////////////
184 /// <summary>
185 /// Concatenate multiple arrays.
186 /// </summary>
187 /// <typeparam name="T">the array type</typeparam>
188 /// <param name="arrays">multiple arrays</param>
189 /// <returns>a flat array with all arrays concatenated</returns>
190 public static T[] wasConcatenateArrays<T>(params T[][] arrays)
191 {
192 int resultLength = 0;
193 foreach (T[] o in arrays)
194 {
195 resultLength += o.Length;
196 }
197 T[] result = new T[resultLength];
198 int offset = 0;
199 for (int x = 0; x < arrays.Length; x++)
200 {
201 arrays[x].CopyTo(result, offset);
202 offset += arrays[x].Length;
203 }
204 return result;
205 }
206  
207 ///////////////////////////////////////////////////////////////////////////
208 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
209 ///////////////////////////////////////////////////////////////////////////
210 /// <summary>
211 /// Permutes an array in reverse a given number of times.
212 /// </summary>
213 /// <typeparam name="T">the array type</typeparam>
214 /// <param name="input">the array</param>
215 /// <param name="times">the number of times to permute</param>
216 /// <returns>the array with the elements permuted</returns>
217 private static T[] wasReversePermuteArrayElements<T>(T[] input, int times)
218 {
219 if (times.Equals(0)) return input;
220 T[] slice = new T[input.Length];
221 Array.Copy(input, 1, slice, 0, input.Length - 1);
222 Array.Copy(input, 0, slice, input.Length - 1, 1);
223 return wasReversePermuteArrayElements(slice, --times);
224 }
225  
226 ///////////////////////////////////////////////////////////////////////////
227 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
228 ///////////////////////////////////////////////////////////////////////////
229 /// <summary>
230 /// Permutes an array forward a given number of times.
231 /// </summary>
232 /// <typeparam name="T">the array type</typeparam>
233 /// <param name="input">the array</param>
234 /// <param name="times">the number of times to permute</param>
235 /// <returns>the array with the elements permuted</returns>
236 private static T[] wasForwardPermuteArrayElements<T>(T[] input, int times)
237 {
238 if (times.Equals(0)) return input;
239 T[] slice = new T[input.Length];
240 Array.Copy(input, input.Length - 1, slice, 0, 1);
241 Array.Copy(input, 0, slice, 1, input.Length - 1);
242 return wasForwardPermuteArrayElements(slice, --times);
243 }
244  
245 ///////////////////////////////////////////////////////////////////////////
246 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
247 ///////////////////////////////////////////////////////////////////////////
248 /// <summary>
249 /// Encrypt or decrypt a message given a set of rotors, plugs and a reflector.
250 /// </summary>
251 /// <param name="message">the message to encyrpt or decrypt</param>
252 /// <param name="rotors">any combination of: 1, 2, 3, 4, 5, 6, 7, 8, b, g</param>
253 /// <param name="plugs">the letter representing the start character for the rotor</param>
254 /// <param name="reflector">any one of: B, b, C, c</param>
255 /// <returns>either a decrypted or encrypted string</returns>
256 private static string wasEnigma(string message, char[] rotors, char[] plugs, char reflector)
257 {
258 Dictionary<char, char[]> def_rotors = new Dictionary<char, char[]>
259 {
260 {
261 '1', new[]
262 {
263 'e', 'k', 'm', 'f', 'l',
264 'g', 'd', 'q', 'v', 'z',
265 'n', 't', 'o', 'w', 'y',
266 'h', 'x', 'u', 's', 'p',
267 'a', 'i', 'b', 'r', 'c',
268 'j'
269 }
270 },
271 {
272 '2', new[]
273 {
274 'a', 'j', 'd', 'k', 's',
275 'i', 'r', 'u', 'x', 'b',
276 'l', 'h', 'w', 't', 'm',
277 'c', 'q', 'g', 'z', 'n',
278 'p', 'y', 'f', 'v', 'o',
279 'e'
280 }
281 },
282 {
283 '3', new[]
284 {
285 'b', 'd', 'f', 'h', 'j',
286 'l', 'c', 'p', 'r', 't',
287 'x', 'v', 'z', 'n', 'y',
288 'e', 'i', 'w', 'g', 'a',
289 'k', 'm', 'u', 's', 'q',
290 'o'
291 }
292 },
293 {
294 '4', new[]
295 {
296 'e', 's', 'o', 'v', 'p',
297 'z', 'j', 'a', 'y', 'q',
298 'u', 'i', 'r', 'h', 'x',
299 'l', 'n', 'f', 't', 'g',
300 'k', 'd', 'c', 'm', 'w',
301 'b'
302 }
303 },
304 {
305 '5', new[]
306 {
307 'v', 'z', 'b', 'r', 'g',
308 'i', 't', 'y', 'u', 'p',
309 's', 'd', 'n', 'h', 'l',
310 'x', 'a', 'w', 'm', 'j',
311 'q', 'o', 'f', 'e', 'c',
312 'k'
313 }
314 },
315 {
316 '6', new[]
317 {
318 'j', 'p', 'g', 'v', 'o',
319 'u', 'm', 'f', 'y', 'q',
320 'b', 'e', 'n', 'h', 'z',
321 'r', 'd', 'k', 'a', 's',
322 'x', 'l', 'i', 'c', 't',
323 'w'
324 }
325 },
326 {
327 '7', new[]
328 {
329 'n', 'z', 'j', 'h', 'g',
330 'r', 'c', 'x', 'm', 'y',
331 's', 'w', 'b', 'o', 'u',
332 'f', 'a', 'i', 'v', 'l',
333 'p', 'e', 'k', 'q', 'd',
334 't'
335 }
336 },
337 {
338 '8', new[]
339 {
340 'f', 'k', 'q', 'h', 't',
341 'l', 'x', 'o', 'c', 'b',
342 'j', 's', 'p', 'd', 'z',
343 'r', 'a', 'm', 'e', 'w',
344 'n', 'i', 'u', 'y', 'g',
345 'v'
346 }
347 },
348 {
349 'b', new[]
350 {
351 'l', 'e', 'y', 'j', 'v',
352 'c', 'n', 'i', 'x', 'w',
353 'p', 'b', 'q', 'm', 'd',
354 'r', 't', 'a', 'k', 'z',
355 'g', 'f', 'u', 'h', 'o',
356 's'
357 }
358 },
359 {
360 'g', new[]
361 {
362 'f', 's', 'o', 'k', 'a',
363 'n', 'u', 'e', 'r', 'h',
364 'm', 'b', 't', 'i', 'y',
365 'c', 'w', 'l', 'q', 'p',
366 'z', 'x', 'v', 'g', 'j',
367 'd'
368 }
369 }
370 };
371  
372 Dictionary<char, char[]> def_reflectors = new Dictionary<char, char[]>
373 {
374 {
375 'B', new[]
376 {
377 'a', 'y', 'b', 'r', 'c', 'u', 'd', 'h',
378 'e', 'q', 'f', 's', 'g', 'l', 'i', 'p',
379 'j', 'x', 'k', 'n', 'm', 'o', 't', 'z',
380 'v', 'w'
381 }
382 },
383 {
384 'b', new[]
385 {
386 'a', 'e', 'b', 'n', 'c', 'k', 'd', 'q',
387 'f', 'u', 'g', 'y', 'h', 'w', 'i', 'j',
388 'l', 'o', 'm', 'p', 'r', 'x', 's', 'z',
389 't', 'v'
390 }
391 },
392 {
393 'C', new[]
394 {
395 'a', 'f', 'b', 'v', 'c', 'p', 'd', 'j',
396 'e', 'i', 'g', 'o', 'h', 'y', 'k', 'r',
397 'l', 'z', 'm', 'x', 'n', 'w', 't', 'q',
398 's', 'u'
399 }
400 },
401 {
402 'c', new[]
403 {
404 'a', 'r', 'b', 'd', 'c', 'o', 'e', 'j',
405 'f', 'n', 'g', 't', 'h', 'k', 'i', 'v',
406 'l', 'm', 'p', 'w', 'q', 'z', 's', 'x',
407 'u', 'y'
408 }
409 }
410 };
411  
412 // Setup rotors from plugs.
413 foreach (char rotor in rotors)
414 {
415 char plug = plugs[Array.IndexOf(rotors, rotor)];
416 int i = Array.IndexOf(def_rotors[rotor], plug);
417 if (i.Equals(0)) continue;
418 def_rotors[rotor] = wasConcatenateArrays(new[] {plug},
419 wasGetSubArray(wasDeleteSubArray(def_rotors[rotor], i, i), i, -1),
420 wasGetSubArray(wasDeleteSubArray(def_rotors[rotor], i + 1, -1), 0, i - 1));
421 }
422  
423 StringBuilder result = new StringBuilder();
424 foreach (char c in message)
425 {
426 if (!char.IsLetter(c))
427 {
428 result.Append(c);
429 continue;
430 }
431  
432 // Normalize to lower.
433 char l = char.ToLower(c);
434  
435 Action<char[]> rotate = o =>
436 {
437 int i = o.Length - 1;
438 do
439 {
440 def_rotors[o[0]] = wasForwardPermuteArrayElements(def_rotors[o[0]], 1);
441 if (i.Equals(0))
442 {
443 rotors = wasReversePermuteArrayElements(o, 1);
444 continue;
445 }
446 l = wasGetElementAt(def_rotors[o[1]], Array.IndexOf(def_rotors[o[0]], l) - 1);
447 o = wasReversePermuteArrayElements(o, 1);
448 } while (--i > -1);
449 };
450  
451 // Forward pass through the Enigma's rotors.
452 rotate.Invoke(rotors);
453  
454 // Reflect
455 int x = Array.IndexOf(def_reflectors[reflector], l);
456 l = (x + 1)%2 == 0 ? def_reflectors[reflector][x - 1] : def_reflectors[reflector][x + 1];
457  
458 // Reverse the order of the rotors.
459 Array.Reverse(rotors);
460  
461 // Reverse pass through the Enigma's rotors.
462 rotate.Invoke(rotors);
463  
464 if (char.IsUpper(c))
465 {
466 l = char.ToUpper(l);
467 }
468 result.Append(l);
469 }
470  
471 return result.ToString();
472 }
473  
474 ///////////////////////////////////////////////////////////////////////////
475 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
476 ///////////////////////////////////////////////////////////////////////////
477 /// <summary>
478 /// Expand the VIGENRE key to the length of the input.
479 /// </summary>
480 /// <param name="input">the input to expand to</param>
481 /// <param name="enc_key">the key to expand</param>
482 /// <returns>the expanded key</returns>
483 private static string wasVigenereExpandKey(string input, string enc_key)
484 {
485 string exp_key = string.Empty;
486 int i = 0, j = 0;
487 do
488 {
489 char p = input[i];
490 if (!char.IsLetter(p))
491 {
492 exp_key += p;
493 ++i;
494 continue;
495 }
496 int m = j%enc_key.Length;
497 exp_key += enc_key[m];
498 ++j;
499 ++i;
500 } while (i < input.Length);
501 return exp_key;
502 }
503  
504 ///////////////////////////////////////////////////////////////////////////
505 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
506 ///////////////////////////////////////////////////////////////////////////
507 /// <summary>
508 /// Encrypt using VIGENERE.
509 /// </summary>
510 /// <param name="input">the input to encrypt</param>
511 /// <param name="enc_key">the key to encrypt with</param>
512 /// <returns>the encrypted input</returns>
513 private static string wasEncryptVIGENERE(string input, string enc_key)
514 {
515 char[] a =
516 {
517 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
518 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
519 };
520  
521 enc_key = wasVigenereExpandKey(input, enc_key);
522 string result = string.Empty;
523 int i = 0;
524 do
525 {
526 char p = input[i];
527 if (!char.IsLetter(p))
528 {
529 result += p;
530 ++i;
531 continue;
532 }
533 char q =
534 wasReversePermuteArrayElements(a, Array.IndexOf(a, enc_key[i]))[
535 Array.IndexOf(a, char.ToLowerInvariant(p))];
536 if (char.IsUpper(p))
537 {
538 q = char.ToUpperInvariant(q);
539 }
540 result += q;
541 ++i;
542 } while (i < input.Length);
543 return result;
544 }
545  
546 ///////////////////////////////////////////////////////////////////////////
547 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
548 ///////////////////////////////////////////////////////////////////////////
549 /// <summary>
550 /// Decrypt using VIGENERE.
551 /// </summary>
552 /// <param name="input">the input to decrypt</param>
553 /// <param name="enc_key">the key to decrypt with</param>
554 /// <returns>the decrypted input</returns>
555 private static string wasDecryptVIGENERE(string input, string enc_key)
556 {
557 char[] a =
558 {
559 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
560 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
561 };
562  
563 enc_key = wasVigenereExpandKey(input, enc_key);
564 string result = string.Empty;
565 int i = 0;
566 do
567 {
568 char p = input[i];
569 if (!char.IsLetter(p))
570 {
571 result += p;
572 ++i;
573 continue;
574 }
575 char q =
576 a[
577 Array.IndexOf(wasReversePermuteArrayElements(a, Array.IndexOf(a, enc_key[i])),
578 char.ToLowerInvariant(p))];
579 if (char.IsUpper(p))
580 {
581 q = char.ToUpperInvariant(q);
582 }
583 result += q;
584 ++i;
585 } while (i < input.Length);
586 return result;
587 }
588  
589 ///////////////////////////////////////////////////////////////////////////
590 // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
591 ///////////////////////////////////////////////////////////////////////////
592 /// <summary>
593 /// An implementation of the ATBASH cypher for latin alphabets.
594 /// </summary>
595 /// <param name="data">the data to encrypt or decrypt</param>
596 /// <returns>the encrypted or decrypted data</returns>
597 private static string wasATBASH(string data)
598 {
599 char[] a =
600 {
601 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
602 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
603 };
604  
605 char[] input = data.ToArray();
606  
607 Parallel.ForEach(Enumerable.Range(0, data.Length), i =>
608 {
609 char e = input[i];
610 if (!char.IsLetter(e)) return;
611 int x = 25 - Array.BinarySearch(a, char.ToLowerInvariant(e));
612 if (!char.IsUpper(e))
613 {
614 input[i] = a[x];
615 return;
616 }
617 input[i] = char.ToUpperInvariant(a[x]);
618 });
619  
620 return new string(input);
621 }
622  
623 #endregion
624  
625 /// <summary>
626 /// Corrade's input filter function.
627 /// </summary>
628 private static readonly Func<string, string> wasInput = o =>
629 {
630 if (string.IsNullOrEmpty(o)) return string.Empty;
631  
632 foreach (Filter filter in vassalConfiguration.InputFilters)
633 {
634 switch (filter)
635 {
636 case Filter.RFC1738:
637 o = wasURLUnescapeDataString(o);
638 break;
639 case Filter.RFC3986:
640 o = wasURIUnescapeDataString(o);
641 break;
642 case Filter.ENIGMA:
643 o = wasEnigma(o, vassalConfiguration.ENIGMA.rotors.ToArray(),
644 vassalConfiguration.ENIGMA.plugs.ToArray(),
645 vassalConfiguration.ENIGMA.reflector);
646 break;
647 case Filter.VIGENERE:
648 o = wasDecryptVIGENERE(o, vassalConfiguration.VIGENERESecret);
649 break;
650 case Filter.ATBASH:
651 o = wasATBASH(o);
652 break;
653 case Filter.BASE64:
654 o = Encoding.UTF8.GetString(Convert.FromBase64String(o));
655 break;
656 }
657 }
658 return o;
659 };
660  
661 /// <summary>
662 /// Corrade's output filter function.
663 /// </summary>
664 private static readonly Func<string, string> wasOutput = o =>
665 {
666 if (string.IsNullOrEmpty(o)) return string.Empty;
667  
668 foreach (Filter filter in vassalConfiguration.OutputFilters)
669 {
670 switch (filter)
671 {
672 case Filter.RFC1738:
673 o = wasURLEscapeDataString(o);
674 break;
675 case Filter.RFC3986:
676 o = wasURIEscapeDataString(o);
677 break;
678 case Filter.ENIGMA:
679 o = wasEnigma(o, vassalConfiguration.ENIGMA.rotors.ToArray(),
680 vassalConfiguration.ENIGMA.plugs.ToArray(),
681 vassalConfiguration.ENIGMA.reflector);
682 break;
683 case Filter.VIGENERE:
684 o = wasEncryptVIGENERE(o, vassalConfiguration.VIGENERESecret);
685 break;
686 case Filter.ATBASH:
687 o = wasATBASH(o);
688 break;
689 case Filter.BASE64:
690 o = Convert.ToBase64String(Encoding.UTF8.GetBytes(o));
691 break;
692 }
693 }
694 return o;
695 };
696  
697 ///////////////////////////////////////////////////////////////////////////
698 // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
699 ///////////////////////////////////////////////////////////////////////////
700 /// <summary>Escapes a dictionary's keys and values for sending as POST data.</summary>
701 /// <param name="data">A dictionary containing keys and values to be escaped</param>
702 private static Dictionary<string, string> wasKeyValueEscape(Dictionary<string, string> data)
703 {
704 return data.AsParallel().ToDictionary(o => wasOutput(o.Key), p => wasOutput(p.Value));
705 }
706  
707 ///////////////////////////////////////////////////////////////////////////
708 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
709 ///////////////////////////////////////////////////////////////////////////
710 /// <summary>
711 /// Converts a list of string to a comma-separated values string.
712 /// </summary>
713 /// <param name="l">a list of strings</param>
714 /// <returns>a commma-separated list of values</returns>
715 /// <remarks>compliant with RFC 4180</remarks>
716 public static string wasEnumerableToCSV(IEnumerable<string> l)
717 {
718 string[] csv = l.Select(o => o.Clone() as string).ToArray();
719 Parallel.ForEach(csv.Select((v, i) => new {i, v}), o =>
720 {
721 string cell = o.v.Replace("\"", "\"\"");
722 switch (new[] {'"', ' ', ',', '\r', '\n'}.Any(p => cell.Contains(p)))
723 {
724 case true:
725 csv[o.i] = "\"" + cell + "\"";
726 break;
727 default:
728 csv[o.i] = cell;
729 break;
730 }
731 });
732 return String.Join(",", csv);
733 }
734  
735 ///////////////////////////////////////////////////////////////////////////
736 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
737 ///////////////////////////////////////////////////////////////////////////
738 /// <summary>
739 /// Converts a comma-separated list of values to a list of strings.
740 /// </summary>
741 /// <param name="csv">a comma-separated list of values</param>
742 /// <returns>a list of strings</returns>
743 /// <remarks>compliant with RFC 4180</remarks>
744 public static IEnumerable<string> wasCSVToEnumerable(string csv)
745 {
746 Stack<char> s = new Stack<char>();
747 StringBuilder m = new StringBuilder();
748 for (int i = 0; i < csv.Length; ++i)
749 {
750 switch (csv[i])
751 {
752 case ',':
753 if (!s.Any() || !s.Peek().Equals('"'))
754 {
755 yield return m.ToString();
756 m = new StringBuilder();
757 continue;
758 }
759 m.Append(csv[i]);
760 continue;
761 case '"':
762 if (i + 1 < csv.Length && csv[i].Equals(csv[i + 1]))
763 {
764 m.Append(csv[i]);
765 ++i;
766 continue;
767 }
768 if (!s.Any() || !s.Peek().Equals(csv[i]))
769 {
770 s.Push(csv[i]);
771 continue;
772 }
773 s.Pop();
774 continue;
775 }
776 m.Append(csv[i]);
777 }
778  
779 yield return m.ToString();
780 }
781  
782 ///////////////////////////////////////////////////////////////////////////
783 // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
784 ///////////////////////////////////////////////////////////////////////////
785 /// <summary>
786 /// Serialises a dictionary to key-value data.
787 /// </summary>
788 /// <param name="data">a dictionary</param>
789 /// <returns>a key-value data encoded string</returns>
790 private static string wasKeyValueEncode(Dictionary<string, string> data)
791 {
792 return String.Join("&", data.AsParallel().Select(o => String.Join("=", o.Key, o.Value)));
793 }
794  
795 ///////////////////////////////////////////////////////////////////////////
796 // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
797 ///////////////////////////////////////////////////////////////////////////
798 /// <summary>
799 /// Sends a post request to an URL with set key-value pairs.
800 /// </summary>
801 /// <param name="URL">the url to send the message to</param>
802 /// <param name="message">key-value pairs to send</param>
803 /// <param name="millisecondsTimeout">the time in milliseconds for the request to timeout</param>
804 private static string wasPOST(string URL, Dictionary<string, string> message, uint millisecondsTimeout)
805 {
806 try
807 {
808 HttpWebRequest request = (HttpWebRequest) WebRequest.Create(URL);
809 request.UserAgent = VASSAL_CONSTANTS.USER_AGENT;
810 request.Proxy = WebRequest.DefaultWebProxy;
811 request.Timeout = (int) millisecondsTimeout;
812 request.AllowAutoRedirect = true;
813 request.AllowWriteStreamBuffering = true;
814 request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
815 request.Method = WebRequestMethods.Http.Post;
816 // set the content type based on chosen output filers
817 switch (vassalConfiguration.OutputFilters.Last())
818 {
819 case Filter.RFC1738:
820 request.ContentType = VASSAL_CONSTANTS.CONTENT_TYPE.WWW_FORM_URLENCODED;
821 break;
822 default:
823 request.ContentType = VASSAL_CONSTANTS.CONTENT_TYPE.TEXT_PLAIN;
824 break;
825 }
826 // send request
827 using (Stream requestStream = request.GetRequestStream())
828 {
829 using (StreamWriter dataStream = new StreamWriter(requestStream))
830 {
831 dataStream.Write(wasKeyValueEncode(message));
832 }
833 }
834 // read response
835 using (HttpWebResponse response = (HttpWebResponse) request.GetResponse())
836 {
837 using (Stream responseStream = response.GetResponseStream())
838 {
839 if (responseStream != null)
840 {
841 using (
842 StreamReader streamReader = new StreamReader(responseStream))
843 {
844 return streamReader.ReadToEnd();
845 }
846 }
847 }
848 }
849 }
850 catch (Exception)
851 {
852  
853 }
854  
855 return null;
856 }
857  
858 /// <summary>
859 /// Constants used by Corrade.
860 /// </summary>
861 public struct VASSAL_CONSTANTS
862 {
863 /// <summary>
864 /// Copyright.
865 /// </summary>
866 public const string COPYRIGHT = @"(c) Copyright 2013 Wizardry and Steamworks";
867  
868 public const string WIZARDRY_AND_STEAMWORKS = @"Wizardry and Steamworks";
869 public const string VASSAL = @"Vassal";
870 public const string WIZARDRY_AND_STEAMWORKS_WEBSITE = @"http://grimore.org";
871  
872 /// <summary>
873 /// Vassal version.
874 /// </summary>
875 public static readonly string VASSAL_VERSION = Assembly.GetEntryAssembly().GetName().Version.ToString();
876  
877 /// <summary>
878 /// Corrade user agent.
879 /// </summary>
880 public static readonly string USER_AGENT =
881 $"{VASSAL}/{VASSAL_VERSION} ({WIZARDRY_AND_STEAMWORKS_WEBSITE})";
882  
883 /// <summary>
884 /// Vassal compile date.
885 /// </summary>
886 public static readonly string VASSAL_COMPILE_DATE = new DateTime(2000, 1, 1).Add(new TimeSpan(
887 TimeSpan.TicksPerDay*Assembly.GetEntryAssembly().GetName().Version.Build + // days since 1 January 2000
888 TimeSpan.TicksPerSecond*2*Assembly.GetEntryAssembly().GetName().Version.Revision)).ToLongDateString();
889  
890 /// <summary>
891 /// Vassal configuration file.
892 /// </summary>
893 public static readonly string VASSAL_CONFIGURATION_FILE = @"Vassal.ini";
894  
895 /// <summary>
896 /// Vassal regions file.
897 /// </summary>
898 public static readonly string VASSAL_REGIONS = @"Regions.csv";
899  
900 /// <summary>
901 /// Conten-types that Corrade can send and receive.
902 /// </summary>
903 public struct CONTENT_TYPE
904 {
905 public const string TEXT_PLAIN = @"text/plain";
906 public const string WWW_FORM_URLENCODED = @"application/x-www-form-urlencoded";
907 }
908 }
909  
910 private static readonly System.Action updateCurrentRegionName = () =>
911 {
912 try
913 {
914 string result = wasPOST(vassalConfiguration.HTTPServerURL,
915 wasKeyValueEscape(new Dictionary<string, string>
916 {
3 eva 917 {"command", "getregiondata"},
918 {"group", vassalConfiguration.Group},
919 {"password", vassalConfiguration.Password},
920 {"data", "Name"}
2 zed 921 }), 60000);
922 bool success;
923 if (string.IsNullOrEmpty(result) ||
924 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
925 {
3 eva 926 vassalForm.BeginInvoke((MethodInvoker) (() =>
2 zed 927 {
928 vassalForm.StatusText.Text = @"Failed to query Corrade for current region.";
929 }));
930 return;
931 }
932 switch (success)
933 {
934 case true:
3 eva 935 vassalForm.BeginInvoke((MethodInvoker) (() =>
2 zed 936 {
937 vassalForm.CurrentRegionAt.Visible = true;
938 vassalForm.CurrentRegionName.Visible = true;
939 vassalForm.CurrentRegionName.Text =
940 wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).Last();
941 }));
942 break;
943 default:
3 eva 944 vassalForm.BeginInvoke((MethodInvoker) (() =>
2 zed 945 {
946 vassalForm.CurrentRegionAt.Visible = false;
947 vassalForm.CurrentRegionName.Visible = false;
948 vassalForm.StatusText.Text = @"Error getting current region: " +
949 wasInput(wasKeyValueGet("error", result));
950 }));
951 break;
952 }
953 }
954 catch (Exception ex)
955 {
3 eva 956 vassalForm.BeginInvoke((MethodInvoker) (() =>
2 zed 957 {
958 vassalForm.StatusText.Text =
959 @"Error getting current region: " +
960 ex.Message;
961 }));
962 }
963 };
964  
965 public Vassal()
966 {
967 InitializeComponent();
968 vassalForm = this;
969 }
970  
971 private void RegionSelected(object sender, EventArgs e)
972 {
3 eva 973 string selectedRegionName = string.Empty;
974 Vector3 selectedRegionPosition = Vector3.Zero;
975 bool startTeleport = false;
2 zed 976 vassalForm.Invoke((MethodInvoker) (() =>
977 {
3 eva 978 ListViewItem listViewItem = LoadedRegions.SelectedItem as ListViewItem;
979 switch (listViewItem != null && LoadedRegions.SelectedIndex != -1)
980 {
981 case true:
982 selectedRegionName = listViewItem.Text;
983 selectedRegionPosition = (Vector3)listViewItem.Tag;
984 startTeleport = true;
985 break;
986 default:
987 startTeleport = false;
988 break;
989 }
2 zed 990 }));
991  
3 eva 992 if (!startTeleport) return;
993  
994  
995 // Announce teleport.
996 vassalForm.Invoke((MethodInvoker)(() =>
2 zed 997 {
3 eva 998 vassalForm.RegionTeleportGroup.Enabled = false;
999 vassalForm.StatusProgress.Value = 0;
1000 vassalForm.StatusText.Text = @"Teleporting to " + selectedRegionName;
1001 }));
2 zed 1002  
1003 new Thread(() =>
1004 {
3 eva 1005 Monitor.Enter(ClientInstanceTeleportLock);
1006 try
2 zed 1007 {
3 eva 1008 int elapsedSeconds = 0;
1009 System.Timers.Timer teleportTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
1010 teleportTimer.Elapsed += (o, p) =>
2 zed 1011 {
1012 vassalForm.Invoke((MethodInvoker) (() =>
1013 {
3 eva 1014 vassalForm.StatusProgress.Value =
1015 Math.Min(
1016 (int)
1017 (100d*
1018 (TimeSpan.FromSeconds(++elapsedSeconds).TotalMilliseconds/
1019 vassalConfiguration.TeleportTimeout)), 100);
2 zed 1020 }));
3 eva 1021 };
1022 teleportTimer.Start();
1023 string result = null;
1024 ManualResetEvent receivedPOST = new ManualResetEvent(false);
1025 new Thread(() =>
1026 {
1027 result = wasInput(wasPOST(vassalConfiguration.HTTPServerURL,
1028 wasKeyValueEscape(new Dictionary<string, string>
1029 {
1030 {"command", "teleport"},
1031 {"group", vassalConfiguration.Group},
1032 {"password", vassalConfiguration.Password},
1033 {"region", selectedRegionName},
1034 {"position", selectedRegionPosition.ToString()},
1035 {"fly", "True"}
1036 }), vassalConfiguration.TeleportTimeout));
1037 receivedPOST.Set();
1038 }) {IsBackground = true}.Start();
1039 receivedPOST.WaitOne((int) vassalConfiguration.TeleportTimeout, false);
1040 teleportTimer.Stop();
1041 switch (!string.IsNullOrEmpty(result) && wasKeyValueGet("success", result) == "True")
1042 {
1043 case true:
2 zed 1044 vassalForm.Invoke((MethodInvoker) (() =>
1045 {
3 eva 1046 vassalForm.StatusText.Text = @"Now at " + selectedRegionName;
1047 vassalForm.CurrentRegionName.Text = selectedRegionName;
2 zed 1048 }));
3 eva 1049 break;
1050 default:
1051 switch (!string.IsNullOrEmpty(result))
2 zed 1052 {
1053 case true:
3 eva 1054 vassalForm.Invoke((MethodInvoker) (() =>
2 zed 1055 {
3 eva 1056 vassalForm.StatusText.Text = @"Failed teleporting to " + selectedRegionName +
1057 @": " +
1058 wasKeyValueGet("error", result);
2 zed 1059 }));
1060 break;
3 eva 1061 default:
1062 vassalForm.Invoke((MethodInvoker) (() =>
1063 {
1064 vassalForm.StatusText.Text = @"Failed teleporting to " + selectedRegionName;
1065 }));
1066 break;
2 zed 1067 }
3 eva 1068 break;
2 zed 1069 }
1070 }
3 eva 1071 catch (Exception ex)
1072 {
1073 vassalForm.Invoke((MethodInvoker) (() =>
1074 {
1075 vassalForm.StatusText.Text = @"Error communicating with Corrade: " + ex.Message;
1076 }));
1077 }
1078 finally
1079 {
1080 Monitor.Exit(ClientInstanceTeleportLock);
1081 vassalForm.Invoke((MethodInvoker) (() =>
1082 {
1083 vassalForm.StatusProgress.Value = 100;
1084 vassalForm.RegionTeleportGroup.Enabled = true;
1085 // Set the map image to the loading spinner.
1086 Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
1087 System.IO.Stream file =
1088 thisAssembly.GetManifestResourceStream("Vassal.img.loading.gif");
1089 switch (file != null)
1090 {
1091 case true:
1092 vassalForm.Invoke((MethodInvoker) (() =>
1093 {
1094 RegionAvatarsMap.SizeMode = PictureBoxSizeMode.CenterImage;
1095 RegionAvatarsMap.Image = Image.FromStream(file);
1096 RegionAvatarsMap.Refresh();
1097 }));
1098 break;
1099 }
1100 // Clear the top scripts table.
1101 TopScriptsGridView.Rows.Clear();
1102 // Clear the top colliders table.
1103 TopCollidersGridView.Rows.Clear();
1104 // Invalidate data for overview tab.
1105 vassalForm.CurrentRegionAt.Visible = false;
1106 vassalForm.CurrentRegionName.Visible = false;
1107 Agents.Text = string.Empty;
1108 Agents.Enabled = false;
1109 LastLag.Text = string.Empty;
1110 LastLag.Enabled = false;
1111 Dilation.Text = string.Empty;
1112 Dilation.Enabled = false;
1113 FPS.Text = string.Empty;
1114 FPS.Enabled = false;
1115 PhysicsFPS.Text = string.Empty;
1116 PhysicsFPS.Enabled = false;
1117 ActiveScripts.Text = string.Empty;
1118 ActiveScripts.Enabled = false;
1119 ScriptTime.Text = string.Empty;
1120 ScriptTime.Enabled = false;
1121 Objects.Text = string.Empty;
1122 Objects.Enabled = false;
1123 }));
1124 }
2 zed 1125 }).Start();
3 eva 1126  
2 zed 1127 }
1128  
1129 private void SettingsRequested(object sender, EventArgs e)
1130 {
1131 SettingsForm settingsForm = new SettingsForm {TopMost = true};
1132 settingsForm.Show();
1133 }
1134  
1135 private void VassalShown(object sender, EventArgs e)
1136 {
3 eva 1137 // Set the version
1138 vassalForm.Version.Text = @"v" + VASSAL_CONSTANTS.VASSAL_VERSION;
1139  
4 vero 1140 // Disable estate manager tabs since we will enable these dynamically.
1141 TopScriptsTab.Enabled = false;
1142 TopCollidersTab.Enabled = false;
1143  
2 zed 1144 // Get the configuration file settings if it exists.
1145 if (File.Exists(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE))
1146 {
4 vero 1147 // Load the configuration.
2 zed 1148 VassalConfiguration.Load(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE, ref vassalConfiguration);
1149 }
1150  
1151 // Get all the regions if they exist.
1152 if (File.Exists(VASSAL_CONSTANTS.VASSAL_REGIONS))
1153 {
1154 Vector3 localPosition;
3 eva 1155 List<KeyValuePair<string, Vector3>> ConfiguredRegions = new List<KeyValuePair<string, Vector3>>(
2 zed 1156 File.ReadAllLines(VASSAL_CONSTANTS.VASSAL_REGIONS)
1157 .Select(o => new List<string>(wasCSVToEnumerable(o)))
1158 .Where(o => o.Count == 2)
1159 .ToDictionary(o => o.First(),
1160 p =>
1161 Vector3.TryParse(p.Last(), out localPosition)
1162 ? localPosition
3 eva 1163 : Vector3.Zero).OrderBy(o => o.Key).ToDictionary(o => o.Key, o => o.Value));
1164 // Populate the loaded regions.
2 zed 1165 LoadedRegions.Items.Clear();
1166 LoadedRegions.Items.AddRange(
3 eva 1167 ConfiguredRegions.Select(o => (object) new ListViewItem {Text = o.Key, Tag = o.Value})
2 zed 1168 .ToArray());
3 eva 1169 // Populate the batch restart grid view.
1170 BatchRestartGridView.Rows.Clear();
1171 foreach (KeyValuePair<string, Vector3> data in ConfiguredRegions)
1172 {
1173 BatchRestartGridView.Rows.Add(data.Key, data.Value.ToString());
1174 }
2 zed 1175 }
1176  
1177 // Set the map image to the loading spinner.
1178 Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
1179 System.IO.Stream file =
1180 thisAssembly.GetManifestResourceStream("Vassal.img.loading.gif");
1181 switch (file != null)
1182 {
1183 case true:
3 eva 1184 vassalForm.Invoke((MethodInvoker) (() =>
2 zed 1185 {
1186 RegionAvatarsMap.SizeMode = PictureBoxSizeMode.CenterImage;
1187 RegionAvatarsMap.Image = Image.FromStream(file);
1188 RegionAvatarsMap.Refresh();
1189 }));
1190 break;
1191 }
1192  
1193 // Start the overview timer.
1194 overviewTabTimer.Elapsed += (o, p) =>
1195 {
1196 if (!Monitor.TryEnter(ClientInstanceTeleportLock))
1197 return;
1198  
1199 try
1200 {
1201 // Do not do anything in case the tab is not selected.
1202 vassalForm.Invoke((MethodInvoker) (() =>
1203 {
1204 if (!Tabs.SelectedTab.Equals(OverviewTab))
1205 throw new Exception();
1206 }));
1207  
1208 // Get the statistics.
1209 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1210 wasKeyValueEscape(new Dictionary<string, string>
1211 {
1212 {"command", "getregiondata"},
1213 {"group", vassalConfiguration.Group},
1214 {"password", vassalConfiguration.Password},
1215 {
1216 "data", wasEnumerableToCSV(new[]
1217 {
1218 "Agents",
1219 "LastLag",
1220 "Dilation",
1221 "FPS",
1222 "PhysicsFPS",
1223 "ActiveScripts",
1224 "ScriptTime",
3 eva 1225 "Objects",
4 vero 1226 "Name",
1227 "IsEstateManager"
2 zed 1228 })
1229 }
1230 }), vassalConfiguration.DataTimeout);
1231  
1232 bool success;
1233 if (string.IsNullOrEmpty(result) ||
1234 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1235 throw new Exception();
1236  
1237 List<string> data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
1238 if (data.Count.Equals(0))
1239 throw new Exception();
1240  
1241 vassalForm.Invoke((MethodInvoker) (() =>
1242 {
4 vero 1243 // Drop access to features if we are not estate managers.
1244 bool isEstateManager;
1245 switch (
1246 bool.TryParse(data[data.IndexOf("IsEstateManager") + 1], out isEstateManager) &&
1247 isEstateManager)
1248 {
1249 case true: // we are an estate manager
1250 TopScriptsTab.Enabled = true;
1251 TopCollidersTab.Enabled = true;
1252 break;
1253 default:
1254 TopScriptsTab.Enabled = false;
1255 TopCollidersTab.Enabled = false;
1256 break;
1257 }
1258  
3 eva 1259 // Show the region name.
1260 vassalForm.CurrentRegionName.Text = data[data.IndexOf("Name") + 1];
1261 vassalForm.CurrentRegionAt.Visible = true;
1262 vassalForm.CurrentRegionName.Visible = true;
1263  
1264 // Populate the overview tab.
2 zed 1265 Agents.Text = data[data.IndexOf("Agents") + 1];
3 eva 1266 Agents.Enabled = true;
2 zed 1267 LastLag.Text = data[data.IndexOf("LastLag") + 1];
3 eva 1268 LastLag.Enabled = true;
2 zed 1269 Dilation.Text = data[data.IndexOf("Dilation") + 1];
3 eva 1270 Dilation.Enabled = true;
2 zed 1271 FPS.Text = data[data.IndexOf("FPS") + 1];
3 eva 1272 FPS.Enabled = true;
2 zed 1273 PhysicsFPS.Text = data[data.IndexOf("PhysicsFPS") + 1];
3 eva 1274 PhysicsFPS.Enabled = true;
2 zed 1275 ActiveScripts.Text = data[data.IndexOf("ActiveScripts") + 1];
3 eva 1276 ActiveScripts.Enabled = true;
2 zed 1277 ScriptTime.Text = data[data.IndexOf("ScriptTime") + 1];
3 eva 1278 ScriptTime.Enabled = true;
2 zed 1279 Objects.Text = data[data.IndexOf("Objects") + 1];
3 eva 1280 Objects.Enabled = true;
2 zed 1281 }));
3 eva 1282  
2 zed 1283 // Get the map image.
1284 result = wasPOST(vassalConfiguration.HTTPServerURL,
1285 wasKeyValueEscape(new Dictionary<string, string>
1286 {
1287 {"command", "getgridregiondata"},
1288 {"group", vassalConfiguration.Group},
1289 {"password", vassalConfiguration.Password},
3 eva 1290 {"data", "MapImageID"}
2 zed 1291 }), vassalConfiguration.DataTimeout);
3 eva 1292  
2 zed 1293 if (string.IsNullOrEmpty(result) ||
1294 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1295 throw new Exception();
1296  
1297 data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
1298 if (!data.Count.Equals(2))
1299 throw new Exception();
1300 result = wasPOST(vassalConfiguration.HTTPServerURL,
1301 wasKeyValueEscape(new Dictionary<string, string>
1302 {
1303 {"command", "download"},
1304 {"group", vassalConfiguration.Group},
1305 {"password", vassalConfiguration.Password},
3 eva 1306 {"item", data.Last()},
1307 {"type", "Texture"},
1308 {"format", "Jpeg"},
2 zed 1309 }), vassalConfiguration.DataTimeout);
1310 if (string.IsNullOrEmpty(result) ||
1311 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1312 throw new Exception();
1313 byte[] mapImageBytes = Convert.FromBase64String(wasInput(wasKeyValueGet("data", result)));
1314 Image mapImage;
1315 using (MemoryStream memoryStream = new MemoryStream(mapImageBytes, 0, mapImageBytes.Length))
1316 {
1317 mapImage = Image.FromStream(memoryStream);
1318 }
1319  
1320 // Get the avatar positions.
1321 result = wasPOST(vassalConfiguration.HTTPServerURL,
1322 wasKeyValueEscape(new Dictionary<string, string>
1323 {
1324 {"command", "getavatarpositions"},
1325 {"group", vassalConfiguration.Group},
1326 {"password", vassalConfiguration.Password},
3 eva 1327 {"entity", "region"}
2 zed 1328 }), vassalConfiguration.DataTimeout);
1329  
1330 if (string.IsNullOrEmpty(result) ||
1331 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1332 throw new Exception();
1333  
1334 // Every thrid element represents a Vecto3 of the avatar position.
3 eva 1335 data =
1336 wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
1337 .Skip(2)
1338 .Where((x, i) => i%3 == 0)
1339 .ToList();
2 zed 1340  
1341 // Draw the avatars onto the map.
1342 Graphics mapGraphics = Graphics.FromImage(mapImage);
1343 Vector3 position;
1344 foreach (string vector in data)
1345 {
1346 switch (Vector3.TryParse(vector, out position))
1347 {
1348 case true:
3 eva 1349 mapGraphics.FillEllipse(Brushes.Chartreuse,
1350 new Rectangle((int) position.X, (int) position.Y, 4, 4));
2 zed 1351 break;
1352 }
1353 }
1354 mapGraphics.DrawImage(mapImage, new Point(0, 0));
1355  
1356 vassalForm.Invoke((MethodInvoker) (() =>
1357 {
1358 RegionAvatarsMap.SizeMode = PictureBoxSizeMode.StretchImage;
1359 RegionAvatarsMap.Image = mapImage;
1360 RegionAvatarsMap.Refresh();
1361 }));
1362 }
1363 catch (Exception)
1364 {
1365  
1366 }
1367 finally
1368 {
1369 Monitor.Exit(ClientInstanceTeleportLock);
1370 }
3 eva 1371  
2 zed 1372 };
1373 overviewTabTimer.Start();
1374  
1375 // Start the top scores timer.
1376 topScriptsTabTimer.Elapsed += (o, p) =>
1377 {
1378 if (!Monitor.TryEnter(ClientInstanceTeleportLock))
1379 return;
1380  
1381 try
1382 {
1383 // Do not do anything in case the tab is not selected.
1384 vassalForm.Invoke((MethodInvoker) (() =>
1385 {
1386 if (!Tabs.SelectedTab.Equals(TopScriptsTab))
1387 throw new Exception();
1388 }));
1389  
1390 // Get the statistics.
1391 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1392 wasKeyValueEscape(new Dictionary<string, string>
1393 {
1394 {"command", "getregiontop"},
1395 {"group", vassalConfiguration.Group},
1396 {"password", vassalConfiguration.Password},
1397 {"type", "scripts"}
1398 }), vassalConfiguration.DataTimeout);
1399  
1400 bool success;
1401 if (string.IsNullOrEmpty(result) ||
1402 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1403 throw new Exception();
1404  
1405 HashSet<List<string>> data =
1406 new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
1407 .Select((x, i) => new {Index = i, Value = x})
1408 .GroupBy(x => x.Index/5)
1409 .Select(x => x.Select(v => v.Value).ToList()));
1410 if (data.Count.Equals(0))
1411 throw new Exception();
1412  
1413 vassalForm.Invoke((MethodInvoker) (() =>
1414 {
3 eva 1415 // Remove rows that are not in the data update.
1416 foreach (
1417 int index in
1418 TopScriptsGridView.Rows.AsParallel().Cast<DataGridViewRow>()
1419 .Where(
1420 topScriptsRow =>
1421 !data.Any(q => q[2].Equals(topScriptsRow.Cells["TopScriptsUUID"].Value)))
1422 .Select(q => q.Index))
2 zed 1423 {
3 eva 1424 TopScriptsGridView.Rows.RemoveAt(index);
2 zed 1425 }
3 eva 1426 // Now update or add new data.
1427 foreach (List<string> dataComponents in data.Where(q => q.Count.Equals(5)))
1428 {
1429 DataGridViewRow row =
1430 TopScriptsGridView.Rows.AsParallel()
1431 .Cast<DataGridViewRow>()
1432 .FirstOrDefault(q => q.Cells["TopScriptsUUID"].Value.Equals(dataComponents[2]));
1433 switch (row != null)
1434 {
1435 case true: // the row exists, so update it.
1436 row.Cells["TopScriptsScore"].Value = dataComponents[0];
1437 row.Cells["TopScriptsTaskName"].Value = dataComponents[1];
1438 row.Cells["TopScriptsUUID"].Value = dataComponents[2];
1439 row.Cells["TopScriptsOwner"].Value = dataComponents[3];
1440 row.Cells["TopScriptsPosition"].Value = dataComponents[4];
1441 break;
1442 case false: // the row dosn't exist, so add it.
1443 TopScriptsGridView.Rows.Add(dataComponents[0], dataComponents[1], dataComponents[2],
1444 dataComponents[3], dataComponents[4]);
1445 break;
1446 }
1447 }
2 zed 1448 }));
1449 }
1450 catch (Exception)
1451 {
1452  
1453 }
1454 finally
1455 {
1456 Monitor.Exit(ClientInstanceTeleportLock);
1457 }
1458 };
1459 topScriptsTabTimer.Start();
1460  
1461 // Start the top colliders timer.
1462 topCollidersTabTimer.Elapsed += (o, p) =>
1463 {
1464 if (!Monitor.TryEnter(ClientInstanceTeleportLock))
1465 return;
1466  
1467 try
1468 {
1469 // Do not do anything in case the tab is not selected.
3 eva 1470 vassalForm.Invoke((MethodInvoker) (() =>
2 zed 1471 {
1472 if (!Tabs.SelectedTab.Equals(TopCollidersTab))
1473 throw new Exception();
1474 }));
1475  
1476 // Get the statistics.
1477 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1478 wasKeyValueEscape(new Dictionary<string, string>
1479 {
1480 {"command", "getregiontop"},
1481 {"group", vassalConfiguration.Group},
1482 {"password", vassalConfiguration.Password},
1483 {"type", "colliders"}
1484 }), vassalConfiguration.DataTimeout);
1485  
1486 bool success;
1487 if (string.IsNullOrEmpty(result) ||
1488 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1489 throw new Exception();
1490  
1491 HashSet<List<string>> data =
1492 new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
3 eva 1493 .Select((x, i) => new {Index = i, Value = x})
1494 .GroupBy(x => x.Index/5)
2 zed 1495 .Select(x => x.Select(v => v.Value).ToList()));
1496 if (data.Count.Equals(0))
1497 throw new Exception();
1498  
3 eva 1499 vassalForm.Invoke((MethodInvoker) (() =>
2 zed 1500 {
3 eva 1501 // Remove rows that are not in the data update.
1502 foreach (
1503 int index in
1504 TopCollidersGridView.Rows.AsParallel().Cast<DataGridViewRow>()
1505 .Where(
1506 topCollidersRow =>
1507 !data.Any(q => q[2].Equals(topCollidersRow.Cells["TopCollidersUUID"].Value)))
1508 .Select(q => q.Index))
2 zed 1509 {
3 eva 1510 TopCollidersGridView.Rows.RemoveAt(index);
2 zed 1511 }
3 eva 1512 // Now update or add new data.
1513 foreach (List<string> dataComponents in data.Where(q => q.Count.Equals(5)))
1514 {
1515 DataGridViewRow row =
1516 TopCollidersGridView.Rows.AsParallel()
1517 .Cast<DataGridViewRow>()
1518 .FirstOrDefault(q => q.Cells["TopCollidersUUID"].Value.Equals(dataComponents[2]));
1519 switch (row != null)
1520 {
1521 case true: // the row exists, so update it.
1522 row.Cells["TopCollidersScore"].Value = dataComponents[0];
1523 row.Cells["TopSCollidersTaskName"].Value = dataComponents[1];
1524 row.Cells["TopCollidersUUID"].Value = dataComponents[2];
1525 row.Cells["TopCollidersOwner"].Value = dataComponents[3];
1526 row.Cells["TopCollidersPosition"].Value = dataComponents[4];
1527 break;
1528 case false: // the row dosn't exist, so add it.
1529 TopCollidersGridView.Rows.Add(dataComponents[0], dataComponents[1], dataComponents[2],
1530 dataComponents[3], dataComponents[4]);
1531 break;
1532 }
1533 }
2 zed 1534  
1535 }));
1536 }
1537 catch (Exception)
1538 {
1539  
1540 }
1541 finally
1542 {
1543 Monitor.Exit(ClientInstanceTeleportLock);
1544 }
1545 };
1546 topCollidersTabTimer.Start();
1547 }
1548  
1549 private void RequestedEditRegions(object sender, EventArgs e)
1550 {
1551 // Clear any selection.
1552 LoadedRegions.SelectedIndex = -1;
3 eva 1553 RegionEditForm regionEditForm = new RegionEditForm {TopMost = true};
2 zed 1554 regionEditForm.Show();
1555 }
1556  
1557 private void RequestSelecting(object sender, TabControlCancelEventArgs e)
1558 {
1559 e.Cancel = !e.TabPage.Enabled;
1560 }
3 eva 1561  
1562 private void RequestExportTopScripts(object sender, EventArgs e)
1563 {
1564 vassalForm.BeginInvoke((MethodInvoker) (() =>
1565 {
1566 switch (vassalForm.ExportCSVDialog.ShowDialog())
1567 {
1568 case DialogResult.OK:
1569 string file = vassalForm.ExportCSVDialog.FileName;
1570 new Thread(() =>
1571 {
1572 vassalForm.BeginInvoke((MethodInvoker) (() =>
1573 {
1574 try
1575 {
1576 vassalForm.StatusText.Text = @"exporting...";
1577 vassalForm.StatusProgress.Value = 0;
1578  
1579 using (StreamWriter streamWriter = new StreamWriter(file, false, Encoding.UTF8))
1580 {
1581 foreach (DataGridViewRow topScriptsRow in TopScriptsGridView.Rows)
1582 {
1583 streamWriter.WriteLine(wasEnumerableToCSV(new[]
1584 {
1585 topScriptsRow.Cells["TopScriptsScore"].Value.ToString(),
1586 topScriptsRow.Cells["TopScriptsTaskName"].Value.ToString(),
1587 topScriptsRow.Cells["TopScriptsUUID"].Value.ToString(),
1588 topScriptsRow.Cells["TopScriptsOwner"].Value.ToString(),
1589 topScriptsRow.Cells["TopScriptsPosition"].Value.ToString()
1590 }));
1591 }
1592 }
1593  
1594 vassalForm.StatusText.Text = @"exported";
1595 vassalForm.StatusProgress.Value = 100;
1596 }
1597 catch (Exception ex)
1598 {
1599 vassalForm.StatusText.Text = ex.Message;
1600 }
1601 }));
1602 })
1603 {IsBackground = true, Priority = ThreadPriority.Normal}.Start();
1604 break;
1605 }
1606 }));
1607 }
1608  
1609 private void RequestExportTopColliders(object sender, EventArgs e)
1610 {
1611 vassalForm.BeginInvoke((MethodInvoker) (() =>
1612 {
1613 switch (vassalForm.ExportCSVDialog.ShowDialog())
1614 {
1615 case DialogResult.OK:
1616 string file = vassalForm.ExportCSVDialog.FileName;
1617 new Thread(() =>
1618 {
1619 vassalForm.BeginInvoke((MethodInvoker) (() =>
1620 {
1621 try
1622 {
1623 vassalForm.StatusText.Text = @"exporting...";
1624 vassalForm.StatusProgress.Value = 0;
1625  
1626 using (StreamWriter streamWriter = new StreamWriter(file, false, Encoding.UTF8))
1627 {
1628 foreach (DataGridViewRow topCollidersRow in TopCollidersGridView.Rows)
1629 {
1630 streamWriter.WriteLine(wasEnumerableToCSV(new[]
1631 {
1632 topCollidersRow.Cells["TopCollidersScore"].Value.ToString(),
1633 topCollidersRow.Cells["TopCollidersTaskName"].Value.ToString(),
1634 topCollidersRow.Cells["TopCollidersUUID"].Value.ToString(),
1635 topCollidersRow.Cells["TopCollidersOwner"].Value.ToString(),
1636 topCollidersRow.Cells["TopCollidersPosition"].Value.ToString()
1637 }));
1638 }
1639 }
1640  
1641 vassalForm.StatusText.Text = @"exported";
1642 vassalForm.StatusProgress.Value = 100;
1643 }
1644 catch (Exception ex)
1645 {
1646 vassalForm.StatusText.Text = ex.Message;
1647 }
1648 }));
1649 })
1650 {IsBackground = true, Priority = ThreadPriority.Normal}.Start();
1651 break;
1652 }
1653 }));
1654 }
1655  
1656 private void RequestFilterTopScripts(object sender, EventArgs e)
1657 {
1658 vassalForm.BeginInvoke((MethodInvoker) (() =>
1659 {
1660 Regex topScriptsRowRegex;
1661 switch (!string.IsNullOrEmpty(TopScriptsFilter.Text))
1662 {
1663 case true:
1664 topScriptsRowRegex = new Regex(TopScriptsFilter.Text, RegexOptions.Compiled);
1665 break;
1666 default:
1667 topScriptsRowRegex = new Regex(@".+?", RegexOptions.Compiled);
1668 break;
1669 }
1670 foreach (DataGridViewRow topScriptsRow in TopScriptsGridView.Rows.AsParallel().Cast<DataGridViewRow>())
1671 {
1672 topScriptsRow.Visible =
1673 topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsScore"].Value.ToString()) ||
1674 topScriptsRowRegex.IsMatch(
1675 topScriptsRow.Cells["TopScriptsTaskName"].Value.ToString()) ||
1676 topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsUUID"].Value.ToString()) ||
1677 topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsOwner"].Value.ToString()) ||
1678 topScriptsRowRegex.IsMatch(
1679 topScriptsRow.Cells["TopScriptsPosition"].Value.ToString());
1680 }
1681 }));
1682 }
1683  
1684 private void RequestFilterTopColliders(object sender, EventArgs e)
1685 {
1686 vassalForm.BeginInvoke((MethodInvoker)(() =>
1687 {
1688 Regex topCollidersRowRegex;
1689 switch (!string.IsNullOrEmpty(TopScriptsFilter.Text))
1690 {
1691 case true:
1692 topCollidersRowRegex = new Regex(TopScriptsFilter.Text, RegexOptions.Compiled);
1693 break;
1694 default:
1695 topCollidersRowRegex = new Regex(@".+?", RegexOptions.Compiled);
1696 break;
1697 }
1698 foreach (DataGridViewRow topCollidersRow in TopCollidersGridView.Rows.AsParallel().Cast<DataGridViewRow>())
1699 {
1700 topCollidersRow.Visible =
1701 topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersScore"].Value.ToString()) ||
1702 topCollidersRowRegex.IsMatch(
1703 topCollidersRow.Cells["TopCollidersTaskName"].Value.ToString()) ||
1704 topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersUUID"].Value.ToString()) ||
1705 topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersOwner"].Value.ToString()) ||
1706 topCollidersRowRegex.IsMatch(
1707 topCollidersRow.Cells["TopCollidersPosition"].Value.ToString());
1708 }
1709 }));
1710 }
1711  
1712 private void RequestReturnTopScriptsObjects(object sender, EventArgs e)
1713 {
1714 // Block teleports and disable button.
1715 vassalForm.Invoke((MethodInvoker) (() =>
1716 {
1717 vassalForm.ReturnTopScriptsButton.Enabled = false;
1718 RegionTeleportGroup.Enabled = false;
1719 }));
1720  
1721 // Enqueue all the UUIDs to return.
1722 Queue<KeyValuePair<UUID, Vector3>> returnUUIDs = new Queue<KeyValuePair<UUID, Vector3>>();
1723 vassalForm.Invoke((MethodInvoker) (() =>
1724 {
1725 foreach (
1726 DataGridViewRow topScriptsRow in
1727 TopScriptsGridView.Rows.AsParallel()
1728 .Cast<DataGridViewRow>()
1729 .Where(o => o.Selected || o.Cells.Cast<DataGridViewCell>().Any(p => p.Selected)))
1730 {
1731 Vector3 objectPosition;
1732 UUID returnUUID;
1733 if (!UUID.TryParse(topScriptsRow.Cells["TopScriptsUUID"].Value.ToString(), out returnUUID) ||
1734 !Vector3.TryParse(topScriptsRow.Cells["TopScriptsPosition"].Value.ToString(), out objectPosition))
1735 continue;
1736 returnUUIDs.Enqueue(new KeyValuePair<UUID, Vector3>(returnUUID, objectPosition));
1737 }
1738 }));
1739  
1740 // If no rows were selected, enable teleports, the return button and return.
1741 if (returnUUIDs.Count.Equals(0))
1742 {
1743 vassalForm.Invoke((MethodInvoker)(() =>
1744 {
1745 vassalForm.ReturnTopScriptsButton.Enabled = true;
1746 RegionTeleportGroup.Enabled = true;
1747 }));
1748 return;
1749 }
1750  
1751 new Thread(() =>
1752 {
1753 Monitor.Enter(ClientInstanceTeleportLock);
1754  
1755 try
1756 {
1757 vassalForm.Invoke((MethodInvoker) (() =>
1758 {
1759 vassalForm.StatusProgress.Value = 0;
1760 }));
1761 int totalObjects = returnUUIDs.Count;
1762 do
1763 {
1764 // Dequeue the first object.
1765 KeyValuePair<UUID, Vector3> objectData = returnUUIDs.Dequeue();
1766  
1767 vassalForm.Invoke((MethodInvoker) (() =>
1768 {
1769 vassalForm.StatusText.Text = @"Returning object UUID: " + objectData.Key.ToString();
1770 }));
1771  
1772 string currentRegionName = string.Empty;
1773 vassalForm.Invoke((MethodInvoker) (() =>
1774 {
1775 currentRegionName = CurrentRegionName.Text;
1776 }));
1777  
1778 // Teleport to the object.
1779 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1780 wasKeyValueEscape(new Dictionary<string, string>
1781 {
1782 {"command", "teleport"},
1783 {"group", vassalConfiguration.Group},
1784 {"password", vassalConfiguration.Password},
1785 {"position", objectData.Value.ToString()},
1786 {"region", currentRegionName},
1787 {"fly", "True"}
1788 }), vassalConfiguration.TeleportTimeout);
1789  
1790 if (string.IsNullOrEmpty(result))
1791 {
1792 vassalForm.Invoke((MethodInvoker)(() =>
1793 {
1794 vassalForm.StatusText.Text = @"Error communicating with Corrade.";
1795 }));
1796 continue;
1797 }
1798  
1799 // Return the object.
1800 result = wasPOST(vassalConfiguration.HTTPServerURL,
1801 wasKeyValueEscape(new Dictionary<string, string>
1802 {
1803 {"command", "derez"},
1804 {"group", vassalConfiguration.Group},
1805 {"password", vassalConfiguration.Password},
1806 {"item", objectData.Key.ToString()},
1807 {"range", "32" }, // maximal prim size = 64 - middle bounding box at half
1808 {"type", "ReturnToOwner"}
1809 }), vassalConfiguration.DataTimeout);
1810  
1811 if (string.IsNullOrEmpty(result))
1812 {
1813 vassalForm.Invoke((MethodInvoker) (() =>
1814 {
1815 vassalForm.StatusText.Text = @"Error communicating with Corrade.";
1816 }));
1817 continue;
1818 }
1819  
1820 bool success;
1821 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1822 {
1823 vassalForm.Invoke((MethodInvoker) (() =>
1824 {
1825 vassalForm.StatusText.Text = @"No success status could be retrieved. ";
1826 }));
1827 continue;
1828 }
1829  
1830 switch (success)
1831 {
1832 case true:
1833 vassalForm.Invoke((MethodInvoker) (() =>
1834 {
1835 vassalForm.StatusText.Text = @"Returned object: " + objectData.Key.ToString();
1836 // Remove the row from the grid view.
1837 DataGridViewRow row =
1838 TopScriptsGridView.Rows.AsParallel()
1839 .Cast<DataGridViewRow>()
1840 .FirstOrDefault(
1841 o => o.Cells["TopScriptsUUID"].Value.Equals(objectData.Key.ToString()));
1842 if (row == null) return;
1843 int i = row.Index;
1844 TopScriptsGridView.Rows.RemoveAt(i);
1845 }));
1846 break;
1847 case false:
1848 vassalForm.Invoke((MethodInvoker) (() =>
1849 {
1850 vassalForm.StatusText.Text = @"Could not return object " + objectData.Key.ToString() +
1851 @": " +
1852 wasInput(wasKeyValueGet("error", result));
1853 }));
1854 break;
1855 }
1856  
1857 vassalForm.Invoke((MethodInvoker) (() =>
1858 {
1859 vassalForm.StatusProgress.Value =
1860 Math.Min((int) (Math.Abs(returnUUIDs.Count - totalObjects)/
1861 (double) totalObjects), 100);
1862 }));
1863 } while (!returnUUIDs.Count.Equals(0));
1864 vassalForm.Invoke((MethodInvoker) (() =>
1865 {
1866 vassalForm.StatusProgress.Value = 100;
1867 }));
1868 }
1869 catch (Exception ex)
1870 {
1871 vassalForm.Invoke((MethodInvoker)(() =>
1872 {
1873 vassalForm.StatusText.Text = @"Unexpected error: " + ex.Message;
1874 }));
1875 }
1876 finally
1877 {
1878 Monitor.Exit(ClientInstanceTeleportLock);
1879 // Allow teleports and enable button.
1880 vassalForm.BeginInvoke((MethodInvoker)(() =>
1881 {
1882 vassalForm.ReturnTopScriptsButton.Enabled = true;
1883 RegionTeleportGroup.Enabled = true;
1884 }));
1885 }
1886 })
1887 {IsBackground = true}.Start();
1888 }
1889  
1890 private void RequestReturnTopCollidersObjects(object sender, EventArgs e)
1891 {
1892 // Block teleports and disable button.
1893 vassalForm.Invoke((MethodInvoker)(() =>
1894 {
1895 vassalForm.ReturnTopCollidersButton.Enabled = false;
1896 RegionTeleportGroup.Enabled = false;
1897 }));
1898  
1899 // Enqueue all the UUIDs to return.
1900 Queue<KeyValuePair<UUID, Vector3>> returnUUIDs = new Queue<KeyValuePair<UUID, Vector3>>();
1901 vassalForm.Invoke((MethodInvoker)(() =>
1902 {
1903 foreach (
1904 DataGridViewRow topCollidersRow in
1905 TopCollidersGridView.Rows.AsParallel()
1906 .Cast<DataGridViewRow>()
1907 .Where(o => o.Selected || o.Cells.Cast<DataGridViewCell>().Any(p => p.Selected)))
1908 {
1909 Vector3 objectPosition;
1910 UUID returnUUID;
1911 if (!UUID.TryParse(topCollidersRow.Cells["TopCollidersUUID"].Value.ToString(), out returnUUID) ||
1912 !Vector3.TryParse(topCollidersRow.Cells["TopCollidersPosition"].Value.ToString(), out objectPosition))
1913 continue;
1914 returnUUIDs.Enqueue(new KeyValuePair<UUID, Vector3>(returnUUID, objectPosition));
1915 }
1916 }));
1917  
1918 // If no rows were selected, enable teleports, the return button and return.
1919 if (returnUUIDs.Count.Equals(0))
1920 {
1921 vassalForm.Invoke((MethodInvoker)(() =>
1922 {
1923 vassalForm.ReturnTopCollidersButton.Enabled = true;
1924 RegionTeleportGroup.Enabled = true;
1925 }));
1926 return;
1927 }
1928  
1929 new Thread(() =>
1930 {
1931 Monitor.Enter(ClientInstanceTeleportLock);
1932  
1933 try
1934 {
1935 vassalForm.Invoke((MethodInvoker)(() =>
1936 {
1937 vassalForm.StatusProgress.Value = 0;
1938 }));
1939 int totalObjects = returnUUIDs.Count;
1940 do
1941 {
1942 // Dequeue the first object.
1943 KeyValuePair<UUID, Vector3> objectData = returnUUIDs.Dequeue();
1944  
1945 vassalForm.Invoke((MethodInvoker)(() =>
1946 {
1947 vassalForm.StatusText.Text = @"Returning UUID: " + objectData.Key.ToString();
1948 }));
1949  
1950 string currentRegionName = string.Empty;
1951 vassalForm.Invoke((MethodInvoker)(() =>
1952 {
1953 currentRegionName = CurrentRegionName.Text;
1954 }));
1955  
1956 // Teleport to the object.
1957 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1958 wasKeyValueEscape(new Dictionary<string, string>
1959 {
1960 {"command", "teleport"},
1961 {"group", vassalConfiguration.Group},
1962 {"password", vassalConfiguration.Password},
1963 {"position", objectData.Value.ToString()},
1964 {"region", currentRegionName},
1965 {"fly", "True"}
1966 }), vassalConfiguration.DataTimeout);
1967  
1968 if (string.IsNullOrEmpty(result))
1969 {
1970 vassalForm.Invoke((MethodInvoker)(() =>
1971 {
1972 vassalForm.StatusText.Text = @"Error communicating with Corrade.";
1973 }));
1974 continue;
1975 }
1976  
1977 // Return the object.
1978 result = wasPOST(vassalConfiguration.HTTPServerURL,
1979 wasKeyValueEscape(new Dictionary<string, string>
1980 {
1981 {"command", "derez"},
1982 {"group", vassalConfiguration.Group},
1983 {"password", vassalConfiguration.Password},
1984 {"item", objectData.Key.ToString()},
1985 {"range", "32" }, // maximal prim size = 64 - middle bounding box at half
1986 {"type", "ReturnToOwner"}
1987 }), vassalConfiguration.DataTimeout);
1988  
1989 if (string.IsNullOrEmpty(result))
1990 {
1991 vassalForm.Invoke((MethodInvoker)(() =>
1992 {
1993 vassalForm.StatusText.Text = @"Error communicating with Corrade.";
1994 }));
1995 continue;
1996 }
1997  
1998 bool success;
1999 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2000 {
2001 vassalForm.Invoke((MethodInvoker)(() =>
2002 {
2003 vassalForm.StatusText.Text = @"No success status could be retrieved. ";
2004 }));
2005 continue;
2006 }
2007  
2008 switch (success)
2009 {
2010 case true:
2011 vassalForm.Invoke((MethodInvoker)(() =>
2012 {
2013 vassalForm.StatusText.Text = @"Returned object: " + objectData.Key.ToString();
2014 // Remove the row from the grid view.
2015 DataGridViewRow row =
2016 TopCollidersGridView.Rows.AsParallel()
2017 .Cast<DataGridViewRow>()
2018 .FirstOrDefault(
2019 o => o.Cells["TopCollidersUUID"].Value.Equals(objectData.Key.ToString()));
2020 if (row == null) return;
2021 int i = row.Index;
2022 TopCollidersGridView.Rows.RemoveAt(i);
2023 }));
2024 break;
2025 case false:
2026 vassalForm.Invoke((MethodInvoker)(() =>
2027 {
2028 vassalForm.StatusText.Text = @"Could not return object " + objectData.Key.ToString() +
2029 @": " +
2030 wasInput(wasKeyValueGet("error", result));
2031 }));
2032 break;
2033 }
2034  
2035 vassalForm.Invoke((MethodInvoker)(() =>
2036 {
2037 vassalForm.StatusProgress.Value =
2038 Math.Min((int)(Math.Abs(returnUUIDs.Count - totalObjects) /
2039 (double)totalObjects), 100);
2040 }));
2041 } while (!returnUUIDs.Count.Equals(0));
2042 vassalForm.Invoke((MethodInvoker)(() =>
2043 {
2044 vassalForm.StatusProgress.Value = 100;
2045 }));
2046 }
2047 catch (Exception ex)
2048 {
2049 vassalForm.Invoke((MethodInvoker)(() =>
2050 {
2051 vassalForm.StatusText.Text = @"Unexpected error: " + ex.Message;
2052 }));
2053 }
2054 finally
2055 {
2056 Monitor.Exit(ClientInstanceTeleportLock);
2057 // Allow teleports and enable button.
2058 vassalForm.BeginInvoke((MethodInvoker)(() =>
2059 {
2060 vassalForm.ReturnTopScriptsButton.Enabled = true;
2061 RegionTeleportGroup.Enabled = true;
2062 }));
2063 }
2064 })
2065 { IsBackground = true }.Start();
2066 }
2067  
2068 private void RequestBatchRestart(object sender, EventArgs e)
2069 {
2070  
2071 }
2 zed 2072 }
2073 }