support for mods - items add/overwrite interface

This commit is contained in:
Robert Paciorek 2024-01-13 01:04:54 +00:00
parent 18f7e89509
commit 7888445eee
8 changed files with 164 additions and 7 deletions

View File

@ -19,6 +19,7 @@ builder.Services.AddControllers(options => {
}); });
builder.Services.AddDbContext<DBContext>(); builder.Services.AddDbContext<DBContext>();
builder.Services.AddSingleton<ModdingService>();
builder.Services.AddSingleton<MissionStoreSingleton>(); builder.Services.AddSingleton<MissionStoreSingleton>();
builder.Services.AddSingleton<AchievementStoreSingleton>(); builder.Services.AddSingleton<AchievementStoreSingleton>();
builder.Services.AddSingleton<ItemService>(); builder.Services.AddSingleton<ItemService>();
@ -44,6 +45,10 @@ using var scope = app.Services.CreateScope();
scope.ServiceProvider.GetRequiredService<DBContext>().Database.EnsureCreated(); scope.ServiceProvider.GetRequiredService<DBContext>().Database.EnsureCreated();
// create Modding Service singleton ... do this before start http server to avoid serve invalid assets list
new ModdingService();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (assetServer) if (assetServer)

17
src/Schema/ModAction.cs Normal file
View File

@ -0,0 +1,17 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
public enum ModAction {
[XmlEnum("defualt")]
Default = 0,
[XmlEnum("add")]
Add = 1,
[XmlEnum("replace")]
Replace = 2,
[XmlEnum("remove")]
Remove = 3,
}

17
src/Schema/ModItem.cs Normal file
View File

@ -0,0 +1,17 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
public class ModItem {
[XmlAttribute("action")]
public ModAction action { get; set; } = ModAction.Default;
[XmlElement(ElementName = "id")]
public int? ItemID;
[XmlElement(ElementName = "storeID")]
public int[] stores;
[XmlElement(ElementName = "data")]
public ItemData data;
}

View File

@ -0,0 +1,9 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "sodoffmod", Namespace = "")]
public class ModManifest {
[XmlArrayItem(ElementName = "item")]
public ModItem [] items { get; set; }
}

View File

@ -120,6 +120,7 @@ namespace sodoff.Services {
foreach (InventoryItem item in items) { foreach (InventoryItem item in items) {
if (item.Quantity == 0) continue; // Don't include an item that the viking doesn't have if (item.Quantity == 0) continue; // Don't include an item that the viking doesn't have
ItemData itemData = itemService.GetItem(item.ItemId); ItemData itemData = itemService.GetItem(item.ItemId);
if (itemData is null) continue; // Don't include items removed from item database
UserItemData uid = new UserItemData { UserItemData uid = new UserItemData {
UserInventoryID = item.Id, UserInventoryID = item.Id,
ItemID = itemData.ItemID, ItemID = itemData.ItemID,

View File

@ -11,17 +11,23 @@ namespace sodoff.Services {
int[] itemsRewardForDT; int[] itemsRewardForDT;
Random random = new Random(); Random random = new Random();
public ItemService() { public ItemService(ModdingService moddingService) {
ServerItemArray itemArray = XmlUtil.DeserializeXml<ServerItemArray>(XmlUtil.ReadResourceXmlString("items")); ServerItemArray itemArray = XmlUtil.DeserializeXml<ServerItemArray>(XmlUtil.ReadResourceXmlString("items"));
foreach (var item in itemArray.ItemDataArray) { foreach (var item in itemArray.ItemDataArray) {
items.Add(item.ItemID, item); items.Add(item.ItemID, item);
} }
itemsRewardForDT = XmlUtil.DeserializeXml<int[]>(XmlUtil.ReadResourceXmlString("dtrewards")); itemsRewardForDT = XmlUtil.DeserializeXml<int[]>(XmlUtil.ReadResourceXmlString("dtrewards"));
moddingService.UpdateItems(ref items);
} }
public ItemData GetItem(int id) { public ItemData GetItem(int id) {
return items[id]; try {
return items[id];
} catch {
return null;
}
} }
public ItemData GetDTReward(Gender gender) { public ItemData GetDTReward(Gender gender) {

View File

@ -0,0 +1,94 @@
using sodoff.Schema;
using sodoff.Util;
using System.Xml.Serialization;
namespace sodoff.Services;
public class ModdingService {
private Dictionary<int, ModItem> itemsToUpdate = new();
private Dictionary<int, List<int>> itemsInStore = new();
public ModdingService() {
if (!Directory.Exists("mods/"))
return;
foreach (var dir in Directory.GetDirectories("mods/")) {
string manifestFile = dir + "/manifest.xml";
if (File.Exists(manifestFile)) {
Console.WriteLine($"Load mod manifest from {manifestFile}");
ModManifest modManifest = XmlUtil.DeserializeXml<ModManifest>(System.IO.File.ReadAllText(manifestFile));
if (modManifest.items != null) {
foreach (ModItem item in modManifest.items) {
// get item id from mod-level info
int? itemID = item.ItemID;
// process item data itemID value
if (itemID is null)
itemID = item.data?.ItemID;
else if (item.data != null)
item.data.ItemID = (int)itemID;
// check for unset itemID
if (itemID is null) {
Console.WriteLine("Missing item id.");
System.Environment.Exit(1);
}
try {
itemsToUpdate.Add((int)itemID, item);
if (item.stores != null && (item.action == ModAction.Add || item.action == ModAction.Default)) {
foreach (int storeID in item.stores) {
try {
itemsInStore[storeID].Add((int)itemID);
} catch (System.Collections.Generic.KeyNotFoundException) {
itemsInStore.Add(storeID, new List<int> {(int)itemID});
}
}
}
} catch (System.ArgumentException) {
Console.WriteLine($"Conflict for ItemID = {itemID} with previous modification.");
System.Environment.Exit(1);
}
}
}
} else {
Console.WriteLine($"Skip mod directory {dir} (missing manifest file)");
}
}
}
public void UpdateItems(ref Dictionary<int, ItemData> items) {
foreach (var item in itemsToUpdate) {
if (item.Value.action == ModAction.Remove) {
try {
items.Remove(item.Key);
} catch {
Console.WriteLine($"Can't remove item with ID = {item.Key} - not found");
}
} else if (item.Value.action == ModAction.Replace) {
try {
items[item.Key] = item.Value.data;
} catch {
Console.WriteLine($"Can't replace item with ID = {item.Key} - not found");
}
} else /*if (item.Value.action == ModAction.Add || item.Value.action == ModAction.Default)*/ {
try {
items.Add(item.Key, item.Value.data);
} catch {
Console.WriteLine($"Item with ID = {item.Key} is already defined in base item set.");
System.Environment.Exit(1);
}
}
}
itemsToUpdate.Clear();
}
public List<int> GetStoreItem(int storeID) {
if (itemsInStore.ContainsKey(storeID)) {
return itemsInStore[storeID];
} else {
return new List<int>();
}
}
}

View File

@ -6,23 +6,31 @@ namespace sodoff.Services;
public class StoreService { public class StoreService {
Dictionary<int, ItemsInStoreData> stores = new(); Dictionary<int, ItemsInStoreData> stores = new();
public StoreService(ItemService itemService) { public StoreService(ItemService itemService, ModdingService moddingService) {
StoreData[] storeArray = XmlUtil.DeserializeXml<StoreData[]>(XmlUtil.ReadResourceXmlString("store")); StoreData[] storeArray = XmlUtil.DeserializeXml<StoreData[]>(XmlUtil.ReadResourceXmlString("store"));
foreach (var s in storeArray) { foreach (var s in storeArray) {
var newStore = new ItemsInStoreData { ItemsInStoreData newStore = new() {
ID = s.Id, ID = s.Id,
StoreName = s.StoreName, StoreName = s.StoreName,
Description = s.Description, Description = s.Description,
Items = new ItemData[s.ItemId.Length],
SalesAtStore = s.SalesAtStore, SalesAtStore = s.SalesAtStore,
PopularItems = s.PopularItems PopularItems = s.PopularItems
}; };
List<ItemData> itemsList = new();
IEnumerable<ItemsInStoreDataSale>? memberSales = s.SalesAtStore?.Where(x => x.ForMembers == true); IEnumerable<ItemsInStoreDataSale>? memberSales = s.SalesAtStore?.Where(x => x.ForMembers == true);
IEnumerable<ItemsInStoreDataSale>? normalSales = s.SalesAtStore?.Where(x => x.ForMembers == false || x.ForMembers == null); IEnumerable<ItemsInStoreDataSale>? normalSales = s.SalesAtStore?.Where(x => x.ForMembers == false || x.ForMembers == null);
for (int i = 0; i < s.ItemId.Length; ++i) { for (int i = 0; i < s.ItemId.Length; ++i) {
newStore.Items[i] = itemService.GetItem(s.ItemId[i]); ItemData item = itemService.GetItem(s.ItemId[i]);
UpdateItemSaleModifier(newStore.Items[i], memberSales, normalSales); if (item is null) continue; // skip removed items
itemsList.Add(item);
UpdateItemSaleModifier(item, memberSales, normalSales);
} }
foreach (int itemID in moddingService.GetStoreItem(s.Id)) {
ItemData item = itemService.GetItem(itemID);
itemsList.Add(item);
UpdateItemSaleModifier(item, memberSales, normalSales);
}
newStore.Items = itemsList.ToArray();
stores.Add(s.Id, newStore); stores.Add(s.Id, newStore);
} }
} }