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

Last change on this file since 332 was 332, checked in by alloc, 6 years ago

*Latest optimizations

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