corrade-vassal – Blame information for rev 3

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  
2 zed 1140 // Get the configuration file settings if it exists.
1141 if (File.Exists(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE))
1142 {
1143 VassalConfiguration.Load(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE, ref vassalConfiguration);
1144 }
1145  
1146 // Get all the regions if they exist.
1147 if (File.Exists(VASSAL_CONSTANTS.VASSAL_REGIONS))
1148 {
1149 Vector3 localPosition;
3 eva 1150 List<KeyValuePair<string, Vector3>> ConfiguredRegions = new List<KeyValuePair<string, Vector3>>(
2 zed 1151 File.ReadAllLines(VASSAL_CONSTANTS.VASSAL_REGIONS)
1152 .Select(o => new List<string>(wasCSVToEnumerable(o)))
1153 .Where(o => o.Count == 2)
1154 .ToDictionary(o => o.First(),
1155 p =>
1156 Vector3.TryParse(p.Last(), out localPosition)
1157 ? localPosition
3 eva 1158 : Vector3.Zero).OrderBy(o => o.Key).ToDictionary(o => o.Key, o => o.Value));
1159 // Populate the loaded regions.
2 zed 1160 LoadedRegions.Items.Clear();
1161 LoadedRegions.Items.AddRange(
3 eva 1162 ConfiguredRegions.Select(o => (object) new ListViewItem {Text = o.Key, Tag = o.Value})
2 zed 1163 .ToArray());
3 eva 1164 // Populate the batch restart grid view.
1165 BatchRestartGridView.Rows.Clear();
1166 foreach (KeyValuePair<string, Vector3> data in ConfiguredRegions)
1167 {
1168 BatchRestartGridView.Rows.Add(data.Key, data.Value.ToString());
1169 }
2 zed 1170 }
1171  
1172 // Set the map image to the loading spinner.
1173 Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
1174 System.IO.Stream file =
1175 thisAssembly.GetManifestResourceStream("Vassal.img.loading.gif");
1176 switch (file != null)
1177 {
1178 case true:
3 eva 1179 vassalForm.Invoke((MethodInvoker) (() =>
2 zed 1180 {
1181 RegionAvatarsMap.SizeMode = PictureBoxSizeMode.CenterImage;
1182 RegionAvatarsMap.Image = Image.FromStream(file);
1183 RegionAvatarsMap.Refresh();
1184 }));
1185 break;
1186 }
1187  
1188 // Start the overview timer.
1189 overviewTabTimer.Elapsed += (o, p) =>
1190 {
1191 if (!Monitor.TryEnter(ClientInstanceTeleportLock))
1192 return;
1193  
1194 try
1195 {
1196 // Do not do anything in case the tab is not selected.
1197 vassalForm.Invoke((MethodInvoker) (() =>
1198 {
1199 if (!Tabs.SelectedTab.Equals(OverviewTab))
1200 throw new Exception();
1201 }));
1202  
1203 // Get the statistics.
1204 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1205 wasKeyValueEscape(new Dictionary<string, string>
1206 {
1207 {"command", "getregiondata"},
1208 {"group", vassalConfiguration.Group},
1209 {"password", vassalConfiguration.Password},
1210 {
1211 "data", wasEnumerableToCSV(new[]
1212 {
1213 "Agents",
1214 "LastLag",
1215 "Dilation",
1216 "FPS",
1217 "PhysicsFPS",
1218 "ActiveScripts",
1219 "ScriptTime",
3 eva 1220 "Objects",
1221 "Name"
2 zed 1222 })
1223 }
1224 }), vassalConfiguration.DataTimeout);
1225  
1226 bool success;
1227 if (string.IsNullOrEmpty(result) ||
1228 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1229 throw new Exception();
1230  
1231 List<string> data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
1232 if (data.Count.Equals(0))
1233 throw new Exception();
1234  
1235 vassalForm.Invoke((MethodInvoker) (() =>
1236 {
3 eva 1237 // Show the region name.
1238 vassalForm.CurrentRegionName.Text = data[data.IndexOf("Name") + 1];
1239 vassalForm.CurrentRegionAt.Visible = true;
1240 vassalForm.CurrentRegionName.Visible = true;
1241  
1242 // Populate the overview tab.
2 zed 1243 Agents.Text = data[data.IndexOf("Agents") + 1];
3 eva 1244 Agents.Enabled = true;
2 zed 1245 LastLag.Text = data[data.IndexOf("LastLag") + 1];
3 eva 1246 LastLag.Enabled = true;
2 zed 1247 Dilation.Text = data[data.IndexOf("Dilation") + 1];
3 eva 1248 Dilation.Enabled = true;
2 zed 1249 FPS.Text = data[data.IndexOf("FPS") + 1];
3 eva 1250 FPS.Enabled = true;
2 zed 1251 PhysicsFPS.Text = data[data.IndexOf("PhysicsFPS") + 1];
3 eva 1252 PhysicsFPS.Enabled = true;
2 zed 1253 ActiveScripts.Text = data[data.IndexOf("ActiveScripts") + 1];
3 eva 1254 ActiveScripts.Enabled = true;
2 zed 1255 ScriptTime.Text = data[data.IndexOf("ScriptTime") + 1];
3 eva 1256 ScriptTime.Enabled = true;
2 zed 1257 Objects.Text = data[data.IndexOf("Objects") + 1];
3 eva 1258 Objects.Enabled = true;
2 zed 1259 }));
3 eva 1260  
2 zed 1261 // Get the map image.
1262 result = wasPOST(vassalConfiguration.HTTPServerURL,
1263 wasKeyValueEscape(new Dictionary<string, string>
1264 {
1265 {"command", "getgridregiondata"},
1266 {"group", vassalConfiguration.Group},
1267 {"password", vassalConfiguration.Password},
3 eva 1268 {"data", "MapImageID"}
2 zed 1269 }), vassalConfiguration.DataTimeout);
3 eva 1270  
2 zed 1271 if (string.IsNullOrEmpty(result) ||
1272 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1273 throw new Exception();
1274  
1275 data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
1276 if (!data.Count.Equals(2))
1277 throw new Exception();
1278 result = wasPOST(vassalConfiguration.HTTPServerURL,
1279 wasKeyValueEscape(new Dictionary<string, string>
1280 {
1281 {"command", "download"},
1282 {"group", vassalConfiguration.Group},
1283 {"password", vassalConfiguration.Password},
3 eva 1284 {"item", data.Last()},
1285 {"type", "Texture"},
1286 {"format", "Jpeg"},
2 zed 1287 }), vassalConfiguration.DataTimeout);
1288 if (string.IsNullOrEmpty(result) ||
1289 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1290 throw new Exception();
1291 byte[] mapImageBytes = Convert.FromBase64String(wasInput(wasKeyValueGet("data", result)));
1292 Image mapImage;
1293 using (MemoryStream memoryStream = new MemoryStream(mapImageBytes, 0, mapImageBytes.Length))
1294 {
1295 mapImage = Image.FromStream(memoryStream);
1296 }
1297  
1298 // Get the avatar positions.
1299 result = wasPOST(vassalConfiguration.HTTPServerURL,
1300 wasKeyValueEscape(new Dictionary<string, string>
1301 {
1302 {"command", "getavatarpositions"},
1303 {"group", vassalConfiguration.Group},
1304 {"password", vassalConfiguration.Password},
3 eva 1305 {"entity", "region"}
2 zed 1306 }), vassalConfiguration.DataTimeout);
1307  
1308 if (string.IsNullOrEmpty(result) ||
1309 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1310 throw new Exception();
1311  
1312 // Every thrid element represents a Vecto3 of the avatar position.
3 eva 1313 data =
1314 wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
1315 .Skip(2)
1316 .Where((x, i) => i%3 == 0)
1317 .ToList();
2 zed 1318  
1319 // Draw the avatars onto the map.
1320 Graphics mapGraphics = Graphics.FromImage(mapImage);
1321 Vector3 position;
1322 foreach (string vector in data)
1323 {
1324 switch (Vector3.TryParse(vector, out position))
1325 {
1326 case true:
3 eva 1327 mapGraphics.FillEllipse(Brushes.Chartreuse,
1328 new Rectangle((int) position.X, (int) position.Y, 4, 4));
2 zed 1329 break;
1330 }
1331 }
1332 mapGraphics.DrawImage(mapImage, new Point(0, 0));
1333  
1334 vassalForm.Invoke((MethodInvoker) (() =>
1335 {
1336 RegionAvatarsMap.SizeMode = PictureBoxSizeMode.StretchImage;
1337 RegionAvatarsMap.Image = mapImage;
1338 RegionAvatarsMap.Refresh();
1339 }));
1340 }
1341 catch (Exception)
1342 {
1343  
1344 }
1345 finally
1346 {
1347 Monitor.Exit(ClientInstanceTeleportLock);
1348 }
3 eva 1349  
2 zed 1350 };
1351 overviewTabTimer.Start();
1352  
1353 // Start the top scores timer.
1354 topScriptsTabTimer.Elapsed += (o, p) =>
1355 {
1356 if (!Monitor.TryEnter(ClientInstanceTeleportLock))
1357 return;
1358  
1359 try
1360 {
1361 // Do not do anything in case the tab is not selected.
1362 vassalForm.Invoke((MethodInvoker) (() =>
1363 {
1364 if (!Tabs.SelectedTab.Equals(TopScriptsTab))
1365 throw new Exception();
1366 }));
1367  
1368 // Get the statistics.
1369 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1370 wasKeyValueEscape(new Dictionary<string, string>
1371 {
1372 {"command", "getregiontop"},
1373 {"group", vassalConfiguration.Group},
1374 {"password", vassalConfiguration.Password},
1375 {"type", "scripts"}
1376 }), vassalConfiguration.DataTimeout);
1377  
1378 bool success;
1379 if (string.IsNullOrEmpty(result) ||
1380 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1381 throw new Exception();
1382  
1383 HashSet<List<string>> data =
1384 new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
1385 .Select((x, i) => new {Index = i, Value = x})
1386 .GroupBy(x => x.Index/5)
1387 .Select(x => x.Select(v => v.Value).ToList()));
1388 if (data.Count.Equals(0))
1389 throw new Exception();
1390  
1391 vassalForm.Invoke((MethodInvoker) (() =>
1392 {
3 eva 1393 // Remove rows that are not in the data update.
1394 foreach (
1395 int index in
1396 TopScriptsGridView.Rows.AsParallel().Cast<DataGridViewRow>()
1397 .Where(
1398 topScriptsRow =>
1399 !data.Any(q => q[2].Equals(topScriptsRow.Cells["TopScriptsUUID"].Value)))
1400 .Select(q => q.Index))
2 zed 1401 {
3 eva 1402 TopScriptsGridView.Rows.RemoveAt(index);
2 zed 1403 }
3 eva 1404 // Now update or add new data.
1405 foreach (List<string> dataComponents in data.Where(q => q.Count.Equals(5)))
1406 {
1407 DataGridViewRow row =
1408 TopScriptsGridView.Rows.AsParallel()
1409 .Cast<DataGridViewRow>()
1410 .FirstOrDefault(q => q.Cells["TopScriptsUUID"].Value.Equals(dataComponents[2]));
1411 switch (row != null)
1412 {
1413 case true: // the row exists, so update it.
1414 row.Cells["TopScriptsScore"].Value = dataComponents[0];
1415 row.Cells["TopScriptsTaskName"].Value = dataComponents[1];
1416 row.Cells["TopScriptsUUID"].Value = dataComponents[2];
1417 row.Cells["TopScriptsOwner"].Value = dataComponents[3];
1418 row.Cells["TopScriptsPosition"].Value = dataComponents[4];
1419 break;
1420 case false: // the row dosn't exist, so add it.
1421 TopScriptsGridView.Rows.Add(dataComponents[0], dataComponents[1], dataComponents[2],
1422 dataComponents[3], dataComponents[4]);
1423 break;
1424 }
1425 }
2 zed 1426 }));
1427 }
1428 catch (Exception)
1429 {
1430  
1431 }
1432 finally
1433 {
1434 Monitor.Exit(ClientInstanceTeleportLock);
1435 }
1436 };
1437 topScriptsTabTimer.Start();
1438  
1439 // Start the top colliders timer.
1440 topCollidersTabTimer.Elapsed += (o, p) =>
1441 {
1442 if (!Monitor.TryEnter(ClientInstanceTeleportLock))
1443 return;
1444  
1445 try
1446 {
1447 // Do not do anything in case the tab is not selected.
3 eva 1448 vassalForm.Invoke((MethodInvoker) (() =>
2 zed 1449 {
1450 if (!Tabs.SelectedTab.Equals(TopCollidersTab))
1451 throw new Exception();
1452 }));
1453  
1454 // Get the statistics.
1455 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1456 wasKeyValueEscape(new Dictionary<string, string>
1457 {
1458 {"command", "getregiontop"},
1459 {"group", vassalConfiguration.Group},
1460 {"password", vassalConfiguration.Password},
1461 {"type", "colliders"}
1462 }), vassalConfiguration.DataTimeout);
1463  
1464 bool success;
1465 if (string.IsNullOrEmpty(result) ||
1466 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1467 throw new Exception();
1468  
1469 HashSet<List<string>> data =
1470 new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
3 eva 1471 .Select((x, i) => new {Index = i, Value = x})
1472 .GroupBy(x => x.Index/5)
2 zed 1473 .Select(x => x.Select(v => v.Value).ToList()));
1474 if (data.Count.Equals(0))
1475 throw new Exception();
1476  
3 eva 1477 vassalForm.Invoke((MethodInvoker) (() =>
2 zed 1478 {
3 eva 1479 // Remove rows that are not in the data update.
1480 foreach (
1481 int index in
1482 TopCollidersGridView.Rows.AsParallel().Cast<DataGridViewRow>()
1483 .Where(
1484 topCollidersRow =>
1485 !data.Any(q => q[2].Equals(topCollidersRow.Cells["TopCollidersUUID"].Value)))
1486 .Select(q => q.Index))
2 zed 1487 {
3 eva 1488 TopCollidersGridView.Rows.RemoveAt(index);
2 zed 1489 }
3 eva 1490 // Now update or add new data.
1491 foreach (List<string> dataComponents in data.Where(q => q.Count.Equals(5)))
1492 {
1493 DataGridViewRow row =
1494 TopCollidersGridView.Rows.AsParallel()
1495 .Cast<DataGridViewRow>()
1496 .FirstOrDefault(q => q.Cells["TopCollidersUUID"].Value.Equals(dataComponents[2]));
1497 switch (row != null)
1498 {
1499 case true: // the row exists, so update it.
1500 row.Cells["TopCollidersScore"].Value = dataComponents[0];
1501 row.Cells["TopSCollidersTaskName"].Value = dataComponents[1];
1502 row.Cells["TopCollidersUUID"].Value = dataComponents[2];
1503 row.Cells["TopCollidersOwner"].Value = dataComponents[3];
1504 row.Cells["TopCollidersPosition"].Value = dataComponents[4];
1505 break;
1506 case false: // the row dosn't exist, so add it.
1507 TopCollidersGridView.Rows.Add(dataComponents[0], dataComponents[1], dataComponents[2],
1508 dataComponents[3], dataComponents[4]);
1509 break;
1510 }
1511 }
2 zed 1512  
1513 }));
1514 }
1515 catch (Exception)
1516 {
1517  
1518 }
1519 finally
1520 {
1521 Monitor.Exit(ClientInstanceTeleportLock);
1522 }
1523 };
1524 topCollidersTabTimer.Start();
1525 }
1526  
1527 private void RequestedEditRegions(object sender, EventArgs e)
1528 {
1529 // Clear any selection.
1530 LoadedRegions.SelectedIndex = -1;
3 eva 1531 RegionEditForm regionEditForm = new RegionEditForm {TopMost = true};
2 zed 1532 regionEditForm.Show();
1533 }
1534  
1535 private void RequestSelecting(object sender, TabControlCancelEventArgs e)
1536 {
1537 e.Cancel = !e.TabPage.Enabled;
1538 }
3 eva 1539  
1540 private void RequestExportTopScripts(object sender, EventArgs e)
1541 {
1542 vassalForm.BeginInvoke((MethodInvoker) (() =>
1543 {
1544 switch (vassalForm.ExportCSVDialog.ShowDialog())
1545 {
1546 case DialogResult.OK:
1547 string file = vassalForm.ExportCSVDialog.FileName;
1548 new Thread(() =>
1549 {
1550 vassalForm.BeginInvoke((MethodInvoker) (() =>
1551 {
1552 try
1553 {
1554 vassalForm.StatusText.Text = @"exporting...";
1555 vassalForm.StatusProgress.Value = 0;
1556  
1557 using (StreamWriter streamWriter = new StreamWriter(file, false, Encoding.UTF8))
1558 {
1559 foreach (DataGridViewRow topScriptsRow in TopScriptsGridView.Rows)
1560 {
1561 streamWriter.WriteLine(wasEnumerableToCSV(new[]
1562 {
1563 topScriptsRow.Cells["TopScriptsScore"].Value.ToString(),
1564 topScriptsRow.Cells["TopScriptsTaskName"].Value.ToString(),
1565 topScriptsRow.Cells["TopScriptsUUID"].Value.ToString(),
1566 topScriptsRow.Cells["TopScriptsOwner"].Value.ToString(),
1567 topScriptsRow.Cells["TopScriptsPosition"].Value.ToString()
1568 }));
1569 }
1570 }
1571  
1572 vassalForm.StatusText.Text = @"exported";
1573 vassalForm.StatusProgress.Value = 100;
1574 }
1575 catch (Exception ex)
1576 {
1577 vassalForm.StatusText.Text = ex.Message;
1578 }
1579 }));
1580 })
1581 {IsBackground = true, Priority = ThreadPriority.Normal}.Start();
1582 break;
1583 }
1584 }));
1585 }
1586  
1587 private void RequestExportTopColliders(object sender, EventArgs e)
1588 {
1589 vassalForm.BeginInvoke((MethodInvoker) (() =>
1590 {
1591 switch (vassalForm.ExportCSVDialog.ShowDialog())
1592 {
1593 case DialogResult.OK:
1594 string file = vassalForm.ExportCSVDialog.FileName;
1595 new Thread(() =>
1596 {
1597 vassalForm.BeginInvoke((MethodInvoker) (() =>
1598 {
1599 try
1600 {
1601 vassalForm.StatusText.Text = @"exporting...";
1602 vassalForm.StatusProgress.Value = 0;
1603  
1604 using (StreamWriter streamWriter = new StreamWriter(file, false, Encoding.UTF8))
1605 {
1606 foreach (DataGridViewRow topCollidersRow in TopCollidersGridView.Rows)
1607 {
1608 streamWriter.WriteLine(wasEnumerableToCSV(new[]
1609 {
1610 topCollidersRow.Cells["TopCollidersScore"].Value.ToString(),
1611 topCollidersRow.Cells["TopCollidersTaskName"].Value.ToString(),
1612 topCollidersRow.Cells["TopCollidersUUID"].Value.ToString(),
1613 topCollidersRow.Cells["TopCollidersOwner"].Value.ToString(),
1614 topCollidersRow.Cells["TopCollidersPosition"].Value.ToString()
1615 }));
1616 }
1617 }
1618  
1619 vassalForm.StatusText.Text = @"exported";
1620 vassalForm.StatusProgress.Value = 100;
1621 }
1622 catch (Exception ex)
1623 {
1624 vassalForm.StatusText.Text = ex.Message;
1625 }
1626 }));
1627 })
1628 {IsBackground = true, Priority = ThreadPriority.Normal}.Start();
1629 break;
1630 }
1631 }));
1632 }
1633  
1634 private void RequestFilterTopScripts(object sender, EventArgs e)
1635 {
1636 vassalForm.BeginInvoke((MethodInvoker) (() =>
1637 {
1638 Regex topScriptsRowRegex;
1639 switch (!string.IsNullOrEmpty(TopScriptsFilter.Text))
1640 {
1641 case true:
1642 topScriptsRowRegex = new Regex(TopScriptsFilter.Text, RegexOptions.Compiled);
1643 break;
1644 default:
1645 topScriptsRowRegex = new Regex(@".+?", RegexOptions.Compiled);
1646 break;
1647 }
1648 foreach (DataGridViewRow topScriptsRow in TopScriptsGridView.Rows.AsParallel().Cast<DataGridViewRow>())
1649 {
1650 topScriptsRow.Visible =
1651 topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsScore"].Value.ToString()) ||
1652 topScriptsRowRegex.IsMatch(
1653 topScriptsRow.Cells["TopScriptsTaskName"].Value.ToString()) ||
1654 topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsUUID"].Value.ToString()) ||
1655 topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsOwner"].Value.ToString()) ||
1656 topScriptsRowRegex.IsMatch(
1657 topScriptsRow.Cells["TopScriptsPosition"].Value.ToString());
1658 }
1659 }));
1660 }
1661  
1662 private void RequestFilterTopColliders(object sender, EventArgs e)
1663 {
1664 vassalForm.BeginInvoke((MethodInvoker)(() =>
1665 {
1666 Regex topCollidersRowRegex;
1667 switch (!string.IsNullOrEmpty(TopScriptsFilter.Text))
1668 {
1669 case true:
1670 topCollidersRowRegex = new Regex(TopScriptsFilter.Text, RegexOptions.Compiled);
1671 break;
1672 default:
1673 topCollidersRowRegex = new Regex(@".+?", RegexOptions.Compiled);
1674 break;
1675 }
1676 foreach (DataGridViewRow topCollidersRow in TopCollidersGridView.Rows.AsParallel().Cast<DataGridViewRow>())
1677 {
1678 topCollidersRow.Visible =
1679 topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersScore"].Value.ToString()) ||
1680 topCollidersRowRegex.IsMatch(
1681 topCollidersRow.Cells["TopCollidersTaskName"].Value.ToString()) ||
1682 topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersUUID"].Value.ToString()) ||
1683 topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersOwner"].Value.ToString()) ||
1684 topCollidersRowRegex.IsMatch(
1685 topCollidersRow.Cells["TopCollidersPosition"].Value.ToString());
1686 }
1687 }));
1688 }
1689  
1690 private void RequestReturnTopScriptsObjects(object sender, EventArgs e)
1691 {
1692 // Block teleports and disable button.
1693 vassalForm.Invoke((MethodInvoker) (() =>
1694 {
1695 vassalForm.ReturnTopScriptsButton.Enabled = false;
1696 RegionTeleportGroup.Enabled = false;
1697 }));
1698  
1699 // Enqueue all the UUIDs to return.
1700 Queue<KeyValuePair<UUID, Vector3>> returnUUIDs = new Queue<KeyValuePair<UUID, Vector3>>();
1701 vassalForm.Invoke((MethodInvoker) (() =>
1702 {
1703 foreach (
1704 DataGridViewRow topScriptsRow in
1705 TopScriptsGridView.Rows.AsParallel()
1706 .Cast<DataGridViewRow>()
1707 .Where(o => o.Selected || o.Cells.Cast<DataGridViewCell>().Any(p => p.Selected)))
1708 {
1709 Vector3 objectPosition;
1710 UUID returnUUID;
1711 if (!UUID.TryParse(topScriptsRow.Cells["TopScriptsUUID"].Value.ToString(), out returnUUID) ||
1712 !Vector3.TryParse(topScriptsRow.Cells["TopScriptsPosition"].Value.ToString(), out objectPosition))
1713 continue;
1714 returnUUIDs.Enqueue(new KeyValuePair<UUID, Vector3>(returnUUID, objectPosition));
1715 }
1716 }));
1717  
1718 // If no rows were selected, enable teleports, the return button and return.
1719 if (returnUUIDs.Count.Equals(0))
1720 {
1721 vassalForm.Invoke((MethodInvoker)(() =>
1722 {
1723 vassalForm.ReturnTopScriptsButton.Enabled = true;
1724 RegionTeleportGroup.Enabled = true;
1725 }));
1726 return;
1727 }
1728  
1729 new Thread(() =>
1730 {
1731 Monitor.Enter(ClientInstanceTeleportLock);
1732  
1733 try
1734 {
1735 vassalForm.Invoke((MethodInvoker) (() =>
1736 {
1737 vassalForm.StatusProgress.Value = 0;
1738 }));
1739 int totalObjects = returnUUIDs.Count;
1740 do
1741 {
1742 // Dequeue the first object.
1743 KeyValuePair<UUID, Vector3> objectData = returnUUIDs.Dequeue();
1744  
1745 vassalForm.Invoke((MethodInvoker) (() =>
1746 {
1747 vassalForm.StatusText.Text = @"Returning object UUID: " + objectData.Key.ToString();
1748 }));
1749  
1750 string currentRegionName = string.Empty;
1751 vassalForm.Invoke((MethodInvoker) (() =>
1752 {
1753 currentRegionName = CurrentRegionName.Text;
1754 }));
1755  
1756 // Teleport to the object.
1757 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1758 wasKeyValueEscape(new Dictionary<string, string>
1759 {
1760 {"command", "teleport"},
1761 {"group", vassalConfiguration.Group},
1762 {"password", vassalConfiguration.Password},
1763 {"position", objectData.Value.ToString()},
1764 {"region", currentRegionName},
1765 {"fly", "True"}
1766 }), vassalConfiguration.TeleportTimeout);
1767  
1768 if (string.IsNullOrEmpty(result))
1769 {
1770 vassalForm.Invoke((MethodInvoker)(() =>
1771 {
1772 vassalForm.StatusText.Text = @"Error communicating with Corrade.";
1773 }));
1774 continue;
1775 }
1776  
1777 // Return the object.
1778 result = wasPOST(vassalConfiguration.HTTPServerURL,
1779 wasKeyValueEscape(new Dictionary<string, string>
1780 {
1781 {"command", "derez"},
1782 {"group", vassalConfiguration.Group},
1783 {"password", vassalConfiguration.Password},
1784 {"item", objectData.Key.ToString()},
1785 {"range", "32" }, // maximal prim size = 64 - middle bounding box at half
1786 {"type", "ReturnToOwner"}
1787 }), vassalConfiguration.DataTimeout);
1788  
1789 if (string.IsNullOrEmpty(result))
1790 {
1791 vassalForm.Invoke((MethodInvoker) (() =>
1792 {
1793 vassalForm.StatusText.Text = @"Error communicating with Corrade.";
1794 }));
1795 continue;
1796 }
1797  
1798 bool success;
1799 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1800 {
1801 vassalForm.Invoke((MethodInvoker) (() =>
1802 {
1803 vassalForm.StatusText.Text = @"No success status could be retrieved. ";
1804 }));
1805 continue;
1806 }
1807  
1808 switch (success)
1809 {
1810 case true:
1811 vassalForm.Invoke((MethodInvoker) (() =>
1812 {
1813 vassalForm.StatusText.Text = @"Returned object: " + objectData.Key.ToString();
1814 // Remove the row from the grid view.
1815 DataGridViewRow row =
1816 TopScriptsGridView.Rows.AsParallel()
1817 .Cast<DataGridViewRow>()
1818 .FirstOrDefault(
1819 o => o.Cells["TopScriptsUUID"].Value.Equals(objectData.Key.ToString()));
1820 if (row == null) return;
1821 int i = row.Index;
1822 TopScriptsGridView.Rows.RemoveAt(i);
1823 }));
1824 break;
1825 case false:
1826 vassalForm.Invoke((MethodInvoker) (() =>
1827 {
1828 vassalForm.StatusText.Text = @"Could not return object " + objectData.Key.ToString() +
1829 @": " +
1830 wasInput(wasKeyValueGet("error", result));
1831 }));
1832 break;
1833 }
1834  
1835 vassalForm.Invoke((MethodInvoker) (() =>
1836 {
1837 vassalForm.StatusProgress.Value =
1838 Math.Min((int) (Math.Abs(returnUUIDs.Count - totalObjects)/
1839 (double) totalObjects), 100);
1840 }));
1841 } while (!returnUUIDs.Count.Equals(0));
1842 vassalForm.Invoke((MethodInvoker) (() =>
1843 {
1844 vassalForm.StatusProgress.Value = 100;
1845 }));
1846 }
1847 catch (Exception ex)
1848 {
1849 vassalForm.Invoke((MethodInvoker)(() =>
1850 {
1851 vassalForm.StatusText.Text = @"Unexpected error: " + ex.Message;
1852 }));
1853 }
1854 finally
1855 {
1856 Monitor.Exit(ClientInstanceTeleportLock);
1857 // Allow teleports and enable button.
1858 vassalForm.BeginInvoke((MethodInvoker)(() =>
1859 {
1860 vassalForm.ReturnTopScriptsButton.Enabled = true;
1861 RegionTeleportGroup.Enabled = true;
1862 }));
1863 }
1864 })
1865 {IsBackground = true}.Start();
1866 }
1867  
1868 private void RequestReturnTopCollidersObjects(object sender, EventArgs e)
1869 {
1870 // Block teleports and disable button.
1871 vassalForm.Invoke((MethodInvoker)(() =>
1872 {
1873 vassalForm.ReturnTopCollidersButton.Enabled = false;
1874 RegionTeleportGroup.Enabled = false;
1875 }));
1876  
1877 // Enqueue all the UUIDs to return.
1878 Queue<KeyValuePair<UUID, Vector3>> returnUUIDs = new Queue<KeyValuePair<UUID, Vector3>>();
1879 vassalForm.Invoke((MethodInvoker)(() =>
1880 {
1881 foreach (
1882 DataGridViewRow topCollidersRow in
1883 TopCollidersGridView.Rows.AsParallel()
1884 .Cast<DataGridViewRow>()
1885 .Where(o => o.Selected || o.Cells.Cast<DataGridViewCell>().Any(p => p.Selected)))
1886 {
1887 Vector3 objectPosition;
1888 UUID returnUUID;
1889 if (!UUID.TryParse(topCollidersRow.Cells["TopCollidersUUID"].Value.ToString(), out returnUUID) ||
1890 !Vector3.TryParse(topCollidersRow.Cells["TopCollidersPosition"].Value.ToString(), out objectPosition))
1891 continue;
1892 returnUUIDs.Enqueue(new KeyValuePair<UUID, Vector3>(returnUUID, objectPosition));
1893 }
1894 }));
1895  
1896 // If no rows were selected, enable teleports, the return button and return.
1897 if (returnUUIDs.Count.Equals(0))
1898 {
1899 vassalForm.Invoke((MethodInvoker)(() =>
1900 {
1901 vassalForm.ReturnTopCollidersButton.Enabled = true;
1902 RegionTeleportGroup.Enabled = true;
1903 }));
1904 return;
1905 }
1906  
1907 new Thread(() =>
1908 {
1909 Monitor.Enter(ClientInstanceTeleportLock);
1910  
1911 try
1912 {
1913 vassalForm.Invoke((MethodInvoker)(() =>
1914 {
1915 vassalForm.StatusProgress.Value = 0;
1916 }));
1917 int totalObjects = returnUUIDs.Count;
1918 do
1919 {
1920 // Dequeue the first object.
1921 KeyValuePair<UUID, Vector3> objectData = returnUUIDs.Dequeue();
1922  
1923 vassalForm.Invoke((MethodInvoker)(() =>
1924 {
1925 vassalForm.StatusText.Text = @"Returning UUID: " + objectData.Key.ToString();
1926 }));
1927  
1928 string currentRegionName = string.Empty;
1929 vassalForm.Invoke((MethodInvoker)(() =>
1930 {
1931 currentRegionName = CurrentRegionName.Text;
1932 }));
1933  
1934 // Teleport to the object.
1935 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1936 wasKeyValueEscape(new Dictionary<string, string>
1937 {
1938 {"command", "teleport"},
1939 {"group", vassalConfiguration.Group},
1940 {"password", vassalConfiguration.Password},
1941 {"position", objectData.Value.ToString()},
1942 {"region", currentRegionName},
1943 {"fly", "True"}
1944 }), vassalConfiguration.DataTimeout);
1945  
1946 if (string.IsNullOrEmpty(result))
1947 {
1948 vassalForm.Invoke((MethodInvoker)(() =>
1949 {
1950 vassalForm.StatusText.Text = @"Error communicating with Corrade.";
1951 }));
1952 continue;
1953 }
1954  
1955 // Return the object.
1956 result = wasPOST(vassalConfiguration.HTTPServerURL,
1957 wasKeyValueEscape(new Dictionary<string, string>
1958 {
1959 {"command", "derez"},
1960 {"group", vassalConfiguration.Group},
1961 {"password", vassalConfiguration.Password},
1962 {"item", objectData.Key.ToString()},
1963 {"range", "32" }, // maximal prim size = 64 - middle bounding box at half
1964 {"type", "ReturnToOwner"}
1965 }), vassalConfiguration.DataTimeout);
1966  
1967 if (string.IsNullOrEmpty(result))
1968 {
1969 vassalForm.Invoke((MethodInvoker)(() =>
1970 {
1971 vassalForm.StatusText.Text = @"Error communicating with Corrade.";
1972 }));
1973 continue;
1974 }
1975  
1976 bool success;
1977 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1978 {
1979 vassalForm.Invoke((MethodInvoker)(() =>
1980 {
1981 vassalForm.StatusText.Text = @"No success status could be retrieved. ";
1982 }));
1983 continue;
1984 }
1985  
1986 switch (success)
1987 {
1988 case true:
1989 vassalForm.Invoke((MethodInvoker)(() =>
1990 {
1991 vassalForm.StatusText.Text = @"Returned object: " + objectData.Key.ToString();
1992 // Remove the row from the grid view.
1993 DataGridViewRow row =
1994 TopCollidersGridView.Rows.AsParallel()
1995 .Cast<DataGridViewRow>()
1996 .FirstOrDefault(
1997 o => o.Cells["TopCollidersUUID"].Value.Equals(objectData.Key.ToString()));
1998 if (row == null) return;
1999 int i = row.Index;
2000 TopCollidersGridView.Rows.RemoveAt(i);
2001 }));
2002 break;
2003 case false:
2004 vassalForm.Invoke((MethodInvoker)(() =>
2005 {
2006 vassalForm.StatusText.Text = @"Could not return object " + objectData.Key.ToString() +
2007 @": " +
2008 wasInput(wasKeyValueGet("error", result));
2009 }));
2010 break;
2011 }
2012  
2013 vassalForm.Invoke((MethodInvoker)(() =>
2014 {
2015 vassalForm.StatusProgress.Value =
2016 Math.Min((int)(Math.Abs(returnUUIDs.Count - totalObjects) /
2017 (double)totalObjects), 100);
2018 }));
2019 } while (!returnUUIDs.Count.Equals(0));
2020 vassalForm.Invoke((MethodInvoker)(() =>
2021 {
2022 vassalForm.StatusProgress.Value = 100;
2023 }));
2024 }
2025 catch (Exception ex)
2026 {
2027 vassalForm.Invoke((MethodInvoker)(() =>
2028 {
2029 vassalForm.StatusText.Text = @"Unexpected error: " + ex.Message;
2030 }));
2031 }
2032 finally
2033 {
2034 Monitor.Exit(ClientInstanceTeleportLock);
2035 // Allow teleports and enable button.
2036 vassalForm.BeginInvoke((MethodInvoker)(() =>
2037 {
2038 vassalForm.ReturnTopScriptsButton.Enabled = true;
2039 RegionTeleportGroup.Enabled = true;
2040 }));
2041 }
2042 })
2043 { IsBackground = true }.Start();
2044 }
2045  
2046 private void RequestBatchRestart(object sender, EventArgs e)
2047 {
2048  
2049 }
2 zed 2050 }
2051 }