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

Last change on this file since 309 was 309, checked in by alloc, 2 years ago

Fixes 14_16_21

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