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); }
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)); } }
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); }
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; } }