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

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

*Latest optimizations

File size: 8.3 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Net;
5using System.Text.RegularExpressions;
6using AllocsFixes.JSON;
7using AllocsFixes.PersistentData;
8using UnityEngine.Profiling;
9
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]*)?)$");
14
15#if ENABLE_PROFILER
16                private static readonly CustomSampler jsonSerializeSampler = CustomSampler.Create ("JSON_Build");
17#endif
18
19                public override void HandleRequest (HttpListenerRequest req, HttpListenerResponse resp, WebConnection user,
20                        int permissionLevel) {
21                        AdminTools admTools = GameManager.Instance.adminTools;
22                        user = user ?? new WebConnection ("", IPAddress.None, 0L);
23
24                        bool bViewAll = WebConnection.CanViewAllPlayers (permissionLevel);
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
42                       
43                        List<JSONObject> playerList = new List<JSONObject> ();
44
45#if ENABLE_PROFILER
46                        jsonSerializeSampler.Begin ();
47#endif
48
49                        foreach (KeyValuePair<string, Player> kvp in playersList.Dict) {
50                                Player p = kvp.Value;
51
52                                ulong player_steam_ID;
53                                if (!ulong.TryParse (kvp.Key, out player_steam_ID)) {
54                                        player_steam_ID = 0L;
55                                }
56
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));
62
63                                        JSONObject pJson = new JSONObject ();
64                                        pJson.Add ("steamid", new JSONString (kvp.Key));
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);
70
71                                        pJson.Add ("totalplaytime", new JSONNumber (p.TotalPlayTime));
72                                        pJson.Add ("lastonline",
73                                                new JSONString (p.LastOnline.ToUniversalTime ().ToString ("yyyy-MM-ddTHH:mm:ssZ")));
74                                        pJson.Add ("ping", new JSONNumber (p.IsOnline ? p.ClientInfo.ping : -1));
75
76                                        JSONBoolean banned;
77                                        if (admTools != null) {
78                                                banned = new JSONBoolean (admTools.IsBanned (kvp.Key));
79                                        } else {
80                                                banned = new JSONBoolean (false);
81                                        }
82
83                                        pJson.Add ("banned", banned);
84
85                                        playerList.Add (pJson);
86                                }
87                        }
88
89#if ENABLE_PROFILER
90                        jsonSerializeSampler.End ();
91#endif
92
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
135                private IEnumerable<JSONObject> ExecuteFilter (IEnumerable<JSONObject> _list, string _filterCol,
136                        string _filterVal) {
137                        if (_list.Count () == 0) {
138                                return _list;
139                        }
140
141                        if (_list.First ().ContainsKey (_filterCol)) {
142                                Type colType = _list.First () [_filterCol].GetType ();
143                                if (colType == typeof (JSONNumber)) {
144                                        return ExecuteNumberFilter (_list, _filterCol, _filterVal);
145                                }
146
147                                if (colType == typeof (JSONBoolean)) {
148                                        bool value = StringParsers.ParseBool (_filterVal);
149                                        return _list.Where (line => ((JSONBoolean) line [_filterCol]).GetBool () == value);
150                                }
151
152                                if (colType == typeof (JSONString)) {
153                                        // regex-match whole ^string$, replace * by .*, ? by .?, + by .+
154                                        _filterVal = _filterVal.Replace ("*", ".*").Replace ("?", ".?").Replace ("+", ".+");
155                                        _filterVal = "^" + _filterVal + "$";
156
157                                        //Log.Out ("GetPlayerList: Filter on String with Regex '" + _filterVal + "'");
158                                        Regex matcher = new Regex (_filterVal, RegexOptions.IgnoreCase);
159                                        return _list.Where (line => matcher.IsMatch (((JSONString) line [_filterCol]).GetString ()));
160                                }
161                        }
162
163                        return _list;
164                }
165
166
167                private IEnumerable<JSONObject> ExecuteNumberFilter (IEnumerable<JSONObject> _list, string _filterCol,
168                        string _filterVal) {
169                        // allow value (exact match), =, ==, >=, >, <=, <
170                        Match filterMatch = numberFilterMatcher.Match (_filterVal);
171                        if (filterMatch.Success) {
172                                double value = StringParsers.ParseDouble (filterMatch.Groups [2].Value);
173                                NumberMatchType matchType;
174                                double epsilon = value / 100000;
175                                switch (filterMatch.Groups [1].Value) {
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;
198                                }
199
200                                return _list.Where (delegate (JSONObject line) {
201                                        double objVal = ((JSONNumber) line [_filterCol]).GetDouble ();
202                                        switch (matchType) {
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);
214                                        }
215                                });
216                        }
217
218                        Log.Out ("GetPlayerList: ignoring invalid filter for number-column '{0}': '{1}'", _filterCol, _filterVal);
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 ();
230                                if (colType == typeof (JSONNumber)) {
231                                        if (_ascending) {
232                                                return _list.OrderBy (line => ((JSONNumber) line [_sortCol]).GetDouble ());
233                                        }
234
235                                        return _list.OrderByDescending (line => ((JSONNumber) line [_sortCol]).GetDouble ());
236                                }
237
238                                if (colType == typeof (JSONBoolean)) {
239                                        if (_ascending) {
240                                                return _list.OrderBy (line => ((JSONBoolean) line [_sortCol]).GetBool ());
241                                        }
242
243                                        return _list.OrderByDescending (line => ((JSONBoolean) line [_sortCol]).GetBool ());
244                                }
245
246                                if (_ascending) {
247                                        return _list.OrderBy (line => line [_sortCol].ToString ());
248                                }
249
250                                return _list.OrderByDescending (line => line [_sortCol].ToString ());
251                        }
252
253                        return _list;
254                }
255
256
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);
261
262                        if (a == b) {
263                                return true;
264                        }
265
266                        if (a == 0 || b == 0 || diff < double.Epsilon) {
267                                return diff < epsilon;
268                        }
269
270                        return diff / (absA + absB) < epsilon;
271                }
272
273                private enum NumberMatchType {
274                        Equal,
275                        Greater,
276                        GreaterEqual,
277                        Lesser,
278                        LesserEqual
279                }
280        }
281}
Note: See TracBrowser for help on using the repository browser.