From ea75d182a6a76d07927500626364a71285229903 Mon Sep 17 00:00:00 2001 From: Robert Paciorek Date: Mon, 28 Jul 2025 09:43:36 +0000 Subject: [PATCH] user data export and import interfce (enhance) * update items and dragons id on import * check for viking name unique * add unique constraints in database * add simple import/export html form for localhosted srvers --- .../Common/ImportExportController.cs | 95 +++++++++++++++---- src/Model/Dragon.cs | 4 +- src/Model/User.cs | 5 +- src/Model/Viking.cs | 2 +- src/Resources/html/export.xml | 16 ++++ src/Resources/html/import.xml | 24 +++++ src/Schema/NestData.cs | 13 +++ src/Schema/StableData.cs | 22 +++++ src/Schema/UserRankData.cs | 23 +++++ src/sodoff.csproj | 6 ++ 10 files changed, 188 insertions(+), 22 deletions(-) create mode 100644 src/Resources/html/export.xml create mode 100644 src/Resources/html/import.xml create mode 100644 src/Schema/NestData.cs create mode 100644 src/Schema/StableData.cs create mode 100644 src/Schema/UserRankData.cs diff --git a/src/Controllers/Common/ImportExportController.cs b/src/Controllers/Common/ImportExportController.cs index b429adf..1631bbf 100644 --- a/src/Controllers/Common/ImportExportController.cs +++ b/src/Controllers/Common/ImportExportController.cs @@ -5,6 +5,8 @@ using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Encodings.Web; using sodoff.Model; +using sodoff.Schema; +using sodoff.Util; namespace sodoff.Controllers.Common; public class ExportController : ControllerBase { @@ -15,7 +17,7 @@ public class ExportController : ControllerBase { } [HttpPost] - [Route("ImportExport.asmx/Export")] + [Route("Export")] public IActionResult Export([FromForm] string username, [FromForm] string password) { // Authenticate user by Username User? user = ctx.Users.FirstOrDefault(e => e.Username == username); @@ -36,8 +38,8 @@ public class ExportController : ControllerBase { } [HttpPost] - [Route("ImportExport.asmx/Import")] - public IActionResult Import([FromForm] string username, [FromForm] string password, [FromForm] string vikingName, [FromForm] IFormFile dataFile) { + [Route("Import")] + public IActionResult Import([FromForm] string username, [FromForm] string password, [FromForm] string vikingName, [FromForm] IFormFile dataFile, [FromForm] string? newVikingName) { User? user = ctx.Users.FirstOrDefault(e => e.Username == username); if (user is null || new PasswordHasher().VerifyHashedPassword(null, user.Password, password) == PasswordVerificationResult.Failed) { return Unauthorized("Invalid username or password."); @@ -50,36 +52,81 @@ public class ExportController : ControllerBase { foreach (var v in user_data.Vikings) { if (v.Name == vikingName) { + if (String.IsNullOrEmpty(newVikingName)) + newVikingName = vikingName; + + if (ctx.Vikings.Count(e => e.Name == newVikingName) > 0) { + return Conflict("Viking name already in use"); + } + + if (newVikingName != vikingName) { + AvatarData avatarData = XmlUtil.DeserializeXml(v.AvatarSerialized); + avatarData.DisplayName = newVikingName; + v.AvatarSerialized = XmlUtil.SerializeXml(avatarData); + } + Viking viking = new Viking { - Uid = v.Uid, // TODO check for unique or just generate new? - Name = v.Name, // TODO check for unique + Uid = Guid.NewGuid(), + Name = newVikingName, User = user, AvatarSerialized = v.AvatarSerialized, - CreationDate = v.CreationDate, // TODO or use now? + CreationDate = DateTime.UtcNow, BirthDate = v.BirthDate, Gender = v.Gender, GameVersion = v.GameVersion }; user.Vikings.Add(viking); + Dictionary dragonIds = new(); foreach (var x in v.Dragons) { x.Viking = viking; - // TODO check EntityId for unique or just generate new? - x.Id = 0; // FIXME map old→new value for dragon id to update (stables) xml's + x.EntityId = Guid.NewGuid(); + dragonIds.Add(x.Id, x.EntityId); + x.Id = 0; ctx.Dragons.Add(x); } - foreach (var x in v.Images) { - x.Viking = viking; - ctx.Images.Add(x); - } + Dictionary itemIds = new(); foreach (var x in v.InventoryItems) { - x.Id = 0; // FIXME map old→new value for item id to update xml's and rooms + itemIds.Add(x.Id, x.ItemId); + x.Id = 0; x.Viking = viking; ctx.InventoryItems.Add(x); } + + ctx.SaveChanges(); // need for get new ids of dragons and items + + HashSet usedItemIds = new(); foreach (var x in v.Rooms) { x.Viking = viking; - ctx.Rooms.Add(x); // FIXME need update room name (if numeric) + if (int.TryParse(x.RoomId, out int roomID)) { + // numeric room name is inventory item id + // remap old value to new value based on item id value + roomID = viking.InventoryItems.FirstOrDefault(e => e.ItemId == itemIds[roomID] && !usedItemIds.Contains(e.Id)).Id; + usedItemIds.Add(roomID); + x.RoomId = roomID.ToString(); + } + ctx.Rooms.Add(x); + } + foreach (var x in v.PairData) { + x.Viking = viking; + if (x.PairId == 2014) { // stables data + foreach (var p in x.Pairs.Where(e => e.Key.StartsWith("Stable"))) { + StableData stableData = XmlUtil.DeserializeXml(p.Value); + stableData.InventoryID = viking.InventoryItems.FirstOrDefault(e => e.ItemId == stableData.ItemID && !usedItemIds.Contains(e.Id)).Id; + usedItemIds.Add(stableData.InventoryID); + foreach (var n in stableData.NestList) { + if (n.PetID != 0) + n.PetID = viking.Dragons.FirstOrDefault(d => d.EntityId == dragonIds[n.PetID]).Id; + } + p.Value = XmlUtil.SerializeXml(stableData); + } + } + ctx.PairData.Add(x); + } + + foreach (var x in v.Images) { + x.Viking = viking; + ctx.Images.Add(x); } foreach (var x in v.MissionStates) { x.Viking = viking; @@ -97,10 +144,6 @@ public class ExportController : ControllerBase { x.Viking = viking; ctx.AchievementPoints.Add(x); } - foreach (var x in v.PairData) { - x.Viking = viking; - ctx.PairData.Add(x); // FIXME need update PetID in stable XML - } foreach (var x in v.ProfileAnswers) { x.Viking = viking; ctx.ProfileAnswers.Add(x); @@ -135,7 +178,9 @@ public class ExportController : ControllerBase { v.Neighborhood.Viking = viking; ctx.Neighborhoods.Add(v.Neighborhood); } - // TODO set viking.SelectedDragon + + if (v.SelectedDragon != null) + viking.SelectedDragon = viking.Dragons.FirstOrDefault(d => d.EntityId == dragonIds[v.SelectedDragon.Id]); ctx.SaveChanges(); return Ok("OK"); @@ -143,4 +188,16 @@ public class ExportController : ControllerBase { } return Ok("Viking Not Found"); } + + [HttpGet] + [Route("Export")] + public IActionResult Export() { + return Content(XmlUtil.ReadResourceXmlString("html.export"), "application/xhtml+xml"); + } + + [HttpGet] + [Route("Import")] + public IActionResult Import() { + return Content(XmlUtil.ReadResourceXmlString("html.import"), "application/xhtml+xml"); + } } diff --git a/src/Model/Dragon.cs b/src/Model/Dragon.cs index b3e4ac3..cdfd831 100644 --- a/src/Model/Dragon.cs +++ b/src/Model/Dragon.cs @@ -1,9 +1,11 @@ -using System.ComponentModel.DataAnnotations; +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; namespace sodoff.Model; +[Index(nameof(EntityId), IsUnique = true)] public class Dragon { [Key] // [JsonIgnore] used in serialised xml (stables) diff --git a/src/Model/User.cs b/src/Model/User.cs index 2d3d579..de4beb3 100644 --- a/src/Model/User.cs +++ b/src/Model/User.cs @@ -1,7 +1,10 @@ -using System.ComponentModel.DataAnnotations; +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; namespace sodoff.Model; + +[Index(nameof(Username), IsUnique = true)] public class User { [Key] public Guid Id { get; set; } diff --git a/src/Model/Viking.cs b/src/Model/Viking.cs index e2b8ca4..86a86e9 100644 --- a/src/Model/Viking.cs +++ b/src/Model/Viking.cs @@ -5,7 +5,7 @@ using sodoff.Schema; namespace sodoff.Model; -[Index(nameof(Uid))] +[Index(nameof(Uid), IsUnique = true)] public class Viking { [Key] [JsonIgnore] diff --git a/src/Resources/html/export.xml b/src/Resources/html/export.xml new file mode 100644 index 0000000..d93c359 --- /dev/null +++ b/src/Resources/html/export.xml @@ -0,0 +1,16 @@ + + + + + Export SoDOff account + + +
+
+
+
+

+ +
+ + diff --git a/src/Resources/html/import.xml b/src/Resources/html/import.xml new file mode 100644 index 0000000..3ed291c --- /dev/null +++ b/src/Resources/html/import.xml @@ -0,0 +1,24 @@ + + + + + Import SoDOff account + + +
+
+
+
+

+ +
+
+
+
+
+

+ + +
+ + diff --git a/src/Schema/NestData.cs b/src/Schema/NestData.cs new file mode 100644 index 0000000..c115c54 --- /dev/null +++ b/src/Schema/NestData.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "NestData", Namespace = "")] +[Serializable] +public class NestData { + [XmlElement(ElementName = "PetID")] + public int PetID; + + [XmlElement(ElementName = "ID")] + public int ID; +} diff --git a/src/Schema/StableData.cs b/src/Schema/StableData.cs new file mode 100644 index 0000000..dff5268 --- /dev/null +++ b/src/Schema/StableData.cs @@ -0,0 +1,22 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "StableData", Namespace = "")] +[Serializable] +public class StableData { + [XmlElement(ElementName = "Name")] + public string Name; + + [XmlElement(ElementName = "ID")] + public int ID; + + [XmlElement(ElementName = "ItemID")] + public int ItemID; + + [XmlElement(ElementName = "InventoryID")] + public int InventoryID; + + [XmlElement(ElementName = "Nests")] + public List NestList; +} diff --git a/src/Schema/UserRankData.cs b/src/Schema/UserRankData.cs new file mode 100644 index 0000000..fa9fc48 --- /dev/null +++ b/src/Schema/UserRankData.cs @@ -0,0 +1,23 @@ +using System.Diagnostics; +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "UserRankData", Namespace = "")] +[Serializable] +public class UserRankData { + [XmlElement(ElementName = "UserID")] + public Guid UserID; + + [XmlElement(ElementName = "Points")] + public int Points; + + [XmlElement(ElementName = "CurrentRank")] + public UserRank CurrentRank; + + [XmlElement(ElementName = "MemberRank")] + public UserRank MemberRank; + + [XmlElement(ElementName = "NextRank")] + public UserRank NextRank; +} diff --git a/src/sodoff.csproj b/src/sodoff.csproj index cab8f4d..4e30ed9 100644 --- a/src/sodoff.csproj +++ b/src/sodoff.csproj @@ -155,5 +155,11 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest +