source: binary-improvements2/MapRendering/Web/API/GetPlayerList.cs@ 389

Last change on this file since 389 was 387, checked in by alloc, 2 years ago

Big refactoring in Web to pass around a Context instead of a bunch of individual arguments all the time

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