public async Task <ActionResult> Post(string method, int?winner, string playerData, string data, float duration,
                                              int lastWave, string duelData, long?steamId, string secret_key)
        {
            if (!(await CheckIp() || ValidateSecretKey(secret_key)))
            {
                return(Json(new NoPermissionFailure()));
            }
            LoggingUtil.Log($"Called method {method}");
            switch (method)
            {
            case PostMethods.SaveMatchData:
                return(await SaveMatchData(winner, playerData, duration, lastWave, duelData));

            case PostMethods.UpdateUnitData:
                return(await UpdateUnitData(data));

            case PostMethods.UpdateAbilityData:
                return(await UpdateAbilityData(data));

            case PostMethods.UpdateBuilders:
                return(await UpdateBuilders(data));

            case PostMethods.SavePlayerData:
            default:
                return(Json(new InvalidRequestFailure()));
            }
        }
Пример #2
0
        private async Task <ActionResult> UpdateFractionStatistics()
        {
            if (!await CheckIp())
            {
                return(Json(new NoPermissionFailure()));
            }
            try
            {
                var strategy = _db.Database.CreateExecutionStrategy();
                await strategy.ExecuteAsync(async() =>
                {
                    using var transaction = await _db.Database.BeginTransactionAsync();
                    await UpdateFractionStatistics_Internal();
                    await _db.SaveChangesAsync();
                    lock (_dbLock)
                    {
                        transaction.Commit();
                    }
                });

                LoggingUtil.Log("Fraction statistics have been updated.");
                return(Json(new { success = true }));
            }
            catch (Exception e)
            {
                LoggingUtil.Error("Failed to compute fraction statistics");
                LoggingUtil.Error(e.StackTrace);
                return(Json(new { success = false }));
            }
        }
Пример #3
0
        public static async Task <List <T1> > GetOrCreateAsync <T1, T2>(
            this DbContext db, IEnumerable <T2> ids,
            Func <T1, T2> identifierFunc,
            Func <T2, T1> initFunc,
            string property = null,
            bool exclusive  = false,
            Func <IQueryable <T1>, IQueryable <T1> > query = null) where T1 : class
        {
            var idList = ids.ToList();

            if (idList.Count == 0)
            {
                return(new List <T1>());
            }
            // Gather basic info
            var dbSet      = db.Set <T1>();
            var entityType = db.Model.GetEntityTypes().First(t => t.ClrType == typeof(T1));
            var tableName  = entityType.GetTableName();
            var discrProp  = entityType.GetDiscriminatorProperty();
            var discrName  = entityType.GetDiscriminatorValue();

            if (string.IsNullOrWhiteSpace(property))
            {
                property = entityType.FindPrimaryKey().Properties[0].Name;
            }

            var idString = string.Join(", ", idList.Select(id => $"'{id}'"));
            var sql      = $"SELECT * FROM {tableName} WHERE {property} IN ({idString})";

            if (discrProp != null && exclusive)
            {
                sql += $" AND {discrProp.Name} = '{discrName}'";
            }
            var sqlQuery = dbSet.FromSqlRaw(sql);

            if (query != null)
            {
                sqlQuery = query(sqlQuery);
            }
            var existingData = await sqlQuery.ToDictionaryAsync(o => identifierFunc(o), o => o);

            var result = new List <T1>();

            foreach (var id in idList)
            {
                if (!existingData.ContainsKey(id))
                {
                    var newData = initFunc(id);
                    existingData[id] = newData;
                    dbSet.Add(newData);
                    LoggingUtil.Log($"Added {tableName} with key {id}");
                }
                result.Add(existingData[id]);
            }
            return(result);
        }
        private async Task <ActionResult> UpdatePlayerProfiles()
        {
            if (!await CheckIp())
            {
                return(Json(new NoPermissionFailure()));
            }
            int stepSize = 50;
            await _steamApi.UpdatePlayerInformation(await _db.Players.OrderByDescending(p => p.SteamId).Where(p => p.Avatar == null).Take(stepSize).Select(p => p.SteamId).ToListAsync());

            LoggingUtil.Log($"{stepSize} Player profiles have been requested.");
            return(Json(new { success = true }));
        }
        private async Task UpdateRanking(Models.RankingTypes type, bool asc)
        {
            string key = type + "|" + asc;

            _cache.Set(key, true, DateTimeOffset.Now.AddDays(1));

            await _db.Database.ExecuteSqlCommandAsync($"DELETE FROM Rankings WHERE Type = {(int)type} AND Ascending = {(asc ? 1 : 0)}");

            Console.WriteLine($"Cleared Ranking for {type} {asc}");
            string sql;
            string sqlSelects = "";
            string sqlJoins   = "JOIN Matches AS m \n" +
                                "ON m.MatchId = pm.MatchId \n";
            string sqlOrderBy = "";
            string sqlWheres  = "WHERE m.IsTraining = FALSE \n";

            switch (type)
            {
            case RankingTypes.EarnedTangos:
                sqlSelects = ", SUM(EarnedTangos) AS Gold \n";
                sqlOrderBy = "ORDER BY Gold " + (asc ? "ASC" : "DESC") + " \n";
                break;

            case RankingTypes.EarnedGold:
                sqlSelects = ", SUM(EarnedGold) AS Gold \n";
                sqlOrderBy = "ORDER BY Gold " + (asc ? "ASC" : "DESC") + " \n";
                break;

            case RankingTypes.Rating:
            default:
                sqlSelects = ", SUM(RatingChange) AS Rating \n";
                sqlOrderBy = "ORDER BY Rating " + (asc ? "ASC" : "DESC") + " \n";
                sqlJoins   = "";
                sqlWheres  = "";
                break;
            }
            sql = "INSERT INTO Rankings \n" +
                  "(Type, Ascending, PlayerId, Position) \n" +
                  $"SELECT @t := {(int)type}, @a := {(asc ? "TRUE" : "FALSE")}, PlayerId, @rownum := @rownum + 1 AS position\n" +
                  "FROM (SELECT PlayerId \n" +
                  sqlSelects +
                  "FROM PlayerMatchData AS pm \n" +
                  sqlJoins +
                  sqlWheres +
                  "GROUP BY pm.PlayerId \n" +
                  sqlOrderBy +
                  ") AS pr, \n" +
                  "(SELECT @rownum := 0) AS r \n";
            await _db.Database.ExecuteSqlCommandAsync(sql);

            LoggingUtil.Log("Ranking has been updated.");
        }
Пример #6
0
        /// <summary>
        /// Verify the loaded assembly meets a minimum version number.
        /// </summary>
        /// <param name="name">Assembly name</param>
        /// <param name="version">Minium version</param>
        /// <param name="silent">Silent mode</param>
        /// <returns>The assembly if the version check was successful.  If not, logs and error and returns null.</returns>
        public static Assembly VerifyAssemblyVersion(string name, string version, bool silent = false)
        {
            // Logic courtesy of DMagic
            var assemblies = AssemblyLoader.loadedAssemblies.Where(a => a.assembly.GetName().Name == name);
            var assembly   = assemblies.FirstOrDefault();

            if (assembly != null)
            {
                if (assemblies.Count() > 1)
                {
                    LoggingUtil.LogWarning(typeof(ContractConfigurator), StringBuilderCache.Format("Multiple assemblies with name '{0}' found!", name));
                }

                string receivedStr;

                // First try the informational version
                var ainfoV = Attribute.GetCustomAttribute(assembly.assembly, typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
                if (ainfoV != null)
                {
                    receivedStr = ainfoV.InformationalVersion;
                }
                // If that fails, use the product version
                else
                {
                    receivedStr = FileVersionInfo.GetVersionInfo(assembly.assembly.Location).ProductVersion;
                }
                // If that still fails, fall back on AssemblyVersion
                if (string.IsNullOrEmpty(receivedStr) || receivedStr == " ")
                {
                    receivedStr = assembly.assembly.GetName().Version.ToString();
                }

                System.Version expected = ParseVersion(version);
                System.Version received = ParseVersion(receivedStr);

                if (received >= expected)
                {
                    LoggingUtil.LogVerbose(typeof(ContractConfigurator), "Version check for '{0}' passed.  Minimum required is {1}, version found was {2}", name, version, receivedStr);
                    return(assembly.assembly);
                }
                else
                {
                    LoggingUtil.Log(silent ? LoggingUtil.LogLevel.DEBUG : LoggingUtil.LogLevel.ERROR, typeof(Version), "Version check for '{0}' failed!  Minimum required is {1}, version found was {2}", name, version, receivedStr);
                    return(null);
                }
            }
            else
            {
                LoggingUtil.Log(silent ? LoggingUtil.LogLevel.VERBOSE : LoggingUtil.LogLevel.ERROR, typeof(Version), "Couldn't find assembly for '{0}'!", name);
                return(null);
            }
        }
        private async Task <ActionResult> UpdateUnitStatistics()
        {
            if (!await CheckIp())
            {
                return(Json(new NoPermissionFailure()));
            }
            var units = await _db.Units.ToListAsync();

            foreach (var unit in units)
            {
                await UpdateUnitStatistic(unit.Name);
            }
            LoggingUtil.Log("Unit statistics have been updated.");
            return(Json(new { success = true }));
        }
Пример #8
0
        private async Task <bool> CheckIp()
        {
            var ipAddress = Request.HttpContext.Connection.RemoteIpAddress;
            var ranges    = await GetDotaIpRanges();

            foreach (var range in ranges)
            {
                if (range.IsInRange(ipAddress))
                {
                    LoggingUtil.Log($"Client {ipAddress} is in Range {range.Lower} - {range.Upper}");
                    return(true);
                }
            }
            // LoggingUtil.Warn($"Connection to {ipAddress} refused.");
            return(false);
        }
        private async Task <ActionResult> UpdateFractionStatistics()
        {
            if (!await CheckIp())
            {
                return(Json(new NoPermissionFailure()));
            }
            var fractions = await _db.Fractions.ToListAsync();

            foreach (var fraction in fractions)
            {
                await UpdateFractionStatistic(fraction.Name);
            }
            await _db.SaveChangesAsync();

            LoggingUtil.Log("Fraction statistics have been updated.");
            return(Json(new { success = true }));
        }
Пример #10
0
        /// <summary>
        /// Verify the loaded assembly meets a minimum version number.
        /// </summary>
        /// <param name="name">Assembly name</param>
        /// <param name="version">Minium version</param>
        /// <param name="silent">Silent mode</param>
        /// <returns>The assembly if the version check was successful.  If not, logs and error and returns null.</returns>
        public static Assembly VerifyAssemblyVersion(string name, string version, bool silent = false)
        {
            // Logic courtesy of DMagic
            var assembly = AssemblyLoader.loadedAssemblies.SingleOrDefault(a => a.assembly.GetName().Name == name);

            if (assembly != null)
            {
                string receivedStr;

                // First try the informational version
                var ainfoV = Attribute.GetCustomAttribute(assembly.assembly, typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
                if (ainfoV != null)
                {
                    receivedStr = ainfoV.InformationalVersion;
                }
                // If that fails, use the product version
                else
                {
                    receivedStr = FileVersionInfo.GetVersionInfo(assembly.assembly.Location).ProductVersion;
                }

                System.Version expected = ParseVersion(version);
                System.Version received = ParseVersion(receivedStr);

                if (received >= expected)
                {
                    LoggingUtil.LogVerbose(typeof(ContractConfigurator), "Version check for '" + name + "' passed.  Minimum required is " + version + ", version found was " + receivedStr);
                    return(assembly.assembly);
                }
                else
                {
                    LoggingUtil.Log(silent ? LoggingUtil.LogLevel.VERBOSE : LoggingUtil.LogLevel.ERROR, typeof(ContractConfigurator), "Version check for '" + name + "' failed!  Minimum required is " + version + ", version found was " + receivedStr);
                    return(null);
                }
            }
            else
            {
                LoggingUtil.Log(silent ? LoggingUtil.LogLevel.VERBOSE : LoggingUtil.LogLevel.ERROR, typeof(ContractConfigurator), "Couldn't find assembly for '" + name + "'!");
                return(null);
            }
        }
Пример #11
0
        /// <summary>
        /// Parses the child DATA nodes out of the given config node, and returns the parsed values back in dataValues.
        /// </summary>
        /// <param name="configNode">The ConfigNode to load child DATA nodes from.</param>
        /// <param name="obj">The ContractConfigurator object to load from.</param>
        /// <param name="dataValues"></param>
        /// <param name="uniquenessChecks"></param>
        /// <returns></returns>
        public bool ParseDataNodes(ConfigNode configNode, IContractConfiguratorFactory obj,
                                   Dictionary <string, ContractType.DataValueInfo> dataValues, Dictionary <string, UniquenessCheck> uniquenessChecks)
        {
            bool valid = true;

            foreach (ConfigNode data in ConfigNodeUtil.GetChildNodes(configNode, "DATA"))
            {
                Type   type          = null;
                bool   requiredValue = true;
                bool   hidden        = true;
                bool   isLiteral     = false;
                string title         = "";

                ConfigNodeUtil.SetCurrentDataNode(null);
                valid &= ConfigNodeUtil.ParseValue <Type>(data, "type", x => type = x, obj);
                valid &= ConfigNodeUtil.ParseValue <bool>(data, "requiredValue", x => requiredValue = x, obj, true);
                valid &= ConfigNodeUtil.ParseValue <string>(data, "title", x => title = x, obj, "");
                valid &= ConfigNodeUtil.ParseValue <bool>(data, "hidden", x => hidden = x, obj, false);
                valid &= ConfigNodeUtil.ParseValue <bool>(data, "isLiteral", x => isLiteral = x, obj, false);

                bool doneTitleWarning = false;

                UniquenessCheck uniquenessCheck = UniquenessCheck.NONE;
                // Backwards compatibility for Contract Configurator 1.8.3
                if (data.HasValue("uniqueValue") || data.HasValue("activeUniqueValue"))
                {
                    LoggingUtil.LogWarning(this, "The use of uniqueValue and activeUniqueValue is obsolete since Contract Configurator 1.9.0, use uniquenessCheck instead.");

                    bool uniqueValue       = false;
                    bool activeUniqueValue = false;
                    valid &= ConfigNodeUtil.ParseValue <bool>(data, "uniqueValue", x => uniqueValue = x, obj, false);
                    valid &= ConfigNodeUtil.ParseValue <bool>(data, "activeUniqueValue", x => activeUniqueValue = x, obj, false);

                    uniquenessCheck = activeUniqueValue ? UniquenessCheck.CONTRACT_ACTIVE : uniqueValue ? UniquenessCheck.CONTRACT_ALL : UniquenessCheck.NONE;
                }
                else
                {
                    valid &= ConfigNodeUtil.ParseValue <UniquenessCheck>(data, "uniquenessCheck", x => uniquenessCheck = x, obj, UniquenessCheck.NONE);
                }

                ConfigNodeUtil.SetCurrentDataNode(this);

                if (type != null)
                {
                    foreach (ConfigNode.Value pair in data.values)
                    {
                        string name = pair.name;
                        if (name != "type" && name != "title" && name != "hidden" && name != "requiredValue" && name != "uniqueValue" && name != "activeUniqueValue" && name != "uniquenessCheck" && name != "isLiteral")
                        {
                            if (uniquenessCheck != UniquenessCheck.NONE)
                            {
                                uniquenessChecks[name] = uniquenessCheck;
                            }

                            object value = null;

                            // Create the setter function
                            Type     actionType = typeof(Action <>).MakeGenericType(type);
                            Delegate del        = Delegate.CreateDelegate(actionType, value, typeof(DataNode).GetMethod("NullAction"));

                            // Set the ParseValue method generic
                            MethodInfo method = (isLiteral ? methodParseValueLiteral : methodParseValue).MakeGenericMethod(new Type[] { type });

                            // Invoke the ParseValue method
                            if (isLiteral)
                            {
                                this[name] = method.Invoke(null, new object[] { data, name, false });
                            }
                            else
                            {
                                valid &= (bool)method.Invoke(null, new object[] { data, name, del, obj });
                            }

                            dataValues[name] = new ContractType.DataValueInfo(title, requiredValue, hidden, type);

                            // Recommend a title
                            if (!data.HasValue("title") && requiredValue && !IsDeterministic(name) && !hidden && !doneTitleWarning && !dataValues[name].IsIgnoredType())
                            {
                                doneTitleWarning = true;

                                LoggingUtil.Log(obj.minVersion >= ContractConfigurator.ENHANCED_UI_VERSION ? LoggingUtil.LogLevel.ERROR : LoggingUtil.LogLevel.WARNING, this,
                                                obj.ErrorPrefix() + ": " + name + ": The field 'title' is required in for data node values where 'requiredValue' is true.  Alternatively, the attribute 'hidden' can be set to true (but be careful - this can cause player confusion if all lines for the contract type show as 'Met' and the contract isn't generating).");

                                // Error on newer versions of contract packs
                                if (obj.minVersion >= ContractConfigurator.ENHANCED_UI_VERSION)
                                {
                                    valid = false;
                                }
                            }
                        }
                    }
                }
            }

            return(valid);
        }
Пример #12
0
        public async Task <ActionResult> SaveMatchData(int?winner, string playerDataString, float duration,
                                                       int lastWave, string duelDataString)
        {
            if (!winner.HasValue || string.IsNullOrWhiteSpace(playerDataString))
            {
                return(Json(new MissingArgumentFailure()));
            }

            try
            {
                var strategy = _db.Database.CreateExecutionStrategy();
                var matchId  = await strategy.ExecuteAsync(async() =>
                {
                    using var transaction = await _db.Database.BeginTransactionAsync();
                    //Creating Match
                    Match match = new Match
                    {
                        Winner     = winner.Value,
                        Duration   = duration,
                        LastWave   = lastWave,
                        Date       = DateTime.UtcNow,
                        IsTraining = true,
                        Duels      = new List <Duel>(),
                        PlayerData = new List <PlayerMatchData>()
                    };
                    _db.Matches.Add(match);
                    await _db.SaveChangesAsync();
                    using var loggingContext = new LoggingContext($"#{match.MatchId}");
                    LoggingUtil.Log($"Adding Match {match.MatchId}");

                    //Adding Duels
                    int createdDuels = 0;
                    if (duelDataString.TryToJson(out JsonDocument duelDocument) &&
                        duelDocument.RootElement.ValueKind == JsonValueKind.Object)
                    {
                        foreach (var duelProp in duelDocument.RootElement.EnumerateObject())
                        {
                            var order      = int.Parse(duelProp.Name);
                            var time       = duelProp.Value.GetFloatOrDefault("time");
                            var duelWinner = duelProp.Value.GetIntOrDefault("winner");
                            Duel duel      = new Duel
                            {
                                Match     = match,
                                MatchId   = match.MatchId,
                                Order     = order,
                                Winner    = duelWinner,
                                TimeStamp = time
                            };
                            match.Duels.Add(duel);
                            _db.Duels.Add(duel);
                            createdDuels += 1;
                        }
                    }
                    else
                    {
                        LoggingUtil.Warn($"No duel data available for Game");
                    }
                    await _db.SaveChangesAsync();

                    //Adding player Data
                    var playerObjs = playerDataString.ToJsonElement();
                    var steamIds   = playerObjs.EnumerateObject().Select(p => long.Parse(p.Name)).ToList();
                    var players    = await _db.GetOrCreateAsync(
                        steamIds,
                        p => p.SteamId,
                        steamId => new Player
                    {
                        SteamId = steamId,
                        Matches = new List <PlayerMatchData>()
                    },
                        query: players => players
                        .Include(p => p.Matches)
                        .ThenInclude(m => m.Match)
                        ); // We request the match history to calculate the rating change
                    try
                    {
                        var steamInfo = await _steamApi.RequestPlayerInformation(steamIds);
                        players.ForEach(p => p.Update(steamInfo[p.SteamId]));
                    }
                    catch (Exception)
                    {
                        LoggingUtil.Warn("Couldn't retrieve steam info.");
                    }
                    await _db.SaveChangesAsync();


                    // Enter player match data
                    var playerData = new List <PlayerMatchData>();
                    foreach (var(steamId, player) in steamIds.Zip(players))
                    {
                        var data        = playerObjs.GetProperty(steamId.ToString());
                        var rawUnitData = data.GetProperty("unit_data");
                        var unitData    = new Dictionary <string, UnitData>();
                        if (rawUnitData.ValueKind == JsonValueKind.Object)
                        {
                            unitData = ExtractPlayerUnitData(rawUnitData);
                        }
                        else
                        {
                            LoggingUtil.Warn($"No unit data for {steamId}");
                        }
                        var newData = new PlayerMatchData
                        {
                            Player       = player,
                            PlayerId     = steamId,
                            Match        = match,
                            MatchId      = match.MatchId,
                            Abandoned    = data.GetBoolOrDefault("abandoned"),
                            Team         = data.GetIntOrDefault("team"),
                            FractionName = data.GetValueOrDefault("fraction"),
                            EarnedTangos = data.GetIntOrDefault("earned_tangos"),
                            EarnedGold   = data.GetIntOrDefault("earned_gold"),
                            UnitData     = unitData
                        };
                        player.Matches.Add(newData);
                        match.PlayerData.Add(newData);
                        playerData.Add(newData);
                    }
                    // Retrieve exp for units
                    var unitNames   = playerData.SelectMany(p => p.UnitData.Object.Keys).Distinct().ToList();
                    var units       = await _db.GetOrCreateAsync(unitNames, u => u.Name, name => CreateUnitOrBuilder(name));
                    var experiences = units.ToDictionary(u => u.Name, u => u.Experience);
                    // Check whether this is a training match
                    match.IsTraining = DecideIsTraining(match, playerData);
                    // Update ratings and statistics
                    foreach (var pd in playerData)
                    {
                        pd.CalculateStats(experiences);
                        pd.RatingChange = pd.CalculateRatingChange();  // this requires having all player histories loaded
                    }
                    _db.PlayerMatchData.AddRange(playerData);
                    await _db.SaveChangesAsync();
                    lock (_dbLock)
                    {
                        transaction.Commit();
                    }
                    LoggingUtil.Log($"Succesfully saved; Wave {lastWave}; Duration {duration}; Players: {players.Count}; Duels: {createdDuels}; IsTraining: {match.IsTraining}");
                    return(match.MatchId);
                });
Пример #13
0
 private bool ValidateSecretKey(string secretKey)
 {
     LoggingUtil.Log($"Received secret key: {secretKey}");
     return(secretKey == this._dedicatedServerKey);
 }
Пример #14
0
        private async Task UpdateRanking(RankingTypes type, bool asc)
        {
            string key = type + "|" + asc;

            _cache.Set(key, true, DateTime.UtcNow.AddDays(1));
            var oldTimeout = _db.Database.GetCommandTimeout();

            _db.Database.SetCommandTimeout(TimeSpan.FromHours(1));
            try
            {
                var strategy = _db.Database.CreateExecutionStrategy();
                await strategy.ExecuteAsync(async() =>
                {
                    using var transcation = await _db.Database.BeginTransactionAsync();
                    await _db.Database.ExecuteSqlRawAsync($"DELETE FROM Rankings WHERE Type = {(int)type} AND Ascending = {(asc ? 1 : 0)}");
                    LoggingUtil.Log($"Cleared Ranking for {type} {asc}");
                    string sql;
                    string sqlJoins = "JOIN Matches AS m \n" +
                                      "ON m.MatchId = pm.MatchId \n";
                    string sqlWheres = "WHERE m.IsTraining = FALSE \n";
                    string sqlSelects, sqlOrderBy;
                    switch (type)
                    {
                    case RankingTypes.EarnedTangos:
                        sqlSelects = ", SUM(EarnedTangos) AS Gold \n";
                        sqlOrderBy = "ORDER BY Gold " + (asc ? "ASC" : "DESC") + " \n";
                        break;

                    case RankingTypes.EarnedGold:
                        sqlSelects = ", SUM(EarnedGold) AS Gold \n";
                        sqlOrderBy = "ORDER BY Gold " + (asc ? "ASC" : "DESC") + " \n";
                        break;

                    case RankingTypes.Rating:
                    default:
                        sqlSelects = ", SUM(RatingChange) AS Rating \n";
                        sqlOrderBy = "ORDER BY Rating " + (asc ? "ASC" : "DESC") + " \n";
                        sqlJoins   = "";
                        sqlWheres  = "";
                        break;
                    }
                    sql = "INSERT INTO Rankings \n" +
                          "(Type, Ascending, PlayerId, Position) \n" +
                          $"SELECT @t := {(int)type}, @a := {(asc ? "TRUE" : "FALSE")}, PlayerId, @rownum := @rownum + 1 AS position\n" +
                          "FROM (SELECT PlayerId \n" +
                          sqlSelects +
                          "FROM PlayerMatchData AS pm \n" +
                          sqlJoins +
                          sqlWheres +
                          "GROUP BY pm.PlayerId \n" +
                          sqlOrderBy +
                          ") AS pr, \n" +
                          "(SELECT @rownum := 0) AS r \n";
                    await _db.Database.ExecuteSqlRawAsync(sql);
                    lock (_dbLock)
                    {
                        transcation.Commit();
                    }
                });

                LoggingUtil.Log("Ranking has been updated.");
            }
            catch (Exception e)
            {
                LoggingUtil.Error(e.ToString());
                LoggingUtil.Error("Failed to update ranking");
            }
            finally
            {
                _db.Database.SetCommandTimeout(oldTimeout);
            }
        }