/////////////////////////////////////////////////////////////////////////// // 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.Generic; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Net.Sockets; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; using OpenMetaverse; using wasSharp; using wasSharp.Web; using wasSharp.Web.Utilities; using Parallel = System.Threading.Tasks.Parallel; using Timer = System.Timers.Timer; namespace Vassal { public partial class Vassal : Form { public static Timer vassalTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds); public static Timer overviewTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds); public static Timer residentListTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds); public static Timer estateTopTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds); public static Timer regionsStateTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds); public static Timer estateTexturesTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds); public static Image[] groundTextureImages = new Image[4]; public static volatile int regionsStateCheckIndex; public static VassalConfiguration vassalConfiguration = new VassalConfiguration(); public static Vassal vassalForm; public static readonly object ClientInstanceTeleportLock = new object(); public static readonly object RegionsStateCheckLock = new object(); public static wasHTTPClient HTTPClient; /// /// Corrade version. /// public static readonly string VASSAL_VERSION = Assembly.GetEntryAssembly().GetName().Version.ToString(); /// /// Corrade's input filter function. /// private static readonly Func wasInput = o => { if (string.IsNullOrEmpty(o)) return string.Empty; foreach (var filter in vassalConfiguration.InputFilters) { switch (filter) { case Filter.RFC1738: o = Extensions.URLUnescapeDataString(o); break; case Filter.RFC3986: o = Extensions.URIUnescapeDataString(o); break; case Filter.ENIGMA: o = Cryptography.ENIGMA(o, vassalConfiguration.ENIGMA.rotors.ToArray(), vassalConfiguration.ENIGMA.plugs.ToArray(), vassalConfiguration.ENIGMA.reflector); break; case Filter.VIGENERE: o = Cryptography.DecryptVIGENERE(o, vassalConfiguration.VIGENERESecret); break; case Filter.ATBASH: o = Cryptography.ATBASH(o); break; case Filter.BASE64: o = Encoding.UTF8.GetString(Convert.FromBase64String(o)); break; } } return o; }; /// /// Corrade's output filter function. /// private static readonly Func wasOutput = o => { if (string.IsNullOrEmpty(o)) return string.Empty; foreach (var filter in vassalConfiguration.OutputFilters) { switch (filter) { case Filter.RFC1738: o = Extensions.URLEscapeDataString(o); break; case Filter.RFC3986: o = Extensions.URIEscapeDataString(o); break; case Filter.ENIGMA: o = Cryptography.ENIGMA(o, vassalConfiguration.ENIGMA.rotors.ToArray(), vassalConfiguration.ENIGMA.plugs.ToArray(), vassalConfiguration.ENIGMA.reflector); break; case Filter.VIGENERE: o = Cryptography.EncryptVIGENERE(o, vassalConfiguration.VIGENERESecret); break; case Filter.ATBASH: o = Cryptography.ATBASH(o); break; case Filter.BASE64: o = Convert.ToBase64String(Encoding.UTF8.GetBytes(o)); break; } } return o; }; private static readonly Action updateCurrentRegionName = () => { try { var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getregiondata"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"data", "Name"} }, wasOutput)).Result); bool success; if (string.IsNullOrEmpty(result) || !bool.TryParse(wasInput(KeyValue.Get("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 = CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))).Last(); })); break; default: vassalForm.BeginInvoke((MethodInvoker) (() => { vassalForm.CurrentRegionAt.Visible = false; vassalForm.CurrentRegionName.Visible = false; vassalForm.StatusText.Text = @"Error getting current region: " + wasInput(KeyValue.Get("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 ShowToolTip(object sender, EventArgs e) { vassalForm.BeginInvoke( (Action) (() => { var pictureBox = sender as PictureBox; if (pictureBox != null) { toolTip1.Show(toolTip1.GetToolTip(pictureBox), pictureBox); } })); } private void RegionSelected(object sender, EventArgs e) { new Thread(() => { try { // Stop timers. vassalTimer.Stop(); overviewTabTimer.Stop(); regionsStateTabTimer.Stop(); residentListTabTimer.Stop(); estateTopTabTimer.Stop(); estateTexturesTabTimer.Stop(); Monitor.Enter(ClientInstanceTeleportLock); var selectedRegionName = string.Empty; var selectedRegionPosition = Vector3.Zero; var startTeleport = false; vassalForm.Invoke((MethodInvoker) (() => { var listViewItem = LoadedRegionsBox.SelectedItem as ListViewItem; switch (listViewItem != null && LoadedRegionsBox.SelectedIndex != -1) { case true: selectedRegionName = listViewItem.Text; selectedRegionPosition = (Vector3) listViewItem.Tag; startTeleport = true; break; default: startTeleport = false; break; } })); if (!startTeleport) throw new Exception(); // Announce teleport. vassalForm.Invoke((MethodInvoker) (() => { vassalForm.RegionTeleportGroup.Enabled = false; vassalForm.StatusProgress.Value = 0; vassalForm.StatusText.Text = @"Teleporting to " + selectedRegionName; })); // Pause for teleport (10 teleports / 15s allowed). Thread.Sleep(700); var elapsedSeconds = 0; var 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; var receivedPOST = new ManualResetEvent(false); new Thread(() => { result = wasInput(Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "teleport"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"entity", "region"}, {"region", selectedRegionName}, {"position", selectedRegionPosition.ToString()}, {"fly", "True"} }, wasOutput)).Result)); receivedPOST.Set(); }) {IsBackground = true}.Start(); receivedPOST.WaitOne((int) vassalConfiguration.TeleportTimeout, false); teleportTimer.Stop(); switch (!string.IsNullOrEmpty(result) && KeyValue.Get("success", result) == "True") { case true: vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Now at " + selectedRegionName; vassalForm.CurrentRegionName.Text = selectedRegionName; })); break; default: switch (!string.IsNullOrEmpty(result)) { case true: vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Failed teleporting to " + selectedRegionName + @": " + KeyValue.Get("error", result); })); break; default: vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Failed teleporting to " + selectedRegionName; })); break; } break; } } catch (Exception ex) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade: " + ex.Message; })); } finally { vassalForm.BeginInvoke((MethodInvoker) (() => { vassalForm.StatusProgress.Value = 100; vassalForm.RegionTeleportGroup.Enabled = true; // Set the map image to the loading spinner. var thisAssembly = Assembly.GetExecutingAssembly(); var 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 resident list table. ResidentListGridView.Rows.Clear(); // Clear the top scripts table. TopScriptsGridView.Rows.Clear(); // Clear the top colliders table. TopCollidersGridView.Rows.Clear(); // Clear the estate list table. EstateListGridView.Rows.Clear(); // Clear the estate list selection box. EstateListSelectBox.SelectedIndex = -1; // Invalidate data for overview tab. vassalForm.CurrentRegionAt.Visible = false; vassalForm.CurrentRegionName.Visible = false; Agents.Text = string.Empty; Agents.Enabled = false; LastLag.Text = string.Empty; LastLag.Enabled = false; Dilation.Text = string.Empty; Dilation.Enabled = false; FPS.Text = string.Empty; FPS.Enabled = false; PhysicsFPS.Text = string.Empty; PhysicsFPS.Enabled = false; ActiveScripts.Text = string.Empty; ActiveScripts.Enabled = false; ScriptTime.Text = string.Empty; ScriptTime.Enabled = false; FrameTime.Text = string.Empty; FrameTime.Enabled = false; ScriptedObjects.Text = string.Empty; ScriptedObjects.Enabled = false; PhysicsTime.Text = string.Empty; PhysicsTime.Enabled = false; NetTime.Text = string.Empty; NetTime.Enabled = false; Objects.Text = string.Empty; Objects.Enabled = false; })); Monitor.Exit(ClientInstanceTeleportLock); // start timers vassalTimer.Start(); overviewTabTimer.Start(); regionsStateTabTimer.Start(); residentListTabTimer.Start(); estateTopTabTimer.Start(); estateTexturesTabTimer.Start(); } }) {IsBackground = true}.Start(); } private void SettingsRequested(object sender, EventArgs e) { vassalForm.BeginInvoke((MethodInvoker) (() => { VassalStatusGroup.Enabled = false; })); var settingsForm = new SettingsForm {TopMost = true}; settingsForm.Show(); } private void VassalShown(object sender, EventArgs e) { // Set the version vassalForm.Version.Text = @"v" + VASSAL_CONSTANTS.VASSAL_VERSION; // Disable estate manager tabs since we will enable these dynamically. EstateTopTab.Enabled = false; EstateListsTab.Enabled = false; ResidentListBanGroup.Enabled = false; ResidentListTeleportHomeGroup.Enabled = false; EstateTerrainDownloadUploadGroup.Enabled = false; EstateVariablesGroup.Enabled = false; // Estate textures RegionTexturesLowUUIDApplyBox.Enabled = false; RegionTexturesLowUUIDApplyButton.Enabled = false; RegionTexturesMiddleLowUUIDApplyBox.Enabled = false; RegionTexturesMiddleLowUUIDApplyButton.Enabled = false; RegionTexturesMiddleHighUUIDApplyBox.Enabled = false; RegionTexturesMiddleHighUUIDApplyButton.Enabled = false; RegionTexturesHighUUIDApplyBox.Enabled = false; RegionTexturesHighUUIDApplyButton.Enabled = false; // Get the configuration file settings if it exists. if (File.Exists(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE)) { // Load the configuration. VassalConfiguration.Load(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE, ref vassalConfiguration); // Apply settings RegionRestartDelayBox.Text = vassalConfiguration.RegionRestartDelay.ToString(Utils.EnUsCulture); // HTTP string mediaType; switch (vassalConfiguration.InputFilters.LastOrDefault()) { case Filter.RFC1738: mediaType = @"application/x-www-form-urlencoded"; break; default: mediaType = @"text/plain"; break; } // Create HTTP Client HTTPClient = new wasHTTPClient(new ProductInfoHeaderValue(@"Vassal", VASSAL_VERSION), mediaType, vassalConfiguration.ServicesTimeout); } // Get all the regions if they exist. if (File.Exists(VASSAL_CONSTANTS.VASSAL_REGIONS)) { Vector3 localPosition; var ConfiguredRegions = new List>( File.ReadAllLines(VASSAL_CONSTANTS.VASSAL_REGIONS) .Select(o => new List(CSV.ToEnumerable(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)); // Populate the loaded regions. LoadedRegionsBox.Items.Clear(); LoadedRegionsBox.Items.AddRange( ConfiguredRegions.Select(o => (object) new ListViewItem {Text = o.Key, Tag = o.Value}) .ToArray()); // Populate the batch restart grid view. BatchRestartGridView.Rows.Clear(); foreach (var data in ConfiguredRegions) { BatchRestartGridView.Rows.Add(data.Key, data.Value.ToString()); } // Populate the batch covenant grid view. BatchSetCovenantGridView.Rows.Clear(); foreach (var data in ConfiguredRegions) { BatchSetCovenantGridView.Rows.Add(data.Key, data.Value.ToString()); } // Populate the regions state grid view. foreach (var data in ConfiguredRegions) { RegionsStateGridView.Rows.Add(data.Key, "Check pening..."); } } // Start the vassal timer. vassalTimer.Elapsed += (o, p) => { if (!Monitor.TryEnter(ClientInstanceTeleportLock)) return; try { // Check Corrade connection status. var tcpClient = new TcpClient(); var uri = new Uri(vassalConfiguration.HTTPServerURL); tcpClient.Connect(uri.Host, uri.Port); // port open vassalForm.BeginInvoke((MethodInvoker) (() => { vassalForm.CurrentRegionAt.Visible = true; vassalForm.CurrentRegionName.Visible = true; var thisAssembly = Assembly.GetExecutingAssembly(); var file = thisAssembly.GetManifestResourceStream("Vassal.img.online.png"); switch (file != null) { case true: vassalForm.BeginInvoke((MethodInvoker) (() => { ConnectionStatusPictureBox.Image = Image.FromStream(file); ConnectionStatusPictureBox.Refresh(); })); break; } Tabs.Enabled = true; RegionTeleportGroup.Enabled = true; })); } catch (Exception) { // port closed vassalForm.BeginInvoke((MethodInvoker) (() => { vassalForm.CurrentRegionAt.Visible = false; vassalForm.CurrentRegionName.Visible = false; var thisAssembly = Assembly.GetExecutingAssembly(); var file = thisAssembly.GetManifestResourceStream("Vassal.img.offline.png"); switch (file != null) { case true: vassalForm.BeginInvoke((MethodInvoker) (() => { ConnectionStatusPictureBox.Image = Image.FromStream(file); ConnectionStatusPictureBox.Refresh(); })); break; } Tabs.Enabled = false; RegionTeleportGroup.Enabled = false; })); } try { // Get the simulator name and if we are an estate manager. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getregiondata"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, { "data", CSV.FromEnumerable(new[] { "Name", "IsEstateManager" }) } }, wasOutput)).Result); bool success; if (string.IsNullOrEmpty(result) || !bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception(); var data = CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))).ToList(); if (data.Count.Equals(0)) throw new Exception(); vassalForm.BeginInvoke((MethodInvoker) (() => { // Drop access to features if we are not estate managers. bool isEstateManager; switch ( bool.TryParse(data[data.IndexOf("IsEstateManager") + 1], out isEstateManager) && isEstateManager) { case true: // we are an estate manager EstateTopTab.Enabled = true; EstateListsTab.Enabled = true; ResidentListBanGroup.Enabled = true; ResidentListTeleportHomeGroup.Enabled = true; EstateTerrainDownloadUploadGroup.Enabled = true; EstateVariablesGroup.Enabled = true; RegionToolsRegionDebugGroup.Enabled = true; RegionToolsRegionInfoGroup.Enabled = true; // Estate textures RegionTexturesLowUUIDApplyBox.Enabled = true; RegionTexturesLowUUIDApplyButton.Enabled = true; RegionTexturesMiddleLowUUIDApplyBox.Enabled = true; RegionTexturesMiddleLowUUIDApplyButton.Enabled = true; RegionTexturesMiddleHighUUIDApplyBox.Enabled = true; RegionTexturesMiddleHighUUIDApplyButton.Enabled = true; RegionTexturesHighUUIDApplyBox.Enabled = true; RegionTexturesHighUUIDApplyButton.Enabled = true; break; default: EstateTopTab.Enabled = false; EstateListsTab.Enabled = false; ResidentListBanGroup.Enabled = false; ResidentListTeleportHomeGroup.Enabled = false; EstateTerrainDownloadUploadGroup.Enabled = false; EstateVariablesGroup.Enabled = false; RegionToolsRegionDebugGroup.Enabled = false; RegionToolsRegionInfoGroup.Enabled = false; // Estate textures RegionTexturesLowUUIDApplyBox.Enabled = false; RegionTexturesLowUUIDApplyButton.Enabled = false; RegionTexturesMiddleLowUUIDApplyBox.Enabled = false; RegionTexturesMiddleLowUUIDApplyButton.Enabled = false; RegionTexturesMiddleHighUUIDApplyBox.Enabled = false; RegionTexturesMiddleHighUUIDApplyButton.Enabled = false; RegionTexturesHighUUIDApplyBox.Enabled = false; RegionTexturesHighUUIDApplyButton.Enabled = false; break; } // Show the region name. vassalForm.CurrentRegionName.Text = data[data.IndexOf("Name") + 1]; vassalForm.CurrentRegionAt.Visible = true; vassalForm.CurrentRegionName.Visible = true; })); } catch (Exception) { } Monitor.Exit(ClientInstanceTeleportLock); }; vassalTimer.Start(); // Start the overview timer. overviewTabTimer.Elapsed += (o, p) => { // Do not do anything in case the tab is not selected. var run = false; vassalForm.Invoke((MethodInvoker) (() => { run = Tabs.SelectedTab.Equals(OverviewTab); })); if (!run) { overviewTabTimer.Stop(); return; } overviewTabTimer.Stop(); try { // Start measuring the lag to Corrade. var stopWatch = new Stopwatch(); stopWatch.Start(); // Get the statistics. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getregiondata"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, { "data", CSV.FromEnumerable(new[] { "Stats.Agents", "Stats.LastLag", "Stats.Dilation", "Stats.FPS", "Stats.PhysicsFPS", "Stats.ActiveScripts", "Stats.ScriptTime", "Stats.Objects", "Stats.FrameTime", "Stats.ScriptedObjects", "Stats.PhysicsTime", "Stats.NetTime", "AvatarPositions" }) } }, wasOutput)).Result); stopWatch.Stop(); bool success; if (string.IsNullOrEmpty(result) || !bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception(); var data = CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))).ToList(); if (data.Count.Equals(0)) throw new Exception(); vassalForm.BeginInvoke((MethodInvoker) (() => { // Populate the overview tab. Agents.Text = data[data.IndexOf("Stats.Agents") + 1]; Agents.Enabled = true; LastLag.Text = data[data.IndexOf("Stats.LastLag") + 1]; LastLag.Enabled = true; Dilation.Text = data[data.IndexOf("Stats.Dilation") + 1]; Dilation.Enabled = true; FPS.Text = data[data.IndexOf("Stats.FPS") + 1]; FPS.Enabled = true; PhysicsFPS.Text = data[data.IndexOf("Stats.PhysicsFPS") + 1]; PhysicsFPS.Enabled = true; ActiveScripts.Text = data[data.IndexOf("Stats.ActiveScripts") + 1]; ActiveScripts.Enabled = true; ScriptTime.Text = data[data.IndexOf("Stats.ScriptTime") + 1]; ScriptTime.Enabled = true; FrameTime.Text = data[data.IndexOf("Stats.FrameTime") + 1]; FrameTime.Enabled = true; ScriptedObjects.Text = data[data.IndexOf("Stats.ScriptedObjects") + 1]; ScriptedObjects.Enabled = true; PhysicsTime.Text = data[data.IndexOf("Stats.PhysicsTime") + 1]; PhysicsTime.Enabled = true; NetTime.Text = data[data.IndexOf("Stats.NetTime") + 1]; NetTime.Enabled = true; Objects.Text = data[data.IndexOf("Stats.Objects") + 1]; Objects.Enabled = true; // Show the overview lag time. CorradePollTimeDial.Value = (float) Math.Min(TimeSpan.FromMilliseconds(stopWatch.ElapsedMilliseconds).TotalSeconds, CorradePollTimeDial.MaxValue); })); // Get avatar positions. // Pattern: [...], X, 10, Y, 63, Z, 200, [...], X, 52, Y, 73, Z, 55, [...[...]] float X = 0, Y = 0, Z = 0; var positions = data.Select((x, i) => new {Item = x, Index = i}) .Where(x => x.Item.Equals("X") || x.Item.Equals("Y") || x.Item.Equals("Z")) .Select(z => data[z.Index + 1]).Select((x, i) => new {Value = x, Index = i}) .GroupBy(x => x.Index/3) .Select(x => x.Select(v => v.Value).ToList()) .Where( x => float.TryParse(x[0], out X) && float.TryParse(x[1], out Y) && float.TryParse(x[2], out Z)) .Select(x => new Vector3(X, Y, Z)) .ToList(); // Get the map image. result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getgridregiondata"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"data", "MapImageID"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result) || !bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception(); data = CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))).ToList(); if (!data.Count.Equals(2)) throw new Exception(); result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "download"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"item", data.Last()}, {"type", "Texture"}, {"format", "Jpeg"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result) || !bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception(); var mapImageBytes = Convert.FromBase64String(wasInput(KeyValue.Get("data", result))); Image mapImage; using (var memoryStream = new MemoryStream(mapImageBytes, 0, mapImageBytes.Length)) { mapImage = Image.FromStream(memoryStream); } // Draw the avatars onto the map. var mapGraphics = Graphics.FromImage(mapImage); foreach (var position in positions) { mapGraphics.FillEllipse(Brushes.Chartreuse, new Rectangle((int) position.X, (int) position.Y, 4, 4)); } mapGraphics.DrawImage(mapImage, new Point(0, 0)); vassalForm.BeginInvoke((MethodInvoker) (() => { RegionAvatarsMap.SizeMode = PictureBoxSizeMode.StretchImage; RegionAvatarsMap.Image = mapImage; RegionAvatarsMap.Refresh(); })); } catch (Exception) { } overviewTabTimer.Start(); }; overviewTabTimer.Start(); regionsStateTabTimer.Elapsed += (o, p) => { // Do not do anything in case the tab is not selected. var run = false; vassalForm.Invoke((MethodInvoker) (() => { run = Tabs.SelectedTab.Equals(RegionsStateTab); })); if (!run) { regionsStateTabTimer.Stop(); return; } regionsStateTabTimer.Stop(); try { var regionName = string.Empty; vassalForm.Invoke((MethodInvoker) (() => { regionName = RegionsStateGridView.Rows[regionsStateCheckIndex].Cells["RegionsStateRegionName"].Value .ToString(); RegionsStateGridView.Rows[regionsStateCheckIndex].DefaultCellStyle.BackColor = Color.Gold; })); if (string.IsNullOrEmpty(regionName)) throw new Exception(); // Get the region status. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getgridregiondata"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"region", regionName}, {"data", "Access"} }, wasOutput)).Result); bool success; if (string.IsNullOrEmpty(result) || !bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception(); var data = CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))).ToList(); if (!data.Count.Equals(2)) throw new Exception(); var status = data.Last(); vassalForm.Invoke((MethodInvoker) (() => { switch (status) { case "Unknown": case "Down": case "NonExistent": RegionsStateGridView.Rows[regionsStateCheckIndex].DefaultCellStyle.BackColor = Color.LightPink; RegionsStateGridView.Rows[regionsStateCheckIndex].Cells["RegionsStateLastState"].Value = status; break; default: RegionsStateGridView.Rows[regionsStateCheckIndex].DefaultCellStyle.BackColor = Color.LightGreen; RegionsStateGridView.Rows[regionsStateCheckIndex].Cells["RegionsStateLastState"].Value = "Up"; break; } RegionsStateGridView.Rows[regionsStateCheckIndex].Cells["RegionsStateLastCheck"].Value = DateTime .Now.ToUniversalTime() .ToString(LINDEN_CONSTANTS.LSL.DATE_TIME_STAMP); })); } catch (Exception) { } finally { ++regionsStateCheckIndex; vassalForm.Invoke((MethodInvoker) (() => { if (regionsStateCheckIndex >= RegionsStateGridView.Rows.Count) { regionsStateCheckIndex = 0; } })); } regionsStateTabTimer.Start(); }; regionsStateTabTimer.Start(); // Start the top scores timer. estateTopTabTimer.Elapsed += (o, p) => { // Do not do anything in case the tab is not selected. var run = false; vassalForm.Invoke((MethodInvoker) (() => { run = Tabs.SelectedTab.Equals(EstateTopTab); })); if (!run) { estateTopTabTimer.Stop(); return; } estateTopTabTimer.Stop(); try { // Get the top scripts. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getregiontop"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", "scripts"} }, wasOutput)).Result); bool success; if (string.IsNullOrEmpty(result) || !bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception(); var data = new HashSet>(CSV.ToEnumerable(wasInput(KeyValue.Get("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) (() => { // Remove rows that are not in the data update. foreach ( var index in TopScriptsGridView.Rows.AsParallel().Cast() .Where( topScriptsRow => !data.Any(q => q[2].Equals(topScriptsRow.Cells["TopScriptsUUID"].Value))) .Select(q => q.Index)) { TopScriptsGridView.Rows.RemoveAt(index); } // Now update or add new data. foreach (var dataComponents in data.Where(q => q.Count.Equals(5))) { var row = TopScriptsGridView.Rows.AsParallel() .Cast() .FirstOrDefault(q => q.Cells["TopScriptsUUID"].Value.Equals(dataComponents[2])); switch (row != null) { case true: // the row exists, so update it. row.Cells["TopScriptsScore"].Value = dataComponents[0]; row.Cells["TopScriptsTaskName"].Value = dataComponents[1]; row.Cells["TopScriptsUUID"].Value = dataComponents[2]; row.Cells["TopScriptsOwner"].Value = dataComponents[3]; row.Cells["TopScriptsPosition"].Value = dataComponents[4]; break; case false: // the row dosn't exist, so add it. TopScriptsGridView.Rows.Add(dataComponents[0], dataComponents[1], dataComponents[2], dataComponents[3], dataComponents[4]); break; } } })); // Get the top colliders. result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getregiontop"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", "colliders"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result) || !bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception(); data = new HashSet>(CSV.ToEnumerable(wasInput(KeyValue.Get("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) (() => { // Remove rows that are not in the data update. foreach ( var index in TopCollidersGridView.Rows.AsParallel().Cast() .Where( topCollidersRow => !data.Any(q => q[2].Equals(topCollidersRow.Cells["TopCollidersUUID"].Value))) .Select(q => q.Index)) { TopCollidersGridView.Rows.RemoveAt(index); } // Now update or add new data. foreach (var dataComponents in data.Where(q => q.Count.Equals(5))) { var row = TopCollidersGridView.Rows.AsParallel() .Cast() .FirstOrDefault(q => q.Cells["TopCollidersUUID"].Value.Equals(dataComponents[2])); switch (row != null) { case true: // the row exists, so update it. row.Cells["TopCollidersScore"].Value = dataComponents[0]; row.Cells["TopSCollidersTaskName"].Value = dataComponents[1]; row.Cells["TopCollidersUUID"].Value = dataComponents[2]; row.Cells["TopCollidersOwner"].Value = dataComponents[3]; row.Cells["TopCollidersPosition"].Value = dataComponents[4]; break; case false: // the row dosn't exist, so add it. TopCollidersGridView.Rows.Add(dataComponents[0], dataComponents[1], dataComponents[2], dataComponents[3], dataComponents[4]); break; } } })); } catch (Exception) { } estateTopTabTimer.Start(); }; estateTopTabTimer.Start(); // Start the resident list timer. residentListTabTimer.Elapsed += (o, p) => { // Do not do anything in case the tab is not selected. var run = false; vassalForm.Invoke((MethodInvoker) (() => { run = Tabs.SelectedTab.Equals(ResidentListTab); })); if (!run) { residentListTabTimer.Stop(); return; } residentListTabTimer.Stop(); try { // Get the avatar positions. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getavatarpositions"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"entity", "region"} }, wasOutput)).Result); bool success; if (string.IsNullOrEmpty(result) || !bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception(); var data = new HashSet>(CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))) .Select((x, i) => new {Index = i, Value = x}) .GroupBy(x => x.Index/3) .Select(x => x.Select(v => v.Value).ToList())); if (data.Count.Equals(0)) throw new Exception(); vassalForm.Invoke((MethodInvoker) (() => { // Remove rows that are not in the data update. foreach ( var index in ResidentListGridView.Rows.AsParallel().Cast() .Where( residentListRow => !data.Any(q => q[1].Equals(residentListRow.Cells["ResidentListUUID"].Value))) .Select(q => q.Index)) { ResidentListGridView.Rows.RemoveAt(index); } // Now update or add new data. foreach (var dataComponents in data.Where(q => q.Count.Equals(3))) { var row = ResidentListGridView.Rows.AsParallel() .Cast() .FirstOrDefault(q => q.Cells["ResidentListUUID"].Value.Equals(dataComponents[1])); switch (row != null) { case true: // the row exists, so update it. row.Cells["ResidentListName"].Value = dataComponents[0]; row.Cells["ResidentListUUID"].Value = dataComponents[1]; row.Cells["ResidentListPosition"].Value = dataComponents[2]; break; case false: // the row dosn't exist, so add it. ResidentListGridView.Rows.Add(dataComponents[0], dataComponents[1], dataComponents[2]); break; } } })); } catch (Exception) { } residentListTabTimer.Start(); }; residentListTabTimer.Start(); estateTexturesTabTimer.Elapsed += (o, p) => { // Do not do anything in case the tab is not selected. var run = false; vassalForm.Invoke((MethodInvoker) (() => { run = Tabs.SelectedTab.Equals(EstateTexturesTab); })); if (!run) { estateTexturesTabTimer.Stop(); return; } estateTexturesTabTimer.Stop(); try { // Get the region terrain texture UUIDs. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getregionterraintextures"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password} }, wasOutput)).Result); bool success; if (string.IsNullOrEmpty(result) || !bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception(); var data = CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))).ToList(); if (!data.Count.Equals(4)) throw new Exception(); vassalForm.Invoke((MethodInvoker) (() => { RegionTexturesLowUUIDBox.Text = data[0]; if (string.IsNullOrEmpty(RegionTexturesLowUUIDApplyBox.Text)) { RegionTexturesLowUUIDApplyBox.Text = data[0]; } RegionTexturesMiddleLowUUIDBox.Text = data[1]; if (string.IsNullOrEmpty(RegionTexturesMiddleLowUUIDApplyBox.Text)) { RegionTexturesMiddleLowUUIDApplyBox.Text = data[1]; } RegionTexturesMiddleHighUUIDBox.Text = data[2]; if (string.IsNullOrEmpty(RegionTexturesMiddleHighUUIDApplyBox.Text)) { RegionTexturesMiddleHighUUIDApplyBox.Text = data[2]; } RegionTexturesHighUUIDBox.Text = data[3]; if (string.IsNullOrEmpty(RegionTexturesHighUUIDApplyBox.Text)) { RegionTexturesHighUUIDApplyBox.Text = data[1]; } })); Parallel.ForEach(Enumerable.Range(0, 4), i => { result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "download"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"item", data[i]}, {"type", "Texture"}, {"format", "Jpeg"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result) || !bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) return; var mapImageBytes = Convert.FromBase64String(wasInput(KeyValue.Get("data", result))); using (var memoryStream = new MemoryStream(mapImageBytes, 0, mapImageBytes.Length)) { groundTextureImages[i] = Image.FromStream(memoryStream); } switch (i) { case 0: vassalForm.BeginInvoke( (MethodInvoker) (() => { RegionTexturesLowPictureBox.Image = groundTextureImages[0]; })); break; case 1: vassalForm.BeginInvoke( (MethodInvoker) (() => { RegionTexturesMiddleLowPictureBox.Image = groundTextureImages[1]; })); break; case 2: vassalForm.BeginInvoke( (MethodInvoker) (() => { RegionTexturesMiddleHighPictureBox.Image = groundTextureImages[2]; })); break; case 3: vassalForm.BeginInvoke( (MethodInvoker) (() => { RegionTexturesHighPictureBox.Image = groundTextureImages[3]; })); break; } }); } catch (Exception) { } estateTexturesTabTimer.Start(); }; estateTexturesTabTimer.Start(); } private void RequestedEditRegions(object sender, EventArgs e) { // Clear any selection. LoadedRegionsBox.SelectedIndex = -1; var regionEditForm = new RegionEditForm {TopMost = true}; regionEditForm.Show(); } private void RequestSelecting(object sender, TabControlCancelEventArgs e) { e.Cancel = !e.TabPage.Enabled; } private void RequestExportTopScripts(object sender, EventArgs e) { vassalForm.BeginInvoke((MethodInvoker) (() => { switch (vassalForm.ExportCSVDialog.ShowDialog()) { case DialogResult.OK: var file = vassalForm.ExportCSVDialog.FileName; new Thread(() => { vassalForm.BeginInvoke((MethodInvoker) (() => { try { vassalForm.StatusText.Text = @"exporting..."; vassalForm.StatusProgress.Value = 0; using (var streamWriter = new StreamWriter(file, false, Encoding.UTF8)) { foreach (DataGridViewRow topScriptsRow in TopScriptsGridView.Rows) { streamWriter.WriteLine(CSV.FromEnumerable(new[] { topScriptsRow.Cells["TopScriptsScore"].Value.ToString(), topScriptsRow.Cells["TopScriptsTaskName"].Value.ToString(), topScriptsRow.Cells["TopScriptsUUID"].Value.ToString(), topScriptsRow.Cells["TopScriptsOwner"].Value.ToString(), topScriptsRow.Cells["TopScriptsPosition"].Value.ToString() })); } } vassalForm.StatusText.Text = @"exported"; vassalForm.StatusProgress.Value = 100; } catch (Exception ex) { vassalForm.StatusText.Text = ex.Message; } })); }) {IsBackground = true}.Start(); break; } })); } private void RequestExportTopColliders(object sender, EventArgs e) { vassalForm.BeginInvoke((MethodInvoker) (() => { switch (vassalForm.ExportCSVDialog.ShowDialog()) { case DialogResult.OK: var file = vassalForm.ExportCSVDialog.FileName; new Thread(() => { vassalForm.BeginInvoke((MethodInvoker) (() => { try { vassalForm.StatusText.Text = @"exporting..."; vassalForm.StatusProgress.Value = 0; using (var streamWriter = new StreamWriter(file, false, Encoding.UTF8)) { foreach (DataGridViewRow topCollidersRow in TopCollidersGridView.Rows) { streamWriter.WriteLine(CSV.FromEnumerable(new[] { topCollidersRow.Cells["TopCollidersScore"].Value.ToString(), topCollidersRow.Cells["TopCollidersTaskName"].Value.ToString(), topCollidersRow.Cells["TopCollidersUUID"].Value.ToString(), topCollidersRow.Cells["TopCollidersOwner"].Value.ToString(), topCollidersRow.Cells["TopCollidersPosition"].Value.ToString() })); } } vassalForm.StatusText.Text = @"exported"; vassalForm.StatusProgress.Value = 100; } catch (Exception ex) { vassalForm.StatusText.Text = ex.Message; } })); }) {IsBackground = true}.Start(); break; } })); } private void RequestFilterTopScripts(object sender, EventArgs e) { vassalForm.BeginInvoke((MethodInvoker) (() => { Regex topScriptsRowRegex; switch (!string.IsNullOrEmpty(TopScriptsFilter.Text)) { case true: topScriptsRowRegex = new Regex(TopScriptsFilter.Text, RegexOptions.Compiled); break; default: topScriptsRowRegex = new Regex(@".+?", RegexOptions.Compiled); break; } foreach (var topScriptsRow in TopScriptsGridView.Rows.AsParallel().Cast()) { topScriptsRow.Visible = topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsScore"].Value.ToString()) || topScriptsRowRegex.IsMatch( topScriptsRow.Cells["TopScriptsTaskName"].Value.ToString()) || topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsUUID"].Value.ToString()) || topScriptsRowRegex.IsMatch(topScriptsRow.Cells["TopScriptsOwner"].Value.ToString()) || topScriptsRowRegex.IsMatch( topScriptsRow.Cells["TopScriptsPosition"].Value.ToString()); } })); } private void RequestFilterTopColliders(object sender, EventArgs e) { vassalForm.BeginInvoke((MethodInvoker) (() => { Regex topCollidersRowRegex; switch (!string.IsNullOrEmpty(TopScriptsFilter.Text)) { case true: topCollidersRowRegex = new Regex(TopScriptsFilter.Text, RegexOptions.Compiled); break; default: topCollidersRowRegex = new Regex(@".+?", RegexOptions.Compiled); break; } foreach ( var topCollidersRow in TopCollidersGridView.Rows.AsParallel().Cast()) { topCollidersRow.Visible = topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersScore"].Value.ToString()) || topCollidersRowRegex.IsMatch( topCollidersRow.Cells["TopCollidersTaskName"].Value.ToString()) || topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersUUID"].Value.ToString()) || topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersOwner"].Value.ToString()) || topCollidersRowRegex.IsMatch( topCollidersRow.Cells["TopCollidersPosition"].Value.ToString()); } })); } private void RequestReturnTopScriptsObjects(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { vassalForm.ReturnTopScriptsButton.Enabled = false; RegionTeleportGroup.Enabled = false; })); // Enqueue all the UUIDs to return. var returnUUIDs = new Queue>(); vassalForm.Invoke((MethodInvoker) (() => { foreach ( var topScriptsRow in TopScriptsGridView.Rows.AsParallel() .Cast() .Where(o => o.Selected || o.Cells.Cast().Any(p => p.Selected))) { Vector3 objectPosition; UUID returnUUID; if (!UUID.TryParse(topScriptsRow.Cells["TopScriptsUUID"].Value.ToString(), out returnUUID) || !Vector3.TryParse(topScriptsRow.Cells["TopScriptsPosition"].Value.ToString(), out objectPosition)) continue; returnUUIDs.Enqueue(new KeyValuePair(returnUUID, objectPosition)); } })); // If no rows were selected, enable teleports, the return button and return. if (returnUUIDs.Count.Equals(0)) { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.ReturnTopScriptsButton.Enabled = true; RegionTeleportGroup.Enabled = true; })); return; } new Thread(() => { Monitor.Enter(ClientInstanceTeleportLock); try { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusProgress.Value = 0; })); var totalObjects = returnUUIDs.Count; do { // Dequeue the first object. var objectData = returnUUIDs.Dequeue(); vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Returning object UUID: " + objectData.Key; })); var currentRegionName = string.Empty; vassalForm.Invoke((MethodInvoker) (() => { currentRegionName = CurrentRegionName.Text; })); // Teleport to the object. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "teleport"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"entity", "region"}, {"region", currentRegionName}, {"position", objectData.Value.ToString()}, {"fly", "True"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade."; })); continue; } // Return the object. result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "derez"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"item", objectData.Key.ToString()}, {"range", "32"}, // maximal prim size = 64 - middle bounding box at half {"type", "ReturnToOwner"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade."; })); continue; } bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"No success status could be retrieved. "; })); continue; } switch (success) { case true: vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Returned object: " + objectData.Key; // Remove the row from the grid view. var row = TopScriptsGridView.Rows.AsParallel() .Cast() .FirstOrDefault( o => o.Cells["TopScriptsUUID"].Value.Equals(objectData.Key.ToString())); if (row == null) return; var i = row.Index; TopScriptsGridView.Rows.RemoveAt(i); })); break; case false: vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Could not return object " + objectData.Key + @": " + wasInput(KeyValue.Get("error", result)); })); break; } vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusProgress.Value = Math.Min((int) (100d*Math.Abs(returnUUIDs.Count - totalObjects)/totalObjects), 100); })); } while (!returnUUIDs.Count.Equals(0)); vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusProgress.Value = 100; })); } catch (Exception ex) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Unexpected error: " + ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); // Allow teleports and enable button. vassalForm.BeginInvoke((MethodInvoker) (() => { vassalForm.ReturnTopScriptsButton.Enabled = true; RegionTeleportGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestReturnTopCollidersObjects(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { vassalForm.ReturnTopCollidersButton.Enabled = false; RegionTeleportGroup.Enabled = false; })); // Enqueue all the UUIDs to return. var returnObjectUUIDQueue = new Queue>(); vassalForm.Invoke((MethodInvoker) (() => { foreach ( var topCollidersRow in TopCollidersGridView.Rows.AsParallel() .Cast() .Where(o => o.Selected || o.Cells.Cast().Any(p => p.Selected))) { Vector3 objectPosition; UUID returnUUID; if (!UUID.TryParse(topCollidersRow.Cells["TopCollidersUUID"].Value.ToString(), out returnUUID) || !Vector3.TryParse(topCollidersRow.Cells["TopCollidersPosition"].Value.ToString(), out objectPosition)) continue; returnObjectUUIDQueue.Enqueue(new KeyValuePair(returnUUID, objectPosition)); } })); // If no rows were selected, enable teleports, the return button and return. if (returnObjectUUIDQueue.Count.Equals(0)) { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.ReturnTopCollidersButton.Enabled = true; RegionTeleportGroup.Enabled = true; })); return; } new Thread(() => { Monitor.Enter(ClientInstanceTeleportLock); try { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusProgress.Value = 0; })); var totalObjects = returnObjectUUIDQueue.Count; do { // Dequeue the first object. var objectData = returnObjectUUIDQueue.Dequeue(); vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Returning UUID: " + objectData.Key; })); var currentRegionName = string.Empty; vassalForm.Invoke((MethodInvoker) (() => { currentRegionName = CurrentRegionName.Text; })); // Teleport to the object. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "teleport"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"entity", "region"}, {"region", currentRegionName}, {"position", objectData.Value.ToString()}, {"fly", "True"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade."; })); continue; } // Return the object. result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "derez"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"item", objectData.Key.ToString()}, {"range", "32"}, // maximal prim size = 64 - middle bounding box at half {"type", "ReturnToOwner"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade."; })); continue; } bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"No success status could be retrieved. "; })); continue; } switch (success) { case true: vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Returned object: " + objectData.Key; // Remove the row from the grid view. var row = TopCollidersGridView.Rows.AsParallel() .Cast() .FirstOrDefault( o => o.Cells["TopCollidersUUID"].Value.Equals(objectData.Key.ToString())); if (row == null) return; var i = row.Index; TopCollidersGridView.Rows.RemoveAt(i); })); break; case false: vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Could not return object " + objectData.Key + @": " + wasInput(KeyValue.Get("error", result)); })); break; } vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusProgress.Value = Math.Min( (int) (100d*Math.Abs(returnObjectUUIDQueue.Count - totalObjects)/totalObjects), 100); })); } while (!returnObjectUUIDQueue.Count.Equals(0)); vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusProgress.Value = 100; })); } catch (Exception ex) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Unexpected error: " + ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); // Allow teleports and enable button. vassalForm.BeginInvoke((MethodInvoker) (() => { vassalForm.ReturnTopScriptsButton.Enabled = true; RegionTeleportGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestBatchRestart(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { vassalForm.BatchRestartButton.Enabled = false; vassalForm.RegionRestartDelayBox.Enabled = false; RegionTeleportGroup.Enabled = false; })); // Enqueue all the regions to restart. var restartRegionQueue = new Queue>(); vassalForm.Invoke((MethodInvoker) (() => { foreach ( var topCollidersRow in BatchRestartGridView.Rows.AsParallel() .Cast() .Where(o => o.Selected || o.Cells.Cast().Any(p => p.Selected))) { Vector3 objectPosition; var regionName = topCollidersRow.Cells["BatchRestartRegionName"].Value.ToString(); if (string.IsNullOrEmpty(regionName) || !Vector3.TryParse(topCollidersRow.Cells["BatchRestartPosition"].Value.ToString(), out objectPosition)) continue; restartRegionQueue.Enqueue(new KeyValuePair(regionName, objectPosition)); } })); // If no rows were selected, enable teleports, the return button and return. if (restartRegionQueue.Count.Equals(0)) { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.BatchRestartButton.Enabled = true; vassalForm.RegionRestartDelayBox.Enabled = true; RegionTeleportGroup.Enabled = true; })); return; } new Thread(() => { Monitor.Enter(ClientInstanceTeleportLock); try { do { // Dequeue the first object. var restartRegionData = restartRegionQueue.Dequeue(); DataGridViewRow currentDataGridViewRow = null; vassalForm.Invoke((MethodInvoker) (() => { currentDataGridViewRow = vassalForm.BatchRestartGridView.Rows.AsParallel() .Cast() .FirstOrDefault( o => o.Cells["BatchRestartRegionName"].Value.ToString() .Equals(restartRegionData.Key, StringComparison.OrdinalIgnoreCase) && o.Cells["BatchRestartPosition"].Value.ToString() .Equals(restartRegionData.Value.ToString(), StringComparison.OrdinalIgnoreCase)); })); if (currentDataGridViewRow == null) continue; try { var success = false; string result; // Retry to teleport to each region a few times. var teleportRetries = 3; do { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Attempting to teleport to " + restartRegionData.Key + @" " + @"(" + teleportRetries.ToString(Utils.EnUsCulture) + @")"; })); // Teleport to the region. result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "teleport"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"entity", "region"}, {"region", restartRegionData.Key}, {"position", restartRegionData.Value.ToString()}, {"fly", "True"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade."; })); continue; } if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"No success status could be retrieved. "; })); continue; } switch (success) { case true: vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Teleport succeeded."; })); break; default: // In case the destination is to close (Corrade status code 37559), // then we are on the same region so no need to retry. uint status; //37559 switch ( uint.TryParse(wasInput(KeyValue.Get("status", result)), out status) && status.Equals(37559)) { case true: // We are on the region already! success = true; break; default: vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Teleport failed."; })); break; } break; } // Pause for teleport (10 teleports / 15s allowed). Thread.Sleep(700); } while (!success && !(--teleportRetries).Equals(0)); if (!success) throw new Exception("Failed to teleport to region."); result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getregiondata"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"data", "IsEstateManager"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not retrieve estate rights."); var data = CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))).ToList(); if (!data.Count.Equals(2)) throw new Exception("Could not retrieve estate rights."); bool isEstateManager; switch ( bool.TryParse(data[data.IndexOf("IsEstateManager") + 1], out isEstateManager) && isEstateManager) { case true: // we are an estate manager result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "restartregion"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"action", "restart"}, { "delay", vassalForm.RegionRestartDelayBox.Text } }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not schedule a region restart."); vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Region scheduled for restart."; currentDataGridViewRow.Selected = false; currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightGreen; foreach ( var cell in currentDataGridViewRow.Cells.AsParallel().Cast()) { cell.ToolTipText = @"Region scheduled for restart."; } })); break; default: throw new Exception("No estate manager rights for region restart."); } } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = ex.Message; currentDataGridViewRow.Selected = false; currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightPink; foreach ( var cell in currentDataGridViewRow.Cells.AsParallel().Cast()) { cell.ToolTipText = ex.Message; } })); } finally { // Pause for teleport (10 teleports / 15s allowed). Thread.Sleep(700); } } while (!restartRegionQueue.Count.Equals(0)); } catch (Exception) { } finally { Monitor.Exit(ClientInstanceTeleportLock); // Allow teleports and enable button. vassalForm.BeginInvoke((MethodInvoker) (() => { vassalForm.BatchRestartButton.Enabled = true; vassalForm.RegionRestartDelayBox.Enabled = true; RegionTeleportGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestFilterResidentList(object sender, EventArgs e) { vassalForm.BeginInvoke((MethodInvoker) (() => { Regex residentListRowRegex; switch (!string.IsNullOrEmpty(ResidentListFilter.Text)) { case true: residentListRowRegex = new Regex(ResidentListFilter.Text, RegexOptions.Compiled); break; default: residentListRowRegex = new Regex(@".+?", RegexOptions.Compiled); break; } foreach ( var residentListRow in ResidentListGridView.Rows.AsParallel().Cast()) { residentListRow.Visible = residentListRowRegex.IsMatch(residentListRow.Cells["ResidentListName"].Value.ToString()) || residentListRowRegex.IsMatch( residentListRow.Cells["ResidentListUUID"].Value.ToString()) || residentListRowRegex.IsMatch(residentListRow.Cells["ResidentListPosition"].Value.ToString()); } })); } private void RequestBanAgents(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { ResidentListTeleportHomeGroup.Enabled = false; ResidentListBanGroup.Enabled = false; RegionTeleportGroup.Enabled = false; })); // Enqueue all the agents to ban. var agentsQueue = new Queue(); vassalForm.Invoke((MethodInvoker) (() => { foreach ( var residentListRow in ResidentListGridView.Rows.AsParallel() .Cast() .Where(o => o.Selected || o.Cells.Cast().Any(p => p.Selected))) { UUID agentUUID; if (!UUID.TryParse(residentListRow.Cells["ResidentListUUID"].Value.ToString(), out agentUUID)) continue; agentsQueue.Enqueue(agentUUID); } })); // If no rows were selected, enable teleports, the return button and return. if (agentsQueue.Count.Equals(0)) { vassalForm.Invoke((MethodInvoker) (() => { ResidentListBanGroup.Enabled = true; RegionTeleportGroup.Enabled = true; })); return; } new Thread(() => { Monitor.Enter(ClientInstanceTeleportLock); try { do { // Dequeue the first object. var agentUUID = agentsQueue.Dequeue(); DataGridViewRow currentDataGridViewRow = null; vassalForm.Invoke((MethodInvoker) (() => { currentDataGridViewRow = vassalForm.ResidentListGridView.Rows.AsParallel() .Cast() .FirstOrDefault( o => o.Cells["ResidentListUUID"].Value.ToString() .Equals(agentUUID.ToString(), StringComparison.OrdinalIgnoreCase)); })); if (currentDataGridViewRow == null) continue; try { var alsoBan = false; vassalForm.Invoke( (MethodInvoker) (() => { alsoBan = vassalForm.ResidentBanAllEstatesBox.Checked; })); // Ban the resident. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "setestatelist"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", "ban"}, {"action", "add"}, {"agent", agentUUID.ToString()}, {"all", alsoBan.ToString()} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); switch (success) { case true: vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Resident banned."; currentDataGridViewRow.Selected = false; currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightGreen; foreach ( var cell in currentDataGridViewRow.Cells.AsParallel().Cast()) { cell.ToolTipText = @"Resident banned."; } })); break; default: throw new Exception("Unable to ban resident."); } } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = ex.Message; currentDataGridViewRow.Selected = false; currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightPink; foreach ( var cell in currentDataGridViewRow.Cells.AsParallel().Cast()) { cell.ToolTipText = ex.Message; } })); } } while (agentsQueue.Count.Equals(0)); } catch (Exception) { } finally { Monitor.Exit(ClientInstanceTeleportLock); // Allow teleports and enable button. vassalForm.BeginInvoke((MethodInvoker) (() => { ResidentListTeleportHomeGroup.Enabled = true; ResidentListBanGroup.Enabled = true; RegionTeleportGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestRipTerrain(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; RipTerrainButton.Enabled = false; })); new Thread(() => { Monitor.Enter(ClientInstanceTeleportLock); try { // Get the map heights. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getterrainheight"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"entity", "region"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not get terrain heights."); var heights = new List(); foreach (var map in CSV.ToEnumerable(wasInput(KeyValue.Get("data", result)))) { double height; if (!double.TryParse(map, out height)) continue; heights.Add(height); } if (heights.Count.Equals(0)) throw new Exception("Could not get terrain heights."); var maxHeight = heights.Max(); using (var bitmap = new Bitmap(256, 256)) { foreach (var x in Enumerable.Range(1, 255)) { foreach (var y in Enumerable.Range(1, 255)) { bitmap.SetPixel(x, 256 - y, Color.FromArgb( Math.Max( (int) Numerics.MapValueToRange(heights[256*x + y], 0, maxHeight, 0, 255), 0), 0, 0)); } } var closureBitmap = (Bitmap) bitmap.Clone(); vassalForm.BeginInvoke((MethodInvoker) (() => { switch (vassalForm.SavePNGFileDialog.ShowDialog()) { case DialogResult.OK: var file = vassalForm.SavePNGFileDialog.FileName; new Thread(() => { vassalForm.BeginInvoke((MethodInvoker) (() => { try { vassalForm.StatusText.Text = @"saving terrain..."; vassalForm.StatusProgress.Value = 0; closureBitmap.Save(file, ImageFormat.Png); vassalForm.StatusText.Text = @"terrain saved"; vassalForm.StatusProgress.Value = 100; } catch (Exception ex) { vassalForm.StatusText.Text = ex.Message; } finally { closureBitmap.Dispose(); } })); }) {IsBackground = true}.Start(); break; } })); } } catch (Exception ex) { vassalForm.BeginInvoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); vassalForm.BeginInvoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = true; RipTerrainButton.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestDownloadTerrain(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; DownloadTerrainButton.Enabled = false; })); new Thread(() => { Monitor.Enter(ClientInstanceTeleportLock); try { // Download the terrain. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "terrain"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"action", "get"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not download terrain."); var data = Convert.FromBase64String(wasInput(KeyValue.Get("data", result))); vassalForm.BeginInvoke((MethodInvoker) (() => { switch (vassalForm.SaveRawFileDialog.ShowDialog()) { case DialogResult.OK: var file = vassalForm.SaveRawFileDialog.FileName; new Thread(() => { vassalForm.BeginInvoke((MethodInvoker) (() => { try { vassalForm.StatusText.Text = @"saving terrain..."; vassalForm.StatusProgress.Value = 0; File.WriteAllBytes(file, data); vassalForm.StatusText.Text = @"terrain saved"; vassalForm.StatusProgress.Value = 100; } catch (Exception ex) { vassalForm.StatusText.Text = ex.Message; } })); }) {IsBackground = true}.Start(); break; } })); } catch (Exception ex) { vassalForm.BeginInvoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); vassalForm.BeginInvoke((MethodInvoker) (() => { DownloadTerrainButton.Enabled = true; RegionTeleportGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestUploadTerrain(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; UploadTerrainButton.Enabled = false; })); new Thread(() => { Monitor.Enter(ClientInstanceTeleportLock); try { byte[] data = null; vassalForm.Invoke((MethodInvoker) (() => { switch (vassalForm.LoadRawFileDialog.ShowDialog()) { case DialogResult.OK: var file = vassalForm.LoadRawFileDialog.FileName; vassalForm.StatusText.Text = @"loading terrain..."; vassalForm.StatusProgress.Value = 0; data = File.ReadAllBytes(file); vassalForm.StatusText.Text = @"terrain loaded"; vassalForm.StatusProgress.Value = 100; break; } })); // Upload the terrain. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "terrain"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"action", "set"}, {"data", Convert.ToBase64String(data)} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not upload terrain."); } catch (Exception ex) { vassalForm.BeginInvoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); vassalForm.BeginInvoke((MethodInvoker) (() => { UploadTerrainButton.Enabled = true; RegionTeleportGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestTabsChanged(object sender, EventArgs e) { vassalForm.BeginInvoke((MethodInvoker) (() => { overviewTabTimer.Stop(); regionsStateTabTimer.Stop(); residentListTabTimer.Stop(); estateTopTabTimer.Stop(); if (Tabs.SelectedTab.Equals(OverviewTab)) { overviewTabTimer.Start(); return; } if (Tabs.SelectedTab.Equals(RegionsStateTab)) { regionsStateTabTimer.Start(); return; } if (Tabs.SelectedTab.Equals(ResidentListTab)) { residentListTabTimer.Start(); return; } if (Tabs.SelectedTab.Equals(EstateTopTab)) { estateTopTabTimer.Start(); return; } if (Tabs.SelectedTab.Equals(EstateTexturesTab)) { estateTexturesTabTimer.Start(); } })); } private void RequestFilterEstateList(object sender, EventArgs e) { vassalForm.BeginInvoke((MethodInvoker) (() => { Regex estateListRowRegex; switch (!string.IsNullOrEmpty(EstateListFilter.Text)) { case true: estateListRowRegex = new Regex(EstateListFilter.Text, RegexOptions.Compiled); break; default: estateListRowRegex = new Regex(@".+?", RegexOptions.Compiled); break; } foreach (var estateListRow in EstateListGridView.Rows.AsParallel().Cast()) { estateListRow.Visible = estateListRowRegex.IsMatch(estateListRow.Cells["EstateListName"].Value.ToString()); } })); } private void EstateListSelected(object sender, EventArgs e) { var selectedEstateListType = string.Empty; var queryEstateList = false; vassalForm.Invoke((MethodInvoker) (() => { if (vassalForm.EstateListSelectBox.SelectedItem == null) return; selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString(); switch ( !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1) { case true: queryEstateList = true; break; default: EstateListsResidentsGroup.Enabled = false; EstateListsGroupsGroup.Enabled = false; queryEstateList = false; break; } })); if (!queryEstateList) return; new Thread(() => { try { Monitor.Enter(ClientInstanceTeleportLock); // Disable controls. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; EstateListsResidentsGroup.Enabled = false; EstateListsGroupsGroup.Enabled = false; })); // Get the selected estate list. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getestatelist"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", selectedEstateListType} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not retrieve estate list."); vassalForm.Invoke((MethodInvoker) (() => { EstateListGridView.Rows.Clear(); })); foreach (var data in CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))) .Where(x => !string.IsNullOrEmpty(x)) .Select((x, i) => new {Index = i, Value = x}) .GroupBy(x => x.Index/2) .Select(x => x.Select(v => v.Value).ToList()).Where(x => x.Count.Equals(2))) { vassalForm.Invoke( (MethodInvoker) (() => { EstateListGridView.Rows.Add(data.First(), data.Last()); })); } } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); // Enable controls vassalForm.Invoke((MethodInvoker) (() => { vassalForm.RegionTeleportGroup.Enabled = true; switch (selectedEstateListType) { case "ban": case "manager": case "user": EstateListsResidentsGroup.Enabled = true; EstateListsGroupsGroup.Enabled = false; break; case "group": EstateListsResidentsGroup.Enabled = false; EstateListsGroupsGroup.Enabled = true; break; } })); } }) {IsBackground = true}.Start(); } private void RequestRemoveEstateListMember(object sender, EventArgs e) { // Get the estate list type. var selectedEstateListType = string.Empty; var queryEstateList = false; vassalForm.Invoke((MethodInvoker) (() => { if (vassalForm.EstateListSelectBox.SelectedItem == null) { queryEstateList = false; return; } selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString(); switch ( !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1) { case true: queryEstateList = true; break; default: queryEstateList = false; break; } })); // If not estate list type is selected then return. if (!queryEstateList) return; // Enqueue all the regions to restart. var estateListMembersQueue = new Queue(); vassalForm.Invoke((MethodInvoker) (() => { foreach ( var estateListRow in EstateListGridView.Rows.AsParallel() .Cast() .Where(o => o.Selected || o.Cells.Cast().Any(p => p.Selected))) { UUID estateListMemberUUID; if (!UUID.TryParse(estateListRow.Cells["EstateListUUID"].Value.ToString(), out estateListMemberUUID)) continue; estateListMembersQueue.Enqueue(estateListMemberUUID); } })); // If no rows were selected, enable teleports, the return button and return. if (estateListMembersQueue.Count.Equals(0)) return; // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; RemoveEstateListMemberButton.Enabled = false; })); new Thread(() => { try { Monitor.Enter(ClientInstanceTeleportLock); var memberUUID = UUID.Zero; do { try { memberUUID = estateListMembersQueue.Dequeue(); // Remove the agent or group from the list. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "setestatelist"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", selectedEstateListType}, {"action", "remove"}, {selectedEstateListType.Equals("group") ? "target" : "agent", memberUUID.ToString()} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade"); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved"); if (!success) throw new Exception("Unable to remove member"); vassalForm.Invoke((MethodInvoker) (() => { foreach ( var i in EstateListGridView.Rows.AsParallel() .Cast() .Where( o => o.Cells["EstateListUUID"].Value.ToString() .Equals(memberUUID.ToString(), StringComparison.OrdinalIgnoreCase)).Select(o => o.Index) ) { EstateListGridView.Rows.RemoveAt(i); } })); } catch (Exception ex) { vassalForm.Invoke( (MethodInvoker) (() => { StatusText.Text = ex.Message + @": " + memberUUID; })); } } while (!estateListMembersQueue.Count.Equals(0)); } catch (Exception) { } finally { Monitor.Exit(ClientInstanceTeleportLock); // Enable teleports and enable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = true; RemoveEstateListMemberButton.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestEstateListsAddResident(object sender, EventArgs e) { // Get the estate list type. var selectedEstateListType = string.Empty; var queryEstateList = false; vassalForm.Invoke((MethodInvoker) (() => { selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString(); switch ( !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1) { case true: queryEstateList = true; break; default: queryEstateList = false; break; } })); // If not estate list type is selected then return. if (!queryEstateList) return; var firstName = string.Empty; var lastName = string.Empty; vassalForm.Invoke((MethodInvoker) (() => { switch (string.IsNullOrEmpty(EstateListsResidentFirstName.Text)) { case true: EstateListsResidentFirstName.BackColor = Color.MistyRose; return; default: EstateListsResidentFirstName.BackColor = Color.Empty; break; } firstName = EstateListsResidentFirstName.Text; switch (string.IsNullOrEmpty(EstateListsResidentLastName.Text)) { case true: EstateListsResidentLastName.BackColor = Color.MistyRose; return; default: EstateListsResidentLastName.BackColor = Color.Empty; break; } lastName = EstateListsResidentLastName.Text; })); if (string.IsNullOrEmpty(firstName) || string.IsNullOrEmpty(lastName)) return; // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; EstateListsResidentsGroup.Enabled = false; })); new Thread(() => { try { Monitor.Enter(ClientInstanceTeleportLock); // Add the resident to the list. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "setestatelist"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", selectedEstateListType}, {"action", "add"}, {"firstname", firstName}, {"lastname", lastName} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade"); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved"); if (!success) throw new Exception("Unable to add resident"); // Retrieve the estate list for updates. result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getestatelist"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", selectedEstateListType} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not retrieve estate list."); vassalForm.Invoke((MethodInvoker) (() => { EstateListGridView.Rows.Clear(); })); foreach (var data in CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))) .Where(x => !string.IsNullOrEmpty(x)) .Select((x, i) => new {Index = i, Value = x}) .GroupBy(x => x.Index/2) .Select(x => x.Select(v => v.Value).ToList()).Where(x => x.Count.Equals(2))) { vassalForm.Invoke( (MethodInvoker) (() => { EstateListGridView.Rows.Add(data.First(), data.Last()); })); } } catch (Exception ex) { vassalForm.Invoke( (MethodInvoker) (() => { StatusText.Text = ex.Message + @": " + firstName + @" " + lastName; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); // Enable teleports and enable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = true; EstateListsResidentsGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestEstateListsAddGroup(object sender, EventArgs e) { // Get the estate list type. var selectedEstateListType = string.Empty; var queryEstateList = false; vassalForm.Invoke((MethodInvoker) (() => { selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString(); switch ( !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1) { case true: queryEstateList = true; break; default: queryEstateList = false; break; } })); // If not estate list type is selected then return. if (!queryEstateList) return; var target = string.Empty; vassalForm.Invoke((MethodInvoker) (() => { switch (string.IsNullOrEmpty(EstateListsAddGroupBox.Text)) { case true: EstateListsAddGroupBox.BackColor = Color.MistyRose; return; default: EstateListsAddGroupBox.BackColor = Color.Empty; break; } target = EstateListsAddGroupBox.Text; })); if (string.IsNullOrEmpty(target)) return; // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; EstateListsGroupsGroup.Enabled = false; })); new Thread(() => { try { Monitor.Enter(ClientInstanceTeleportLock); // Add the group to the list. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "setestatelist"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", selectedEstateListType}, {"action", "add"}, {"target", target} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade"); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved"); if (!success) throw new Exception("Unable to add group"); // Retrieve the estate list for updates. result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getestatelist"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", selectedEstateListType} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not retrieve estate list."); vassalForm.Invoke((MethodInvoker) (() => { EstateListGridView.Rows.Clear(); })); foreach (var data in CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))) .Where(x => !string.IsNullOrEmpty(x)) .Select((x, i) => new {Index = i, Value = x}) .GroupBy(x => x.Index/2) .Select(x => x.Select(v => v.Value).ToList()).Where(x => x.Count.Equals(2))) { vassalForm.Invoke( (MethodInvoker) (() => { EstateListGridView.Rows.Add(data.First(), data.Last()); })); } } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message + @": " + target; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); // Enable teleports and enable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = true; EstateListsGroupsGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestRegionDebugApply(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; ApplyRegionDebugButton.Enabled = false; })); new Thread(() => { try { Monitor.Enter(ClientInstanceTeleportLock); var scripts = false; var collisons = false; var physics = false; vassalForm.Invoke((MethodInvoker) (() => { scripts = RegionDebugScriptsBox.Checked; collisons = RegionDebugCollisionsBox.Checked; physics = RegionDebugPhysicsBox.Checked; })); // Set the debug settings. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "setregiondebug"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"scripts", scripts.ToString()}, {"collisions", collisons.ToString()}, {"physics", physics.ToString()} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade"); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved"); if (!success) throw new Exception("Unable to set region debug"); } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); } // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = true; ApplyRegionDebugButton.Enabled = true; })); }) {IsBackground = true}.Start(); } private void RequestApplyRegionInfo(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; ApplyRegionInfoButton.Enabled = false; })); new Thread(() => { try { Monitor.Enter(ClientInstanceTeleportLock); var terraform = false; var fly = false; var damage = false; var resell = false; var push = false; var parcel = false; var mature = false; uint agentLimit = 20; var objectBonus = 2.0; var run = false; vassalForm.Invoke((MethodInvoker) (() => { terraform = RegionInfoTerraformBox.Checked; fly = RegionInfoFlyBox.Checked; damage = RegionInfoDamageBox.Checked; resell = RegioninfoResellBox.Checked; push = RegionInfoPushBox.Checked; parcel = RegionInfoParcelBox.Checked; mature = RegionInfoMatureBox.Checked; switch (!uint.TryParse(RegionInfoAgentLimitBox.Text, out agentLimit)) { case true: RegionInfoAgentLimitBox.BackColor = Color.MistyRose; return; default: RegionInfoAgentLimitBox.BackColor = Color.Empty; run = true; break; } switch (!double.TryParse(RegionInfoObjectBonusBox.Text, out objectBonus)) { case true: RegionInfoObjectBonusBox.BackColor = Color.MistyRose; return; default: RegionInfoAgentLimitBox.BackColor = Color.Empty; break; } run = true; })); if (!run) return; // Set the debug settings. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "setregioninfo"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"terraform", terraform.ToString()}, {"fly", fly.ToString()}, {"damage", damage.ToString()}, {"resell", resell.ToString()}, {"push", push.ToString()}, {"parcel", parcel.ToString()}, {"mature", mature.ToString()}, {"limit", agentLimit.ToString(Utils.EnUsCulture)}, {"bonus", objectBonus.ToString(Utils.EnUsCulture)} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade"); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved"); if (!success) throw new Exception("Unable to set region info"); } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); } // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = true; ApplyRegionInfoButton.Enabled = true; })); }) {IsBackground = true}.Start(); } private void RequestEstateTexturesApply(object sender, EventArgs e) { var groundTextureUUIDs = new List(); vassalForm.Invoke((MethodInvoker) (() => { UUID textureUUID; switch (!UUID.TryParse(RegionTexturesLowUUIDApplyBox.Text, out textureUUID)) { case true: RegionTexturesLowUUIDApplyBox.BackColor = Color.MistyRose; return; default: RegionTexturesLowUUIDApplyBox.BackColor = Color.Empty; break; } groundTextureUUIDs.Add(textureUUID); switch (!UUID.TryParse(RegionTexturesMiddleLowUUIDApplyBox.Text, out textureUUID)) { case true: RegionTexturesMiddleLowUUIDApplyBox.BackColor = Color.MistyRose; return; default: RegionTexturesMiddleLowUUIDApplyBox.BackColor = Color.Empty; break; } groundTextureUUIDs.Add(textureUUID); switch (!UUID.TryParse(RegionTexturesMiddleHighUUIDApplyBox.Text, out textureUUID)) { case true: RegionTexturesMiddleHighUUIDApplyBox.BackColor = Color.MistyRose; return; default: RegionTexturesMiddleHighUUIDApplyBox.BackColor = Color.Empty; break; } groundTextureUUIDs.Add(textureUUID); switch (!UUID.TryParse(RegionTexturesHighUUIDApplyBox.Text, out textureUUID)) { case true: RegionTexturesHighUUIDApplyBox.BackColor = Color.MistyRose; return; default: RegionTexturesHighUUIDApplyBox.BackColor = Color.Empty; break; } groundTextureUUIDs.Add(textureUUID); })); if (!groundTextureUUIDs.Count.Equals(4)) return; // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; GroundTexturesGroup.Enabled = false; })); new Thread(() => { try { Monitor.Enter(ClientInstanceTeleportLock); // Set the debug settings. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "setregionterraintextures"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"data", CSV.FromEnumerable(groundTextureUUIDs.Select(o => o.ToString()))} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade"); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved"); if (!success) throw new Exception("Unable to apply estate ground textures"); } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); } // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = true; GroundTexturesGroup.Enabled = true; })); }) {IsBackground = true}; } private void RequestDownloadRegionTexture(object sender, EventArgs e) { var textureName = string.Empty; var textureUUID = UUID.Zero; var run = false; vassalForm.Invoke((MethodInvoker) (() => { var button = sender as Button; if (button == null) { run = false; return; } textureName = button.Tag.ToString(); switch (textureName) { case "Low": if (!UUID.TryParse(RegionTexturesLowUUIDBox.Text, out textureUUID)) { run = false; return; } goto case "MiddleLow"; case "MiddleLow": if (!UUID.TryParse(RegionTexturesMiddleLowUUIDBox.Text, out textureUUID)) { run = false; return; } goto case "MiddleHigh"; case "MiddleHigh": if (!UUID.TryParse(RegionTexturesMiddleHighUUIDBox.Text, out textureUUID)) { run = false; return; } goto case "High"; case "High": if (!UUID.TryParse(RegionTexturesHighUUIDBox.Text, out textureUUID)) { run = false; return; } run = true; break; } })); if (!run) return; // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; GroundTexturesGroup.Enabled = false; })); new Thread(() => { try { Monitor.Enter(ClientInstanceTeleportLock); var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "download"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"item", textureUUID.ToString()}, {"type", "Texture"}, {"format", "Png"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade"); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved"); var mapImageBytes = Convert.FromBase64String(wasInput(KeyValue.Get("data", result))); Image mapImage; using (var memoryStream = new MemoryStream(mapImageBytes, 0, mapImageBytes.Length)) { mapImage = Image.FromStream(memoryStream); } vassalForm.BeginInvoke((MethodInvoker) (() => { switch (vassalForm.SavePNGFileDialog.ShowDialog()) { case DialogResult.OK: var file = vassalForm.SavePNGFileDialog.FileName; new Thread(() => { vassalForm.BeginInvoke((MethodInvoker) (() => { try { vassalForm.StatusText.Text = @"saving texture..."; vassalForm.StatusProgress.Value = 0; mapImage.Save(file, ImageFormat.Png); vassalForm.StatusText.Text = @"texture saved"; vassalForm.StatusProgress.Value = 100; } catch (Exception ex) { vassalForm.StatusText.Text = ex.Message; } finally { mapImage.Dispose(); } })); }) {IsBackground = true}.Start(); break; } })); } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); } // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = true; GroundTexturesGroup.Enabled = true; })); }) {IsBackground = true}.Start(); } private void RequestEstateListsAddGroupsFromCSV(object sender, EventArgs e) { // Get the estate list type. var selectedEstateListType = string.Empty; var queryEstateList = false; vassalForm.Invoke((MethodInvoker) (() => { if (vassalForm.EstateListSelectBox.SelectedItem == null) return; selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString(); switch ( !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1) { case true: queryEstateList = true; break; default: queryEstateList = false; break; } })); // If not estate list type is selected then return. if (!queryEstateList) return; var targets = new Queue>(); vassalForm.Invoke((MethodInvoker) (() => { switch (vassalForm.LoadCSVFile.ShowDialog()) { case DialogResult.OK: var file = vassalForm.LoadCSVFile.FileName; try { vassalForm.StatusText.Text = @"loading group list..."; vassalForm.StatusProgress.Value = 0; // import groups UUID targetUUID; foreach (var target in File.ReadAllLines(file) .AsParallel() .Select(o => new List(CSV.ToEnumerable(o))) .Where(o => o.Count == 2) .ToDictionary(o => o.First(), p => UUID.TryParse(p.Last(), out targetUUID) ? targetUUID : UUID.Zero)) { targets.Enqueue(target); } vassalForm.StatusText.Text = @"group list loaded"; vassalForm.StatusProgress.Value = 100; } catch (Exception ex) { vassalForm.StatusText.Text = ex.Message; } break; } })); if (targets.Count.Equals(0)) return; var initialQueueSize = targets.Count; // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; EstateListsGroupsGroup.Enabled = false; })); new Thread(() => { try { Monitor.Enter(ClientInstanceTeleportLock); do { var target = targets.Dequeue(); vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = @"Adding to estate list: " + target.Key; vassalForm.StatusProgress.Value = Math.Min((int) (100d*Math.Abs(targets.Count - initialQueueSize)/initialQueueSize), 100); })); // Skip any items that already exist. var run = false; vassalForm.Invoke((MethodInvoker) (() => { run = !EstateListGridView.Rows.AsParallel() .Cast() .Any(o => o.Cells["EstateListUUID"].Value.ToString().Equals(target.Value.ToString())); })); if (!run) continue; // Skip broken UUIDs. if (target.Value.Equals(UUID.Zero)) continue; try { // Add the group to the list. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "setestatelist"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", selectedEstateListType}, {"action", "add"}, {"target", target.Value.ToString()} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade"); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved"); if (!success) throw new Exception("Unable to add group"); } catch (Exception ex) { vassalForm.Invoke( (MethodInvoker) (() => { StatusText.Text = ex.Message + @": " + target.Value; })); } } while (!targets.Count.Equals(0)); // Retrieve the estate list. try { // Retrieve the estate list for updates. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getestatelist"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", selectedEstateListType} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not retrieve estate list."); vassalForm.Invoke((MethodInvoker) (() => { EstateListGridView.Rows.Clear(); })); foreach (var data in CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))) .Where(x => !string.IsNullOrEmpty(x)) .Select((x, i) => new {Index = i, Value = x}) .GroupBy(x => x.Index/2) .Select(x => x.Select(v => v.Value).ToList())) { vassalForm.BeginInvoke( (MethodInvoker) (() => { EstateListGridView.Rows.Add(data.First(), data.Last()); })); } } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); // Enable teleports and enable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = true; EstateListsGroupsGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestEstateListsAddResidentsFromCSV(object sender, EventArgs e) { // Get the estate list type. var selectedEstateListType = string.Empty; var queryEstateList = false; vassalForm.Invoke((MethodInvoker) (() => { if (vassalForm.EstateListSelectBox.SelectedItem == null) return; selectedEstateListType = vassalForm.EstateListSelectBox.SelectedItem.ToString(); switch ( !string.IsNullOrEmpty(selectedEstateListType) && vassalForm.EstateListSelectBox.SelectedIndex != -1) { case true: queryEstateList = true; break; default: queryEstateList = false; break; } })); // If not estate list type is selected then return. if (!queryEstateList) return; var targets = new Queue>(); vassalForm.Invoke((MethodInvoker) (() => { switch (vassalForm.LoadCSVFile.ShowDialog()) { case DialogResult.OK: var file = vassalForm.LoadCSVFile.FileName; try { vassalForm.StatusText.Text = @"loading residents list..."; vassalForm.StatusProgress.Value = 0; // import groups UUID targetUUID; foreach (var target in File.ReadAllLines(file) .AsParallel() .Select(o => new List(CSV.ToEnumerable(o))) .Where(o => o.Count == 2) .ToDictionary(o => o.First(), p => UUID.TryParse(p.Last(), out targetUUID) ? targetUUID : UUID.Zero)) { targets.Enqueue(target); } vassalForm.StatusText.Text = @"residents list loaded"; vassalForm.StatusProgress.Value = 100; } catch (Exception ex) { vassalForm.StatusText.Text = ex.Message; } break; } })); if (targets.Count.Equals(0)) return; var initialQueueSize = targets.Count; // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; EstateListsResidentsGroup.Enabled = false; })); new Thread(() => { try { Monitor.Enter(ClientInstanceTeleportLock); do { var target = targets.Dequeue(); vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = @"Adding to estate list: " + target.Key; vassalForm.StatusProgress.Value = Math.Min((int) (100d*Math.Abs(targets.Count - initialQueueSize)/initialQueueSize), 100); })); // Skip any items that already exist. var run = false; vassalForm.Invoke((MethodInvoker) (() => { run = !EstateListGridView.Rows.AsParallel() .Cast() .Any(o => o.Cells["EstateListUUID"].Value.ToString().Equals(target.Value.ToString())); })); if (!run) continue; // Skip broken UUIDs. if (target.Value.Equals(UUID.Zero)) continue; try { // Add the group to the list. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "setestatelist"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", selectedEstateListType}, {"action", "add"}, {"agent", target.Value.ToString()} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade"); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved"); if (!success) throw new Exception("Unable to add resident"); } catch (Exception ex) { vassalForm.Invoke( (MethodInvoker) (() => { StatusText.Text = ex.Message + @": " + target.Value; })); } } while (!targets.Count.Equals(0)); // Retrieve the estate list. try { // Retrieve the estate list for updates. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getestatelist"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"type", selectedEstateListType} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not retrieve estate list."); vassalForm.Invoke((MethodInvoker) (() => { EstateListGridView.Rows.Clear(); })); foreach (var data in CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))) .Where(x => !string.IsNullOrEmpty(x)) .Select((x, i) => new {Index = i, Value = x}) .GroupBy(x => x.Index/2) .Select(x => x.Select(v => v.Value).ToList())) { vassalForm.BeginInvoke( (MethodInvoker) (() => { EstateListGridView.Rows.Add(data.First(), data.Last()); })); } } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); // Enable teleports and enable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = true; EstateListsResidentsGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestExportEstateList(object sender, EventArgs e) { vassalForm.BeginInvoke((MethodInvoker) (() => { switch (vassalForm.ExportCSVDialog.ShowDialog()) { case DialogResult.OK: var file = vassalForm.ExportCSVDialog.FileName; new Thread(() => { vassalForm.BeginInvoke((MethodInvoker) (() => { try { vassalForm.StatusText.Text = @"exporting..."; vassalForm.StatusProgress.Value = 0; using (var streamWriter = new StreamWriter(file, false, Encoding.UTF8)) { foreach (DataGridViewRow estateListRow in EstateListGridView.Rows) { streamWriter.WriteLine(CSV.FromEnumerable(new[] { estateListRow.Cells["EstateListName"].Value.ToString(), estateListRow.Cells["EstateListUUID"].Value.ToString() })); } } vassalForm.StatusText.Text = @"exported"; vassalForm.StatusProgress.Value = 100; } catch (Exception ex) { vassalForm.StatusText.Text = ex.Message; } })); }) {IsBackground = true}.Start(); break; } })); } private void RequestTeleportHome(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { ResidentListTeleportHomeGroup.Enabled = false; ResidentListBanGroup.Enabled = false; RegionTeleportGroup.Enabled = false; })); // Enqueue all the agents to teleport home. var agentsQueue = new Queue(); vassalForm.Invoke((MethodInvoker) (() => { foreach ( var residentListRow in ResidentListGridView.Rows.AsParallel() .Cast() .Where(o => o.Selected || o.Cells.Cast().Any(p => p.Selected))) { UUID agentUUID; if (!UUID.TryParse(residentListRow.Cells["ResidentListUUID"].Value.ToString(), out agentUUID)) continue; agentsQueue.Enqueue(agentUUID); } })); // If no rows were selected, enable teleports, the return button and return. if (agentsQueue.Count.Equals(0)) { vassalForm.Invoke((MethodInvoker) (() => { ResidentListBanGroup.Enabled = true; RegionTeleportGroup.Enabled = true; })); return; } new Thread(() => { Monitor.Enter(ClientInstanceTeleportLock); try { do { // Dequeue the first object. var agentUUID = agentsQueue.Dequeue(); DataGridViewRow currentDataGridViewRow = null; vassalForm.Invoke((MethodInvoker) (() => { currentDataGridViewRow = vassalForm.ResidentListGridView.Rows.AsParallel() .Cast() .FirstOrDefault( o => o.Cells["ResidentListUUID"].Value.ToString() .Equals(agentUUID.ToString(), StringComparison.OrdinalIgnoreCase)); })); if (currentDataGridViewRow == null) continue; try { // Teleport the user home. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "estateteleportusershome"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"avatars", agentUUID.ToString()} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); switch (success) { case true: vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Resident teleported home."; currentDataGridViewRow.Selected = false; currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightGreen; foreach ( var cell in currentDataGridViewRow.Cells.AsParallel().Cast()) { cell.ToolTipText = @"Resident teleported home."; } })); break; default: throw new Exception("Unable to teleport resident home."); } } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = ex.Message; currentDataGridViewRow.Selected = false; currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightPink; foreach ( var cell in currentDataGridViewRow.Cells.AsParallel().Cast()) { cell.ToolTipText = ex.Message; } })); } } while (agentsQueue.Count.Equals(0)); } catch (Exception) { } finally { Monitor.Exit(ClientInstanceTeleportLock); // Allow teleports and enable button. vassalForm.BeginInvoke((MethodInvoker) (() => { ResidentListTeleportHomeGroup.Enabled = true; ResidentListBanGroup.Enabled = true; RegionTeleportGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } private void RequestSetVariables(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = false; SetTerrainVariablesButton.Enabled = false; })); new Thread(() => { try { Monitor.Enter(ClientInstanceTeleportLock); var waterHeight = 10; var terrainRaiseLimit = 100; var terrainLowerLimit = -100; var useEstateSun = true; var fixedSun = false; var sunPosition = 18; var run = false; vassalForm.Invoke((MethodInvoker) (() => { useEstateSun = TerrainToolsUseEstateSunBox.Checked; fixedSun = TerrainToolsFixedSunBox.Checked; switch (!int.TryParse(TerrainToolsWaterHeightBox.Text, out waterHeight)) { case true: TerrainToolsWaterHeightBox.BackColor = Color.MistyRose; return; default: TerrainToolsWaterHeightBox.BackColor = Color.Empty; break; } switch (!int.TryParse(TerrainToolsTerrainRaiseLimitBox.Text, out terrainRaiseLimit)) { case true: TerrainToolsTerrainRaiseLimitBox.BackColor = Color.MistyRose; return; default: TerrainToolsTerrainRaiseLimitBox.BackColor = Color.Empty; break; } switch (!int.TryParse(TerrainToolsTerrainLowerLimitBox.Text, out terrainLowerLimit)) { case true: TerrainToolsTerrainLowerLimitBox.BackColor = Color.MistyRose; return; default: TerrainToolsTerrainLowerLimitBox.BackColor = Color.Empty; break; } switch (!int.TryParse(TerrainToolsSunPositionBox.Text, out sunPosition)) { case true: TerrainToolsSunPositionBox.BackColor = Color.MistyRose; return; default: TerrainToolsSunPositionBox.BackColor = Color.Empty; break; } run = true; })); if (!run) return; // Set the terrain variables. var result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "setregionterrainvariables"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"waterheight", waterHeight.ToString()}, {"terrainraiselimit", terrainRaiseLimit.ToString()}, {"terrainlowerlimit", terrainLowerLimit.ToString()}, {"useestatesun", useEstateSun.ToString()}, {"fixedsun", fixedSun.ToString()}, {"sunposition", sunPosition.ToString()} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade"); bool success; if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved"); if (!success) throw new Exception("Unable to set region variables"); } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { StatusText.Text = ex.Message; })); } finally { Monitor.Exit(ClientInstanceTeleportLock); } // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { RegionTeleportGroup.Enabled = true; SetTerrainVariablesButton.Enabled = true; })); }) {IsBackground = true}.Start(); } private void ReconnectRequested(object sender, EventArgs e) { // Spawn a thread to check Corrade's connection status. new Thread(() => { var tcpClient = new TcpClient(); try { var uri = new Uri(vassalConfiguration.HTTPServerURL); tcpClient.Connect(uri.Host, uri.Port); // port open vassalForm.BeginInvoke((MethodInvoker) (() => { vassalForm.Tabs.Enabled = true; })); // set the loading spinner if (vassalForm.RegionAvatarsMap.Image == null) { var thisAssembly = Assembly.GetExecutingAssembly(); var file = thisAssembly.GetManifestResourceStream("Vassal.img.loading.gif"); switch (file != null) { case true: vassalForm.BeginInvoke((MethodInvoker) (() => { vassalForm.RegionAvatarsMap.SizeMode = PictureBoxSizeMode.CenterImage; vassalForm.RegionAvatarsMap.Image = Image.FromStream(file); vassalForm.RegionAvatarsMap.Refresh(); })); break; } } } catch (Exception) { // port closed vassalForm.BeginInvoke((MethodInvoker) (() => { vassalForm.Tabs.Enabled = false; })); } }) {IsBackground = true}.Start(); } private void RequestBatchSetCovenant(object sender, EventArgs e) { // Block teleports and disable button. vassalForm.Invoke((MethodInvoker) (() => { vassalForm.BatchSetCovenantButton.Enabled = false; vassalForm.SetCovenantNotecardUUIDBox.Enabled = false; RegionTeleportGroup.Enabled = false; })); // Enqueue all the regions to set covenant. var batchSetCovenantQueue = new Queue>(); vassalForm.Invoke((MethodInvoker) (() => { foreach ( var topCollidersRow in BatchSetCovenantGridView.Rows.AsParallel() .Cast() .Where(o => o.Selected || o.Cells.Cast().Any(p => p.Selected))) { Vector3 objectPosition; var regionName = topCollidersRow.Cells["BatchSetCovenantRegionName"].Value.ToString(); if (string.IsNullOrEmpty(regionName) || !Vector3.TryParse(topCollidersRow.Cells["BatchSetCovenantPosition"].Value.ToString(), out objectPosition)) continue; batchSetCovenantQueue.Enqueue(new KeyValuePair(regionName, objectPosition)); } })); // If no rows were selected, enable teleports, the return button and return. if (batchSetCovenantQueue.Count.Equals(0)) { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.BatchSetCovenantButton.Enabled = true; vassalForm.SetCovenantNotecardUUIDBox.Enabled = true; RegionTeleportGroup.Enabled = true; })); return; } new Thread(() => { Monitor.Enter(ClientInstanceTeleportLock); try { do { // Dequeue the first object. var batchSetCovenantRegionData = batchSetCovenantQueue.Dequeue(); DataGridViewRow currentDataGridViewRow = null; vassalForm.Invoke((MethodInvoker) (() => { currentDataGridViewRow = vassalForm.BatchSetCovenantGridView.Rows.AsParallel() .Cast() .FirstOrDefault( o => o.Cells["BatchSetCovenantRegionName"].Value.ToString() .Equals(batchSetCovenantRegionData.Key, StringComparison.OrdinalIgnoreCase) && o.Cells["BatchSetCovenantPosition"].Value.ToString() .Equals(batchSetCovenantRegionData.Value.ToString(), StringComparison.OrdinalIgnoreCase)); })); if (currentDataGridViewRow == null) continue; try { var success = false; string result; // Retry to teleport to each region a few times. var teleportRetries = 3; do { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Attempting to teleport to " + batchSetCovenantRegionData.Key + @" " + @"(" + teleportRetries.ToString(Utils.EnUsCulture) + @")"; })); // Teleport to the region. result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "teleport"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"entity", "region"}, {"region", batchSetCovenantRegionData.Key}, {"position", batchSetCovenantRegionData.Value.ToString()}, {"fly", "True"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Error communicating with Corrade."; })); continue; } if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) { vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"No success status could be retrieved. "; })); continue; } switch (success) { case true: vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Teleport succeeded."; })); break; default: // In case the destination is to close (Corrade status code 37559), // then we are on the same region so no need to retry. uint status; //37559 switch ( uint.TryParse(wasInput(KeyValue.Get("status", result)), out status) && status.Equals(37559)) { case true: // We are on the region already! success = true; break; default: vassalForm.Invoke( (MethodInvoker) (() => { vassalForm.StatusText.Text = @"Teleport failed."; })); break; } break; } // Pause for teleport (10 teleports / 15s allowed). Thread.Sleep(700); } while (!success && !(--teleportRetries).Equals(0)); if (!success) throw new Exception("Failed to teleport to region."); result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "getregiondata"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"data", "IsEstateManager"} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not retrieve estate rights."); var data = CSV.ToEnumerable(wasInput(KeyValue.Get("data", result))).ToList(); if (!data.Count.Equals(2)) throw new Exception("Could not retrieve estate rights."); bool isEstateManager; switch ( bool.TryParse(data[data.IndexOf("IsEstateManager") + 1], out isEstateManager) && isEstateManager) { case true: // we are an estate manager result = Encoding.UTF8.GetString(HTTPClient.POST(vassalConfiguration.HTTPServerURL, KeyValue.Escape(new Dictionary { {"command", "setestatecovenant"}, {"group", vassalConfiguration.Group}, {"password", vassalConfiguration.Password}, {"item", vassalForm.SetCovenantNotecardUUIDBox.Text} }, wasOutput)).Result); if (string.IsNullOrEmpty(result)) throw new Exception("Error communicating with Corrade."); if (!bool.TryParse(wasInput(KeyValue.Get("success", result)), out success)) throw new Exception("No success status could be retrieved."); if (!success) throw new Exception("Could not set region covenant."); vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = @"Region covenant set."; currentDataGridViewRow.Selected = false; currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightGreen; foreach ( var cell in currentDataGridViewRow.Cells.AsParallel().Cast()) { cell.ToolTipText = @"Region covenant set."; } })); break; default: throw new Exception("No estate manager rights for setting covenant."); } } catch (Exception ex) { vassalForm.Invoke((MethodInvoker) (() => { vassalForm.StatusText.Text = ex.Message; currentDataGridViewRow.Selected = false; currentDataGridViewRow.DefaultCellStyle.BackColor = Color.LightPink; foreach ( var cell in currentDataGridViewRow.Cells.AsParallel().Cast()) { cell.ToolTipText = ex.Message; } })); } finally { // Pause for teleport (10 teleports / 15s allowed). Thread.Sleep(700); } } while (!batchSetCovenantQueue.Count.Equals(0)); } catch (Exception) { } finally { Monitor.Exit(ClientInstanceTeleportLock); // Allow teleports and enable button. vassalForm.BeginInvoke((MethodInvoker) (() => { vassalForm.BatchSetCovenantButton.Enabled = true; vassalForm.SetCovenantNotecardUUIDBox.Enabled = true; RegionTeleportGroup.Enabled = true; })); } }) {IsBackground = true}.Start(); } /// /// Linden constants. /// public struct LINDEN_CONSTANTS { public struct ALERTS { public const string NO_ROOM_TO_SIT_HERE = @"No room to sit here, try another spot."; public const string UNABLE_TO_SET_HOME = @"You can only set your 'Home Location' on your land or at a mainland Infohub."; public const string HOME_SET = @"Home position set."; } public struct ASSETS { public struct NOTECARD { public const string NEWLINE = "\n"; public const uint MAXIMUM_BODY_LENTH = 65536; } } public struct AVATARS { public const uint SET_DISPLAY_NAME_SUCCESS = 200; public const string LASTNAME_PLACEHOLDER = @"Resident"; public const uint MAXIMUM_DISPLAY_NAME_CHARACTERS = 31; public const uint MINIMUM_DISPLAY_NAME_CHARACTERS = 1; public const uint MAXIMUM_NUMBER_OF_ATTACHMENTS = 38; public struct PROFILE { public const uint SECOND_LIFE_TEXT_SIZE = 510; public const uint FIRST_LIFE_TEXT_SIZE = 253; } public struct PICKS { public const uint MAXIMUM_PICKS = 10; public const uint MAXIMUM_PICK_DESCRIPTION_SIZE = 1022; } public struct CLASSIFIEDS { public const uint MAXIMUM_CLASSIFIEDS = 100; } } public struct PRIMITIVES { public const uint MAXIMUM_NAME_SIZE = 63; public const uint MAXIMUM_DESCRIPTION_SIZE = 127; public const double MAXIMUM_REZ_HEIGHT = 4096.0; public const double MINIMUM_SIZE_X = 0.01; public const double MINIMUM_SIZE_Y = 0.01; public const double MINIMUM_SIZE_Z = 0.01; public const double MAXIMUM_SIZE_X = 64.0; public const double MAXIMUM_SIZE_Y = 64.0; public const double MAXIMUM_SIZE_Z = 64.0; } public struct OBJECTS { public const uint MAXIMUM_PRIMITIVE_COUNT = 256; } public struct DIRECTORY { public struct EVENT { public const uint SEARCH_RESULTS_COUNT = 200; } public struct GROUP { public const uint SEARCH_RESULTS_COUNT = 100; } public struct LAND { public const uint SEARCH_RESULTS_COUNT = 100; } public struct PEOPLE { public const uint SEARCH_RESULTS_COUNT = 100; } } public struct ESTATE { public const uint REGION_RESTART_DELAY = 120; public const uint MAXIMUM_BAN_LIST_LENGTH = 500; public const uint MAXIMUM_GROUP_LIST_LENGTH = 63; public const uint MAXIMUM_USER_LIST_LENGTH = 500; public const uint MAXIMUM_MANAGER_LIST_LENGTH = 10; public struct MESSAGES { public const string REGION_RESTART_MESSAGE = @"restart"; } } public struct PARCELS { public const double MAXIMUM_AUTO_RETURN_TIME = 999999; public const uint MINIMUM_AUTO_RETURN_TIME = 0; public const uint MAXIMUM_NAME_LENGTH = 63; public const uint MAXIMUM_DESCRIPTION_LENGTH = 255; } public struct GRID { public const string SECOND_LIFE = @"Second Life"; public const string TIME_ZONE = @"Pacific Standard Time"; } public struct CHAT { public const uint MAXIMUM_MESSAGE_LENGTH = 1024; } public struct GROUPS { public const uint MAXIMUM_NUMBER_OF_ROLES = 10; public const string EVERYONE_ROLE_NAME = @"Everyone"; public const uint MAXIMUM_GROUP_NAME_LENGTH = 35; public const uint MAXIMUM_GROUP_TITLE_LENGTH = 20; } public struct NOTICES { public const uint MAXIMUM_NOTICE_MESSAGE_LENGTH = 512; } public struct LSL { public const string CSV_DELIMITER = @", "; public const float SENSOR_RANGE = 96; public const string DATE_TIME_STAMP = @"yyy-MM-ddTHH:mm:ss.ffffffZ"; } public struct REGION { public const float TELEPORT_MINIMUM_DISTANCE = 1; public const float DEFAULT_AGENT_LIMIT = 40; public const float DEFAULT_OBJECT_BONUS = 1; public const bool DEFAULT_FIXED_SUN = false; public const float DEFAULT_TERRAIN_LOWER_LIMIT = -4; public const float DEFAULT_TERRAIN_RAISE_LIMIT = 4; public const bool DEFAULT_USE_ESTATE_SUN = true; public const float DEFAULT_WATER_HEIGHT = 20; public const float SUNRISE = 6; } public struct VIEWER { public const float MAXIMUM_DRAW_DISTANCE = 4096; } public struct TELEPORTS { public struct THROTTLE { public const uint MAX_TELEPORTS = 10; public const uint GRACE_SECONDS = 15; } } } /// /// Constants used by Corrade. /// public struct VASSAL_CONSTANTS { /// /// Copyright. /// 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"; /// /// Vassal version. /// public static readonly string VASSAL_VERSION = Assembly.GetEntryAssembly().GetName().Version.ToString(); /// /// Corrade user agent. /// public static readonly string USER_AGENT = $"{VASSAL}/{VASSAL_VERSION} ({WIZARDRY_AND_STEAMWORKS_WEBSITE})"; /// /// Vassal compile date. /// 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(); /// /// Vassal configuration file. /// public static readonly string VASSAL_CONFIGURATION_FILE = @"Vassal.ini"; /// /// Vassal regions file. /// public static readonly string VASSAL_REGIONS = @"Regions.csv"; /// /// Conten-types that Corrade can send and receive. /// public struct CONTENT_TYPE { public const string TEXT_PLAIN = @"text/plain"; public const string WWW_FORM_URLENCODED = @"application/x-www-form-urlencoded"; } } } }