source: binary-improvements/MapRendering/API/GetPlayerList.cs@ 454

Last change on this file since 454 was 454, checked in by alloc, 16 months ago

24_29_43
Switched over to vanilla Web infrastructure

File size: 8.4 KB
RevLine 
[279]1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text.RegularExpressions;
[325]5using AllocsFixes.JSON;
6using AllocsFixes.PersistentData;
[454]7using Webserver;
8using Webserver.Permissions;
9using Webserver.WebAPI;
[279]10
[454]11namespace AllocsFixes.WebAPIs {
12 public class GetPlayerList : AbsWebAPI {
[325]13 private static readonly Regex numberFilterMatcher =
14 new Regex (@"^(>=|=>|>|<=|=<|<|==|=)?\s*([0-9]+(\.[0-9]*)?)$");
[279]15
[332]16#if ENABLE_PROFILER
[369]17 private static readonly UnityEngine.Profiling.CustomSampler jsonSerializeSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Build");
[332]18#endif
19
[454]20 public override void HandleRequest (RequestContext _context) {
[325]21 AdminTools admTools = GameManager.Instance.adminTools;
[454]22 PlatformUserIdentifierAbs userId = _context.Connection?.UserId;
[279]23
[454]24 bool bViewAll = PermissionUtils.CanViewAllPlayers (_context.PermissionLevel);
[279]25
26 // 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)
27
28 int rowsPerPage = 25;
[454]29 if (_context.Request.QueryString ["rowsperpage"] != null) {
30 int.TryParse (_context.Request.QueryString ["rowsperpage"], out rowsPerPage);
[279]31 }
32
33 int page = 0;
[454]34 if (_context.Request.QueryString ["page"] != null) {
35 int.TryParse (_context.Request.QueryString ["page"], out page);
[279]36 }
37
38 int firstEntry = page * rowsPerPage;
39
40 Players playersList = PersistentContainer.Instance.Players;
41
[332]42
[279]43 List<JSONObject> playerList = new List<JSONObject> ();
44
[332]45#if ENABLE_PROFILER
46 jsonSerializeSampler.Begin ();
47#endif
[279]48
[369]49 foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in playersList.Dict) {
[332]50 Player p = kvp.Value;
51
[446]52 if (bViewAll || p.InternalId.Equals (userId)) {
[325]53 JSONObject pos = new JSONObject ();
54 pos.Add ("x", new JSONNumber (p.LastPosition.x));
55 pos.Add ("y", new JSONNumber (p.LastPosition.y));
56 pos.Add ("z", new JSONNumber (p.LastPosition.z));
[279]57
[325]58 JSONObject pJson = new JSONObject ();
[446]59 pJson.Add ("steamid", new JSONString (kvp.Value.PlatformId.CombinedString));
60 pJson.Add ("crossplatformid", new JSONString (kvp.Value.CrossPlatformId?.CombinedString ?? ""));
[325]61 pJson.Add ("entityid", new JSONNumber (p.EntityID));
62 pJson.Add ("ip", new JSONString (p.IP));
63 pJson.Add ("name", new JSONString (p.Name));
64 pJson.Add ("online", new JSONBoolean (p.IsOnline));
65 pJson.Add ("position", pos);
[279]66
67 pJson.Add ("totalplaytime", new JSONNumber (p.TotalPlayTime));
[325]68 pJson.Add ("lastonline",
69 new JSONString (p.LastOnline.ToUniversalTime ().ToString ("yyyy-MM-ddTHH:mm:ssZ")));
[279]70 pJson.Add ("ping", new JSONNumber (p.IsOnline ? p.ClientInfo.ping : -1));
71
[420]72 JSONBoolean banned = admTools != null ? new JSONBoolean (admTools.Blacklist.IsBanned (kvp.Key, out _, out _)) : new JSONBoolean (false);
[325]73
[279]74 pJson.Add ("banned", banned);
75
76 playerList.Add (pJson);
[325]77 }
78 }
[279]79
[332]80#if ENABLE_PROFILER
81 jsonSerializeSampler.End ();
82#endif
83
[279]84 IEnumerable<JSONObject> list = playerList;
85
[454]86 foreach (string key in _context.Request.QueryString.AllKeys) {
[279]87 if (!string.IsNullOrEmpty (key) && key.StartsWith ("filter[")) {
88 string filterCol = key.Substring (key.IndexOf ('[') + 1);
89 filterCol = filterCol.Substring (0, filterCol.Length - 1);
[454]90 string filterVal = _context.Request.QueryString.Get (key).Trim ();
[279]91
92 list = ExecuteFilter (list, filterCol, filterVal);
93 }
94 }
95
96 int totalAfterFilter = list.Count ();
97
[454]98 foreach (string key in _context.Request.QueryString.AllKeys) {
[279]99 if (!string.IsNullOrEmpty (key) && key.StartsWith ("sort[")) {
100 string sortCol = key.Substring (key.IndexOf ('[') + 1);
101 sortCol = sortCol.Substring (0, sortCol.Length - 1);
[454]102 string sortVal = _context.Request.QueryString.Get (key);
[279]103
104 list = ExecuteSort (list, sortCol, sortVal == "0");
105 }
106 }
107
108 list = list.Skip (firstEntry);
109 list = list.Take (rowsPerPage);
110
111
112 JSONArray playersJsResult = new JSONArray ();
113 foreach (JSONObject jsO in list) {
114 playersJsResult.Add (jsO);
115 }
116
117 JSONObject result = new JSONObject ();
118 result.Add ("total", new JSONNumber (totalAfterFilter));
119 result.Add ("totalUnfiltered", new JSONNumber (playerList.Count));
120 result.Add ("firstResult", new JSONNumber (firstEntry));
121 result.Add ("players", playersJsResult);
122
[454]123 LegacyApiHelper.WriteJSON (_context.Response, result);
[279]124 }
125
[325]126 private IEnumerable<JSONObject> ExecuteFilter (IEnumerable<JSONObject> _list, string _filterCol,
127 string _filterVal) {
[351]128 if (!_list.Any()) {
[279]129 return _list;
130 }
131
132 if (_list.First ().ContainsKey (_filterCol)) {
133 Type colType = _list.First () [_filterCol].GetType ();
[325]134 if (colType == typeof (JSONNumber)) {
[279]135 return ExecuteNumberFilter (_list, _filterCol, _filterVal);
[325]136 }
137
138 if (colType == typeof (JSONBoolean)) {
[326]139 bool value = StringParsers.ParseBool (_filterVal);
[351]140 return _list.Where (_line => ((JSONBoolean) _line [_filterCol]).GetBool () == value);
[325]141 }
142
143 if (colType == typeof (JSONString)) {
[279]144 // regex-match whole ^string$, replace * by .*, ? by .?, + by .+
145 _filterVal = _filterVal.Replace ("*", ".*").Replace ("?", ".?").Replace ("+", ".+");
146 _filterVal = "^" + _filterVal + "$";
[325]147
[279]148 //Log.Out ("GetPlayerList: Filter on String with Regex '" + _filterVal + "'");
149 Regex matcher = new Regex (_filterVal, RegexOptions.IgnoreCase);
[351]150 return _list.Where (_line => matcher.IsMatch (((JSONString) _line [_filterCol]).GetString ()));
[279]151 }
152 }
[325]153
[279]154 return _list;
155 }
156
157
[325]158 private IEnumerable<JSONObject> ExecuteNumberFilter (IEnumerable<JSONObject> _list, string _filterCol,
159 string _filterVal) {
[279]160 // allow value (exact match), =, ==, >=, >, <=, <
161 Match filterMatch = numberFilterMatcher.Match (_filterVal);
162 if (filterMatch.Success) {
[324]163 double value = StringParsers.ParseDouble (filterMatch.Groups [2].Value);
[279]164 NumberMatchType matchType;
165 double epsilon = value / 100000;
166 switch (filterMatch.Groups [1].Value) {
[325]167 case "":
168 case "=":
169 case "==":
170 matchType = NumberMatchType.Equal;
171 break;
172 case ">":
173 matchType = NumberMatchType.Greater;
174 break;
175 case ">=":
176 case "=>":
177 matchType = NumberMatchType.GreaterEqual;
178 break;
179 case "<":
180 matchType = NumberMatchType.Lesser;
181 break;
182 case "<=":
183 case "=<":
184 matchType = NumberMatchType.LesserEqual;
185 break;
186 default:
187 matchType = NumberMatchType.Equal;
188 break;
[279]189 }
[325]190
[351]191 return _list.Where (delegate (JSONObject _line) {
192 double objVal = ((JSONNumber) _line [_filterCol]).GetDouble ();
[279]193 switch (matchType) {
[325]194 case NumberMatchType.Greater:
195 return objVal > value;
196 case NumberMatchType.GreaterEqual:
197 return objVal >= value;
198 case NumberMatchType.Lesser:
199 return objVal < value;
200 case NumberMatchType.LesserEqual:
201 return objVal <= value;
202 case NumberMatchType.Equal:
203 default:
204 return NearlyEqual (objVal, value, epsilon);
[279]205 }
206 });
207 }
[325]208
209 Log.Out ("GetPlayerList: ignoring invalid filter for number-column '{0}': '{1}'", _filterCol, _filterVal);
[279]210 return _list;
211 }
212
213
214 private IEnumerable<JSONObject> ExecuteSort (IEnumerable<JSONObject> _list, string _sortCol, bool _ascending) {
215 if (_list.Count () == 0) {
216 return _list;
217 }
218
219 if (_list.First ().ContainsKey (_sortCol)) {
220 Type colType = _list.First () [_sortCol].GetType ();
[325]221 if (colType == typeof (JSONNumber)) {
[279]222 if (_ascending) {
[351]223 return _list.OrderBy (_line => ((JSONNumber) _line [_sortCol]).GetDouble ());
[279]224 }
[325]225
[351]226 return _list.OrderByDescending (_line => ((JSONNumber) _line [_sortCol]).GetDouble ());
[325]227 }
228
229 if (colType == typeof (JSONBoolean)) {
[279]230 if (_ascending) {
[351]231 return _list.OrderBy (_line => ((JSONBoolean) _line [_sortCol]).GetBool ());
[279]232 }
[325]233
[351]234 return _list.OrderByDescending (_line => ((JSONBoolean) _line [_sortCol]).GetBool ());
[279]235 }
[325]236
237 if (_ascending) {
[351]238 return _list.OrderBy (_line => _line [_sortCol].ToString ());
[325]239 }
240
[351]241 return _list.OrderByDescending (_line => _line [_sortCol].ToString ());
[279]242 }
[325]243
[279]244 return _list;
245 }
246
247
[351]248 private bool NearlyEqual (double _a, double _b, double _epsilon) {
249 double absA = Math.Abs (_a);
250 double absB = Math.Abs (_b);
251 double diff = Math.Abs (_a - _b);
[279]252
[351]253 if (_a == _b) {
[279]254 return true;
[325]255 }
256
[351]257 if (_a == 0 || _b == 0 || diff < double.Epsilon) {
258 return diff < _epsilon;
[279]259 }
[325]260
[351]261 return diff / (absA + absB) < _epsilon;
[279]262 }
263
[325]264 private enum NumberMatchType {
265 Equal,
266 Greater,
267 GreaterEqual,
268 Lesser,
269 LesserEqual
270 }
[279]271 }
[325]272}
Note: See TracBrowser for help on using the repository browser.