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

Last change on this file since 419 was 369, checked in by alloc, 3 years ago

Preparations for A20 release
Changes usage of "SteamID" to "UserID" in console commands
Also changes a bunch of the WebAPI stuff to show / use UserIDs

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