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

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

More cleanup, allocation improvements

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