示例#1
0
 public bool DeepEquals(DestinyItemSourceDefinition?other)
 {
     return(other is not null &&
            Level == other.Level &&
            MinQuality == other.MinQuality &&
            MaxQuality == other.MaxQuality &&
            MinLevelRequired == other.MinLevelRequired &&
            MaxLevelRequired == other.MaxLevelRequired &&
            ComputedStats.DeepEqualsDictionary(other.ComputedStats) &&
            SourceHashes.DeepEqualsListNaive(other.SourceHashes));
 }
 public bool DeepEquals(InventoryItemSourceBlockSource other)
 {
     return(other != null &&
            Level == other.Level &&
            MinQuality == other.MinQuality &&
            MaxQuality == other.MaxQuality &&
            MinLevelRequired == other.MinLevelRequired &&
            MaxLevelRequired == other.MaxLevelRequired &&
            ComputedStats.DeepEqualsReadOnlyDictionaryWithDefinitionKeyAndSimpleValue(other.ComputedStats) &&
            Sources.DeepEqualsReadOnlyCollections(other.Sources));
 }
        public static void ComputedStats()
        {
            var encodedSaveData = File.ReadAllText("TestData\\ValidZlib.txt");
            var savedGame       = SavedGame.Parse(encodedSaveData);
            var computedStats   = new ComputedStats(savedGame);

            Assert.Equal("1.002390000000000000e+005", computedStats.HeroSoulsSpent);
            Assert.Equal("5.223865765430567e99", computedStats.HeroSoulsSacrificed);
            Assert.Equal("5.22386475134114e99", computedStats.TitanDamage);
            Assert.Equal(498, computedStats.TotalAncientSouls);
            Assert.Equal(0.051918352081052083, computedStats.TranscendentPower);
            Assert.Equal(260, computedStats.Rubies);
            Assert.Equal(695, computedStats.HighestZoneThisTranscension);
            Assert.Equal(23274, computedStats.HighestZoneLifetime);
            Assert.Equal(4, computedStats.AscensionsThisTranscension);
            Assert.Equal(3080, computedStats.AscensionsLifetime);
        }
示例#4
0
 public void Update(DestinyItemSourceDefinition?other)
 {
     if (other is null)
     {
         return;
     }
     if (Level != other.Level)
     {
         Level = other.Level;
         OnPropertyChanged(nameof(Level));
     }
     if (MinQuality != other.MinQuality)
     {
         MinQuality = other.MinQuality;
         OnPropertyChanged(nameof(MinQuality));
     }
     if (MaxQuality != other.MaxQuality)
     {
         MaxQuality = other.MaxQuality;
         OnPropertyChanged(nameof(MaxQuality));
     }
     if (MinLevelRequired != other.MinLevelRequired)
     {
         MinLevelRequired = other.MinLevelRequired;
         OnPropertyChanged(nameof(MinLevelRequired));
     }
     if (MaxLevelRequired != other.MaxLevelRequired)
     {
         MaxLevelRequired = other.MaxLevelRequired;
         OnPropertyChanged(nameof(MaxLevelRequired));
     }
     if (!ComputedStats.DeepEqualsDictionary(other.ComputedStats))
     {
         ComputedStats = other.ComputedStats;
         OnPropertyChanged(nameof(ComputedStats));
     }
     if (!SourceHashes.DeepEqualsListNaive(other.SourceHashes))
     {
         SourceHashes = other.SourceHashes;
         OnPropertyChanged(nameof(SourceHashes));
     }
 }
示例#5
0
        public bool Equals(DestinyItemSourceDefinition input)
        {
            if (input == null)
            {
                return(false);
            }

            return
                ((
                     Level == input.Level ||
                     (Level.Equals(input.Level))
                     ) &&
                 (
                     MinQuality == input.MinQuality ||
                     (MinQuality.Equals(input.MinQuality))
                 ) &&
                 (
                     MaxQuality == input.MaxQuality ||
                     (MaxQuality.Equals(input.MaxQuality))
                 ) &&
                 (
                     MinLevelRequired == input.MinLevelRequired ||
                     (MinLevelRequired.Equals(input.MinLevelRequired))
                 ) &&
                 (
                     MaxLevelRequired == input.MaxLevelRequired ||
                     (MaxLevelRequired.Equals(input.MaxLevelRequired))
                 ) &&
                 (
                     ComputedStats == input.ComputedStats ||
                     (ComputedStats != null && ComputedStats.SequenceEqual(input.ComputedStats))
                 ) &&
                 (
                     SourceHashes == input.SourceHashes ||
                     (SourceHashes != null && SourceHashes.SequenceEqual(input.SourceHashes))
                 ));
        }
        public async Task <ActionResult <int> > Add([FromForm] UploadRequest uploadRequest)
        {
            // Only associate it with the user if they requested that it be added to their progress.
            var userId = uploadRequest.AddToProgress && this.User.Identity.IsAuthenticated
                ? this.userManager.GetUserId(this.User)
                : null;

            var savedGame = SavedGame.Parse(uploadRequest.EncodedSaveData);

            if (savedGame == null)
            {
                // Not a valid save
                return(this.BadRequest());
            }

            // Kick off a clan update in parallel
            var gameUserId     = savedGame.Object.Value <string>("uniqueId");
            var passwordHash   = savedGame.Object.Value <string>("passwordHash");
            var updateClanTask = this.clanManager.UpdateClanAsync(userId, gameUserId, passwordHash);

            PlayStyle playStyle;

            if (uploadRequest.PlayStyle.HasValue)
            {
                playStyle = uploadRequest.PlayStyle.Value;
            }
            else
            {
                var userSettings = await this.userSettingsProvider.GetAsync(userId);

                playStyle = userSettings.PlayStyle.GetValueOrDefault(PlayStyle.Hybrid);
            }

            // unixTimestamp is in milliseconds instead of seconds
            var saveTime = (savedGame.Object.Value <double>("unixTimestamp") / 1000).UnixTimeStampToDateTime();

            var ancientLevels = new AncientLevelsModel(
                this.gameData,
                savedGame);
            var outsiderLevels = new OutsiderLevelsModel(
                this.gameData,
                savedGame);
            var computedStats = new ComputedStats(savedGame);

            int uploadId;

            using (var command = this.databaseCommandFactory.Create())
            {
                await command.BeginTransactionAsync();

                // Insert Upload
                command.CommandText = @"
	                INSERT INTO Uploads(UserId, UploadContent, PlayStyle, SaveTime)
                    VALUES(@UserId, @UploadContent, @PlayStyle, @SaveTime);
                    SELECT SCOPE_IDENTITY();";
                command.Parameters  = new Dictionary <string, object>
                {
                    { "@UserId", userId },
                    { "@UploadContent", uploadRequest.EncodedSaveData },
                    { "@PlayStyle", playStyle.ToString() },
                    { "@SaveTime", saveTime },
                };
                uploadId = Convert.ToInt32(await command.ExecuteScalarAsync());

                // Insert computed stats
                command.CommandText = @"
                    INSERT INTO ComputedStats(
                        UploadId,
                        TitanDamage,
                        SoulsSpent,
                        HeroSoulsSacrificed,
                        TotalAncientSouls,
                        TranscendentPower,
                        Rubies,
                        HighestZoneThisTranscension,
                        HighestZoneLifetime,
                        AscensionsThisTranscension,
                        AscensionsLifetime)
                    VALUES(
                        @UploadId,
                        @TitanDamage,
                        @SoulsSpent,
                        @HeroSoulsSacrificed,
                        @TotalAncientSouls,
                        @TranscendentPower,
                        @Rubies,
                        @HighestZoneThisTranscension,
                        @HighestZoneLifetime,
                        @AscensionsThisTranscension,
                        @AscensionsLifetime);";
                command.Parameters  = new Dictionary <string, object>
                {
                    { "@UploadId", uploadId },
                    { "@TitanDamage", computedStats.TitanDamage },
                    { "@SoulsSpent", computedStats.HeroSoulsSpent },
                    { "@HeroSoulsSacrificed", computedStats.HeroSoulsSacrificed },
                    { "@TotalAncientSouls", computedStats.TotalAncientSouls },
                    { "@TranscendentPower", computedStats.TranscendentPower },
                    { "@Rubies", computedStats.Rubies },
                    { "@HighestZoneThisTranscension", computedStats.HighestZoneThisTranscension },
                    { "@HighestZoneLifetime", computedStats.HighestZoneLifetime },
                    { "@AscensionsThisTranscension", computedStats.AscensionsThisTranscension },
                    { "@AscensionsLifetime", computedStats.AscensionsLifetime },
                };
                await command.ExecuteNonQueryAsync();

                // Insert ancient levels
                var ancientLevelsCommandText = new StringBuilder("INSERT INTO AncientLevels(UploadId, AncientId, Level) VALUES");
                var ancientLevelsParameters  = new Dictionary <string, object>
                {
                    { "@UploadId", uploadId },
                };
                var isFirstAncient = true;
                foreach (var pair in ancientLevels.AncientLevels)
                {
                    if (!isFirstAncient)
                    {
                        ancientLevelsCommandText.Append(',');
                    }

                    // Looks like: (@UploadId, @AncientId{AncientId}, @AncientLevel{AncientId})
                    var idParamName    = "@AncientId" + pair.Key;
                    var levelParamName = "@AncientLevel" + pair.Key;
                    ancientLevelsCommandText.Append("(@UploadId,");
                    ancientLevelsCommandText.Append(idParamName);
                    ancientLevelsCommandText.Append(',');
                    ancientLevelsCommandText.Append(levelParamName);
                    ancientLevelsCommandText.Append(')');

                    ancientLevelsParameters.Add(idParamName, pair.Key);
                    ancientLevelsParameters.Add(levelParamName, pair.Value.ToTransportableString());

                    isFirstAncient = false;
                }

                command.CommandText = ancientLevelsCommandText.ToString();
                command.Parameters  = ancientLevelsParameters;
                await command.ExecuteNonQueryAsync();

                // Insert outsider levels
                var outsiderLevelsCommandText = new StringBuilder("INSERT INTO OutsiderLevels(UploadId, OutsiderId, Level) VALUES");
                var outsiderLevelsParameters  = new Dictionary <string, object>
                {
                    { "@UploadId", uploadId },
                };
                var isFirstOutsider = true;
                foreach (var pair in outsiderLevels.OutsiderLevels)
                {
                    if (!isFirstOutsider)
                    {
                        outsiderLevelsCommandText.Append(',');
                    }

                    // Looks like: (@UploadId, @OutsiderId{OutsiderId}, @Level{OutsiderId})
                    var idParamName    = "@OutsiderId" + pair.Key;
                    var levelParamName = "@OutsiderLevel" + pair.Key;
                    outsiderLevelsCommandText.Append("(@UploadId,");
                    outsiderLevelsCommandText.Append(idParamName);
                    outsiderLevelsCommandText.Append(',');
                    outsiderLevelsCommandText.Append(levelParamName);
                    outsiderLevelsCommandText.Append(')');

                    outsiderLevelsParameters.Add(idParamName, pair.Key);
                    outsiderLevelsParameters.Add(levelParamName, pair.Value);

                    isFirstOutsider = false;
                }

                command.CommandText = outsiderLevelsCommandText.ToString();
                command.Parameters  = outsiderLevelsParameters;
                await command.ExecuteNonQueryAsync();

                command.CommitTransaction();
            }

            // Wait for the task to finish, but don't fail the request if it fails
            try
            {
                await updateClanTask;
            }
            catch (Exception e)
            {
                var properties = new Dictionary <string, string>
                {
                    { "UploadId", uploadId.ToString() },
                };
                this.telemetryClient.TrackException(e, properties);
            }

            return(uploadId);
        }
示例#7
0
        private async Task <bool> ProcessMessageAsync(CloudQueueMessage queueMessage)
        {
            var properties = new Dictionary <string, string>
            {
                { "CloudQueueMessage-DequeueCount", queueMessage.DequeueCount.ToString() },
                { "CloudQueueMessage-InsertionTime", queueMessage.InsertionTime.ToString() },
                { "CloudQueueMessage-Id", queueMessage.Id },
            };

            this.telemetryClient.TrackEvent("UploadProcessor-Recieved", properties);

            var databaseCommandFactory = new DatabaseCommandFactory(this.databaseSettingsOptions);

            try
            {
                var message = JsonConvert.DeserializeObject <UploadProcessingMessage>(queueMessage.AsString);
                if (message == null)
                {
                    this.telemetryClient.TrackEvent("UploadProcessor-Abandoned-ParseMessage", properties);
                    return(false);
                }

                var uploadId = message.UploadId;
                properties.Add("UploadId", uploadId.ToString());

                this.CurrentUploadId = uploadId;

                var(uploadContent, userId) = await GetUploadDetailsAsync(databaseCommandFactory, uploadId);

                properties.Add("UserId", userId);
                if (string.IsNullOrWhiteSpace(uploadContent))
                {
                    this.telemetryClient.TrackEvent("UploadProcessor-Abandoned-FetchUpload", properties);
                    return(false);
                }

                // Handle legacy uplaods where the upload content was missing.
                if (uploadContent.Equals("LEGACY", StringComparison.OrdinalIgnoreCase))
                {
                    this.telemetryClient.TrackEvent("UploadProcessor-Complete-Legacy", properties);
                    using (var command = databaseCommandFactory.Create())
                    {
                        command.CommandText = "UPDATE Uploads SET LastComputeTime = getutcdate() WHERE Id = " + uploadId;
                        await command.ExecuteNonQueryAsync();
                    }

                    return(true);
                }

                var savedGame = SavedGame.Parse(uploadContent);
                if (savedGame == null)
                {
                    this.telemetryClient.TrackEvent("UploadProcessor-Abandoned-ParseUpload", properties);
                    return(false);
                }

                this.telemetryClient.TrackEvent("UploadProcessor-Processing", properties);
                var ancientLevels = new AncientLevelsModel(
                    this.gameData,
                    savedGame);
                var outsiderLevels = new OutsiderLevelsModel(
                    this.gameData,
                    savedGame);
                var computedStats = new ComputedStats(savedGame);

                /* Build a query that looks like this:
                 *  MERGE INTO AncientLevels WITH (HOLDLOCK)
                 *  USING
                 *      (VALUES (123, 1, '100'), (123, 2, '100'), ... )
                 *          AS Input(UploadId, AncientId, Level)
                 *      ON AncientLevels.UploadId = Input.UploadId
                 *      AND AncientLevels.AncientId = Input.AncientId
                 *  WHEN MATCHED THEN
                 *      UPDATE
                 *      SET
                 *          Level = Input.Level
                 *  WHEN NOT MATCHED THEN
                 *      INSERT (UploadId, AncientId, Level)
                 *      VALUES (Input.UploadId, Input.AncientId, Input.Level);
                 */
                var ancientLevelsCommandText = new StringBuilder();
                if (ancientLevels.AncientLevels.Count > 0)
                {
                    ancientLevelsCommandText.Append(@"
                    MERGE INTO AncientLevels WITH (HOLDLOCK)
                    USING
                        ( VALUES ");
                    var isFirst = true;
                    foreach (var pair in ancientLevels.AncientLevels)
                    {
                        if (!isFirst)
                        {
                            ancientLevelsCommandText.Append(",");
                        }

                        // No need to sanitize, these are all just numbers
                        ancientLevelsCommandText.Append("(");
                        ancientLevelsCommandText.Append(uploadId);
                        ancientLevelsCommandText.Append(",");
                        ancientLevelsCommandText.Append(pair.Key);
                        ancientLevelsCommandText.Append(",'");
                        ancientLevelsCommandText.Append(pair.Value.ToTransportableString());
                        ancientLevelsCommandText.Append("')");

                        isFirst = false;
                    }

                    ancientLevelsCommandText.Append(@"
                        )
                            AS Input(UploadId, AncientId, Level)
                        ON AncientLevels.UploadId = Input.UploadId
                        AND AncientLevels.AncientId = Input.AncientId
                    WHEN MATCHED THEN
                        UPDATE
                        SET
                            Level = Input.Level
                    WHEN NOT MATCHED THEN
                        INSERT (UploadId, AncientId, Level)
                        VALUES (Input.UploadId, Input.AncientId, Input.Level);");
                }

                /* Build a query that looks like this:
                 *  MERGE INTO OutsiderLevels WITH (HOLDLOCK)
                 *  USING
                 *      (VALUES (123, 1, 100), (123, 2, 100), ... )
                 *          AS Input(UploadId, OutsiderId, Level)
                 *      ON OutsiderLevels.UploadId = Input.UploadId
                 *      AND OutsiderLevels.OutsiderId = Input.OutsiderId
                 *  WHEN MATCHED THEN
                 *      UPDATE
                 *      SET
                 *          Level = Input.Level
                 *  WHEN NOT MATCHED THEN
                 *      INSERT (UploadId, OutsiderId, Level)
                 *      VALUES (Input.UploadId, Input.OutsiderId, Input.Level);
                 */
                var outsiderLevelsCommandText = new StringBuilder();
                if (outsiderLevels.OutsiderLevels.Count > 0)
                {
                    outsiderLevelsCommandText.Append(@"
                    MERGE INTO OutsiderLevels WITH (HOLDLOCK)
                    USING
                        ( VALUES ");
                    var isFirst = true;
                    foreach (var pair in outsiderLevels.OutsiderLevels)
                    {
                        if (!isFirst)
                        {
                            outsiderLevelsCommandText.Append(",");
                        }

                        // No need to sanitize, these are all just numbers
                        outsiderLevelsCommandText.Append("(");
                        outsiderLevelsCommandText.Append(uploadId);
                        outsiderLevelsCommandText.Append(",");
                        outsiderLevelsCommandText.Append(pair.Key);
                        outsiderLevelsCommandText.Append(",");
                        outsiderLevelsCommandText.Append(pair.Value);
                        outsiderLevelsCommandText.Append(")");

                        isFirst = false;
                    }

                    outsiderLevelsCommandText.Append(@"
                        )
                            AS Input(UploadId, OutsiderId, Level)
                        ON OutsiderLevels.UploadId = Input.UploadId
                        AND OutsiderLevels.OutsiderId = Input.OutsiderId
                    WHEN MATCHED THEN
                        UPDATE
                        SET
                            Level = Input.Level
                    WHEN NOT MATCHED THEN
                        INSERT (UploadId, OutsiderId, Level)
                        VALUES (Input.UploadId, Input.OutsiderId, Input.Level);");
                }

                const string ComputedStatsCommandText       = @"
                    MERGE INTO ComputedStats WITH (HOLDLOCK)
                    USING
                        (VALUES (
                                @UploadId,
                                @TitanDamage,
                                @SoulsSpent,
                                @HeroSoulsSacrificed,
                                @TotalAncientSouls,
                                @TranscendentPower,
                                @Rubies,
                                @HighestZoneThisTranscension,
                                @HighestZoneLifetime,
                                @AscensionsThisTranscension,
                                @AscensionsLifetime) )
                            AS Input(
                                UploadId,
                                TitanDamage,
                                SoulsSpent,
                                HeroSoulsSacrificed,
                                TotalAncientSouls,
                                TranscendentPower,
                                Rubies,
                                HighestZoneThisTranscension,
                                HighestZoneLifetime,
                                AscensionsThisTranscension,
                                AscensionsLifetime)
                        ON ComputedStats.UploadId = Input.UploadId
                    WHEN MATCHED THEN
                        UPDATE
                        SET
                            TitanDamage = Input.TitanDamage,
                            SoulsSpent = Input.SoulsSpent,
                            HeroSoulsSacrificed = Input.HeroSoulsSacrificed,
                            TotalAncientSouls = Input.TotalAncientSouls,
                            TranscendentPower = Input.TranscendentPower,
                            Rubies = Input.Rubies,
                            HighestZoneThisTranscension = Input.HighestZoneThisTranscension,
                            HighestZoneLifetime = Input.HighestZoneLifetime,
                            AscensionsThisTranscension = Input.AscensionsThisTranscension,
                            AscensionsLifetime = Input.AscensionsLifetime
                    WHEN NOT MATCHED THEN
                        INSERT (
                            UploadId,
                            TitanDamage,
                            SoulsSpent,
                            HeroSoulsSacrificed,
                            TotalAncientSouls,
                            TranscendentPower,
                            Rubies,
                            HighestZoneThisTranscension,
                            HighestZoneLifetime,
                            AscensionsThisTranscension,
                            AscensionsLifetime)
                        VALUES (
                            Input.UploadId,
                            Input.TitanDamage,
                            Input.SoulsSpent,
                            Input.HeroSoulsSacrificed,
                            Input.TotalAncientSouls,
                            Input.TranscendentPower,
                            Input.Rubies,
                            Input.HighestZoneThisTranscension,
                            Input.HighestZoneLifetime,
                            Input.AscensionsThisTranscension,
                            Input.AscensionsLifetime);";
                var          computedStatsCommandParameters = new Dictionary <string, object>
                {
                    { "@UploadId", uploadId },
                    { "@TitanDamage", computedStats.TitanDamage },
                    { "@SoulsSpent", computedStats.HeroSoulsSpent },
                    { "@HeroSoulsSacrificed", computedStats.HeroSoulsSacrificed },
                    { "@TotalAncientSouls", computedStats.TotalAncientSouls },
                    { "@TranscendentPower", computedStats.TranscendentPower },
                    { "@Rubies", computedStats.Rubies },
                    { "@HighestZoneThisTranscension", computedStats.HighestZoneThisTranscension },
                    { "@HighestZoneLifetime", computedStats.HighestZoneLifetime },
                    { "@AscensionsThisTranscension", computedStats.AscensionsThisTranscension },
                    { "@AscensionsLifetime", computedStats.AscensionsLifetime },
                };

                using (var command = databaseCommandFactory.Create())
                {
                    await command.BeginTransactionAsync();

                    if (ancientLevelsCommandText.Length > 0)
                    {
                        command.CommandText = ancientLevelsCommandText.ToString();
                        await command.ExecuteNonQueryAsync();
                    }

                    if (outsiderLevelsCommandText.Length > 0)
                    {
                        command.CommandText = outsiderLevelsCommandText.ToString();
                        await command.ExecuteNonQueryAsync();
                    }

                    command.CommandText = ComputedStatsCommandText;
                    command.Parameters  = computedStatsCommandParameters;
                    await command.ExecuteNonQueryAsync();

                    command.CommandText = "UPDATE Uploads SET LastComputeTime = getutcdate() WHERE Id = " + uploadId;
                    await command.ExecuteNonQueryAsync();

                    try
                    {
                        command.CommitTransaction();
                    }
                    catch (Exception)
                    {
                        this.telemetryClient.TrackEvent("UploadProcessor-Abandoned-CommitTransaction", properties);
                        return(false);
                    }
                }

                this.telemetryClient.TrackEvent("UploadProcessor-Complete", properties);
                return(true);
            }
            catch (Exception e)
            {
                this.telemetryClient.TrackException(e, properties);
                return(false);
            }
            finally
            {
                this.CurrentUploadId = null;
            }
        }