corrade-vassal

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 6  →  ?path2? @ 7
/Vassal/Vassal/VassalForm.cs
@@ -10,9 +10,11 @@
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
@@ -29,15 +31,211 @@
public partial class Vassal : Form
{
public static System.Timers.Timer overviewTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
public static System.Timers.Timer residentListTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
public static System.Timers.Timer topScriptsTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
public static System.Timers.Timer topCollidersTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
public static System.Timers.Timer regionsStateTabTimer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
public static volatile int regionsStateCheckIndex = 0;
public static VassalConfiguration vassalConfiguration = new VassalConfiguration();
public static Vassal vassalForm;
public static readonly object ClientInstanceTeleportLock = new object();
public static readonly object RegionsStateCheckLock = new object();
 
/// <summary>
/// Linden constants.
/// </summary>
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;
}
}
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
private static double wasMapValueToRange(double value, double xMin, double xMax, double yMin, double yMax)
{
return yMin + (
(
yMax - yMin
)
*
(
value - xMin
)
/
(
xMax - xMin
)
);
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>RFC1738 URL Escapes a string</summary>
/// <param name="data">a string to escape</param>
/// <returns>an RFC1738 escaped string</returns>
@@ -980,7 +1178,7 @@
{
case true:
selectedRegionName = listViewItem.Text;
selectedRegionPosition = (Vector3)listViewItem.Tag;
selectedRegionPosition = (Vector3) listViewItem.Tag;
startTeleport = true;
break;
default:
@@ -990,10 +1188,10 @@
}));
 
if (!startTeleport) return;
 
 
// Announce teleport.
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.RegionTeleportGroup.Enabled = false;
vassalForm.StatusProgress.Value = 0;
@@ -1078,7 +1276,7 @@
finally
{
Monitor.Exit(ClientInstanceTeleportLock);
vassalForm.Invoke((MethodInvoker) (() =>
vassalForm.BeginInvoke((MethodInvoker) (() =>
{
vassalForm.StatusProgress.Value = 100;
vassalForm.RegionTeleportGroup.Enabled = true;
@@ -1097,6 +1295,8 @@
}));
break;
}
// Clear the resident list table.
ResidentListGridView.Rows.Clear();
// Clear the top scripts table.
TopScriptsGridView.Rows.Clear();
// Clear the top colliders table.
@@ -1122,8 +1322,8 @@
Objects.Enabled = false;
}));
}
}).Start();
 
})
{IsBackground = true}.Start();
}
 
private void SettingsRequested(object sender, EventArgs e)
@@ -1140,6 +1340,7 @@
// Disable estate manager tabs since we will enable these dynamically.
TopScriptsTab.Enabled = false;
TopCollidersTab.Enabled = false;
ResidentListBanGroup.Enabled = false;
 
// Get the configuration file settings if it exists.
if (File.Exists(VASSAL_CONSTANTS.VASSAL_CONFIGURATION_FILE))
@@ -1150,6 +1351,40 @@
RegionRestartDelayBox.Text = vassalConfiguration.RegionRestartDelay.ToString(Utils.EnUsCulture);
}
 
// Spawn a thread to check Corrade's connection status.
new Thread(() =>
{
TcpClient tcpClient = new TcpClient();
try
{
System.Uri uri = new System.Uri(vassalConfiguration.HTTPServerURL);
tcpClient.Connect(uri.Host, uri.Port);
// port open
vassalForm.BeginInvoke((MethodInvoker) (() => { Tabs.Enabled = true; }));
// set the loading spinner
Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
System.IO.Stream file =
thisAssembly.GetManifestResourceStream("Vassal.img.loading.gif");
switch (file != null)
{
case true:
vassalForm.BeginInvoke((MethodInvoker) (() =>
{
RegionAvatarsMap.SizeMode = PictureBoxSizeMode.CenterImage;
RegionAvatarsMap.Image = Image.FromStream(file);
RegionAvatarsMap.Refresh();
}));
break;
}
}
catch (Exception)
{
// port closed
vassalForm.BeginInvoke((MethodInvoker) (() => { Tabs.Enabled = false; }));
}
})
{IsBackground = true}.Start();
 
// Get all the regions if they exist.
if (File.Exists(VASSAL_CONSTANTS.VASSAL_REGIONS))
{
@@ -1174,24 +1409,13 @@
{
BatchRestartGridView.Rows.Add(data.Key, data.Value.ToString());
}
// Populate the regions state grid view.
foreach (KeyValuePair<string, Vector3> data in ConfiguredRegions)
{
RegionsStateGridView.Rows.Add(data.Key, "Check pening...");
}
}
 
// Set the map image to the loading spinner.
Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
System.IO.Stream file =
thisAssembly.GetManifestResourceStream("Vassal.img.loading.gif");
switch (file != null)
{
case true:
vassalForm.Invoke((MethodInvoker) (() =>
{
RegionAvatarsMap.SizeMode = PictureBoxSizeMode.CenterImage;
RegionAvatarsMap.Image = Image.FromStream(file);
RegionAvatarsMap.Refresh();
}));
break;
}
 
// Start the overview timer.
overviewTabTimer.Elapsed += (o, p) =>
{
@@ -1200,12 +1424,7 @@
 
try
{
// Do not do anything in case the tab is not selected.
vassalForm.Invoke((MethodInvoker) (() =>
{
if (!Tabs.SelectedTab.Equals(OverviewTab))
throw new Exception();
}));
// Always run the overview regardless which tab is active.
 
// Get the statistics.
string result = wasPOST(vassalConfiguration.HTTPServerURL,
@@ -1240,7 +1459,7 @@
if (data.Count.Equals(0))
throw new Exception();
 
vassalForm.Invoke((MethodInvoker) (() =>
vassalForm.BeginInvoke((MethodInvoker) (() =>
{
// Drop access to features if we are not estate managers.
bool isEstateManager;
@@ -1251,10 +1470,12 @@
case true: // we are an estate manager
TopScriptsTab.Enabled = true;
TopCollidersTab.Enabled = true;
ResidentListBanGroup.Enabled = true;
break;
default:
TopScriptsTab.Enabled = false;
TopCollidersTab.Enabled = false;
ResidentListBanGroup.Enabled = false;
break;
}
 
@@ -1355,7 +1576,7 @@
}
mapGraphics.DrawImage(mapImage, new Point(0, 0));
 
vassalForm.Invoke((MethodInvoker) (() =>
vassalForm.BeginInvoke((MethodInvoker) (() =>
{
RegionAvatarsMap.SizeMode = PictureBoxSizeMode.StretchImage;
RegionAvatarsMap.Image = mapImage;
@@ -1374,21 +1595,113 @@
};
overviewTabTimer.Start();
 
// Start the top scores timer.
topScriptsTabTimer.Elapsed += (o, p) =>
regionsStateTabTimer.Elapsed += (o, p) =>
{
if (!Monitor.TryEnter(ClientInstanceTeleportLock))
// Do not do anything in case the tab is not selected.
bool run = false;
vassalForm.Invoke((MethodInvoker) (() =>
{
run = Tabs.SelectedTab.Equals(RegionsStateTab);
}));
if (!run) return;
 
if (!Monitor.TryEnter(RegionsStateCheckLock))
return;
 
try
{
// Do not do anything in case the tab is not selected.
string regionName = string.Empty;
vassalForm.Invoke((MethodInvoker) (() =>
{
if (!Tabs.SelectedTab.Equals(TopScriptsTab))
throw new Exception();
regionName =
RegionsStateGridView.Rows[regionsStateCheckIndex].Cells["RegionsStateRegionName"].Value
.ToString();
}));
 
if (string.IsNullOrEmpty(regionName))
throw new Exception();
 
// Get the region status.
string result = wasPOST(vassalConfiguration.HTTPServerURL,
wasKeyValueEscape(new Dictionary<string, string>
{
{"command", "getgridregiondata"},
{"group", vassalConfiguration.Group},
{"password", vassalConfiguration.Password},
{"region", regionName},
{"data", "Access"}
}), vassalConfiguration.DataTimeout);
 
bool success;
if (string.IsNullOrEmpty(result) ||
!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
throw new Exception();
 
List<string> data = wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result))).ToList();
if (!data.Count.Equals(2))
throw new Exception();
 
string 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(Vassal.LINDEN_CONSTANTS.LSL.DATE_TIME_STAMP);
 
}));
}
catch (Exception)
{
 
}
finally
{
Monitor.Exit(RegionsStateCheckLock);
++regionsStateCheckIndex;
vassalForm.Invoke((MethodInvoker) (() =>
{
if (regionsStateCheckIndex >= RegionsStateGridView.Rows.Count)
{
regionsStateCheckIndex = 0;
}
}));
}
};
regionsStateTabTimer.Start();
 
// Start the top scores timer.
topScriptsTabTimer.Elapsed += (o, p) =>
{
// Do not do anything in case the tab is not selected.
bool run = false;
vassalForm.Invoke((MethodInvoker) (() =>
{
run = Tabs.SelectedTab.Equals(TopScriptsTab);
}));
if (!run) return;
 
if (!Monitor.TryEnter(ClientInstanceTeleportLock))
return;
 
try
{
// Get the statistics.
string result = wasPOST(vassalConfiguration.HTTPServerURL,
wasKeyValueEscape(new Dictionary<string, string>
@@ -1463,18 +1776,19 @@
// Start the top colliders timer.
topCollidersTabTimer.Elapsed += (o, p) =>
{
// Do not do anything in case the tab is not selected.
bool run = false;
vassalForm.Invoke((MethodInvoker) (() =>
{
run = Tabs.SelectedTab.Equals(TopCollidersTab);
}));
if (!run) return;
 
if (!Monitor.TryEnter(ClientInstanceTeleportLock))
return;
 
try
{
// Do not do anything in case the tab is not selected.
vassalForm.Invoke((MethodInvoker) (() =>
{
if (!Tabs.SelectedTab.Equals(TopCollidersTab))
throw new Exception();
}));
 
// Get the statistics.
string result = wasPOST(vassalConfiguration.HTTPServerURL,
wasKeyValueEscape(new Dictionary<string, string>
@@ -1528,7 +1842,8 @@
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],
TopCollidersGridView.Rows.Add(dataComponents[0], dataComponents[1],
dataComponents[2],
dataComponents[3], dataComponents[4]);
break;
}
@@ -1546,6 +1861,92 @@
}
};
topCollidersTabTimer.Start();
 
// Start the resident list timer.
residentListTimer.Elapsed += (o, p) =>
{
// Do not do anything in case the tab is not selected.
bool run = false;
vassalForm.Invoke((MethodInvoker) (() =>
{
run = Tabs.SelectedTab.Equals(ResidentListTab);
}));
if (!run) return;
 
if (!Monitor.TryEnter(ClientInstanceTeleportLock))
return;
 
try
{
// Get the avatar positions.
string result = wasPOST(vassalConfiguration.HTTPServerURL,
wasKeyValueEscape(new Dictionary<string, string>
{
{"command", "getavatarpositions"},
{"group", vassalConfiguration.Group},
{"password", vassalConfiguration.Password},
{"entity", "region"}
}), vassalConfiguration.DataTimeout);
 
bool success;
if (string.IsNullOrEmpty(result) ||
!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
throw new Exception();
 
HashSet<List<string>> data =
new HashSet<List<string>>(wasCSVToEnumerable(wasInput(wasKeyValueGet("data", result)))
.Select((x, i) => new {Index = i, Value = x})
.GroupBy(x => x.Index/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 (
int index in
ResidentListGridView.Rows.AsParallel().Cast<DataGridViewRow>()
.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 (List<string> dataComponents in data.Where(q => q.Count.Equals(3)))
{
DataGridViewRow row =
ResidentListGridView.Rows.AsParallel()
.Cast<DataGridViewRow>()
.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)
{
 
}
finally
{
Monitor.Exit(ClientInstanceTeleportLock);
}
};
residentListTimer.Start();
}
 
private void RequestedEditRegions(object sender, EventArgs e)
@@ -1602,7 +2003,7 @@
}
}));
})
{IsBackground = true, Priority = ThreadPriority.Normal}.Start();
{IsBackground = true}.Start();
break;
}
}));
@@ -1649,7 +2050,7 @@
}
}));
})
{IsBackground = true, Priority = ThreadPriority.Normal}.Start();
{IsBackground = true}.Start();
break;
}
}));
@@ -1685,7 +2086,7 @@
 
private void RequestFilterTopColliders(object sender, EventArgs e)
{
vassalForm.BeginInvoke((MethodInvoker)(() =>
vassalForm.BeginInvoke((MethodInvoker) (() =>
{
Regex topCollidersRowRegex;
switch (!string.IsNullOrEmpty(TopScriptsFilter.Text))
@@ -1697,7 +2098,8 @@
topCollidersRowRegex = new Regex(@".+?", RegexOptions.Compiled);
break;
}
foreach (DataGridViewRow topCollidersRow in TopCollidersGridView.Rows.AsParallel().Cast<DataGridViewRow>())
foreach (
DataGridViewRow topCollidersRow in TopCollidersGridView.Rows.AsParallel().Cast<DataGridViewRow>())
{
topCollidersRow.Visible =
topCollidersRowRegex.IsMatch(topCollidersRow.Cells["TopCollidersScore"].Value.ToString()) ||
@@ -1742,7 +2144,7 @@
// If no rows were selected, enable teleports, the return button and return.
if (returnUUIDs.Count.Equals(0))
{
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.ReturnTopScriptsButton.Enabled = true;
RegionTeleportGroup.Enabled = true;
@@ -1776,7 +2178,7 @@
{
currentRegionName = CurrentRegionName.Text;
}));
 
// Teleport to the object.
string result = wasPOST(vassalConfiguration.HTTPServerURL,
wasKeyValueEscape(new Dictionary<string, string>
@@ -1791,7 +2193,7 @@
 
if (string.IsNullOrEmpty(result))
{
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusText.Text = @"Error communicating with Corrade.";
}));
@@ -1806,7 +2208,7 @@
{"group", vassalConfiguration.Group},
{"password", vassalConfiguration.Password},
{"item", objectData.Key.ToString()},
{"range", "32" }, // maximal prim size = 64 - middle bounding box at half
{"range", "32"}, // maximal prim size = 64 - middle bounding box at half
{"type", "ReturnToOwner"}
}), vassalConfiguration.DataTimeout);
 
@@ -1836,11 +2238,11 @@
{
vassalForm.StatusText.Text = @"Returned object: " + objectData.Key.ToString();
// Remove the row from the grid view.
DataGridViewRow row =
TopScriptsGridView.Rows.AsParallel()
.Cast<DataGridViewRow>()
.FirstOrDefault(
o => o.Cells["TopScriptsUUID"].Value.Equals(objectData.Key.ToString()));
DataGridViewRow row =
TopScriptsGridView.Rows.AsParallel()
.Cast<DataGridViewRow>()
.FirstOrDefault(
o => o.Cells["TopScriptsUUID"].Value.Equals(objectData.Key.ToString()));
if (row == null) return;
int i = row.Index;
TopScriptsGridView.Rows.RemoveAt(i);
@@ -1870,7 +2272,7 @@
}
catch (Exception ex)
{
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusText.Text = @"Unexpected error: " + ex.Message;
}));
@@ -1879,7 +2281,7 @@
{
Monitor.Exit(ClientInstanceTeleportLock);
// Allow teleports and enable button.
vassalForm.BeginInvoke((MethodInvoker)(() =>
vassalForm.BeginInvoke((MethodInvoker) (() =>
{
vassalForm.ReturnTopScriptsButton.Enabled = true;
RegionTeleportGroup.Enabled = true;
@@ -1892,7 +2294,7 @@
private void RequestReturnTopCollidersObjects(object sender, EventArgs e)
{
// Block teleports and disable button.
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.ReturnTopCollidersButton.Enabled = false;
RegionTeleportGroup.Enabled = false;
@@ -1899,8 +2301,8 @@
}));
 
// Enqueue all the UUIDs to return.
Queue<KeyValuePair<UUID, Vector3>> returnUUIDs = new Queue<KeyValuePair<UUID, Vector3>>();
vassalForm.Invoke((MethodInvoker)(() =>
Queue<KeyValuePair<UUID, Vector3>> returnObjectUUIDQueue = new Queue<KeyValuePair<UUID, Vector3>>();
vassalForm.Invoke((MethodInvoker) (() =>
{
foreach (
DataGridViewRow topCollidersRow in
@@ -1911,16 +2313,17 @@
Vector3 objectPosition;
UUID returnUUID;
if (!UUID.TryParse(topCollidersRow.Cells["TopCollidersUUID"].Value.ToString(), out returnUUID) ||
!Vector3.TryParse(topCollidersRow.Cells["TopCollidersPosition"].Value.ToString(), out objectPosition))
!Vector3.TryParse(topCollidersRow.Cells["TopCollidersPosition"].Value.ToString(),
out objectPosition))
continue;
returnUUIDs.Enqueue(new KeyValuePair<UUID, Vector3>(returnUUID, objectPosition));
returnObjectUUIDQueue.Enqueue(new KeyValuePair<UUID, Vector3>(returnUUID, objectPosition));
}
}));
 
// If no rows were selected, enable teleports, the return button and return.
if (returnUUIDs.Count.Equals(0))
if (returnObjectUUIDQueue.Count.Equals(0))
{
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.ReturnTopCollidersButton.Enabled = true;
RegionTeleportGroup.Enabled = true;
@@ -1934,23 +2337,23 @@
 
try
{
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusProgress.Value = 0;
}));
int totalObjects = returnUUIDs.Count;
int totalObjects = returnObjectUUIDQueue.Count;
do
{
// Dequeue the first object.
KeyValuePair<UUID, Vector3> objectData = returnUUIDs.Dequeue();
KeyValuePair<UUID, Vector3> objectData = returnObjectUUIDQueue.Dequeue();
 
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusText.Text = @"Returning UUID: " + objectData.Key.ToString();
}));
 
string currentRegionName = string.Empty;
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
currentRegionName = CurrentRegionName.Text;
}));
@@ -1969,7 +2372,7 @@
 
if (string.IsNullOrEmpty(result))
{
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusText.Text = @"Error communicating with Corrade.";
}));
@@ -1984,13 +2387,13 @@
{"group", vassalConfiguration.Group},
{"password", vassalConfiguration.Password},
{"item", objectData.Key.ToString()},
{"range", "32" }, // maximal prim size = 64 - middle bounding box at half
{"range", "32"}, // maximal prim size = 64 - middle bounding box at half
{"type", "ReturnToOwner"}
}), vassalConfiguration.DataTimeout);
 
if (string.IsNullOrEmpty(result))
{
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusText.Text = @"Error communicating with Corrade.";
}));
@@ -2000,7 +2403,7 @@
bool success;
if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
{
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusText.Text = @"No success status could be retrieved. ";
}));
@@ -2010,15 +2413,15 @@
switch (success)
{
case true:
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusText.Text = @"Returned object: " + objectData.Key.ToString();
// Remove the row from the grid view.
DataGridViewRow row =
TopCollidersGridView.Rows.AsParallel()
.Cast<DataGridViewRow>()
.FirstOrDefault(
o => o.Cells["TopCollidersUUID"].Value.Equals(objectData.Key.ToString()));
TopCollidersGridView.Rows.AsParallel()
.Cast<DataGridViewRow>()
.FirstOrDefault(
o => o.Cells["TopCollidersUUID"].Value.Equals(objectData.Key.ToString()));
if (row == null) return;
int i = row.Index;
TopCollidersGridView.Rows.RemoveAt(i);
@@ -2025,7 +2428,7 @@
}));
break;
case false:
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusText.Text = @"Could not return object " + objectData.Key.ToString() +
@": " +
@@ -2034,14 +2437,14 @@
break;
}
 
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusProgress.Value =
Math.Min((int)(Math.Abs(returnUUIDs.Count - totalObjects) /
(double)totalObjects), 100);
Math.Min((int) (Math.Abs(returnObjectUUIDQueue.Count - totalObjects)/
(double) totalObjects), 100);
}));
} while (!returnUUIDs.Count.Equals(0));
vassalForm.Invoke((MethodInvoker)(() =>
} while (!returnObjectUUIDQueue.Count.Equals(0));
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusProgress.Value = 100;
}));
@@ -2048,7 +2451,7 @@
}
catch (Exception ex)
{
vassalForm.Invoke((MethodInvoker)(() =>
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusText.Text = @"Unexpected error: " + ex.Message;
}));
@@ -2057,7 +2460,7 @@
{
Monitor.Exit(ClientInstanceTeleportLock);
// Allow teleports and enable button.
vassalForm.BeginInvoke((MethodInvoker)(() =>
vassalForm.BeginInvoke((MethodInvoker) (() =>
{
vassalForm.ReturnTopScriptsButton.Enabled = true;
RegionTeleportGroup.Enabled = true;
@@ -2064,12 +2467,539 @@
}));
}
})
{ IsBackground = true }.Start();
{IsBackground = true}.Start();
}
 
private void RequestBatchRestart(object sender, EventArgs e)
{
// Block teleports and disable button.
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.BatchRestartButton.Enabled = false;
RegionTeleportGroup.Enabled = false;
}));
 
// Enqueue all the regions to restart.
Queue<KeyValuePair<string, Vector3>> restartRegionQueue = new Queue<KeyValuePair<string, Vector3>>();
vassalForm.Invoke((MethodInvoker) (() =>
{
foreach (
DataGridViewRow topCollidersRow in
BatchRestartGridView.Rows.AsParallel()
.Cast<DataGridViewRow>()
.Where(o => o.Selected || o.Cells.Cast<DataGridViewCell>().Any(p => p.Selected)))
{
Vector3 objectPosition;
string regionName = topCollidersRow.Cells["BatchRestartRegionName"].Value.ToString();
if (string.IsNullOrEmpty(regionName) ||
!Vector3.TryParse(topCollidersRow.Cells["BatchRestartPosition"].Value.ToString(),
out objectPosition))
continue;
restartRegionQueue.Enqueue(new KeyValuePair<string, Vector3>(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;
RegionTeleportGroup.Enabled = true;
}));
return;
}
 
new Thread(() =>
{
Monitor.Enter(ClientInstanceTeleportLock);
 
try
{
do
{
// Dequeue the first object.
KeyValuePair<string, Vector3> restartRegionData = restartRegionQueue.Dequeue();
DataGridViewRow currentDataGridViewRow = null;
vassalForm.Invoke((MethodInvoker) (() =>
{
currentDataGridViewRow = vassalForm.BatchRestartGridView.Rows.AsParallel()
.Cast<DataGridViewRow>()
.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
{
bool success = false;
string result;
 
// Retry to teleport to each region a few times.
int teleportRetries = 3;
do
{
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusText.Text = @"Attempting to teleport to " + restartRegionData.Key +
@" " + @"(" +
teleportRetries.ToString(Utils.EnUsCulture) +
@")";
}));
 
// Teleport to the region.
result = wasPOST(vassalConfiguration.HTTPServerURL,
wasKeyValueEscape(new Dictionary<string, string>
{
{"command", "teleport"},
{"group", vassalConfiguration.Group},
{"password", vassalConfiguration.Password},
{"position", restartRegionData.Value.ToString()},
{"region", restartRegionData.Key},
{"fly", "True"}
}), vassalConfiguration.DataTimeout);
 
if (string.IsNullOrEmpty(result))
{
vassalForm.Invoke((MethodInvoker) (() =>
{
vassalForm.StatusText.Text = @"Error communicating with Corrade.";
}));
continue;
}
if (!bool.TryParse(wasInput(wasKeyValueGet("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.";
}));
Thread.Sleep(TimeSpan.FromSeconds(1).Milliseconds);
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(wasKeyValueGet("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.";
}));
Thread.Sleep(10000);
break;
}
break;
}
} while (!success && !(--teleportRetries).Equals(0));
 
if (!success)
throw new Exception("Failed to teleport to region.");
 
result = wasPOST(vassalConfiguration.HTTPServerURL,
wasKeyValueEscape(new Dictionary<string, string>
{
{"command", "getregiondata"},
{"group", vassalConfiguration.Group},
{"password", vassalConfiguration.Password},
{"data", "IsEstateManager"}
}), vassalConfiguration.DataTimeout);
 
if (string.IsNullOrEmpty(result))
throw new Exception("Error communicating with Corrade.");
 
if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
throw new Exception("No success status could be retrieved.");
 
if (!success)
throw new Exception("Could not retrieve estate rights.");
 
List<string> data = wasCSVToEnumerable(wasInput(wasKeyValueGet("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 = wasPOST(vassalConfiguration.HTTPServerURL,
wasKeyValueEscape(new Dictionary<string, string>
{
{"command", "restartregion"},
{"group", vassalConfiguration.Group},
{"password", vassalConfiguration.Password},
{"action", "restart"},
{
"delay", vassalConfiguration.RegionRestartDelay.ToString(Utils.EnUsCulture)
}
}), vassalConfiguration.DataTimeout);
 
if (string.IsNullOrEmpty(result))
throw new Exception("Error communicating with Corrade.");
 
if (!bool.TryParse(wasInput(wasKeyValueGet("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 (
DataGridViewCell cell in
currentDataGridViewRow.Cells.AsParallel().Cast<DataGridViewCell>())
{
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 (
DataGridViewCell cell in
currentDataGridViewRow.Cells.AsParallel().Cast<DataGridViewCell>())
{
cell.ToolTipText = ex.Message;
}
}));
}
} while (!restartRegionQueue.Count.Equals(0));
}
catch (Exception)
{
 
}
finally
{
Monitor.Exit(ClientInstanceTeleportLock);
// Allow teleports and enable button.
vassalForm.BeginInvoke((MethodInvoker) (() =>
{
vassalForm.BatchRestartButton.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 (
DataGridViewRow residentListRow in ResidentListGridView.Rows.AsParallel().Cast<DataGridViewRow>())
{
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) (() =>
{
ResidentListBanGroup.Enabled = false;
RegionTeleportGroup.Enabled = false;
}));
 
// Enqueue all the regions to restart.
Queue<UUID> agentsQueue = new Queue<UUID>();
vassalForm.Invoke((MethodInvoker) (() =>
{
foreach (
DataGridViewRow residentListRow in
ResidentListGridView.Rows.AsParallel()
.Cast<DataGridViewRow>()
.Where(o => o.Selected || o.Cells.Cast<DataGridViewCell>().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.
UUID agentUUID = agentsQueue.Dequeue();
DataGridViewRow currentDataGridViewRow = null;
vassalForm.Invoke((MethodInvoker) (() =>
{
currentDataGridViewRow = vassalForm.ResidentListGridView.Rows.AsParallel()
.Cast<DataGridViewRow>()
.FirstOrDefault(
o =>
o.Cells["ResidentListUUID"].Value.ToString()
.Equals(agentUUID.ToString(), StringComparison.OrdinalIgnoreCase));
}));
 
if (currentDataGridViewRow == null) continue;
 
try
{
bool alsoBan = false;
vassalForm.Invoke((MethodInvoker) (() =>
{
alsoBan = vassalForm.ResidentBanAllEstatesBox.Checked;
}));
 
// Teleport to the region.
string result = wasPOST(vassalConfiguration.HTTPServerURL,
wasKeyValueEscape(new Dictionary<string, string>
{
{"command", "setestatelist"},
{"group", vassalConfiguration.Group},
{"password", vassalConfiguration.Password},
{"type", "ban"},
{"action", "add"},
{"agent", agentUUID.ToString()},
{"all", alsoBan.ToString()}
}), vassalConfiguration.DataTimeout);
 
if (string.IsNullOrEmpty(result))
throw new Exception("Error communicating with Corrade.");
 
bool success;
if (!bool.TryParse(wasInput(wasKeyValueGet("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 (
DataGridViewCell cell in
currentDataGridViewRow.Cells.AsParallel().Cast<DataGridViewCell>())
{
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 (
DataGridViewCell cell in
currentDataGridViewRow.Cells.AsParallel().Cast<DataGridViewCell>())
{
cell.ToolTipText = ex.Message;
}
}));
}
 
} while (agentsQueue.Count.Equals(0));
}
catch (Exception)
{
 
}
finally
{
Monitor.Exit(ClientInstanceTeleportLock);
// Allow teleports and enable button.
vassalForm.BeginInvoke((MethodInvoker) (() =>
{
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 statistics.
string result = wasPOST(vassalConfiguration.HTTPServerURL,
wasKeyValueEscape(new Dictionary<string, string>
{
{"command", "getterrainheight"},
{"group", vassalConfiguration.Group},
{"password", vassalConfiguration.Password},
{"entity", "region"}
}), vassalConfiguration.DataTimeout);
 
if (string.IsNullOrEmpty(result))
throw new Exception("Error communicating with Corrade.");
 
bool success;
if (!bool.TryParse(wasInput(wasKeyValueGet("success", result)), out success))
throw new Exception("No success status could be retrieved.");
 
if (!success)
throw new Exception("Could not get terrain heights.");
 
List<double> heights = new List<double>();
foreach (string map in wasCSVToEnumerable(wasInput(wasKeyValueGet("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.");
 
double maxHeight = heights.Max();
using (Bitmap bitmap = new Bitmap(256, 256))
{
foreach (int x in Enumerable.Range(1, 255))
{
foreach (int y in Enumerable.Range(1, 255))
{
bitmap.SetPixel(x, 256 - y,
Color.FromArgb((int) wasMapValueToRange(heights[256*x + y], 0, maxHeight, 0, 255), 0, 0));
}
}
Bitmap closureBitmap = (Bitmap)bitmap.Clone();
vassalForm.BeginInvoke((MethodInvoker) (() =>
{
switch (vassalForm.SaveTerrainFileDialog.ShowDialog())
{
case DialogResult.OK:
string file = vassalForm.SaveTerrainFileDialog.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, Priority = ThreadPriority.Normal}.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();
}
}
}