diff --git a/README.md b/README.md index 991302b..1ef2684 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Then run School of Dragons. - GetQuestions (doesn't return all questions, probably doesn't need to) - GetRules (doesn't return any rules, probably doesn't need to) - GetSubscriptionInfo (always returns member, with end date 10 years from now) +- SendRawGameData - SetNextItemState - SetTaskState (only the TaskCanBeDone status is supported; might contain a serious problem - see the MissionService class) - SetUserAchievementAndGetReward (works like SetAchievementAndGetReward) @@ -113,6 +114,8 @@ Then run School of Dragons. #### Partially implemented - ApplyPayout (doesn't calculate rewards properly) - ApplyRewards +- GetGameDataByGame (friend tab displays all players - friend filter is not yet implemented because friend lists are not implemented) +- GetGameDataByGameForDateRange (friend tab displays all players) - GetUserAchievements (used by Magic & Mythies) - GetUserRoomList (room categories are not implemented, but it's enough for SoD) - ProcessRewardedItems (gives gems, but doesn't give gold, gold is not yet implemented) diff --git a/mitm-redirect.py b/mitm-redirect.py index dc88cae..00706cc 100644 --- a/mitm-redirect.py +++ b/mitm-redirect.py @@ -81,6 +81,9 @@ methods = [ 'ProcessRewardedItems', 'ApplyPayout', 'RedeemMysteryBoxItems', + 'SendRawGameData', + 'GetGameDataByGame', + 'GetGameDataByGameForDateRange', ] def routable(path): diff --git a/src/Controllers/Common/ContentController.cs b/src/Controllers/Common/ContentController.cs index 3599d23..15aaa61 100644 --- a/src/Controllers/Common/ContentController.cs +++ b/src/Controllers/Common/ContentController.cs @@ -1,11 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; using sodoff.Attributes; using sodoff.Model; using sodoff.Schema; using sodoff.Services; using sodoff.Util; using System; +using System.Globalization; namespace sodoff.Controllers.Common; public class ContentController : Controller { @@ -17,8 +17,9 @@ public class ContentController : Controller { private RoomService roomService; private AchievementService achievementService; private InventoryService inventoryService; + private GameDataService gameDataService; private Random random = new Random(); - public ContentController(DBContext ctx, KeyValueService keyValueService, ItemService itemService, MissionService missionService, RoomService roomService, AchievementService achievementService, InventoryService inventoryService) { + public ContentController(DBContext ctx, KeyValueService keyValueService, ItemService itemService, MissionService missionService, RoomService roomService, AchievementService achievementService, InventoryService inventoryService, GameDataService gameDataService) { this.ctx = ctx; this.keyValueService = keyValueService; this.itemService = itemService; @@ -26,6 +27,7 @@ public class ContentController : Controller { this.roomService = roomService; this.achievementService = achievementService; this.inventoryService = inventoryService; + this.gameDataService = gameDataService; } [HttpPost] @@ -1332,6 +1334,31 @@ public class ContentController : Controller { }); } + [HttpPost] + [Produces("application/xml")] + [Route("ContentWebService.asmx/SendRawGameData")] + [VikingSession(UseLock = true)] + public IActionResult SendRawGameData(Viking viking, [FromForm] int gameId, bool isMultiplayer, int difficulty, int gameLevel, string xmlDocumentData, bool win, bool loss) { + return Ok(gameDataService.SaveGameData(viking, gameId, isMultiplayer, difficulty, gameLevel, xmlDocumentData, win, loss)); + } + + [HttpPost] + [Produces("application/xml")] + [Route("ContentWebService.asmx/GetGameDataByGame")] + [VikingSession(UseLock = true)] + public IActionResult GetGameDataByGame(Viking viking, [FromForm] int gameId, bool isMultiplayer, int difficulty, int gameLevel, string key, int count, bool AscendingOrder, int score, bool buddyFilter) { + return Ok(gameDataService.GetGameData(viking, gameId, isMultiplayer, difficulty, gameLevel, key, count, AscendingOrder, buddyFilter)); + } + + [HttpPost] + [Produces("application/xml")] + [Route("V2/ContentWebService.asmx/GetGameDataByGameForDateRange")] + [VikingSession(UseLock = true)] + public IActionResult GetGameDataByGameForDateRange(Viking viking, [FromForm] int gameId, bool isMultiplayer, int difficulty, int gameLevel, string key, int count, bool AscendingOrder, int score, string startDate, string endDate, bool buddyFilter) { + CultureInfo usCulture = new CultureInfo("en-US", false); + return Ok(gameDataService.GetGameData(viking, gameId, isMultiplayer, difficulty, gameLevel, key, count, AscendingOrder, buddyFilter, DateTime.Parse(startDate, usCulture), DateTime.Parse(endDate, usCulture))); + } + private static RaisedPetData GetRaisedPetDataFromDragon (Dragon dragon, int? selectedDragonId = null) { if (selectedDragonId is null) selectedDragonId = dragon.Viking.SelectedDragonId; diff --git a/src/Model/DBContext.cs b/src/Model/DBContext.cs index 46d462b..7640af4 100644 --- a/src/Model/DBContext.cs +++ b/src/Model/DBContext.cs @@ -14,6 +14,8 @@ public class DBContext : DbContext { public DbSet MissionStates { get; set; } = null!; public DbSet Rooms { get; set; } = null!; public DbSet RoomItems { get; set; } = null!; + public DbSet GameData { get; set; } = null; + public DbSet GameDataPairs { get; set; } = null; public string DbPath { get; } @@ -79,6 +81,9 @@ public class DBContext : DbContext { .WithOne() .HasForeignKey(e => e.SelectedDragonId); + builder.Entity().HasMany(v => v.GameData) + .WithOne(e => e.Viking); + // Dragons builder.Entity().HasOne(d => d.Viking) .WithMany(e => e.Dragons) @@ -134,6 +139,17 @@ public class DBContext : DbContext { .WithMany(r => r.Items) .HasForeignKey(e => e.RoomId); + // GameData + + builder.Entity().HasOne(e => e.Viking) + .WithMany(e => e.GameData); + + builder.Entity().HasMany(e => e.GameDataPairs) + .WithOne(e => e.GameData); + + builder.Entity().HasOne(e => e.GameData) + .WithMany(e => e.GameDataPairs); + // Others .. builder.Entity().HasOne(s => s.Viking) .WithMany(e => e.Images) diff --git a/src/Model/GameData.cs b/src/Model/GameData.cs new file mode 100644 index 0000000..56f801c --- /dev/null +++ b/src/Model/GameData.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; + +namespace sodoff.Model; +public class GameData { + [Key] + public int Id { get; set; } + + public int VikingId { get; set; } + + public int GameId { get; set; } + public int Difficulty { get; set; } + public int GameLevel { get; set; } + public DateTime DatePlayed { get; set; } + public bool IsMultiplayer { get; set; } + public bool Win { get; set; } + public bool Loss { get; set; } + public virtual ICollection GameDataPairs { get; set; } = null!; + public virtual Viking Viking { get; set; } = null!; + +} diff --git a/src/Model/GameDataPair.cs b/src/Model/GameDataPair.cs new file mode 100644 index 0000000..9543936 --- /dev/null +++ b/src/Model/GameDataPair.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; + +namespace sodoff.Model; +public class GameDataPair { + [Key] + public int Id { get; set; } + public int GameDataId { get; set; } + public string Name { get; set; } = null!; + public int Value { get; set; } + public virtual GameData GameData { get; set; } = null!; +} diff --git a/src/Model/Viking.cs b/src/Model/Viking.cs index 6017b21..6b0e9af 100644 --- a/src/Model/Viking.cs +++ b/src/Model/Viking.cs @@ -30,5 +30,6 @@ public class Viking { public virtual ICollection AchievementPoints { get; set; } = null!; public virtual ICollection PairData { get; set; } = null!; public virtual ICollection InventoryItems { get; set; } = null!; + public virtual ICollection GameData { get; set; } = null!; public virtual Dragon? SelectedDragon { get; set; } } diff --git a/src/Program.cs b/src/Program.cs index 085564d..03f6181 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -25,6 +25,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); diff --git a/src/Services/GameDataService.cs b/src/Services/GameDataService.cs new file mode 100644 index 0000000..c4c396a --- /dev/null +++ b/src/Services/GameDataService.cs @@ -0,0 +1,122 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using sodoff.Model; +using sodoff.Schema; +using System.Text.RegularExpressions; + +namespace sodoff.Services; +public class GameDataService { + private readonly DBContext ctx; + + public GameDataService(DBContext ctx) { + this.ctx = ctx; + } + + public bool SaveGameData(Viking viking, int gameId, bool isMultiplayer, int difficulty, int gameLevel, string xmlDocumentData, bool win, bool loss) { + Model.GameData? gameData = viking.GameData.FirstOrDefault(x => x.GameId == gameId && x.IsMultiplayer == isMultiplayer && x.Difficulty == difficulty && x.GameLevel == gameLevel && x.Win == win && x.Loss == loss); + if (gameData == null) { + gameData = new Model.GameData { + GameId = gameId, + IsMultiplayer = isMultiplayer, + Difficulty = difficulty, + GameLevel = gameLevel, + Win = win, + Loss = loss, + GameDataPairs = new List() + }; + viking.GameData.Add(gameData); + + } + gameData.DatePlayed = DateTime.Now; + SavePairs(gameData, xmlDocumentData); + ctx.SaveChanges(); + return true; + } + + public GameDataSummary GetGameData(Viking viking, [FromForm] int gameId, bool isMultiplayer, int difficulty, int gameLevel, string key, int count, bool AscendingOrder, bool buddyFilter, DateTime? startDate = null, DateTime? endDate = null) { + // TODO: Buddy filter + List selectedData; + IQueryable query = ctx.GameData.Where(x => x.GameId == gameId && x.IsMultiplayer == false && x.Difficulty == difficulty && x.GameLevel == gameLevel); + + if (startDate != null && endDate != null) + query = query.Where(x => x.DatePlayed >= startDate && x.DatePlayed <= endDate.Value.AddMinutes(2)); + + selectedData = query.SelectMany(e => e.GameDataPairs) + .Where(x => x.Name == key) + .Select(e => new GameDataResponse(e.GameData.Viking.Name, e.GameData.Viking.Uid, e.GameData.DatePlayed, e.GameData.Win, e.GameData.Loss, e.Value)) + .Take(count) + .ToList(); + + if (AscendingOrder) + selectedData.Sort((a, b) => a.Value.CompareTo(b.Value)); + else + selectedData.Sort((a, b) => b.Value.CompareTo(a.Value)); + + return GetSummaryFromResponse(viking, isMultiplayer, difficulty, gameLevel, key, selectedData); + } + + private GameDataSummary GetSummaryFromResponse(Viking viking, bool isMultiplayer, int difficulty, int gameLevel, string key, List selectedData) { + GameDataSummary gameData = new(); + gameData.IsMultiplayer = isMultiplayer; + gameData.Difficulty = difficulty; + gameData.GameLevel = gameLevel; + gameData.Key = key; + gameData.UserPosition = -1; + gameData.GameDataList = new Schema.GameData[selectedData.Count]; + for (int i = 0; i < selectedData.Count; i++) { + Schema.GameData data = new(); + data.RankID = i + 1; + data.IsMember = true; + data.UserName = selectedData[i].Name; + data.Value = selectedData[i].Value; + data.DatePlayed = selectedData[i].DatePlayed; + data.Win = selectedData[i].Win ? 1 : 0; + data.Loss = selectedData[i].Loss ? 1 : 0; + data.UserID = selectedData[i].Uid; + gameData.GameDataList[i] = data; + if (data.UserName == viking.Name && gameData.UserPosition == -1) + gameData.UserPosition = i; + } + if (gameData.UserPosition == -1) + gameData.UserPosition = selectedData.Count; + return gameData; + } + + private void SavePairs(Model.GameData gameData, string xmlDocumentData) { + foreach (var pair in GetGameDataPairs(xmlDocumentData)) { + GameDataPair? dbPair = gameData.GameDataPairs.FirstOrDefault(x => x.Name == pair.Name); + if (dbPair == null) + gameData.GameDataPairs.Add(pair); + else + dbPair.Value = pair.Value; + } + } + + private ICollection GetGameDataPairs(string xmlDocumentData) { + List pairs = new(); + foreach (Match match in Regex.Matches(xmlDocumentData, @"<(\w+)>(.*?)<\/\1>")) { + pairs.Add(new GameDataPair { + Name = match.Groups[1].Value, + Value = int.Parse(match.Groups[2].Value) + }); + } + return pairs; + } + + struct GameDataResponse { + public GameDataResponse(string Name, Guid Uid, DateTime DatePlayed, bool Win, bool Loss, int Value) { + this.Name = Name; + this.Uid = Uid; + this.DatePlayed = DatePlayed; + this.Win = Win; + this.Loss = Loss; + this.Value = Value; + } + public string Name; + public Guid Uid; + public DateTime DatePlayed; + public bool Win; + public bool Loss; + public int Value; + } +}