public async Task<IActionResult> PostCurrentArmy([FromBody]JSON.PlayerArmy currentArmySetJson) { if (!ModelState.IsValid) return ValidationProblem(ModelState); if (currentArmySetJson.TroopData.Count == 0) return Ok(); var villageIds = currentArmySetJson.TroopData.Select(a => a.VillageId.Value).ToList(); var scaffoldCurrentVillages = await Profile("Get existing scaffold current villages", () => ( from cv in CurrentSets.CurrentVillage .Include(v => v.ArmyOwned) .Include(v => v.ArmyAtHome) .Include(v => v.ArmyStationed) .Include(v => v.ArmySupporting) .Include(v => v.ArmyTraveling) where villageIds.Contains(cv.VillageId) select cv ).ToListAsync()); var villagesWithPlayerIds = await Profile("Get village player IDs", () => ( from v in CurrentSets.Village where villageIds.Contains(v.VillageId) select new { v.PlayerId, v.VillageId } ).ToListAsync()); var villageIdsByPlayerId = villagesWithPlayerIds.ToDictionary(v => v.VillageId, v => v.PlayerId); var mappedScaffoldVillages = villageIds.ToDictionary(id => id, id => scaffoldCurrentVillages.SingleOrDefault(cv => cv.VillageId == id)); var missingScaffoldVillageIds = mappedScaffoldVillages.Where(kvp => kvp.Value == null).Select(kvp => kvp.Key).ToList(); var missingVillageData = mappedScaffoldVillages.Values.Count(v => v == null) == 0 ? new List<Scaffold.Village>() : await Profile("Get missing village data", () => ( from v in CurrentSets.Village where missingScaffoldVillageIds.Contains(v.VillageId) select v ).ToListAsync() ); var mappedMissingVillageData = missingVillageData.ToDictionary(vd => vd.VillageId, vd => vd); // Get or make CurrentVillage Profile("Populating missing village data", () => { foreach (var missingVillageId in missingScaffoldVillageIds) { var village = mappedMissingVillageData[missingVillageId]; var newCurrentVillage = new Scaffold.CurrentVillage(); newCurrentVillage.VillageId = missingVillageId; newCurrentVillage.WorldId = CurrentWorldId; newCurrentVillage.AccessGroupId = CurrentAccessGroupId; context.CurrentVillage.Add(newCurrentVillage); mappedScaffoldVillages[missingVillageId] = newCurrentVillage; } }); Profile("Generate scaffold armies", () => { foreach (var armySetJson in currentArmySetJson.TroopData) { var currentVillage = mappedScaffoldVillages[armySetJson.VillageId.Value]; var villagePlayerId = villageIdsByPlayerId[currentVillage.VillageId]; if (!Configuration.Security.AllowUploadArmyForNonOwner && villagePlayerId != CurrentPlayerId) { context.InvalidDataRecord.Add(MakeInvalidDataRecord( JsonConvert.SerializeObject(currentArmySetJson), $"Attempted to upload current army to village {villagePlayerId} but that village is not owned by the requestor" )); } var fullArmy = armySetJson.AtHome + armySetJson.Traveling + armySetJson.Supporting; currentVillage.ArmyOwned = ArmyConvert.JsonToArmy(fullArmy, CurrentWorldId, currentVillage.ArmyOwned, context, emptyIfNull: true); currentVillage.ArmyStationed = ArmyConvert.JsonToArmy(armySetJson.Stationed, CurrentWorldId, currentVillage.ArmyStationed, context, emptyIfNull: true); currentVillage.ArmyTraveling = ArmyConvert.JsonToArmy(armySetJson.Traveling, CurrentWorldId, currentVillage.ArmyTraveling, context, emptyIfNull: true); currentVillage.ArmyAtHome = ArmyConvert.JsonToArmy(armySetJson.AtHome, CurrentWorldId, currentVillage.ArmyAtHome, context, emptyIfNull: true); currentVillage.ArmySupporting = ArmyConvert.JsonToArmy(armySetJson.Supporting, CurrentWorldId, currentVillage.ArmySupporting, context, emptyIfNull: true); currentVillage.ArmyOwned.LastUpdated = CurrentServerTime; currentVillage.ArmyStationed.LastUpdated = CurrentServerTime; currentVillage.ArmyTraveling.LastUpdated = CurrentServerTime; currentVillage.ArmyAtHome.LastUpdated = CurrentServerTime; currentVillage.ArmySupporting.LastUpdated = CurrentServerTime; } }); var currentPlayer = await EFUtil.GetOrCreateCurrentPlayer(context, CurrentPlayerId, CurrentWorldId, CurrentAccessGroupId); currentPlayer.CurrentPossibleNobles = currentArmySetJson.PossibleNobles; await Profile("Save changes", () => context.SaveChangesAsync()); // Run upload history update in separate query to prevent creating multiple history // entries var userUploadHistory = await EFUtil.GetOrCreateUserUploadHistory(context, CurrentUserId); userUploadHistory.LastUploadedTroopsAt = CurrentServerTime; await context.SaveChangesAsync(); return Ok(); }
public async Task <IActionResult> Post([FromBody] JSON.Report jsonReport) { if (ModelState.IsValid) { if (!Configuration.Security.ReportIgnoreExpectedPopulationBounds && !ArmyValidate.MeetsPopulationRestrictions(jsonReport.AttackingArmy)) { context.InvalidDataRecord.Add(MakeInvalidDataRecord( JsonConvert.SerializeObject(jsonReport), "Troops in attacking army exceed possible village population" )); return(BadRequest()); } if (!Configuration.Security.ReportIgnoreExpectedPopulationBounds && !ArmyValidate.MeetsPopulationRestrictions(jsonReport.TravelingTroops)) { context.InvalidDataRecord.Add(MakeInvalidDataRecord( JsonConvert.SerializeObject(jsonReport), "Troops in traveling army exceed possible village population" )); } if (jsonReport.OccurredAt.Value > CurrentServerTime) { context.InvalidDataRecord.Add(MakeInvalidDataRecord( JsonConvert.SerializeObject(jsonReport), "The report 'OccurredAt' is in the future" )); // Return 200/OK to trick malicious actors return(Ok()); } bool isDuplicate = false; var scaffoldReport = await Profile("Find existing report by ID", () => ( from report in CurrentSets.Report.IncludeReportData() where report.ReportId == jsonReport.ReportId.Value select report ).FirstOrDefaultAsync() ); if (scaffoldReport == null) { await Profile("Find existing report by contents", async() => { var reportsMatchingDetails = await( from report in CurrentSets.Report.IncludeReportData() where report.OccuredAt == jsonReport.OccurredAt where report.AttackerPlayerId == jsonReport.AttackingPlayerId where report.AttackerVillageId == jsonReport.AttackingVillageId where report.DefenderPlayerId == jsonReport.DefendingPlayerId where report.DefenderVillageId == jsonReport.DefendingVillageId select report ).ToListAsync(); var existingDuplicate = reportsMatchingDetails.FirstOrDefault((r) => jsonReport.AttackingArmy == r.AttackerArmy && jsonReport.DefendingArmy == r.DefenderArmy && jsonReport.AttackingArmyLosses == r.AttackerLossesArmy && jsonReport.DefendingArmyLosses == r.DefenderLossesArmy && jsonReport.TravelingTroops == r.DefenderTravelingArmy ); isDuplicate = existingDuplicate != null; }); } var tx = BuildTransaction(); context.Transaction.Add(tx); if (isDuplicate) { var isIgnored = await context.IgnoredReport.AnyAsync(r => r.ReportId == jsonReport.ReportId.Value); if (!isIgnored) { context.IgnoredReport.Add(new IgnoredReport { AccessGroupId = CurrentAccessGroupId, ReportId = jsonReport.ReportId.Value, WorldId = CurrentWorldId }); } } else { Profile("Populate scaffold report", () => { if (scaffoldReport == null) { scaffoldReport = new Scaffold.Report(); scaffoldReport.WorldId = CurrentWorldId; scaffoldReport.AccessGroupId = CurrentAccessGroupId; context.Report.Add(scaffoldReport); } else { var existingJsonReport = ReportConvert.ModelToJson(scaffoldReport); if (existingJsonReport != jsonReport && scaffoldReport.TxId.HasValue) { context.ConflictingDataRecord.Add(new Scaffold.ConflictingDataRecord { ConflictingTx = tx, OldTxId = scaffoldReport.TxId.Value }); } } jsonReport.ToModel(CurrentWorldId, scaffoldReport, context); scaffoldReport.Tx = tx; }); if (jsonReport.AttackingPlayerId != null) { await Profile("Update command troop type", async() => { var lostAllTroops = jsonReport.AttackingArmy == jsonReport.AttackingArmyLosses; var command = await Model.UtilQuery.FindCommandForReport(scaffoldReport, context); if (command == null && !lostAllTroops && (jsonReport.Loyalty == null || jsonReport.Loyalty > 0)) { // WARNING - This will auto-generate a command with a random ID, // if a new TW command is uploaded with the given ID any backtime // calculations for this old command will get screwy try { await context.SaveChangesAsync(); } catch (Exception e) { throw e; } command = new Scaffold.Command(); command.Tx = tx; command.WorldId = CurrentWorldId; command.AccessGroupId = CurrentAccessGroupId; command.IsReturning = true; command.FirstSeenAt = CurrentServerTime; command.IsAttack = true; command.SourcePlayerId = jsonReport.AttackingPlayerId.Value; command.TargetPlayerId = jsonReport.DefendingPlayerId; command.SourceVillageId = jsonReport.AttackingVillageId.Value; command.TargetVillageId = jsonReport.DefendingVillageId.Value; command.LandsAt = jsonReport.OccurredAt.Value; bool madeCommand = false; // Need to auto-generate a random command ID while (!madeCommand) { try { command.CommandId = Random.NextLong >> 14; context.Add(command); await context.SaveChangesAsync(); madeCommand = true; } catch (Exception) { } } } if (command != null) { JSON.TroopType?slowestType = null; float slowestSpeed = -1; foreach (var troopType in jsonReport.AttackingArmy.Where(kvp => kvp.Value > 0).Select(kvp => kvp.Key)) { var travelSpeed = Native.ArmyStats.TravelSpeed[troopType]; if (slowestType == null) { slowestType = troopType; slowestSpeed = travelSpeed; } else if (travelSpeed > slowestSpeed) { slowestType = troopType; slowestSpeed = travelSpeed; } } var attackingVillage = await CurrentSets.Village .FromWorld(CurrentWorldId) .Where(v => v.VillageId == jsonReport.AttackingVillageId) .FirstOrDefaultAsync(); var defendingVillage = await CurrentSets.Village .FromWorld(CurrentWorldId) .Where(v => v.VillageId == jsonReport.DefendingVillageId) .FirstOrDefaultAsync(); var travelCalculator = new Features.Simulation.TravelCalculator(CurrentWorldSettings.GameSpeed, CurrentWorldSettings.UnitSpeed); var travelTime = travelCalculator.CalculateTravelTime(slowestType.Value, attackingVillage, defendingVillage); command.TroopType = slowestType.Value.ToTroopString(); command.Army = ArmyConvert.JsonToArmy(jsonReport.AttackingArmy - jsonReport.AttackingArmyLosses, CurrentWorldId, command.Army, context); if (command.Army != null) { command.Army.WorldId = CurrentWorldId; } command.ReturnsAt = scaffoldReport.OccuredAt + travelTime; command.IsReturning = true; } }); } } //if (jsonReport.Loyalty <= 0) //{ // var conquer = new Scaffold.Conquer // { // WorldId = CurrentWorldId, // OldOwner = jsonReport.DefendingPlayerId, // NewOwner = jsonReport.AttackingPlayerId, // VillageId = jsonReport.DefendingVillageId, // UnixTimestamp = new DateTimeOffset(jsonReport.OccurredAt.Value).ToUnixTimeSeconds() // }; // context.Add(conquer); //} await Profile("Save changes", () => context.SaveChangesAsync()); // Run upload history update in separate query to prevent creating multiple history // entries var userUploadHistory = await EFUtil.GetOrCreateUserUploadHistory(context, CurrentUserId); userUploadHistory.LastUploadedReportsAt = CurrentServerTime; await context.SaveChangesAsync(); return(Ok()); } else { return(BadRequest(ModelState)); } }