Index: binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerList.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetPlayerList.cs	(revision 399)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerList.cs	(revision 402)
@@ -1,264 +1,263 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text.RegularExpressions;
-using AllocsFixes.JSON;
-using AllocsFixes.PersistentData;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetPlayerList : AbsWebAPI {
-		private static readonly Regex numberFilterMatcher =
-			new Regex (@"^(>=|=>|>|<=|=<|<|==|=)?\s*([0-9]+(\.[0-9]*)?)$");
-
-		private static readonly UnityEngine.Profiling.CustomSampler jsonSerializeSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Build");
-
-		public override void HandleRequest (RequestContext _context) {
-			AdminTools admTools = GameManager.Instance.adminTools;
-			PlatformUserIdentifierAbs userId = _context.Connection?.UserId;
-
-			bool bViewAll = WebConnection.CanViewAllPlayers (_context.PermissionLevel);
-
-			// TODO: Sort (and filter?) prior to converting to JSON ... hard as how to get the correct column's data? (i.e. column name matches JSON object field names, not source data)
-
-			int rowsPerPage = 25;
-			if (_context.Request.QueryString ["rowsperpage"] != null) {
-				int.TryParse (_context.Request.QueryString ["rowsperpage"], out rowsPerPage);
-			}
-
-			int page = 0;
-			if (_context.Request.QueryString ["page"] != null) {
-				int.TryParse (_context.Request.QueryString ["page"], out page);
-			}
-
-			int firstEntry = page * rowsPerPage;
-
-			Players playersList = PersistentContainer.Instance.Players;
-
-			
-			List<JsonObject> playerList = new List<JsonObject> ();
-
-			jsonSerializeSampler.Begin ();
-
-			foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in playersList.Dict) {
-				Player p = kvp.Value;
-
-				if (bViewAll || p.PlatformId.Equals (userId)) {
-					JsonObject pos = new JsonObject ();
-					pos.Add ("x", new JsonNumber (p.LastPosition.x));
-					pos.Add ("y", new JsonNumber (p.LastPosition.y));
-					pos.Add ("z", new JsonNumber (p.LastPosition.z));
-
-					JsonObject pJson = new JsonObject ();
-					pJson.Add ("steamid", new JsonString (kvp.Key.CombinedString));
-					pJson.Add ("entityid", new JsonNumber (p.EntityID));
-					pJson.Add ("ip", new JsonString (p.IP));
-					pJson.Add ("name", new JsonString (p.Name));
-					pJson.Add ("online", new JsonBoolean (p.IsOnline));
-					pJson.Add ("position", pos);
-
-					pJson.Add ("totalplaytime", new JsonNumber (p.TotalPlayTime));
-					pJson.Add ("lastonline",
-						new JsonString (p.LastOnline.ToUniversalTime ().ToString ("yyyy-MM-ddTHH:mm:ssZ")));
-					pJson.Add ("ping", new JsonNumber (p.IsOnline ? p.ClientInfo.ping : -1));
-
-					JsonBoolean banned = admTools != null ? new JsonBoolean (admTools.IsBanned (kvp.Key, out _, out _)) : new JsonBoolean (false);
-
-					pJson.Add ("banned", banned);
-
-					playerList.Add (pJson);
-				}
-			}
-
-			jsonSerializeSampler.End ();
-
-			IEnumerable<JsonObject> list = playerList;
-
-			foreach (string key in _context.Request.QueryString.AllKeys) {
-				if (!string.IsNullOrEmpty (key) && key.StartsWith ("filter[")) {
-					string filterCol = key.Substring (key.IndexOf ('[') + 1);
-					filterCol = filterCol.Substring (0, filterCol.Length - 1);
-					string filterVal = _context.Request.QueryString.Get (key).Trim ();
-
-					list = ExecuteFilter (list, filterCol, filterVal);
-				}
-			}
-
-			int totalAfterFilter = list.Count ();
-
-			foreach (string key in _context.Request.QueryString.AllKeys) {
-				if (!string.IsNullOrEmpty (key) && key.StartsWith ("sort[")) {
-					string sortCol = key.Substring (key.IndexOf ('[') + 1);
-					sortCol = sortCol.Substring (0, sortCol.Length - 1);
-					string sortVal = _context.Request.QueryString.Get (key);
-
-					list = ExecuteSort (list, sortCol, sortVal == "0");
-				}
-			}
-
-			list = list.Skip (firstEntry);
-			list = list.Take (rowsPerPage);
-
-
-			JsonArray playersJsResult = new JsonArray ();
-			foreach (JsonObject jsO in list) {
-				playersJsResult.Add (jsO);
-			}
-
-			JsonObject result = new JsonObject ();
-			result.Add ("total", new JsonNumber (totalAfterFilter));
-			result.Add ("totalUnfiltered", new JsonNumber (playerList.Count));
-			result.Add ("firstResult", new JsonNumber (firstEntry));
-			result.Add ("players", playersJsResult);
-
-			WebUtils.WriteJson (_context.Response, result);
-		}
-
-		private IEnumerable<JsonObject> ExecuteFilter (IEnumerable<JsonObject> _list, string _filterCol,
-			string _filterVal) {
-			if (!_list.Any()) {
-				return _list;
-			}
-
-			if (_list.First ().ContainsKey (_filterCol)) {
-				Type colType = _list.First () [_filterCol].GetType ();
-				if (colType == typeof (JsonNumber)) {
-					return ExecuteNumberFilter (_list, _filterCol, _filterVal);
-				}
-
-				if (colType == typeof (JsonBoolean)) {
-					bool value = StringParsers.ParseBool (_filterVal);
-					return _list.Where (_line => ((JsonBoolean) _line [_filterCol]).GetBool () == value);
-				}
-
-				if (colType == typeof (JsonString)) {
-					// regex-match whole ^string$, replace * by .*, ? by .?, + by .+
-					_filterVal = _filterVal.Replace ("*", ".*").Replace ("?", ".?").Replace ("+", ".+");
-					_filterVal = "^" + _filterVal + "$";
-
-					//Log.Out ("GetPlayerList: Filter on String with Regex '" + _filterVal + "'");
-					Regex matcher = new Regex (_filterVal, RegexOptions.IgnoreCase);
-					return _list.Where (_line => matcher.IsMatch (((JsonString) _line [_filterCol]).GetString ()));
-				}
-			}
-
-			return _list;
-		}
-
-
-		private IEnumerable<JsonObject> ExecuteNumberFilter (IEnumerable<JsonObject> _list, string _filterCol,
-			string _filterVal) {
-			// allow value (exact match), =, ==, >=, >, <=, <
-			Match filterMatch = numberFilterMatcher.Match (_filterVal);
-			if (filterMatch.Success) {
-				double value = StringParsers.ParseDouble (filterMatch.Groups [2].Value);
-				NumberMatchType matchType;
-				double epsilon = value / 100000;
-				switch (filterMatch.Groups [1].Value) {
-					case "":
-					case "=":
-					case "==":
-						matchType = NumberMatchType.Equal;
-						break;
-					case ">":
-						matchType = NumberMatchType.Greater;
-						break;
-					case ">=":
-					case "=>":
-						matchType = NumberMatchType.GreaterEqual;
-						break;
-					case "<":
-						matchType = NumberMatchType.Lesser;
-						break;
-					case "<=":
-					case "=<":
-						matchType = NumberMatchType.LesserEqual;
-						break;
-					default:
-						matchType = NumberMatchType.Equal;
-						break;
-				}
-
-				return _list.Where (delegate (JsonObject _line) {
-					double objVal = ((JsonNumber) _line [_filterCol]).GetDouble ();
-					switch (matchType) {
-						case NumberMatchType.Greater:
-							return objVal > value;
-						case NumberMatchType.GreaterEqual:
-							return objVal >= value;
-						case NumberMatchType.Lesser:
-							return objVal < value;
-						case NumberMatchType.LesserEqual:
-							return objVal <= value;
-						case NumberMatchType.Equal:
-						default:
-							return NearlyEqual (objVal, value, epsilon);
-					}
-				});
-			}
-
-			Log.Out ("[Web] GetPlayerList: ignoring invalid filter for number-column '{0}': '{1}'", _filterCol, _filterVal);
-			return _list;
-		}
-
-
-		private IEnumerable<JsonObject> ExecuteSort (IEnumerable<JsonObject> _list, string _sortCol, bool _ascending) {
-			if (_list.Count () == 0) {
-				return _list;
-			}
-
-			if (_list.First ().ContainsKey (_sortCol)) {
-				Type colType = _list.First () [_sortCol].GetType ();
-				if (colType == typeof (JsonNumber)) {
-					if (_ascending) {
-						return _list.OrderBy (_line => ((JsonNumber) _line [_sortCol]).GetDouble ());
-					}
-
-					return _list.OrderByDescending (_line => ((JsonNumber) _line [_sortCol]).GetDouble ());
-				}
-
-				if (colType == typeof (JsonBoolean)) {
-					if (_ascending) {
-						return _list.OrderBy (_line => ((JsonBoolean) _line [_sortCol]).GetBool ());
-					}
-
-					return _list.OrderByDescending (_line => ((JsonBoolean) _line [_sortCol]).GetBool ());
-				}
-
-				if (_ascending) {
-					return _list.OrderBy (_line => _line [_sortCol].ToString ());
-				}
-
-				return _list.OrderByDescending (_line => _line [_sortCol].ToString ());
-			}
-
-			return _list;
-		}
-
-
-		private bool NearlyEqual (double _a, double _b, double _epsilon) {
-			double absA = Math.Abs (_a);
-			double absB = Math.Abs (_b);
-			double diff = Math.Abs (_a - _b);
-
-			if (_a == _b) {
-				return true;
-			}
-
-			if (_a == 0 || _b == 0 || diff < double.Epsilon) {
-				return diff < _epsilon;
-			}
-
-			return diff / (absA + absB) < _epsilon;
-		}
-
-		private enum NumberMatchType {
-			Equal,
-			Greater,
-			GreaterEqual,
-			Lesser,
-			LesserEqual
-		}
-	}
-}
+// using System;
+// using System.Collections.Generic;
+// using System.Linq;
+// using System.Text.RegularExpressions;
+// using AllocsFixes.PersistentData;
+// using JetBrains.Annotations;
+//
+// namespace Webserver.WebAPI.APIs {
+// 	[UsedImplicitly]
+// 	public class GetPlayerList : AbsWebAPI {
+// 		private static readonly Regex numberFilterMatcher =
+// 			new Regex (@"^(>=|=>|>|<=|=<|<|==|=)?\s*([0-9]+(\.[0-9]*)?)$");
+//
+// 		private static readonly UnityEngine.Profiling.CustomSampler jsonSerializeSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Build");
+//
+// 		public override void HandleRequest (RequestContext _context) {
+// 			AdminTools admTools = GameManager.Instance.adminTools;
+// 			PlatformUserIdentifierAbs userId = _context.Connection?.UserId;
+//
+// 			bool bViewAll = WebConnection.CanViewAllPlayers (_context.PermissionLevel);
+//
+// 			// TODO: Sort (and filter?) prior to converting to JSON ... hard as how to get the correct column's data? (i.e. column name matches JSON object field names, not source data)
+//
+// 			int rowsPerPage = 25;
+// 			if (_context.Request.QueryString ["rowsperpage"] != null) {
+// 				int.TryParse (_context.Request.QueryString ["rowsperpage"], out rowsPerPage);
+// 			}
+//
+// 			int page = 0;
+// 			if (_context.Request.QueryString ["page"] != null) {
+// 				int.TryParse (_context.Request.QueryString ["page"], out page);
+// 			}
+//
+// 			int firstEntry = page * rowsPerPage;
+//
+// 			Players playersList = PersistentContainer.Instance.Players;
+//
+// 			
+// 			List<JsonObject> playerList = new List<JsonObject> ();
+//
+// 			jsonSerializeSampler.Begin ();
+//
+// 			foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in playersList.Dict) {
+// 				Player p = kvp.Value;
+//
+// 				if (bViewAll || p.PlatformId.Equals (userId)) {
+// 					JsonObject pos = new JsonObject ();
+// 					pos.Add ("x", new JsonNumber (p.LastPosition.x));
+// 					pos.Add ("y", new JsonNumber (p.LastPosition.y));
+// 					pos.Add ("z", new JsonNumber (p.LastPosition.z));
+//
+// 					JsonObject pJson = new JsonObject ();
+// 					pJson.Add ("steamid", new JsonString (kvp.Key.CombinedString));
+// 					pJson.Add ("entityid", new JsonNumber (p.EntityID));
+// 					pJson.Add ("ip", new JsonString (p.IP));
+// 					pJson.Add ("name", new JsonString (p.Name));
+// 					pJson.Add ("online", new JsonBoolean (p.IsOnline));
+// 					pJson.Add ("position", pos);
+//
+// 					pJson.Add ("totalplaytime", new JsonNumber (p.TotalPlayTime));
+// 					pJson.Add ("lastonline",
+// 						new JsonString (p.LastOnline.ToUniversalTime ().ToString ("yyyy-MM-ddTHH:mm:ssZ")));
+// 					pJson.Add ("ping", new JsonNumber (p.IsOnline ? p.ClientInfo.ping : -1));
+//
+// 					JsonBoolean banned = admTools != null ? new JsonBoolean (admTools.IsBanned (kvp.Key, out _, out _)) : new JsonBoolean (false);
+//
+// 					pJson.Add ("banned", banned);
+//
+// 					playerList.Add (pJson);
+// 				}
+// 			}
+//
+// 			jsonSerializeSampler.End ();
+//
+// 			IEnumerable<JsonObject> list = playerList;
+//
+// 			foreach (string key in _context.Request.QueryString.AllKeys) {
+// 				if (!string.IsNullOrEmpty (key) && key.StartsWith ("filter[")) {
+// 					string filterCol = key.Substring (key.IndexOf ('[') + 1);
+// 					filterCol = filterCol.Substring (0, filterCol.Length - 1);
+// 					string filterVal = _context.Request.QueryString.Get (key).Trim ();
+//
+// 					list = ExecuteFilter (list, filterCol, filterVal);
+// 				}
+// 			}
+//
+// 			int totalAfterFilter = list.Count ();
+//
+// 			foreach (string key in _context.Request.QueryString.AllKeys) {
+// 				if (!string.IsNullOrEmpty (key) && key.StartsWith ("sort[")) {
+// 					string sortCol = key.Substring (key.IndexOf ('[') + 1);
+// 					sortCol = sortCol.Substring (0, sortCol.Length - 1);
+// 					string sortVal = _context.Request.QueryString.Get (key);
+//
+// 					list = ExecuteSort (list, sortCol, sortVal == "0");
+// 				}
+// 			}
+//
+// 			list = list.Skip (firstEntry);
+// 			list = list.Take (rowsPerPage);
+//
+//
+// 			JsonArray playersJsResult = new JsonArray ();
+// 			foreach (JsonObject jsO in list) {
+// 				playersJsResult.Add (jsO);
+// 			}
+//
+// 			JsonObject result = new JsonObject ();
+// 			result.Add ("total", new JsonNumber (totalAfterFilter));
+// 			result.Add ("totalUnfiltered", new JsonNumber (playerList.Count));
+// 			result.Add ("firstResult", new JsonNumber (firstEntry));
+// 			result.Add ("players", playersJsResult);
+//
+// 			WebUtils.WriteJson (_context.Response, result);
+// 		}
+//
+// 		private IEnumerable<JsonObject> ExecuteFilter (IEnumerable<JsonObject> _list, string _filterCol,
+// 			string _filterVal) {
+// 			if (!_list.Any()) {
+// 				return _list;
+// 			}
+//
+// 			if (_list.First ().ContainsKey (_filterCol)) {
+// 				Type colType = _list.First () [_filterCol].GetType ();
+// 				if (colType == typeof (JsonNumber)) {
+// 					return ExecuteNumberFilter (_list, _filterCol, _filterVal);
+// 				}
+//
+// 				if (colType == typeof (JsonBoolean)) {
+// 					bool value = StringParsers.ParseBool (_filterVal);
+// 					return _list.Where (_line => ((JsonBoolean) _line [_filterCol]).GetBool () == value);
+// 				}
+//
+// 				if (colType == typeof (JsonString)) {
+// 					// regex-match whole ^string$, replace * by .*, ? by .?, + by .+
+// 					_filterVal = _filterVal.Replace ("*", ".*").Replace ("?", ".?").Replace ("+", ".+");
+// 					_filterVal = "^" + _filterVal + "$";
+//
+// 					//Log.Out ("GetPlayerList: Filter on String with Regex '" + _filterVal + "'");
+// 					Regex matcher = new Regex (_filterVal, RegexOptions.IgnoreCase);
+// 					return _list.Where (_line => matcher.IsMatch (((JsonString) _line [_filterCol]).GetString ()));
+// 				}
+// 			}
+//
+// 			return _list;
+// 		}
+//
+//
+// 		private IEnumerable<JsonObject> ExecuteNumberFilter (IEnumerable<JsonObject> _list, string _filterCol,
+// 			string _filterVal) {
+// 			// allow value (exact match), =, ==, >=, >, <=, <
+// 			Match filterMatch = numberFilterMatcher.Match (_filterVal);
+// 			if (filterMatch.Success) {
+// 				double value = StringParsers.ParseDouble (filterMatch.Groups [2].Value);
+// 				NumberMatchType matchType;
+// 				double epsilon = value / 100000;
+// 				switch (filterMatch.Groups [1].Value) {
+// 					case "":
+// 					case "=":
+// 					case "==":
+// 						matchType = NumberMatchType.Equal;
+// 						break;
+// 					case ">":
+// 						matchType = NumberMatchType.Greater;
+// 						break;
+// 					case ">=":
+// 					case "=>":
+// 						matchType = NumberMatchType.GreaterEqual;
+// 						break;
+// 					case "<":
+// 						matchType = NumberMatchType.Lesser;
+// 						break;
+// 					case "<=":
+// 					case "=<":
+// 						matchType = NumberMatchType.LesserEqual;
+// 						break;
+// 					default:
+// 						matchType = NumberMatchType.Equal;
+// 						break;
+// 				}
+//
+// 				return _list.Where (delegate (JsonObject _line) {
+// 					double objVal = ((JsonNumber) _line [_filterCol]).GetDouble ();
+// 					switch (matchType) {
+// 						case NumberMatchType.Greater:
+// 							return objVal > value;
+// 						case NumberMatchType.GreaterEqual:
+// 							return objVal >= value;
+// 						case NumberMatchType.Lesser:
+// 							return objVal < value;
+// 						case NumberMatchType.LesserEqual:
+// 							return objVal <= value;
+// 						case NumberMatchType.Equal:
+// 						default:
+// 							return NearlyEqual (objVal, value, epsilon);
+// 					}
+// 				});
+// 			}
+//
+// 			global::Log.Out ("[Web] GetPlayerList: ignoring invalid filter for number-column '{0}': '{1}'", _filterCol, _filterVal);
+// 			return _list;
+// 		}
+//
+//
+// 		private IEnumerable<JsonObject> ExecuteSort (IEnumerable<JsonObject> _list, string _sortCol, bool _ascending) {
+// 			if (_list.Count () == 0) {
+// 				return _list;
+// 			}
+//
+// 			if (_list.First ().ContainsKey (_sortCol)) {
+// 				Type colType = _list.First () [_sortCol].GetType ();
+// 				if (colType == typeof (JsonNumber)) {
+// 					if (_ascending) {
+// 						return _list.OrderBy (_line => ((JsonNumber) _line [_sortCol]).GetDouble ());
+// 					}
+//
+// 					return _list.OrderByDescending (_line => ((JsonNumber) _line [_sortCol]).GetDouble ());
+// 				}
+//
+// 				if (colType == typeof (JsonBoolean)) {
+// 					if (_ascending) {
+// 						return _list.OrderBy (_line => ((JsonBoolean) _line [_sortCol]).GetBool ());
+// 					}
+//
+// 					return _list.OrderByDescending (_line => ((JsonBoolean) _line [_sortCol]).GetBool ());
+// 				}
+//
+// 				if (_ascending) {
+// 					return _list.OrderBy (_line => _line [_sortCol].ToString ());
+// 				}
+//
+// 				return _list.OrderByDescending (_line => _line [_sortCol].ToString ());
+// 			}
+//
+// 			return _list;
+// 		}
+//
+//
+// 		private bool NearlyEqual (double _a, double _b, double _epsilon) {
+// 			double absA = Math.Abs (_a);
+// 			double absB = Math.Abs (_b);
+// 			double diff = Math.Abs (_a - _b);
+//
+// 			if (_a == _b) {
+// 				return true;
+// 			}
+//
+// 			if (_a == 0 || _b == 0 || diff < double.Epsilon) {
+// 				return diff < _epsilon;
+// 			}
+//
+// 			return diff / (absA + absB) < _epsilon;
+// 		}
+//
+// 		private enum NumberMatchType {
+// 			Equal,
+// 			Greater,
+// 			GreaterEqual,
+// 			Lesser,
+// 			LesserEqual
+// 		}
+// 	}
+// }
