From 3877a8ba376065e31c633ebabcccd699bb88cc03 Mon Sep 17 00:00:00 2001
From: ABrokenTV <110191923+ABrokenTV@users.noreply.github.com>
Date: Mon, 23 Jun 2025 03:51:11 -0500
Subject: [PATCH 01/18] Fix spawn for Skulder in "Boneknapper, Kidnapped?
[2020]" (#23)
---
src/Resources/missions/missions_sod.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Resources/missions/missions_sod.xml b/src/Resources/missions/missions_sod.xml
index c9f1525..28a10dc 100644
--- a/src/Resources/missions/missions_sod.xml
+++ b/src/Resources/missions/missions_sod.xml
@@ -49061,7 +49061,7 @@
2897
Dreadfall2020Q01
10
- <Data><Repeat>0</Repeat><Hidden>0</Hidden><Reward><Asset>PfUiMissionRewardDBDO</Asset></Reward><Random>0</Random><Title><Text>Boneknapper, Kidnapped? [2020]</Text></Title><Icon>RS_DATA/SquadTacticsDreadfallDO.unity3d/IcoDWDragonsHUDDazeReward</Icon></Data>
+ <Data><Repeat>0</Repeat><Hidden>0</Hidden><Reward><Asset>PfUiMissionRewardDBDO</Asset></Reward><Random>0</Random><Title><Text>Boneknapper, Kidnapped? [2020]</Text></Title><Icon>RS_DATA/SquadTacticsDreadfallDO.unity3d/IcoDWDragonsHUDDazeReward</Icon><Setup><Scene>HubDragonsEdgeDO</Scene><Asset>RS_DATA/PfDWArchaeologist.unity3d/PfDWArchaeologist</Asset><Location>PfMarker_Archaeologist</Location><Recursive>false</Recursive><Persistent>false</Persistent></Setup></Data>
false
0
From 640d7ba664c17ad73db2ca8391fe8cf13e486e6c Mon Sep 17 00:00:00 2001
From: Spirtix
Date: Thu, 26 Jun 2025 17:43:59 +0200
Subject: [PATCH 02/18] switch to .net 9
---
src/sodoff.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sodoff.csproj b/src/sodoff.csproj
index b8fe201..83633a9 100644
--- a/src/sodoff.csproj
+++ b/src/sodoff.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net9.0
enable
enable
true
From 3ffced2b60b20849a0a289d684a085257147d3f8 Mon Sep 17 00:00:00 2001
From: Spirtix
Date: Thu, 26 Jun 2025 18:06:29 +0200
Subject: [PATCH 03/18] password rehashing asp net identity v3 uses a new
hashing algorithm (hmac-sha256)
---
src/Controllers/Common/AuthenticationController.cs | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/Controllers/Common/AuthenticationController.cs b/src/Controllers/Common/AuthenticationController.cs
index 3fc5548..465680a 100644
--- a/src/Controllers/Common/AuthenticationController.cs
+++ b/src/Controllers/Common/AuthenticationController.cs
@@ -48,11 +48,15 @@ public class AuthenticationController : Controller {
} else {
user = ctx.Users.FirstOrDefault(e => e.Username == data.UserName);
}
-
- if (user is null || new PasswordHasher
-
+
-
+
-
+
-
+
From 2aac24c37eb62929d213a71bed5ad9f80b82fe05 Mon Sep 17 00:00:00 2001
From: Spirtix
Date: Fri, 27 Jun 2025 14:17:05 +0200
Subject: [PATCH 06/18] fix indent
---
src/Schema/AchievementReward.cs | 62 +++++------
src/Schema/BluePrint.cs | 12 +-
src/Schema/BluePrintDeductibleConfig.cs | 14 +--
src/Schema/BluePrintSpecification.cs | 36 +++---
src/Schema/CompletionAction.cs | 4 +-
src/Schema/ItemAttribute.cs | 8 +-
src/Schema/ItemAvailability.cs | 8 +-
src/Schema/ItemData.cs | 142 ++++++++++++------------
src/Schema/ItemDataCategory.cs | 12 +-
src/Schema/ItemDataRelationship.cs | 20 ++--
src/Schema/ItemDataRollover.cs | 8 +-
src/Schema/ItemDataTexture.cs | 16 +--
src/Schema/ItemPossibleStatsMap.cs | 16 +--
src/Schema/ItemSaleConfig.cs | 20 ++--
src/Schema/ItemStat.cs | 16 +--
src/Schema/ItemState.cs | 22 ++--
src/Schema/ItemStateCriteria.cs | 4 +-
src/Schema/ItemStateRule.cs | 8 +-
src/Schema/ItemStatsMap.cs | 12 +-
src/Schema/Mission.cs | 4 +-
src/Schema/MissionCriteria.cs | 2 +-
src/Schema/MissionRule.cs | 2 +-
src/Schema/Pair.cs | 16 +--
src/Schema/PairData.cs | 2 +-
src/Schema/PrerequisiteItem.cs | 2 +-
src/Schema/RuleItem.cs | 2 +-
src/Schema/Stat.cs | 20 ++--
src/Schema/StatRangeMap.cs | 20 ++--
src/Schema/Task.cs | 2 +-
src/Schema/UserItemData.cs | 40 +++----
30 files changed, 276 insertions(+), 276 deletions(-)
diff --git a/src/Schema/AchievementReward.cs b/src/Schema/AchievementReward.cs
index 20f0a26..b6f4f48 100644
--- a/src/Schema/AchievementReward.cs
+++ b/src/Schema/AchievementReward.cs
@@ -5,11 +5,11 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "AR", Namespace = "")]
[Serializable]
public class AchievementReward {
- public AchievementReward() {}
+ public AchievementReward() { }
public AchievementReward(AchievementReward other) {
- if (other.UserItem != null)
- UserItem = new UserItemData(other.UserItem);
+ if (other.UserItem != null)
+ UserItem = new UserItemData(other.UserItem);
Amount = other.Amount;
PointTypeID = other.PointTypeID;
@@ -26,45 +26,45 @@ public class AchievementReward {
}
[XmlElement(ElementName = "ui", IsNullable = true)]
- public UserItemData UserItem { get; set; }
+ public UserItemData UserItem { get; set; }
- [XmlElement(ElementName = "a")]
- public int? Amount;
+ [XmlElement(ElementName = "a")]
+ public int? Amount;
- [XmlElement(ElementName = "p", IsNullable = true)]
- public AchievementPointTypes? PointTypeID;
+ [XmlElement(ElementName = "p", IsNullable = true)]
+ public AchievementPointTypes? PointTypeID;
- [XmlElement(ElementName = "ii")]
- public int ItemID;
+ [XmlElement(ElementName = "ii")]
+ public int ItemID;
- [XmlElement(ElementName = "i", IsNullable = true)]
- public Guid? EntityID;
+ [XmlElement(ElementName = "i", IsNullable = true)]
+ public Guid? EntityID;
- [XmlElement(ElementName = "t")]
- public int EntityTypeID;
+ [XmlElement(ElementName = "t")]
+ public int EntityTypeID;
- [XmlElement(ElementName = "r")]
- public int RewardID;
+ [XmlElement(ElementName = "r")]
+ public int RewardID;
- [XmlElement(ElementName = "ai")]
- public int AchievementID;
+ [XmlElement(ElementName = "ai")]
+ public int AchievementID;
- [XmlElement(ElementName = "amulti")]
- public bool AllowMultiple;
+ [XmlElement(ElementName = "amulti")]
+ public bool AllowMultiple;
- [XmlElement(ElementName = "mina", IsNullable = true)]
- public int? MinAmount;
+ [XmlElement(ElementName = "mina", IsNullable = true)]
+ public int? MinAmount;
- [XmlElement(ElementName = "maxa", IsNullable = true)]
- public int? MaxAmount;
+ [XmlElement(ElementName = "maxa", IsNullable = true)]
+ public int? MaxAmount;
- [XmlElement(ElementName = "d", IsNullable = true)]
- public DateTime? Date;
+ [XmlElement(ElementName = "d", IsNullable = true)]
+ public DateTime? Date;
- [XmlElement(ElementName = "cid")]
- public int CommonInventoryID;
+ [XmlElement(ElementName = "cid")]
+ public int CommonInventoryID;
- public AchievementReward Clone() {
- return (AchievementReward) this.MemberwiseClone();
- }
+ public AchievementReward Clone() {
+ return (AchievementReward)this.MemberwiseClone();
+ }
}
diff --git a/src/Schema/BluePrint.cs b/src/Schema/BluePrint.cs
index e7f81b8..47b48d9 100644
--- a/src/Schema/BluePrint.cs
+++ b/src/Schema/BluePrint.cs
@@ -6,7 +6,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "BP", Namespace = "", IsNullable = true)]
[Serializable]
public class BluePrint {
- public BluePrint() {}
+ public BluePrint() { }
public BluePrint(BluePrint other) {
Deductibles = other.Deductibles.Select(d => new BluePrintDeductibleConfig(d)).ToList();
@@ -15,11 +15,11 @@ public class BluePrint {
}
[XmlElement(ElementName = "BPDC", IsNullable = true)]
- public List Deductibles { get; set; }
+ public List Deductibles { get; set; }
- [XmlElement(ElementName = "ING", IsNullable = false)]
- public List Ingredients { get; set; }
+ [XmlElement(ElementName = "ING", IsNullable = false)]
+ public List Ingredients { get; set; }
- [XmlElement(ElementName = "OUT", IsNullable = false)]
- public List Outputs { get; set; }
+ [XmlElement(ElementName = "OUT", IsNullable = false)]
+ public List Outputs { get; set; }
}
diff --git a/src/Schema/BluePrintDeductibleConfig.cs b/src/Schema/BluePrintDeductibleConfig.cs
index a83012c..e6ceb8c 100644
--- a/src/Schema/BluePrintDeductibleConfig.cs
+++ b/src/Schema/BluePrintDeductibleConfig.cs
@@ -15,14 +15,14 @@ public class BluePrintDeductibleConfig {
}
[XmlElement(ElementName = "BPIID", IsNullable = false)]
- public int BluePrintItemID { get; set; }
+ public int BluePrintItemID { get; set; }
- [XmlElement(ElementName = "DT", IsNullable = false)]
- public DeductibleType DeductibleType { get; set; }
+ [XmlElement(ElementName = "DT", IsNullable = false)]
+ public DeductibleType DeductibleType { get; set; }
- [XmlElement(ElementName = "IID", IsNullable = true)]
- public int? ItemID { get; set; }
+ [XmlElement(ElementName = "IID", IsNullable = true)]
+ public int? ItemID { get; set; }
- [XmlElement(ElementName = "QTY", IsNullable = false)]
- public int Quantity { get; set; }
+ [XmlElement(ElementName = "QTY", IsNullable = false)]
+ public int Quantity { get; set; }
}
diff --git a/src/Schema/BluePrintSpecification.cs b/src/Schema/BluePrintSpecification.cs
index 2851c8c..6146f1d 100644
--- a/src/Schema/BluePrintSpecification.cs
+++ b/src/Schema/BluePrintSpecification.cs
@@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace sodoff.Schema;
public class BluePrintSpecification {
- public BluePrintSpecification() {}
+ public BluePrintSpecification() { }
public BluePrintSpecification(BluePrintSpecification other) {
BluePrintSpecID = other.BluePrintSpecID;
@@ -17,30 +17,30 @@ public class BluePrintSpecification {
}
[XmlElement(ElementName = "BPSID", IsNullable = false)]
- public int BluePrintSpecID { get; set; }
+ public int BluePrintSpecID { get; set; }
- [XmlElement(ElementName = "BPIID", IsNullable = false)]
- public int BluePrintItemID { get; set; }
+ [XmlElement(ElementName = "BPIID", IsNullable = false)]
+ public int BluePrintItemID { get; set; }
- public bool ShouldSerializeBluePrintItemID() { return BluePrintItemID != 0; }
+ public bool ShouldSerializeBluePrintItemID() { return BluePrintItemID != 0; }
- [XmlElement(ElementName = "IID", IsNullable = true)]
- public int? ItemID { get; set; }
+ [XmlElement(ElementName = "IID", IsNullable = true)]
+ public int? ItemID { get; set; }
- [XmlElement(ElementName = "CID", IsNullable = true)]
- public int? CategoryID { get; set; }
+ [XmlElement(ElementName = "CID", IsNullable = true)]
+ public int? CategoryID { get; set; }
- [XmlElement(ElementName = "IR", IsNullable = true)]
- public ItemRarity? ItemRarity { get; set; }
+ [XmlElement(ElementName = "IR", IsNullable = true)]
+ public ItemRarity? ItemRarity { get; set; }
- [XmlElement(ElementName = "T", IsNullable = true)]
- public ItemTier? Tier { get; set; }
+ [XmlElement(ElementName = "T", IsNullable = true)]
+ public ItemTier? Tier { get; set; }
- [XmlElement(ElementName = "QTY", IsNullable = false)]
- public int Quantity { get; set; }
+ [XmlElement(ElementName = "QTY", IsNullable = false)]
+ public int Quantity { get; set; }
- [XmlElement(ElementName = "ST", IsNullable = true)]
- public SpecificationType? SpecificationType { get; set; }
+ [XmlElement(ElementName = "ST", IsNullable = true)]
+ public SpecificationType? SpecificationType { get; set; }
- public bool ShouldSerializeSpecificationType() { return SpecificationType != null; }
+ public bool ShouldSerializeSpecificationType() { return SpecificationType != null; }
}
diff --git a/src/Schema/CompletionAction.cs b/src/Schema/CompletionAction.cs
index 8e9e9e2..5d49da3 100644
--- a/src/Schema/CompletionAction.cs
+++ b/src/Schema/CompletionAction.cs
@@ -4,12 +4,12 @@ namespace sodoff.Schema;
[Serializable]
public class CompletionAction {
- public CompletionAction() {}
+ public CompletionAction() { }
public CompletionAction(CompletionAction other) {
Transition = other.Transition;
}
[XmlElement(ElementName = "Transition")]
- public StateTransition Transition;
+ public StateTransition Transition;
}
diff --git a/src/Schema/ItemAttribute.cs b/src/Schema/ItemAttribute.cs
index a5c3056..bfa76d8 100644
--- a/src/Schema/ItemAttribute.cs
+++ b/src/Schema/ItemAttribute.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "AT", Namespace = "")]
[Serializable]
public class ItemAttribute {
- public ItemAttribute() {}
+ public ItemAttribute() { }
public ItemAttribute(ItemAttribute other) {
Key = other.Key;
@@ -13,8 +13,8 @@ public class ItemAttribute {
}
[XmlElement(ElementName = "k")]
- public string Key;
+ public string Key;
- [XmlElement(ElementName = "v")]
- public string Value;
+ [XmlElement(ElementName = "v")]
+ public string Value;
}
diff --git a/src/Schema/ItemAvailability.cs b/src/Schema/ItemAvailability.cs
index 898a666..3b79bc6 100644
--- a/src/Schema/ItemAvailability.cs
+++ b/src/Schema/ItemAvailability.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "Availability", Namespace = "")]
[Serializable]
public class ItemAvailability {
- public ItemAvailability() {}
+ public ItemAvailability() { }
public ItemAvailability(ItemAvailability other) {
StartDate = other.StartDate;
@@ -13,8 +13,8 @@ public class ItemAvailability {
}
[XmlElement(ElementName = "sdate", IsNullable = true)]
- public DateTime? StartDate;
+ public DateTime? StartDate;
- [XmlElement(ElementName = "edate", IsNullable = true)]
- public DateTime? EndDate;
+ [XmlElement(ElementName = "edate", IsNullable = true)]
+ public DateTime? EndDate;
}
diff --git a/src/Schema/ItemData.cs b/src/Schema/ItemData.cs
index c8b591a..73c5762 100644
--- a/src/Schema/ItemData.cs
+++ b/src/Schema/ItemData.cs
@@ -4,7 +4,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "I", Namespace = "", IsNullable = true)]
public class ItemData {
- public ItemData() {}
+ public ItemData() { }
public ItemData(ItemData other) {
ItemStates = other.ItemStates.Select(s => new ItemState(s)).ToList();
@@ -43,108 +43,108 @@ public class ItemData {
}
[XmlElement(ElementName = "is")]
- public List ItemStates { get; set; }
+ public List ItemStates { get; set; }
- [XmlElement(ElementName = "ir", IsNullable = true)]
- public ItemRarity? ItemRarity { get; set; }
+ [XmlElement(ElementName = "ir", IsNullable = true)]
+ public ItemRarity? ItemRarity { get; set; }
- [XmlElement(ElementName = "ipsm", IsNullable = true)]
- public ItemPossibleStatsMap PossibleStatsMap { get; set; }
+ [XmlElement(ElementName = "ipsm", IsNullable = true)]
+ public ItemPossibleStatsMap PossibleStatsMap { get; set; }
- [XmlElement(ElementName = "ism", IsNullable = true)]
- public ItemStatsMap ItemStatsMap { get; set; }
+ [XmlElement(ElementName = "ism", IsNullable = true)]
+ public ItemStatsMap ItemStatsMap { get; set; }
- [XmlElement(ElementName = "iscs", IsNullable = true)]
- public ItemSaleConfig[] ItemSaleConfigs { get; set; }
+ [XmlElement(ElementName = "iscs", IsNullable = true)]
+ public ItemSaleConfig[] ItemSaleConfigs { get; set; }
- [XmlElement(ElementName = "bp", IsNullable = true)]
- public BluePrint BluePrint { get; set; }
+ [XmlElement(ElementName = "bp", IsNullable = true)]
+ public BluePrint BluePrint { get; set; }
- [XmlElement(ElementName = "an")]
- public string AssetName;
+ [XmlElement(ElementName = "an")]
+ public string AssetName;
- [XmlElement(ElementName = "at", IsNullable = true)]
- public ItemAttribute[] Attribute;
+ [XmlElement(ElementName = "at", IsNullable = true)]
+ public ItemAttribute[] Attribute;
- [XmlElement(ElementName = "c")]
- public ItemDataCategory[] Category;
+ [XmlElement(ElementName = "c")]
+ public ItemDataCategory[] Category;
- [XmlElement(ElementName = "ct")]
- public int Cost;
+ [XmlElement(ElementName = "ct")]
+ public int Cost;
- [XmlElement(ElementName = "ct2")]
- public int CashCost;
+ [XmlElement(ElementName = "ct2")]
+ public int CashCost;
- [XmlElement(ElementName = "cp")]
- public int CreativePoints;
+ [XmlElement(ElementName = "cp")]
+ public int CreativePoints;
- [XmlElement(ElementName = "d")]
- public string Description;
+ [XmlElement(ElementName = "d")]
+ public string Description;
- [XmlElement(ElementName = "icn")]
- public string IconName;
+ [XmlElement(ElementName = "icn")]
+ public string IconName;
- [XmlElement(ElementName = "im")]
- public int InventoryMax;
+ [XmlElement(ElementName = "im")]
+ public int InventoryMax;
- [XmlElement(ElementName = "id")]
- public int ItemID;
+ [XmlElement(ElementName = "id")]
+ public int ItemID;
- [XmlElement(ElementName = "itn")]
- public string ItemName;
+ [XmlElement(ElementName = "itn")]
+ public string ItemName;
- [XmlElement(ElementName = "itnp")]
- public string ItemNamePlural;
+ [XmlElement(ElementName = "itnp")]
+ public string ItemNamePlural;
- [XmlElement(ElementName = "l")]
- public bool Locked;
+ [XmlElement(ElementName = "l")]
+ public bool Locked;
- [XmlElement(ElementName = "g", IsNullable = true)]
- public string Geometry2;
+ [XmlElement(ElementName = "g", IsNullable = true)]
+ public string Geometry2;
- [XmlElement(ElementName = "ro", IsNullable = true)]
- public ItemDataRollover Rollover;
+ [XmlElement(ElementName = "ro", IsNullable = true)]
+ public ItemDataRollover Rollover;
- [XmlElement(ElementName = "rid", IsNullable = true)]
- public int? RankId;
+ [XmlElement(ElementName = "rid", IsNullable = true)]
+ public int? RankId;
- [XmlElement(ElementName = "r")]
- public ItemDataRelationship[] Relationship;
+ [XmlElement(ElementName = "r")]
+ public ItemDataRelationship[] Relationship;
- [XmlElement(ElementName = "s")]
- public bool Stackable;
+ [XmlElement(ElementName = "s")]
+ public bool Stackable;
- [XmlElement(ElementName = "as")]
- public bool AllowStacking;
+ [XmlElement(ElementName = "as")]
+ public bool AllowStacking;
- [XmlElement(ElementName = "sf")]
- public int SaleFactor;
+ [XmlElement(ElementName = "sf")]
+ public int SaleFactor;
- [XmlElement(ElementName = "t")]
- public ItemDataTexture[] Texture;
+ [XmlElement(ElementName = "t")]
+ public ItemDataTexture[] Texture;
- [XmlElement(ElementName = "u")]
- public int Uses;
+ [XmlElement(ElementName = "u")]
+ public int Uses;
- [XmlElement(ElementName = "av")]
- public ItemAvailability[] Availability;
+ [XmlElement(ElementName = "av")]
+ public ItemAvailability[] Availability;
- [XmlElement(ElementName = "rtid")]
- public int RewardTypeID;
+ [XmlElement(ElementName = "rtid")]
+ public int RewardTypeID;
- [XmlElement(ElementName = "p", IsNullable = true)]
- public int? Points;
+ [XmlElement(ElementName = "p", IsNullable = true)]
+ public int? Points;
- [XmlIgnore]
- public float NormalDiscoutModifier;
+ [XmlIgnore]
+ public float NormalDiscoutModifier;
- [XmlIgnore]
- public float MemberDiscountModifier;
+ [XmlIgnore]
+ public float MemberDiscountModifier;
- [XmlIgnore]
- public float FinalDiscoutModifier {
- get {
- return Math.Min(1f, (1f - NormalDiscoutModifier) * (1f - MemberDiscountModifier));
+ [XmlIgnore]
+ public float FinalDiscoutModifier {
+ get {
+ return Math.Min(1f, (1f - NormalDiscoutModifier) * (1f - MemberDiscountModifier));
}
- }
+ }
}
diff --git a/src/Schema/ItemDataCategory.cs b/src/Schema/ItemDataCategory.cs
index 6e5cb55..d9b31b6 100644
--- a/src/Schema/ItemDataCategory.cs
+++ b/src/Schema/ItemDataCategory.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "IC", Namespace = "")]
[Serializable]
public class ItemDataCategory {
- public ItemDataCategory() {}
+ public ItemDataCategory() { }
public ItemDataCategory(ItemDataCategory other) {
CategoryId = other.CategoryId;
@@ -14,11 +14,11 @@ public class ItemDataCategory {
}
[XmlElement(ElementName = "cid")]
- public int CategoryId;
+ public int CategoryId;
- [XmlElement(ElementName = "cn")]
- public string CategoryName;
+ [XmlElement(ElementName = "cn")]
+ public string CategoryName;
- [XmlElement(ElementName = "i", IsNullable = true)]
- public string IconName;
+ [XmlElement(ElementName = "i", IsNullable = true)]
+ public string IconName;
}
diff --git a/src/Schema/ItemDataRelationship.cs b/src/Schema/ItemDataRelationship.cs
index f340ecf..58c0817 100644
--- a/src/Schema/ItemDataRelationship.cs
+++ b/src/Schema/ItemDataRelationship.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "IRE", Namespace = "")]
[Serializable]
public class ItemDataRelationship {
- public ItemDataRelationship() {}
+ public ItemDataRelationship() { }
public ItemDataRelationship(ItemDataRelationship other) {
Type = other.Type;
@@ -16,17 +16,17 @@ public class ItemDataRelationship {
}
[XmlElement(ElementName = "t")]
- public string Type;
+ public string Type;
- [XmlElement(ElementName = "id")]
- public int ItemId;
+ [XmlElement(ElementName = "id")]
+ public int ItemId;
- [XmlElement(ElementName = "wt")]
- public int Weight;
+ [XmlElement(ElementName = "wt")]
+ public int Weight;
- [XmlElement(ElementName = "q")]
- public int Quantity;
+ [XmlElement(ElementName = "q")]
+ public int Quantity;
- [XmlElement(ElementName = "mxq")]
- public int? MaxQuantity;
+ [XmlElement(ElementName = "mxq")]
+ public int? MaxQuantity;
}
diff --git a/src/Schema/ItemDataRollover.cs b/src/Schema/ItemDataRollover.cs
index baee86f..7e03179 100644
--- a/src/Schema/ItemDataRollover.cs
+++ b/src/Schema/ItemDataRollover.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "IRO", Namespace = "")]
[Serializable]
public class ItemDataRollover {
- public ItemDataRollover() {}
+ public ItemDataRollover() { }
public ItemDataRollover(ItemDataRollover other) {
DialogName = other.DialogName;
@@ -13,8 +13,8 @@ public class ItemDataRollover {
}
[XmlElement(ElementName = "d")]
- public string DialogName;
+ public string DialogName;
- [XmlElement(ElementName = "b")]
- public string Bundle;
+ [XmlElement(ElementName = "b")]
+ public string Bundle;
}
diff --git a/src/Schema/ItemDataTexture.cs b/src/Schema/ItemDataTexture.cs
index 1650352..053d109 100644
--- a/src/Schema/ItemDataTexture.cs
+++ b/src/Schema/ItemDataTexture.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "IT", Namespace = "")]
[Serializable]
public class ItemDataTexture {
- public ItemDataTexture() {}
+ public ItemDataTexture() { }
public ItemDataTexture(ItemDataTexture other) {
TextureName = other.TextureName;
@@ -15,14 +15,14 @@ public class ItemDataTexture {
}
[XmlElement(ElementName = "n")]
- public string TextureName;
+ public string TextureName;
- [XmlElement(ElementName = "t")]
- public string TextureTypeName;
+ [XmlElement(ElementName = "t")]
+ public string TextureTypeName;
- [XmlElement(ElementName = "x", IsNullable = true)]
- public float? OffsetX;
+ [XmlElement(ElementName = "x", IsNullable = true)]
+ public float? OffsetX;
- [XmlElement(ElementName = "y", IsNullable = true)]
- public float? OffsetY;
+ [XmlElement(ElementName = "y", IsNullable = true)]
+ public float? OffsetY;
}
diff --git a/src/Schema/ItemPossibleStatsMap.cs b/src/Schema/ItemPossibleStatsMap.cs
index ac577b0..4070725 100644
--- a/src/Schema/ItemPossibleStatsMap.cs
+++ b/src/Schema/ItemPossibleStatsMap.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "IPSM", Namespace = "", IsNullable = false)]
[Serializable]
public class ItemPossibleStatsMap {
- public ItemPossibleStatsMap() {}
+ public ItemPossibleStatsMap() { }
public ItemPossibleStatsMap(ItemPossibleStatsMap other) {
ItemID = other.ItemID;
@@ -15,14 +15,14 @@ public class ItemPossibleStatsMap {
}
[XmlElement(ElementName = "IID", IsNullable = false)]
- public int ItemID { get; set; }
+ public int ItemID { get; set; }
- [XmlElement(ElementName = "SC", IsNullable = false)]
- public int ItemStatsCount { get; set; }
+ [XmlElement(ElementName = "SC", IsNullable = false)]
+ public int ItemStatsCount { get; set; }
- [XmlElement(ElementName = "SID", IsNullable = false)]
- public int SetID { get; set; }
+ [XmlElement(ElementName = "SID", IsNullable = false)]
+ public int SetID { get; set; }
- [XmlElement(ElementName = "SS", IsNullable = false)]
- public List Stats { get; set; }
+ [XmlElement(ElementName = "SS", IsNullable = false)]
+ public List Stats { get; set; }
}
diff --git a/src/Schema/ItemSaleConfig.cs b/src/Schema/ItemSaleConfig.cs
index 0528167..c7644e6 100644
--- a/src/Schema/ItemSaleConfig.cs
+++ b/src/Schema/ItemSaleConfig.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "ISC", Namespace = "", IsNullable = true)]
[Serializable]
public class ItemSaleConfig {
- public ItemSaleConfig() {}
+ public ItemSaleConfig() { }
public ItemSaleConfig(ItemSaleConfig other) {
ItemID = other.ItemID;
@@ -16,17 +16,17 @@ public class ItemSaleConfig {
}
[XmlElement(ElementName = "IID", IsNullable = true)]
- public int? ItemID { get; set; }
+ public int? ItemID { get; set; }
- [XmlElement(ElementName = "CID", IsNullable = true)]
- public int? CategoryID { get; set; }
+ [XmlElement(ElementName = "CID", IsNullable = true)]
+ public int? CategoryID { get; set; }
- [XmlElement(ElementName = "RID", IsNullable = true)]
- public int? RarityID { get; set; }
+ [XmlElement(ElementName = "RID", IsNullable = true)]
+ public int? RarityID { get; set; }
- [XmlElement(ElementName = "QTY", IsNullable = false)]
- public int Quantity { get; set; }
+ [XmlElement(ElementName = "QTY", IsNullable = false)]
+ public int Quantity { get; set; }
- [XmlElement(ElementName = "RIID", IsNullable = false)]
- public int RewardItemID { get; set; }
+ [XmlElement(ElementName = "RIID", IsNullable = false)]
+ public int RewardItemID { get; set; }
}
diff --git a/src/Schema/ItemStat.cs b/src/Schema/ItemStat.cs
index 3a1b9c0..3782a33 100644
--- a/src/Schema/ItemStat.cs
+++ b/src/Schema/ItemStat.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "IS", Namespace = "")]
[Serializable]
public class ItemStat {
- public ItemStat() {}
+ public ItemStat() { }
public ItemStat(ItemStat other) {
ItemStatID = other.ItemStatID;
@@ -15,14 +15,14 @@ public class ItemStat {
}
[XmlElement(ElementName = "ID")]
- public int ItemStatID { get; set; }
+ public int ItemStatID { get; set; }
- [XmlElement(ElementName = "N")]
- public string Name { get; set; }
+ [XmlElement(ElementName = "N")]
+ public string Name { get; set; }
- [XmlElement(ElementName = "V")]
- public string Value { get; set; }
+ [XmlElement(ElementName = "V")]
+ public string Value { get; set; }
- [XmlElement(ElementName = "DTI")]
- public DataTypeInfo DataType { get; set; }
+ [XmlElement(ElementName = "DTI")]
+ public DataTypeInfo DataType { get; set; }
}
diff --git a/src/Schema/ItemState.cs b/src/Schema/ItemState.cs
index a39af77..3d90ab4 100644
--- a/src/Schema/ItemState.cs
+++ b/src/Schema/ItemState.cs
@@ -17,20 +17,20 @@ public class ItemState {
}
[XmlElement(ElementName = "ItemStateID")]
- public int ItemStateID;
+ public int ItemStateID;
- [XmlElement(ElementName = "Name")]
- public string Name;
+ [XmlElement(ElementName = "Name")]
+ public string Name;
- [XmlElement(ElementName = "Rule")]
- public ItemStateRule Rule;
+ [XmlElement(ElementName = "Rule")]
+ public ItemStateRule Rule;
- [XmlElement(ElementName = "Order")]
- public int Order;
+ [XmlElement(ElementName = "Order")]
+ public int Order;
- [XmlElement(ElementName = "AchievementID", IsNullable = true)]
- public int? AchievementID;
+ [XmlElement(ElementName = "AchievementID", IsNullable = true)]
+ public int? AchievementID;
- [XmlElement(ElementName = "Rewards")]
- public AchievementReward[] Rewards;
+ [XmlElement(ElementName = "Rewards")]
+ public AchievementReward[] Rewards;
}
diff --git a/src/Schema/ItemStateCriteria.cs b/src/Schema/ItemStateCriteria.cs
index 715946f..5a482c4 100644
--- a/src/Schema/ItemStateCriteria.cs
+++ b/src/Schema/ItemStateCriteria.cs
@@ -11,12 +11,12 @@ namespace sodoff.Schema;
[XmlInclude(typeof(ItemStateCriteriaExpiry))]
[Serializable]
public class ItemStateCriteria {
- public ItemStateCriteria() {}
+ public ItemStateCriteria() { }
public ItemStateCriteria(ItemStateCriteria other) {
Type = other.Type;
}
[XmlElement(ElementName = "Type")]
- public ItemStateCriteriaType Type;
+ public ItemStateCriteriaType Type;
}
diff --git a/src/Schema/ItemStateRule.cs b/src/Schema/ItemStateRule.cs
index 162efb4..e8eac05 100644
--- a/src/Schema/ItemStateRule.cs
+++ b/src/Schema/ItemStateRule.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "ItemStateRule", Namespace = "")]
[Serializable]
public class ItemStateRule {
- public ItemStateRule() {}
+ public ItemStateRule() { }
public ItemStateRule(ItemStateRule other) {
Criterias = other.Criterias.Select(c => new ItemStateCriteria(c)).ToList();
@@ -13,8 +13,8 @@ public class ItemStateRule {
}
[XmlElement(ElementName = "Criterias")]
- public List Criterias;
+ public List Criterias;
- [XmlElement(ElementName = "CompletionAction")]
- public CompletionAction CompletionAction;
+ [XmlElement(ElementName = "CompletionAction")]
+ public CompletionAction CompletionAction;
}
diff --git a/src/Schema/ItemStatsMap.cs b/src/Schema/ItemStatsMap.cs
index 280500a..f694446 100644
--- a/src/Schema/ItemStatsMap.cs
+++ b/src/Schema/ItemStatsMap.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "ISM", Namespace = "", IsNullable = false)]
[Serializable]
public class ItemStatsMap {
- public ItemStatsMap() {}
+ public ItemStatsMap() { }
public ItemStatsMap(ItemStatsMap other) {
ItemID = other.ItemID;
@@ -14,11 +14,11 @@ public class ItemStatsMap {
}
[XmlElement(ElementName = "IID", IsNullable = false)]
- public int ItemID { get; set; }
+ public int ItemID { get; set; }
- [XmlElement(ElementName = "IT", IsNullable = false)]
- public ItemTier ItemTier { get; set; }
+ [XmlElement(ElementName = "IT", IsNullable = false)]
+ public ItemTier ItemTier { get; set; }
- [XmlElement(ElementName = "ISS", IsNullable = false)]
- public ItemStat[] ItemStats { get; set; }
+ [XmlElement(ElementName = "ISS", IsNullable = false)]
+ public ItemStat[] ItemStats { get; set; }
}
diff --git a/src/Schema/Mission.cs b/src/Schema/Mission.cs
index 1e49c29..278e4ce 100644
--- a/src/Schema/Mission.cs
+++ b/src/Schema/Mission.cs
@@ -3,9 +3,9 @@
namespace sodoff.Schema;
[XmlRoot(ElementName = "Mission", Namespace = "")]
-[Serializable] // FIXME: Remove serializable once we have a different way of deep copying than BinaryFormatter
+[Serializable]
public class Mission {
- public Mission() {}
+ public Mission() { }
public Mission(Mission other) {
if (other == null) throw new ArgumentNullException(nameof(other));
diff --git a/src/Schema/MissionCriteria.cs b/src/Schema/MissionCriteria.cs
index 29c867f..154a6ee 100644
--- a/src/Schema/MissionCriteria.cs
+++ b/src/Schema/MissionCriteria.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "MissionCriteria", Namespace = "")]
[Serializable]
public class MissionCriteria {
- public MissionCriteria() {}
+ public MissionCriteria() { }
public MissionCriteria(MissionCriteria other) {
Type = other.Type;
diff --git a/src/Schema/MissionRule.cs b/src/Schema/MissionRule.cs
index 3a9e6a5..d34905b 100644
--- a/src/Schema/MissionRule.cs
+++ b/src/Schema/MissionRule.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "MissionRule", Namespace = "")]
[Serializable]
public class MissionRule {
- public MissionRule() {}
+ public MissionRule() { }
public MissionRule(MissionRule other) {
Prerequisites = other.Prerequisites.Select(p => new PrerequisiteItem(p)).ToList();
diff --git a/src/Schema/Pair.cs b/src/Schema/Pair.cs
index 485d3d8..c100b2f 100644
--- a/src/Schema/Pair.cs
+++ b/src/Schema/Pair.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "Pair", Namespace = "")]
[Serializable]
public class Pair {
- public Pair() {}
+ public Pair() { }
public Pair(Pair other) {
PairKey = other.PairKey;
@@ -14,11 +14,11 @@ public class Pair {
}
[XmlElement(ElementName = "PairKey")]
- public string PairKey;
-
- [XmlElement(ElementName = "PairValue")]
- public string PairValue;
-
- [XmlElement(ElementName = "UpdateDate")]
- public DateTime UpdateDate;
+ public string PairKey;
+
+ [XmlElement(ElementName = "PairValue")]
+ public string PairValue;
+
+ [XmlElement(ElementName = "UpdateDate")]
+ public DateTime UpdateDate;
}
\ No newline at end of file
diff --git a/src/Schema/PairData.cs b/src/Schema/PairData.cs
index 46438ff..8b7d995 100644
--- a/src/Schema/PairData.cs
+++ b/src/Schema/PairData.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "Pairs", Namespace = "", IsNullable = true)]
[Serializable]
public class PairData {
- public PairData() {}
+ public PairData() { }
public PairData(PairData other) {
Pairs = other.Pairs.Select(p => new Pair(p)).ToArray();
diff --git a/src/Schema/PrerequisiteItem.cs b/src/Schema/PrerequisiteItem.cs
index 8d555a4..11fb74f 100644
--- a/src/Schema/PrerequisiteItem.cs
+++ b/src/Schema/PrerequisiteItem.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "PrerequisiteItem", Namespace = "")]
[Serializable]
public class PrerequisiteItem {
- public PrerequisiteItem() {}
+ public PrerequisiteItem() { }
public PrerequisiteItem(PrerequisiteItem other) {
Type = other.Type;
diff --git a/src/Schema/RuleItem.cs b/src/Schema/RuleItem.cs
index 3478454..ac4fcd1 100644
--- a/src/Schema/RuleItem.cs
+++ b/src/Schema/RuleItem.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "RuleItem", Namespace = "")]
[Serializable]
public class RuleItem {
- public RuleItem() {}
+ public RuleItem() { }
public RuleItem(RuleItem other) {
Type = other.Type;
diff --git a/src/Schema/Stat.cs b/src/Schema/Stat.cs
index ea9fc3a..23fd6f0 100644
--- a/src/Schema/Stat.cs
+++ b/src/Schema/Stat.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "STAT", Namespace = "", IsNullable = false)]
[Serializable]
public class Stat {
- public Stat() {}
+ public Stat() { }
public Stat(Stat other) {
ItemID = other.ItemID;
@@ -16,17 +16,17 @@ public class Stat {
}
[XmlElement(ElementName = "IID", IsNullable = false)]
- public int ItemID { get; set; }
+ public int ItemID { get; set; }
- [XmlElement(ElementName = "ISID", IsNullable = false)]
- public int ItemStatsID { get; set; }
+ [XmlElement(ElementName = "ISID", IsNullable = false)]
+ public int ItemStatsID { get; set; }
- [XmlElement(ElementName = "SID", IsNullable = false)]
- public int SetID { get; set; }
+ [XmlElement(ElementName = "SID", IsNullable = false)]
+ public int SetID { get; set; }
- [XmlElement(ElementName = "PROB", IsNullable = false)]
- public int Probability { get; set; }
+ [XmlElement(ElementName = "PROB", IsNullable = false)]
+ public int Probability { get; set; }
- [XmlElement(ElementName = "ISRM", IsNullable = false)]
- public List ItemStatsRangeMaps { get; set; }
+ [XmlElement(ElementName = "ISRM", IsNullable = false)]
+ public List ItemStatsRangeMaps { get; set; }
}
diff --git a/src/Schema/StatRangeMap.cs b/src/Schema/StatRangeMap.cs
index 6b8c7c5..0f27ce9 100644
--- a/src/Schema/StatRangeMap.cs
+++ b/src/Schema/StatRangeMap.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "SRM", Namespace = "", IsNullable = false)]
[Serializable]
public class StatRangeMap {
- public StatRangeMap() {}
+ public StatRangeMap() { }
public StatRangeMap(StatRangeMap other) {
ItemStatsID = other.ItemStatsID;
@@ -16,17 +16,17 @@ public class StatRangeMap {
}
[XmlElement(ElementName = "ISID", IsNullable = false)]
- public int ItemStatsID { get; set; }
+ public int ItemStatsID { get; set; }
- [XmlElement(ElementName = "ISN", IsNullable = false)]
- public string ItemStatsName { get; set; }
+ [XmlElement(ElementName = "ISN", IsNullable = false)]
+ public string ItemStatsName { get; set; }
- [XmlElement(ElementName = "ITID", IsNullable = false)]
- public int ItemTierID { get; set; }
+ [XmlElement(ElementName = "ITID", IsNullable = false)]
+ public int ItemTierID { get; set; }
- [XmlElement(ElementName = "SR", IsNullable = false)]
- public int StartRange { get; set; }
+ [XmlElement(ElementName = "SR", IsNullable = false)]
+ public int StartRange { get; set; }
- [XmlElement(ElementName = "ER", IsNullable = false)]
- public int EndRange { get; set; }
+ [XmlElement(ElementName = "ER", IsNullable = false)]
+ public int EndRange { get; set; }
}
diff --git a/src/Schema/Task.cs b/src/Schema/Task.cs
index f0f19b6..e909f21 100644
--- a/src/Schema/Task.cs
+++ b/src/Schema/Task.cs
@@ -8,7 +8,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "Task", Namespace = "")]
[Serializable]
public class Task {
- public Task() {}
+ public Task() { }
public Task(Task other) {
TaskID = other.TaskID;
diff --git a/src/Schema/UserItemData.cs b/src/Schema/UserItemData.cs
index 1e52f1c..4974e42 100644
--- a/src/Schema/UserItemData.cs
+++ b/src/Schema/UserItemData.cs
@@ -5,7 +5,7 @@ namespace sodoff.Schema;
[XmlRoot(ElementName = "UserItem", Namespace = "")]
[Serializable]
public class UserItemData {
- public UserItemData() {}
+ public UserItemData() { }
public UserItemData(UserItemData other) {
ItemID = other.ItemID;
@@ -21,32 +21,32 @@ public class UserItemData {
}
[XmlElement(ElementName = "iid")]
- public int ItemID { get; set; }
+ public int ItemID { get; set; }
- [XmlElement(ElementName = "md", IsNullable = true)]
- public DateTime? ModifiedDate { get; set; }
+ [XmlElement(ElementName = "md", IsNullable = true)]
+ public DateTime? ModifiedDate { get; set; }
- [XmlElement(ElementName = "uia", IsNullable = true)]
- public PairData UserItemAttributes { get; set; }
+ [XmlElement(ElementName = "uia", IsNullable = true)]
+ public PairData UserItemAttributes { get; set; }
- [XmlElement(ElementName = "iss", IsNullable = true)]
- public ItemStat[] ItemStats { get; set; }
+ [XmlElement(ElementName = "iss", IsNullable = true)]
+ public ItemStat[] ItemStats { get; set; }
- [XmlElement(ElementName = "IT", IsNullable = true)]
- public ItemTier? ItemTier { get; set; }
+ [XmlElement(ElementName = "IT", IsNullable = true)]
+ public ItemTier? ItemTier { get; set; }
- [XmlElement(ElementName = "cd", IsNullable = true)]
- public DateTime? CreatedDate { get; set; }
+ [XmlElement(ElementName = "cd", IsNullable = true)]
+ public DateTime? CreatedDate { get; set; }
- [XmlElement(ElementName = "uiid")]
- public int UserInventoryID;
+ [XmlElement(ElementName = "uiid")]
+ public int UserInventoryID;
- [XmlElement(ElementName = "q")]
- public int Quantity;
+ [XmlElement(ElementName = "q")]
+ public int Quantity;
- [XmlElement(ElementName = "u")]
- public int Uses;
+ [XmlElement(ElementName = "u")]
+ public int Uses;
- [XmlElement(ElementName = "i")]
- public ItemData Item;
+ [XmlElement(ElementName = "i")]
+ public ItemData Item;
}
From a3db9c2bcf19396c47e37e8422577e02967335bf Mon Sep 17 00:00:00 2001
From: Robert Paciorek
Date: Fri, 27 Jun 2025 14:37:19 +0000
Subject: [PATCH 07/18] update dotnet version in Dockerfile
---
src/Dockerfile | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Dockerfile b/src/Dockerfile
index e4fc8a9..8c3d2e5 100644
--- a/src/Dockerfile
+++ b/src/Dockerfile
@@ -1,5 +1,5 @@
# Use the official .NET SDK image for building the application
-FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
# Copy the source code
@@ -13,7 +13,7 @@ RUN mv src/mods /app && ln -s /app/mods src/
RUN mv src/assets /app && ln -s /app/assets src/
# Create clean run environment (without source and sdk)
-# FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
+# FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
# WORKDIR /app
# COPY --from=build /app .
From 12bb663076dab4b6091628b7b2b2f3037a174208 Mon Sep 17 00:00:00 2001
From: Robert Paciorek
Date: Fri, 27 Jun 2025 14:46:23 +0000
Subject: [PATCH 08/18] fix null exception in LoginParent
---
src/Controllers/Common/AuthenticationController.cs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/Controllers/Common/AuthenticationController.cs b/src/Controllers/Common/AuthenticationController.cs
index 465680a..96e1065 100644
--- a/src/Controllers/Common/AuthenticationController.cs
+++ b/src/Controllers/Common/AuthenticationController.cs
@@ -48,8 +48,13 @@ public class AuthenticationController : Controller {
} else {
user = ctx.Users.FirstOrDefault(e => e.Username == data.UserName);
}
+
+ if (user is null) {
+ return Ok(new ParentLoginInfo { Status = MembershipUserStatus.InvalidUserName });
+ }
+
PasswordVerificationResult result = new PasswordHasher().VerifyHashedPassword(null, user.Password, data.Password);
- if (user is null || result == PasswordVerificationResult.Failed) {
+ if (result == PasswordVerificationResult.Failed) {
return Ok(new ParentLoginInfo { Status = MembershipUserStatus.InvalidPassword });
}
From bca383c4d0e2683ba0f18f00effa4649a545dca6 Mon Sep 17 00:00:00 2001
From: Spirtix
Date: Mon, 30 Jun 2025 15:26:47 +0200
Subject: [PATCH 09/18] remove PairData.Update this is expensive and
unnecessary because the entity is already tracked
---
src/Services/KeyValueService.cs | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/Services/KeyValueService.cs b/src/Services/KeyValueService.cs
index 1144108..9eb85c7 100644
--- a/src/Services/KeyValueService.cs
+++ b/src/Services/KeyValueService.cs
@@ -50,9 +50,7 @@ public class KeyValueService {
}
}
- if (exists)
- ctx.PairData.Update(pair);
- else
+ if (!exists)
ctx.PairData.Add(pair);
ctx.SaveChanges();
From 7dbcc456b98046ffc734d38aba7ee926389ceb8f Mon Sep 17 00:00:00 2001
From: Spirtix
Date: Mon, 30 Jun 2025 19:33:49 +0200
Subject: [PATCH 10/18] async locking for VikingSession
---
src/Attributes/VikingSession.cs | 33 ++++++++++++++++++++++++++++-----
1 file changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/Attributes/VikingSession.cs b/src/Attributes/VikingSession.cs
index e93eb2b..044adaf 100644
--- a/src/Attributes/VikingSession.cs
+++ b/src/Attributes/VikingSession.cs
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
using sodoff.Model;
namespace sodoff.Attributes;
@@ -12,6 +13,9 @@ public class VikingSession : Attribute, IAsyncActionFilter {
public Modes Mode { get; set; } = Modes.VIKING;
public bool UseLock = false;
+ private static Dictionary semaphores = new();
+ private static SemaphoreSlim dictSemaphore = new(1, 1);
+
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) {
DBContext ctx = context.HttpContext.RequestServices.GetService(typeof(DBContext)) as DBContext;
@@ -22,7 +26,7 @@ public class VikingSession : Attribute, IAsyncActionFilter {
return;
}
- Session? session = ctx.Sessions.FirstOrDefault(x => x.ApiToken == Guid.Parse(context.HttpContext.Request.Form[ApiToken].ToString()));
+ Session? session = await ctx.Sessions.FirstOrDefaultAsync(x => x.ApiToken == Guid.Parse(context.HttpContext.Request.Form[ApiToken].ToString()));
// get viking / user id from session
@@ -44,15 +48,16 @@ public class VikingSession : Attribute, IAsyncActionFilter {
// NOTE: we can't refer to session.User / session.Viking here,
// because it may cause to ignore modifications from the threads we are waiting for
// we can use its only after vikingMutex.WaitOne()
-
- Mutex vikingMutex = new Mutex(false, "SoDOffViking:" + userVikingId);
+ string semKey = "SoDOffViking:" + userVikingId;
+ SemaphoreSlim semaphore = await GetSemaphore(semKey);
try {
- vikingMutex.WaitOne();
+ await semaphore.WaitAsync();
context.ActionArguments["user"] = session.User;
context.ActionArguments["viking"] = session.Viking;
await next();
} finally {
- vikingMutex.ReleaseMutex();
+ semaphore.Release();
+ await RemoveSemaphore(semKey, semaphore);
}
} else {
context.ActionArguments["user"] = session.User;
@@ -60,4 +65,22 @@ public class VikingSession : Attribute, IAsyncActionFilter {
await next();
}
}
+
+ private static async Task GetSemaphore(string key) {
+ await dictSemaphore.WaitAsync();
+ if (!semaphores.TryGetValue(key, out SemaphoreSlim semaphore)) {
+ semaphore = new SemaphoreSlim(1, 1);
+ semaphores.Add(key, semaphore);
+ }
+ dictSemaphore.Release();
+ return semaphore;
+ }
+
+ private static async Task RemoveSemaphore(string key, SemaphoreSlim sem) {
+ await dictSemaphore.WaitAsync();
+ if (sem.CurrentCount == 1) {
+ semaphores.Remove(key);
+ }
+ dictSemaphore.Release();
+ }
}
From 1b22c9c3ddd92e482d20eefe466622d37d7ac4d3 Mon Sep 17 00:00:00 2001
From: Spirtix
Date: Mon, 30 Jun 2025 22:21:10 +0200
Subject: [PATCH 11/18] remove async query
---
src/Attributes/VikingSession.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Attributes/VikingSession.cs b/src/Attributes/VikingSession.cs
index 044adaf..8cdb829 100644
--- a/src/Attributes/VikingSession.cs
+++ b/src/Attributes/VikingSession.cs
@@ -26,7 +26,7 @@ public class VikingSession : Attribute, IAsyncActionFilter {
return;
}
- Session? session = await ctx.Sessions.FirstOrDefaultAsync(x => x.ApiToken == Guid.Parse(context.HttpContext.Request.Form[ApiToken].ToString()));
+ Session? session = ctx.Sessions.FirstOrDefault(x => x.ApiToken == Guid.Parse(context.HttpContext.Request.Form[ApiToken].ToString()));
// get viking / user id from session
From 0923b80cdf7df5972b880ecb8be9d52fc142af2d Mon Sep 17 00:00:00 2001
From: Spirtix
Date: Tue, 1 Jul 2025 16:40:56 +0200
Subject: [PATCH 12/18] lock CreatePet and SetImage
---
src/Controllers/Common/ContentController.cs | 10 +++-------
src/appsettings.json | 2 +-
2 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/src/Controllers/Common/ContentController.cs b/src/Controllers/Common/ContentController.cs
index 49e0f18..6c72812 100644
--- a/src/Controllers/Common/ContentController.cs
+++ b/src/Controllers/Common/ContentController.cs
@@ -570,7 +570,7 @@ public class ContentController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/CreatePet")]
- [VikingSession]
+ [VikingSession(UseLock = true)]
public IActionResult CreatePet(Viking viking, [FromForm] string request) {
RaisedPetRequest raisedPetRequest = XmlUtil.DeserializeXml(request);
// TODO: Investigate SetAsSelectedPet and UnSelectOtherPets - they don't seem to do anything
@@ -602,7 +602,6 @@ public class ContentController : Controller {
if (raisedPetRequest.SetAsSelectedPet == true) {
viking.SelectedDragon = dragon;
- ctx.Update(viking);
}
ctx.Dragons.Add(dragon);
ctx.Images.Add(image);
@@ -905,7 +904,7 @@ public class ContentController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/SetImage")]
- [VikingSession]
+ [VikingSession(UseLock = true)]
public bool SetImage(Viking viking, [FromForm] string ImageType, [FromForm] int ImageSlot, [FromForm] string contentXML, [FromForm] string imageFile) {
// TODO: the other properties of contentXML
ImageData data = XmlUtil.DeserializeXml(contentXML);
@@ -925,11 +924,8 @@ public class ContentController : Controller {
image.ImageData = imageFile;
image.TemplateName = data.TemplateName;
- if (newImage) {
+ if (newImage)
ctx.Images.Add(image);
- } else {
- ctx.Images.Update(image);
- }
ctx.SaveChanges();
return true;
diff --git a/src/appsettings.json b/src/appsettings.json
index de5849a..32c1f6c 100644
--- a/src/appsettings.json
+++ b/src/appsettings.json
@@ -74,7 +74,7 @@
"Logging": {
"LogLevel": {
"Default": "Information",
- "Microsoft.AspNetCore": "Warning"
+ "Microsoft.AspNetCore": "Information"
}
},
"AllowedHosts": "*"
From 1a6db72d7a542c424f16d93b8f68f40ab5ea23de Mon Sep 17 00:00:00 2001
From: Spirtix
Date: Wed, 2 Jul 2025 21:28:53 +0200
Subject: [PATCH 13/18] downgrade ef to 7
There's a performance regression for synchronous insert/update in npgsql
ef adapter, we'll have to switch to asynchronous db calls before
updating
---
src/sodoff.csproj | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/sodoff.csproj b/src/sodoff.csproj
index 70c7c4a..68159ae 100644
--- a/src/sodoff.csproj
+++ b/src/sodoff.csproj
@@ -11,26 +11,26 @@
-
+
-
+
-
+
-
+
From 278f04d38134650d6f73ef52dcc926c56fedb555 Mon Sep 17 00:00:00 2001
From: Robert Paciorek
Date: Sun, 27 Jul 2025 10:30:34 +0000
Subject: [PATCH 14/18] disable null-related warnings
---
src/sodoff.csproj | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/sodoff.csproj b/src/sodoff.csproj
index 68159ae..cab8f4d 100644
--- a/src/sodoff.csproj
+++ b/src/sodoff.csproj
@@ -8,6 +8,8 @@
USE_SQLITE;$(DefineConstants)
USE_POSTGRESQL;$(DefineConstants)
USE_MYSQL;$(DefineConstants)
+
+ 8600,8601,8602,8603,8604,8618,8625,8629
From fb6c935e7e88403aa9f6e14d1384ce88cbbfbe5d Mon Sep 17 00:00:00 2001
From: Robert Paciorek
Date: Sun, 27 Jul 2025 10:34:28 +0000
Subject: [PATCH 15/18] fix upcoming missions in GetUserMissionState
* this is bugfix for upcoming missions issue in SoD 2.9 after 60cc00d
* also removed TODO because ProductGroupID is not related to mission.GroupID and ProductGroupID filtering is covered by gameVersion
---
src/Controllers/Common/ContentController.cs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/Controllers/Common/ContentController.cs b/src/Controllers/Common/ContentController.cs
index 6c72812..1c2e6eb 100644
--- a/src/Controllers/Common/ContentController.cs
+++ b/src/Controllers/Common/ContentController.cs
@@ -1109,14 +1109,17 @@ public class ContentController : Controller {
foreach (var m in filterV2.MissionPair)
if (m.MissionID != null)
result.Missions.Add(missionService.GetMissionWithProgress((int)m.MissionID, viking.Id, gameVersion));
- // TODO: probably should also check for mission based on filterV2.ProductGroupID vs mission.GroupID
} else {
if (filterV2.GetCompletedMission ?? false) {
foreach (var mission in viking.MissionStates.Where(x => x.MissionStatus == MissionStatus.Completed))
result.Missions.Add(missionService.GetMissionWithProgress(mission.MissionId, viking.Id, gameVersion));
} else {
- foreach (var mission in viking.MissionStates.Where(x => x.MissionStatus != MissionStatus.Completed))
- result.Missions.Add(missionService.GetMissionWithProgress(mission.MissionId, viking.Id, gameVersion));
+ var missionStatesById = viking.MissionStates.Where(x => x.MissionStatus != MissionStatus.Completed).ToDictionary(ms => ms.MissionId);
+ HashSet upcomingMissionIds = new(missionStore.GetUpcomingMissions(gameVersion));
+ var combinedMissionIds = new HashSet(missionStatesById.Keys);
+ combinedMissionIds.UnionWith(upcomingMissionIds);
+ foreach (var missionId in combinedMissionIds)
+ result.Missions.Add(missionService.GetMissionWithProgress(missionId, viking.Id, gameVersion));
}
}
From 74b24d8ff5f544be0ebc3f00c520c9dc9699cad4 Mon Sep 17 00:00:00 2001
From: Robert Paciorek
Date: Thu, 17 Jul 2025 17:39:25 +0000
Subject: [PATCH 16/18] user data export and import interfce (WIP)
---
.../Common/ImportExportController.cs | 146 ++++++++++++++++++
src/Model/AchievementPoints.cs | 3 +
src/Model/AchievementTaskState.cs | 3 +
src/Model/DBContext.cs | 3 +
src/Model/Dragon.cs | 7 +-
src/Model/GameData.cs | 5 +-
src/Model/GameDataPair.cs | 4 +
src/Model/Image.cs | 3 +
src/Model/InventoryItem.cs | 4 +
src/Model/MMORole.cs | 3 +
src/Model/MissionState.cs | 6 +-
src/Model/Neighborhood.cs | 6 +-
src/Model/Pair.cs | 4 +
src/Model/PairData.cs | 8 +
src/Model/Party.cs | 5 +-
src/Model/ProfileAnswer.cs | 4 +
src/Model/Rating.cs | 4 +
src/Model/RatingRank.cs | 3 +
src/Model/Room.cs | 6 +-
src/Model/RoomItem.cs | 4 +
src/Model/SaveData.cs | 6 +-
src/Model/SceneData.cs | 4 +
src/Model/TaskStatus.cs | 6 +-
src/Model/User.cs | 2 +
src/Model/UserBadgeCompleteData.cs | 7 +-
src/Model/UserMissionData.cs | 3 +
src/Model/Viking.cs | 8 +
27 files changed, 257 insertions(+), 10 deletions(-)
create mode 100644 src/Controllers/Common/ImportExportController.cs
diff --git a/src/Controllers/Common/ImportExportController.cs b/src/Controllers/Common/ImportExportController.cs
new file mode 100644
index 0000000..b429adf
--- /dev/null
+++ b/src/Controllers/Common/ImportExportController.cs
@@ -0,0 +1,146 @@
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Text.Encodings.Web;
+using sodoff.Model;
+
+namespace sodoff.Controllers.Common;
+public class ExportController : ControllerBase {
+ private readonly DBContext ctx;
+
+ public ExportController(DBContext ctx) {
+ this.ctx = ctx;
+ }
+
+ [HttpPost]
+ [Route("ImportExport.asmx/Export")]
+ public IActionResult Export([FromForm] string username, [FromForm] string password) {
+ // Authenticate user by Username
+ 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.");
+ }
+
+ // Serialize to JSON
+ var options = new JsonSerializerOptions
+ {
+ ReferenceHandler = ReferenceHandler.IgnoreCycles,
+ Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = true
+ };
+ string jsonData = JsonSerializer.Serialize(user, options);
+
+ return Ok(jsonData);
+ }
+
+ [HttpPost]
+ [Route("ImportExport.asmx/Import")]
+ public IActionResult Import([FromForm] string username, [FromForm] string password, [FromForm] string vikingName, [FromForm] IFormFile dataFile) {
+ 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.");
+ }
+
+ User user_data;
+ using (var reader = new StreamReader(dataFile.OpenReadStream())) {
+ user_data = System.Text.Json.JsonSerializer.Deserialize(reader.ReadToEnd());
+ }
+
+ foreach (var v in user_data.Vikings) {
+ if (v.Name == vikingName) {
+ Viking viking = new Viking {
+ Uid = v.Uid, // TODO check for unique or just generate new?
+ Name = v.Name, // TODO check for unique
+ User = user,
+ AvatarSerialized = v.AvatarSerialized,
+ CreationDate = v.CreationDate, // TODO or use now?
+ BirthDate = v.BirthDate,
+ Gender = v.Gender,
+ GameVersion = v.GameVersion
+ };
+ user.Vikings.Add(viking);
+
+ 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
+ ctx.Dragons.Add(x);
+ }
+ foreach (var x in v.Images) {
+ x.Viking = viking;
+ ctx.Images.Add(x);
+ }
+ foreach (var x in v.InventoryItems) {
+ x.Id = 0; // FIXME map old→new value for item id to update xml's and rooms
+ x.Viking = viking;
+ ctx.InventoryItems.Add(x);
+ }
+ foreach (var x in v.Rooms) {
+ x.Viking = viking;
+ ctx.Rooms.Add(x); // FIXME need update room name (if numeric)
+ }
+ foreach (var x in v.MissionStates) {
+ x.Viking = viking;
+ ctx.MissionStates.Add(x);
+ }
+ foreach (var x in v.TaskStatuses) {
+ x.Viking = viking;
+ ctx.TaskStatuses.Add(x);
+ }
+ foreach (var x in v.AchievementTaskStates) {
+ x.Viking = viking;
+ ctx.AchievementTaskState.Add(x);
+ }
+ foreach (var x in v.AchievementPoints) {
+ 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);
+ }
+ foreach (var x in v.GameData) {
+ x.Viking = viking;
+ ctx.GameData.Add(x);
+ }
+ foreach (var x in v.SavedData) {
+ x.Viking = viking;
+ ctx.SavedData.Add(x);
+ }
+ foreach (var x in v.Parties) {
+ x.Viking = viking;
+ ctx.Parties.Add(x);
+ }
+ foreach (var x in v.UserMissions) {
+ x.Viking = viking;
+ ctx.UserMissions.Add(x);
+ }
+ foreach (var x in v.UserBadgesCompleted) {
+ x.Viking = viking;
+ ctx.UserBadgesCompleted.Add(x);
+ }
+ if (v.Ratings.Count > 0) {
+ viking.Ratings = new List();
+ foreach (var x in v.Ratings) {
+ // TODO (non-SoD) add rating via SetRating(viking, x.Rank.CategoryID, x.Rank.RatedEntityID, x.Rank.RatedUserID, x.Value);
+ }
+ }
+ if (v.Neighborhood != null) {
+ v.Neighborhood.Viking = viking;
+ ctx.Neighborhoods.Add(v.Neighborhood);
+ }
+ // TODO set viking.SelectedDragon
+
+ ctx.SaveChanges();
+ return Ok("OK");
+ }
+ }
+ return Ok("Viking Not Found");
+ }
+}
diff --git a/src/Model/AchievementPoints.cs b/src/Model/AchievementPoints.cs
index 6cfc402..94c5b2b 100644
--- a/src/Model/AchievementPoints.cs
+++ b/src/Model/AchievementPoints.cs
@@ -1,12 +1,15 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class AchievementPoints {
+ [JsonIgnore]
public int VikingId { get; set; }
public int Type { get; set; }
public int Value { get; set; }
+ [JsonIgnore]
public virtual Viking? Viking { get; set; }
}
diff --git a/src/Model/AchievementTaskState.cs b/src/Model/AchievementTaskState.cs
index ef2edb6..27f4f03 100644
--- a/src/Model/AchievementTaskState.cs
+++ b/src/Model/AchievementTaskState.cs
@@ -1,15 +1,18 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
using Microsoft.EntityFrameworkCore;
namespace sodoff.Model;
[PrimaryKey(nameof(TaskId), nameof(VikingId))]
public class AchievementTaskState {
+ [JsonIgnore]
public int VikingId { get; set; }
public int TaskId { get; set; }
public int Points { get; set; }
+ [JsonIgnore]
public virtual Viking? Viking { get; set; }
}
diff --git a/src/Model/DBContext.cs b/src/Model/DBContext.cs
index 1832763..02b01a9 100644
--- a/src/Model/DBContext.cs
+++ b/src/Model/DBContext.cs
@@ -23,6 +23,9 @@ public class DBContext : DbContext {
public DbSet ProfileAnswers { get; set; } = null!;
public DbSet MMORoles { get; set; } = null!;
public DbSet Parties { get; set; } = null!;
+ public DbSet AchievementTaskState { get; set; } = null!;
+ public DbSet SavedData { get; set; } = null!;
+ public DbSet UserMissions { get; set; } = null!;
public DbSet Neighborhoods { get; set; } = null!;
// we had a brief debate on whether it's neighborhoods or neighborheed
public DbSet Groups { get; set; } = null!;
diff --git a/src/Model/Dragon.cs b/src/Model/Dragon.cs
index f8f636a..b3e4ac3 100644
--- a/src/Model/Dragon.cs
+++ b/src/Model/Dragon.cs
@@ -1,22 +1,27 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class Dragon {
[Key]
+ // [JsonIgnore] used in serialised xml (stables)
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public Guid EntityId { get; set; }
+
[Required]
+ [JsonIgnore]
public int VikingId { get; set; }
public string? RaisedPetData { get; set; }
public int? PetXP { get; set; }
-
+
+ [JsonIgnore]
public virtual Viking Viking { get; set; } = null!;
public virtual ICollection PairData { get; set; } = null!;
}
diff --git a/src/Model/GameData.cs b/src/Model/GameData.cs
index 56f801c..7db40d3 100644
--- a/src/Model/GameData.cs
+++ b/src/Model/GameData.cs
@@ -1,10 +1,13 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class GameData {
[Key]
+ [JsonIgnore]
public int Id { get; set; }
+ [JsonIgnore]
public int VikingId { get; set; }
public int GameId { get; set; }
@@ -15,6 +18,6 @@ public class GameData {
public bool Win { get; set; }
public bool Loss { get; set; }
public virtual ICollection GameDataPairs { get; set; } = null!;
+ [JsonIgnore]
public virtual Viking Viking { get; set; } = null!;
-
}
diff --git a/src/Model/GameDataPair.cs b/src/Model/GameDataPair.cs
index 9543936..e5d45aa 100644
--- a/src/Model/GameDataPair.cs
+++ b/src/Model/GameDataPair.cs
@@ -1,11 +1,15 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class GameDataPair {
[Key]
+ [JsonIgnore]
public int Id { get; set; }
+ [JsonIgnore]
public int GameDataId { get; set; }
public string Name { get; set; } = null!;
public int Value { get; set; }
+ [JsonIgnore]
public virtual GameData GameData { get; set; } = null!;
}
diff --git a/src/Model/Image.cs b/src/Model/Image.cs
index f12727c..f671c4d 100644
--- a/src/Model/Image.cs
+++ b/src/Model/Image.cs
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
using Microsoft.EntityFrameworkCore;
namespace sodoff.Model;
@@ -12,11 +13,13 @@ public class Image {
public int ImageSlot { get; set; }
[Required]
+ [JsonIgnore]
public int VikingId { get; set; }
public string? ImageData { get; set; }
public string? TemplateName { get; set; }
+ [JsonIgnore]
public virtual Viking Viking { get; set; } = null!;
}
diff --git a/src/Model/InventoryItem.cs b/src/Model/InventoryItem.cs
index ea6c5d1..563fba9 100644
--- a/src/Model/InventoryItem.cs
+++ b/src/Model/InventoryItem.cs
@@ -1,18 +1,22 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model {
public class InventoryItem {
[Key]
+ // [JsonIgnore] used as room id, used in serialised xml (pairs, ...)
public int Id { get; set; }
public int ItemId { get; set; }
+ [JsonIgnore]
public int VikingId { get; set; }
public string? StatsSerialized { get; set; }
public string? AttributesSerialized { get; set; }
+ [JsonIgnore]
public virtual Viking Viking { get; set; } = null!;
public int Quantity { get; set; }
diff --git a/src/Model/MMORole.cs b/src/Model/MMORole.cs
index 8f97762..4a72c1a 100644
--- a/src/Model/MMORole.cs
+++ b/src/Model/MMORole.cs
@@ -1,15 +1,18 @@
using sodoff.Schema;
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class MMORole {
[Key]
+ [JsonIgnore]
public int Id { get; set; }
public int VikingId { get; set; }
public Role Role { get; set; }
+ [JsonIgnore]
public virtual Viking? Viking { get; set; }
}
diff --git a/src/Model/MissionState.cs b/src/Model/MissionState.cs
index b82f1b0..c99d738 100644
--- a/src/Model/MissionState.cs
+++ b/src/Model/MissionState.cs
@@ -1,15 +1,19 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class MissionState {
[Key]
+ [JsonIgnore]
public int Id { get; set; }
public int MissionId { get; set; }
+ [JsonIgnore]
public int VikingId { get; set; }
+ [JsonIgnore]
public virtual Viking? Viking { get; set; }
public MissionStatus MissionStatus { get; set; }
@@ -19,4 +23,4 @@ public class MissionState {
public enum MissionStatus {
Upcoming,Active,Completed
-}
\ No newline at end of file
+}
diff --git a/src/Model/Neighborhood.cs b/src/Model/Neighborhood.cs
index a748c76..67b363c 100644
--- a/src/Model/Neighborhood.cs
+++ b/src/Model/Neighborhood.cs
@@ -1,18 +1,20 @@
using sodoff.Schema;
using System.ComponentModel.DataAnnotations;
-using System.Data;
-using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization;
namespace sodoff.Model
{
public class Neighborhood
{
+ [JsonIgnore]
public virtual Viking? Viking { get; set; }
[Key]
+ [JsonIgnore]
public int Id { get; set; }
[Required]
+ [JsonIgnore]
public int VikingId { get; set; }
[Required]
diff --git a/src/Model/Pair.cs b/src/Model/Pair.cs
index 5c6aca6..abc55cc 100644
--- a/src/Model/Pair.cs
+++ b/src/Model/Pair.cs
@@ -1,18 +1,22 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class Pair {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ [JsonIgnore]
public int Id { get; set; }
public string Key { get; set; } = null!;
public string Value { get; set; } = null!;
+ [JsonIgnore]
public int MasterId { get; set; }
+ [JsonIgnore]
public virtual PairData PairData { get; set; } = null!;
}
diff --git a/src/Model/PairData.cs b/src/Model/PairData.cs
index 9f11ce5..9db929a 100644
--- a/src/Model/PairData.cs
+++ b/src/Model/PairData.cs
@@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
@@ -9,21 +10,28 @@ namespace sodoff.Model;
public class PairData {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ [JsonIgnore]
public int Id { get; set; }
public int PairId { get; set; }
+ [JsonIgnore]
public Guid? UserId { get; set; }
+ [JsonIgnore]
public int? VikingId { get; set; }
+ [JsonIgnore]
public int? DragonId { get; set; }
public virtual ICollection Pairs { get; set; }
+ [JsonIgnore]
public virtual User? User { get; set; }
+ [JsonIgnore]
public virtual Viking? Viking { get; set; }
+ [JsonIgnore]
public virtual Dragon? Dragon { get; set; }
}
diff --git a/src/Model/Party.cs b/src/Model/Party.cs
index 9667da5..dd4b5ab 100644
--- a/src/Model/Party.cs
+++ b/src/Model/Party.cs
@@ -1,18 +1,21 @@
using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
+using System.Text.Json.Serialization;
namespace sodoff.Model
{
public class Party
{
[Key]
+ [JsonIgnore]
public int Id { get; set; }
public string Location { get; set; } = null!;
+ [JsonIgnore]
public int VikingId { get; set; }
public DateTime ExpirationDate { get; set; } = DateTime.UtcNow;
public bool? PrivateParty { get; set; }
public string LocationIconAsset { get; set; } = null!;
public string AssetBundle { get; set; } = null!;
+ [JsonIgnore]
public virtual Viking? Viking { get; set; }
}
}
diff --git a/src/Model/ProfileAnswer.cs b/src/Model/ProfileAnswer.cs
index 4aae801..ff4982b 100644
--- a/src/Model/ProfileAnswer.cs
+++ b/src/Model/ProfileAnswer.cs
@@ -1,15 +1,19 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model
{
public class ProfileAnswer
{
[Key]
+ [JsonIgnore]
public int Id { get; set; }
+ [JsonIgnore]
public int VikingId { get; set; }
public int QuestionID { get; set; }
public int AnswerID { get; set; }
+ [JsonIgnore]
public virtual Viking Viking { get; set; } = null!;
}
}
diff --git a/src/Model/Rating.cs b/src/Model/Rating.cs
index 565e736..a195a54 100644
--- a/src/Model/Rating.cs
+++ b/src/Model/Rating.cs
@@ -1,17 +1,21 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class Rating {
[Key]
+ [JsonIgnore]
public int Id { get; set; }
+ [JsonIgnore]
public int VikingId { get; set; }
public int RankId { get; set; }
public int Value { get; set; }
public DateTime Date { get; set; }
+ [JsonIgnore]
public virtual Viking Viking { get; set; }
public virtual RatingRank Rank { get; set; }
}
diff --git a/src/Model/RatingRank.cs b/src/Model/RatingRank.cs
index 1eebec1..359f9b3 100644
--- a/src/Model/RatingRank.cs
+++ b/src/Model/RatingRank.cs
@@ -1,9 +1,11 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class RatingRank {
[Key]
+ [JsonIgnore]
public int Id { get; set; }
public int CategoryID { get; set; }
@@ -14,5 +16,6 @@ public class RatingRank {
public float RatingAverage { get; set; } // On a scale of 1-5
public DateTime UpdateDate { get; set; }
+ [JsonIgnore]
public virtual ICollection Ratings { get; set; } = null!;
}
diff --git a/src/Model/Room.cs b/src/Model/Room.cs
index 6089e1f..6f8fe48 100644
--- a/src/Model/Room.cs
+++ b/src/Model/Room.cs
@@ -1,18 +1,22 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class Room {
[Key]
+ [JsonIgnore]
public int Id { get; set; }
public string RoomId { get; set; }
+ [JsonIgnore]
public int VikingId { get; set; }
public string? Name { get; set; }
+ [JsonIgnore]
public virtual Viking? Viking { get; set; }
public virtual ICollection Items { get; set; } = null!;
-}
\ No newline at end of file
+}
diff --git a/src/Model/RoomItem.cs b/src/Model/RoomItem.cs
index 329b0bd..8701dd9 100644
--- a/src/Model/RoomItem.cs
+++ b/src/Model/RoomItem.cs
@@ -1,12 +1,16 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class RoomItem {
[Key]
+ [JsonIgnore]
public int Id { get; set; }
+ [JsonIgnore]
public int RoomId { get; set; }
+ [JsonIgnore]
public virtual Room Room { get; set; }
public string RoomItemData { get; set; }
diff --git a/src/Model/SaveData.cs b/src/Model/SaveData.cs
index 0d411cf..f455241 100644
--- a/src/Model/SaveData.cs
+++ b/src/Model/SaveData.cs
@@ -1,8 +1,12 @@
-namespace sodoff.Model;
+using System.Text.Json.Serialization;
+
+namespace sodoff.Model;
public class SavedData {
+ [JsonIgnore]
public int VikingId { get; set; }
public uint SaveId { get; set; }
public string? SerializedData { get; set; }
+ [JsonIgnore]
public virtual Viking Viking { get; set; } = null!;
}
diff --git a/src/Model/SceneData.cs b/src/Model/SceneData.cs
index 5e17821..6a39cfc 100644
--- a/src/Model/SceneData.cs
+++ b/src/Model/SceneData.cs
@@ -1,14 +1,18 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model
{
public class SceneData
{
[Key]
+ [JsonIgnore]
public int Id { get; set; }
public int VikingId { get; set; }
public string SceneName { get; set; } = null!;
public string XmlData { get; set; } = null!;
+
+ [JsonIgnore]
public virtual Viking Viking { get; set; } = null!;
}
}
diff --git a/src/Model/TaskStatus.cs b/src/Model/TaskStatus.cs
index 9e19f2b..a83857c 100644
--- a/src/Model/TaskStatus.cs
+++ b/src/Model/TaskStatus.cs
@@ -1,11 +1,15 @@
-namespace sodoff.Model {
+using System.Text.Json.Serialization;
+
+namespace sodoff.Model {
public class TaskStatus {
public int Id { get; set; }
public int MissionId { get; set; }
+ [JsonIgnore]
public int VikingId { get; set; }
+ [JsonIgnore]
public virtual Viking? Viking { get; set; }
public string? Payload { get; set; }
diff --git a/src/Model/User.cs b/src/Model/User.cs
index 358feb1..2d3d579 100644
--- a/src/Model/User.cs
+++ b/src/Model/User.cs
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
namespace sodoff.Model;
public class User {
@@ -14,6 +15,7 @@ public class User {
[Required]
public string Password { get; set; } = null!;
+ [JsonIgnore]
public virtual ICollection Sessions { get; set; } = null!;
public virtual ICollection Vikings { get; set; } = null!;
public virtual ICollection PairData { get; set; } = null!;
diff --git a/src/Model/UserBadgeCompleteData.cs b/src/Model/UserBadgeCompleteData.cs
index efab4c9..d96eafd 100644
--- a/src/Model/UserBadgeCompleteData.cs
+++ b/src/Model/UserBadgeCompleteData.cs
@@ -1,11 +1,16 @@
-namespace sodoff.Model
+using System.Text.Json.Serialization;
+
+namespace sodoff.Model
{
public class UserBadgeCompleteData
{
+ [JsonIgnore]
public int Id { get; set; }
+ [JsonIgnore]
public int VikingId { get; set; }
public int BadgeId { get; set; }
+ [JsonIgnore]
public virtual Viking? Viking { get; set; }
}
}
diff --git a/src/Model/UserMissionData.cs b/src/Model/UserMissionData.cs
index 1a971b9..16a5cf8 100644
--- a/src/Model/UserMissionData.cs
+++ b/src/Model/UserMissionData.cs
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
using Microsoft.EntityFrameworkCore;
namespace sodoff.Model
@@ -6,6 +7,7 @@ namespace sodoff.Model
[PrimaryKey(nameof(VikingId), nameof(WorldId), nameof(MissionId))]
public class UserMissionData
{
+ [JsonIgnore]
public int VikingId { get; set; }
public int WorldId { get; set; }
public int MissionId { get; set; }
@@ -13,6 +15,7 @@ namespace sodoff.Model
public int TaskId { get; set; }
public bool IsCompleted { get; set; } = false;
+ [JsonIgnore]
public virtual Viking? Viking { get; set; }
}
}
diff --git a/src/Model/Viking.cs b/src/Model/Viking.cs
index a787107..e2b8ca4 100644
--- a/src/Model/Viking.cs
+++ b/src/Model/Viking.cs
@@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
using sodoff.Schema;
namespace sodoff.Model;
@@ -7,6 +8,7 @@ namespace sodoff.Model;
[Index(nameof(Uid))]
public class Viking {
[Key]
+ [JsonIgnore]
public int Id { get; set; }
public Guid Uid { get; set; }
@@ -15,13 +17,17 @@ public class Viking {
public string Name { get; set; } = null!;
[Required]
+ [JsonIgnore]
public Guid UserId { get; set; }
public string? AvatarSerialized { get; set; }
+ [JsonIgnore]
public int? SelectedDragonId { get; set; }
+ [JsonIgnore]
public virtual ICollection Sessions { get; set; } = null!;
+ [JsonIgnore]
public virtual User User { get; set; } = null!;
public virtual ICollection Dragons { get; set; } = null!;
public virtual ICollection Images { get; set; } = null!;
@@ -37,8 +43,10 @@ public class Viking {
public virtual ICollection ProfileAnswers { get; set; } = null!;
public virtual ICollection SavedData { get; set; } = null!;
public virtual ICollection Parties { get; set; } = null!;
+ [JsonIgnore]
public virtual ICollection MMORoles { get; set; } = null!;
public virtual Neighborhood? Neighborhood { get; set; } = null!;
+ [JsonIgnore]
public virtual ICollection Groups { get; set; } = null!;
public virtual ICollection Ratings { get; set; } = null!;
public virtual Dragon? SelectedDragon { get; set; }
From ea75d182a6a76d07927500626364a71285229903 Mon Sep 17 00:00:00 2001
From: Robert Paciorek
Date: Mon, 28 Jul 2025 09:43:36 +0000
Subject: [PATCH 17/18] 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
+
From 59c722fe65bc0fc9fb5fb8b0ec7d0226c1fe10f7 Mon Sep 17 00:00:00 2001
From: Robert Paciorek
Date: Tue, 12 Aug 2025 20:36:44 +0000
Subject: [PATCH 18/18] importer bugfix - error on stable imports
stables do not use unique inventory slots
---
src/Controllers/Common/ImportExportController.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Controllers/Common/ImportExportController.cs b/src/Controllers/Common/ImportExportController.cs
index 1631bbf..92dfea9 100644
--- a/src/Controllers/Common/ImportExportController.cs
+++ b/src/Controllers/Common/ImportExportController.cs
@@ -112,7 +112,7 @@ public class ExportController : ControllerBase {
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;
+ stableData.InventoryID = viking.InventoryItems.FirstOrDefault(e => e.ItemId == stableData.ItemID).Id;
usedItemIds.Add(stableData.InventoryID);
foreach (var n in stableData.NestList) {
if (n.PetID != 0)