corrade-vassal – Blame information for rev 2

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 Dictionary<string, Vector3> ConfiguredRegions = new Dictionary<string, Vector3>();
35 public static VassalConfiguration vassalConfiguration = new VassalConfiguration();
36 public static Vassal vassalForm;
37 public static readonly object ClientInstanceTeleportLock = new object();
38  
39 ///////////////////////////////////////////////////////////////////////////
40 // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
41 ///////////////////////////////////////////////////////////////////////////
42 /// <summary>RFC1738 URL Escapes a string</summary>
43 /// <param name="data">a string to escape</param>
44 /// <returns>an RFC1738 escaped string</returns>
45 private static string wasURLEscapeDataString(string data)
46 {
47 return HttpUtility.UrlEncode(data);
48 }
49  
50 ///////////////////////////////////////////////////////////////////////////
51 // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
52 ///////////////////////////////////////////////////////////////////////////
53 /// <summary>RFC1738 URL Unescape a string</summary>
54 /// <param name="data">a string to unescape</param>
55 /// <returns>an RFC1738 unescaped string</returns>
56 private static string wasURLUnescapeDataString(string data)
57 {
58 return HttpUtility.UrlDecode(data);
59 }
60  
61 ///////////////////////////////////////////////////////////////////////////
62 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
63 ///////////////////////////////////////////////////////////////////////////
64 /// <summary>URI unescapes an RFC3986 URI escaped string</summary>
65 /// <param name="data">a string to unescape</param>
66 /// <returns>the resulting string</returns>
67 private static string wasURIUnescapeDataString(string data)
68 {
69 // Uri.UnescapeDataString can only handle 32766 characters at a time
70 return string.Join("", Enumerable.Range(0, (data.Length + 32765)/32766)
71 .Select(o => Uri.UnescapeDataString(data.Substring(o*32766, Math.Min(32766, data.Length - (o*32766)))))
72 .ToArray());
73 }
74  
75 ///////////////////////////////////////////////////////////////////////////
76 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
77 ///////////////////////////////////////////////////////////////////////////
78 /// <summary>RFC3986 URI Escapes a string</summary>
79 /// <param name="data">a string to escape</param>
80 /// <returns>an RFC3986 escaped string</returns>
81 private static string wasURIEscapeDataString(string data)
82 {
83 // Uri.EscapeDataString can only handle 32766 characters at a time
84 return string.Join("", Enumerable.Range(0, (data.Length + 32765)/32766)
85 .Select(o => Uri.EscapeDataString(data.Substring(o*32766, Math.Min(32766, data.Length - (o*32766)))))
86 .ToArray());
87 }
88  
89 ///////////////////////////////////////////////////////////////////////////
90 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
91 ///////////////////////////////////////////////////////////////////////////
92 /// <summary>
93 /// Gets an array element at a given modulo index.
94 /// </summary>
95 /// <typeparam name="T">the array type</typeparam>
96 /// <param name="index">a positive or negative index of the element</param>
97 /// <param name="data">the array</param>
98 /// <return>an array element</return>
99 public static T wasGetElementAt<T>(T[] data, int index)
100 {
101 switch (index < 0)
102 {
103 case true:
104 return data[((index%data.Length) + data.Length)%data.Length];
105 default:
106 return data[index%data.Length];
107 }
108 }
109  
110 #region KEY-VALUE DATA
111  
112 ///////////////////////////////////////////////////////////////////////////
113 // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
114 ///////////////////////////////////////////////////////////////////////////
115 /// <summary>
116 /// Returns the value of a key from a key-value data string.
117 /// </summary>
118 /// <param name="key">the key of the value</param>
119 /// <param name="data">the key-value data segment</param>
120 /// <returns>true if the key was found in data</returns>
121 private static string wasKeyValueGet(string key, string data)
122 {
123 return data.Split('&')
124 .AsParallel()
125 .Select(o => o.Split('=').ToList())
126 .Where(o => o.Count.Equals(2))
127 .Select(o => new
128 {
129 k = o.First(),
130 v = o.Last()
131 })
132 .Where(o => o.k.Equals(key))
133 .Select(o => o.v)
134 .FirstOrDefault();
135 }
136 #endregion
137  
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 {
917 {"command", "getregiondata"},
918 {"group", vassalConfiguration.Group},
919 {"password", vassalConfiguration.Password},
920 {"data", "Name"}
921 }), 60000);
922 bool success;
923 if (string.IsNullOrEmpty(result) ||
924 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
925 {
926 vassalForm.BeginInvoke((MethodInvoker)(() =>
927 {
928 vassalForm.StatusText.Text = @"Failed to query Corrade for current region.";
929 }));
930 return;
931 }
932 switch (success)
933 {
934 case true:
935 vassalForm.BeginInvoke((MethodInvoker)(() =>
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:
944 vassalForm.BeginInvoke((MethodInvoker)(() =>
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 {
956 vassalForm.BeginInvoke((MethodInvoker)(() =>
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 {
973 ListViewItem listViewItem = null;
974 vassalForm.Invoke((MethodInvoker) (() =>
975 {
976 listViewItem = LoadedRegions.SelectedItem as ListViewItem;
977 }));
978  
979 switch (listViewItem != null)
980 {
981 case true:
982 vassalForm.Invoke((MethodInvoker) (() =>
983 {
984 vassalForm.StatusText.Text = @"Teleporting to " +
985 listViewItem.Text;
986 }));
987 break;
988 default:
989 return;
990 }
991  
992 new Thread(() =>
993 {
994 lock (ClientInstanceTeleportLock)
995 {
996 try
997 {
998 vassalForm.Invoke((MethodInvoker) (() =>
999 {
1000 vassalForm.RegionTeleportGroup.Enabled = false;
1001 vassalForm.StatusProgress.Value = 0;
1002 }));
1003 int elapsedSeconds = 0;
1004 System.Timers.Timer teleportTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
1005 teleportTimer.Elapsed += (o, p) =>
1006 {
1007 vassalForm.Invoke((MethodInvoker) (() =>
1008 {
1009 vassalForm.StatusProgress.Value =
1010 Math.Min(
1011 (int)
1012 (100d*
1013 (TimeSpan.FromSeconds(++elapsedSeconds).TotalMilliseconds/
1014 vassalConfiguration.TeleportTimeout)), 100);
1015 }));
1016 };
1017 teleportTimer.Start();
1018 string result = null;
1019 ManualResetEvent receivedPOST = new ManualResetEvent(false);
1020 new Thread(() =>
1021 {
1022 result = wasInput(wasPOST(vassalConfiguration.HTTPServerURL,
1023 wasKeyValueEscape(new Dictionary<string, string>
1024 {
1025 {"command", "teleport"},
1026 {"group", vassalConfiguration.Group},
1027 {"password", vassalConfiguration.Password},
1028 {"region", listViewItem.Text},
1029 {"position", ((Vector3) listViewItem.Tag).ToString()},
1030 {"fly", "True"}
1031 }), vassalConfiguration.TeleportTimeout));
1032 receivedPOST.Set();
1033 }) {IsBackground = true}.Start();
1034 receivedPOST.WaitOne((int) vassalConfiguration.TeleportTimeout, false);
1035 teleportTimer.Stop();
1036 switch (!string.IsNullOrEmpty(result) && wasKeyValueGet("success", result) == "True")
1037 {
1038 case true:
1039 vassalForm.Invoke((MethodInvoker) (() =>
1040 {
1041 vassalForm.StatusText.Text = @"Now at " + listViewItem.Text;
1042 }));
1043 break;
1044 default:
1045 switch (!string.IsNullOrEmpty(result))
1046 {
1047 case true:
1048 vassalForm.Invoke((MethodInvoker) (() =>
1049 {
1050 vassalForm.StatusText.Text = @"Failed teleporting to " + listViewItem.Text +
1051 @": " +
1052 wasKeyValueGet("error", result);
1053 }));
1054 break;
1055 default:
1056 vassalForm.Invoke((MethodInvoker) (() =>
1057 {
1058 vassalForm.StatusText.Text = @"Failed teleporting to " + listViewItem.Text;
1059 }));
1060 break;
1061 }
1062 break;
1063 }
1064 }
1065 catch (Exception ex)
1066 {
1067 vassalForm.Invoke((MethodInvoker) (() =>
1068 {
1069 vassalForm.StatusText.Text = @"Error communicating with Corrade: " + ex.Message;
1070 }));
1071 }
1072 finally
1073 {
1074 vassalForm.Invoke((MethodInvoker) (() =>
1075 {
1076 vassalForm.StatusProgress.Value = 100;
1077 vassalForm.RegionTeleportGroup.Enabled = true;
1078 // Set the map image to the loading spinner.
1079 Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
1080 System.IO.Stream file =
1081 thisAssembly.GetManifestResourceStream("Vassal.img.loading.gif");
1082 switch (file != null)
1083 {
1084 case true:
1085 vassalForm.Invoke((MethodInvoker)(() =>
1086 {
1087 RegionAvatarsMap.SizeMode = PictureBoxSizeMode.CenterImage;
1088 RegionAvatarsMap.Image = Image.FromStream(file);
1089 RegionAvatarsMap.Refresh();
1090 }));
1091 break;
1092 }
1093 // Clear the top scripts table.
1094 TopScriptsGridView.Rows.Clear();
1095 // Clear the top colliders table.
1096 TopCollidersGridView.Rows.Clear();
1097 }));
1098 updateCurrentRegionName.BeginInvoke(updateCurrentRegionName.EndInvoke, null);
1099 }
1100 }
1101 }).Start();
1102  
1103 }
1104  
1105 private void SettingsRequested(object sender, EventArgs e)
1106 {
1107 SettingsForm settingsForm = new SettingsForm {TopMost = true};
1108 settingsForm.Show();
1109 }
1110  
1111 private void VassalShown(object sender, EventArgs e)
1112 {
1113 // Get the configuration file settings if it exists.
1114 if (File.Exists(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE))
1115 {
1116 VassalConfiguration.Load(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE, ref vassalConfiguration);
1117 }
1118  
1119 // Get all the regions if they exist.
1120 if (File.Exists(VASSAL_CONSTANTS.VASSAL_REGIONS))
1121 {
1122 Vector3 localPosition;
1123 ConfiguredRegions =
1124 File.ReadAllLines(VASSAL_CONSTANTS.VASSAL_REGIONS)
1125 .Select(o => new List<string>(wasCSVToEnumerable(o)))
1126 .Where(o => o.Count == 2)
1127 .ToDictionary(o => o.First(),
1128 p =>
1129 Vector3.TryParse(p.Last(), out localPosition)
1130 ? localPosition
1131 : Vector3.Zero).OrderBy(o => o.Key).ToDictionary(o => o.Key, o => o.Value);
1132 LoadedRegions.Items.Clear();
1133 LoadedRegions.Items.AddRange(
1134 ConfiguredRegions.Select(o => (object)new ListViewItem {Text = o.Key, Tag = o.Value})
1135 .ToArray());
1136 }
1137  
1138 // Update the current region in case the configuration is initialized.
1139 if (!vassalConfiguration.Equals(default(VassalConfiguration)))
1140 {
1141 updateCurrentRegionName.BeginInvoke(updateCurrentRegionName.EndInvoke, null);
1142 }
1143  
1144 // Set the map image to the loading spinner.
1145 Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
1146 System.IO.Stream file =
1147 thisAssembly.GetManifestResourceStream("Vassal.img.loading.gif");
1148 switch (file != null)
1149 {
1150 case true:
1151 vassalForm.Invoke((MethodInvoker)(() =>
1152 {
1153 RegionAvatarsMap.SizeMode = PictureBoxSizeMode.CenterImage;
1154 RegionAvatarsMap.Image = Image.FromStream(file);
1155 RegionAvatarsMap.Refresh();
1156 }));
1157 break;
1158 }
1159  
1160 // Start the overview timer.
1161 overviewTabTimer.Elapsed += (o, p) =>
1162 {
1163 if (!Monitor.TryEnter(ClientInstanceTeleportLock))
1164 return;
1165  
1166 try
1167 {
1168 // Do not do anything in case the tab is not selected.
1169 vassalForm.Invoke((MethodInvoker) (() =>
1170 {
1171 if (!Tabs.SelectedTab.Equals(OverviewTab))
1172 throw new Exception();
1173 }));
1174  
1175 // Get the statistics.
1176 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1177 wasKeyValueEscape(new Dictionary<string, string>
1178 {
1179 {"command", "getregiondata"},
1180 {"group", vassalConfiguration.Group},
1181 {"password", vassalConfiguration.Password},
1182 {
1183 "data", wasEnumerableToCSV(new[]
1184 {
1185 "Agents",
1186 "LastLag",
1187 "Dilation",
1188 "FPS",
1189 "PhysicsFPS",
1190 "ActiveScripts",
1191 "ScriptTime",
1192 "Objects"
1193 })
1194 }
1195 }), vassalConfiguration.DataTimeout);
1196  
1197 bool success;
1198 if (string.IsNullOrEmpty(result) ||
1199 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1200 throw new Exception();
1201  
1202 List<string> data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
1203 if (data.Count.Equals(0))
1204 throw new Exception();
1205  
1206 vassalForm.Invoke((MethodInvoker) (() =>
1207 {
1208 Agents.Text = data[data.IndexOf("Agents") + 1];
1209 LastLag.Text = data[data.IndexOf("LastLag") + 1];
1210 Dilation.Text = data[data.IndexOf("Dilation") + 1];
1211 FPS.Text = data[data.IndexOf("FPS") + 1];
1212 PhysicsFPS.Text = data[data.IndexOf("PhysicsFPS") + 1];
1213 ActiveScripts.Text = data[data.IndexOf("ActiveScripts") + 1];
1214 ScriptTime.Text = data[data.IndexOf("ScriptTime") + 1];
1215 Objects.Text = data[data.IndexOf("Objects") + 1];
1216 }));
1217  
1218 // Get the map image.
1219 result = wasPOST(vassalConfiguration.HTTPServerURL,
1220 wasKeyValueEscape(new Dictionary<string, string>
1221 {
1222 {"command", "getgridregiondata"},
1223 {"group", vassalConfiguration.Group},
1224 {"password", vassalConfiguration.Password},
1225 { "data", "MapImageID"}
1226 }), vassalConfiguration.DataTimeout);
1227  
1228 if (string.IsNullOrEmpty(result) ||
1229 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1230 throw new Exception();
1231  
1232 data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
1233 if (!data.Count.Equals(2))
1234 throw new Exception();
1235 result = wasPOST(vassalConfiguration.HTTPServerURL,
1236 wasKeyValueEscape(new Dictionary<string, string>
1237 {
1238 {"command", "download"},
1239 {"group", vassalConfiguration.Group},
1240 {"password", vassalConfiguration.Password},
1241 {"item", data.Last() },
1242 {"type", "Texture" },
1243 {"format", "Jpeg" },
1244 }), vassalConfiguration.DataTimeout);
1245 if (string.IsNullOrEmpty(result) ||
1246 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1247 throw new Exception();
1248 byte[] mapImageBytes = Convert.FromBase64String(wasInput(wasKeyValueGet("data", result)));
1249 Image mapImage;
1250 using (MemoryStream memoryStream = new MemoryStream(mapImageBytes, 0, mapImageBytes.Length))
1251 {
1252 mapImage = Image.FromStream(memoryStream);
1253 }
1254  
1255 // Get the avatar positions.
1256 result = wasPOST(vassalConfiguration.HTTPServerURL,
1257 wasKeyValueEscape(new Dictionary<string, string>
1258 {
1259 {"command", "getavatarpositions"},
1260 {"group", vassalConfiguration.Group},
1261 {"password", vassalConfiguration.Password},
1262 { "entity", "region"}
1263 }), vassalConfiguration.DataTimeout);
1264  
1265 if (string.IsNullOrEmpty(result) ||
1266 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1267 throw new Exception();
1268  
1269 // Every thrid element represents a Vecto3 of the avatar position.
1270 data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).Skip(2).Where((x, i) => i % 3 == 0).ToList();
1271  
1272 // Draw the avatars onto the map.
1273 Graphics mapGraphics = Graphics.FromImage(mapImage);
1274 Vector3 position;
1275 foreach (string vector in data)
1276 {
1277 switch (Vector3.TryParse(vector, out position))
1278 {
1279 case true:
1280 mapGraphics.FillEllipse(Brushes.Chartreuse, new Rectangle((int)position.X, (int)position.Y, 4, 4));
1281 break;
1282 }
1283 }
1284 mapGraphics.DrawImage(mapImage, new Point(0, 0));
1285  
1286 vassalForm.Invoke((MethodInvoker) (() =>
1287 {
1288 RegionAvatarsMap.SizeMode = PictureBoxSizeMode.StretchImage;
1289 RegionAvatarsMap.Image = mapImage;
1290 RegionAvatarsMap.Refresh();
1291 }));
1292 }
1293 catch (Exception)
1294 {
1295  
1296 }
1297 finally
1298 {
1299 Monitor.Exit(ClientInstanceTeleportLock);
1300 }
1301  
1302 };
1303 overviewTabTimer.Start();
1304  
1305 // Start the top scores timer.
1306 topScriptsTabTimer.Elapsed += (o, p) =>
1307 {
1308 if (!Monitor.TryEnter(ClientInstanceTeleportLock))
1309 return;
1310  
1311 try
1312 {
1313 // Do not do anything in case the tab is not selected.
1314 vassalForm.Invoke((MethodInvoker) (() =>
1315 {
1316 if (!Tabs.SelectedTab.Equals(TopScriptsTab))
1317 throw new Exception();
1318 }));
1319  
1320 // Get the statistics.
1321 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1322 wasKeyValueEscape(new Dictionary<string, string>
1323 {
1324 {"command", "getregiontop"},
1325 {"group", vassalConfiguration.Group},
1326 {"password", vassalConfiguration.Password},
1327 {"type", "scripts"}
1328 }), vassalConfiguration.DataTimeout);
1329  
1330 bool success;
1331 if (string.IsNullOrEmpty(result) ||
1332 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1333 throw new Exception();
1334  
1335 HashSet<List<string>> data =
1336 new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
1337 .Select((x, i) => new {Index = i, Value = x})
1338 .GroupBy(x => x.Index/5)
1339 .Select(x => x.Select(v => v.Value).ToList()));
1340 if (data.Count.Equals(0))
1341 throw new Exception();
1342  
1343 vassalForm.Invoke((MethodInvoker) (() =>
1344 {
1345 switch (!TopScriptsGridView.Rows.Count.Equals(0))
1346 {
1347 case true:
1348 foreach (DataGridViewRow topScriptsRow in (IEnumerable)TopScriptsGridView.Rows)
1349 {
1350 List<string> updateRowData =
1351 data.AsParallel()
1352 .FirstOrDefault(q => q[2].Equals(topScriptsRow.Cells["TopScriptsUUID"].Value.ToString()));
1353 switch (updateRowData != null && updateRowData.Count.Equals(5))
1354 {
1355 case true:
1356 topScriptsRow.Cells["TopScriptsScore"].Value = updateRowData[0];
1357 topScriptsRow.Cells["TopScriptsTaskName"].Value = updateRowData[1];
1358 topScriptsRow.Cells["TopScriptsUUID"].Value = updateRowData[2];
1359 topScriptsRow.Cells["TopScriptsOwner"].Value = updateRowData[3];
1360 topScriptsRow.Cells["TopScriptsPosition"].Value = updateRowData[4];
1361 break;
1362 }
1363 }
1364 break;
1365 default:
1366 foreach (List<string> updateRowData in data)
1367 {
1368 TopScriptsGridView.Rows.Add(updateRowData[0], updateRowData[1], updateRowData[2],
1369 updateRowData[3], updateRowData[4]);
1370 }
1371 break;
1372 }
1373  
1374 }));
1375 }
1376 catch (Exception)
1377 {
1378  
1379 }
1380 finally
1381 {
1382 Monitor.Exit(ClientInstanceTeleportLock);
1383 }
1384 };
1385 topScriptsTabTimer.Start();
1386  
1387 // Start the top colliders timer.
1388 topCollidersTabTimer.Elapsed += (o, p) =>
1389 {
1390 if (!Monitor.TryEnter(ClientInstanceTeleportLock))
1391 return;
1392  
1393 try
1394 {
1395 // Do not do anything in case the tab is not selected.
1396 vassalForm.Invoke((MethodInvoker)(() =>
1397 {
1398 if (!Tabs.SelectedTab.Equals(TopCollidersTab))
1399 throw new Exception();
1400 }));
1401  
1402 // Get the statistics.
1403 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1404 wasKeyValueEscape(new Dictionary<string, string>
1405 {
1406 {"command", "getregiontop"},
1407 {"group", vassalConfiguration.Group},
1408 {"password", vassalConfiguration.Password},
1409 {"type", "colliders"}
1410 }), vassalConfiguration.DataTimeout);
1411  
1412 bool success;
1413 if (string.IsNullOrEmpty(result) ||
1414 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1415 throw new Exception();
1416  
1417 HashSet<List<string>> data =
1418 new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
1419 .Select((x, i) => new { Index = i, Value = x })
1420 .GroupBy(x => x.Index / 5)
1421 .Select(x => x.Select(v => v.Value).ToList()));
1422 if (data.Count.Equals(0))
1423 throw new Exception();
1424  
1425 vassalForm.Invoke((MethodInvoker)(() =>
1426 {
1427 switch (!TopCollidersGridView.Rows.Count.Equals(0))
1428 {
1429 case true:
1430 foreach (DataGridViewRow topCollidersRow in (IEnumerable)TopCollidersGridView.Rows)
1431 {
1432 List<string> updateRowData =
1433 data.AsParallel()
1434 .FirstOrDefault(q => q[2].Equals(topCollidersRow.Cells["TopCollidersUUID"].Value.ToString()));
1435 switch (updateRowData != null && updateRowData.Count.Equals(5))
1436 {
1437 case true:
1438 topCollidersRow.Cells["TopCollidersScore"].Value = updateRowData[0];
1439 topCollidersRow.Cells["TopCollidersTaskName"].Value = updateRowData[1];
1440 topCollidersRow.Cells["TopCollidersUUID"].Value = updateRowData[2];
1441 topCollidersRow.Cells["TopCollidersOwner"].Value = updateRowData[3];
1442 topCollidersRow.Cells["TopCollidersPosition"].Value = updateRowData[4];
1443 break;
1444 }
1445 }
1446 break;
1447 default:
1448 foreach (List<string> updateRowData in data)
1449 {
1450 TopCollidersGridView.Rows.Add(updateRowData[0], updateRowData[1], updateRowData[2],
1451 updateRowData[3], updateRowData[4]);
1452 }
1453 break;
1454 }
1455  
1456 }));
1457 }
1458 catch (Exception)
1459 {
1460  
1461 }
1462 finally
1463 {
1464 Monitor.Exit(ClientInstanceTeleportLock);
1465 }
1466 };
1467 topCollidersTabTimer.Start();
1468 }
1469  
1470 private void RequestedEditRegions(object sender, EventArgs e)
1471 {
1472 // Clear any selection.
1473 LoadedRegions.SelectedIndex = -1;
1474 RegionEditForm regionEditForm = new RegionEditForm { TopMost = true };
1475 regionEditForm.Show();
1476 }
1477  
1478 private void RequestSelecting(object sender, TabControlCancelEventArgs e)
1479 {
1480 e.Cancel = !e.TabPage.Enabled;
1481 }
1482 }
1483 }