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

Last change on this file since 326 was 326, checked in by alloc, 5 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.