Ignore:
Timestamp:
Jan 27, 2023, 7:28:00 PM (22 months ago)
Author:
alloc
Message:
  • Major refactoring
  • Using Utf8Json for (de)serialization
  • Moving APIs to REST
  • Removing dependencies from WebServer and MapRenderer to ServerFixes
Location:
binary-improvements2/WebServer/src/WebAPI/APIs
Files:
1 added
1 moved

Legend:

Unmodified
Added
Removed
  • binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerList.cs

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