corrade-vassal – Blame information for rev 8

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.Generic;
9 using System.Drawing;
7 zed 10 using System.Drawing.Imaging;
2 zed 11 using System.IO;
12 using System.Linq;
13 using System.Net;
7 zed 14 using System.Net.Sockets;
2 zed 15 using System.Reflection;
16 using System.Text;
17 using System.Text.RegularExpressions;
18 using System.Threading;
19 using System.Web;
20 using System.Windows.Forms;
21 using OpenMetaverse;
22 using Parallel = System.Threading.Tasks.Parallel;
23 using Timer = System.Timers.Timer;
24  
25 namespace Vassal
26 {
27 public partial class Vassal : Form
28 {
8 eva 29 public static Timer vassalTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
30 public static Timer overviewTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
31 public static Timer residentListTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
32 public static Timer estateTopTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
33 public static Timer regionsStateTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
34 public static Timer estateTexturesTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
35 public static Image[] groundTextureImages = new Image[4];
36 public static volatile int regionsStateCheckIndex;
2 zed 37 public static VassalConfiguration vassalConfiguration = new VassalConfiguration();
38 public static Vassal vassalForm;
39 public static readonly object ClientInstanceTeleportLock = new object();
7 zed 40 public static readonly object RegionsStateCheckLock = new object();
2 zed 41  
7 zed 42 /// <summary>
8 eva 43 /// Corrade's input filter function.
7 zed 44 /// </summary>
8 eva 45 private static readonly Func<string, string> wasInput = o =>
7 zed 46 {
8 eva 47 if (string.IsNullOrEmpty(o)) return string.Empty;
7 zed 48  
8 eva 49 foreach (Filter filter in vassalConfiguration.InputFilters)
7 zed 50 {
8 eva 51 switch (filter)
7 zed 52 {
8 eva 53 case Filter.RFC1738:
54 o = wasURLUnescapeDataString(o);
55 break;
56 case Filter.RFC3986:
57 o = wasURIUnescapeDataString(o);
58 break;
59 case Filter.ENIGMA:
60 o = wasEnigma(o, vassalConfiguration.ENIGMA.rotors.ToArray(),
61 vassalConfiguration.ENIGMA.plugs.ToArray(),
62 vassalConfiguration.ENIGMA.reflector);
63 break;
64 case Filter.VIGENERE:
65 o = wasDecryptVIGENERE(o, vassalConfiguration.VIGENERESecret);
66 break;
67 case Filter.ATBASH:
68 o = wasATBASH(o);
69 break;
70 case Filter.BASE64:
71 o = Encoding.UTF8.GetString(Convert.FromBase64String(o));
72 break;
7 zed 73 }
74 }
8 eva 75 return o;
76 };
7 zed 77  
8 eva 78 /// <summary>
79 /// Corrade's output filter function.
80 /// </summary>
81 private static readonly Func<string, string> wasOutput = o =>
82 {
83 if (string.IsNullOrEmpty(o)) return string.Empty;
84  
85 foreach (Filter filter in vassalConfiguration.OutputFilters)
7 zed 86 {
8 eva 87 switch (filter)
7 zed 88 {
8 eva 89 case Filter.RFC1738:
90 o = wasURLEscapeDataString(o);
91 break;
92 case Filter.RFC3986:
93 o = wasURIEscapeDataString(o);
94 break;
95 case Filter.ENIGMA:
96 o = wasEnigma(o, vassalConfiguration.ENIGMA.rotors.ToArray(),
97 vassalConfiguration.ENIGMA.plugs.ToArray(),
98 vassalConfiguration.ENIGMA.reflector);
99 break;
100 case Filter.VIGENERE:
101 o = wasEncryptVIGENERE(o, vassalConfiguration.VIGENERESecret);
102 break;
103 case Filter.ATBASH:
104 o = wasATBASH(o);
105 break;
106 case Filter.BASE64:
107 o = Convert.ToBase64String(Encoding.UTF8.GetBytes(o));
108 break;
7 zed 109 }
110 }
8 eva 111 return o;
112 };
7 zed 113  
8 eva 114 private static readonly Action updateCurrentRegionName = () =>
115 {
116 try
7 zed 117 {
8 eva 118 string result = wasPOST(vassalConfiguration.HTTPServerURL,
119 wasKeyValueEscape(new Dictionary<string, string>
120 {
121 {"command", "getregiondata"},
122 {"group", vassalConfiguration.Group},
123 {"password", vassalConfiguration.Password},
124 {"data", "Name"}
125 }), 60000);
126 bool success;
127 if (string.IsNullOrEmpty(result) ||
128 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
7 zed 129 {
8 eva 130 vassalForm.BeginInvoke(
131 (MethodInvoker)
132 (() => { vassalForm.StatusText.Text = @"Failed to query Corrade for current region."; }));
133 return;
7 zed 134 }
8 eva 135 switch (success)
7 zed 136 {
8 eva 137 case true:
138 vassalForm.BeginInvoke((MethodInvoker) (() =>
139 {
140 vassalForm.CurrentRegionAt.Visible = true;
141 vassalForm.CurrentRegionName.Visible = true;
142 vassalForm.CurrentRegionName.Text =
143 wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).Last();
144 }));
145 break;
146 default:
147 vassalForm.BeginInvoke((MethodInvoker) (() =>
148 {
149 vassalForm.CurrentRegionAt.Visible = false;
150 vassalForm.CurrentRegionName.Visible = false;
151 vassalForm.StatusText.Text = @"Error getting current region: " +
152 wasInput(wasKeyValueGet("error", result));
153 }));
154 break;
7 zed 155 }
156 }
8 eva 157 catch (Exception ex)
7 zed 158 {
8 eva 159 vassalForm.BeginInvoke((MethodInvoker) (() =>
7 zed 160 {
8 eva 161 vassalForm.StatusText.Text =
162 @"Error getting current region: " +
163 ex.Message;
164 }));
7 zed 165 }
8 eva 166 };
7 zed 167  
8 eva 168 public Vassal()
169 {
170 InitializeComponent();
171 vassalForm = this;
7 zed 172 }
173  
2 zed 174 ///////////////////////////////////////////////////////////////////////////
175 // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
176 ///////////////////////////////////////////////////////////////////////////
7 zed 177 private static double wasMapValueToRange(double value, double xMin, double xMax, double yMin, double yMax)
178 {
179 return yMin + (
180 (
181 yMax - yMin
182 )
183 *
184 (
185 value - xMin
186 )
187 /
188 (
189 xMax - xMin
190 )
191 );
192 }
193  
194 ///////////////////////////////////////////////////////////////////////////
195 // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
196 ///////////////////////////////////////////////////////////////////////////
2 zed 197 /// <summary>RFC1738 URL Escapes a string</summary>
198 /// <param name="data">a string to escape</param>
199 /// <returns>an RFC1738 escaped string</returns>
200 private static string wasURLEscapeDataString(string data)
201 {
202 return HttpUtility.UrlEncode(data);
203 }
204  
205 ///////////////////////////////////////////////////////////////////////////
206 // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
207 ///////////////////////////////////////////////////////////////////////////
208 /// <summary>RFC1738 URL Unescape a string</summary>
209 /// <param name="data">a string to unescape</param>
210 /// <returns>an RFC1738 unescaped string</returns>
211 private static string wasURLUnescapeDataString(string data)
212 {
213 return HttpUtility.UrlDecode(data);
214 }
215  
216 ///////////////////////////////////////////////////////////////////////////
217 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
218 ///////////////////////////////////////////////////////////////////////////
219 /// <summary>URI unescapes an RFC3986 URI escaped string</summary>
220 /// <param name="data">a string to unescape</param>
221 /// <returns>the resulting string</returns>
222 private static string wasURIUnescapeDataString(string data)
223 {
224 // Uri.UnescapeDataString can only handle 32766 characters at a time
225 return string.Join("", Enumerable.Range(0, (data.Length + 32765)/32766)
226 .Select(o => Uri.UnescapeDataString(data.Substring(o*32766, Math.Min(32766, data.Length - (o*32766)))))
227 .ToArray());
228 }
229  
230 ///////////////////////////////////////////////////////////////////////////
231 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
232 ///////////////////////////////////////////////////////////////////////////
233 /// <summary>RFC3986 URI Escapes a string</summary>
234 /// <param name="data">a string to escape</param>
235 /// <returns>an RFC3986 escaped string</returns>
236 private static string wasURIEscapeDataString(string data)
237 {
238 // Uri.EscapeDataString can only handle 32766 characters at a time
239 return string.Join("", Enumerable.Range(0, (data.Length + 32765)/32766)
240 .Select(o => Uri.EscapeDataString(data.Substring(o*32766, Math.Min(32766, data.Length - (o*32766)))))
241 .ToArray());
242 }
243  
244 ///////////////////////////////////////////////////////////////////////////
245 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
246 ///////////////////////////////////////////////////////////////////////////
247 /// <summary>
248 /// Gets an array element at a given modulo index.
249 /// </summary>
250 /// <typeparam name="T">the array type</typeparam>
251 /// <param name="index">a positive or negative index of the element</param>
252 /// <param name="data">the array</param>
253 /// <return>an array element</return>
254 public static T wasGetElementAt<T>(T[] data, int index)
255 {
256 switch (index < 0)
257 {
258 case true:
259 return data[((index%data.Length) + data.Length)%data.Length];
260 default:
261 return data[index%data.Length];
262 }
263 }
264  
265 #region KEY-VALUE DATA
266  
267 ///////////////////////////////////////////////////////////////////////////
268 // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
269 ///////////////////////////////////////////////////////////////////////////
270 /// <summary>
271 /// Returns the value of a key from a key-value data string.
272 /// </summary>
273 /// <param name="key">the key of the value</param>
274 /// <param name="data">the key-value data segment</param>
275 /// <returns>true if the key was found in data</returns>
276 private static string wasKeyValueGet(string key, string data)
277 {
278 return data.Split('&')
279 .AsParallel()
280 .Select(o => o.Split('=').ToList())
281 .Where(o => o.Count.Equals(2))
282 .Select(o => new
283 {
284 k = o.First(),
285 v = o.Last()
286 })
287 .Where(o => o.k.Equals(key))
288 .Select(o => o.v)
289 .FirstOrDefault();
290 }
291  
3 eva 292 #endregion
293  
2 zed 294 ///////////////////////////////////////////////////////////////////////////
295 // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
296 ///////////////////////////////////////////////////////////////////////////
297 /// <summary>Escapes a dictionary's keys and values for sending as POST data.</summary>
298 /// <param name="data">A dictionary containing keys and values to be escaped</param>
299 private static Dictionary<string, string> wasKeyValueEscape(Dictionary<string, string> data)
300 {
301 return data.AsParallel().ToDictionary(o => wasOutput(o.Key), p => wasOutput(p.Value));
302 }
303  
304 ///////////////////////////////////////////////////////////////////////////
305 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
306 ///////////////////////////////////////////////////////////////////////////
307 /// <summary>
308 /// Converts a list of string to a comma-separated values string.
309 /// </summary>
310 /// <param name="l">a list of strings</param>
311 /// <returns>a commma-separated list of values</returns>
312 /// <remarks>compliant with RFC 4180</remarks>
313 public static string wasEnumerableToCSV(IEnumerable<string> l)
314 {
315 string[] csv = l.Select(o => o.Clone() as string).ToArray();
316 Parallel.ForEach(csv.Select((v, i) => new {i, v}), o =>
317 {
318 string cell = o.v.Replace("\"", "\"\"");
319 switch (new[] {'"', ' ', ',', '\r', '\n'}.Any(p => cell.Contains(p)))
320 {
321 case true:
322 csv[o.i] = "\"" + cell + "\"";
323 break;
324 default:
325 csv[o.i] = cell;
326 break;
327 }
328 });
8 eva 329 return string.Join(",", csv);
2 zed 330 }
331  
332 ///////////////////////////////////////////////////////////////////////////
333 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
334 ///////////////////////////////////////////////////////////////////////////
335 /// <summary>
336 /// Converts a comma-separated list of values to a list of strings.
337 /// </summary>
338 /// <param name="csv">a comma-separated list of values</param>
339 /// <returns>a list of strings</returns>
340 /// <remarks>compliant with RFC 4180</remarks>
341 public static IEnumerable<string> wasCSVToEnumerable(string csv)
342 {
343 Stack<char> s = new Stack<char>();
344 StringBuilder m = new StringBuilder();
345 for (int i = 0; i < csv.Length; ++i)
346 {
347 switch (csv[i])
348 {
349 case ',':
350 if (!s.Any() || !s.Peek().Equals('"'))
351 {
352 yield return m.ToString();
353 m = new StringBuilder();
354 continue;
355 }
356 m.Append(csv[i]);
357 continue;
358 case '"':
359 if (i + 1 < csv.Length && csv[i].Equals(csv[i + 1]))
360 {
361 m.Append(csv[i]);
362 ++i;
363 continue;
364 }
365 if (!s.Any() || !s.Peek().Equals(csv[i]))
366 {
367 s.Push(csv[i]);
368 continue;
369 }
370 s.Pop();
371 continue;
372 }
373 m.Append(csv[i]);
374 }
375  
376 yield return m.ToString();
377 }
378  
379 ///////////////////////////////////////////////////////////////////////////
380 // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
381 ///////////////////////////////////////////////////////////////////////////
382 /// <summary>
383 /// Serialises a dictionary to key-value data.
384 /// </summary>
385 /// <param name="data">a dictionary</param>
386 /// <returns>a key-value data encoded string</returns>
387 private static string wasKeyValueEncode(Dictionary<string, string> data)
388 {
8 eva 389 return string.Join("&", data.AsParallel().Select(o => string.Join("=", o.Key, o.Value)));
2 zed 390 }
391  
392 ///////////////////////////////////////////////////////////////////////////
393 // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
394 ///////////////////////////////////////////////////////////////////////////
395 /// <summary>
396 /// Sends a post request to an URL with set key-value pairs.
397 /// </summary>
398 /// <param name="URL">the url to send the message to</param>
399 /// <param name="message">key-value pairs to send</param>
400 /// <param name="millisecondsTimeout">the time in milliseconds for the request to timeout</param>
401 private static string wasPOST(string URL, Dictionary<string, string> message, uint millisecondsTimeout)
402 {
403 try
404 {
405 HttpWebRequest request = (HttpWebRequest) WebRequest.Create(URL);
406 request.UserAgent = VASSAL_CONSTANTS.USER_AGENT;
407 request.Proxy = WebRequest.DefaultWebProxy;
8 eva 408 request.Pipelined = true;
409 request.KeepAlive = true;
2 zed 410 request.Timeout = (int) millisecondsTimeout;
8 eva 411 request.ReadWriteTimeout = (int) millisecondsTimeout;
2 zed 412 request.AllowAutoRedirect = true;
413 request.AllowWriteStreamBuffering = true;
414 request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
415 request.Method = WebRequestMethods.Http.Post;
416 // set the content type based on chosen output filers
417 switch (vassalConfiguration.OutputFilters.Last())
418 {
419 case Filter.RFC1738:
420 request.ContentType = VASSAL_CONSTANTS.CONTENT_TYPE.WWW_FORM_URLENCODED;
421 break;
422 default:
423 request.ContentType = VASSAL_CONSTANTS.CONTENT_TYPE.TEXT_PLAIN;
424 break;
425 }
426 // send request
427 using (Stream requestStream = request.GetRequestStream())
428 {
429 using (StreamWriter dataStream = new StreamWriter(requestStream))
430 {
431 dataStream.Write(wasKeyValueEncode(message));
432 }
433 }
434 // read response
435 using (HttpWebResponse response = (HttpWebResponse) request.GetResponse())
436 {
437 using (Stream responseStream = response.GetResponseStream())
438 {
439 if (responseStream != null)
440 {
441 using (
442 StreamReader streamReader = new StreamReader(responseStream))
443 {
444 return streamReader.ReadToEnd();
445 }
446 }
447 }
448 }
449 }
450 catch (Exception)
451 {
452 }
453  
454 return null;
455 }
456  
8 eva 457 private void RegionSelected(object sender, EventArgs e)
2 zed 458 {
8 eva 459 new Thread(() =>
460 {
461 try
462 {
463 // Stop timers.
464 vassalTimer.Stop();
465 overviewTabTimer.Stop();
466 regionsStateTabTimer.Stop();
467 residentListTabTimer.Stop();
468 estateTopTabTimer.Stop();
469 estateTexturesTabTimer.Stop();
2 zed 470  
8 eva 471 Monitor.Enter(ClientInstanceTeleportLock);
2 zed 472  
8 eva 473 string selectedRegionName = string.Empty;
474 Vector3 selectedRegionPosition = Vector3.Zero;
475 bool startTeleport = false;
476 vassalForm.Invoke((MethodInvoker) (() =>
477 {
478 ListViewItem listViewItem = LoadedRegionsBox.SelectedItem as ListViewItem;
479 switch (listViewItem != null && LoadedRegionsBox.SelectedIndex != -1)
480 {
481 case true:
482 selectedRegionName = listViewItem.Text;
483 selectedRegionPosition = (Vector3) listViewItem.Tag;
484 startTeleport = true;
485 break;
486 default:
487 startTeleport = false;
488 break;
489 }
490 }));
2 zed 491  
8 eva 492 if (!startTeleport) throw new Exception();
2 zed 493  
8 eva 494 // Announce teleport.
495 vassalForm.Invoke((MethodInvoker) (() =>
2 zed 496 {
8 eva 497 vassalForm.RegionTeleportGroup.Enabled = false;
498 vassalForm.StatusProgress.Value = 0;
499 vassalForm.StatusText.Text = @"Teleporting to " + selectedRegionName;
2 zed 500 }));
501  
3 eva 502 int elapsedSeconds = 0;
8 eva 503 Timer teleportTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
3 eva 504 teleportTimer.Elapsed += (o, p) =>
2 zed 505 {
506 vassalForm.Invoke((MethodInvoker) (() =>
507 {
3 eva 508 vassalForm.StatusProgress.Value =
509 Math.Min(
510 (int)
511 (100d*
512 (TimeSpan.FromSeconds(++elapsedSeconds).TotalMilliseconds/
513 vassalConfiguration.TeleportTimeout)), 100);
2 zed 514 }));
3 eva 515 };
516 teleportTimer.Start();
517 string result = null;
518 ManualResetEvent receivedPOST = new ManualResetEvent(false);
519 new Thread(() =>
520 {
521 result = wasInput(wasPOST(vassalConfiguration.HTTPServerURL,
522 wasKeyValueEscape(new Dictionary<string, string>
523 {
524 {"command", "teleport"},
525 {"group", vassalConfiguration.Group},
526 {"password", vassalConfiguration.Password},
527 {"region", selectedRegionName},
528 {"position", selectedRegionPosition.ToString()},
529 {"fly", "True"}
530 }), vassalConfiguration.TeleportTimeout));
531 receivedPOST.Set();
532 }) {IsBackground = true}.Start();
533 receivedPOST.WaitOne((int) vassalConfiguration.TeleportTimeout, false);
534 teleportTimer.Stop();
535 switch (!string.IsNullOrEmpty(result) && wasKeyValueGet("success", result) == "True")
536 {
537 case true:
2 zed 538 vassalForm.Invoke((MethodInvoker) (() =>
539 {
3 eva 540 vassalForm.StatusText.Text = @"Now at " + selectedRegionName;
541 vassalForm.CurrentRegionName.Text = selectedRegionName;
2 zed 542 }));
3 eva 543 break;
544 default:
545 switch (!string.IsNullOrEmpty(result))
2 zed 546 {
547 case true:
3 eva 548 vassalForm.Invoke((MethodInvoker) (() =>
2 zed 549 {
3 eva 550 vassalForm.StatusText.Text = @"Failed teleporting to " + selectedRegionName +
551 @": " +
552 wasKeyValueGet("error", result);
2 zed 553 }));
554 break;
3 eva 555 default:
8 eva 556 vassalForm.Invoke(
557 (MethodInvoker)
558 (() =>
559 {
560 vassalForm.StatusText.Text = @"Failed teleporting to " +
561 selectedRegionName;
562 }));
3 eva 563 break;
2 zed 564 }
3 eva 565 break;
2 zed 566 }
567 }
3 eva 568 catch (Exception ex)
569 {
8 eva 570 vassalForm.Invoke(
571 (MethodInvoker)
572 (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade: " + ex.Message; }));
3 eva 573 }
574 finally
575 {
7 zed 576 vassalForm.BeginInvoke((MethodInvoker) (() =>
3 eva 577 {
578 vassalForm.StatusProgress.Value = 100;
579 vassalForm.RegionTeleportGroup.Enabled = true;
580 // Set the map image to the loading spinner.
8 eva 581 Assembly thisAssembly = Assembly.GetExecutingAssembly();
582 Stream file =
3 eva 583 thisAssembly.GetManifestResourceStream("Vassal.img.loading.gif");
584 switch (file != null)
585 {
586 case true:
587 vassalForm.Invoke((MethodInvoker) (() =>
588 {
589 RegionAvatarsMap.SizeMode = PictureBoxSizeMode.CenterImage;
590 RegionAvatarsMap.Image = Image.FromStream(file);
591 RegionAvatarsMap.Refresh();
592 }));
593 break;
594 }
7 zed 595 // Clear the resident list table.
596 ResidentListGridView.Rows.Clear();
3 eva 597 // Clear the top scripts table.
598 TopScriptsGridView.Rows.Clear();
599 // Clear the top colliders table.
600 TopCollidersGridView.Rows.Clear();
8 eva 601 // Clear the estate list table.
602 EstateListGridView.Rows.Clear();
603 // Clear the estate list selection box.
604 EstateListSelectBox.SelectedIndex = -1;
3 eva 605 // Invalidate data for overview tab.
606 vassalForm.CurrentRegionAt.Visible = false;
607 vassalForm.CurrentRegionName.Visible = false;
608 Agents.Text = string.Empty;
609 Agents.Enabled = false;
610 LastLag.Text = string.Empty;
611 LastLag.Enabled = false;
612 Dilation.Text = string.Empty;
613 Dilation.Enabled = false;
614 FPS.Text = string.Empty;
615 FPS.Enabled = false;
616 PhysicsFPS.Text = string.Empty;
617 PhysicsFPS.Enabled = false;
618 ActiveScripts.Text = string.Empty;
619 ActiveScripts.Enabled = false;
620 ScriptTime.Text = string.Empty;
621 ScriptTime.Enabled = false;
622 Objects.Text = string.Empty;
623 Objects.Enabled = false;
624 }));
8 eva 625  
626 Monitor.Exit(ClientInstanceTeleportLock);
627  
628 // start timers
629 vassalTimer.Start();
630 overviewTabTimer.Start();
631 regionsStateTabTimer.Start();
632 residentListTabTimer.Start();
633 estateTopTabTimer.Start();
634 estateTexturesTabTimer.Start();
3 eva 635 }
7 zed 636 })
637 {IsBackground = true}.Start();
2 zed 638 }
639  
640 private void SettingsRequested(object sender, EventArgs e)
641 {
8 eva 642 vassalForm.BeginInvoke((MethodInvoker) (() => { VassalStatusGroup.Enabled = false; }));
2 zed 643 SettingsForm settingsForm = new SettingsForm {TopMost = true};
644 settingsForm.Show();
645 }
646  
647 private void VassalShown(object sender, EventArgs e)
648 {
3 eva 649 // Set the version
650 vassalForm.Version.Text = @"v" + VASSAL_CONSTANTS.VASSAL_VERSION;
651  
4 vero 652 // Disable estate manager tabs since we will enable these dynamically.
8 eva 653 EstateTopTab.Enabled = false;
654 EstateListsTab.Enabled = false;
7 zed 655 ResidentListBanGroup.Enabled = false;
8 eva 656 EstateTerrainDownloadUploadGroup.Enabled = false;
657 // Estate textures
658 RegionTexturesLowUUIDApplyBox.Enabled = false;
659 RegionTexturesLowUUIDApplyButton.Enabled = false;
660 RegionTexturesMiddleLowUUIDApplyBox.Enabled = false;
661 RegionTexturesMiddleLowUUIDApplyButton.Enabled = false;
662 RegionTexturesMiddleHighUUIDApplyBox.Enabled = false;
663 RegionTexturesMiddleHighUUIDApplyButton.Enabled = false;
664 RegionTexturesHighUUIDApplyBox.Enabled = false;
665 RegionTexturesHighUUIDApplyButton.Enabled = false;
4 vero 666  
2 zed 667 // Get the configuration file settings if it exists.
668 if (File.Exists(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE))
669 {
4 vero 670 // Load the configuration.
2 zed 671 VassalConfiguration.Load(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE, ref vassalConfiguration);
5 eva 672 // Apply settings
673 RegionRestartDelayBox.Text = vassalConfiguration.RegionRestartDelay.ToString(Utils.EnUsCulture);
2 zed 674 }
675  
676 // Get all the regions if they exist.
677 if (File.Exists(VASSAL_CONSTANTS.VASSAL_REGIONS))
678 {
679 Vector3 localPosition;
3 eva 680 List<KeyValuePair<string, Vector3>> ConfiguredRegions = new List<KeyValuePair<string, Vector3>>(
2 zed 681 File.ReadAllLines(VASSAL_CONSTANTS.VASSAL_REGIONS)
682 .Select(o => new List<string>(wasCSVToEnumerable(o)))
683 .Where(o => o.Count == 2)
684 .ToDictionary(o => o.First(),
685 p =>
686 Vector3.TryParse(p.Last(), out localPosition)
687 ? localPosition
3 eva 688 : Vector3.Zero).OrderBy(o => o.Key).ToDictionary(o => o.Key, o => o.Value));
689 // Populate the loaded regions.
8 eva 690 LoadedRegionsBox.Items.Clear();
691 LoadedRegionsBox.Items.AddRange(
3 eva 692 ConfiguredRegions.Select(o => (object) new ListViewItem {Text = o.Key, Tag = o.Value})
2 zed 693 .ToArray());
3 eva 694 // Populate the batch restart grid view.
695 BatchRestartGridView.Rows.Clear();
696 foreach (KeyValuePair<string, Vector3> data in ConfiguredRegions)
697 {
698 BatchRestartGridView.Rows.Add(data.Key, data.Value.ToString());
699 }
7 zed 700 // Populate the regions state grid view.
701 foreach (KeyValuePair<string, Vector3> data in ConfiguredRegions)
702 {
703 RegionsStateGridView.Rows.Add(data.Key, "Check pening...");
704 }
2 zed 705 }
706  
8 eva 707 // Start the vassal timer.
708 vassalTimer.Elapsed += (o, p) =>
2 zed 709 {
710 if (!Monitor.TryEnter(ClientInstanceTeleportLock))
711 return;
8 eva 712 try
713 {
714 // Check Corrade connection status.
715 TcpClient tcpClient = new TcpClient();
716 Uri uri = new Uri(vassalConfiguration.HTTPServerURL);
717 tcpClient.Connect(uri.Host, uri.Port);
718 // port open
719 vassalForm.BeginInvoke((MethodInvoker) (() =>
720 {
721 vassalForm.CurrentRegionAt.Visible = true;
722 vassalForm.CurrentRegionName.Visible = true;
723 Assembly thisAssembly = Assembly.GetExecutingAssembly();
724 Stream file =
725 thisAssembly.GetManifestResourceStream("Vassal.img.online.png");
726 switch (file != null)
727 {
728 case true:
729 vassalForm.BeginInvoke((MethodInvoker) (() =>
730 {
731 ConnectionStatusPictureBox.Image = Image.FromStream(file);
732 ConnectionStatusPictureBox.Refresh();
733 }));
734 break;
735 }
736 Tabs.Enabled = true;
737 }));
738 }
739 catch (Exception)
740 {
741 // port closed
742 vassalForm.BeginInvoke((MethodInvoker) (() =>
743 {
744 vassalForm.CurrentRegionAt.Visible = false;
745 vassalForm.CurrentRegionName.Visible = false;
746 Assembly thisAssembly = Assembly.GetExecutingAssembly();
747 Stream file =
748 thisAssembly.GetManifestResourceStream("Vassal.img.offline.png");
749 switch (file != null)
750 {
751 case true:
752 vassalForm.BeginInvoke((MethodInvoker) (() =>
753 {
754 ConnectionStatusPictureBox.Image = Image.FromStream(file);
755 ConnectionStatusPictureBox.Refresh();
756 }));
757 break;
758 }
759 Tabs.Enabled = false;
760 }));
761 }
2 zed 762  
763 try
764 {
8 eva 765 // Get the simulator name and if we are an estate manager.
2 zed 766 string result = wasPOST(vassalConfiguration.HTTPServerURL,
767 wasKeyValueEscape(new Dictionary<string, string>
768 {
769 {"command", "getregiondata"},
770 {"group", vassalConfiguration.Group},
771 {"password", vassalConfiguration.Password},
772 {
773 "data", wasEnumerableToCSV(new[]
774 {
4 vero 775 "Name",
776 "IsEstateManager"
2 zed 777 })
778 }
779 }), vassalConfiguration.DataTimeout);
780  
781 bool success;
782 if (string.IsNullOrEmpty(result) ||
783 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
784 throw new Exception();
785  
786 List<string> data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
787 if (data.Count.Equals(0))
788 throw new Exception();
789  
7 zed 790 vassalForm.BeginInvoke((MethodInvoker) (() =>
2 zed 791 {
4 vero 792 // Drop access to features if we are not estate managers.
793 bool isEstateManager;
794 switch (
795 bool.TryParse(data[data.IndexOf("IsEstateManager") + 1], out isEstateManager) &&
796 isEstateManager)
797 {
798 case true: // we are an estate manager
8 eva 799 EstateTopTab.Enabled = true;
800 EstateListsTab.Enabled = true;
7 zed 801 ResidentListBanGroup.Enabled = true;
8 eva 802 EstateTerrainDownloadUploadGroup.Enabled = true;
803 RegionToolsRegionDebugGroup.Enabled = true;
804 RegionToolsRegionInfoGroup.Enabled = true;
805 // Estate textures
806 RegionTexturesLowUUIDApplyBox.Enabled = true;
807 RegionTexturesLowUUIDApplyButton.Enabled = true;
808 RegionTexturesMiddleLowUUIDApplyBox.Enabled = true;
809 RegionTexturesMiddleLowUUIDApplyButton.Enabled = true;
810 RegionTexturesMiddleHighUUIDApplyBox.Enabled = true;
811 RegionTexturesMiddleHighUUIDApplyButton.Enabled = true;
812 RegionTexturesHighUUIDApplyBox.Enabled = true;
813 RegionTexturesHighUUIDApplyButton.Enabled = true;
4 vero 814 break;
815 default:
8 eva 816 EstateTopTab.Enabled = false;
817 EstateListsTab.Enabled = false;
7 zed 818 ResidentListBanGroup.Enabled = false;
8 eva 819 EstateTerrainDownloadUploadGroup.Enabled = false;
820 RegionToolsRegionDebugGroup.Enabled = false;
821 RegionToolsRegionInfoGroup.Enabled = false;
822 // Estate textures
823 RegionTexturesLowUUIDApplyBox.Enabled = false;
824 RegionTexturesLowUUIDApplyButton.Enabled = false;
825 RegionTexturesMiddleLowUUIDApplyBox.Enabled = false;
826 RegionTexturesMiddleLowUUIDApplyButton.Enabled = false;
827 RegionTexturesMiddleHighUUIDApplyBox.Enabled = false;
828 RegionTexturesMiddleHighUUIDApplyButton.Enabled = false;
829 RegionTexturesHighUUIDApplyBox.Enabled = false;
830 RegionTexturesHighUUIDApplyButton.Enabled = false;
4 vero 831 break;
832 }
833  
3 eva 834 // Show the region name.
835 vassalForm.CurrentRegionName.Text = data[data.IndexOf("Name") + 1];
836 vassalForm.CurrentRegionAt.Visible = true;
837 vassalForm.CurrentRegionName.Visible = true;
8 eva 838 }));
839 }
840 catch (Exception)
841 {
842 }
3 eva 843  
8 eva 844 Monitor.Exit(ClientInstanceTeleportLock);
845 };
846 vassalTimer.Start();
847  
848 // Start the overview timer.
849 overviewTabTimer.Elapsed += (o, p) =>
850 {
851 // Do not do anything in case the tab is not selected.
852 bool run = false;
853 vassalForm.Invoke((MethodInvoker) (() => { run = Tabs.SelectedTab.Equals(OverviewTab); }));
854  
855 if (!run)
856 {
857 overviewTabTimer.Stop();
858 return;
859 }
860  
861 overviewTabTimer.Stop();
862  
863 try
864 {
865 // Get the statistics.
866 string result = wasPOST(vassalConfiguration.HTTPServerURL,
867 wasKeyValueEscape(new Dictionary<string, string>
868 {
869 {"command", "getregiondata"},
870 {"group", vassalConfiguration.Group},
871 {"password", vassalConfiguration.Password},
872 {
873 "data", wasEnumerableToCSV(new[]
874 {
875 "Agents",
876 "LastLag",
877 "Dilation",
878 "FPS",
879 "PhysicsFPS",
880 "ActiveScripts",
881 "ScriptTime",
882 "Objects",
883 "AvatarPositions"
884 })
885 }
886 }), vassalConfiguration.DataTimeout);
887  
888 bool success;
889 if (string.IsNullOrEmpty(result) ||
890 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
891 throw new Exception();
892  
893 List<string> data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
894 if (data.Count.Equals(0))
895 throw new Exception();
896  
897 vassalForm.BeginInvoke((MethodInvoker) (() =>
898 {
3 eva 899 // Populate the overview tab.
2 zed 900 Agents.Text = data[data.IndexOf("Agents") + 1];
3 eva 901 Agents.Enabled = true;
2 zed 902 LastLag.Text = data[data.IndexOf("LastLag") + 1];
3 eva 903 LastLag.Enabled = true;
2 zed 904 Dilation.Text = data[data.IndexOf("Dilation") + 1];
3 eva 905 Dilation.Enabled = true;
2 zed 906 FPS.Text = data[data.IndexOf("FPS") + 1];
3 eva 907 FPS.Enabled = true;
2 zed 908 PhysicsFPS.Text = data[data.IndexOf("PhysicsFPS") + 1];
3 eva 909 PhysicsFPS.Enabled = true;
2 zed 910 ActiveScripts.Text = data[data.IndexOf("ActiveScripts") + 1];
3 eva 911 ActiveScripts.Enabled = true;
2 zed 912 ScriptTime.Text = data[data.IndexOf("ScriptTime") + 1];
3 eva 913 ScriptTime.Enabled = true;
2 zed 914 Objects.Text = data[data.IndexOf("Objects") + 1];
3 eva 915 Objects.Enabled = true;
2 zed 916 }));
3 eva 917  
8 eva 918 // Get avatar positions.
919 // Pattern: [...], X, 10, Y, 63, Z, 200, [...], X, 52, Y, 73, Z, 55, [...[...]]
920 float X = 0, Y = 0, Z = 0;
921 List<Vector3> positions = data.Select((x, i) => new {Item = x, Index = i})
922 .Where(x => x.Item.Equals("X") || x.Item.Equals("Y") || x.Item.Equals("Z"))
923 .Select(z => data[z.Index + 1]).Select((x, i) => new {Value = x, Index = i})
924 .GroupBy(x => x.Index/3)
925 .Select(x => x.Select(v => v.Value).ToList())
926 .Where(
927 x =>
928 float.TryParse(x[0], out X) && float.TryParse(x[1], out Y) &&
929 float.TryParse(x[2], out Z))
930 .Select(x => new Vector3(X, Y, Z))
931 .ToList();
932  
2 zed 933 // Get the map image.
934 result = wasPOST(vassalConfiguration.HTTPServerURL,
935 wasKeyValueEscape(new Dictionary<string, string>
936 {
937 {"command", "getgridregiondata"},
938 {"group", vassalConfiguration.Group},
939 {"password", vassalConfiguration.Password},
3 eva 940 {"data", "MapImageID"}
2 zed 941 }), vassalConfiguration.DataTimeout);
3 eva 942  
2 zed 943 if (string.IsNullOrEmpty(result) ||
944 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
945 throw new Exception();
946  
947 data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
948 if (!data.Count.Equals(2))
949 throw new Exception();
950 result = wasPOST(vassalConfiguration.HTTPServerURL,
951 wasKeyValueEscape(new Dictionary<string, string>
952 {
953 {"command", "download"},
954 {"group", vassalConfiguration.Group},
955 {"password", vassalConfiguration.Password},
3 eva 956 {"item", data.Last()},
957 {"type", "Texture"},
8 eva 958 {"format", "Jpeg"}
2 zed 959 }), vassalConfiguration.DataTimeout);
960 if (string.IsNullOrEmpty(result) ||
961 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
962 throw new Exception();
963 byte[] mapImageBytes = Convert.FromBase64String(wasInput(wasKeyValueGet("data", result)));
964 Image mapImage;
965 using (MemoryStream memoryStream = new MemoryStream(mapImageBytes, 0, mapImageBytes.Length))
966 {
967 mapImage = Image.FromStream(memoryStream);
968 }
969  
970 // Draw the avatars onto the map.
971 Graphics mapGraphics = Graphics.FromImage(mapImage);
8 eva 972 foreach (Vector3 position in positions)
2 zed 973 {
8 eva 974 mapGraphics.FillEllipse(Brushes.Chartreuse,
975 new Rectangle((int) position.X, (int) position.Y, 4, 4));
2 zed 976 }
977 mapGraphics.DrawImage(mapImage, new Point(0, 0));
978  
7 zed 979 vassalForm.BeginInvoke((MethodInvoker) (() =>
2 zed 980 {
981 RegionAvatarsMap.SizeMode = PictureBoxSizeMode.StretchImage;
982 RegionAvatarsMap.Image = mapImage;
983 RegionAvatarsMap.Refresh();
984 }));
985 }
986 catch (Exception)
987 {
988 }
3 eva 989  
8 eva 990 overviewTabTimer.Start();
2 zed 991 };
992 overviewTabTimer.Start();
993  
7 zed 994 regionsStateTabTimer.Elapsed += (o, p) =>
2 zed 995 {
7 zed 996 // Do not do anything in case the tab is not selected.
997 bool run = false;
8 eva 998 vassalForm.Invoke((MethodInvoker) (() => { run = Tabs.SelectedTab.Equals(RegionsStateTab); }));
999  
1000 if (!run)
7 zed 1001 {
8 eva 1002 regionsStateTabTimer.Stop();
2 zed 1003 return;
8 eva 1004 }
2 zed 1005  
8 eva 1006 regionsStateTabTimer.Stop();
1007  
2 zed 1008 try
1009 {
7 zed 1010 string regionName = string.Empty;
2 zed 1011 vassalForm.Invoke((MethodInvoker) (() =>
1012 {
7 zed 1013 regionName =
1014 RegionsStateGridView.Rows[regionsStateCheckIndex].Cells["RegionsStateRegionName"].Value
1015 .ToString();
8 eva 1016 RegionsStateGridView.Rows[regionsStateCheckIndex].DefaultCellStyle.BackColor =
1017 Color.Gold;
2 zed 1018 }));
1019  
7 zed 1020 if (string.IsNullOrEmpty(regionName))
1021 throw new Exception();
1022  
1023 // Get the region status.
1024 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1025 wasKeyValueEscape(new Dictionary<string, string>
1026 {
1027 {"command", "getgridregiondata"},
1028 {"group", vassalConfiguration.Group},
1029 {"password", vassalConfiguration.Password},
1030 {"region", regionName},
1031 {"data", "Access"}
1032 }), vassalConfiguration.DataTimeout);
1033  
1034 bool success;
1035 if (string.IsNullOrEmpty(result) ||
1036 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1037 throw new Exception();
1038  
1039 List<string> data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
1040 if (!data.Count.Equals(2))
1041 throw new Exception();
1042  
1043 string status = data.Last();
1044 vassalForm.Invoke((MethodInvoker) (() =>
1045 {
1046 switch (status)
1047 {
1048 case "Unknown":
1049 case "Down":
1050 case "NonExistent":
1051 RegionsStateGridView.Rows[regionsStateCheckIndex].DefaultCellStyle.BackColor =
1052 Color.LightPink;
1053 RegionsStateGridView.Rows[regionsStateCheckIndex].Cells["RegionsStateLastState"].Value =
1054 status;
1055 break;
1056 default:
1057 RegionsStateGridView.Rows[regionsStateCheckIndex].DefaultCellStyle.BackColor =
1058 Color.LightGreen;
1059 RegionsStateGridView.Rows[regionsStateCheckIndex].Cells["RegionsStateLastState"].Value =
1060 "Up";
1061 break;
1062 }
1063 RegionsStateGridView.Rows[regionsStateCheckIndex].Cells["RegionsStateLastCheck"].Value = DateTime
1064 .Now.ToUniversalTime()
8 eva 1065 .ToString(LINDEN_CONSTANTS.LSL.DATE_TIME_STAMP);
7 zed 1066 }));
1067 }
1068 catch (Exception)
1069 {
1070 }
1071 finally
1072 {
1073 ++regionsStateCheckIndex;
1074 vassalForm.Invoke((MethodInvoker) (() =>
1075 {
1076 if (regionsStateCheckIndex >= RegionsStateGridView.Rows.Count)
1077 {
1078 regionsStateCheckIndex = 0;
1079 }
1080 }));
1081 }
8 eva 1082  
1083 regionsStateTabTimer.Start();
7 zed 1084 };
1085 regionsStateTabTimer.Start();
1086  
1087 // Start the top scores timer.
8 eva 1088 estateTopTabTimer.Elapsed += (o, p) =>
7 zed 1089 {
1090 // Do not do anything in case the tab is not selected.
1091 bool run = false;
8 eva 1092 vassalForm.Invoke((MethodInvoker) (() => { run = Tabs.SelectedTab.Equals(EstateTopTab); }));
1093  
1094 if (!run)
7 zed 1095 {
8 eva 1096 estateTopTabTimer.Stop();
7 zed 1097 return;
8 eva 1098 }
7 zed 1099  
8 eva 1100 estateTopTabTimer.Stop();
1101  
7 zed 1102 try
1103 {
8 eva 1104 // Get the top scripts.
2 zed 1105 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1106 wasKeyValueEscape(new Dictionary<string, string>
1107 {
1108 {"command", "getregiontop"},
1109 {"group", vassalConfiguration.Group},
1110 {"password", vassalConfiguration.Password},
1111 {"type", "scripts"}
1112 }), vassalConfiguration.DataTimeout);
1113  
1114 bool success;
1115 if (string.IsNullOrEmpty(result) ||
1116 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1117 throw new Exception();
1118  
1119 HashSet<List<string>> data =
1120 new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
1121 .Select((x, i) => new {Index = i, Value = x})
1122 .GroupBy(x => x.Index/5)
1123 .Select(x => x.Select(v => v.Value).ToList()));
1124 if (data.Count.Equals(0))
1125 throw new Exception();
1126  
1127 vassalForm.Invoke((MethodInvoker) (() =>
1128 {
3 eva 1129 // Remove rows that are not in the data update.
1130 foreach (
1131 int index in
1132 TopScriptsGridView.Rows.AsParallel().Cast<DataGridViewRow>()
1133 .Where(
1134 topScriptsRow =>
1135 !data.Any(q => q[2].Equals(topScriptsRow.Cells["TopScriptsUUID"].Value)))
1136 .Select(q => q.Index))
2 zed 1137 {
3 eva 1138 TopScriptsGridView.Rows.RemoveAt(index);
2 zed 1139 }
3 eva 1140 // Now update or add new data.
1141 foreach (List<string> dataComponents in data.Where(q => q.Count.Equals(5)))
1142 {
1143 DataGridViewRow row =
1144 TopScriptsGridView.Rows.AsParallel()
1145 .Cast<DataGridViewRow>()
1146 .FirstOrDefault(q => q.Cells["TopScriptsUUID"].Value.Equals(dataComponents[2]));
1147 switch (row != null)
1148 {
1149 case true: // the row exists, so update it.
1150 row.Cells["TopScriptsScore"].Value = dataComponents[0];
1151 row.Cells["TopScriptsTaskName"].Value = dataComponents[1];
1152 row.Cells["TopScriptsUUID"].Value = dataComponents[2];
1153 row.Cells["TopScriptsOwner"].Value = dataComponents[3];
1154 row.Cells["TopScriptsPosition"].Value = dataComponents[4];
1155 break;
1156 case false: // the row dosn't exist, so add it.
1157 TopScriptsGridView.Rows.Add(dataComponents[0], dataComponents[1], dataComponents[2],
1158 dataComponents[3], dataComponents[4]);
1159 break;
1160 }
1161 }
2 zed 1162 }));
1163  
8 eva 1164 // Get the top colliders.
1165 result = wasPOST(vassalConfiguration.HTTPServerURL,
2 zed 1166 wasKeyValueEscape(new Dictionary<string, string>
1167 {
1168 {"command", "getregiontop"},
1169 {"group", vassalConfiguration.Group},
1170 {"password", vassalConfiguration.Password},
1171 {"type", "colliders"}
1172 }), vassalConfiguration.DataTimeout);
1173  
1174 if (string.IsNullOrEmpty(result) ||
1175 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1176 throw new Exception();
1177  
8 eva 1178 data = new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
1179 .Select((x, i) => new {Index = i, Value = x})
1180 .GroupBy(x => x.Index/5)
1181 .Select(x => x.Select(v => v.Value).ToList()));
2 zed 1182 if (data.Count.Equals(0))
1183 throw new Exception();
1184  
3 eva 1185 vassalForm.Invoke((MethodInvoker) (() =>
2 zed 1186 {
3 eva 1187 // Remove rows that are not in the data update.
1188 foreach (
1189 int index in
1190 TopCollidersGridView.Rows.AsParallel().Cast<DataGridViewRow>()
1191 .Where(
1192 topCollidersRow =>
1193 !data.Any(q => q[2].Equals(topCollidersRow.Cells["TopCollidersUUID"].Value)))
1194 .Select(q => q.Index))
2 zed 1195 {
3 eva 1196 TopCollidersGridView.Rows.RemoveAt(index);
2 zed 1197 }
3 eva 1198 // Now update or add new data.
1199 foreach (List<string> dataComponents in data.Where(q => q.Count.Equals(5)))
1200 {
1201 DataGridViewRow row =
1202 TopCollidersGridView.Rows.AsParallel()
1203 .Cast<DataGridViewRow>()
1204 .FirstOrDefault(q => q.Cells["TopCollidersUUID"].Value.Equals(dataComponents[2]));
1205 switch (row != null)
1206 {
1207 case true: // the row exists, so update it.
1208 row.Cells["TopCollidersScore"].Value = dataComponents[0];
1209 row.Cells["TopSCollidersTaskName"].Value = dataComponents[1];
1210 row.Cells["TopCollidersUUID"].Value = dataComponents[2];
1211 row.Cells["TopCollidersOwner"].Value = dataComponents[3];
1212 row.Cells["TopCollidersPosition"].Value = dataComponents[4];
1213 break;
1214 case false: // the row dosn't exist, so add it.
7 zed 1215 TopCollidersGridView.Rows.Add(dataComponents[0], dataComponents[1],
1216 dataComponents[2],
3 eva 1217 dataComponents[3], dataComponents[4]);
1218 break;
1219 }
1220 }
2 zed 1221 }));
1222 }
1223 catch (Exception)
1224 {
8 eva 1225 }
2 zed 1226  
8 eva 1227 estateTopTabTimer.Start();
2 zed 1228 };
8 eva 1229 estateTopTabTimer.Start();
7 zed 1230  
1231 // Start the resident list timer.
8 eva 1232 residentListTabTimer.Elapsed += (o, p) =>
7 zed 1233 {
1234 // Do not do anything in case the tab is not selected.
1235 bool run = false;
8 eva 1236 vassalForm.Invoke((MethodInvoker) (() => { run = Tabs.SelectedTab.Equals(ResidentListTab); }));
1237  
1238 if (!run)
7 zed 1239 {
8 eva 1240 residentListTabTimer.Stop();
7 zed 1241 return;
8 eva 1242 }
7 zed 1243  
8 eva 1244 residentListTabTimer.Stop();
1245  
7 zed 1246 try
1247 {
1248 // Get the avatar positions.
1249 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1250 wasKeyValueEscape(new Dictionary<string, string>
1251 {
1252 {"command", "getavatarpositions"},
1253 {"group", vassalConfiguration.Group},
1254 {"password", vassalConfiguration.Password},
1255 {"entity", "region"}
1256 }), vassalConfiguration.DataTimeout);
1257  
1258 bool success;
1259 if (string.IsNullOrEmpty(result) ||
1260 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1261 throw new Exception();
1262  
1263 HashSet<List<string>> data =
1264 new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
1265 .Select((x, i) => new {Index = i, Value = x})
1266 .GroupBy(x => x.Index/3)
1267 .Select(x => x.Select(v => v.Value).ToList()));
1268 if (data.Count.Equals(0))
1269 throw new Exception();
1270  
1271 vassalForm.Invoke((MethodInvoker) (() =>
1272 {
1273 // Remove rows that are not in the data update.
1274 foreach (
1275 int index in
1276 ResidentListGridView.Rows.AsParallel().Cast<DataGridViewRow>()
1277 .Where(
1278 residentListRow =>
1279 !data.Any(q => q[1].Equals(residentListRow.Cells["ResidentListUUID"].Value)))
1280 .Select(q => q.Index))
1281 {
1282 ResidentListGridView.Rows.RemoveAt(index);
1283 }
1284 // Now update or add new data.
1285 foreach (List<string> dataComponents in data.Where(q => q.Count.Equals(3)))
1286 {
1287 DataGridViewRow row =
1288 ResidentListGridView.Rows.AsParallel()
1289 .Cast<DataGridViewRow>()
1290 .FirstOrDefault(q => q.Cells["ResidentListUUID"].Value.Equals(dataComponents[1]));
1291 switch (row != null)
1292 {
1293 case true: // the row exists, so update it.
1294 row.Cells["ResidentListName"].Value = dataComponents[0];
1295 row.Cells["ResidentListUUID"].Value = dataComponents[1];
1296 row.Cells["ResidentListPosition"].Value = dataComponents[2];
1297 break;
1298 case false: // the row dosn't exist, so add it.
1299 ResidentListGridView.Rows.Add(dataComponents[0], dataComponents[1],
1300 dataComponents[2]);
1301 break;
1302 }
1303 }
1304 }));
1305 }
1306 catch (Exception)
1307 {
8 eva 1308 }
7 zed 1309  
8 eva 1310 residentListTabTimer.Start();
1311 };
1312 residentListTabTimer.Start();
1313  
1314 estateTexturesTabTimer.Elapsed += (o, p) =>
1315 {
1316 // Do not do anything in case the tab is not selected.
1317 bool run = false;
1318 vassalForm.Invoke((MethodInvoker) (() => { run = Tabs.SelectedTab.Equals(EstateTexturesTab); }));
1319  
1320 if (!run)
1321 {
1322 estateTexturesTabTimer.Stop();
1323 return;
7 zed 1324 }
8 eva 1325  
1326 estateTexturesTabTimer.Stop();
1327  
1328 try
7 zed 1329 {
8 eva 1330 // Get the region terrain texture UUIDs.
1331 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1332 wasKeyValueEscape(new Dictionary<string, string>
1333 {
1334 {"command", "getregionterraintextures"},
1335 {"group", vassalConfiguration.Group},
1336 {"password", vassalConfiguration.Password}
1337 }), vassalConfiguration.DataTimeout);
1338  
1339 bool success;
1340 if (string.IsNullOrEmpty(result) ||
1341 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1342 throw new Exception();
1343  
1344 List<string> data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
1345 if (!data.Count.Equals(4))
1346 throw new Exception();
1347  
1348 vassalForm.Invoke((MethodInvoker) (() =>
1349 {
1350 RegionTexturesLowUUIDBox.Text = data[0];
1351 if (string.IsNullOrEmpty(RegionTexturesLowUUIDApplyBox.Text))
1352 {
1353 RegionTexturesLowUUIDApplyBox.Text = data[0];
1354 }
1355 RegionTexturesMiddleLowUUIDBox.Text = data[1];
1356 if (string.IsNullOrEmpty(RegionTexturesMiddleLowUUIDApplyBox.Text))
1357 {
1358 RegionTexturesMiddleLowUUIDApplyBox.Text = data[1];
1359 }
1360 RegionTexturesMiddleHighUUIDBox.Text = data[2];
1361 if (string.IsNullOrEmpty(RegionTexturesMiddleHighUUIDApplyBox.Text))
1362 {
1363 RegionTexturesMiddleHighUUIDApplyBox.Text = data[2];
1364 }
1365 RegionTexturesHighUUIDBox.Text = data[3];
1366 if (string.IsNullOrEmpty(RegionTexturesHighUUIDApplyBox.Text))
1367 {
1368 RegionTexturesHighUUIDApplyBox.Text = data[1];
1369 }
1370 }));
1371  
1372 Parallel.ForEach(Enumerable.Range(0, 4), i =>
1373 {
1374 result = wasPOST(vassalConfiguration.HTTPServerURL,
1375 wasKeyValueEscape(new Dictionary<string, string>
1376 {
1377 {"command", "download"},
1378 {"group", vassalConfiguration.Group},
1379 {"password", vassalConfiguration.Password},
1380 {"item", data[i]},
1381 {"type", "Texture"},
1382 {"format", "Jpeg"}
1383 }), vassalConfiguration.DataTimeout);
1384  
1385 if (string.IsNullOrEmpty(result) ||
1386 !bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1387 return;
1388  
1389 byte[] mapImageBytes = Convert.FromBase64String(wasInput(wasKeyValueGet("data", result)));
1390 using (MemoryStream memoryStream = new MemoryStream(mapImageBytes, 0, mapImageBytes.Length))
1391 {
1392 groundTextureImages[i] = Image.FromStream(memoryStream);
1393 }
1394  
1395 switch (i)
1396 {
1397 case 0:
1398 vassalForm.BeginInvoke(
1399 (MethodInvoker)
1400 (() => { RegionTexturesLowPictureBox.Image = groundTextureImages[0]; }));
1401  
1402 break;
1403 case 1:
1404 vassalForm.BeginInvoke(
1405 (MethodInvoker)
1406 (() => { RegionTexturesMiddleLowPictureBox.Image = groundTextureImages[1]; }));
1407  
1408 break;
1409 case 2:
1410 vassalForm.BeginInvoke(
1411 (MethodInvoker)
1412 (() => { RegionTexturesMiddleHighPictureBox.Image = groundTextureImages[2]; }));
1413 break;
1414 case 3:
1415 vassalForm.BeginInvoke(
1416 (MethodInvoker)
1417 (() => { RegionTexturesHighPictureBox.Image = groundTextureImages[3]; }));
1418 break;
1419 }
1420 });
7 zed 1421 }
8 eva 1422 catch (Exception)
1423 {
1424 }
1425  
1426 estateTexturesTabTimer.Start();
7 zed 1427 };
8 eva 1428 estateTexturesTabTimer.Start();
2 zed 1429 }
1430  
1431 private void RequestedEditRegions(object sender, EventArgs e)
1432 {
1433 // Clear any selection.
8 eva 1434 LoadedRegionsBox.SelectedIndex = -1;
3 eva 1435 RegionEditForm regionEditForm = new RegionEditForm {TopMost = true};
2 zed 1436 regionEditForm.Show();
1437 }
1438  
1439 private void RequestSelecting(object sender, TabControlCancelEventArgs e)
1440 {
1441 e.Cancel = !e.TabPage.Enabled;
1442 }
3 eva 1443  
1444 private void RequestExportTopScripts(object sender, EventArgs e)
1445 {
1446 vassalForm.BeginInvoke((MethodInvoker) (() =>
1447 {
1448 switch (vassalForm.ExportCSVDialog.ShowDialog())
1449 {
1450 case DialogResult.OK:
1451 string file = vassalForm.ExportCSVDialog.FileName;
1452 new Thread(() =>
1453 {
1454 vassalForm.BeginInvoke((MethodInvoker) (() =>
1455 {
1456 try
1457 {
1458 vassalForm.StatusText.Text = @"exporting...";
1459 vassalForm.StatusProgress.Value = 0;
1460  
1461 using (StreamWriter streamWriter = new StreamWriter(file, false, Encoding.UTF8))
1462 {
1463 foreach (DataGridViewRow topScriptsRow in TopScriptsGridView.Rows)
1464 {
1465 streamWriter.WriteLine(wasEnumerableToCSV(new[]
1466 {
1467 topScriptsRow.Cells["TopScriptsScore"].Value.ToString(),
1468 topScriptsRow.Cells["TopScriptsTaskName"].Value.ToString(),
1469 topScriptsRow.Cells["TopScriptsUUID"].Value.ToString(),
1470 topScriptsRow.Cells["TopScriptsOwner"].Value.ToString(),
1471 topScriptsRow.Cells["TopScriptsPosition"].Value.ToString()
1472 }));
1473 }
1474 }
1475  
1476 vassalForm.StatusText.Text = @"exported";
1477 vassalForm.StatusProgress.Value = 100;
1478 }
1479 catch (Exception ex)
1480 {
1481 vassalForm.StatusText.Text = ex.Message;
1482 }
1483 }));
1484 })
7 zed 1485 {IsBackground = true}.Start();
3 eva 1486 break;
1487 }
1488 }));
1489 }
1490  
1491 private void RequestExportTopColliders(object sender, EventArgs e)
1492 {
1493 vassalForm.BeginInvoke((MethodInvoker) (() =>
1494 {
1495 switch (vassalForm.ExportCSVDialog.ShowDialog())
1496 {
1497 case DialogResult.OK:
1498 string file = vassalForm.ExportCSVDialog.FileName;
1499 new Thread(() =>
1500 {
1501 vassalForm.BeginInvoke((MethodInvoker) (() =>
1502 {
1503 try
1504 {
1505 vassalForm.StatusText.Text = @"exporting...";
1506 vassalForm.StatusProgress.Value = 0;
1507  
1508 using (StreamWriter streamWriter = new StreamWriter(file, false, Encoding.UTF8))
1509 {
1510 foreach (DataGridViewRow topCollidersRow in TopCollidersGridView.Rows)
1511 {
1512 streamWriter.WriteLine(wasEnumerableToCSV(new[]
1513 {
1514 topCollidersRow.Cells["TopCollidersScore"].Value.ToString(),
1515 topCollidersRow.Cells["TopCollidersTaskName"].Value.ToString(),
1516 topCollidersRow.Cells["TopCollidersUUID"].Value.ToString(),
1517 topCollidersRow.Cells["TopCollidersOwner"].Value.ToString(),
1518 topCollidersRow.Cells["TopCollidersPosition"].Value.ToString()
1519 }));
1520 }
1521 }
1522  
1523 vassalForm.StatusText.Text = @"exported";
1524 vassalForm.StatusProgress.Value = 100;
1525 }
1526 catch (Exception ex)
1527 {
1528 vassalForm.StatusText.Text = ex.Message;
1529 }
1530 }));
1531 })
7 zed 1532 {IsBackground = true}.Start();
3 eva 1533 break;
1534 }
1535 }));
1536 }
1537  
1538 private void RequestFilterTopScripts(object sender, EventArgs e)
1539 {
1540 vassalForm.BeginInvoke((MethodInvoker) (() =>
1541 {
1542 Regex topScriptsRowRegex;
1543 switch (!string.IsNullOrEmpty(TopScriptsFilter.Text))
1544 {
1545 case true:
1546 topScriptsRowRegex = new Regex(TopScriptsFilter.Text, RegexOptions.Compiled);
1547 break;
1548 default:
1549 topScriptsRowRegex = new Regex(@".+?", RegexOptions.Compiled);
1550 break;
1551 }
1552 foreach (DataGridViewRow topScriptsRow in TopScriptsGridView.Rows.AsParallel().Cast<DataGridViewRow>())
1553 {
1554 topScriptsRow.Visible =
1555 topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsScore"].Value.ToString()) ||
1556 topScriptsRowRegex.IsMatch(
1557 topScriptsRow.Cells["TopScriptsTaskName"].Value.ToString()) ||
1558 topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsUUID"].Value.ToString()) ||
1559 topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsOwner"].Value.ToString()) ||
1560 topScriptsRowRegex.IsMatch(
1561 topScriptsRow.Cells["TopScriptsPosition"].Value.ToString());
1562 }
1563 }));
1564 }
1565  
1566 private void RequestFilterTopColliders(object sender, EventArgs e)
1567 {
7 zed 1568 vassalForm.BeginInvoke((MethodInvoker) (() =>
3 eva 1569 {
1570 Regex topCollidersRowRegex;
1571 switch (!string.IsNullOrEmpty(TopScriptsFilter.Text))
1572 {
1573 case true:
1574 topCollidersRowRegex = new Regex(TopScriptsFilter.Text, RegexOptions.Compiled);
1575 break;
1576 default:
1577 topCollidersRowRegex = new Regex(@".+?", RegexOptions.Compiled);
1578 break;
1579 }
7 zed 1580 foreach (
1581 DataGridViewRow topCollidersRow in TopCollidersGridView.Rows.AsParallel().Cast<DataGridViewRow>())
3 eva 1582 {
1583 topCollidersRow.Visible =
1584 topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersScore"].Value.ToString()) ||
1585 topCollidersRowRegex.IsMatch(
1586 topCollidersRow.Cells["TopCollidersTaskName"].Value.ToString()) ||
1587 topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersUUID"].Value.ToString()) ||
1588 topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersOwner"].Value.ToString()) ||
1589 topCollidersRowRegex.IsMatch(
1590 topCollidersRow.Cells["TopCollidersPosition"].Value.ToString());
1591 }
1592 }));
1593 }
1594  
1595 private void RequestReturnTopScriptsObjects(object sender, EventArgs e)
1596 {
1597 // Block teleports and disable button.
1598 vassalForm.Invoke((MethodInvoker) (() =>
1599 {
1600 vassalForm.ReturnTopScriptsButton.Enabled = false;
1601 RegionTeleportGroup.Enabled = false;
1602 }));
1603  
1604 // Enqueue all the UUIDs to return.
1605 Queue<KeyValuePair<UUID, Vector3>> returnUUIDs = new Queue<KeyValuePair<UUID, Vector3>>();
1606 vassalForm.Invoke((MethodInvoker) (() =>
1607 {
1608 foreach (
1609 DataGridViewRow topScriptsRow in
1610 TopScriptsGridView.Rows.AsParallel()
1611 .Cast<DataGridViewRow>()
1612 .Where(o => o.Selected || o.Cells.Cast<DataGridViewCell>().Any(p => p.Selected)))
1613 {
1614 Vector3 objectPosition;
1615 UUID returnUUID;
1616 if (!UUID.TryParse(topScriptsRow.Cells["TopScriptsUUID"].Value.ToString(), out returnUUID) ||
1617 !Vector3.TryParse(topScriptsRow.Cells["TopScriptsPosition"].Value.ToString(), out objectPosition))
1618 continue;
1619 returnUUIDs.Enqueue(new KeyValuePair<UUID, Vector3>(returnUUID, objectPosition));
1620 }
1621 }));
1622  
1623 // If no rows were selected, enable teleports, the return button and return.
1624 if (returnUUIDs.Count.Equals(0))
1625 {
7 zed 1626 vassalForm.Invoke((MethodInvoker) (() =>
3 eva 1627 {
1628 vassalForm.ReturnTopScriptsButton.Enabled = true;
1629 RegionTeleportGroup.Enabled = true;
1630 }));
1631 return;
1632 }
1633  
1634 new Thread(() =>
1635 {
1636 Monitor.Enter(ClientInstanceTeleportLock);
1637  
1638 try
1639 {
8 eva 1640 vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusProgress.Value = 0; }));
3 eva 1641 int totalObjects = returnUUIDs.Count;
1642 do
1643 {
1644 // Dequeue the first object.
1645 KeyValuePair<UUID, Vector3> objectData = returnUUIDs.Dequeue();
1646  
8 eva 1647 vassalForm.Invoke(
1648 (MethodInvoker)
1649 (() => { vassalForm.StatusText.Text = @"Returning object UUID: " + objectData.Key; }));
3 eva 1650  
1651 string currentRegionName = string.Empty;
8 eva 1652 vassalForm.Invoke((MethodInvoker) (() => { currentRegionName = CurrentRegionName.Text; }));
7 zed 1653  
3 eva 1654 // Teleport to the object.
1655 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1656 wasKeyValueEscape(new Dictionary<string, string>
1657 {
1658 {"command", "teleport"},
1659 {"group", vassalConfiguration.Group},
1660 {"password", vassalConfiguration.Password},
1661 {"position", objectData.Value.ToString()},
1662 {"region", currentRegionName},
1663 {"fly", "True"}
1664 }), vassalConfiguration.TeleportTimeout);
1665  
1666 if (string.IsNullOrEmpty(result))
1667 {
8 eva 1668 vassalForm.Invoke(
1669 (MethodInvoker)
1670 (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade."; }));
3 eva 1671 continue;
1672 }
1673  
1674 // Return the object.
1675 result = wasPOST(vassalConfiguration.HTTPServerURL,
1676 wasKeyValueEscape(new Dictionary<string, string>
1677 {
1678 {"command", "derez"},
1679 {"group", vassalConfiguration.Group},
1680 {"password", vassalConfiguration.Password},
1681 {"item", objectData.Key.ToString()},
7 zed 1682 {"range", "32"}, // maximal prim size = 64 - middle bounding box at half
3 eva 1683 {"type", "ReturnToOwner"}
1684 }), vassalConfiguration.DataTimeout);
1685  
1686 if (string.IsNullOrEmpty(result))
1687 {
8 eva 1688 vassalForm.Invoke(
1689 (MethodInvoker)
1690 (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade."; }));
3 eva 1691 continue;
1692 }
1693  
1694 bool success;
1695 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1696 {
8 eva 1697 vassalForm.Invoke(
1698 (MethodInvoker)
1699 (() => { vassalForm.StatusText.Text = @"No success status could be retrieved. "; }));
3 eva 1700 continue;
1701 }
1702  
1703 switch (success)
1704 {
1705 case true:
1706 vassalForm.Invoke((MethodInvoker) (() =>
1707 {
8 eva 1708 vassalForm.StatusText.Text = @"Returned object: " + objectData.Key;
3 eva 1709 // Remove the row from the grid view.
7 zed 1710 DataGridViewRow row =
1711 TopScriptsGridView.Rows.AsParallel()
1712 .Cast<DataGridViewRow>()
1713 .FirstOrDefault(
1714 o => o.Cells["TopScriptsUUID"].Value.Equals(objectData.Key.ToString()));
3 eva 1715 if (row == null) return;
1716 int i = row.Index;
1717 TopScriptsGridView.Rows.RemoveAt(i);
1718 }));
1719 break;
1720 case false:
1721 vassalForm.Invoke((MethodInvoker) (() =>
1722 {
8 eva 1723 vassalForm.StatusText.Text = @"Could not return object " + objectData.Key +
3 eva 1724 @": " +
1725 wasInput(wasKeyValueGet("error", result));
1726 }));
1727 break;
1728 }
1729 vassalForm.Invoke((MethodInvoker) (() =>
1730 {
1731 vassalForm.StatusProgress.Value =
8 eva 1732 Math.Min((int) (100d*Math.Abs(returnUUIDs.Count - totalObjects)/totalObjects), 100);
3 eva 1733 }));
1734 } while (!returnUUIDs.Count.Equals(0));
8 eva 1735 vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusProgress.Value = 100; }));
3 eva 1736 }
1737 catch (Exception ex)
1738 {
8 eva 1739 vassalForm.Invoke(
1740 (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Unexpected error: " + ex.Message; }));
3 eva 1741 }
1742 finally
1743 {
1744 Monitor.Exit(ClientInstanceTeleportLock);
1745 // Allow teleports and enable button.
7 zed 1746 vassalForm.BeginInvoke((MethodInvoker) (() =>
3 eva 1747 {
1748 vassalForm.ReturnTopScriptsButton.Enabled = true;
1749 RegionTeleportGroup.Enabled = true;
1750 }));
1751 }
1752 })
1753 {IsBackground = true}.Start();
1754 }
1755  
1756 private void RequestReturnTopCollidersObjects(object sender, EventArgs e)
1757 {
1758 // Block teleports and disable button.
7 zed 1759 vassalForm.Invoke((MethodInvoker) (() =>
3 eva 1760 {
1761 vassalForm.ReturnTopCollidersButton.Enabled = false;
1762 RegionTeleportGroup.Enabled = false;
1763 }));
1764  
1765 // Enqueue all the UUIDs to return.
7 zed 1766 Queue<KeyValuePair<UUID, Vector3>> returnObjectUUIDQueue = new Queue<KeyValuePair<UUID, Vector3>>();
1767 vassalForm.Invoke((MethodInvoker) (() =>
3 eva 1768 {
1769 foreach (
1770 DataGridViewRow topCollidersRow in
1771 TopCollidersGridView.Rows.AsParallel()
1772 .Cast<DataGridViewRow>()
1773 .Where(o => o.Selected || o.Cells.Cast<DataGridViewCell>().Any(p => p.Selected)))
1774 {
1775 Vector3 objectPosition;
1776 UUID returnUUID;
1777 if (!UUID.TryParse(topCollidersRow.Cells["TopCollidersUUID"].Value.ToString(), out returnUUID) ||
7 zed 1778 !Vector3.TryParse(topCollidersRow.Cells["TopCollidersPosition"].Value.ToString(),
1779 out objectPosition))
3 eva 1780 continue;
7 zed 1781 returnObjectUUIDQueue.Enqueue(new KeyValuePair<UUID, Vector3>(returnUUID, objectPosition));
3 eva 1782 }
1783 }));
1784  
1785 // If no rows were selected, enable teleports, the return button and return.
7 zed 1786 if (returnObjectUUIDQueue.Count.Equals(0))
3 eva 1787 {
7 zed 1788 vassalForm.Invoke((MethodInvoker) (() =>
3 eva 1789 {
1790 vassalForm.ReturnTopCollidersButton.Enabled = true;
1791 RegionTeleportGroup.Enabled = true;
1792 }));
1793 return;
1794 }
1795  
1796 new Thread(() =>
1797 {
1798 Monitor.Enter(ClientInstanceTeleportLock);
1799  
1800 try
1801 {
8 eva 1802 vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusProgress.Value = 0; }));
7 zed 1803 int totalObjects = returnObjectUUIDQueue.Count;
3 eva 1804 do
1805 {
1806 // Dequeue the first object.
7 zed 1807 KeyValuePair<UUID, Vector3> objectData = returnObjectUUIDQueue.Dequeue();
3 eva 1808  
8 eva 1809 vassalForm.Invoke(
1810 (MethodInvoker)
1811 (() => { vassalForm.StatusText.Text = @"Returning UUID: " + objectData.Key; }));
3 eva 1812  
1813 string currentRegionName = string.Empty;
8 eva 1814 vassalForm.Invoke((MethodInvoker) (() => { currentRegionName = CurrentRegionName.Text; }));
3 eva 1815  
1816 // Teleport to the object.
1817 string result = wasPOST(vassalConfiguration.HTTPServerURL,
1818 wasKeyValueEscape(new Dictionary<string, string>
1819 {
1820 {"command", "teleport"},
1821 {"group", vassalConfiguration.Group},
1822 {"password", vassalConfiguration.Password},
1823 {"position", objectData.Value.ToString()},
1824 {"region", currentRegionName},
1825 {"fly", "True"}
1826 }), vassalConfiguration.DataTimeout);
1827  
1828 if (string.IsNullOrEmpty(result))
1829 {
8 eva 1830 vassalForm.Invoke(
1831 (MethodInvoker)
1832 (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade."; }));
3 eva 1833 continue;
1834 }
1835  
1836 // Return the object.
1837 result = wasPOST(vassalConfiguration.HTTPServerURL,
1838 wasKeyValueEscape(new Dictionary<string, string>
1839 {
1840 {"command", "derez"},
1841 {"group", vassalConfiguration.Group},
1842 {"password", vassalConfiguration.Password},
1843 {"item", objectData.Key.ToString()},
7 zed 1844 {"range", "32"}, // maximal prim size = 64 - middle bounding box at half
3 eva 1845 {"type", "ReturnToOwner"}
1846 }), vassalConfiguration.DataTimeout);
1847  
1848 if (string.IsNullOrEmpty(result))
1849 {
8 eva 1850 vassalForm.Invoke(
1851 (MethodInvoker)
1852 (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade."; }));
3 eva 1853 continue;
1854 }
1855  
1856 bool success;
1857 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
1858 {
8 eva 1859 vassalForm.Invoke(
1860 (MethodInvoker)
1861 (() => { vassalForm.StatusText.Text = @"No success status could be retrieved. "; }));
3 eva 1862 continue;
1863 }
1864  
1865 switch (success)
1866 {
1867 case true:
7 zed 1868 vassalForm.Invoke((MethodInvoker) (() =>
3 eva 1869 {
8 eva 1870 vassalForm.StatusText.Text = @"Returned object: " + objectData.Key;
3 eva 1871 // Remove the row from the grid view.
1872 DataGridViewRow row =
7 zed 1873 TopCollidersGridView.Rows.AsParallel()
1874 .Cast<DataGridViewRow>()
1875 .FirstOrDefault(
1876 o => o.Cells["TopCollidersUUID"].Value.Equals(objectData.Key.ToString()));
3 eva 1877 if (row == null) return;
1878 int i = row.Index;
1879 TopCollidersGridView.Rows.RemoveAt(i);
1880 }));
1881 break;
1882 case false:
7 zed 1883 vassalForm.Invoke((MethodInvoker) (() =>
3 eva 1884 {
8 eva 1885 vassalForm.StatusText.Text = @"Could not return object " + objectData.Key +
3 eva 1886 @": " +
1887 wasInput(wasKeyValueGet("error", result));
1888 }));
1889 break;
1890 }
7 zed 1891 vassalForm.Invoke((MethodInvoker) (() =>
3 eva 1892 {
1893 vassalForm.StatusProgress.Value =
8 eva 1894 Math.Min(
1895 (int) (100d*Math.Abs(returnObjectUUIDQueue.Count - totalObjects)/totalObjects), 100);
3 eva 1896 }));
7 zed 1897 } while (!returnObjectUUIDQueue.Count.Equals(0));
8 eva 1898 vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusProgress.Value = 100; }));
3 eva 1899 }
1900 catch (Exception ex)
1901 {
8 eva 1902 vassalForm.Invoke(
1903 (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Unexpected error: " + ex.Message; }));
3 eva 1904 }
1905 finally
1906 {
1907 Monitor.Exit(ClientInstanceTeleportLock);
1908 // Allow teleports and enable button.
7 zed 1909 vassalForm.BeginInvoke((MethodInvoker) (() =>
3 eva 1910 {
1911 vassalForm.ReturnTopScriptsButton.Enabled = true;
1912 RegionTeleportGroup.Enabled = true;
1913 }));
1914 }
1915 })
7 zed 1916 {IsBackground = true}.Start();
3 eva 1917 }
1918  
1919 private void RequestBatchRestart(object sender, EventArgs e)
1920 {
7 zed 1921 // Block teleports and disable button.
1922 vassalForm.Invoke((MethodInvoker) (() =>
1923 {
1924 vassalForm.BatchRestartButton.Enabled = false;
1925 RegionTeleportGroup.Enabled = false;
1926 }));
3 eva 1927  
7 zed 1928 // Enqueue all the regions to restart.
1929 Queue<KeyValuePair<string, Vector3>> restartRegionQueue = new Queue<KeyValuePair<string, Vector3>>();
1930 vassalForm.Invoke((MethodInvoker) (() =>
1931 {
1932 foreach (
1933 DataGridViewRow topCollidersRow in
1934 BatchRestartGridView.Rows.AsParallel()
1935 .Cast<DataGridViewRow>()
1936 .Where(o => o.Selected || o.Cells.Cast<DataGridViewCell>().Any(p => p.Selected)))
1937 {
1938 Vector3 objectPosition;
1939 string regionName = topCollidersRow.Cells["BatchRestartRegionName"].Value.ToString();
1940 if (string.IsNullOrEmpty(regionName) ||
1941 !Vector3.TryParse(topCollidersRow.Cells["BatchRestartPosition"].Value.ToString(),
1942 out objectPosition))
1943 continue;
1944 restartRegionQueue.Enqueue(new KeyValuePair<string, Vector3>(regionName, objectPosition));
1945 }
1946 }));
1947  
1948 // If no rows were selected, enable teleports, the return button and return.
1949 if (restartRegionQueue.Count.Equals(0))
1950 {
1951 vassalForm.Invoke((MethodInvoker) (() =>
1952 {
1953 vassalForm.BatchRestartButton.Enabled = true;
1954 RegionTeleportGroup.Enabled = true;
1955 }));
1956 return;
1957 }
1958  
1959 new Thread(() =>
1960 {
1961 Monitor.Enter(ClientInstanceTeleportLock);
1962  
1963 try
1964 {
1965 do
1966 {
1967 // Dequeue the first object.
1968 KeyValuePair<string, Vector3> restartRegionData = restartRegionQueue.Dequeue();
1969 DataGridViewRow currentDataGridViewRow = null;
1970 vassalForm.Invoke((MethodInvoker) (() =>
1971 {
1972 currentDataGridViewRow = vassalForm.BatchRestartGridView.Rows.AsParallel()
1973 .Cast<DataGridViewRow>()
1974 .FirstOrDefault(
1975 o =>
1976 o.Cells["BatchRestartRegionName"].Value.ToString()
1977 .Equals(restartRegionData.Key, StringComparison.OrdinalIgnoreCase) &&
1978 o.Cells["BatchRestartPosition"].Value.ToString()
1979 .Equals(restartRegionData.Value.ToString(),
1980 StringComparison.OrdinalIgnoreCase));
1981 }));
1982  
1983 if (currentDataGridViewRow == null) continue;
1984  
1985 try
1986 {
1987 bool success = false;
1988 string result;
1989  
1990 // Retry to teleport to each region a few times.
1991 int teleportRetries = 3;
1992 do
1993 {
1994 vassalForm.Invoke((MethodInvoker) (() =>
1995 {
1996 vassalForm.StatusText.Text = @"Attempting to teleport to " + restartRegionData.Key +
1997 @" " + @"(" +
1998 teleportRetries.ToString(Utils.EnUsCulture) +
1999 @")";
2000 }));
2001  
2002 // Teleport to the region.
2003 result = wasPOST(vassalConfiguration.HTTPServerURL,
2004 wasKeyValueEscape(new Dictionary<string, string>
2005 {
2006 {"command", "teleport"},
2007 {"group", vassalConfiguration.Group},
2008 {"password", vassalConfiguration.Password},
2009 {"position", restartRegionData.Value.ToString()},
2010 {"region", restartRegionData.Key},
2011 {"fly", "True"}
2012 }), vassalConfiguration.DataTimeout);
2013  
2014 if (string.IsNullOrEmpty(result))
2015 {
8 eva 2016 vassalForm.Invoke(
2017 (MethodInvoker)
2018 (() =>
2019 {
2020 vassalForm.StatusText.Text = @"Error communicating with Corrade.";
2021 }));
7 zed 2022 continue;
2023 }
2024 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2025 {
8 eva 2026 vassalForm.Invoke(
2027 (MethodInvoker)
2028 (() =>
2029 {
2030 vassalForm.StatusText.Text = @"No success status could be retrieved. ";
2031 }));
7 zed 2032 continue;
2033 }
2034 switch (success)
2035 {
2036 case true:
8 eva 2037 vassalForm.Invoke(
2038 (MethodInvoker)
2039 (() => { vassalForm.StatusText.Text = @"Teleport succeeded."; }));
7 zed 2040 Thread.Sleep(TimeSpan.FromSeconds(1).Milliseconds);
2041 break;
2042 default:
2043 // In case the destination is to close (Corrade status code 37559),
2044 // then we are on the same region so no need to retry.
2045 uint status; //37559
2046 switch (
2047 uint.TryParse(wasInput(wasKeyValueGet("status", result)), out status) &&
2048 status.Equals(37559))
2049 {
2050 case true: // We are on the region already!
2051 success = true;
2052 break;
2053 default:
8 eva 2054 vassalForm.Invoke(
2055 (MethodInvoker)
2056 (() => { vassalForm.StatusText.Text = @"Teleport failed."; }));
7 zed 2057 Thread.Sleep(10000);
2058 break;
2059 }
2060 break;
2061 }
2062 } while (!success && !(--teleportRetries).Equals(0));
2063  
2064 if (!success)
2065 throw new Exception("Failed to teleport to region.");
2066  
2067 result = wasPOST(vassalConfiguration.HTTPServerURL,
2068 wasKeyValueEscape(new Dictionary<string, string>
2069 {
2070 {"command", "getregiondata"},
2071 {"group", vassalConfiguration.Group},
2072 {"password", vassalConfiguration.Password},
2073 {"data", "IsEstateManager"}
2074 }), vassalConfiguration.DataTimeout);
2075  
2076 if (string.IsNullOrEmpty(result))
2077 throw new Exception("Error communicating with Corrade.");
2078  
2079 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2080 throw new Exception("No success status could be retrieved.");
2081  
2082 if (!success)
2083 throw new Exception("Could not retrieve estate rights.");
2084  
2085 List<string> data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
2086 if (!data.Count.Equals(2))
2087 throw new Exception("Could not retrieve estate rights.");
2088  
2089 bool isEstateManager;
2090 switch (
2091 bool.TryParse(data[data.IndexOf("IsEstateManager") + 1], out isEstateManager) &&
2092 isEstateManager)
2093 {
2094 case true: // we are an estate manager
2095 result = wasPOST(vassalConfiguration.HTTPServerURL,
2096 wasKeyValueEscape(new Dictionary<string, string>
2097 {
2098 {"command", "restartregion"},
2099 {"group", vassalConfiguration.Group},
2100 {"password", vassalConfiguration.Password},
2101 {"action", "restart"},
2102 {
8 eva 2103 "delay",
2104 vassalConfiguration.RegionRestartDelay.ToString(Utils.EnUsCulture)
7 zed 2105 }
2106 }), vassalConfiguration.DataTimeout);
2107  
2108 if (string.IsNullOrEmpty(result))
2109 throw new Exception("Error communicating with Corrade.");
2110  
2111 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2112 throw new Exception("No success status could be retrieved.");
2113  
2114 if (!success)
2115 throw new Exception("Could not schedule a region restart.");
2116  
2117 vassalForm.Invoke((MethodInvoker) (() =>
2118 {
2119 vassalForm.StatusText.Text = @"Region scheduled for restart.";
2120 currentDataGridViewRow.Selected = false;
2121 currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightGreen;
2122 foreach (
2123 DataGridViewCell cell in
2124 currentDataGridViewRow.Cells.AsParallel().Cast<DataGridViewCell>())
2125 {
2126 cell.ToolTipText = @"Region scheduled for restart.";
2127 }
2128 }));
2129 break;
2130 default:
2131 throw new Exception("No estate manager rights for region restart.");
2132 }
2133 }
2134 catch (Exception ex)
2135 {
2136 vassalForm.Invoke((MethodInvoker) (() =>
2137 {
2138 vassalForm.StatusText.Text = ex.Message;
2139 currentDataGridViewRow.Selected = false;
2140 currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightPink;
2141 foreach (
2142 DataGridViewCell cell in
2143 currentDataGridViewRow.Cells.AsParallel().Cast<DataGridViewCell>())
2144 {
2145 cell.ToolTipText = ex.Message;
2146 }
2147 }));
2148 }
2149 } while (!restartRegionQueue.Count.Equals(0));
2150 }
2151 catch (Exception)
2152 {
2153 }
2154 finally
2155 {
2156 Monitor.Exit(ClientInstanceTeleportLock);
2157 // Allow teleports and enable button.
2158 vassalForm.BeginInvoke((MethodInvoker) (() =>
2159 {
2160 vassalForm.BatchRestartButton.Enabled = true;
2161 RegionTeleportGroup.Enabled = true;
2162 }));
2163 }
2164 })
2165 {IsBackground = true}.Start();
3 eva 2166 }
7 zed 2167  
2168 private void RequestFilterResidentList(object sender, EventArgs e)
2169 {
2170 vassalForm.BeginInvoke((MethodInvoker) (() =>
2171 {
2172 Regex residentListRowRegex;
2173 switch (!string.IsNullOrEmpty(ResidentListFilter.Text))
2174 {
2175 case true:
2176 residentListRowRegex = new Regex(ResidentListFilter.Text, RegexOptions.Compiled);
2177 break;
2178 default:
2179 residentListRowRegex = new Regex(@".+?", RegexOptions.Compiled);
2180 break;
2181 }
2182 foreach (
2183 DataGridViewRow residentListRow in ResidentListGridView.Rows.AsParallel().Cast<DataGridViewRow>())
2184 {
2185 residentListRow.Visible =
2186 residentListRowRegex.IsMatch(residentListRow.Cells["ResidentListName"].Value.ToString()) ||
2187 residentListRowRegex.IsMatch(
2188 residentListRow.Cells["ResidentListUUID"].Value.ToString()) ||
2189 residentListRowRegex.IsMatch(residentListRow.Cells["ResidentListPosition"].Value.ToString());
2190 }
2191 }));
2192 }
2193  
2194 private void RequestBanAgents(object sender, EventArgs e)
2195 {
2196 // Block teleports and disable button.
2197 vassalForm.Invoke((MethodInvoker) (() =>
2198 {
2199 ResidentListBanGroup.Enabled = false;
2200 RegionTeleportGroup.Enabled = false;
2201 }));
2202  
2203 // Enqueue all the regions to restart.
2204 Queue<UUID> agentsQueue = new Queue<UUID>();
2205 vassalForm.Invoke((MethodInvoker) (() =>
2206 {
2207 foreach (
2208 DataGridViewRow residentListRow in
2209 ResidentListGridView.Rows.AsParallel()
2210 .Cast<DataGridViewRow>()
2211 .Where(o => o.Selected || o.Cells.Cast<DataGridViewCell>().Any(p => p.Selected)))
2212 {
2213 UUID agentUUID;
2214 if (!UUID.TryParse(residentListRow.Cells["ResidentListUUID"].Value.ToString(), out agentUUID))
2215 continue;
2216 agentsQueue.Enqueue(agentUUID);
2217 }
2218 }));
2219  
2220 // If no rows were selected, enable teleports, the return button and return.
2221 if (agentsQueue.Count.Equals(0))
2222 {
2223 vassalForm.Invoke((MethodInvoker) (() =>
2224 {
2225 ResidentListBanGroup.Enabled = true;
2226 RegionTeleportGroup.Enabled = true;
2227 }));
2228 return;
2229 }
2230  
2231 new Thread(() =>
2232 {
2233 Monitor.Enter(ClientInstanceTeleportLock);
2234 try
2235 {
2236 do
2237 {
2238 // Dequeue the first object.
2239 UUID agentUUID = agentsQueue.Dequeue();
2240 DataGridViewRow currentDataGridViewRow = null;
2241 vassalForm.Invoke((MethodInvoker) (() =>
2242 {
2243 currentDataGridViewRow = vassalForm.ResidentListGridView.Rows.AsParallel()
2244 .Cast<DataGridViewRow>()
2245 .FirstOrDefault(
2246 o =>
2247 o.Cells["ResidentListUUID"].Value.ToString()
2248 .Equals(agentUUID.ToString(), StringComparison.OrdinalIgnoreCase));
2249 }));
2250  
2251 if (currentDataGridViewRow == null) continue;
2252  
2253 try
2254 {
2255 bool alsoBan = false;
8 eva 2256 vassalForm.Invoke(
2257 (MethodInvoker) (() => { alsoBan = vassalForm.ResidentBanAllEstatesBox.Checked; }));
7 zed 2258  
2259 // Teleport to the region.
2260 string result = wasPOST(vassalConfiguration.HTTPServerURL,
2261 wasKeyValueEscape(new Dictionary<string, string>
2262 {
2263 {"command", "setestatelist"},
2264 {"group", vassalConfiguration.Group},
2265 {"password", vassalConfiguration.Password},
2266 {"type", "ban"},
2267 {"action", "add"},
2268 {"agent", agentUUID.ToString()},
2269 {"all", alsoBan.ToString()}
2270 }), vassalConfiguration.DataTimeout);
2271  
2272 if (string.IsNullOrEmpty(result))
2273 throw new Exception("Error communicating with Corrade.");
2274  
2275 bool success;
2276 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2277 throw new Exception("No success status could be retrieved.");
2278  
2279 switch (success)
2280 {
2281 case true:
2282 vassalForm.Invoke((MethodInvoker) (() =>
2283 {
2284 vassalForm.StatusText.Text = @"Resident banned.";
2285 currentDataGridViewRow.Selected = false;
2286 currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightGreen;
2287 foreach (
2288 DataGridViewCell cell in
2289 currentDataGridViewRow.Cells.AsParallel().Cast<DataGridViewCell>())
2290 {
2291 cell.ToolTipText = @"Resident banned.";
2292 }
2293 }));
2294 break;
2295 default:
2296 throw new Exception("Unable to ban resident.");
2297 }
2298 }
2299 catch (Exception ex)
2300 {
2301 vassalForm.Invoke((MethodInvoker) (() =>
2302 {
2303 vassalForm.StatusText.Text = ex.Message;
2304 currentDataGridViewRow.Selected = false;
2305 currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightPink;
2306 foreach (
2307 DataGridViewCell cell in
2308 currentDataGridViewRow.Cells.AsParallel().Cast<DataGridViewCell>())
2309 {
2310 cell.ToolTipText = ex.Message;
2311 }
2312 }));
2313 }
2314 } while (agentsQueue.Count.Equals(0));
2315 }
2316 catch (Exception)
2317 {
2318 }
2319 finally
2320 {
2321 Monitor.Exit(ClientInstanceTeleportLock);
2322 // Allow teleports and enable button.
2323 vassalForm.BeginInvoke((MethodInvoker) (() =>
2324 {
2325 ResidentListBanGroup.Enabled = true;
2326 RegionTeleportGroup.Enabled = true;
2327 }));
2328 }
2329 })
2330 {IsBackground = true}.Start();
2331 }
2332  
2333 private void RequestRipTerrain(object sender, EventArgs e)
2334 {
2335 // Block teleports and disable button.
2336 vassalForm.Invoke((MethodInvoker) (() =>
2337 {
2338 RegionTeleportGroup.Enabled = false;
2339 RipTerrainButton.Enabled = false;
2340 }));
2341  
2342 new Thread(() =>
2343 {
2344 Monitor.Enter(ClientInstanceTeleportLock);
2345  
2346 try
2347 {
8 eva 2348 // Get the map heights.
7 zed 2349 string result = wasPOST(vassalConfiguration.HTTPServerURL,
2350 wasKeyValueEscape(new Dictionary<string, string>
2351 {
2352 {"command", "getterrainheight"},
2353 {"group", vassalConfiguration.Group},
2354 {"password", vassalConfiguration.Password},
2355 {"entity", "region"}
2356 }), vassalConfiguration.DataTimeout);
2357  
2358 if (string.IsNullOrEmpty(result))
2359 throw new Exception("Error communicating with Corrade.");
2360  
2361 bool success;
2362 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2363 throw new Exception("No success status could be retrieved.");
2364  
2365 if (!success)
2366 throw new Exception("Could not get terrain heights.");
2367  
2368 List<double> heights = new List<double>();
2369 foreach (string map in wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))))
2370 {
2371 double height;
2372 if (!double.TryParse(map, out height))
2373 continue;
2374 heights.Add(height);
2375 }
2376 if (heights.Count.Equals(0))
2377 throw new Exception("Could not get terrain heights.");
2378  
2379 double maxHeight = heights.Max();
2380 using (Bitmap bitmap = new Bitmap(256, 256))
2381 {
2382 foreach (int x in Enumerable.Range(1, 255))
2383 {
2384 foreach (int y in Enumerable.Range(1, 255))
2385 {
2386 bitmap.SetPixel(x, 256 - y,
8 eva 2387 Color.FromArgb(
2388 Math.Max((int) wasMapValueToRange(heights[256*x + y], 0, maxHeight, 0, 255), 0),
2389 0, 0));
7 zed 2390 }
2391 }
8 eva 2392 Bitmap closureBitmap = (Bitmap) bitmap.Clone();
7 zed 2393 vassalForm.BeginInvoke((MethodInvoker) (() =>
2394 {
8 eva 2395 switch (vassalForm.SavePNGFileDialog.ShowDialog())
7 zed 2396 {
2397 case DialogResult.OK:
8 eva 2398 string file = vassalForm.SavePNGFileDialog.FileName;
7 zed 2399 new Thread(() =>
2400 {
2401 vassalForm.BeginInvoke((MethodInvoker) (() =>
2402 {
2403 try
2404 {
2405 vassalForm.StatusText.Text = @"saving terrain...";
2406 vassalForm.StatusProgress.Value = 0;
2407  
2408 closureBitmap.Save(file, ImageFormat.Png);
2409  
2410 vassalForm.StatusText.Text = @"terrain saved";
2411 vassalForm.StatusProgress.Value = 100;
2412 }
2413 catch (Exception ex)
2414 {
2415 vassalForm.StatusText.Text = ex.Message;
2416 }
2417 finally
2418 {
2419 closureBitmap.Dispose();
2420 }
2421 }));
2422 })
8 eva 2423 {IsBackground = true}.Start();
7 zed 2424 break;
2425 }
2426 }));
2427 }
2428 }
2429 catch (Exception ex)
2430 {
8 eva 2431 vassalForm.BeginInvoke((MethodInvoker) (() => { StatusText.Text = ex.Message; }));
2432 }
2433 finally
2434 {
2435 Monitor.Exit(ClientInstanceTeleportLock);
2436 vassalForm.BeginInvoke((MethodInvoker) (() =>
7 zed 2437 {
8 eva 2438 RegionTeleportGroup.Enabled = true;
2439 RipTerrainButton.Enabled = true;
7 zed 2440 }));
2441 }
8 eva 2442 }) {IsBackground = true}.Start();
2443 }
2444  
2445 private void RequestDownloadTerrain(object sender, EventArgs e)
2446 {
2447 // Block teleports and disable button.
2448 vassalForm.Invoke((MethodInvoker) (() =>
2449 {
2450 RegionTeleportGroup.Enabled = false;
2451 DownloadTerrainButton.Enabled = false;
2452 }));
2453  
2454 new Thread(() =>
2455 {
2456 Monitor.Enter(ClientInstanceTeleportLock);
2457  
2458 try
2459 {
2460 // Download the terrain.
2461 string result = wasPOST(vassalConfiguration.HTTPServerURL,
2462 wasKeyValueEscape(new Dictionary<string, string>
2463 {
2464 {"command", "terrain"},
2465 {"group", vassalConfiguration.Group},
2466 {"password", vassalConfiguration.Password},
2467 {"action", "get"}
2468 }), vassalConfiguration.DataTimeout);
2469  
2470 if (string.IsNullOrEmpty(result))
2471 throw new Exception("Error communicating with Corrade.");
2472  
2473 bool success;
2474 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2475 throw new Exception("No success status could be retrieved.");
2476  
2477 if (!success)
2478 throw new Exception("Could not download terrain.");
2479  
2480 byte[] data = Convert.FromBase64String(wasInput(wasKeyValueGet("data", result)));
2481  
2482 vassalForm.BeginInvoke((MethodInvoker) (() =>
2483 {
2484 switch (vassalForm.SaveRawFileDialog.ShowDialog())
2485 {
2486 case DialogResult.OK:
2487 string file = vassalForm.SaveRawFileDialog.FileName;
2488 new Thread(() =>
2489 {
2490 vassalForm.BeginInvoke((MethodInvoker) (() =>
2491 {
2492 try
2493 {
2494 vassalForm.StatusText.Text = @"saving terrain...";
2495 vassalForm.StatusProgress.Value = 0;
2496  
2497 File.WriteAllBytes(file, data);
2498  
2499 vassalForm.StatusText.Text = @"terrain saved";
2500 vassalForm.StatusProgress.Value = 100;
2501 }
2502 catch (Exception ex)
2503 {
2504 vassalForm.StatusText.Text = ex.Message;
2505 }
2506 }));
2507 })
2508 {IsBackground = true}.Start();
2509 break;
2510 }
2511 }));
2512 }
2513 catch (Exception ex)
2514 {
2515 vassalForm.BeginInvoke((MethodInvoker) (() => { StatusText.Text = ex.Message; }));
2516 }
7 zed 2517 finally
2518 {
2519 Monitor.Exit(ClientInstanceTeleportLock);
8 eva 2520 vassalForm.BeginInvoke((MethodInvoker) (() =>
7 zed 2521 {
8 eva 2522 DownloadTerrainButton.Enabled = true;
7 zed 2523 RegionTeleportGroup.Enabled = true;
2524 }));
2525 }
8 eva 2526 })
2527 {IsBackground = true}.Start();
2528 }
7 zed 2529  
8 eva 2530 private void RequestUploadTerrain(object sender, EventArgs e)
2531 {
2532 // Block teleports and disable button.
2533 vassalForm.Invoke((MethodInvoker) (() =>
2534 {
2535 RegionTeleportGroup.Enabled = false;
2536 UploadTerrainButton.Enabled = false;
2537 }));
2538  
2539 new Thread(() =>
2540 {
2541 Monitor.Enter(ClientInstanceTeleportLock);
2542  
2543 try
2544 {
2545 byte[] data = null;
2546 vassalForm.Invoke((MethodInvoker) (() =>
2547 {
2548 switch (vassalForm.LoadRawFileDialog.ShowDialog())
2549 {
2550 case DialogResult.OK:
2551 string file = vassalForm.LoadRawFileDialog.FileName;
2552 vassalForm.StatusText.Text = @"loading terrain...";
2553 vassalForm.StatusProgress.Value = 0;
2554  
2555 data = File.ReadAllBytes(file);
2556  
2557 vassalForm.StatusText.Text = @"terrain loaded";
2558 vassalForm.StatusProgress.Value = 100;
2559 break;
2560 }
2561 }));
2562  
2563 // Upload the terrain.
2564 string result = wasPOST(vassalConfiguration.HTTPServerURL,
2565 wasKeyValueEscape(new Dictionary<string, string>
2566 {
2567 {"command", "terrain"},
2568 {"group", vassalConfiguration.Group},
2569 {"password", vassalConfiguration.Password},
2570 {"action", "set"},
2571 {"data", Convert.ToBase64String(data)}
2572 }), vassalConfiguration.DataTimeout);
2573  
2574 if (string.IsNullOrEmpty(result))
2575 throw new Exception("Error communicating with Corrade.");
2576  
2577 bool success;
2578 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2579 throw new Exception("No success status could be retrieved.");
2580  
2581 if (!success)
2582 throw new Exception("Could not upload terrain.");
2583 }
2584 catch (Exception ex)
2585 {
2586 vassalForm.BeginInvoke((MethodInvoker) (() => { StatusText.Text = ex.Message; }));
2587 }
2588 finally
2589 {
2590 Monitor.Exit(ClientInstanceTeleportLock);
2591 vassalForm.BeginInvoke((MethodInvoker) (() =>
2592 {
2593 UploadTerrainButton.Enabled = true;
2594 RegionTeleportGroup.Enabled = true;
2595 }));
2596 }
2597 })
2598 {IsBackground = true}.Start();
7 zed 2599 }
8 eva 2600  
2601 private void RequestTabsChanged(object sender, EventArgs e)
2602 {
2603 vassalForm.BeginInvoke((MethodInvoker) (() =>
2604 {
2605 overviewTabTimer.Stop();
2606 regionsStateTabTimer.Stop();
2607 residentListTabTimer.Stop();
2608 estateTopTabTimer.Stop();
2609  
2610 if (Tabs.SelectedTab.Equals(OverviewTab))
2611 {
2612 overviewTabTimer.Start();
2613 return;
2614 }
2615 if (Tabs.SelectedTab.Equals(RegionsStateTab))
2616 {
2617 regionsStateTabTimer.Start();
2618 return;
2619 }
2620 if (Tabs.SelectedTab.Equals(ResidentListTab))
2621 {
2622 residentListTabTimer.Start();
2623 return;
2624 }
2625 if (Tabs.SelectedTab.Equals(EstateTopTab))
2626 {
2627 estateTopTabTimer.Start();
2628 return;
2629 }
2630 if (Tabs.SelectedTab.Equals(EstateTexturesTab))
2631 {
2632 estateTexturesTabTimer.Start();
2633 }
2634 }));
2635 }
2636  
2637 private void RequestFilterEstateList(object sender, EventArgs e)
2638 {
2639 vassalForm.BeginInvoke((MethodInvoker) (() =>
2640 {
2641 Regex estateListRowRegex;
2642 switch (!string.IsNullOrEmpty(EstateListFilter.Text))
2643 {
2644 case true:
2645 estateListRowRegex = new Regex(EstateListFilter.Text, RegexOptions.Compiled);
2646 break;
2647 default:
2648 estateListRowRegex = new Regex(@".+?", RegexOptions.Compiled);
2649 break;
2650 }
2651 foreach (DataGridViewRow estateListRow in EstateListGridView.Rows.AsParallel().Cast<DataGridViewRow>())
2652 {
2653 estateListRow.Visible =
2654 estateListRowRegex.IsMatch(estateListRow.Cells["EstateListName"].Value.ToString());
2655 }
2656 }));
2657 }
2658  
2659 private void EstateListSelected(object sender, EventArgs e)
2660 {
2661 string selectedEstateListType = string.Empty;
2662 bool queryEstateList = false;
2663 vassalForm.Invoke((MethodInvoker) (() =>
2664 {
2665 if (vassalForm.EstateListSelectBox.SelectedItem == null) return;
2666 selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString();
2667 switch (
2668 !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1)
2669 {
2670 case true:
2671 queryEstateList = true;
2672 break;
2673 default:
2674 EstateListsResidentsGroup.Enabled = false;
2675 EstateListsGroupsGroup.Enabled = false;
2676 queryEstateList = false;
2677 break;
2678 }
2679 }));
2680  
2681 if (!queryEstateList) return;
2682  
2683 new Thread(() =>
2684 {
2685 try
2686 {
2687 Monitor.Enter(ClientInstanceTeleportLock);
2688  
2689 // Disable controls.
2690 vassalForm.Invoke((MethodInvoker) (() =>
2691 {
2692 RegionTeleportGroup.Enabled = false;
2693 EstateListsResidentsGroup.Enabled = false;
2694 EstateListsGroupsGroup.Enabled = false;
2695 }));
2696  
2697 // Get the selected estate list.
2698 string result = wasPOST(vassalConfiguration.HTTPServerURL,
2699 wasKeyValueEscape(new Dictionary<string, string>
2700 {
2701 {"command", "getestatelist"},
2702 {"group", vassalConfiguration.Group},
2703 {"password", vassalConfiguration.Password},
2704 {"type", selectedEstateListType}
2705 }), vassalConfiguration.DataTimeout);
2706  
2707 if (string.IsNullOrEmpty(result))
2708 throw new Exception("Error communicating with Corrade.");
2709  
2710 bool success;
2711 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2712 throw new Exception("No success status could be retrieved.");
2713  
2714 if (!success)
2715 throw new Exception("Could not retrieve estate list.");
2716  
2717 vassalForm.Invoke((MethodInvoker) (() => { EstateListGridView.Rows.Clear(); }));
2718 foreach (List<string> data in wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
2719 .Where(x => !string.IsNullOrEmpty(x))
2720 .Select((x, i) => new {Index = i, Value = x})
2721 .GroupBy(x => x.Index/2)
2722 .Select(x => x.Select(v => v.Value).ToList()).Where(x => x.Count.Equals(2)))
2723 {
2724 vassalForm.Invoke(
2725 (MethodInvoker) (() => { EstateListGridView.Rows.Add(data.First(), data.Last()); }));
2726 }
2727 }
2728 catch (Exception ex)
2729 {
2730 vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = ex.Message; }));
2731 }
2732 finally
2733 {
2734 Monitor.Exit(ClientInstanceTeleportLock);
2735 // Enable controls
2736 vassalForm.Invoke((MethodInvoker) (() =>
2737 {
2738 vassalForm.RegionTeleportGroup.Enabled = true;
2739  
2740 switch (selectedEstateListType)
2741 {
2742 case "ban":
2743 case "manager":
2744 case "user":
2745 EstateListsResidentsGroup.Enabled = true;
2746 EstateListsGroupsGroup.Enabled = false;
2747 break;
2748 case "group":
2749 EstateListsResidentsGroup.Enabled = false;
2750 EstateListsGroupsGroup.Enabled = true;
2751 break;
2752 }
2753 }));
2754 }
2755 })
2756 {IsBackground = true}.Start();
2757 }
2758  
2759 private void RequestRemoveEstateListMember(object sender, EventArgs e)
2760 {
2761 // Get the estate list type.
2762 string selectedEstateListType = string.Empty;
2763 bool queryEstateList = false;
2764 vassalForm.Invoke((MethodInvoker) (() =>
2765 {
2766 if (vassalForm.EstateListSelectBox.SelectedItem == null)
2767 {
2768 queryEstateList = false;
2769 return;
2770 }
2771 selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString();
2772 switch (
2773 !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1)
2774 {
2775 case true:
2776 queryEstateList = true;
2777 break;
2778 default:
2779 queryEstateList = false;
2780 break;
2781 }
2782 }));
2783  
2784 // If not estate list type is selected then return.
2785 if (!queryEstateList) return;
2786  
2787 // Enqueue all the regions to restart.
2788 Queue<UUID> estateListMembersQueue = new Queue<UUID>();
2789 vassalForm.Invoke((MethodInvoker) (() =>
2790 {
2791 foreach (
2792 DataGridViewRow estateListRow in
2793 EstateListGridView.Rows.AsParallel()
2794 .Cast<DataGridViewRow>()
2795 .Where(o => o.Selected || o.Cells.Cast<DataGridViewCell>().Any(p => p.Selected)))
2796 {
2797 UUID estateListMemberUUID;
2798 if (!UUID.TryParse(estateListRow.Cells["EstateListUUID"].Value.ToString(),
2799 out estateListMemberUUID))
2800 continue;
2801 estateListMembersQueue.Enqueue(estateListMemberUUID);
2802 }
2803 }));
2804  
2805 // If no rows were selected, enable teleports, the return button and return.
2806 if (estateListMembersQueue.Count.Equals(0)) return;
2807  
2808 // Block teleports and disable button.
2809 vassalForm.Invoke((MethodInvoker) (() =>
2810 {
2811 RegionTeleportGroup.Enabled = false;
2812 RemoveEstateListMemberButton.Enabled = false;
2813 }));
2814  
2815 new Thread(() =>
2816 {
2817 try
2818 {
2819 Monitor.Enter(ClientInstanceTeleportLock);
2820 UUID memberUUID = UUID.Zero;
2821 do
2822 {
2823 try
2824 {
2825 memberUUID = estateListMembersQueue.Dequeue();
2826  
2827 // Remove the agent or group from the list.
2828 string result = wasPOST(vassalConfiguration.HTTPServerURL,
2829 wasKeyValueEscape(new Dictionary<string, string>
2830 {
2831 {"command", "setestatelist"},
2832 {"group", vassalConfiguration.Group},
2833 {"password", vassalConfiguration.Password},
2834 {"type", selectedEstateListType},
2835 {"action", "remove"},
2836 {selectedEstateListType.Equals("group") ? "target" : "agent", memberUUID.ToString()}
2837 }), vassalConfiguration.DataTimeout);
2838  
2839 if (string.IsNullOrEmpty(result))
2840 throw new Exception("Error communicating with Corrade");
2841  
2842 bool success;
2843 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2844 throw new Exception("No success status could be retrieved");
2845  
2846 if (!success)
2847 throw new Exception("Unable to remove member");
2848  
2849 vassalForm.Invoke((MethodInvoker) (() =>
2850 {
2851 foreach (
2852 int i in
2853 EstateListGridView.Rows.AsParallel()
2854 .Cast<DataGridViewRow>()
2855 .Where(
2856 o =>
2857 o.Cells["EstateListUUID"].Value.ToString()
2858 .Equals(memberUUID.ToString(),
2859 StringComparison.OrdinalIgnoreCase)).Select(o => o.Index)
2860 )
2861 {
2862 EstateListGridView.Rows.RemoveAt(i);
2863 }
2864 }));
2865 }
2866 catch (Exception ex)
2867 {
2868 vassalForm.Invoke(
2869 (MethodInvoker) (() => { StatusText.Text = ex.Message + @": " + memberUUID; }));
2870 }
2871 } while (!estateListMembersQueue.Count.Equals(0));
2872 }
2873 catch (Exception)
2874 {
2875 }
2876 finally
2877 {
2878 Monitor.Exit(ClientInstanceTeleportLock);
2879 // Enable teleports and enable button.
2880 vassalForm.Invoke((MethodInvoker) (() =>
2881 {
2882 RegionTeleportGroup.Enabled = true;
2883 RemoveEstateListMemberButton.Enabled = true;
2884 }));
2885 }
2886 })
2887 {IsBackground = true}.Start();
2888 }
2889  
2890 private void RequestEstateListsAddResident(object sender, EventArgs e)
2891 {
2892 // Get the estate list type.
2893 string selectedEstateListType = string.Empty;
2894 bool queryEstateList = false;
2895 vassalForm.Invoke((MethodInvoker) (() =>
2896 {
2897 selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString();
2898 switch (
2899 !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1)
2900 {
2901 case true:
2902 queryEstateList = true;
2903 break;
2904 default:
2905 queryEstateList = false;
2906 break;
2907 }
2908 }));
2909  
2910 // If not estate list type is selected then return.
2911 if (!queryEstateList) return;
2912  
2913 string firstName = string.Empty;
2914 string lastName = string.Empty;
2915  
2916 vassalForm.Invoke((MethodInvoker) (() =>
2917 {
2918 switch (string.IsNullOrEmpty(EstateListsResidentFirstName.Text))
2919 {
2920 case true:
2921 EstateListsResidentFirstName.BackColor = Color.MistyRose;
2922 return;
2923 default:
2924 EstateListsResidentFirstName.BackColor = Color.Empty;
2925 break;
2926 }
2927 firstName = EstateListsResidentFirstName.Text;
2928 switch (string.IsNullOrEmpty(EstateListsResidentLastName.Text))
2929 {
2930 case true:
2931 EstateListsResidentLastName.BackColor = Color.MistyRose;
2932 return;
2933 default:
2934 EstateListsResidentLastName.BackColor = Color.Empty;
2935 break;
2936 }
2937 lastName = EstateListsResidentLastName.Text;
2938 }));
2939  
2940 if (string.IsNullOrEmpty(firstName) || string.IsNullOrEmpty(lastName)) return;
2941  
2942 // Block teleports and disable button.
2943 vassalForm.Invoke((MethodInvoker) (() =>
2944 {
2945 RegionTeleportGroup.Enabled = false;
2946 EstateListsResidentsGroup.Enabled = false;
2947 }));
2948  
2949 new Thread(() =>
2950 {
2951 try
2952 {
2953 Monitor.Enter(ClientInstanceTeleportLock);
2954  
2955 // Add the resident to the list.
2956 string result = wasPOST(vassalConfiguration.HTTPServerURL,
2957 wasKeyValueEscape(new Dictionary<string, string>
2958 {
2959 {"command", "setestatelist"},
2960 {"group", vassalConfiguration.Group},
2961 {"password", vassalConfiguration.Password},
2962 {"type", selectedEstateListType},
2963 {"action", "add"},
2964 {"firstname", firstName},
2965 {"lastname", lastName}
2966 }), vassalConfiguration.DataTimeout);
2967  
2968 if (string.IsNullOrEmpty(result))
2969 throw new Exception("Error communicating with Corrade");
2970  
2971 bool success;
2972 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2973 throw new Exception("No success status could be retrieved");
2974  
2975 if (!success)
2976 throw new Exception("Unable to add resident");
2977  
2978 // Retrieve the estate list for updates.
2979 result = wasPOST(vassalConfiguration.HTTPServerURL,
2980 wasKeyValueEscape(new Dictionary<string, string>
2981 {
2982 {"command", "getestatelist"},
2983 {"group", vassalConfiguration.Group},
2984 {"password", vassalConfiguration.Password},
2985 {"type", selectedEstateListType}
2986 }), vassalConfiguration.DataTimeout);
2987  
2988 if (string.IsNullOrEmpty(result))
2989 throw new Exception("Error communicating with Corrade.");
2990  
2991 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
2992 throw new Exception("No success status could be retrieved.");
2993  
2994 if (!success)
2995 throw new Exception("Could not retrieve estate list.");
2996  
2997 vassalForm.Invoke((MethodInvoker) (() => { EstateListGridView.Rows.Clear(); }));
2998 foreach (List<string> data in wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
2999 .Where(x => !string.IsNullOrEmpty(x))
3000 .Select((x, i) => new {Index = i, Value = x})
3001 .GroupBy(x => x.Index/2)
3002 .Select(x => x.Select(v => v.Value).ToList()).Where(x => x.Count.Equals(2)))
3003 {
3004 vassalForm.Invoke(
3005 (MethodInvoker) (() => { EstateListGridView.Rows.Add(data.First(), data.Last()); }));
3006 }
3007 }
3008 catch (Exception ex)
3009 {
3010 vassalForm.Invoke(
3011 (MethodInvoker) (() => { StatusText.Text = ex.Message + @": " + firstName + @" " + lastName; }));
3012 }
3013 finally
3014 {
3015 Monitor.Exit(ClientInstanceTeleportLock);
3016 // Enable teleports and enable button.
3017 vassalForm.Invoke((MethodInvoker) (() =>
3018 {
3019 RegionTeleportGroup.Enabled = true;
3020 EstateListsResidentsGroup.Enabled = true;
3021 }));
3022 }
3023 })
3024 {IsBackground = true}.Start();
3025 }
3026  
3027 private void RequestEstateListsAddGroup(object sender, EventArgs e)
3028 {
3029 // Get the estate list type.
3030 string selectedEstateListType = string.Empty;
3031 bool queryEstateList = false;
3032 vassalForm.Invoke((MethodInvoker) (() =>
3033 {
3034 selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString();
3035 switch (
3036 !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1)
3037 {
3038 case true:
3039 queryEstateList = true;
3040 break;
3041 default:
3042 queryEstateList = false;
3043 break;
3044 }
3045 }));
3046  
3047 // If not estate list type is selected then return.
3048 if (!queryEstateList) return;
3049  
3050 string target = string.Empty;
3051  
3052 vassalForm.Invoke((MethodInvoker) (() =>
3053 {
3054 switch (string.IsNullOrEmpty(EstateListsAddGroupBox.Text))
3055 {
3056 case true:
3057 EstateListsAddGroupBox.BackColor = Color.MistyRose;
3058 return;
3059 default:
3060 EstateListsAddGroupBox.BackColor = Color.Empty;
3061 break;
3062 }
3063 target = EstateListsAddGroupBox.Text;
3064 }));
3065  
3066 if (string.IsNullOrEmpty(target)) return;
3067  
3068 // Block teleports and disable button.
3069 vassalForm.Invoke((MethodInvoker) (() =>
3070 {
3071 RegionTeleportGroup.Enabled = false;
3072 EstateListsGroupsGroup.Enabled = false;
3073 }));
3074  
3075 new Thread(() =>
3076 {
3077 try
3078 {
3079 Monitor.Enter(ClientInstanceTeleportLock);
3080  
3081 // Add the group to the list.
3082 string result = wasPOST(vassalConfiguration.HTTPServerURL,
3083 wasKeyValueEscape(new Dictionary<string, string>
3084 {
3085 {"command", "setestatelist"},
3086 {"group", vassalConfiguration.Group},
3087 {"password", vassalConfiguration.Password},
3088 {"type", selectedEstateListType},
3089 {"action", "add"},
3090 {"target", target}
3091 }), vassalConfiguration.DataTimeout);
3092  
3093 if (string.IsNullOrEmpty(result))
3094 throw new Exception("Error communicating with Corrade");
3095  
3096 bool success;
3097 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
3098 throw new Exception("No success status could be retrieved");
3099  
3100 if (!success)
3101 throw new Exception("Unable to add group");
3102  
3103 // Retrieve the estate list for updates.
3104 result = wasPOST(vassalConfiguration.HTTPServerURL,
3105 wasKeyValueEscape(new Dictionary<string, string>
3106 {
3107 {"command", "getestatelist"},
3108 {"group", vassalConfiguration.Group},
3109 {"password", vassalConfiguration.Password},
3110 {"type", selectedEstateListType}
3111 }), vassalConfiguration.DataTimeout);
3112  
3113 if (string.IsNullOrEmpty(result))
3114 throw new Exception("Error communicating with Corrade.");
3115  
3116 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
3117 throw new Exception("No success status could be retrieved.");
3118  
3119 if (!success)
3120 throw new Exception("Could not retrieve estate list.");
3121  
3122 vassalForm.Invoke((MethodInvoker) (() => { EstateListGridView.Rows.Clear(); }));
3123 foreach (List<string> data in wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
3124 .Where(x => !string.IsNullOrEmpty(x))
3125 .Select((x, i) => new {Index = i, Value = x})
3126 .GroupBy(x => x.Index/2)
3127 .Select(x => x.Select(v => v.Value).ToList()).Where(x => x.Count.Equals(2)))
3128 {
3129 vassalForm.Invoke(
3130 (MethodInvoker) (() => { EstateListGridView.Rows.Add(data.First(), data.Last()); }));
3131 }
3132 }
3133 catch (Exception ex)
3134 {
3135 vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message + @": " + target; }));
3136 }
3137 finally
3138 {
3139 Monitor.Exit(ClientInstanceTeleportLock);
3140 // Enable teleports and enable button.
3141 vassalForm.Invoke((MethodInvoker) (() =>
3142 {
3143 RegionTeleportGroup.Enabled = true;
3144 EstateListsGroupsGroup.Enabled = true;
3145 }));
3146 }
3147 })
3148 {IsBackground = true}.Start();
3149 }
3150  
3151 private void RequestRegionDebugApply(object sender, EventArgs e)
3152 {
3153 // Block teleports and disable button.
3154 vassalForm.Invoke((MethodInvoker) (() =>
3155 {
3156 RegionTeleportGroup.Enabled = false;
3157 ApplyRegionDebugButton.Enabled = false;
3158 }));
3159  
3160 new Thread(() =>
3161 {
3162 try
3163 {
3164 Monitor.Enter(ClientInstanceTeleportLock);
3165  
3166 bool scripts = false;
3167 bool collisons = false;
3168 bool physics = false;
3169 vassalForm.Invoke((MethodInvoker) (() =>
3170 {
3171 scripts = RegionDebugScriptsBox.Checked;
3172 collisons = RegionDebugCollisionsBox.Checked;
3173 physics = RegionDebugPhysicsBox.Checked;
3174 }));
3175  
3176 // Set the debug settings.
3177 string result = wasPOST(vassalConfiguration.HTTPServerURL,
3178 wasKeyValueEscape(new Dictionary<string, string>
3179 {
3180 {"command", "setregiondebug"},
3181 {"group", vassalConfiguration.Group},
3182 {"password", vassalConfiguration.Password},
3183 {"scripts", scripts.ToString()},
3184 {"collisions", collisons.ToString()},
3185 {"physics", physics.ToString()}
3186 }), vassalConfiguration.DataTimeout);
3187  
3188 if (string.IsNullOrEmpty(result))
3189 throw new Exception("Error communicating with Corrade");
3190  
3191 bool success;
3192 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
3193 throw new Exception("No success status could be retrieved");
3194  
3195 if (!success)
3196 throw new Exception("Unable to set region debug");
3197 }
3198 catch (Exception ex)
3199 {
3200 vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; }));
3201 }
3202 finally
3203 {
3204 Monitor.Exit(ClientInstanceTeleportLock);
3205 }
3206  
3207 // Block teleports and disable button.
3208 vassalForm.Invoke((MethodInvoker) (() =>
3209 {
3210 RegionTeleportGroup.Enabled = true;
3211 ApplyRegionDebugButton.Enabled = true;
3212 }));
3213 })
3214 {IsBackground = true}.Start();
3215 }
3216  
3217 private void RequestApplyRegionInfo(object sender, EventArgs e)
3218 {
3219 // Block teleports and disable button.
3220 vassalForm.Invoke((MethodInvoker) (() =>
3221 {
3222 RegionTeleportGroup.Enabled = false;
3223 ApplyRegionInfoButton.Enabled = false;
3224 }));
3225  
3226 new Thread(() =>
3227 {
3228 try
3229 {
3230 Monitor.Enter(ClientInstanceTeleportLock);
3231  
3232 bool terraform = false;
3233 bool fly = false;
3234 bool damage = false;
3235 bool resell = false;
3236 bool push = false;
3237 bool parcel = false;
3238 bool mature = false;
3239 uint agentLimit = 20;
3240 double objectBonus = 2.0;
3241  
3242 bool run = false;
3243  
3244 vassalForm.Invoke((MethodInvoker) (() =>
3245 {
3246 terraform = RegionInfoTerraformBox.Checked;
3247 fly = RegionInfoFlyBox.Checked;
3248 damage = RegionInfoDamageBox.Checked;
3249 resell = RegioninfoResellBox.Checked;
3250 push = RegionInfoPushBox.Checked;
3251 parcel = RegionInfoParcelBox.Checked;
3252 mature = RegionInfoMatureBox.Checked;
3253 switch (!uint.TryParse(RegionInfoAgentLimitBox.Text, out agentLimit))
3254 {
3255 case true:
3256 RegionInfoAgentLimitBox.BackColor = Color.MistyRose;
3257 return;
3258 default:
3259 RegionInfoAgentLimitBox.BackColor = Color.Empty;
3260 run = true;
3261 break;
3262 }
3263 switch (!double.TryParse(RegionInfoObjectBonusBox.Text, out objectBonus))
3264 {
3265 case true:
3266 RegionInfoObjectBonusBox.BackColor = Color.MistyRose;
3267 return;
3268 default:
3269 RegionInfoAgentLimitBox.BackColor = Color.Empty;
3270 break;
3271 }
3272  
3273 run = true;
3274 }));
3275  
3276 if (!run) return;
3277  
3278 // Set the debug settings.
3279 string result = wasPOST(vassalConfiguration.HTTPServerURL,
3280 wasKeyValueEscape(new Dictionary<string, string>
3281 {
3282 {"command", "setregioninfo"},
3283 {"group", vassalConfiguration.Group},
3284 {"password", vassalConfiguration.Password},
3285 {"terraform", terraform.ToString()},
3286 {"fly", fly.ToString()},
3287 {"damage", damage.ToString()},
3288 {"resell", resell.ToString()},
3289 {"push", push.ToString()},
3290 {"parcel", parcel.ToString()},
3291 {"mature", mature.ToString()},
3292 {"limit", agentLimit.ToString(Utils.EnUsCulture)},
3293 {"bonus", objectBonus.ToString(Utils.EnUsCulture)}
3294 }), vassalConfiguration.DataTimeout);
3295  
3296 if (string.IsNullOrEmpty(result))
3297 throw new Exception("Error communicating with Corrade");
3298  
3299 bool success;
3300 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
3301 throw new Exception("No success status could be retrieved");
3302  
3303 if (!success)
3304 throw new Exception("Unable to set region info");
3305 }
3306 catch (Exception ex)
3307 {
3308 vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; }));
3309 }
3310 finally
3311 {
3312 Monitor.Exit(ClientInstanceTeleportLock);
3313 }
3314  
3315 // Block teleports and disable button.
3316 vassalForm.Invoke((MethodInvoker) (() =>
3317 {
3318 RegionTeleportGroup.Enabled = true;
3319 ApplyRegionInfoButton.Enabled = true;
3320 }));
3321 })
3322 {IsBackground = true}.Start();
3323 }
3324  
3325 private void RequestEstateTexturesApply(object sender, EventArgs e)
3326 {
3327 List<UUID> groundTextureUUIDs = new List<UUID>();
3328 vassalForm.Invoke((MethodInvoker) (() =>
3329 {
3330 UUID textureUUID;
3331 switch (!UUID.TryParse(RegionTexturesLowUUIDApplyBox.Text, out textureUUID))
3332 {
3333 case true:
3334 RegionTexturesLowUUIDApplyBox.BackColor = Color.MistyRose;
3335 return;
3336 default:
3337 RegionTexturesLowUUIDApplyBox.BackColor = Color.Empty;
3338 break;
3339 }
3340 groundTextureUUIDs.Add(textureUUID);
3341  
3342 switch (!UUID.TryParse(RegionTexturesMiddleLowUUIDApplyBox.Text, out textureUUID))
3343 {
3344 case true:
3345 RegionTexturesMiddleLowUUIDApplyBox.BackColor = Color.MistyRose;
3346 return;
3347 default:
3348 RegionTexturesMiddleLowUUIDApplyBox.BackColor = Color.Empty;
3349 break;
3350 }
3351 groundTextureUUIDs.Add(textureUUID);
3352  
3353 switch (!UUID.TryParse(RegionTexturesMiddleHighUUIDApplyBox.Text, out textureUUID))
3354 {
3355 case true:
3356 RegionTexturesMiddleHighUUIDApplyBox.BackColor = Color.MistyRose;
3357 return;
3358 default:
3359 RegionTexturesMiddleHighUUIDApplyBox.BackColor = Color.Empty;
3360 break;
3361 }
3362 groundTextureUUIDs.Add(textureUUID);
3363  
3364 switch (!UUID.TryParse(RegionTexturesHighUUIDApplyBox.Text, out textureUUID))
3365 {
3366 case true:
3367 RegionTexturesHighUUIDApplyBox.BackColor = Color.MistyRose;
3368 return;
3369 default:
3370 RegionTexturesHighUUIDApplyBox.BackColor = Color.Empty;
3371 break;
3372 }
3373 groundTextureUUIDs.Add(textureUUID);
3374 }));
3375  
3376 if (!groundTextureUUIDs.Count.Equals(4)) return;
3377  
3378 // Block teleports and disable button.
3379 vassalForm.Invoke((MethodInvoker) (() =>
3380 {
3381 RegionTeleportGroup.Enabled = false;
3382 GroundTexturesGroup.Enabled = false;
3383 }));
3384  
3385 new Thread(() =>
3386 {
3387 try
3388 {
3389 Monitor.Enter(ClientInstanceTeleportLock);
3390  
3391 // Set the debug settings.
3392 string result = wasPOST(vassalConfiguration.HTTPServerURL,
3393 wasKeyValueEscape(new Dictionary<string, string>
3394 {
3395 {"command", "setregionterraintextures"},
3396 {"group", vassalConfiguration.Group},
3397 {"password", vassalConfiguration.Password},
3398 {"data", wasEnumerableToCSV(groundTextureUUIDs.Select(o => o.ToString()))}
3399 }), vassalConfiguration.DataTimeout);
3400  
3401 if (string.IsNullOrEmpty(result))
3402 throw new Exception("Error communicating with Corrade");
3403  
3404 bool success;
3405 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
3406 throw new Exception("No success status could be retrieved");
3407  
3408 if (!success)
3409 throw new Exception("Unable to apply estate ground textures");
3410 }
3411 catch (Exception ex)
3412 {
3413 vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; }));
3414 }
3415 finally
3416 {
3417 Monitor.Exit(ClientInstanceTeleportLock);
3418 }
3419  
3420 // Block teleports and disable button.
3421 vassalForm.Invoke((MethodInvoker) (() =>
3422 {
3423 RegionTeleportGroup.Enabled = true;
3424 GroundTexturesGroup.Enabled = true;
3425 }));
3426 })
3427 {IsBackground = true};
3428 }
3429  
3430 private void RequestDownloadRegionTexture(object sender, EventArgs e)
3431 {
3432 string textureName = string.Empty;
3433 UUID textureUUID = UUID.Zero;
3434 bool run = false;
3435 vassalForm.Invoke((MethodInvoker) (() =>
3436 {
3437 Button button = sender as Button;
3438 if (button == null)
3439 {
3440 run = false;
3441 return;
3442 }
3443 textureName = button.Tag.ToString();
3444 switch (textureName)
3445 {
3446 case "Low":
3447 if (!UUID.TryParse(RegionTexturesLowUUIDBox.Text, out textureUUID))
3448 {
3449 run = false;
3450 return;
3451 }
3452 goto case "MiddleLow";
3453 case "MiddleLow":
3454 if (!UUID.TryParse(RegionTexturesMiddleLowUUIDBox.Text, out textureUUID))
3455 {
3456 run = false;
3457 return;
3458 }
3459 goto case "MiddleHigh";
3460 case "MiddleHigh":
3461 if (!UUID.TryParse(RegionTexturesMiddleHighUUIDBox.Text, out textureUUID))
3462 {
3463 run = false;
3464 return;
3465 }
3466 goto case "High";
3467 case "High":
3468 if (!UUID.TryParse(RegionTexturesHighUUIDBox.Text, out textureUUID))
3469 {
3470 run = false;
3471 return;
3472 }
3473 run = true;
3474 break;
3475 }
3476 }));
3477  
3478 if (!run) return;
3479  
3480 // Block teleports and disable button.
3481 vassalForm.Invoke((MethodInvoker) (() =>
3482 {
3483 RegionTeleportGroup.Enabled = false;
3484 GroundTexturesGroup.Enabled = false;
3485 }));
3486  
3487 new Thread(() =>
3488 {
3489 try
3490 {
3491 Monitor.Enter(ClientInstanceTeleportLock);
3492  
3493 string result = wasPOST(vassalConfiguration.HTTPServerURL,
3494 wasKeyValueEscape(new Dictionary<string, string>
3495 {
3496 {"command", "download"},
3497 {"group", vassalConfiguration.Group},
3498 {"password", vassalConfiguration.Password},
3499 {"item", textureUUID.ToString()},
3500 {"type", "Texture"},
3501 {"format", "Png"}
3502 }), vassalConfiguration.DataTimeout);
3503  
3504 if (string.IsNullOrEmpty(result))
3505 throw new Exception("Error communicating with Corrade");
3506  
3507 bool success;
3508 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
3509 throw new Exception("No success status could be retrieved");
3510  
3511 byte[] mapImageBytes = Convert.FromBase64String(wasInput(wasKeyValueGet("data", result)));
3512 Image mapImage;
3513 using (MemoryStream memoryStream = new MemoryStream(mapImageBytes, 0, mapImageBytes.Length))
3514 {
3515 mapImage = Image.FromStream(memoryStream);
3516 }
3517  
3518 vassalForm.BeginInvoke((MethodInvoker) (() =>
3519 {
3520 switch (vassalForm.SavePNGFileDialog.ShowDialog())
3521 {
3522 case DialogResult.OK:
3523 string file = vassalForm.SavePNGFileDialog.FileName;
3524 new Thread(() =>
3525 {
3526 vassalForm.BeginInvoke((MethodInvoker) (() =>
3527 {
3528 try
3529 {
3530 vassalForm.StatusText.Text = @"saving texture...";
3531 vassalForm.StatusProgress.Value = 0;
3532  
3533 mapImage.Save(file, ImageFormat.Png);
3534  
3535 vassalForm.StatusText.Text = @"texture saved";
3536 vassalForm.StatusProgress.Value = 100;
3537 }
3538 catch (Exception ex)
3539 {
3540 vassalForm.StatusText.Text = ex.Message;
3541 }
3542 finally
3543 {
3544 mapImage.Dispose();
3545 }
3546 }));
3547 })
3548 {IsBackground = true}.Start();
3549 break;
3550 }
3551 }));
3552 }
3553 catch (Exception ex)
3554 {
3555 vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; }));
3556 }
3557 finally
3558 {
3559 Monitor.Exit(ClientInstanceTeleportLock);
3560 }
3561  
3562 // Block teleports and disable button.
3563 vassalForm.Invoke((MethodInvoker) (() =>
3564 {
3565 RegionTeleportGroup.Enabled = true;
3566 GroundTexturesGroup.Enabled = true;
3567 }));
3568 })
3569 {IsBackground = true}.Start();
3570 }
3571  
3572 private void RequestEstateListsAddGroupsFromCSV(object sender, EventArgs e)
3573 {
3574 // Get the estate list type.
3575 string selectedEstateListType = string.Empty;
3576 bool queryEstateList = false;
3577 vassalForm.Invoke((MethodInvoker) (() =>
3578 {
3579 if (vassalForm.EstateListSelectBox.SelectedItem == null) return;
3580 selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString();
3581 switch (
3582 !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1)
3583 {
3584 case true:
3585 queryEstateList = true;
3586 break;
3587 default:
3588 queryEstateList = false;
3589 break;
3590 }
3591 }));
3592  
3593 // If not estate list type is selected then return.
3594 if (!queryEstateList) return;
3595  
3596 Queue<KeyValuePair<string, UUID>> targets = new Queue<KeyValuePair<string, UUID>>();
3597  
3598 vassalForm.Invoke((MethodInvoker) (() =>
3599 {
3600 switch (vassalForm.LoadCSVFile.ShowDialog())
3601 {
3602 case DialogResult.OK:
3603 string file = vassalForm.LoadCSVFile.FileName;
3604 try
3605 {
3606 vassalForm.StatusText.Text = @"loading group list...";
3607 vassalForm.StatusProgress.Value = 0;
3608  
3609 // import groups
3610 UUID targetUUID;
3611 foreach (KeyValuePair<string, UUID> target in
3612 File.ReadAllLines(file)
3613 .AsParallel()
3614 .Select(o => new List<string>(wasCSVToEnumerable(o)))
3615 .Where(o => o.Count == 2)
3616 .ToDictionary(o => o.First(),
3617 p =>
3618 UUID.TryParse(p.Last(), out targetUUID)
3619 ? targetUUID
3620 : UUID.Zero))
3621 {
3622 targets.Enqueue(target);
3623 }
3624  
3625 vassalForm.StatusText.Text = @"group list loaded";
3626 vassalForm.StatusProgress.Value = 100;
3627 }
3628 catch (Exception ex)
3629 {
3630 vassalForm.StatusText.Text = ex.Message;
3631 }
3632 break;
3633 }
3634 }));
3635  
3636 if (targets.Count.Equals(0)) return;
3637 int initialQueueSize = targets.Count;
3638  
3639 // Block teleports and disable button.
3640 vassalForm.Invoke((MethodInvoker) (() =>
3641 {
3642 RegionTeleportGroup.Enabled = false;
3643 EstateListsGroupsGroup.Enabled = false;
3644 }));
3645  
3646 new Thread(() =>
3647 {
3648 try
3649 {
3650 Monitor.Enter(ClientInstanceTeleportLock);
3651  
3652 do
3653 {
3654 KeyValuePair<string, UUID> target = targets.Dequeue();
3655  
3656 vassalForm.Invoke((MethodInvoker) (() =>
3657 {
3658 StatusText.Text = @"Adding to estate list: " + target.Key;
3659 vassalForm.StatusProgress.Value =
3660 Math.Min((int) (100d*Math.Abs(targets.Count - initialQueueSize)/initialQueueSize), 100);
3661 }));
3662  
3663 // Skip any items that already exist.
3664 bool run = false;
3665 vassalForm.Invoke((MethodInvoker) (() =>
3666 {
3667 run =
3668 !EstateListGridView.Rows.AsParallel()
3669 .Cast<DataGridViewRow>()
3670 .Any(o => o.Cells["EstateListUUID"].Value.ToString().Equals(target.Value.ToString()));
3671 }));
3672 if (!run) continue;
3673  
3674 // Skip broken UUIDs.
3675 if (target.Value.Equals(UUID.Zero)) continue;
3676  
3677 try
3678 {
3679 // Add the group to the list.
3680 string result = wasPOST(vassalConfiguration.HTTPServerURL,
3681 wasKeyValueEscape(new Dictionary<string, string>
3682 {
3683 {"command", "setestatelist"},
3684 {"group", vassalConfiguration.Group},
3685 {"password", vassalConfiguration.Password},
3686 {"type", selectedEstateListType},
3687 {"action", "add"},
3688 {"target", target.Value.ToString()}
3689 }), vassalConfiguration.DataTimeout);
3690  
3691 if (string.IsNullOrEmpty(result))
3692 throw new Exception("Error communicating with Corrade");
3693  
3694 bool success;
3695 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
3696 throw new Exception("No success status could be retrieved");
3697  
3698 if (!success)
3699 throw new Exception("Unable to add group");
3700 }
3701 catch (Exception ex)
3702 {
3703 vassalForm.Invoke(
3704 (MethodInvoker) (() => { StatusText.Text = ex.Message + @": " + target.Value; }));
3705 }
3706 } while (!targets.Count.Equals(0));
3707  
3708 // Retrieve the estate list.
3709 try
3710 {
3711 // Retrieve the estate list for updates.
3712 string result = wasPOST(vassalConfiguration.HTTPServerURL,
3713 wasKeyValueEscape(new Dictionary<string, string>
3714 {
3715 {"command", "getestatelist"},
3716 {"group", vassalConfiguration.Group},
3717 {"password", vassalConfiguration.Password},
3718 {"type", selectedEstateListType}
3719 }), vassalConfiguration.DataTimeout);
3720  
3721 if (string.IsNullOrEmpty(result))
3722 throw new Exception("Error communicating with Corrade.");
3723  
3724 bool success;
3725 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
3726 throw new Exception("No success status could be retrieved.");
3727  
3728 if (!success)
3729 throw new Exception("Could not retrieve estate list.");
3730  
3731 vassalForm.Invoke((MethodInvoker) (() => { EstateListGridView.Rows.Clear(); }));
3732 foreach (List<string> data in wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
3733 .Where(x => !string.IsNullOrEmpty(x))
3734 .Select((x, i) => new {Index = i, Value = x})
3735 .GroupBy(x => x.Index/2)
3736 .Select(x => x.Select(v => v.Value).ToList()))
3737 {
3738 vassalForm.BeginInvoke(
3739 (MethodInvoker) (() => { EstateListGridView.Rows.Add(data.First(), data.Last()); }));
3740 }
3741 }
3742 catch (Exception ex)
3743 {
3744 vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; }));
3745 }
3746 }
3747 catch (Exception ex)
3748 {
3749 vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; }));
3750 }
3751 finally
3752 {
3753 Monitor.Exit(ClientInstanceTeleportLock);
3754 // Enable teleports and enable button.
3755 vassalForm.Invoke((MethodInvoker) (() =>
3756 {
3757 RegionTeleportGroup.Enabled = true;
3758 EstateListsGroupsGroup.Enabled = true;
3759 }));
3760 }
3761 })
3762 {IsBackground = true}.Start();
3763 }
3764  
3765 private void RequestEstateListsAddResidentsFromCSV(object sender, EventArgs e)
3766 {
3767 // Get the estate list type.
3768 string selectedEstateListType = string.Empty;
3769 bool queryEstateList = false;
3770 vassalForm.Invoke((MethodInvoker) (() =>
3771 {
3772 if (vassalForm.EstateListSelectBox.SelectedItem == null) return;
3773 selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString();
3774 switch (
3775 !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1)
3776 {
3777 case true:
3778 queryEstateList = true;
3779 break;
3780 default:
3781 queryEstateList = false;
3782 break;
3783 }
3784 }));
3785  
3786 // If not estate list type is selected then return.
3787 if (!queryEstateList) return;
3788  
3789 Queue<KeyValuePair<string, UUID>> targets = new Queue<KeyValuePair<string, UUID>>();
3790  
3791 vassalForm.Invoke((MethodInvoker) (() =>
3792 {
3793 switch (vassalForm.LoadCSVFile.ShowDialog())
3794 {
3795 case DialogResult.OK:
3796 string file = vassalForm.LoadCSVFile.FileName;
3797 try
3798 {
3799 vassalForm.StatusText.Text = @"loading residents list...";
3800 vassalForm.StatusProgress.Value = 0;
3801  
3802 // import groups
3803 UUID targetUUID;
3804 foreach (KeyValuePair<string, UUID> target in
3805 File.ReadAllLines(file)
3806 .AsParallel()
3807 .Select(o => new List<string>(wasCSVToEnumerable(o)))
3808 .Where(o => o.Count == 2)
3809 .ToDictionary(o => o.First(),
3810 p =>
3811 UUID.TryParse(p.Last(), out targetUUID)
3812 ? targetUUID
3813 : UUID.Zero))
3814 {
3815 targets.Enqueue(target);
3816 }
3817  
3818 vassalForm.StatusText.Text = @"residents list loaded";
3819 vassalForm.StatusProgress.Value = 100;
3820 }
3821 catch (Exception ex)
3822 {
3823 vassalForm.StatusText.Text = ex.Message;
3824 }
3825 break;
3826 }
3827 }));
3828  
3829 if (targets.Count.Equals(0)) return;
3830 int initialQueueSize = targets.Count;
3831  
3832 // Block teleports and disable button.
3833 vassalForm.Invoke((MethodInvoker) (() =>
3834 {
3835 RegionTeleportGroup.Enabled = false;
3836 EstateListsResidentsGroup.Enabled = false;
3837 }));
3838  
3839 new Thread(() =>
3840 {
3841 try
3842 {
3843 Monitor.Enter(ClientInstanceTeleportLock);
3844  
3845 do
3846 {
3847 KeyValuePair<string, UUID> target = targets.Dequeue();
3848  
3849 vassalForm.Invoke((MethodInvoker) (() =>
3850 {
3851 StatusText.Text = @"Adding to estate list: " + target.Key;
3852 vassalForm.StatusProgress.Value =
3853 Math.Min((int) (100d*Math.Abs(targets.Count - initialQueueSize)/initialQueueSize), 100);
3854 }));
3855  
3856 // Skip any items that already exist.
3857 bool run = false;
3858 vassalForm.Invoke((MethodInvoker) (() =>
3859 {
3860 run =
3861 !EstateListGridView.Rows.AsParallel()
3862 .Cast<DataGridViewRow>()
3863 .Any(o => o.Cells["EstateListUUID"].Value.ToString().Equals(target.Value.ToString()));
3864 }));
3865 if (!run) continue;
3866  
3867 // Skip broken UUIDs.
3868 if (target.Value.Equals(UUID.Zero)) continue;
3869  
3870 try
3871 {
3872 // Add the group to the list.
3873 string result = wasPOST(vassalConfiguration.HTTPServerURL,
3874 wasKeyValueEscape(new Dictionary<string, string>
3875 {
3876 {"command", "setestatelist"},
3877 {"group", vassalConfiguration.Group},
3878 {"password", vassalConfiguration.Password},
3879 {"type", selectedEstateListType},
3880 {"action", "add"},
3881 {"agent", target.Value.ToString()}
3882 }), vassalConfiguration.DataTimeout);
3883  
3884 if (string.IsNullOrEmpty(result))
3885 throw new Exception("Error communicating with Corrade");
3886  
3887 bool success;
3888 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
3889 throw new Exception("No success status could be retrieved");
3890  
3891 if (!success)
3892 throw new Exception("Unable to add resident");
3893 }
3894 catch (Exception ex)
3895 {
3896 vassalForm.Invoke(
3897 (MethodInvoker) (() => { StatusText.Text = ex.Message + @": " + target.Value; }));
3898 }
3899 } while (!targets.Count.Equals(0));
3900  
3901 // Retrieve the estate list.
3902 try
3903 {
3904 // Retrieve the estate list for updates.
3905 string result = wasPOST(vassalConfiguration.HTTPServerURL,
3906 wasKeyValueEscape(new Dictionary<string, string>
3907 {
3908 {"command", "getestatelist"},
3909 {"group", vassalConfiguration.Group},
3910 {"password", vassalConfiguration.Password},
3911 {"type", selectedEstateListType}
3912 }), vassalConfiguration.DataTimeout);
3913  
3914 if (string.IsNullOrEmpty(result))
3915 throw new Exception("Error communicating with Corrade.");
3916  
3917 bool success;
3918 if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
3919 throw new Exception("No success status could be retrieved.");
3920  
3921 if (!success)
3922 throw new Exception("Could not retrieve estate list.");
3923  
3924 vassalForm.Invoke((MethodInvoker) (() => { EstateListGridView.Rows.Clear(); }));
3925 foreach (List<string> data in wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
3926 .Where(x => !string.IsNullOrEmpty(x))
3927 .Select((x, i) => new {Index = i, Value = x})
3928 .GroupBy(x => x.Index/2)
3929 .Select(x => x.Select(v => v.Value).ToList()))
3930 {
3931 vassalForm.BeginInvoke(
3932 (MethodInvoker) (() => { EstateListGridView.Rows.Add(data.First(), data.Last()); }));
3933 }
3934 }
3935 catch (Exception ex)
3936 {
3937 vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; }));
3938 }
3939 }
3940 catch (Exception ex)
3941 {
3942 vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; }));
3943 }
3944 finally
3945 {
3946 Monitor.Exit(ClientInstanceTeleportLock);
3947 // Enable teleports and enable button.
3948 vassalForm.Invoke((MethodInvoker) (() =>
3949 {
3950 RegionTeleportGroup.Enabled = true;
3951 EstateListsResidentsGroup.Enabled = true;
3952 }));
3953 }
3954 })
3955 {IsBackground = true}.Start();
3956 }
3957  
3958 private void RequestExportEstateList(object sender, EventArgs e)
3959 {
3960 vassalForm.BeginInvoke((MethodInvoker) (() =>
3961 {
3962 switch (vassalForm.ExportCSVDialog.ShowDialog())
3963 {
3964 case DialogResult.OK:
3965 string file = vassalForm.ExportCSVDialog.FileName;
3966 new Thread(() =>
3967 {
3968 vassalForm.BeginInvoke((MethodInvoker) (() =>
3969 {
3970 try
3971 {
3972 vassalForm.StatusText.Text = @"exporting...";
3973 vassalForm.StatusProgress.Value = 0;
3974  
3975 using (StreamWriter streamWriter = new StreamWriter(file, false, Encoding.UTF8))
3976 {
3977 foreach (DataGridViewRow estateListRow in EstateListGridView.Rows)
3978 {
3979 streamWriter.WriteLine(wasEnumerableToCSV(new[]
3980 {
3981 estateListRow.Cells["EstateListName"].Value.ToString(),
3982 estateListRow.Cells["EstateListUUID"].Value.ToString()
3983 }));
3984 }
3985 }
3986  
3987 vassalForm.StatusText.Text = @"exported";
3988 vassalForm.StatusProgress.Value = 100;
3989 }
3990 catch (Exception ex)
3991 {
3992 vassalForm.StatusText.Text = ex.Message;
3993 }
3994 }));
3995 })
3996 {IsBackground = true}.Start();
3997 break;
3998 }
3999 }));
4000 }
4001  
4002 /// <summary>
4003 /// Linden constants.
4004 /// </summary>
4005 public struct LINDEN_CONSTANTS
4006 {
4007 public struct ALERTS
4008 {
4009 public const string NO_ROOM_TO_SIT_HERE = @"No room to sit here, try another spot.";
4010  
4011 public const string UNABLE_TO_SET_HOME =
4012 @"You can only set your 'Home Location' on your land or at a mainland Infohub.";
4013  
4014 public const string HOME_SET = @"Home position set.";
4015 }
4016  
4017 public struct ASSETS
4018 {
4019 public struct NOTECARD
4020 {
4021 public const string NEWLINE = "\n";
4022 public const uint MAXIMUM_BODY_LENTH = 65536;
4023 }
4024 }
4025  
4026 public struct AVATARS
4027 {
4028 public const uint SET_DISPLAY_NAME_SUCCESS = 200;
4029 public const string LASTNAME_PLACEHOLDER = @"Resident";
4030 public const uint MAXIMUM_DISPLAY_NAME_CHARACTERS = 31;
4031 public const uint MINIMUM_DISPLAY_NAME_CHARACTERS = 1;
4032 public const uint MAXIMUM_NUMBER_OF_ATTACHMENTS = 38;
4033  
4034 public struct PROFILE
4035 {
4036 public const uint SECOND_LIFE_TEXT_SIZE = 510;
4037 public const uint FIRST_LIFE_TEXT_SIZE = 253;
4038 }
4039  
4040 public struct PICKS
4041 {
4042 public const uint MAXIMUM_PICKS = 10;
4043 public const uint MAXIMUM_PICK_DESCRIPTION_SIZE = 1022;
4044 }
4045  
4046 public struct CLASSIFIEDS
4047 {
4048 public const uint MAXIMUM_CLASSIFIEDS = 100;
4049 }
4050 }
4051  
4052 public struct PRIMITIVES
4053 {
4054 public const uint MAXIMUM_NAME_SIZE = 63;
4055 public const uint MAXIMUM_DESCRIPTION_SIZE = 127;
4056 public const double MAXIMUM_REZ_HEIGHT = 4096.0;
4057 public const double MINIMUM_SIZE_X = 0.01;
4058 public const double MINIMUM_SIZE_Y = 0.01;
4059 public const double MINIMUM_SIZE_Z = 0.01;
4060 public const double MAXIMUM_SIZE_X = 64.0;
4061 public const double MAXIMUM_SIZE_Y = 64.0;
4062 public const double MAXIMUM_SIZE_Z = 64.0;
4063 }
4064  
4065 public struct OBJECTS
4066 {
4067 public const uint MAXIMUM_PRIMITIVE_COUNT = 256;
4068 }
4069  
4070 public struct DIRECTORY
4071 {
4072 public struct EVENT
4073 {
4074 public const uint SEARCH_RESULTS_COUNT = 200;
4075 }
4076  
4077 public struct GROUP
4078 {
4079 public const uint SEARCH_RESULTS_COUNT = 100;
4080 }
4081  
4082 public struct LAND
4083 {
4084 public const uint SEARCH_RESULTS_COUNT = 100;
4085 }
4086  
4087 public struct PEOPLE
4088 {
4089 public const uint SEARCH_RESULTS_COUNT = 100;
4090 }
4091 }
4092  
4093 public struct ESTATE
4094 {
4095 public const uint REGION_RESTART_DELAY = 120;
4096 public const uint MAXIMUM_BAN_LIST_LENGTH = 500;
4097 public const uint MAXIMUM_GROUP_LIST_LENGTH = 63;
4098 public const uint MAXIMUM_USER_LIST_LENGTH = 500;
4099 public const uint MAXIMUM_MANAGER_LIST_LENGTH = 10;
4100  
4101 public struct MESSAGES
4102 {
4103 public const string REGION_RESTART_MESSAGE = @"restart";
4104 }
4105 }
4106  
4107 public struct PARCELS
4108 {
4109 public const double MAXIMUM_AUTO_RETURN_TIME = 999999;
4110 public const uint MINIMUM_AUTO_RETURN_TIME = 0;
4111 public const uint MAXIMUM_NAME_LENGTH = 63;
4112 public const uint MAXIMUM_DESCRIPTION_LENGTH = 255;
4113 }
4114  
4115 public struct GRID
4116 {
4117 public const string SECOND_LIFE = @"Second Life";
4118 public const string TIME_ZONE = @"Pacific Standard Time";
4119 }
4120  
4121 public struct CHAT
4122 {
4123 public const uint MAXIMUM_MESSAGE_LENGTH = 1024;
4124 }
4125  
4126 public struct GROUPS
4127 {
4128 public const uint MAXIMUM_NUMBER_OF_ROLES = 10;
4129 public const string EVERYONE_ROLE_NAME = @"Everyone";
4130 public const uint MAXIMUM_GROUP_NAME_LENGTH = 35;
4131 public const uint MAXIMUM_GROUP_TITLE_LENGTH = 20;
4132 }
4133  
4134 public struct NOTICES
4135 {
4136 public const uint MAXIMUM_NOTICE_MESSAGE_LENGTH = 512;
4137 }
4138  
4139 public struct LSL
4140 {
4141 public const string CSV_DELIMITER = @", ";
4142 public const float SENSOR_RANGE = 96;
4143 public const string DATE_TIME_STAMP = @"yyy-MM-ddTHH:mm:ss.ffffffZ";
4144 }
4145  
4146 public struct REGION
4147 {
4148 public const float TELEPORT_MINIMUM_DISTANCE = 1;
4149 public const float DEFAULT_AGENT_LIMIT = 40;
4150 public const float DEFAULT_OBJECT_BONUS = 1;
4151 public const bool DEFAULT_FIXED_SUN = false;
4152 public const float DEFAULT_TERRAIN_LOWER_LIMIT = -4;
4153 public const float DEFAULT_TERRAIN_RAISE_LIMIT = 4;
4154 public const bool DEFAULT_USE_ESTATE_SUN = true;
4155 public const float DEFAULT_WATER_HEIGHT = 20;
4156 public const float SUNRISE = 6;
4157 }
4158  
4159 public struct VIEWER
4160 {
4161 public const float MAXIMUM_DRAW_DISTANCE = 4096;
4162 }
4163  
4164 public struct TELEPORTS
4165 {
4166 public struct THROTTLE
4167 {
4168 public const uint MAX_TELEPORTS = 10;
4169 public const uint GRACE_SECONDS = 15;
4170 }
4171 }
4172 }
4173  
4174 /// <summary>
4175 /// Constants used by Corrade.
4176 /// </summary>
4177 public struct VASSAL_CONSTANTS
4178 {
4179 /// <summary>
4180 /// Copyright.
4181 /// </summary>
4182 public const string COPYRIGHT = @"(c) Copyright 2013 Wizardry and Steamworks";
4183  
4184 public const string WIZARDRY_AND_STEAMWORKS = @"Wizardry and Steamworks";
4185 public const string VASSAL = @"Vassal";
4186 public const string WIZARDRY_AND_STEAMWORKS_WEBSITE = @"http://grimore.org";
4187  
4188 /// <summary>
4189 /// Vassal version.
4190 /// </summary>
4191 public static readonly string VASSAL_VERSION = Assembly.GetEntryAssembly().GetName().Version.ToString();
4192  
4193 /// <summary>
4194 /// Corrade user agent.
4195 /// </summary>
4196 public static readonly string USER_AGENT =
4197 $"{VASSAL}/{VASSAL_VERSION} ({WIZARDRY_AND_STEAMWORKS_WEBSITE})";
4198  
4199 /// <summary>
4200 /// Vassal compile date.
4201 /// </summary>
4202 public static readonly string VASSAL_COMPILE_DATE = new DateTime(2000, 1, 1).Add(new TimeSpan(
4203 TimeSpan.TicksPerDay*Assembly.GetEntryAssembly().GetName().Version.Build + // days since 1 January 2000
4204 TimeSpan.TicksPerSecond*2*Assembly.GetEntryAssembly().GetName().Version.Revision)).ToLongDateString();
4205  
4206 /// <summary>
4207 /// Vassal configuration file.
4208 /// </summary>
4209 public static readonly string VASSAL_CONFIGURATION_FILE = @"Vassal.ini";
4210  
4211 /// <summary>
4212 /// Vassal regions file.
4213 /// </summary>
4214 public static readonly string VASSAL_REGIONS = @"Regions.csv";
4215  
4216 /// <summary>
4217 /// Conten-types that Corrade can send and receive.
4218 /// </summary>
4219 public struct CONTENT_TYPE
4220 {
4221 public const string TEXT_PLAIN = @"text/plain";
4222 public const string WWW_FORM_URLENCODED = @"application/x-www-form-urlencoded";
4223 }
4224 }
4225  
4226 #region CRYPTOGRAPHY
4227  
4228 ///////////////////////////////////////////////////////////////////////////
4229 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
4230 ///////////////////////////////////////////////////////////////////////////
4231 /// <summary>
4232 /// Gets a sub-array from an array.
4233 /// </summary>
4234 /// <typeparam name="T">the array type</typeparam>
4235 /// <param name="data">the array</param>
4236 /// <param name="start">the start index</param>
4237 /// <param name="stop">the stop index (-1 denotes the end)</param>
4238 /// <returns>the array slice between start and stop</returns>
4239 public static T[] wasGetSubArray<T>(T[] data, int start, int stop)
4240 {
4241 if (stop.Equals(-1))
4242 stop = data.Length - 1;
4243 T[] result = new T[stop - start + 1];
4244 Array.Copy(data, start, result, 0, stop - start + 1);
4245 return result;
4246 }
4247  
4248 ///////////////////////////////////////////////////////////////////////////
4249 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
4250 ///////////////////////////////////////////////////////////////////////////
4251 /// <summary>
4252 /// Delete a sub-array and return the result.
4253 /// </summary>
4254 /// <typeparam name="T">the array type</typeparam>
4255 /// <param name="data">the array</param>
4256 /// <param name="start">the start index</param>
4257 /// <param name="stop">the stop index (-1 denotes the end)</param>
4258 /// <returns>the array without elements between start and stop</returns>
4259 public static T[] wasDeleteSubArray<T>(T[] data, int start, int stop)
4260 {
4261 if (stop.Equals(-1))
4262 stop = data.Length - 1;
4263 T[] result = new T[data.Length - (stop - start) - 1];
4264 Array.Copy(data, 0, result, 0, start);
4265 Array.Copy(data, stop + 1, result, start, data.Length - stop - 1);
4266 return result;
4267 }
4268  
4269 ///////////////////////////////////////////////////////////////////////////
4270 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
4271 ///////////////////////////////////////////////////////////////////////////
4272 /// <summary>
4273 /// Concatenate multiple arrays.
4274 /// </summary>
4275 /// <typeparam name="T">the array type</typeparam>
4276 /// <param name="arrays">multiple arrays</param>
4277 /// <returns>a flat array with all arrays concatenated</returns>
4278 public static T[] wasConcatenateArrays<T>(params T[][] arrays)
4279 {
4280 int resultLength = 0;
4281 foreach (T[] o in arrays)
4282 {
4283 resultLength += o.Length;
4284 }
4285 T[] result = new T[resultLength];
4286 int offset = 0;
4287 for (int x = 0; x < arrays.Length; x++)
4288 {
4289 arrays[x].CopyTo(result, offset);
4290 offset += arrays[x].Length;
4291 }
4292 return result;
4293 }
4294  
4295 ///////////////////////////////////////////////////////////////////////////
4296 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
4297 ///////////////////////////////////////////////////////////////////////////
4298 /// <summary>
4299 /// Permutes an array in reverse a given number of times.
4300 /// </summary>
4301 /// <typeparam name="T">the array type</typeparam>
4302 /// <param name="input">the array</param>
4303 /// <param name="times">the number of times to permute</param>
4304 /// <returns>the array with the elements permuted</returns>
4305 private static T[] wasReversePermuteArrayElements<T>(T[] input, int times)
4306 {
4307 if (times.Equals(0)) return input;
4308 T[] slice = new T[input.Length];
4309 Array.Copy(input, 1, slice, 0, input.Length - 1);
4310 Array.Copy(input, 0, slice, input.Length - 1, 1);
4311 return wasReversePermuteArrayElements(slice, --times);
4312 }
4313  
4314 ///////////////////////////////////////////////////////////////////////////
4315 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
4316 ///////////////////////////////////////////////////////////////////////////
4317 /// <summary>
4318 /// Permutes an array forward a given number of times.
4319 /// </summary>
4320 /// <typeparam name="T">the array type</typeparam>
4321 /// <param name="input">the array</param>
4322 /// <param name="times">the number of times to permute</param>
4323 /// <returns>the array with the elements permuted</returns>
4324 private static T[] wasForwardPermuteArrayElements<T>(T[] input, int times)
4325 {
4326 if (times.Equals(0)) return input;
4327 T[] slice = new T[input.Length];
4328 Array.Copy(input, input.Length - 1, slice, 0, 1);
4329 Array.Copy(input, 0, slice, 1, input.Length - 1);
4330 return wasForwardPermuteArrayElements(slice, --times);
4331 }
4332  
4333 ///////////////////////////////////////////////////////////////////////////
4334 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
4335 ///////////////////////////////////////////////////////////////////////////
4336 /// <summary>
4337 /// Encrypt or decrypt a message given a set of rotors, plugs and a reflector.
4338 /// </summary>
4339 /// <param name="message">the message to encyrpt or decrypt</param>
4340 /// <param name="rotors">any combination of: 1, 2, 3, 4, 5, 6, 7, 8, b, g</param>
4341 /// <param name="plugs">the letter representing the start character for the rotor</param>
4342 /// <param name="reflector">any one of: B, b, C, c</param>
4343 /// <returns>either a decrypted or encrypted string</returns>
4344 private static string wasEnigma(string message, char[] rotors, char[] plugs, char reflector)
4345 {
4346 Dictionary<char, char[]> def_rotors = new Dictionary<char, char[]>
4347 {
4348 {
4349 '1', new[]
4350 {
4351 'e', 'k', 'm', 'f', 'l',
4352 'g', 'd', 'q', 'v', 'z',
4353 'n', 't', 'o', 'w', 'y',
4354 'h', 'x', 'u', 's', 'p',
4355 'a', 'i', 'b', 'r', 'c',
4356 'j'
4357 }
4358 },
4359 {
4360 '2', new[]
4361 {
4362 'a', 'j', 'd', 'k', 's',
4363 'i', 'r', 'u', 'x', 'b',
4364 'l', 'h', 'w', 't', 'm',
4365 'c', 'q', 'g', 'z', 'n',
4366 'p', 'y', 'f', 'v', 'o',
4367 'e'
4368 }
4369 },
4370 {
4371 '3', new[]
4372 {
4373 'b', 'd', 'f', 'h', 'j',
4374 'l', 'c', 'p', 'r', 't',
4375 'x', 'v', 'z', 'n', 'y',
4376 'e', 'i', 'w', 'g', 'a',
4377 'k', 'm', 'u', 's', 'q',
4378 'o'
4379 }
4380 },
4381 {
4382 '4', new[]
4383 {
4384 'e', 's', 'o', 'v', 'p',
4385 'z', 'j', 'a', 'y', 'q',
4386 'u', 'i', 'r', 'h', 'x',
4387 'l', 'n', 'f', 't', 'g',
4388 'k', 'd', 'c', 'm', 'w',
4389 'b'
4390 }
4391 },
4392 {
4393 '5', new[]
4394 {
4395 'v', 'z', 'b', 'r', 'g',
4396 'i', 't', 'y', 'u', 'p',
4397 's', 'd', 'n', 'h', 'l',
4398 'x', 'a', 'w', 'm', 'j',
4399 'q', 'o', 'f', 'e', 'c',
4400 'k'
4401 }
4402 },
4403 {
4404 '6', new[]
4405 {
4406 'j', 'p', 'g', 'v', 'o',
4407 'u', 'm', 'f', 'y', 'q',
4408 'b', 'e', 'n', 'h', 'z',
4409 'r', 'd', 'k', 'a', 's',
4410 'x', 'l', 'i', 'c', 't',
4411 'w'
4412 }
4413 },
4414 {
4415 '7', new[]
4416 {
4417 'n', 'z', 'j', 'h', 'g',
4418 'r', 'c', 'x', 'm', 'y',
4419 's', 'w', 'b', 'o', 'u',
4420 'f', 'a', 'i', 'v', 'l',
4421 'p', 'e', 'k', 'q', 'd',
4422 't'
4423 }
4424 },
4425 {
4426 '8', new[]
4427 {
4428 'f', 'k', 'q', 'h', 't',
4429 'l', 'x', 'o', 'c', 'b',
4430 'j', 's', 'p', 'd', 'z',
4431 'r', 'a', 'm', 'e', 'w',
4432 'n', 'i', 'u', 'y', 'g',
4433 'v'
4434 }
4435 },
4436 {
4437 'b', new[]
4438 {
4439 'l', 'e', 'y', 'j', 'v',
4440 'c', 'n', 'i', 'x', 'w',
4441 'p', 'b', 'q', 'm', 'd',
4442 'r', 't', 'a', 'k', 'z',
4443 'g', 'f', 'u', 'h', 'o',
4444 's'
4445 }
4446 },
4447 {
4448 'g', new[]
4449 {
4450 'f', 's', 'o', 'k', 'a',
4451 'n', 'u', 'e', 'r', 'h',
4452 'm', 'b', 't', 'i', 'y',
4453 'c', 'w', 'l', 'q', 'p',
4454 'z', 'x', 'v', 'g', 'j',
4455 'd'
4456 }
4457 }
4458 };
4459  
4460 Dictionary<char, char[]> def_reflectors = new Dictionary<char, char[]>
4461 {
4462 {
4463 'B', new[]
4464 {
4465 'a', 'y', 'b', 'r', 'c', 'u', 'd', 'h',
4466 'e', 'q', 'f', 's', 'g', 'l', 'i', 'p',
4467 'j', 'x', 'k', 'n', 'm', 'o', 't', 'z',
4468 'v', 'w'
4469 }
4470 },
4471 {
4472 'b', new[]
4473 {
4474 'a', 'e', 'b', 'n', 'c', 'k', 'd', 'q',
4475 'f', 'u', 'g', 'y', 'h', 'w', 'i', 'j',
4476 'l', 'o', 'm', 'p', 'r', 'x', 's', 'z',
4477 't', 'v'
4478 }
4479 },
4480 {
4481 'C', new[]
4482 {
4483 'a', 'f', 'b', 'v', 'c', 'p', 'd', 'j',
4484 'e', 'i', 'g', 'o', 'h', 'y', 'k', 'r',
4485 'l', 'z', 'm', 'x', 'n', 'w', 't', 'q',
4486 's', 'u'
4487 }
4488 },
4489 {
4490 'c', new[]
4491 {
4492 'a', 'r', 'b', 'd', 'c', 'o', 'e', 'j',
4493 'f', 'n', 'g', 't', 'h', 'k', 'i', 'v',
4494 'l', 'm', 'p', 'w', 'q', 'z', 's', 'x',
4495 'u', 'y'
4496 }
4497 }
4498 };
4499  
4500 // Setup rotors from plugs.
4501 foreach (char rotor in rotors)
4502 {
4503 char plug = plugs[Array.IndexOf(rotors, rotor)];
4504 int i = Array.IndexOf(def_rotors[rotor], plug);
4505 if (i.Equals(0)) continue;
4506 def_rotors[rotor] = wasConcatenateArrays(new[] {plug},
4507 wasGetSubArray(wasDeleteSubArray(def_rotors[rotor], i, i), i, -1),
4508 wasGetSubArray(wasDeleteSubArray(def_rotors[rotor], i + 1, -1), 0, i - 1));
4509 }
4510  
4511 StringBuilder result = new StringBuilder();
4512 foreach (char c in message)
4513 {
4514 if (!char.IsLetter(c))
4515 {
4516 result.Append(c);
4517 continue;
4518 }
4519  
4520 // Normalize to lower.
4521 char l = char.ToLower(c);
4522  
4523 Action<char[]> rotate = o =>
4524 {
4525 int i = o.Length - 1;
4526 do
4527 {
4528 def_rotors[o[0]] = wasForwardPermuteArrayElements(def_rotors[o[0]], 1);
4529 if (i.Equals(0))
4530 {
4531 rotors = wasReversePermuteArrayElements(o, 1);
4532 continue;
4533 }
4534 l = wasGetElementAt(def_rotors[o[1]], Array.IndexOf(def_rotors[o[0]], l) - 1);
4535 o = wasReversePermuteArrayElements(o, 1);
4536 } while (--i > -1);
4537 };
4538  
4539 // Forward pass through the Enigma's rotors.
4540 rotate.Invoke(rotors);
4541  
4542 // Reflect
4543 int x = Array.IndexOf(def_reflectors[reflector], l);
4544 l = (x + 1)%2 == 0 ? def_reflectors[reflector][x - 1] : def_reflectors[reflector][x + 1];
4545  
4546 // Reverse the order of the rotors.
4547 Array.Reverse(rotors);
4548  
4549 // Reverse pass through the Enigma's rotors.
4550 rotate.Invoke(rotors);
4551  
4552 if (char.IsUpper(c))
4553 {
4554 l = char.ToUpper(l);
4555 }
4556 result.Append(l);
4557 }
4558  
4559 return result.ToString();
4560 }
4561  
4562 ///////////////////////////////////////////////////////////////////////////
4563 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
4564 ///////////////////////////////////////////////////////////////////////////
4565 /// <summary>
4566 /// Expand the VIGENRE key to the length of the input.
4567 /// </summary>
4568 /// <param name="input">the input to expand to</param>
4569 /// <param name="enc_key">the key to expand</param>
4570 /// <returns>the expanded key</returns>
4571 private static string wasVigenereExpandKey(string input, string enc_key)
4572 {
4573 string exp_key = string.Empty;
4574 int i = 0, j = 0;
4575 do
4576 {
4577 char p = input[i];
4578 if (!char.IsLetter(p))
4579 {
4580 exp_key += p;
4581 ++i;
4582 continue;
4583 }
4584 int m = j%enc_key.Length;
4585 exp_key += enc_key[m];
4586 ++j;
4587 ++i;
4588 } while (i < input.Length);
4589 return exp_key;
4590 }
4591  
4592 ///////////////////////////////////////////////////////////////////////////
4593 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
4594 ///////////////////////////////////////////////////////////////////////////
4595 /// <summary>
4596 /// Encrypt using VIGENERE.
4597 /// </summary>
4598 /// <param name="input">the input to encrypt</param>
4599 /// <param name="enc_key">the key to encrypt with</param>
4600 /// <returns>the encrypted input</returns>
4601 private static string wasEncryptVIGENERE(string input, string enc_key)
4602 {
4603 char[] a =
4604 {
4605 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
4606 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
4607 };
4608  
4609 enc_key = wasVigenereExpandKey(input, enc_key);
4610 string result = string.Empty;
4611 int i = 0;
4612 do
4613 {
4614 char p = input[i];
4615 if (!char.IsLetter(p))
4616 {
4617 result += p;
4618 ++i;
4619 continue;
4620 }
4621 char q =
4622 wasReversePermuteArrayElements(a, Array.IndexOf(a, enc_key[i]))[
4623 Array.IndexOf(a, char.ToLowerInvariant(p))];
4624 if (char.IsUpper(p))
4625 {
4626 q = char.ToUpperInvariant(q);
4627 }
4628 result += q;
4629 ++i;
4630 } while (i < input.Length);
4631 return result;
4632 }
4633  
4634 ///////////////////////////////////////////////////////////////////////////
4635 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
4636 ///////////////////////////////////////////////////////////////////////////
4637 /// <summary>
4638 /// Decrypt using VIGENERE.
4639 /// </summary>
4640 /// <param name="input">the input to decrypt</param>
4641 /// <param name="enc_key">the key to decrypt with</param>
4642 /// <returns>the decrypted input</returns>
4643 private static string wasDecryptVIGENERE(string input, string enc_key)
4644 {
4645 char[] a =
4646 {
4647 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
4648 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
4649 };
4650  
4651 enc_key = wasVigenereExpandKey(input, enc_key);
4652 string result = string.Empty;
4653 int i = 0;
4654 do
4655 {
4656 char p = input[i];
4657 if (!char.IsLetter(p))
4658 {
4659 result += p;
4660 ++i;
4661 continue;
4662 }
4663 char q =
4664 a[
4665 Array.IndexOf(wasReversePermuteArrayElements(a, Array.IndexOf(a, enc_key[i])),
4666 char.ToLowerInvariant(p))];
4667 if (char.IsUpper(p))
4668 {
4669 q = char.ToUpperInvariant(q);
4670 }
4671 result += q;
4672 ++i;
4673 } while (i < input.Length);
4674 return result;
4675 }
4676  
4677 ///////////////////////////////////////////////////////////////////////////
4678 // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
4679 ///////////////////////////////////////////////////////////////////////////
4680 /// <summary>
4681 /// An implementation of the ATBASH cypher for latin alphabets.
4682 /// </summary>
4683 /// <param name="data">the data to encrypt or decrypt</param>
4684 /// <returns>the encrypted or decrypted data</returns>
4685 private static string wasATBASH(string data)
4686 {
4687 char[] a =
4688 {
4689 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
4690 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
4691 };
4692  
4693 char[] input = data.ToArray();
4694  
4695 Parallel.ForEach(Enumerable.Range(0, data.Length), i =>
4696 {
4697 char e = input[i];
4698 if (!char.IsLetter(e)) return;
4699 int x = 25 - Array.BinarySearch(a, char.ToLowerInvariant(e));
4700 if (!char.IsUpper(e))
4701 {
4702 input[i] = a[x];
4703 return;
4704 }
4705 input[i] = char.ToUpperInvariant(a[x]);
4706 });
4707  
4708 return new string(input);
4709 }
4710  
4711 #endregion
2 zed 4712 }
8 eva 4713 }