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

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