Exemplo n.º 1
0
        public async Task <DataGameList> GetGamesNew()
        {
            using (var http = new HttpClient())
            {
                var res = await http.GetStringAsync(queryUrl).ConfigureAwait(false);

                var gamelist = JsonConvert.DeserializeObject <BZCCRaknetData>(res);
                //gamelist.SetBz2Service(this);
                //return gamelist;

                DataGameList data = new DataGameList()
                {
                    GameTitle = this.Title,
                    Header    = new DataGameListHeader()
                    {
                        Description = "List of games currently on Battlezone CC matchmaking servers",
                        Image       = "http://discord.battlezone.report/resources/logos/bzcc.png",
                        Credit      = "Brought to you by Nielk1's Raknet Bot"
                    }
                };

                {
                    bool isOnIonDriver = gamelist.GET.Any(game => game.IsOnIonDriver());
                    DataGameListServerStatus IonDriverStatus = new DataGameListServerStatus()
                    {
                        Name = "IonDriver", Updated = null
                    };
                    //if (isIonDriver)
                    //{
                    //    IonDriverStatus.Status = EDataGameListServerStatus.Online;
                    //}
                    //else if (isIonDriver)
                    //{
                    //    IonDriverStatus.Status = EDataGameListServerStatus.NoMarker;
                    //}
                    //else
                    //{
                    //    IonDriverStatus.Status = EDataGameListServerStatus.Unknown;
                    //}
                    IonDriverStatus.Status = EDataGameListServerStatus.NotSet;

                    bool     isOnRebellion                   = gamelist.GET.Any(game => game.IsOnRebellion());
                    bool     haveRebellionStatus             = gamelist.proxyStatus.ContainsKey("Rebellion");
                    bool     isRebellionUp                   = haveRebellionStatus && gamelist.proxyStatus["Rebellion"].success == true;
                    string   statusRebellion                 = haveRebellionStatus ? gamelist.proxyStatus["Rebellion"].status : null;
                    DateTime?dateRebellion                   = haveRebellionStatus ? gamelist.proxyStatus["Rebellion"].updated : null;
                    DataGameListServerStatus RebellionStatus = new DataGameListServerStatus()
                    {
                        Name = "Rebellion (Primary)", Updated = dateRebellion
                    };
                    if (isRebellionUp)
                    {
                        RebellionStatus.Status = EDataGameListServerStatus.Online;
                    }
                    else if (!isRebellionUp)
                    {
                        RebellionStatus.Status = EDataGameListServerStatus.Offline;
                    }
                    else
                    {
                        RebellionStatus.Status = EDataGameListServerStatus.Unknown;
                    }

                    data.Header.ServerStatus = new DataGameListServerStatus[] { IonDriverStatus, RebellionStatus };
                }

                data.Games = (await Task.WhenAll(
                                  gamelist.GET
                                  .Select(async raw =>
                {
                    DataGameListGame game = new DataGameListGame();

                    game.Name = raw.Name;
                    game.Image = "http://discord.battlezone.report/resources/logos/nomap.png";

                    game.CurPlayers = raw.CurPlayers;
                    game.MaxPlayers = raw.MaxPlayers;

                    if (raw.Locked)
                    {
                        game.Status = EDataGameListServerGameStatus.Locked;
                    }
                    else if (raw.Passworded)
                    {
                        game.Status = EDataGameListServerGameStatus.Passworded;
                    }
                    else if (!raw.MaxPlayers.HasValue)
                    {
                        game.Status = EDataGameListServerGameStatus.Unknown;
                    }
                    else
                    {
                        game.Status = EDataGameListServerGameStatus.Open;
                    }

                    game.MapFilename = raw.MapFile;
                    game.Footer = raw.MapFile + @".bzn";

                    if (!string.IsNullOrWhiteSpace(raw.MOTD))
                    {
                        game.TopInfo.Add(Format.Sanitize(raw.MOTD));
                    }

                    foreach (string mod in raw.Mods)
                    {
                        ulong workshopIdNum = 0;
                        if (!string.IsNullOrWhiteSpace(mod) && mod != "0")
                        {
                            if (ulong.TryParse(mod, out workshopIdNum) && workshopIdNum > 0)
                            {
                                Task <string> modNameTask = Task.Run(async() =>
                                {
                                    string modNameRet = await _steam.GetSteamWorkshopName(mod);
                                    return(modNameRet);
                                });
                                var modName = modNameTask?.Result ?? mod;

                                if (!string.IsNullOrWhiteSpace(modName))
                                {
                                    game.TopInfo.Add($"Mod: [{Format.Sanitize(modName)}](http://steamcommunity.com/sharedfiles/filedetails/?id={mod})");
                                }
                                else
                                {
                                    game.TopInfo.Add($"Mod: [{Format.Sanitize(mod)}](http://steamcommunity.com/sharedfiles/filedetails/?id={mod}");
                                }
                            }
                            else
                            {
                                game.TopInfo.Add("Mod: " + mod);
                            }
                        }
                        break;     // hack to only do the first entry until I properly redo this
                    }

                    {
                        int k = 1;
                        int d = 1;
                        int s = 1;
                        bool scoreNeedsSign = false;

                        raw.pl.ForEach(dr =>
                        {
                            k = Math.Max(k, (dr.Kills.HasValue ? dr.Kills.Value : 0).ToString().Length);
                            d = Math.Max(d, (dr.Deaths.HasValue ? dr.Deaths.Value : 0).ToString().Length);
                            s = Math.Max(s, Math.Abs(dr.Score.HasValue ? dr.Score.Value : 0).ToString().Length);
                            scoreNeedsSign = scoreNeedsSign || (dr.Score < 0);
                        });

                        await Task.WhenAll(raw.pl.Select(async dr =>
                        {
                            int Killv = dr.Kills.HasValue ? dr.Kills.Value : 0;
                            int Deathsv = dr.Deaths.HasValue ? dr.Deaths.Value : 0;
                            int Scorev = dr.Score.HasValue ? dr.Score.Value : 0;

                            string scoresign = "0";
                            if (Scorev > 0)
                            {
                                scoresign = "+";
                            }
                            if (Scorev < 0)
                            {
                                scoresign = "-";
                            }
                            if (!scoreNeedsSign)
                            {
                                scoresign = string.Empty;
                            }

                            UserData userData = await GetUserData(dr.PlayerID);

                            game.Players.Add(new DataGameListPlayer()
                            {
                                Index = dr.Team,
                                Name = dr.Name,
                                PlayerClass = $"{Killv.ToString().PadLeft(k, '0')}/{Deathsv.ToString().PadLeft(d, '0')}/{scoresign}{Math.Abs(Scorev).ToString().PadLeft(s, '0')}",
                                Url = userData?.ProfileUrl
                            });
                        }));

                        game.PlayersHeader = "[T] (K/D/S) Players";
                    }

                    {
                        string name = await GetBZCCGameProperty("name", raw.MapFile);

                        if (string.IsNullOrWhiteSpace(name))
                        {
                            game.Properties.Add(new Tuple <string, string>("Map", $"[{raw.MapFile}]"));
                        }
                        else
                        {
                            game.Properties.Add(new Tuple <string, string>("Map", $"{name}"));
                        }

                        if (!string.IsNullOrWhiteSpace(raw.v))
                        {
                            game.Properties.Add(new Tuple <string, string>("Version", $"{raw.v}"));
                        }

                        /*if (!string.IsNullOrWhiteSpace(raw.d))
                         * {
                         *  game.Properties.Add(new Tuple<string, string>("Mod", $"[{raw.d}]"));
                         * }*/

                        if (!string.IsNullOrWhiteSpace(raw.t))
                        {
                            switch (raw.t)
                            {
                            case "0":
                                game.Properties.Add(new Tuple <string, string>("NAT", $"NONE"));        /// Works with anyone
                                break;

                            case "1":
                                game.Properties.Add(new Tuple <string, string>("NAT", $"FULL CONE"));        /// Accepts any datagrams to a port that has been previously used. Will accept the first datagram from the remote peer.
                                break;

                            case "2":
                                game.Properties.Add(new Tuple <string, string>("NAT", $"ADDRESS RESTRICTED"));        /// Accepts datagrams to a port as long as the datagram source IP address is a system we have already sent to. Will accept the first datagram if both systems send simultaneously. Otherwise, will accept the first datagram after we have sent one datagram.
                                break;

                            case "3":
                                game.Properties.Add(new Tuple <string, string>("NAT", $"PORT RESTRICTED"));        /// Same as address-restricted cone NAT, but we had to send to both the correct remote IP address and correct remote port. The same source address and port to a different destination uses the same mapping.
                                break;

                            case "4":
                                game.Properties.Add(new Tuple <string, string>("NAT", $"SYMMETRIC"));        /// A different port is chosen for every remote destination. The same source address and port to a different destination uses a different mapping. Since the port will be different, the first external punchthrough attempt will fail. For this to work it requires port-prediction (MAX_PREDICTIVE_PORT_RANGE>1) and that the router chooses ports sequentially.
                                break;

                            case "5":
                                game.Properties.Add(new Tuple <string, string>("NAT", $"UNKNOWN"));        /// Hasn't been determined. NATTypeDetectionClient does not use this, but other plugins might
                                break;

                            case "6":
                                game.Properties.Add(new Tuple <string, string>("NAT", $"DETECTION IN PROGRESS"));        /// In progress. NATTypeDetectionClient does not use this, but other plugins might
                                break;

                            case "7":
                                game.Properties.Add(new Tuple <string, string>("NAT", $"SUPPORTS UPNP"));        /// Didn't bother figuring it out, as we support UPNP, so it is equivalent to NAT_TYPE_NONE. NATTypeDetectionClient does not use this, but other plugins might
                                break;

                            default:
                                game.Properties.Add(new Tuple <string, string>("NAT", $"[" + raw.t + "]"));
                                break;
                            }
                        }

                        switch (raw.proxySource)
                        {
                        case "Rebellion":
                            game.Properties.Add(new Tuple <string, string>("List", $"Rebellion"));
                            break;

                        default:
                            game.Properties.Add(new Tuple <string, string>("List", $"IonDriver"));
                            break;
                        }

                        {
                            switch (raw.GameType)
                            {
                            case 0:
                                game.Properties.Add(new Tuple <string, string>("Type", $"All"));
                                break;

                            case 1:
                                switch (raw.GameSubType)
                                {
                                case 0:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"DM"));
                                    break;

                                case 1:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"KOTH"));
                                    break;

                                case 2:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"CTF"));
                                    break;

                                case 3:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"Loot"));
                                    break;

                                case 4:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"DM [RESERVED]"));
                                    break;

                                case 5:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"Race"));
                                    break;

                                case 6:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"Race (Vehicle Only)"));
                                    break;

                                case 7:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"DM (Vehicle Only)"));
                                    break;

                                default:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"DM [UNKNOWN {raw.GameSubType}]"));
                                    break;
                                }
                                break;

                            case 2:
                                switch (raw.GameSubType)
                                {
                                case 12:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"STRAT"));
                                    break;

                                case 13:
                                case 41:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"MPI"));
                                    break;

                                default:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"STRAT [UNKNOWN {raw.GameSubType}]"));
                                    break;
                                }
                                break;

                            case 3:
                                game.Properties.Add(new Tuple <string, string>("Type", $"MPI [Invalid]"));
                                break;
                            }

                            if (raw.GameTimeMinutes.HasValue)
                            {
                                switch (raw.ServerInfoMode)
                                {
                                case 1:
                                    if (raw.GameTimeMinutes.Value == 255)
                                    {
                                        game.Properties.Add(new Tuple <string, string>("Time", $"Not playing or in shell"));
                                    }
                                    else
                                    {
                                        game.Properties.Add(new Tuple <string, string>("Time", $"Not playing or in shell for {raw.GameTimeMinutes.Value} minutes"));
                                    }
                                    break;

                                case 3:
                                    game.Properties.Add(new Tuple <string, string>("Time", $"Playing for {raw.GameTimeMinutes.Value} minutes"));
                                    break;
                                }
                            }

                            if (raw.TPS.HasValue && raw.TPS > 0)
                            {
                                game.Properties.Add(new Tuple <string, string>("TPS", $"{raw.TPS}"));
                            }

                            if (raw.MaxPing.HasValue && raw.MaxPing > 0)
                            {
                                game.Properties.Add(new Tuple <string, string>("MaxPing", $"{raw.MaxPing}"));
                            }

                            if (raw.TimeLimit.HasValue && raw.TimeLimit > 0)
                            {
                                game.Properties.Add(new Tuple <string, string>("TimeLim", $"{raw.TimeLimit}"));
                            }

                            if (raw.KillLimit.HasValue && raw.KillLimit > 0)
                            {
                                game.Properties.Add(new Tuple <string, string>("KillLim", $"{raw.KillLimit}"));
                            }
                        }
                    }

                    return(game);
                }))).ToArray();

                return(data);
            }
        }
Exemplo n.º 2
0
        public async Task <DataGameList> GetGamesNew()
        {
            Tuple <string, DateTime> gameData = await TryReadText(filePath, TimeSpan.FromSeconds(5));

            if (gameData == null || string.IsNullOrWhiteSpace(gameData.Item1))
            {
                return(null);
            }

            var LobbyData = JsonConvert.DeserializeObject <Dictionary <string, Lobby> >(gameData.Item1);
            //return new BZ98ServerData()
            //{
            //    Games = LobbyData.Where(dr => !dr.Value.isChat
            //                               && (!dr.Value.isPrivate || (dr.Value.isPrivate && dr.Value.IsPassworded == true))
            //                               && ((!string.IsNullOrWhiteSpace(dr.Value.clientVersion)) && ("0123456789".Contains(dr.Value.clientVersion[0]))) // not mobile which starts with MB or something
            //                               && ((!string.IsNullOrWhiteSpace(dr.Value.clientVersion)) && (dr.Value.clientVersion != "0.0.0")) // not test game
            //                           ).Select(dr => dr.Value).ToList(),
            //    Modified = gameData.Item2
            //};

            DataGameList data = new DataGameList()
            {
                GameTitle = this.Title,
                Header    = new DataGameListHeader()
                {
                    Description = "List of games currently on BZ98 Redux matchmaking server",
                    Image       = "http://discord.battlezone.report/resources/logos/bz98r.png",
                    Credit      = $"Fetched by Nielk1's BZ98Bridge Bot"
                }
            };

            data.Header.ServerStatus = new DataGameListServerStatus[] {
                new DataGameListServerStatus()
                {
                    Name = "Rebellion", Status = EDataGameListServerStatus.NotSet, Updated = gameData.Item2
                }
            };

            data.Games = (await Task.WhenAll(
                              LobbyData
                              .Where(dr => !dr.Value.isChat &&
                                     (!dr.Value.isPrivate || (dr.Value.isPrivate && dr.Value.IsPassworded == true))
                                     //&& ((!string.IsNullOrWhiteSpace(dr.Value.clientVersion)) && ("0123456789".Contains(dr.Value.clientVersion[0]))) // not mobile which starts with MB or something
                                     && ((!string.IsNullOrWhiteSpace(dr.Value.clientVersion)) && (dr.Value.clientVersion != "0.0.0")) // not test game
                                     ).Select(dr => dr.Value)
                              .Select(async raw =>
            {
                DataGameListGame game = new DataGameListGame();

                game.Name = raw.Name;
                game.Image = await GetShellMap(raw.MapFile) ?? "http://discord.battlezone.report/resources/logos/nomap.png";

                game.CurPlayers = raw.userCount;
                game.MaxPlayers = raw.PlayerLimit;

                if (raw.isLocked)
                {
                    game.Status = EDataGameListServerGameStatus.Locked;
                }
                else if (raw.IsPassworded == true)
                {
                    game.Status = EDataGameListServerGameStatus.Passworded;
                }
                else
                {
                    game.Status = EDataGameListServerGameStatus.Open;
                }

                game.MapFilename = raw.MapFile;
                game.Footer = raw.MapFile + @".bzn";
                if (!string.IsNullOrWhiteSpace(raw.clientVersion))
                {
                    game.Footer += $" <{raw.clientVersion}>";
                }

                {
                    await Task.WhenAll(raw.users
                                       .Select(async dr =>
                    {
                        string team = dr.Value.metadata.ContainsKey("team") ? dr.Value.metadata["team"] : string.Empty;
                        string vehicle = dr.Value.metadata.ContainsKey("vehicle") ? dr.Value.metadata["vehicle"] : string.Empty;

                        int teamInt = 0;
                        UserData userData = await GetUserData(dr.Value.id, dr.Value.authType);

                        game.Players.Add(new DataGameListPlayer()
                        {
                            Index = int.TryParse(team, out teamInt) ? (int?)teamInt : null,
                            Name = dr.Value.name,
                            PlayerClass = vehicle,
                            Url = userData?.ProfileUrl
                        });
                    }));

                    game.PlayersHeader = "Players";
                }

                {
                    ulong workshopIdNum = 0;
                    if (!string.IsNullOrWhiteSpace(raw.WorkshopID) && raw.WorkshopID != "0")
                    {
                        if (ulong.TryParse(raw.WorkshopID, out workshopIdNum) && workshopIdNum > 0)
                        {
                            Task <string> modNameTask = Task.Run(async() =>
                            {
                                string modNameRet = await _steam.GetSteamWorkshopName(raw.WorkshopID);
                                return(modNameRet);
                            });
                            var modName = modNameTask?.Result ?? raw.WorkshopID;

                            if (!string.IsNullOrWhiteSpace(modName))
                            {
                                game.TopInfo.Add($"Mod: [{Format.Sanitize(modName)}](http://steamcommunity.com/sharedfiles/filedetails/?id={raw.WorkshopID})");
                            }
                            else
                            {
                                game.TopInfo.Add($"Mod: [{Format.Sanitize(raw.WorkshopID)}](http://steamcommunity.com/sharedfiles/filedetails/?id={raw.WorkshopID}");
                            }
                        }
                        else
                        {
                            game.TopInfo.Add("Mod: " + raw.WorkshopID);
                        }
                    }
                }

                game.Properties.Add(new Tuple <string, string>("Map", "[" + raw.MapFile + "]"));
                game.Properties.Add(new Tuple <string, string>("State", raw.IsEnded ? "Ended" : raw.IsLaunched ? "Launched" : "In Shell"));
                if (raw.TimeLimit.HasValue && raw.TimeLimit.Value > 0)
                {
                    game.Properties.Add(new Tuple <string, string>("TimeLimit", raw.TimeLimit.Value.ToString()));
                }
                if (raw.KillLimit.HasValue && raw.KillLimit.Value > 0)
                {
                    game.Properties.Add(new Tuple <string, string>("KillLimit", raw.KillLimit.Value.ToString()));
                }
                if (raw.Lives.HasValue && raw.Lives.Value > 0)
                {
                    game.Properties.Add(new Tuple <string, string>("Lives", raw.Lives.Value.ToString()));
                }
                if (raw.SyncJoin.HasValue)
                {
                    game.Properties.Add(new Tuple <string, string>("SyncJoin", raw.SyncJoin.Value ? "On" : "Off"));
                }
                if (raw.SatelliteEnabled.HasValue)
                {
                    game.Properties.Add(new Tuple <string, string>("Satellite", raw.SatelliteEnabled.Value ? "On" : "Off"));
                }
                if (raw.BarracksEnabled.HasValue)
                {
                    game.Properties.Add(new Tuple <string, string>("Barracks", raw.BarracksEnabled.Value ? "On" : "Off"));
                }
                if (raw.SniperEnabled.HasValue)
                {
                    game.Properties.Add(new Tuple <string, string>("Sniper", raw.SniperEnabled.Value ? "On" : "Off"));
                }
                if (raw.SplinterEnabled.HasValue)
                {
                    game.Properties.Add(new Tuple <string, string>("Splinter", raw.SplinterEnabled.Value ? "On" : "Off"));
                }

                return(game);
            })
                              )).ToArray();

            return(data);
        }
Exemplo n.º 3
0
        public async Task <DataGameList> GetGamesNew()
        {
            using (var http = new HttpClient())
            {
                var res = await http.GetStringAsync(queryUrl).ConfigureAwait(false);

                var gamelist = JsonConvert.DeserializeObject <RaknetData>(res);
                //gamelist.SetBz2Service(this);
                //return gamelist;

                DataGameList data = new DataGameList()
                {
                    GameTitle = this.Title,
                    Header    = new DataGameListHeader()
                    {
                        Description = "List of games currently on Battlezone II matchmaking servers",
                        Image       = "http://discord.battlezone.report/resources/logos/bz2.png",
                        Credit      = "Brought to you by Nielk1's Raknet Bot"
                    }
                };

                {
                    bool isIonDriver   = gamelist.GET.Any(game => game.IsIonDriverMarker());
                    bool isOnIonDriver = gamelist.GET.Any(game => game.IsOnIonDriver());
                    DataGameListServerStatus IonDriverStatus = new DataGameListServerStatus()
                    {
                        Name = "IonDriver", Updated = null
                    };
                    if (isIonDriver || isOnIonDriver)
                    {
                        IonDriverStatus.Status = EDataGameListServerStatus.Online;
                    }
                    else
                    {
                        IonDriverStatus.Status = EDataGameListServerStatus.NoGames;
                    }

                    bool     isMatesFamily                     = gamelist.GET.Any(game => game.IsMatesFamilyMarker());
                    bool     isOnMatesFamily                   = gamelist.GET.Any(game => game.IsOnMatesFamily());
                    bool     haveMatesFamilyStatus             = gamelist.proxyStatus.ContainsKey("masterserver.matesfamily.org");
                    bool     isMatesFamilyUp                   = haveMatesFamilyStatus && gamelist.proxyStatus["masterserver.matesfamily.org"].success == true;
                    string   statusMatesFamily                 = haveMatesFamilyStatus ? gamelist.proxyStatus["masterserver.matesfamily.org"].status : null;
                    DateTime?dateMatesFamily                   = haveMatesFamilyStatus ? gamelist.proxyStatus["masterserver.matesfamily.org"].updated : null;
                    DataGameListServerStatus MatesFamilyStatus = new DataGameListServerStatus()
                    {
                        Name = "MatesFamily (Primary)", Updated = dateMatesFamily
                    };
                    if (isOnMatesFamily || isMatesFamily)
                    {
                        MatesFamilyStatus.Status = EDataGameListServerStatus.Online;
                    }
                    else if (isMatesFamilyUp)
                    {
                        MatesFamilyStatus.Status = EDataGameListServerStatus.NoGames;
                    }
                    else if (!isMatesFamilyUp)
                    {
                        MatesFamilyStatus.Status = EDataGameListServerStatus.Offline;
                    }
                    else
                    {
                        MatesFamilyStatus.Status = EDataGameListServerStatus.Unknown;
                    }

                    data.Header.ServerStatus = new DataGameListServerStatus[] { IonDriverStatus, MatesFamilyStatus };
                }

                data.Games = (await Task.WhenAll(
                                  gamelist.GET
                                  .Where(dr => !dr.IsMarker())
                                  .Select(async raw =>
                {
                    DataGameListGame game = new DataGameListGame();

                    game.Name = raw.Name;
                    game.Image = await GetBZ2GameProperty("shell", raw.MapFile) ?? "http://discord.battlezone.report/resources/logos/nomap.png";

                    game.CurPlayers = raw.CurPlayers;
                    game.MaxPlayers = raw.MaxPlayers;

                    if (raw.Locked)
                    {
                        game.Status = EDataGameListServerGameStatus.Locked;
                    }
                    else if (raw.Passworded)
                    {
                        game.Status = EDataGameListServerGameStatus.Passworded;
                    }
                    else if (!raw.CurPlayers.HasValue || !raw.MaxPlayers.HasValue)
                    {
                        game.Status = EDataGameListServerGameStatus.Unknown;
                    }
                    else
                    {
                        game.Status = EDataGameListServerGameStatus.Open;
                    }

                    game.MapFilename = raw.MapFile;
                    game.Footer = raw.MapFile + @".bzn";

                    if (raw.pong != null && raw.pong.CompressedData != null)
                    {
                        if (raw.pong.CompressedData.MOTD.Length > 0)
                        {
                            game.TopInfo.Add(Format.Sanitize(raw.pong.CompressedData.MOTD));
                        }

                        if (raw.pong.CompressedData.MapURL.Length > 0)
                        {
                            game.TopInfo.Add(Format.Sanitize(raw.pong.CompressedData.MapURL));
                        }
                    }

                    {
                        string name = await GetBZ2GameProperty("name", raw.MapFile);
                        string version = await GetBZ2GameProperty("version", raw.v);
                        string mod = await GetBZ2GameProperty("mod", raw.d);

                        if (string.IsNullOrWhiteSpace(name))
                        {
                            game.Properties.Add(new Tuple <string, string>("Map", $"[{raw.MapFile}]"));
                        }
                        else
                        {
                            game.Properties.Add(new Tuple <string, string>("Map", $"{name}"));
                        }

                        if (string.IsNullOrWhiteSpace(version))
                        {
                            game.Properties.Add(new Tuple <string, string>("Version", $"[{raw.v}]"));
                        }
                        else
                        {
                            game.Properties.Add(new Tuple <string, string>("Version", $"{version}"));
                        }

                        if (string.IsNullOrWhiteSpace(mod))
                        {
                            game.Properties.Add(new Tuple <string, string>("Mod", $"[{raw.d}]"));
                        }
                        else
                        {
                            game.Properties.Add(new Tuple <string, string>("Mod", $"{mod}"));
                        }

                        switch (raw.t)
                        {
                        case "0":
                            game.Properties.Add(new Tuple <string, string>("NAT", $"NONE"));       /// Works with anyone
                            break;

                        case "1":
                            game.Properties.Add(new Tuple <string, string>("NAT", $"FULL CONE"));      /// Accepts any datagrams to a port that has been previously used. Will accept the first datagram from the remote peer.
                            break;

                        case "2":
                            game.Properties.Add(new Tuple <string, string>("NAT", $"ADDRESS RESTRICTED"));      /// Accepts datagrams to a port as long as the datagram source IP address is a system we have already sent to. Will accept the first datagram if both systems send simultaneously. Otherwise, will accept the first datagram after we have sent one datagram.
                            break;

                        case "3":
                            game.Properties.Add(new Tuple <string, string>("NAT", $"PORT RESTRICTED"));       /// Same as address-restricted cone NAT, but we had to send to both the correct remote IP address and correct remote port. The same source address and port to a different destination uses the same mapping.
                            break;

                        case "4":
                            game.Properties.Add(new Tuple <string, string>("NAT", $"SYMMETRIC"));      /// A different port is chosen for every remote destination. The same source address and port to a different destination uses a different mapping. Since the port will be different, the first external punchthrough attempt will fail. For this to work it requires port-prediction (MAX_PREDICTIVE_PORT_RANGE>1) and that the router chooses ports sequentially.
                            break;

                        case "5":
                            game.Properties.Add(new Tuple <string, string>("NAT", $"UNKNOWN"));      /// Hasn't been determined. NATTypeDetectionClient does not use this, but other plugins might
                            break;

                        case "6":
                            game.Properties.Add(new Tuple <string, string>("NAT", $"DETECTION IN PROGRESS"));      /// In progress. NATTypeDetectionClient does not use this, but other plugins might
                            break;

                        case "7":
                            game.Properties.Add(new Tuple <string, string>("NAT", $"SUPPORTS UPNP"));      /// Didn't bother figuring it out, as we support UPNP, so it is equivalent to NAT_TYPE_NONE. NATTypeDetectionClient does not use this, but other plugins might
                            break;

                        default:
                            game.Properties.Add(new Tuple <string, string>("NAT", $"[" + raw.t + "]"));
                            break;
                        }

                        switch (raw.proxySource)
                        {
                        case "masterserver.matesfamily.org":
                            game.Properties.Add(new Tuple <string, string>("List", $"MatesFamily"));
                            break;

                        case "gamelist.kebbz.com":
                            game.Properties.Add(new Tuple <string, string>("List", $"KebbzNet"));
                            break;

                        default:
                            game.Properties.Add(new Tuple <string, string>("List", $"IonDriver"));
                            break;
                        }

                        if (raw.pong != null)
                        {
                            if (raw.pong.CompressedData != null)
                            {
                                int k = 1;
                                int d = 1;
                                int s = 1;
                                bool scoreNeedsSign = false;

                                raw.pong.CompressedData.Players.ForEach(dr =>
                                {
                                    k = Math.Max(k, dr.Kills.ToString().Length);
                                    d = Math.Max(d, dr.Deaths.ToString().Length);
                                    s = Math.Max(s, Math.Abs(dr.Score).ToString().Length);
                                    scoreNeedsSign = scoreNeedsSign || (dr.Score < 0);
                                });

                                raw.pong.CompressedData.Players.ForEach(dr =>
                                {
                                    string scoresign = "0";
                                    if (dr.Score > 0)
                                    {
                                        scoresign = "+";
                                    }
                                    if (dr.Score < 0)
                                    {
                                        scoresign = "-";
                                    }
                                    if (!scoreNeedsSign)
                                    {
                                        scoresign = string.Empty;
                                    }

                                    game.Players.Add(new DataGameListPlayer()
                                    {
                                        Index = dr.Team,
                                        Name = dr.UserName,
                                        PlayerClass = $"{dr.Kills.ToString().PadLeft(k, '0')}/{dr.Deaths.ToString().PadLeft(d, '0')}/{scoresign}{Math.Abs(dr.Score).ToString().PadLeft(s, '0')}",
                                        Url = null
                                    });
                                });

                                game.PlayersHeader = "[T] (K/D/S) Players";
                            }

                            switch (raw.pong.GameType)
                            {
                            case 0:
                                game.Properties.Add(new Tuple <string, string>("Type", $"All"));
                                break;

                            case 1:
                                switch (raw.pong.GameSubType)
                                {
                                case 0:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"DM"));
                                    break;

                                case 1:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"KOTH"));
                                    break;

                                case 2:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"CTF"));
                                    break;

                                case 3:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"Loot"));
                                    break;

                                case 4:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"DM [RESERVED]"));
                                    break;

                                case 5:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"Race"));
                                    break;

                                case 6:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"Race (Vehicle Only)"));
                                    break;

                                case 7:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"DM (Vehicle Only)"));
                                    break;

                                default:
                                    game.Properties.Add(new Tuple <string, string>("Type", $"DM [UNKNOWN]"));
                                    break;
                                }
                                break;

                            case 2:
                                if (raw.pong.TeamsOn && raw.pong.OnlyOneTeam)
                                {
                                    game.Properties.Add(new Tuple <string, string>("Type", $"MPI"));
                                }
                                else
                                {
                                    game.Properties.Add(new Tuple <string, string>("Type", $"Strat"));
                                }
                                break;

                            case 3:
                                game.Properties.Add(new Tuple <string, string>("Type", $"MPI [Invalid]"));
                                break;
                            }

                            switch (raw.pong.ServerInfoMode)
                            {
                            case 1:
                                game.Properties.Add(new Tuple <string, string>("Time", $"Not playing or in shell for {raw.pong.GameTimeMinutes} minutes"));
                                break;

                            case 3:
                                game.Properties.Add(new Tuple <string, string>("Time", $"Playing for {raw.pong.GameTimeMinutes} minutes"));
                                break;
                            }

                            game.Properties.Add(new Tuple <string, string>("TPS", $"{raw.pong.TPS}"));

                            if (raw.pong.MaxPing > 0)
                            {
                                game.Properties.Add(new Tuple <string, string>("MaxPing", $"{raw.pong.MaxPing}"));
                            }

                            if (raw.pong.TimeLimit > 0)
                            {
                                game.Properties.Add(new Tuple <string, string>("TimeLim", $"{raw.pong.TimeLimit}"));
                            }

                            if (raw.pong.KillLimit > 0)
                            {
                                game.Properties.Add(new Tuple <string, string>("KillLim", $"{raw.pong.KillLimit}"));
                            }
                        }
                    }

                    return(game);
                }))).ToArray();

                return(data);
            }
        }