corrade-vassal – Blame information for rev 5

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