public async Task <GameListData> GetGameList(bool admin)
        {
            using (var http = new HttpClient())
            {
                var res_raw = await http.GetAsync(queryUrl).ConfigureAwait(false);

                var res = await res_raw.Content.ReadAsStringAsync();

                var gamelist = JsonConvert.DeserializeObject <Dictionary <string, Lobby> >(res);

                SessionItem DefaultSession = new SessionItem();
                DefaultSession.Type = GAMELIST_TERMS.TYPE_LISTEN;
                DefaultSession.Attributes.Add(GAMELIST_TERMS.ATTRIBUTE_LISTSERVER, $"Rebellion");

                DataCache Metadata = new DataCache();
                if (res_raw.Content.Headers.LastModified.HasValue)
                {
                    Metadata.AddObjectPath($"{GAMELIST_TERMS.ATTRIBUTE_LISTSERVER}:Rebellion:Timestamp", res_raw.Content.Headers.LastModified.Value.ToUniversalTime().UtcDateTime);
                }

                DataCache DataCache = new DataCache();
                DataCache Mods      = new DataCache();
                DataCache Heroes    = new DataCache();

                List <SessionItem> Sessions = new List <SessionItem>();

                List <Task>   Tasks         = new List <Task>();
                SemaphoreSlim DataCacheLock = new SemaphoreSlim(1);
                SemaphoreSlim ModsLock      = new SemaphoreSlim(1);
                SemaphoreSlim HeroesLock    = new SemaphoreSlim(1);
                SemaphoreSlim SessionsLock  = new SemaphoreSlim(1);

                foreach (var raw in gamelist.Values)
                {
                    if (raw.LobbyType != Lobby.ELobbyType.Game)
                    {
                        continue;
                    }

                    if (raw.isPrivate && !(raw.IsPassworded ?? false))
                    {
                        continue;
                    }

                    Tasks.Add(Task.Run(async() =>
                    {
                        SessionItem game = new SessionItem();

                        game.Name = raw.Name;

                        game.Address["LobbyID"] = $"B{raw.id}";

                        game.PlayerTypes.Add(new PlayerTypeData()
                        {
                            Types = new List <string>()
                            {
                                GAMELIST_TERMS.PLAYERTYPE_PLAYER
                            },
                            Max = raw.PlayerLimit
                        });

                        game.PlayerCount.Add(GAMELIST_TERMS.PLAYERTYPE_PLAYER, raw.userCount);

                        string modID     = (raw.WorkshopID ?? @"0");
                        string mapID     = System.IO.Path.GetFileNameWithoutExtension(raw.MapFile).ToLowerInvariant();
                        game.Level["ID"] = $"{modID}:{mapID}";

                        game.Level["MapFile"] = raw.MapFile;
                        game.Level["CRC32"]   = raw.CRC32;

                        Task <MapData> mapDataTask = mapDataInterface.GetJson($"{mapUrl.TrimEnd('/')}/getdata.php?map={mapID}&mods={modID},0");

                        if (!string.IsNullOrWhiteSpace(raw.WorkshopID) && raw.WorkshopID != "0")
                        {
                            game.Level.Add("Mod", raw.WorkshopID);
                        }

                        if (raw.TimeLimit.HasValue && raw.TimeLimit > 0)
                        {
                            game.Level.AddObjectPath("Attributes:TimeLimit", raw.TimeLimit);
                        }
                        if (raw.KillLimit.HasValue && raw.KillLimit > 0)
                        {
                            game.Level.AddObjectPath("Attributes:KillLimit", raw.KillLimit);
                        }
                        if (raw.Lives.HasValue && raw.Lives.Value > 0)
                        {
                            game.Level.AddObjectPath("Attributes:Lives", raw.Lives.Value);
                        }
                        if (raw.SatelliteEnabled.HasValue)
                        {
                            game.Level.AddObjectPath("Attributes:Satellite", raw.SatelliteEnabled.Value);
                        }
                        if (raw.BarracksEnabled.HasValue)
                        {
                            game.Level.AddObjectPath("Attributes:Barracks", raw.BarracksEnabled.Value);
                        }
                        if (raw.SniperEnabled.HasValue)
                        {
                            game.Level.AddObjectPath("Attributes:Sniper", raw.SniperEnabled.Value);
                        }
                        if (raw.SplinterEnabled.HasValue)
                        {
                            game.Level.AddObjectPath("Attributes:Splinter", raw.SplinterEnabled.Value);
                        }

                        // unlocked in progress games with SyncJoin will trap the user due to a bug, just list as locked
                        if (raw.SyncJoin.HasValue && raw.SyncJoin.Value && (!raw.IsEnded && raw.IsLaunched))
                        {
                            game.Status.Add(GAMELIST_TERMS.STATUS_LOCKED, true);
                        }
                        else
                        {
                            game.Status.Add(GAMELIST_TERMS.STATUS_LOCKED, raw.isLocked);
                        }
                        game.Status.Add(GAMELIST_TERMS.STATUS_PASSWORD, raw.IsPassworded);
                        game.Status.Add(GAMELIST_TERMS.STATUS_STATE, Enum.GetName(typeof(ESessionState), raw.IsEnded ? ESessionState.PostGame : raw.IsLaunched ? ESessionState.InGame : ESessionState.PreGame));

                        foreach (var dr in raw.users.Values)
                        {
                            PlayerItem player = new PlayerItem();

                            player.Name = dr.name;
                            player.Type = GAMELIST_TERMS.PLAYERTYPE_PLAYER;
                            if (admin)
                            {
                                player.Attributes.Add("wanAddress", dr.wanAddress);
                            }
                            player.Attributes.Add("Launched", dr.Launched);
                            if (admin)
                            {
                                player.Attributes.Add("lanAddresses", JArray.FromObject(dr.lanAddresses));
                            }
                            player.Attributes.Add("isAuth", dr.isAuth);

                            if (dr.Team.HasValue)
                            {
                                player.Team    = new PlayerTeam();
                                player.Team.ID = dr.Team.Value.ToString();
                                player.GetIDData("Slot").Add("ID", dr.Team);
                            }

                            //player.Attributes.Add("Vehicle", dr.Vehicle);
                            if (dr.Vehicle != null)
                            {
                                player.Hero    = new PlayerHero();
                                player.Hero.ID = (raw.WorkshopID ?? @"0") + @":" + dr.Vehicle.ToLowerInvariant();
                                player.Hero.Attributes["ODF"] = dr.Vehicle;
                            }

                            if (!string.IsNullOrWhiteSpace(dr.id))
                            {
                                player.GetIDData("BZRNet").Add("ID", dr.id);
                                if (dr.id == raw.owner)
                                {
                                    player.Attributes.Add("IsOwner", true);
                                }
                                switch (dr.id[0])
                                {
                                case 'S':     // dr.authType == "steam"
                                    {
                                        player.GetIDData("Steam").Add("Raw", dr.id.Substring(1));
                                        try
                                        {
                                            ulong playerID = 0;
                                            if (ulong.TryParse(dr.id.Substring(1), out playerID))
                                            {
                                                player.GetIDData("Steam").Add("ID", playerID.ToString());

                                                await DataCacheLock.WaitAsync();
                                                try
                                                {
                                                    if (!DataCache.ContainsPath($"Players:IDs:Steam:{playerID.ToString()}"))
                                                    {
                                                        PlayerSummaryModel playerData = await steamInterface.Users(playerID);
                                                        DataCache.AddObjectPath($"Players:IDs:Steam:{playerID.ToString()}:AvatarUrl", playerData.AvatarFullUrl);
                                                        DataCache.AddObjectPath($"Players:IDs:Steam:{playerID.ToString()}:Nickname", playerData.Nickname);
                                                        DataCache.AddObjectPath($"Players:IDs:Steam:{playerID.ToString()}:ProfileUrl", playerData.ProfileUrl);
                                                    }
                                                }
                                                finally
                                                {
                                                    DataCacheLock.Release();
                                                }
                                            }
                                        }
                                        catch { }
                                    }
                                    break;

                                case 'G':
                                    {
                                        player.GetIDData("Gog").Add("Raw", dr.id.Substring(1));
                                        try
                                        {
                                            ulong playerID = 0;
                                            if (ulong.TryParse(dr.id.Substring(1), out playerID))
                                            {
                                                playerID = GogInterface.CleanGalaxyUserId(playerID);
                                                player.GetIDData("Gog").Add("ID", playerID.ToString());

                                                await DataCacheLock.WaitAsync();
                                                try
                                                {
                                                    if (!DataCache.ContainsPath($"Players:IDs:Gog:{playerID.ToString()}"))
                                                    {
                                                        GogUserData playerData = await gogInterface.Users(playerID);
                                                        DataCache.AddObjectPath($"Players:IDs:Gog:{playerID.ToString()}:AvatarUrl", playerData.Avatar.sdk_img_184 ?? playerData.Avatar.large_2x ?? playerData.Avatar.large);
                                                        DataCache.AddObjectPath($"Players:IDs:Gog:{playerID.ToString()}:Username", playerData.username);
                                                        DataCache.AddObjectPath($"Players:IDs:Gog:{playerID.ToString()}:ProfileUrl", $"https://www.gog.com/u/{playerData.username}");
                                                    }
                                                }
                                                finally
                                                {
                                                    DataCacheLock.Release();
                                                }
                                            }
                                        }
                                        catch { }
                                    }
                                    break;
                                }
                            }

                            game.Players.Add(player);
                        }

                        if (!string.IsNullOrWhiteSpace(raw.clientVersion))
                        {
                            game.Game["Version"] = raw.clientVersion;
                        }
                        else if (!string.IsNullOrWhiteSpace(raw.GameVersion))
                        {
                            game.Game["Version"] = raw.GameVersion;
                        }

                        if (raw.SyncJoin.HasValue)
                        {
                            game.Attributes.Add("SyncJoin", raw.SyncJoin.Value);
                        }
                        if (raw.MetaDataVersion.HasValue)
                        {
                            game.Attributes.Add("MetaDataVersion", raw.MetaDataVersion);
                        }

                        MapData mapData = null;
                        if (mapDataTask != null)
                        {
                            mapData = await mapDataTask;
                        }
                        if (mapData != null)
                        {
                            game.Level["Image"]    = $"{mapUrl.TrimEnd('/')}/{mapData.image ?? "nomap.png"}";
                            game.Level["Name"]     = mapData?.map?.title;
                            game.Level["GameType"] = mapData?.map?.type;
                            if (string.IsNullOrWhiteSpace(mapData?.map?.custom_type))
                            {
                                if (!string.IsNullOrWhiteSpace(mapData?.map?.type))
                                {
                                    switch (mapData?.map?.type)
                                    {
                                    case "D":
                                        game.Level["GameMode"] = "Deathmatch";
                                        break;

                                    case "S":
                                        game.Level["GameMode"] = "Strategy";
                                        break;

                                    case "K":
                                        game.Level["GameMode"] = "King of the Hill";
                                        break;

                                    case "M":
                                        game.Level["GameMode"] = "Mission MPI";
                                        break;

                                    case "A":
                                        game.Level["GameMode"] = "Action MPI";
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                game.Level["GameMode"] = mapData?.map?.custom_type;
                            }

                            await ModsLock.WaitAsync();
                            if (mapData?.mods != null)
                            {
                                foreach (var mod in mapData.mods)
                                {
                                    if (!Mods.ContainsKey(mod.Key))
                                    {
                                        Mods.AddObjectPath($"{mod.Key}:Name", mod.Value?.name ?? mod.Value?.workshop_name);
                                        Mods.AddObjectPath($"{mod.Key}:ID", mod.Key);
                                        if (UInt64.TryParse(mod.Key, out _))
                                        {
                                            Mods.AddObjectPath($"{mod.Key}:Url", $"http://steamcommunity.com/sharedfiles/filedetails/?id={mod.Key}");
                                        }
                                    }
                                }
                            }
                            ModsLock.Release();

                            game.Level["AllowedHeroes"] = new JArray(mapData.map.vehicles.Select(dr => $"{dr}").ToArray());
                            foreach (var vehicle in mapData.vehicles)
                            {
                                await HeroesLock.WaitAsync();
                                try
                                {
                                    if (!Heroes.ContainsPath($"{vehicle.Key.Replace(":", "\\:")}"))
                                    {
                                        Heroes.AddObjectPath($"{vehicle.Key.Replace(":", "\\:")}:Name", vehicle.Value.name);
                                        if (vehicle.Value.description != null)
                                        {
                                            if (vehicle.Value.description.ContainsKey("en"))
                                            {
                                                Heroes.AddObjectPath($"{vehicle.Key.Replace(":", "\\:")}:Description", vehicle.Value.description["en"].content);
                                            }
                                            else if (vehicle.Value.description.ContainsKey("default"))
                                            {
                                                Heroes.AddObjectPath($"{vehicle.Key.Replace(":", "\\:")}:Description", vehicle.Value.description["default"].content);
                                            }
                                        }
                                    }
                                }
                                finally
                                {
                                    HeroesLock.Release();
                                }
                            }

                            foreach (var player in game.Players)
                            {
                                if (player.Hero != null)
                                {
                                    string ODF = player.Hero.Attributes["ODF"]?.Value <string>();
                                    if (!string.IsNullOrWhiteSpace(ODF))
                                    {
                                        string ProperHeroID = mapData.map?.vehicles?.Where(heroData => heroData.EndsWith($":{ODF}")).FirstOrDefault();
                                        if (!string.IsNullOrWhiteSpace(ProperHeroID))
                                        {
                                            player.Hero.ID = ProperHeroID;
                                        }
                                        else
                                        {
                                            ProperHeroID = Heroes.Where(heroData => heroData.Key.EndsWith($":{ODF}")).Select(dr => dr.Key).FirstOrDefault();
                                            if (!string.IsNullOrWhiteSpace(ProperHeroID))
                                            {
                                                player.Hero.ID = ProperHeroID;
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        await SessionsLock.WaitAsync();
                        try
                        {
                            Sessions.Add(game);
                        }
                        finally
                        {
                            SessionsLock.Release();
                        }
                    }));
                }

                Task.WaitAll(Tasks.ToArray());

                return(new GameListData()
                {
                    Metadata = Metadata,
                    SessionDefault = DefaultSession,
                    DataCache = DataCache,
                    Sessions = Sessions,
                    Mods = Mods,
                    Heroes = Heroes,
                    Raw = admin ? res : null,
                });
            }
        }
Exemplo n.º 2
0
        public async Task <(SessionItem, IEnumerable <SessionItem>, JToken)> GetGameList()
        {
            using (var http = new HttpClient())
            {
                var res = await http.GetStringAsync(queryUrl).ConfigureAwait(false);

                var gamelist = JsonConvert.DeserializeObject <BZCCRaknetData>(res);

                SessionItem DefaultSession = new SessionItem()
                {
                    Type = "listen",
                    SpectatorPossible = false, // unless we add special mod support
                    //SpectatorSeperate = false,
                };

                List <SessionItem> Sessions = new List <SessionItem>();

                foreach (var raw in gamelist.GET)
                {
                    SessionItem game = new SessionItem();

                    game.Name = raw.Name;
                    if (!string.IsNullOrWhiteSpace(raw.MOTD))
                    {
                        game.Message = raw.MOTD;
                    }

                    game.PlayerCount = raw.CurPlayers;
                    game.PlayerMax   = raw.MaxPlayers;

                    game.Level.Add("MapFile", raw.MapFile);
                    game.Level.Add("MapID", GameID + @":" + (raw.Mods?.FirstOrDefault() ?? @"0") + @":" + raw.MapFile);

                    game.Status.Add("Locked", raw.Locked);
                    game.Status.Add("Passworded", raw.Passworded);


                    if (raw.ServerInfoMode.HasValue)
                    {
                        switch (raw.ServerInfoMode)
                        {
                        case 1:
                            game.Status.Add("State", @"Lobby");
                            break;

                        case 2:
                            game.Status.Add("State", @"Loading");     // guess
                            break;

                        case 3:
                            game.Status.Add("State", @"InGame");
                            break;

                        case 4:
                            game.Status.Add("State", @"Over");     // guess
                            break;
                        }
                    }


                    if ((raw.Mods?.Length ?? 0) > 0)
                    {
                        game.Attributes.Add("Mods", JArray.FromObject(raw.Mods));
                    }

                    if (!string.IsNullOrWhiteSpace(raw.v))
                    {
                        game.Attributes.Add("Version", raw.v);
                    }

                    if (raw.TPS.HasValue && raw.TPS > 0)
                    {
                        game.Attributes.Add("TPS", raw.TPS);
                    }

                    if (raw.MaxPing.HasValue && raw.MaxPing > 0)
                    {
                        game.Attributes.Add("MaxPing", raw.MaxPing);
                    }

                    if (raw.TimeLimit.HasValue && raw.TimeLimit > 0)
                    {
                        game.Attributes.Add("TimeLimit", raw.TimeLimit);
                    }

                    if (raw.KillLimit.HasValue && raw.KillLimit > 0)
                    {
                        game.Attributes.Add("KillLimit", raw.KillLimit);
                    }

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

                        case "1":
                            game.Attributes.Add("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.Attributes.Add("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.Attributes.Add("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.Attributes.Add("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.Attributes.Add("NAT", $"UNKNOWN");     /// Hasn't been determined. NATTypeDetectionClient does not use this, but other plugins might
                            break;

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

                        case "7":
                            game.Attributes.Add("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.Attributes.Add("NAT", $"[" + raw.t + "]");
                            break;
                        }
                    }

                    switch (raw.proxySource)
                    {
                    case "Rebellion":
                        game.Attributes.Add("List", $"Rebellion");
                        break;

                    default:
                        game.Attributes.Add("List", $"IonDriver");
                        break;
                    }

                    bool m_TeamsOn     = false;
                    bool m_OnlyOneTeam = false;
                    switch (raw.GameType)
                    {
                    case 0:
                        game.Attributes.Add("Type", $"All");
                        break;

                    case 1:
                    {
                        int  GetGameModeOutput = raw.GameSubType.Value % (int)GameMode.GAMEMODE_MAX; // extract if we are team or not
                        int  detailed          = raw.GameSubType.Value / (int)GameMode.GAMEMODE_MAX; // ivar7
                        bool RespawnSameRace   = (detailed & 256) == 256;
                        bool RespawnAnyRace    = (detailed & 512) == 512;
                        game.Attributes.Add("Respawn", RespawnSameRace ? "Race" : RespawnAnyRace ? "Any" : "One");
                        detailed = (detailed & 0xff);
                        switch ((GameMode)GetGameModeOutput)
                        {
                        case GameMode.GAMEMODE_TEAM_DM:
                        case GameMode.GAMEMODE_TEAM_KOTH:
                        case GameMode.GAMEMODE_TEAM_CTF:
                        case GameMode.GAMEMODE_TEAM_LOOT:
                        case GameMode.GAMEMODE_TEAM_RACE:
                            m_TeamsOn = true;
                            break;

                        case GameMode.GAMEMODE_DM:
                        case GameMode.GAMEMODE_KOTH:
                        case GameMode.GAMEMODE_CTF:
                        case GameMode.GAMEMODE_LOOT:
                        case GameMode.GAMEMODE_RACE:
                        default:
                            m_TeamsOn = false;
                            break;
                        }

                        switch (detailed)         // first byte of ivar7?  might be all of ivar7 // Deathmatch subtype (0 = normal; 1 = KOH; 2 = CTF; add 256 for random respawn on same race, or add 512 for random respawn w/o regard to race)
                        {
                        case 0:
                            game.Level.Add("GameMode", (m_TeamsOn ? "TEAM " : String.Empty) + $"DM");
                            break;

                        case 1:
                            game.Level.Add("GameMode", (m_TeamsOn ? "TEAM " : String.Empty) + $"KOTH");
                            break;

                        case 2:
                            game.Level.Add("GameMode", (m_TeamsOn ? "TEAM " : String.Empty) + $"CTF");
                            break;

                        case 3:
                            game.Level.Add("GameMode", (m_TeamsOn ? "TEAM " : String.Empty) + $"Loot");
                            break;

                        case 4:
                            game.Level.Add("GameMode", (m_TeamsOn ? "TEAM " : String.Empty) + $"DM [RESERVED]");
                            break;

                        case 5:
                            game.Level.Add("GameMode", (m_TeamsOn ? "TEAM " : String.Empty) + $"Race");
                            break;

                        case 6:
                            game.Level.Add("GameMode", (m_TeamsOn ? "TEAM " : String.Empty) + $"Race (Vehicle Only)");
                            break;

                        case 7:
                            game.Level.Add("GameMode", (m_TeamsOn ? "TEAM " : String.Empty) + $"DM (Vehicle Only)");
                            break;

                        default:
                            game.Level.Add("GameMode", (m_TeamsOn ? "TEAM " : String.Empty) + $"DM [UNKNOWN {raw.GameSubType}]");
                            break;
                        }
                    }
                    break;

                    case 2:
                    {
                        int GetGameModeOutput = raw.GameSubType.Value % (int)GameMode.GAMEMODE_MAX;         // extract if we are team or not
                        switch ((GameMode)GetGameModeOutput)
                        {
                        case GameMode.GAMEMODE_TEAM_STRAT:
                            game.Level.Add("GameMode", $"TEAM STRAT");
                            m_TeamsOn     = true;
                            m_OnlyOneTeam = false;
                            break;

                        case GameMode.GAMEMODE_STRAT:
                            game.Level.Add("GameMode", $"STRAT");
                            m_TeamsOn     = false;
                            m_OnlyOneTeam = false;
                            break;

                        case GameMode.GAMEMODE_MPI:
                            game.Level.Add("GameMode", $"MPI");
                            m_TeamsOn     = true;
                            m_OnlyOneTeam = true;
                            break;

                        default:
                            game.Level.Add("GameMode", $"STRAT [UNKNOWN {GetGameModeOutput}]");
                            break;
                        }
                    }
                    break;

                    case 3:     // impossible, BZCC limits to 0-2
                        game.Attributes.Add("Type", $"MPI [Invalid]");
                        break;
                    }

                    if (!string.IsNullOrWhiteSpace(raw.d))
                    {
                        game.Attributes.Add("ModHash", raw.d); // base64 encoded CRC32
                    }

                    foreach (var dr in raw.pl)
                    {
                        PlayerItem player = new PlayerItem();

                        player.Name = dr.Name;

                        if ((dr.Team ?? 255) != 255) // 255 means not on a team yet? could be understood as -1
                        {
                            player.Team = new PlayerTeam();
                            if (m_TeamsOn)
                            {
                                if (!m_OnlyOneTeam)
                                {
                                    if (dr.Team >= 1 && dr.Team <= 5)
                                    {
                                        player.Team.ID = 1;
                                    }
                                    if (dr.Team >= 6 && dr.Team <= 10)
                                    {
                                        player.Team.ID = 2;
                                    }
                                    if (dr.Team == 1 || dr.Team == 6)
                                    {
                                        player.Team.Leader = true;
                                    }
                                }
                            }
                            player.Team.SubTeam = new PlayerTeam()
                            {
                                ID = dr.Team.Value
                            };
                            player.GetIDData("Slot").Add("ID", dr.Team);
                        }

                        if (dr.Kills.HasValue)
                        {
                            player.Stats.Add("Kills", dr.Kills);
                        }
                        if (dr.Deaths.HasValue)
                        {
                            player.Stats.Add("Deaths", dr.Deaths);
                        }
                        if (dr.Score.HasValue)
                        {
                            player.Stats.Add("Score", dr.Score);
                        }

                        if (!string.IsNullOrWhiteSpace(dr.PlayerID))
                        {
                            player.GetIDData("BZRNet").Add("ID", dr.PlayerID);
                            switch (dr.PlayerID[0])
                            {
                            case 'S':
                            {
                                player.GetIDData("Steam").Add("Raw", dr.PlayerID.Substring(1));
                                try
                                {
                                    ulong playerID = 0;
                                    if (ulong.TryParse(dr.PlayerID.Substring(1), out playerID))
                                    {
                                        player.GetIDData("Steam").Add("ID", playerID);

                                        PlayerSummaryModel playerData = await steamInterface.Users(playerID);

                                        player.GetIDData("Steam").Add("AvatarUrl", playerData.AvatarFullUrl);
                                        player.GetIDData("Steam").Add("Nickname", playerData.Nickname);
                                        player.GetIDData("Steam").Add("ProfileUrl", playerData.ProfileUrl);
                                    }
                                }
                                catch { }
                            }
                            break;

                            case 'G':
                            {
                                player.GetIDData("Gog").Add("Raw", dr.PlayerID.Substring(1));
                                try
                                {
                                    ulong playerID = 0;
                                    if (ulong.TryParse(dr.PlayerID.Substring(1), out playerID))
                                    {
                                        //player.GetIDData("Gog").Add("LargeID", playerID);
                                        playerID &= 0x00ffffffffffffff;
                                        player.GetIDData("Gog").Add("ID", playerID);

                                        GogUserData playerData = await gogInterface.Users(playerID);

                                        player.GetIDData("Gog").Add("AvatarUrl", playerData.Avatar.sdk_img_184 ?? playerData.Avatar.large_2x ?? playerData.Avatar.large);
                                        player.GetIDData("Gog").Add("UserName", playerData.username);
                                        player.GetIDData("Gog").Add("ProfileUrl", $"https://www.gog.com/u/{playerData.username}");
                                    }
                                }
                                catch { }
                            }
                            break;
                            }
                        }

                        game.Players.Add(player);
                    }

                    if (raw.GameTimeMinutes.HasValue)
                    {
                        if (raw.GameTimeMinutes.Value == 255) // 255 appears to mean it maxed out?  Does for currently playing.
                        {
                            game.Attributes.Add("GameTimeMinutes", "255+");
                        }
                        else
                        {
                            game.Attributes.Add("GameTimeMinutes", raw.GameTimeMinutes);
                        }
                    }

                    Sessions.Add(game);
                }

                return(DefaultSession, Sessions, JObject.Parse(res));
            }
        }
Exemplo n.º 3
0
        public async Task <GameListData> GetGameList(bool admin)
        {
            using (var http = new HttpClient())
            {
                var res = await http.GetStringAsync(queryUrl).ConfigureAwait(false);

                var gamelist = JsonConvert.DeserializeObject <BZCCRaknetData>(res);

                SessionItem DefaultSession = new SessionItem();
                DefaultSession.Type = GAMELIST_TERMS.TYPE_LISTEN;

                DataCache Metadata = new DataCache();

                foreach (var proxyStatus in gamelist.proxyStatus)
                {
                    Metadata.AddObjectPath($"{GAMELIST_TERMS.ATTRIBUTE_LISTSERVER}:{proxyStatus.Key}:Status", proxyStatus.Value.status);
                    Metadata.AddObjectPath($"{GAMELIST_TERMS.ATTRIBUTE_LISTSERVER}:{proxyStatus.Key}:Success", proxyStatus.Value.success);
                    Metadata.AddObjectPath($"{GAMELIST_TERMS.ATTRIBUTE_LISTSERVER}:{proxyStatus.Key}:Timestamp", proxyStatus.Value.updated);
                }

                DataCache DataCache = new DataCache();
                DataCache Mods      = new DataCache();

                List <SessionItem> Sessions = new List <SessionItem>();

                List <Task>   Tasks         = new List <Task>();
                SemaphoreSlim DataCacheLock = new SemaphoreSlim(1);
                SemaphoreSlim ModsLock      = new SemaphoreSlim(1);
                SemaphoreSlim SessionsLock  = new SemaphoreSlim(1);

                foreach (var raw in gamelist.GET)
                {
                    Tasks.Add(Task.Run(async() =>
                    {
                        SessionItem game = new SessionItem();

                        game.Address["NAT"] = raw.g;
                        //if (!raw.Passworded)
                        //{
                        //    game.Address["Rich"] = string.Join(null, $"N,{raw.Name.Length},{raw.Name},{raw.mm.Length},{raw.mm},{raw.g},0,".Select(dr => $"{((int)dr):x2}"));
                        //}

                        game.Name = raw.Name;
                        if (!string.IsNullOrWhiteSpace(raw.MOTD))
                        {
                            game.Message = raw.MOTD;
                        }

                        game.PlayerTypes.Add(new PlayerTypeData()
                        {
                            Types = new List <string>()
                            {
                                GAMELIST_TERMS.PLAYERTYPE_PLAYER
                            },
                            Max = raw.MaxPlayers
                        });

                        game.PlayerCount.Add(GAMELIST_TERMS.PLAYERTYPE_PLAYER, raw.CurPlayers);

                        if (!string.IsNullOrWhiteSpace(raw.MapFile))
                        {
                            game.Level["MapFile"] = raw.MapFile + @".bzn";
                        }
                        string modID     = (raw.Mods?.FirstOrDefault() ?? @"0");
                        string mapID     = raw.MapFile?.ToLowerInvariant();
                        game.Level["ID"] = $"{modID}:{mapID}";

                        Task <MapData> mapDataTask = null;
                        if (!string.IsNullOrWhiteSpace(raw.MapFile))
                        {
                            mapDataTask = mapDataInterface.GetJson($"{mapUrl.TrimEnd('/')}/getdata.php?map={mapID}&mod={modID}");
                        }

                        game.Status.Add(GAMELIST_TERMS.STATUS_LOCKED, raw.Locked);
                        game.Status.Add(GAMELIST_TERMS.STATUS_PASSWORD, raw.Passworded);

                        string ServerState = null;
                        if (raw.ServerInfoMode.HasValue)
                        {
                            switch (raw.ServerInfoMode)
                            {
                            case 0:     // ServerInfoMode_Unknown
                                ServerState = Enum.GetName(typeof(ESessionState), ESessionState.Unknown);
                                break;

                            case 1:     // ServerInfoMode_OpenWaiting
                            case 2:     // ServerInfoMode_ClosedWaiting (full)
                                ServerState = Enum.GetName(typeof(ESessionState), ESessionState.PreGame);
                                break;

                            case 3:     // ServerInfoMode_OpenPlaying
                            case 4:     // ServerInfoMode_ClosedPlaying (full)
                                ServerState = Enum.GetName(typeof(ESessionState), ESessionState.InGame);
                                break;

                            case 5:     // ServerInfoMode_Exiting
                                ServerState = Enum.GetName(typeof(ESessionState), ESessionState.PostGame);
                                break;
                            }
                        }
                        if (!string.IsNullOrWhiteSpace(ServerState))
                        {
                            game.Status.Add("State", ServerState);
                        }

                        int ModsLen = (raw.Mods?.Length ?? 0);
                        if (ModsLen > 0 && raw.Mods[0] != "0")
                        {
                            game.Game.Add("Mod", raw.Mods[0]);
                        }
                        if (ModsLen > 1)
                        {
                            game.Game.Add("Mods", JArray.FromObject(raw.Mods.Skip(1)));
                        }

                        if (!string.IsNullOrWhiteSpace(raw.v))
                        {
                            game.Game["Version"] = raw.v;
                        }

                        if (raw.TPS.HasValue && raw.TPS > 0)
                        {
                            game.Attributes.Add("TPS", raw.TPS);
                        }

                        if (raw.MaxPing.HasValue && raw.MaxPing > 0)
                        {
                            game.Attributes.Add("MaxPing", raw.MaxPing);
                        }


                        if (raw.TimeLimit.HasValue && raw.TimeLimit > 0)
                        {
                            game.Level.AddObjectPath("Attributes:TimeLimit", raw.TimeLimit);
                        }
                        if (raw.KillLimit.HasValue && raw.KillLimit > 0)
                        {
                            game.Level.AddObjectPath("Attributes:KillLimit", raw.KillLimit);
                        }

                        if (!string.IsNullOrWhiteSpace(raw.t))
                        {
                            switch (raw.t)
                            {
                            case "0":
                                game.Address.Add("NAT_TYPE", $"NONE");     /// Works with anyone
                                break;

                            case "1":
                                game.Address.Add("NAT_TYPE", $"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.Address.Add("NAT_TYPE", $"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.Address.Add("NAT_TYPE", $"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.Address.Add("NAT_TYPE", $"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.Address.Add("NAT_TYPE", $"UNKNOWN");     /// Hasn't been determined. NATTypeDetectionClient does not use this, but other plugins might
                                break;

                            case "6":
                                game.Address.Add("NAT_TYPE", $"DETECTION IN PROGRESS");     /// In progress. NATTypeDetectionClient does not use this, but other plugins might
                                break;

                            case "7":
                                game.Address.Add("NAT_TYPE", $"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.Address.Add("NAT_TYPE", $"[" + raw.t + "]");
                                break;
                            }
                        }

                        if (string.IsNullOrWhiteSpace(raw.proxySource))
                        {
                            game.Attributes.Add(GAMELIST_TERMS.ATTRIBUTE_LISTSERVER, $"IonDriver");
                        }
                        else
                        {
                            game.Attributes.Add(GAMELIST_TERMS.ATTRIBUTE_LISTSERVER, raw.proxySource);
                        }

                        bool m_TeamsOn     = false;
                        bool m_OnlyOneTeam = false;
                        switch (raw.GameType)
                        {
                        case 0:
                            game.Level["GameType"] = $"All";
                            break;

                        case 1:
                            {
                                int GetGameModeOutput = raw.GameSubType.Value % (int)GameMode.GAMEMODE_MAX; // extract if we are team or not
                                int detailed          = raw.GameSubType.Value / (int)GameMode.GAMEMODE_MAX; // ivar7
                                bool RespawnSameRace  = (detailed & 256) == 256;
                                bool RespawnAnyRace   = (detailed & 512) == 512;
                                game.Level.AddObjectPath("Attributes:Respawn", RespawnSameRace ? "Race" : RespawnAnyRace ? "Any" : "One");
                                detailed = (detailed & 0xff);
                                switch ((GameMode)GetGameModeOutput)
                                {
                                case GameMode.GAMEMODE_TEAM_DM:
                                case GameMode.GAMEMODE_TEAM_KOTH:
                                case GameMode.GAMEMODE_TEAM_CTF:
                                case GameMode.GAMEMODE_TEAM_LOOT:
                                case GameMode.GAMEMODE_TEAM_RACE:
                                    m_TeamsOn = true;
                                    break;

                                case GameMode.GAMEMODE_DM:
                                case GameMode.GAMEMODE_KOTH:
                                case GameMode.GAMEMODE_CTF:
                                case GameMode.GAMEMODE_LOOT:
                                case GameMode.GAMEMODE_RACE:
                                default:
                                    m_TeamsOn = false;
                                    break;
                                }

                                switch (detailed)     // first byte of ivar7?  might be all of ivar7 // Deathmatch subtype (0 = normal; 1 = KOH; 2 = CTF; add 256 for random respawn on same race, or add 512 for random respawn w/o regard to race)
                                {
                                case 0:
                                    game.Level["GameType"] = "DM";
                                    game.Level["GameMode"] = (m_TeamsOn ? "Team " : String.Empty) + "Deathmatch";
                                    break;

                                case 1:
                                    game.Level["GameType"] = "KOTH";
                                    game.Level["GameMode"] = (m_TeamsOn ? "Team " : String.Empty) + "King of the Hill";
                                    break;

                                case 2:
                                    game.Level["GameType"] = "CTF";
                                    game.Level["GameMode"] = (m_TeamsOn ? "Team " : String.Empty) + "Capture the Flag";
                                    break;

                                case 3:
                                    game.Level["GameType"] = "Loot";
                                    game.Level["GameMode"] = (m_TeamsOn ? "Team " : String.Empty) + "Loot";
                                    break;

                                case 4:         // DM [RESERVED]
                                    game.Level["GameType"] = "DM";
                                    break;

                                case 5:
                                    game.Level["GameType"] = "Race";
                                    game.Level["GameMode"] = (m_TeamsOn ? "Team " : String.Empty) + "Race";
                                    break;

                                case 6:         // Race (Vehicle Only)
                                    game.Level["GameType"] = "Race";
                                    game.Level["GameMode"] = (m_TeamsOn ? "Team " : String.Empty) + "Race";
                                    game.Level.AddObjectPath("Attributes:VehicleOnly", true);
                                    break;

                                case 7:         // DM (Vehicle Only)
                                    game.Level["GameType"] = "DM";
                                    game.Level["GameMode"] = (m_TeamsOn ? "Team " : String.Empty) + "Deathmatch";
                                    game.Level.AddObjectPath("Attributes:VehicleOnly", true);
                                    break;

                                default:
                                    game.Level["GameType"] = "DM";
                                    //game.Level["GameMode"] = (m_TeamsOn ? "TEAM " : String.Empty) + "DM [UNKNOWN {raw.GameSubType}]";
                                    break;
                                }
                            }
                            break;

                        case 2:
                            {
                                int GetGameModeOutput = raw.GameSubType.Value % (int)GameMode.GAMEMODE_MAX;     // extract if we are team or not
                                switch ((GameMode)GetGameModeOutput)
                                {
                                case GameMode.GAMEMODE_TEAM_STRAT:
                                    game.Level["GameType"] = "STRAT";
                                    game.Level["GameMode"] = "STRAT";
                                    m_TeamsOn     = true;
                                    m_OnlyOneTeam = false;
                                    break;

                                case GameMode.GAMEMODE_STRAT:
                                    game.Level["GameType"] = "STRAT";
                                    game.Level["GameMode"] = "FFA";
                                    m_TeamsOn     = false;
                                    m_OnlyOneTeam = false;
                                    break;

                                case GameMode.GAMEMODE_MPI:
                                    game.Level["GameType"] = "MPI";
                                    game.Level["GameMode"] = "MPI";
                                    m_TeamsOn     = true;
                                    m_OnlyOneTeam = true;
                                    break;

                                default:
                                    //game.Level["GameType"] = $"STRAT [UNKNOWN {GetGameModeOutput}]";
                                    game.Level["GameType"] = "STRAT";
                                    //game.Level["GameMode"] = null;
                                    break;
                                }
                            }
                            break;

                        case 3:                             // impossible, BZCC limits to 0-2
                            game.Level["GameType"] = "MPI"; //  "MPI [Invalid]";
                            break;
                        }

                        if (!string.IsNullOrWhiteSpace(raw.d))
                        {
                            game.Game.Add("ModHash", raw.d); // base64 encoded CRC32
                        }

                        foreach (var dr in raw.pl)
                        {
                            PlayerItem player = new PlayerItem();

                            player.Name = dr.Name;
                            player.Type = GAMELIST_TERMS.PLAYERTYPE_PLAYER;

                            if ((dr.Team ?? 255) != 255) // 255 means not on a team yet? could be understood as -1
                            {
                                player.Team = new PlayerTeam();
                                if (m_TeamsOn)
                                {
                                    if (!m_OnlyOneTeam)
                                    {
                                        if (dr.Team >= 1 && dr.Team <= 5)
                                        {
                                            player.Team.ID = "1";
                                        }
                                        if (dr.Team >= 6 && dr.Team <= 10)
                                        {
                                            player.Team.ID = "2";
                                        }
                                        if (dr.Team == 1 || dr.Team == 6)
                                        {
                                            player.Team.Leader = true;
                                        }
                                    }
                                }
                                player.Team.SubTeam = new PlayerTeam()
                                {
                                    ID = dr.Team.Value.ToString()
                                };
                                player.GetIDData("Slot").Add("ID", dr.Team);
                            }

                            if (dr.Kills.HasValue)
                            {
                                player.Stats.Add("Kills", dr.Kills);
                            }
                            if (dr.Deaths.HasValue)
                            {
                                player.Stats.Add("Deaths", dr.Deaths);
                            }
                            if (dr.Score.HasValue)
                            {
                                player.Stats.Add("Score", dr.Score);
                            }

                            if (!string.IsNullOrWhiteSpace(dr.PlayerID))
                            {
                                player.GetIDData("BZRNet").Add("ID", dr.PlayerID);
                                switch (dr.PlayerID[0])
                                {
                                case 'S':
                                    {
                                        player.GetIDData("Steam").Add("Raw", dr.PlayerID.Substring(1));
                                        try
                                        {
                                            ulong playerID = 0;
                                            if (ulong.TryParse(dr.PlayerID.Substring(1), out playerID))
                                            {
                                                player.GetIDData("Steam").Add("ID", playerID.ToString());

                                                await DataCacheLock.WaitAsync();
                                                try
                                                {
                                                    if (!DataCache.ContainsPath($"Players:IDs:Steam:{playerID.ToString()}"))
                                                    {
                                                        PlayerSummaryModel playerData = await steamInterface.Users(playerID);
                                                        DataCache.AddObjectPath($"Players:IDs:Steam:{playerID.ToString()}:AvatarUrl", playerData.AvatarFullUrl);
                                                        DataCache.AddObjectPath($"Players:IDs:Steam:{playerID.ToString()}:Nickname", playerData.Nickname);
                                                        DataCache.AddObjectPath($"Players:IDs:Steam:{playerID.ToString()}:ProfileUrl", playerData.ProfileUrl);
                                                    }
                                                }
                                                finally
                                                {
                                                    DataCacheLock.Release();
                                                }
                                            }
                                        }
                                        catch { }
                                    }
                                    break;

                                case 'G':
                                    {
                                        player.GetIDData("Gog").Add("Raw", dr.PlayerID.Substring(1));
                                        try
                                        {
                                            ulong playerID = 0;
                                            if (ulong.TryParse(dr.PlayerID.Substring(1), out playerID))
                                            {
                                                playerID = GogInterface.CleanGalaxyUserId(playerID);
                                                player.GetIDData("Gog").Add("ID", playerID.ToString());

                                                await DataCacheLock.WaitAsync();
                                                try
                                                {
                                                    if (!DataCache.ContainsPath($"Players:IDs:Gog:{playerID.ToString()}"))
                                                    {
                                                        GogUserData playerData = await gogInterface.Users(playerID);
                                                        DataCache.AddObjectPath($"Players:IDs:Gog:{playerID.ToString()}:AvatarUrl", playerData.Avatar.sdk_img_184 ?? playerData.Avatar.large_2x ?? playerData.Avatar.large);
                                                        DataCache.AddObjectPath($"Players:IDs:Gog:{playerID.ToString()}:Username", playerData.username);
                                                        DataCache.AddObjectPath($"Players:IDs:Gog:{playerID.ToString()}:ProfileUrl", $"https://www.gog.com/u/{playerData.username}");
                                                    }
                                                }
                                                finally
                                                {
                                                    DataCacheLock.Release();
                                                }
                                            }
                                        }
                                        catch { }
                                    }
                                    break;
                                }
                            }

                            game.Players.Add(player);
                        }

                        if (raw.GameTimeMinutes.HasValue)
                        {
                            game.Time.AddObjectPath("Seconds", raw.GameTimeMinutes * 60);
                            game.Time.AddObjectPath("Resolution", 60);
                            game.Time.AddObjectPath("Max", raw.GameTimeMinutes.Value == 255); // 255 appears to mean it maxed out?  Does for currently playing.
                            if (!string.IsNullOrWhiteSpace(ServerState))
                            {
                                game.Time.AddObjectPath("Context", ServerState);
                            }
                        }

                        MapData mapData = null;
                        if (mapDataTask != null)
                        {
                            mapData = await mapDataTask;
                        }
                        if (mapData != null)
                        {
                            game.Level["Image"]       = $"{mapUrl.TrimEnd('/')}/{mapData.image ?? "nomap.png"}";
                            game.Level["Name"]        = mapData?.title;
                            game.Level["Description"] = mapData?.description;
                            //game.Level.AddObjectPath("Attributes:Vehicles", new JArray(mapData.map.vehicles.Select(dr => $"{modID}:{dr}").ToArray()));

                            await ModsLock.WaitAsync();
                            if (mapData?.mods != null)
                            {
                                foreach (var mod in mapData.mods)
                                {
                                    if (!Mods.ContainsKey(mod.Key))
                                    {
                                        Mods.AddObjectPath($"{mod.Key}:Name", mod.Value?.name ?? mod.Value?.workshop_name);
                                        Mods.AddObjectPath($"{mod.Key}:ID", mod.Key);
                                        if (UInt64.TryParse(mod.Key, out _))
                                        {
                                            Mods.AddObjectPath($"{mod.Key}:Url", $"http://steamcommunity.com/sharedfiles/filedetails/?id={mod.Key}");
                                        }
                                    }
                                }
                            }
                            ModsLock.Release();
                        }

                        await SessionsLock.WaitAsync();
                        try
                        {
                            Sessions.Add(game);
                        }
                        finally
                        {
                            SessionsLock.Release();
                        }
                    }));
                }

                Task.WaitAll(Tasks.ToArray());

                return(new GameListData()
                {
                    Metadata = Metadata,
                    SessionDefault = DefaultSession,
                    DataCache = DataCache,
                    Sessions = Sessions,
                    Mods = Mods,
                    Raw = admin ? res : null,
                });
            }
        }
Exemplo n.º 4
0
        public async Task <(SessionItem, IEnumerable <SessionItem>, JToken)> GetGameList()
        {
            using (var http = new HttpClient())
            {
                var res = await http.GetStringAsync(queryUrl).ConfigureAwait(false);

                var gamelist = JsonConvert.DeserializeObject <Dictionary <string, Lobby> >(res);

                SessionItem DefaultSession = new SessionItem()
                {
                    Type = "listen",
                    SpectatorPossible = false, // unless we add special mod support
                    //SpectatorSeperate = false,
                };

                List <SessionItem> Sessions = new List <SessionItem>();

                foreach (var raw in gamelist.Values)
                {
                    if (raw.LobbyType != Lobby.ELobbyType.Game)
                    {
                        continue;
                    }

                    SessionItem game = new SessionItem();

                    game.Name = raw.Name;
                    //if (!string.IsNullOrWhiteSpace(raw.MOTD))
                    //    game.Message = raw.MOTD;

                    game.PlayerCount = raw.userCount;
                    game.PlayerMax   = raw.PlayerLimit;

                    game.Level.Add("MapFile", raw.MapFile);
                    game.Level.Add("MapID", GameID + @":" + (raw.WorkshopID ?? @"0") + @":" + raw.MapFile);

                    game.Status.Add("Locked", raw.isLocked);
                    game.Status.Add("Passworded", raw.IsPassworded);

                    game.Status.Add("State", raw.IsEnded ? "Over" : raw.IsLaunched ? "InGame" : "Lobby");

                    if (!string.IsNullOrWhiteSpace(raw.WorkshopID))
                    {
                        game.Attributes.Add("Mod", raw.WorkshopID);
                    }

                    if (!string.IsNullOrWhiteSpace(raw.clientVersion))
                    {
                        game.Attributes.Add("Version", raw.clientVersion);
                    }

                    if (raw.TimeLimit.HasValue && raw.TimeLimit > 0)
                    {
                        game.Attributes.Add("TimeLimit", raw.TimeLimit);
                    }

                    if (raw.KillLimit.HasValue && raw.KillLimit > 0)
                    {
                        game.Attributes.Add("KillLimit", raw.KillLimit);
                    }

                    if (raw.Lives.HasValue && raw.Lives.Value > 0)
                    {
                        game.Attributes.Add("Lives", raw.Lives.Value);
                    }
                    if (raw.SyncJoin.HasValue)
                    {
                        game.Attributes.Add("SyncJoin", raw.SyncJoin.Value);
                    }
                    if (raw.SatelliteEnabled.HasValue)
                    {
                        game.Attributes.Add("Satellite", raw.SatelliteEnabled.Value);
                    }
                    if (raw.BarracksEnabled.HasValue)
                    {
                        game.Attributes.Add("Barracks", raw.BarracksEnabled.Value);
                    }
                    if (raw.SniperEnabled.HasValue)
                    {
                        game.Attributes.Add("Sniper", raw.SniperEnabled.Value);
                    }
                    if (raw.SplinterEnabled.HasValue)
                    {
                        game.Attributes.Add("Splinter", raw.SplinterEnabled.Value);
                    }

                    foreach (var dr in raw.users.Values)
                    {
                        PlayerItem player = new PlayerItem();

                        player.Name = dr.name;

                        if (dr.Team.HasValue)
                        {
                            player.Team    = new PlayerTeam();
                            player.Team.ID = dr.Team;
                            player.GetIDData("Slot").Add("ID", dr.Team);
                        }

                        player.Attributes.Add("Vehicle", dr.Vehicle);

                        if (!string.IsNullOrWhiteSpace(dr.id))
                        {
                            player.GetIDData("BZRNet").Add("ID", dr.id);
                            switch (dr.id[0])
                            {
                            case 'S':     // dr.authType == "steam"
                            {
                                player.GetIDData("Steam").Add("Raw", dr.id.Substring(1));
                                try
                                {
                                    ulong playerID = 0;
                                    if (ulong.TryParse(dr.id.Substring(1), out playerID))
                                    {
                                        player.GetIDData("Steam").Add("ID", playerID);

                                        PlayerSummaryModel playerData = await steamInterface.Users(playerID);

                                        player.GetIDData("Steam").Add("AvatarUrl", playerData.AvatarFullUrl);
                                        player.GetIDData("Steam").Add("Nickname", playerData.Nickname);
                                        player.GetIDData("Steam").Add("ProfileUrl", playerData.ProfileUrl);
                                    }
                                }
                                catch { }
                            }
                            break;

                            case 'G':
                            {
                                player.GetIDData("Gog").Add("Raw", dr.id.Substring(1));
                                try
                                {
                                    ulong playerID = 0;
                                    if (ulong.TryParse(dr.id.Substring(1), out playerID))
                                    {
                                        //player.GetIDData("Gog").Add("LargeID", playerID);
                                        playerID &= 0x00ffffffffffffff;
                                        player.GetIDData("Gog").Add("ID", playerID);

                                        GogUserData playerData = await gogInterface.Users(playerID);

                                        player.GetIDData("Gog").Add("AvatarUrl", playerData.Avatar.sdk_img_184 ?? playerData.Avatar.large_2x ?? playerData.Avatar.large);
                                        player.GetIDData("Gog").Add("UserName", playerData.username);
                                        player.GetIDData("Gog").Add("ProfileUrl", $"https://www.gog.com/u/{playerData.username}");
                                    }
                                }
                                catch { }
                            }
                            break;
                            }
                        }

                        game.Players.Add(player);
                    }

                    Sessions.Add(game);
                }

                return(DefaultSession, Sessions, JObject.Parse(res));
            }
        }