implement store, purchases; fix inventory

This only implements V2 purchases
V1 purchases are used outside of the store
This commit is contained in:
Spirtix 2023-07-03 19:38:52 +02:00
parent 09553c9d4f
commit 0460746824
15 changed files with 2262143 additions and 648127 deletions

View File

@ -51,7 +51,8 @@ methods = [
'SaveMessage',
'GetMMOServerInfoWithZone',
'GetActiveChallenges',
'GetAchievementsByUserID'
'GetAchievementsByUserID',
'PurchaseItems'
]
def routable(path):

View File

@ -146,6 +146,7 @@ public class ContentController : Controller {
List<InventoryItem> items = viking.Inventory.InventoryItems.ToList();
List<UserItemData> userItemData = new();
foreach (InventoryItem item in items) {
if (item.Quantity == 0) continue; // Don't include an item that the viking doesn't have
ItemData itemData = itemService.GetItem(item.ItemId);
UserItemData uid = new UserItemData {
UserInventoryID = viking.Inventory.Id,
@ -175,17 +176,33 @@ public class ContentController : Controller {
// Set inventory items
List<CommonInventoryResponseItem> responseItems = new();
// SetCommonInventory can remove any number of items from the inventory, this checks if it's possible
foreach (var req in request) {
if (req.Quantity >= 0) continue;
InventoryItem? item = viking.Inventory.InventoryItems.FirstOrDefault(e => e.ItemId == req.ItemID);
if (item is null || item.Quantity < req.Quantity)
return Ok(new CommonInventoryResponse { Success = false });
}
// Now that we know the request is valid, update the inventory
foreach (var req in request) {
InventoryItem? item = viking.Inventory.InventoryItems.FirstOrDefault(e => e.ItemId == req.ItemID);
if (item is null) item = new InventoryItem { ItemId = (int)req.ItemID, Quantity = 0 };
int origQuantity = item.Quantity;
item.Quantity = request[0].Quantity;
responseItems.Add(new CommonInventoryResponseItem {
CommonInventoryID = viking.InventoryId,
ItemID = item.ItemId,
Quantity = origQuantity
});
viking.Inventory.InventoryItems.Add(item);
if (item is null) {
item = new InventoryItem { ItemId = (int)req.ItemID, Quantity = 0 };
viking.Inventory.InventoryItems.Add(item);
}
int updateQuantity = 0; // The game expects 0 if quantity got updated by just 1
if (req.Quantity > 1)
updateQuantity = req.Quantity; // Otherwise it expects the quantity from the request
item.Quantity += req.Quantity;
ctx.SaveChanges(); // We need to get an ID of a newly created item
if (req.Quantity > 0)
responseItems.Add(new CommonInventoryResponseItem {
CommonInventoryID = item.Id,
ItemID = item.ItemId,
Quantity = updateQuantity
});
}
CommonInventoryResponse response = new CommonInventoryResponse {
@ -332,9 +349,7 @@ public class ContentController : Controller {
ctx.Update(viking);
ctx.SaveChanges();
return Ok(new SetRaisedPetResponse {
RaisedPetSetResult = RaisedPetSetResult.Success
});
return Ok(true); // RaisedPetSetResult.Success doesn't work, this does
}
[HttpPost]
@ -512,6 +527,44 @@ public class ContentController : Controller {
return Ok(new BuddyList[0]);
}
[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/PurchaseItems")]
public IActionResult PurchaseItems([FromForm] string apiToken, [FromForm] string purchaseItemRequest) {
Viking? viking = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.Viking;
if (viking is null)
return Ok();
PurchaseStoreItemRequest request = XmlUtil.DeserializeXml<PurchaseStoreItemRequest>(purchaseItemRequest);
CommonInventoryResponseItem[] items = new CommonInventoryResponseItem[request.Items.Length];
for (int i = 0; i < request.Items.Length; i++) {
InventoryItem? item = viking.Inventory.InventoryItems.FirstOrDefault(e => e.ItemId == request.Items[i]);
if (item is null) {
item = new InventoryItem { ItemId = request.Items[i], Quantity = 0 };
viking.Inventory.InventoryItems.Add(item);
}
item.Quantity++;
ctx.SaveChanges();
items[i] = new CommonInventoryResponseItem {
CommonInventoryID = item.Id,
ItemID = request.Items[i],
Quantity = 0 // The quantity of purchased items is always 0 and the items are instead duplicated in both the request and the response
};
}
CommonInventoryResponse response = new CommonInventoryResponse {
Success = true,
CommonInventoryIDs = items,
UserGameCurrency = new UserGameCurrency {
UserID = Guid.Parse(viking.Id),
UserGameCurrencyID = 1, // TODO: user's wallet ID?
CashCurrency = 1000,
GameCurrency = 1000,
}
};
return Ok(response);
}
private RaisedPetData GetRaisedPetDataFromDragon (Dragon dragon) {
RaisedPetData data = XmlUtil.DeserializeXml<RaisedPetData>(dragon.RaisedPetData);
data.RaisedPetID = dragon.Id;

View File

@ -2,29 +2,36 @@
using Microsoft.AspNetCore.Mvc;
using sodoff.Model;
using sodoff.Schema;
using sodoff.Services;
using sodoff.Util;
namespace sodoff.Controllers.Common;
public class ItemStoreController : Controller {
private readonly DBContext ctx;
public ItemStoreController(DBContext ctx) {
private StoreService storeService;
public ItemStoreController(DBContext ctx, StoreService storeService) {
this.ctx = ctx;
this.storeService = storeService;
}
[HttpPost]
//[Produces("application/xml")]
[Produces("application/xml")]
[Route("ItemStoreWebService.asmx/GetStore")]
public IActionResult GetStore() {
// TODO, this may be implemented enough, but may not be
var assembly = Assembly.GetExecutingAssembly();
string resourceName = assembly.GetManifestResourceNames().Single(str => str.EndsWith("store.xml"));
public IActionResult GetStore([FromForm] string getStoreRequest) {
GetStoreRequest request = XmlUtil.DeserializeXml<GetStoreRequest>(getStoreRequest);
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream)) {
string result = reader.ReadToEnd();
return Ok(result);
ItemsInStoreData[] stores = new ItemsInStoreData[request.StoreIDs.Length];
for (int i = 0; i < request.StoreIDs.Length; i++) {
stores[i] = storeService.GetStore(request.StoreIDs[i]);
}
GetStoreResponse response = new GetStoreResponse {
Stores = stores
};
return Ok(response);
}
[HttpPost]

View File

@ -156,8 +156,8 @@ public class ProfileController : Controller {
AchievementCount = 0,
MythieCount = 0,
AnswerData = new UserAnswerData { UserID = viking.Id },
GameCurrency = 300,
CashCurrency = 75,
GameCurrency = 1000,
CashCurrency = 1000,
ActivityCount = 0,
UserGradeData = new UserGrade { UserGradeID = 0 }
};

View File

@ -11,6 +11,6 @@ namespace sodoff.Model {
public virtual Inventory Inventory { get; set; }
public int Quantity;
public int Quantity { get; set; }
}
}

View File

@ -16,6 +16,7 @@ builder.Services.AddScoped<KeyValueService>();
builder.Services.AddSingleton<ItemService>();
builder.Services.AddSingleton<MissionStoreSingleton>();
builder.Services.AddScoped<MissionService>();
builder.Services.AddSingleton<StoreService>();
var app = builder.Build();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "GetStoreRequest", Namespace = "")]
[Serializable]
public class GetStoreRequest {
[XmlElement(ElementName = "StoreIDs")]
public int[] StoreIDs;
[XmlElement(ElementName = "PIF")]
public bool GetPopularItems = true;
}

View File

@ -0,0 +1,10 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "GetStoreResponse", Namespace = "", IsNullable = true)]
[Serializable]
public class GetStoreResponse {
[XmlElement(ElementName = "Stores")]
public ItemsInStoreData[] Stores;
}

View File

@ -0,0 +1,25 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "S", Namespace = "", IsNullable = true)]
[Serializable]
public class ItemsInStoreData {
[XmlElement(ElementName = "i", IsNullable = true)]
public int? ID;
[XmlElement(ElementName = "s")]
public string StoreName;
[XmlElement(ElementName = "d")]
public string Description;
[XmlElement(ElementName = "is")]
public ItemData[] Items;
[XmlElement(ElementName = "ss")]
public ItemsInStoreDataSale[] SalesAtStore;
[XmlElement(ElementName = "pitem")]
public PopularStoreItem[] PopularItems;
}

View File

@ -0,0 +1,35 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "SL", Namespace = "", IsNullable = true)]
[Serializable]
public class ItemsInStoreDataSale {
[XmlElement(ElementName = "pcid")]
public int PriceChangeId;
[XmlElement(ElementName = "m")]
public float Modifier;
[XmlElement(ElementName = "ic")]
public string Icon;
[XmlElement(ElementName = "rid", IsNullable = true)]
public int? RankId;
[XmlElement(ElementName = "iids")]
public int[] ItemIDs;
[XmlElement(ElementName = "cids")]
public int[] CategoryIDs;
[XmlElement(ElementName = "ism", IsNullable = true)]
public bool? ForMembers;
[XmlElement(ElementName = "sd", IsNullable = true)]
public DateTime? StartDate;
[XmlElement(ElementName = "ed", IsNullable = true)]
public DateTime? EndDate;
}

View File

@ -0,0 +1,12 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[Serializable]
public class PopularStoreItem {
[XmlElement(ElementName = "id")]
public int ItemID;
[XmlElement(ElementName = "c")]
public int Rank;
}

View File

@ -0,0 +1,22 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "pireq", Namespace = "")]
[Serializable]
public class PurchaseStoreItemRequest {
[XmlElement(ElementName = "cid")]
public int ContainerId { get; set; }
[XmlElement(ElementName = "sid")]
public int? StoreID { get; set; }
[XmlElement(ElementName = "i")]
public int[] Items { get; set; }
[XmlElement(ElementName = "ct")]
public int CurrencyType { get; set; }
[XmlElement(ElementName = "ambi")]
public bool AddMysteryBoxToInventory { get; set; }
}

View File

@ -0,0 +1,21 @@
using sodoff.Schema;
using sodoff.Util;
namespace sodoff.Services;
public class StoreService {
// NOTE: ANother memory waste (slow clap)
Dictionary<int, ItemsInStoreData> stores = new();
public StoreService() {
GetStoreResponse storeArray = XmlUtil.DeserializeXml<GetStoreResponse>(XmlUtil.ReadResourceXmlString("store"));
foreach (var s in storeArray.Stores)
if (s.ID != null)
stores.Add((int)s.ID, s);
}
public ItemsInStoreData GetStore(int id) {
return stores[id];
}
}