/Vassal/Vassal/VassalForm.cs |
@@ -0,0 +1,1483 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
|
using System; |
using System.Collections; |
using System.Collections.Generic; |
using System.ComponentModel; |
using System.Data; |
using System.Drawing; |
using System.IO; |
using System.Linq; |
using System.Net; |
using System.Reflection; |
using System.Text; |
using System.Text.RegularExpressions; |
using System.Timers; |
using System.Threading; |
using System.Web; |
using System.Windows.Forms; |
using OpenMetaverse; |
using Parallel = System.Threading.Tasks.Parallel; |
using Timer = System.Timers.Timer; |
|
namespace Vassal |
{ |
public partial class Vassal : Form |
{ |
public static System.Timers.Timer overviewTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds); |
public static System.Timers.Timer topScriptsTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds); |
public static System.Timers.Timer topCollidersTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds); |
public static Dictionary<string, Vector3> ConfiguredRegions = new Dictionary<string, Vector3>(); |
public static VassalConfiguration vassalConfiguration = new VassalConfiguration(); |
public static Vassal vassalForm; |
public static readonly object ClientInstanceTeleportLock = new object(); |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary>RFC1738 URL Escapes a string</summary> |
/// <param name="data">a string to escape</param> |
/// <returns>an RFC1738 escaped string</returns> |
private static string wasURLEscapeDataString(string data) |
{ |
return HttpUtility.UrlEncode(data); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary>RFC1738 URL Unescape a string</summary> |
/// <param name="data">a string to unescape</param> |
/// <returns>an RFC1738 unescaped string</returns> |
private static string wasURLUnescapeDataString(string data) |
{ |
return HttpUtility.UrlDecode(data); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary>URI unescapes an RFC3986 URI escaped string</summary> |
/// <param name="data">a string to unescape</param> |
/// <returns>the resulting string</returns> |
private static string wasURIUnescapeDataString(string data) |
{ |
// Uri.UnescapeDataString can only handle 32766 characters at a time |
return string.Join("", Enumerable.Range(0, (data.Length + 32765)/32766) |
.Select(o => Uri.UnescapeDataString(data.Substring(o*32766, Math.Min(32766, data.Length - (o*32766))))) |
.ToArray()); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary>RFC3986 URI Escapes a string</summary> |
/// <param name="data">a string to escape</param> |
/// <returns>an RFC3986 escaped string</returns> |
private static string wasURIEscapeDataString(string data) |
{ |
// Uri.EscapeDataString can only handle 32766 characters at a time |
return string.Join("", Enumerable.Range(0, (data.Length + 32765)/32766) |
.Select(o => Uri.EscapeDataString(data.Substring(o*32766, Math.Min(32766, data.Length - (o*32766))))) |
.ToArray()); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Gets an array element at a given modulo index. |
/// </summary> |
/// <typeparam name="T">the array type</typeparam> |
/// <param name="index">a positive or negative index of the element</param> |
/// <param name="data">the array</param> |
/// <return>an array element</return> |
public static T wasGetElementAt<T>(T[] data, int index) |
{ |
switch (index < 0) |
{ |
case true: |
return data[((index%data.Length) + data.Length)%data.Length]; |
default: |
return data[index%data.Length]; |
} |
} |
|
#region KEY-VALUE DATA |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Returns the value of a key from a key-value data string. |
/// </summary> |
/// <param name="key">the key of the value</param> |
/// <param name="data">the key-value data segment</param> |
/// <returns>true if the key was found in data</returns> |
private static string wasKeyValueGet(string key, string data) |
{ |
return data.Split('&') |
.AsParallel() |
.Select(o => o.Split('=').ToList()) |
.Where(o => o.Count.Equals(2)) |
.Select(o => new |
{ |
k = o.First(), |
v = o.Last() |
}) |
.Where(o => o.k.Equals(key)) |
.Select(o => o.v) |
.FirstOrDefault(); |
} |
#endregion |
|
#region CRYPTOGRAPHY |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Gets a sub-array from an array. |
/// </summary> |
/// <typeparam name="T">the array type</typeparam> |
/// <param name="data">the array</param> |
/// <param name="start">the start index</param> |
/// <param name="stop">the stop index (-1 denotes the end)</param> |
/// <returns>the array slice between start and stop</returns> |
public static T[] wasGetSubArray<T>(T[] data, int start, int stop) |
{ |
if (stop.Equals(-1)) |
stop = data.Length - 1; |
T[] result = new T[stop - start + 1]; |
Array.Copy(data, start, result, 0, stop - start + 1); |
return result; |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Delete a sub-array and return the result. |
/// </summary> |
/// <typeparam name="T">the array type</typeparam> |
/// <param name="data">the array</param> |
/// <param name="start">the start index</param> |
/// <param name="stop">the stop index (-1 denotes the end)</param> |
/// <returns>the array without elements between start and stop</returns> |
public static T[] wasDeleteSubArray<T>(T[] data, int start, int stop) |
{ |
if (stop.Equals(-1)) |
stop = data.Length - 1; |
T[] result = new T[data.Length - (stop - start) - 1]; |
Array.Copy(data, 0, result, 0, start); |
Array.Copy(data, stop + 1, result, start, data.Length - stop - 1); |
return result; |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Concatenate multiple arrays. |
/// </summary> |
/// <typeparam name="T">the array type</typeparam> |
/// <param name="arrays">multiple arrays</param> |
/// <returns>a flat array with all arrays concatenated</returns> |
public static T[] wasConcatenateArrays<T>(params T[][] arrays) |
{ |
int resultLength = 0; |
foreach (T[] o in arrays) |
{ |
resultLength += o.Length; |
} |
T[] result = new T[resultLength]; |
int offset = 0; |
for (int x = 0; x < arrays.Length; x++) |
{ |
arrays[x].CopyTo(result, offset); |
offset += arrays[x].Length; |
} |
return result; |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Permutes an array in reverse a given number of times. |
/// </summary> |
/// <typeparam name="T">the array type</typeparam> |
/// <param name="input">the array</param> |
/// <param name="times">the number of times to permute</param> |
/// <returns>the array with the elements permuted</returns> |
private static T[] wasReversePermuteArrayElements<T>(T[] input, int times) |
{ |
if (times.Equals(0)) return input; |
T[] slice = new T[input.Length]; |
Array.Copy(input, 1, slice, 0, input.Length - 1); |
Array.Copy(input, 0, slice, input.Length - 1, 1); |
return wasReversePermuteArrayElements(slice, --times); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Permutes an array forward a given number of times. |
/// </summary> |
/// <typeparam name="T">the array type</typeparam> |
/// <param name="input">the array</param> |
/// <param name="times">the number of times to permute</param> |
/// <returns>the array with the elements permuted</returns> |
private static T[] wasForwardPermuteArrayElements<T>(T[] input, int times) |
{ |
if (times.Equals(0)) return input; |
T[] slice = new T[input.Length]; |
Array.Copy(input, input.Length - 1, slice, 0, 1); |
Array.Copy(input, 0, slice, 1, input.Length - 1); |
return wasForwardPermuteArrayElements(slice, --times); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Encrypt or decrypt a message given a set of rotors, plugs and a reflector. |
/// </summary> |
/// <param name="message">the message to encyrpt or decrypt</param> |
/// <param name="rotors">any combination of: 1, 2, 3, 4, 5, 6, 7, 8, b, g</param> |
/// <param name="plugs">the letter representing the start character for the rotor</param> |
/// <param name="reflector">any one of: B, b, C, c</param> |
/// <returns>either a decrypted or encrypted string</returns> |
private static string wasEnigma(string message, char[] rotors, char[] plugs, char reflector) |
{ |
Dictionary<char, char[]> def_rotors = new Dictionary<char, char[]> |
{ |
{ |
'1', new[] |
{ |
'e', 'k', 'm', 'f', 'l', |
'g', 'd', 'q', 'v', 'z', |
'n', 't', 'o', 'w', 'y', |
'h', 'x', 'u', 's', 'p', |
'a', 'i', 'b', 'r', 'c', |
'j' |
} |
}, |
{ |
'2', new[] |
{ |
'a', 'j', 'd', 'k', 's', |
'i', 'r', 'u', 'x', 'b', |
'l', 'h', 'w', 't', 'm', |
'c', 'q', 'g', 'z', 'n', |
'p', 'y', 'f', 'v', 'o', |
'e' |
} |
}, |
{ |
'3', new[] |
{ |
'b', 'd', 'f', 'h', 'j', |
'l', 'c', 'p', 'r', 't', |
'x', 'v', 'z', 'n', 'y', |
'e', 'i', 'w', 'g', 'a', |
'k', 'm', 'u', 's', 'q', |
'o' |
} |
}, |
{ |
'4', new[] |
{ |
'e', 's', 'o', 'v', 'p', |
'z', 'j', 'a', 'y', 'q', |
'u', 'i', 'r', 'h', 'x', |
'l', 'n', 'f', 't', 'g', |
'k', 'd', 'c', 'm', 'w', |
'b' |
} |
}, |
{ |
'5', new[] |
{ |
'v', 'z', 'b', 'r', 'g', |
'i', 't', 'y', 'u', 'p', |
's', 'd', 'n', 'h', 'l', |
'x', 'a', 'w', 'm', 'j', |
'q', 'o', 'f', 'e', 'c', |
'k' |
} |
}, |
{ |
'6', new[] |
{ |
'j', 'p', 'g', 'v', 'o', |
'u', 'm', 'f', 'y', 'q', |
'b', 'e', 'n', 'h', 'z', |
'r', 'd', 'k', 'a', 's', |
'x', 'l', 'i', 'c', 't', |
'w' |
} |
}, |
{ |
'7', new[] |
{ |
'n', 'z', 'j', 'h', 'g', |
'r', 'c', 'x', 'm', 'y', |
's', 'w', 'b', 'o', 'u', |
'f', 'a', 'i', 'v', 'l', |
'p', 'e', 'k', 'q', 'd', |
't' |
} |
}, |
{ |
'8', new[] |
{ |
'f', 'k', 'q', 'h', 't', |
'l', 'x', 'o', 'c', 'b', |
'j', 's', 'p', 'd', 'z', |
'r', 'a', 'm', 'e', 'w', |
'n', 'i', 'u', 'y', 'g', |
'v' |
} |
}, |
{ |
'b', new[] |
{ |
'l', 'e', 'y', 'j', 'v', |
'c', 'n', 'i', 'x', 'w', |
'p', 'b', 'q', 'm', 'd', |
'r', 't', 'a', 'k', 'z', |
'g', 'f', 'u', 'h', 'o', |
's' |
} |
}, |
{ |
'g', new[] |
{ |
'f', 's', 'o', 'k', 'a', |
'n', 'u', 'e', 'r', 'h', |
'm', 'b', 't', 'i', 'y', |
'c', 'w', 'l', 'q', 'p', |
'z', 'x', 'v', 'g', 'j', |
'd' |
} |
} |
}; |
|
Dictionary<char, char[]> def_reflectors = new Dictionary<char, char[]> |
{ |
{ |
'B', new[] |
{ |
'a', 'y', 'b', 'r', 'c', 'u', 'd', 'h', |
'e', 'q', 'f', 's', 'g', 'l', 'i', 'p', |
'j', 'x', 'k', 'n', 'm', 'o', 't', 'z', |
'v', 'w' |
} |
}, |
{ |
'b', new[] |
{ |
'a', 'e', 'b', 'n', 'c', 'k', 'd', 'q', |
'f', 'u', 'g', 'y', 'h', 'w', 'i', 'j', |
'l', 'o', 'm', 'p', 'r', 'x', 's', 'z', |
't', 'v' |
} |
}, |
{ |
'C', new[] |
{ |
'a', 'f', 'b', 'v', 'c', 'p', 'd', 'j', |
'e', 'i', 'g', 'o', 'h', 'y', 'k', 'r', |
'l', 'z', 'm', 'x', 'n', 'w', 't', 'q', |
's', 'u' |
} |
}, |
{ |
'c', new[] |
{ |
'a', 'r', 'b', 'd', 'c', 'o', 'e', 'j', |
'f', 'n', 'g', 't', 'h', 'k', 'i', 'v', |
'l', 'm', 'p', 'w', 'q', 'z', 's', 'x', |
'u', 'y' |
} |
} |
}; |
|
// Setup rotors from plugs. |
foreach (char rotor in rotors) |
{ |
char plug = plugs[Array.IndexOf(rotors, rotor)]; |
int i = Array.IndexOf(def_rotors[rotor], plug); |
if (i.Equals(0)) continue; |
def_rotors[rotor] = wasConcatenateArrays(new[] {plug}, |
wasGetSubArray(wasDeleteSubArray(def_rotors[rotor], i, i), i, -1), |
wasGetSubArray(wasDeleteSubArray(def_rotors[rotor], i + 1, -1), 0, i - 1)); |
} |
|
StringBuilder result = new StringBuilder(); |
foreach (char c in message) |
{ |
if (!char.IsLetter(c)) |
{ |
result.Append(c); |
continue; |
} |
|
// Normalize to lower. |
char l = char.ToLower(c); |
|
Action<char[]> rotate = o => |
{ |
int i = o.Length - 1; |
do |
{ |
def_rotors[o[0]] = wasForwardPermuteArrayElements(def_rotors[o[0]], 1); |
if (i.Equals(0)) |
{ |
rotors = wasReversePermuteArrayElements(o, 1); |
continue; |
} |
l = wasGetElementAt(def_rotors[o[1]], Array.IndexOf(def_rotors[o[0]], l) - 1); |
o = wasReversePermuteArrayElements(o, 1); |
} while (--i > -1); |
}; |
|
// Forward pass through the Enigma's rotors. |
rotate.Invoke(rotors); |
|
// Reflect |
int x = Array.IndexOf(def_reflectors[reflector], l); |
l = (x + 1)%2 == 0 ? def_reflectors[reflector][x - 1] : def_reflectors[reflector][x + 1]; |
|
// Reverse the order of the rotors. |
Array.Reverse(rotors); |
|
// Reverse pass through the Enigma's rotors. |
rotate.Invoke(rotors); |
|
if (char.IsUpper(c)) |
{ |
l = char.ToUpper(l); |
} |
result.Append(l); |
} |
|
return result.ToString(); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Expand the VIGENRE key to the length of the input. |
/// </summary> |
/// <param name="input">the input to expand to</param> |
/// <param name="enc_key">the key to expand</param> |
/// <returns>the expanded key</returns> |
private static string wasVigenereExpandKey(string input, string enc_key) |
{ |
string exp_key = string.Empty; |
int i = 0, j = 0; |
do |
{ |
char p = input[i]; |
if (!char.IsLetter(p)) |
{ |
exp_key += p; |
++i; |
continue; |
} |
int m = j%enc_key.Length; |
exp_key += enc_key[m]; |
++j; |
++i; |
} while (i < input.Length); |
return exp_key; |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Encrypt using VIGENERE. |
/// </summary> |
/// <param name="input">the input to encrypt</param> |
/// <param name="enc_key">the key to encrypt with</param> |
/// <returns>the encrypted input</returns> |
private static string wasEncryptVIGENERE(string input, string enc_key) |
{ |
char[] a = |
{ |
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', |
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' |
}; |
|
enc_key = wasVigenereExpandKey(input, enc_key); |
string result = string.Empty; |
int i = 0; |
do |
{ |
char p = input[i]; |
if (!char.IsLetter(p)) |
{ |
result += p; |
++i; |
continue; |
} |
char q = |
wasReversePermuteArrayElements(a, Array.IndexOf(a, enc_key[i]))[ |
Array.IndexOf(a, char.ToLowerInvariant(p))]; |
if (char.IsUpper(p)) |
{ |
q = char.ToUpperInvariant(q); |
} |
result += q; |
++i; |
} while (i < input.Length); |
return result; |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Decrypt using VIGENERE. |
/// </summary> |
/// <param name="input">the input to decrypt</param> |
/// <param name="enc_key">the key to decrypt with</param> |
/// <returns>the decrypted input</returns> |
private static string wasDecryptVIGENERE(string input, string enc_key) |
{ |
char[] a = |
{ |
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', |
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' |
}; |
|
enc_key = wasVigenereExpandKey(input, enc_key); |
string result = string.Empty; |
int i = 0; |
do |
{ |
char p = input[i]; |
if (!char.IsLetter(p)) |
{ |
result += p; |
++i; |
continue; |
} |
char q = |
a[ |
Array.IndexOf(wasReversePermuteArrayElements(a, Array.IndexOf(a, enc_key[i])), |
char.ToLowerInvariant(p))]; |
if (char.IsUpper(p)) |
{ |
q = char.ToUpperInvariant(q); |
} |
result += q; |
++i; |
} while (i < input.Length); |
return result; |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// An implementation of the ATBASH cypher for latin alphabets. |
/// </summary> |
/// <param name="data">the data to encrypt or decrypt</param> |
/// <returns>the encrypted or decrypted data</returns> |
private static string wasATBASH(string data) |
{ |
char[] a = |
{ |
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', |
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' |
}; |
|
char[] input = data.ToArray(); |
|
Parallel.ForEach(Enumerable.Range(0, data.Length), i => |
{ |
char e = input[i]; |
if (!char.IsLetter(e)) return; |
int x = 25 - Array.BinarySearch(a, char.ToLowerInvariant(e)); |
if (!char.IsUpper(e)) |
{ |
input[i] = a[x]; |
return; |
} |
input[i] = char.ToUpperInvariant(a[x]); |
}); |
|
return new string(input); |
} |
|
#endregion |
|
/// <summary> |
/// Corrade's input filter function. |
/// </summary> |
private static readonly Func<string, string> wasInput = o => |
{ |
if (string.IsNullOrEmpty(o)) return string.Empty; |
|
foreach (Filter filter in vassalConfiguration.InputFilters) |
{ |
switch (filter) |
{ |
case Filter.RFC1738: |
o = wasURLUnescapeDataString(o); |
break; |
case Filter.RFC3986: |
o = wasURIUnescapeDataString(o); |
break; |
case Filter.ENIGMA: |
o = wasEnigma(o, vassalConfiguration.ENIGMA.rotors.ToArray(), |
vassalConfiguration.ENIGMA.plugs.ToArray(), |
vassalConfiguration.ENIGMA.reflector); |
break; |
case Filter.VIGENERE: |
o = wasDecryptVIGENERE(o, vassalConfiguration.VIGENERESecret); |
break; |
case Filter.ATBASH: |
o = wasATBASH(o); |
break; |
case Filter.BASE64: |
o = Encoding.UTF8.GetString(Convert.FromBase64String(o)); |
break; |
} |
} |
return o; |
}; |
|
/// <summary> |
/// Corrade's output filter function. |
/// </summary> |
private static readonly Func<string, string> wasOutput = o => |
{ |
if (string.IsNullOrEmpty(o)) return string.Empty; |
|
foreach (Filter filter in vassalConfiguration.OutputFilters) |
{ |
switch (filter) |
{ |
case Filter.RFC1738: |
o = wasURLEscapeDataString(o); |
break; |
case Filter.RFC3986: |
o = wasURIEscapeDataString(o); |
break; |
case Filter.ENIGMA: |
o = wasEnigma(o, vassalConfiguration.ENIGMA.rotors.ToArray(), |
vassalConfiguration.ENIGMA.plugs.ToArray(), |
vassalConfiguration.ENIGMA.reflector); |
break; |
case Filter.VIGENERE: |
o = wasEncryptVIGENERE(o, vassalConfiguration.VIGENERESecret); |
break; |
case Filter.ATBASH: |
o = wasATBASH(o); |
break; |
case Filter.BASE64: |
o = Convert.ToBase64String(Encoding.UTF8.GetBytes(o)); |
break; |
} |
} |
return o; |
}; |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary>Escapes a dictionary's keys and values for sending as POST data.</summary> |
/// <param name="data">A dictionary containing keys and values to be escaped</param> |
private static Dictionary<string, string> wasKeyValueEscape(Dictionary<string, string> data) |
{ |
return data.AsParallel().ToDictionary(o => wasOutput(o.Key), p => wasOutput(p.Value)); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Converts a list of string to a comma-separated values string. |
/// </summary> |
/// <param name="l">a list of strings</param> |
/// <returns>a commma-separated list of values</returns> |
/// <remarks>compliant with RFC 4180</remarks> |
public static string wasEnumerableToCSV(IEnumerable<string> l) |
{ |
string[] csv = l.Select(o => o.Clone() as string).ToArray(); |
Parallel.ForEach(csv.Select((v, i) => new {i, v}), o => |
{ |
string cell = o.v.Replace("\"", "\"\""); |
switch (new[] {'"', ' ', ',', '\r', '\n'}.Any(p => cell.Contains(p))) |
{ |
case true: |
csv[o.i] = "\"" + cell + "\""; |
break; |
default: |
csv[o.i] = cell; |
break; |
} |
}); |
return String.Join(",", csv); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Converts a comma-separated list of values to a list of strings. |
/// </summary> |
/// <param name="csv">a comma-separated list of values</param> |
/// <returns>a list of strings</returns> |
/// <remarks>compliant with RFC 4180</remarks> |
public static IEnumerable<string> wasCSVToEnumerable(string csv) |
{ |
Stack<char> s = new Stack<char>(); |
StringBuilder m = new StringBuilder(); |
for (int i = 0; i < csv.Length; ++i) |
{ |
switch (csv[i]) |
{ |
case ',': |
if (!s.Any() || !s.Peek().Equals('"')) |
{ |
yield return m.ToString(); |
m = new StringBuilder(); |
continue; |
} |
m.Append(csv[i]); |
continue; |
case '"': |
if (i + 1 < csv.Length && csv[i].Equals(csv[i + 1])) |
{ |
m.Append(csv[i]); |
++i; |
continue; |
} |
if (!s.Any() || !s.Peek().Equals(csv[i])) |
{ |
s.Push(csv[i]); |
continue; |
} |
s.Pop(); |
continue; |
} |
m.Append(csv[i]); |
} |
|
yield return m.ToString(); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Serialises a dictionary to key-value data. |
/// </summary> |
/// <param name="data">a dictionary</param> |
/// <returns>a key-value data encoded string</returns> |
private static string wasKeyValueEncode(Dictionary<string, string> data) |
{ |
return String.Join("&", data.AsParallel().Select(o => String.Join("=", o.Key, o.Value))); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Sends a post request to an URL with set key-value pairs. |
/// </summary> |
/// <param name="URL">the url to send the message to</param> |
/// <param name="message">key-value pairs to send</param> |
/// <param name="millisecondsTimeout">the time in milliseconds for the request to timeout</param> |
private static string wasPOST(string URL, Dictionary<string, string> message, uint millisecondsTimeout) |
{ |
try |
{ |
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(URL); |
request.UserAgent = VASSAL_CONSTANTS.USER_AGENT; |
request.Proxy = WebRequest.DefaultWebProxy; |
request.Timeout = (int) millisecondsTimeout; |
request.AllowAutoRedirect = true; |
request.AllowWriteStreamBuffering = true; |
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; |
request.Method = WebRequestMethods.Http.Post; |
// set the content type based on chosen output filers |
switch (vassalConfiguration.OutputFilters.Last()) |
{ |
case Filter.RFC1738: |
request.ContentType = VASSAL_CONSTANTS.CONTENT_TYPE.WWW_FORM_URLENCODED; |
break; |
default: |
request.ContentType = VASSAL_CONSTANTS.CONTENT_TYPE.TEXT_PLAIN; |
break; |
} |
// send request |
using (Stream requestStream = request.GetRequestStream()) |
{ |
using (StreamWriter dataStream = new StreamWriter(requestStream)) |
{ |
dataStream.Write(wasKeyValueEncode(message)); |
} |
} |
// read response |
using (HttpWebResponse response = (HttpWebResponse) request.GetResponse()) |
{ |
using (Stream responseStream = response.GetResponseStream()) |
{ |
if (responseStream != null) |
{ |
using ( |
StreamReader streamReader = new StreamReader(responseStream)) |
{ |
return streamReader.ReadToEnd(); |
} |
} |
} |
} |
} |
catch (Exception) |
{ |
|
} |
|
return null; |
} |
|
/// <summary> |
/// Constants used by Corrade. |
/// </summary> |
public struct VASSAL_CONSTANTS |
{ |
/// <summary> |
/// Copyright. |
/// </summary> |
public const string COPYRIGHT = @"(c) Copyright 2013 Wizardry and Steamworks"; |
|
public const string WIZARDRY_AND_STEAMWORKS = @"Wizardry and Steamworks"; |
public const string VASSAL = @"Vassal"; |
public const string WIZARDRY_AND_STEAMWORKS_WEBSITE = @"http://grimore.org"; |
|
/// <summary> |
/// Vassal version. |
/// </summary> |
public static readonly string VASSAL_VERSION = Assembly.GetEntryAssembly().GetName().Version.ToString(); |
|
/// <summary> |
/// Corrade user agent. |
/// </summary> |
public static readonly string USER_AGENT = |
$"{VASSAL}/{VASSAL_VERSION} ({WIZARDRY_AND_STEAMWORKS_WEBSITE})"; |
|
/// <summary> |
/// Vassal compile date. |
/// </summary> |
public static readonly string VASSAL_COMPILE_DATE = new DateTime(2000, 1, 1).Add(new TimeSpan( |
TimeSpan.TicksPerDay*Assembly.GetEntryAssembly().GetName().Version.Build + // days since 1 January 2000 |
TimeSpan.TicksPerSecond*2*Assembly.GetEntryAssembly().GetName().Version.Revision)).ToLongDateString(); |
|
/// <summary> |
/// Vassal configuration file. |
/// </summary> |
public static readonly string VASSAL_CONFIGURATION_FILE = @"Vassal.ini"; |
|
/// <summary> |
/// Vassal regions file. |
/// </summary> |
public static readonly string VASSAL_REGIONS = @"Regions.csv"; |
|
/// <summary> |
/// Conten-types that Corrade can send and receive. |
/// </summary> |
public struct CONTENT_TYPE |
{ |
public const string TEXT_PLAIN = @"text/plain"; |
public const string WWW_FORM_URLENCODED = @"application/x-www-form-urlencoded"; |
} |
} |
|
private static readonly System.Action updateCurrentRegionName = () => |
{ |
try |
{ |
string result = wasPOST(vassalConfiguration.HTTPServerURL, |
wasKeyValueEscape(new Dictionary<string, string> |
{ |
{"command", "getregiondata"}, |
{"group", vassalConfiguration.Group}, |
{"password", vassalConfiguration.Password}, |
{"data", "Name"} |
}), 60000); |
bool success; |
if (string.IsNullOrEmpty(result) || |
!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success)) |
{ |
vassalForm.BeginInvoke((MethodInvoker)(() => |
{ |
vassalForm.StatusText.Text = @"Failed to query Corrade for current region."; |
})); |
return; |
} |
switch (success) |
{ |
case true: |
vassalForm.BeginInvoke((MethodInvoker)(() => |
{ |
vassalForm.CurrentRegionAt.Visible = true; |
vassalForm.CurrentRegionName.Visible = true; |
vassalForm.CurrentRegionName.Text = |
wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).Last(); |
})); |
break; |
default: |
vassalForm.BeginInvoke((MethodInvoker)(() => |
{ |
vassalForm.CurrentRegionAt.Visible = false; |
vassalForm.CurrentRegionName.Visible = false; |
vassalForm.StatusText.Text = @"Error getting current region: " + |
wasInput(wasKeyValueGet("error", result)); |
})); |
break; |
} |
} |
catch (Exception ex) |
{ |
vassalForm.BeginInvoke((MethodInvoker)(() => |
{ |
vassalForm.StatusText.Text = |
@"Error getting current region: " + |
ex.Message; |
})); |
} |
}; |
|
public Vassal() |
{ |
InitializeComponent(); |
vassalForm = this; |
} |
|
private void RegionSelected(object sender, EventArgs e) |
{ |
ListViewItem listViewItem = null; |
vassalForm.Invoke((MethodInvoker) (() => |
{ |
listViewItem = LoadedRegions.SelectedItem as ListViewItem; |
})); |
|
switch (listViewItem != null) |
{ |
case true: |
vassalForm.Invoke((MethodInvoker) (() => |
{ |
vassalForm.StatusText.Text = @"Teleporting to " + |
listViewItem.Text; |
})); |
break; |
default: |
return; |
} |
|
new Thread(() => |
{ |
lock (ClientInstanceTeleportLock) |
{ |
try |
{ |
vassalForm.Invoke((MethodInvoker) (() => |
{ |
vassalForm.RegionTeleportGroup.Enabled = false; |
vassalForm.StatusProgress.Value = 0; |
})); |
int elapsedSeconds = 0; |
System.Timers.Timer teleportTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds); |
teleportTimer.Elapsed += (o, p) => |
{ |
vassalForm.Invoke((MethodInvoker) (() => |
{ |
vassalForm.StatusProgress.Value = |
Math.Min( |
(int) |
(100d* |
(TimeSpan.FromSeconds(++elapsedSeconds).TotalMilliseconds/ |
vassalConfiguration.TeleportTimeout)), 100); |
})); |
}; |
teleportTimer.Start(); |
string result = null; |
ManualResetEvent receivedPOST = new ManualResetEvent(false); |
new Thread(() => |
{ |
result = wasInput(wasPOST(vassalConfiguration.HTTPServerURL, |
wasKeyValueEscape(new Dictionary<string, string> |
{ |
{"command", "teleport"}, |
{"group", vassalConfiguration.Group}, |
{"password", vassalConfiguration.Password}, |
{"region", listViewItem.Text}, |
{"position", ((Vector3) listViewItem.Tag).ToString()}, |
{"fly", "True"} |
}), vassalConfiguration.TeleportTimeout)); |
receivedPOST.Set(); |
}) {IsBackground = true}.Start(); |
receivedPOST.WaitOne((int) vassalConfiguration.TeleportTimeout, false); |
teleportTimer.Stop(); |
switch (!string.IsNullOrEmpty(result) && wasKeyValueGet("success", result) == "True") |
{ |
case true: |
vassalForm.Invoke((MethodInvoker) (() => |
{ |
vassalForm.StatusText.Text = @"Now at " + listViewItem.Text; |
})); |
break; |
default: |
switch (!string.IsNullOrEmpty(result)) |
{ |
case true: |
vassalForm.Invoke((MethodInvoker) (() => |
{ |
vassalForm.StatusText.Text = @"Failed teleporting to " + listViewItem.Text + |
@": " + |
wasKeyValueGet("error", result); |
})); |
break; |
default: |
vassalForm.Invoke((MethodInvoker) (() => |
{ |
vassalForm.StatusText.Text = @"Failed teleporting to " + listViewItem.Text; |
})); |
break; |
} |
break; |
} |
} |
catch (Exception ex) |
{ |
vassalForm.Invoke((MethodInvoker) (() => |
{ |
vassalForm.StatusText.Text = @"Error communicating with Corrade: " + ex.Message; |
})); |
} |
finally |
{ |
vassalForm.Invoke((MethodInvoker) (() => |
{ |
vassalForm.StatusProgress.Value = 100; |
vassalForm.RegionTeleportGroup.Enabled = true; |
// Set the map image to the loading spinner. |
Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly(); |
System.IO.Stream file = |
thisAssembly.GetManifestResourceStream("Vassal.img.loading.gif"); |
switch (file != null) |
{ |
case true: |
vassalForm.Invoke((MethodInvoker)(() => |
{ |
RegionAvatarsMap.SizeMode = PictureBoxSizeMode.CenterImage; |
RegionAvatarsMap.Image = Image.FromStream(file); |
RegionAvatarsMap.Refresh(); |
})); |
break; |
} |
// Clear the top scripts table. |
TopScriptsGridView.Rows.Clear(); |
// Clear the top colliders table. |
TopCollidersGridView.Rows.Clear(); |
})); |
updateCurrentRegionName.BeginInvoke(updateCurrentRegionName.EndInvoke, null); |
} |
} |
}).Start(); |
|
} |
|
private void SettingsRequested(object sender, EventArgs e) |
{ |
SettingsForm settingsForm = new SettingsForm {TopMost = true}; |
settingsForm.Show(); |
} |
|
private void VassalShown(object sender, EventArgs e) |
{ |
// Get the configuration file settings if it exists. |
if (File.Exists(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE)) |
{ |
VassalConfiguration.Load(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE, ref vassalConfiguration); |
} |
|
// Get all the regions if they exist. |
if (File.Exists(VASSAL_CONSTANTS.VASSAL_REGIONS)) |
{ |
Vector3 localPosition; |
ConfiguredRegions = |
File.ReadAllLines(VASSAL_CONSTANTS.VASSAL_REGIONS) |
.Select(o => new List<string>(wasCSVToEnumerable(o))) |
.Where(o => o.Count == 2) |
.ToDictionary(o => o.First(), |
p => |
Vector3.TryParse(p.Last(), out localPosition) |
? localPosition |
: Vector3.Zero).OrderBy(o => o.Key).ToDictionary(o => o.Key, o => o.Value); |
LoadedRegions.Items.Clear(); |
LoadedRegions.Items.AddRange( |
ConfiguredRegions.Select(o => (object)new ListViewItem {Text = o.Key, Tag = o.Value}) |
.ToArray()); |
} |
|
// Update the current region in case the configuration is initialized. |
if (!vassalConfiguration.Equals(default(VassalConfiguration))) |
{ |
updateCurrentRegionName.BeginInvoke(updateCurrentRegionName.EndInvoke, null); |
} |
|
// Set the map image to the loading spinner. |
Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly(); |
System.IO.Stream file = |
thisAssembly.GetManifestResourceStream("Vassal.img.loading.gif"); |
switch (file != null) |
{ |
case true: |
vassalForm.Invoke((MethodInvoker)(() => |
{ |
RegionAvatarsMap.SizeMode = PictureBoxSizeMode.CenterImage; |
RegionAvatarsMap.Image = Image.FromStream(file); |
RegionAvatarsMap.Refresh(); |
})); |
break; |
} |
|
// Start the overview timer. |
overviewTabTimer.Elapsed += (o, p) => |
{ |
if (!Monitor.TryEnter(ClientInstanceTeleportLock)) |
return; |
|
try |
{ |
// Do not do anything in case the tab is not selected. |
vassalForm.Invoke((MethodInvoker) (() => |
{ |
if (!Tabs.SelectedTab.Equals(OverviewTab)) |
throw new Exception(); |
})); |
|
// Get the statistics. |
string result = wasPOST(vassalConfiguration.HTTPServerURL, |
wasKeyValueEscape(new Dictionary<string, string> |
{ |
{"command", "getregiondata"}, |
{"group", vassalConfiguration.Group}, |
{"password", vassalConfiguration.Password}, |
{ |
"data", wasEnumerableToCSV(new[] |
{ |
"Agents", |
"LastLag", |
"Dilation", |
"FPS", |
"PhysicsFPS", |
"ActiveScripts", |
"ScriptTime", |
"Objects" |
}) |
} |
}), vassalConfiguration.DataTimeout); |
|
bool success; |
if (string.IsNullOrEmpty(result) || |
!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success)) |
throw new Exception(); |
|
List<string> data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList(); |
if (data.Count.Equals(0)) |
throw new Exception(); |
|
vassalForm.Invoke((MethodInvoker) (() => |
{ |
Agents.Text = data[data.IndexOf("Agents") + 1]; |
LastLag.Text = data[data.IndexOf("LastLag") + 1]; |
Dilation.Text = data[data.IndexOf("Dilation") + 1]; |
FPS.Text = data[data.IndexOf("FPS") + 1]; |
PhysicsFPS.Text = data[data.IndexOf("PhysicsFPS") + 1]; |
ActiveScripts.Text = data[data.IndexOf("ActiveScripts") + 1]; |
ScriptTime.Text = data[data.IndexOf("ScriptTime") + 1]; |
Objects.Text = data[data.IndexOf("Objects") + 1]; |
})); |
|
// Get the map image. |
result = wasPOST(vassalConfiguration.HTTPServerURL, |
wasKeyValueEscape(new Dictionary<string, string> |
{ |
{"command", "getgridregiondata"}, |
{"group", vassalConfiguration.Group}, |
{"password", vassalConfiguration.Password}, |
{ "data", "MapImageID"} |
}), vassalConfiguration.DataTimeout); |
|
if (string.IsNullOrEmpty(result) || |
!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success)) |
throw new Exception(); |
|
data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList(); |
if (!data.Count.Equals(2)) |
throw new Exception(); |
result = wasPOST(vassalConfiguration.HTTPServerURL, |
wasKeyValueEscape(new Dictionary<string, string> |
{ |
{"command", "download"}, |
{"group", vassalConfiguration.Group}, |
{"password", vassalConfiguration.Password}, |
{"item", data.Last() }, |
{"type", "Texture" }, |
{"format", "Jpeg" }, |
}), vassalConfiguration.DataTimeout); |
if (string.IsNullOrEmpty(result) || |
!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success)) |
throw new Exception(); |
byte[] mapImageBytes = Convert.FromBase64String(wasInput(wasKeyValueGet("data", result))); |
Image mapImage; |
using (MemoryStream memoryStream = new MemoryStream(mapImageBytes, 0, mapImageBytes.Length)) |
{ |
mapImage = Image.FromStream(memoryStream); |
} |
|
// Get the avatar positions. |
result = wasPOST(vassalConfiguration.HTTPServerURL, |
wasKeyValueEscape(new Dictionary<string, string> |
{ |
{"command", "getavatarpositions"}, |
{"group", vassalConfiguration.Group}, |
{"password", vassalConfiguration.Password}, |
{ "entity", "region"} |
}), vassalConfiguration.DataTimeout); |
|
if (string.IsNullOrEmpty(result) || |
!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success)) |
throw new Exception(); |
|
// Every thrid element represents a Vecto3 of the avatar position. |
data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).Skip(2).Where((x, i) => i % 3 == 0).ToList(); |
|
// Draw the avatars onto the map. |
Graphics mapGraphics = Graphics.FromImage(mapImage); |
Vector3 position; |
foreach (string vector in data) |
{ |
switch (Vector3.TryParse(vector, out position)) |
{ |
case true: |
mapGraphics.FillEllipse(Brushes.Chartreuse, new Rectangle((int)position.X, (int)position.Y, 4, 4)); |
break; |
} |
} |
mapGraphics.DrawImage(mapImage, new Point(0, 0)); |
|
vassalForm.Invoke((MethodInvoker) (() => |
{ |
RegionAvatarsMap.SizeMode = PictureBoxSizeMode.StretchImage; |
RegionAvatarsMap.Image = mapImage; |
RegionAvatarsMap.Refresh(); |
})); |
} |
catch (Exception) |
{ |
|
} |
finally |
{ |
Monitor.Exit(ClientInstanceTeleportLock); |
} |
|
}; |
overviewTabTimer.Start(); |
|
// Start the top scores timer. |
topScriptsTabTimer.Elapsed += (o, p) => |
{ |
if (!Monitor.TryEnter(ClientInstanceTeleportLock)) |
return; |
|
try |
{ |
// Do not do anything in case the tab is not selected. |
vassalForm.Invoke((MethodInvoker) (() => |
{ |
if (!Tabs.SelectedTab.Equals(TopScriptsTab)) |
throw new Exception(); |
})); |
|
// Get the statistics. |
string result = wasPOST(vassalConfiguration.HTTPServerURL, |
wasKeyValueEscape(new Dictionary<string, string> |
{ |
{"command", "getregiontop"}, |
{"group", vassalConfiguration.Group}, |
{"password", vassalConfiguration.Password}, |
{"type", "scripts"} |
}), vassalConfiguration.DataTimeout); |
|
bool success; |
if (string.IsNullOrEmpty(result) || |
!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success)) |
throw new Exception(); |
|
HashSet<List<string>> data = |
new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))) |
.Select((x, i) => new {Index = i, Value = x}) |
.GroupBy(x => x.Index/5) |
.Select(x => x.Select(v => v.Value).ToList())); |
if (data.Count.Equals(0)) |
throw new Exception(); |
|
vassalForm.Invoke((MethodInvoker) (() => |
{ |
switch (!TopScriptsGridView.Rows.Count.Equals(0)) |
{ |
case true: |
foreach (DataGridViewRow topScriptsRow in (IEnumerable)TopScriptsGridView.Rows) |
{ |
List<string> updateRowData = |
data.AsParallel() |
.FirstOrDefault(q => q[2].Equals(topScriptsRow.Cells["TopScriptsUUID"].Value.ToString())); |
switch (updateRowData != null && updateRowData.Count.Equals(5)) |
{ |
case true: |
topScriptsRow.Cells["TopScriptsScore"].Value = updateRowData[0]; |
topScriptsRow.Cells["TopScriptsTaskName"].Value = updateRowData[1]; |
topScriptsRow.Cells["TopScriptsUUID"].Value = updateRowData[2]; |
topScriptsRow.Cells["TopScriptsOwner"].Value = updateRowData[3]; |
topScriptsRow.Cells["TopScriptsPosition"].Value = updateRowData[4]; |
break; |
} |
} |
break; |
default: |
foreach (List<string> updateRowData in data) |
{ |
TopScriptsGridView.Rows.Add(updateRowData[0], updateRowData[1], updateRowData[2], |
updateRowData[3], updateRowData[4]); |
} |
break; |
} |
|
})); |
} |
catch (Exception) |
{ |
|
} |
finally |
{ |
Monitor.Exit(ClientInstanceTeleportLock); |
} |
}; |
topScriptsTabTimer.Start(); |
|
// Start the top colliders timer. |
topCollidersTabTimer.Elapsed += (o, p) => |
{ |
if (!Monitor.TryEnter(ClientInstanceTeleportLock)) |
return; |
|
try |
{ |
// Do not do anything in case the tab is not selected. |
vassalForm.Invoke((MethodInvoker)(() => |
{ |
if (!Tabs.SelectedTab.Equals(TopCollidersTab)) |
throw new Exception(); |
})); |
|
// Get the statistics. |
string result = wasPOST(vassalConfiguration.HTTPServerURL, |
wasKeyValueEscape(new Dictionary<string, string> |
{ |
{"command", "getregiontop"}, |
{"group", vassalConfiguration.Group}, |
{"password", vassalConfiguration.Password}, |
{"type", "colliders"} |
}), vassalConfiguration.DataTimeout); |
|
bool success; |
if (string.IsNullOrEmpty(result) || |
!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success)) |
throw new Exception(); |
|
HashSet<List<string>> data = |
new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))) |
.Select((x, i) => new { Index = i, Value = x }) |
.GroupBy(x => x.Index / 5) |
.Select(x => x.Select(v => v.Value).ToList())); |
if (data.Count.Equals(0)) |
throw new Exception(); |
|
vassalForm.Invoke((MethodInvoker)(() => |
{ |
switch (!TopCollidersGridView.Rows.Count.Equals(0)) |
{ |
case true: |
foreach (DataGridViewRow topCollidersRow in (IEnumerable)TopCollidersGridView.Rows) |
{ |
List<string> updateRowData = |
data.AsParallel() |
.FirstOrDefault(q => q[2].Equals(topCollidersRow.Cells["TopCollidersUUID"].Value.ToString())); |
switch (updateRowData != null && updateRowData.Count.Equals(5)) |
{ |
case true: |
topCollidersRow.Cells["TopCollidersScore"].Value = updateRowData[0]; |
topCollidersRow.Cells["TopCollidersTaskName"].Value = updateRowData[1]; |
topCollidersRow.Cells["TopCollidersUUID"].Value = updateRowData[2]; |
topCollidersRow.Cells["TopCollidersOwner"].Value = updateRowData[3]; |
topCollidersRow.Cells["TopCollidersPosition"].Value = updateRowData[4]; |
break; |
} |
} |
break; |
default: |
foreach (List<string> updateRowData in data) |
{ |
TopCollidersGridView.Rows.Add(updateRowData[0], updateRowData[1], updateRowData[2], |
updateRowData[3], updateRowData[4]); |
} |
break; |
} |
|
})); |
} |
catch (Exception) |
{ |
|
} |
finally |
{ |
Monitor.Exit(ClientInstanceTeleportLock); |
} |
}; |
topCollidersTabTimer.Start(); |
} |
|
private void RequestedEditRegions(object sender, EventArgs e) |
{ |
// Clear any selection. |
LoadedRegions.SelectedIndex = -1; |
RegionEditForm regionEditForm = new RegionEditForm { TopMost = true }; |
regionEditForm.Show(); |
} |
|
private void RequestSelecting(object sender, TabControlCancelEventArgs e) |
{ |
e.Cancel = !e.TabPage.Enabled; |
} |
} |
} |