Seeding reorg

This commit is contained in:
gamer147
2026-05-24 21:13:15 -04:00
parent 34bcc579a5
commit c14408ba06
73 changed files with 4611 additions and 369716 deletions

View File

@@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore;
using SVSim.Database;
using SVSim.Database.Enums;
using SVSim.Database.Models;
using SVSim.Database.Models.Config;
using static SVSim.Bootstrap.Importers.ImporterBase;
namespace SVSim.Bootstrap.Importers;
@@ -87,32 +88,95 @@ public class GlobalsImporter
return total;
}
// ---------- GameConfiguration ----------
// ---------- GameConfig sections ----------
private async Task<int> ImportGameConfigurationExtensions(SVSimDbContext context, JsonElement loadIndex)
{
var cfg = await context.GameConfigurations.FirstOrDefaultAsync(g => g.Id == "default");
if (cfg is null)
{
Console.Error.WriteLine("[GlobalsImporter] GameConfigurations 'default' row missing; " +
"DefaultSettingsSeeder should have created it. Skipping extensions.");
return 0;
}
// Reads the prod capture and overwrites the Rotation and (optionally) Challenge sections
// in GameConfigs. Sections are atomic — we read the existing row (or shipped defaults if
// none), mutate, then serialize back to ValueJson. Each section is one row in GameConfigs.
int touched = 0;
// TODO: fixed in Task 6 — writes through Config tree after RefactorGameConfigurationToJsonb
cfg.Config.Rotation.TsRotationId = GetString(loadIndex, "ts_rotation_id");
cfg.Config.Rotation.IsBattlePassPeriod = GetBool(loadIndex, "is_battle_pass_period");
cfg.Config.Rotation.IsBeginnerMission = GetBool(loadIndex, "is_beginner_mission");
cfg.Config.Rotation.CardSetIdForResourceDlView = GetInt(loadIndex, "card_set_id_for_resource_dl_view");
await UpsertSection<RotationConfig>(context, RotationConfig.ShippedDefaults, rot =>
{
rot.TsRotationId = GetString(loadIndex, "ts_rotation_id");
rot.IsBattlePassPeriod = GetBool(loadIndex, "is_battle_pass_period");
rot.IsBeginnerMission = GetBool(loadIndex, "is_beginner_mission");
rot.CardSetIdForResourceDlView = GetInt(loadIndex, "card_set_id_for_resource_dl_view");
Console.WriteLine($"[GlobalsImporter] GameConfigs/Rotation: ts_rotation_id={rot.TsRotationId}");
});
touched++;
if (loadIndex.TryGetProperty("challenge_config", out var cc))
{
cfg.Config.Challenge.UseTwoPickPremiumCard = GetBool(cc, "use_challenge_two_pick_premium_card");
cfg.Config.Challenge.TwoPickSleeveId = GetLong(cc, "challenge_two_pick_sleeve_id");
await UpsertSection<ChallengeConfig>(context, ChallengeConfig.ShippedDefaults, ch =>
{
ch.UseTwoPickPremiumCard = GetBool(cc, "use_challenge_two_pick_premium_card");
ch.TwoPickSleeveId = GetLong(cc, "challenge_two_pick_sleeve_id");
});
touched++;
}
Console.WriteLine($"[GlobalsImporter] GameConfiguration extensions: ts_rotation_id={cfg.Config.Rotation.TsRotationId}");
return 1;
// my_rotation_info.schedules → MyRotationSchedule section. Two named windows, hard-typed
// on both wire and client (Wizard/MyRotationAllInfo.cs:178-192 reads "gathering" and
// "free_battle" by name and binds them to typed fields). Only upsert when both windows
// parse to real DateTimes — a missing or 0001-01-01 capture would lock the feature off,
// which is exactly the bug the section was added to fix.
if (loadIndex.TryGetProperty("my_rotation_info", out var mri)
&& mri.TryGetProperty("schedules", out var schedules))
{
bool gOk = TryParseScheduleWindow(schedules, "gathering", out var gBegin, out var gEnd);
bool fOk = TryParseScheduleWindow(schedules, "free_battle", out var fBegin, out var fEnd);
if (gOk && fOk)
{
await UpsertSection<MyRotationScheduleConfig>(context, MyRotationScheduleConfig.ShippedDefaults, mr =>
{
mr.Gathering = new ScheduleWindow { Begin = gBegin, End = gEnd };
mr.FreeBattle = new ScheduleWindow { Begin = fBegin, End = fEnd };
Console.WriteLine($"[GlobalsImporter] GameConfigs/MyRotationSchedule: free_battle {fBegin:u} → {fEnd:u}");
});
touched++;
}
else
{
Console.Error.WriteLine("[GlobalsImporter] my_rotation_info.schedules missing or malformed — keeping existing/shipped MyRotationSchedule.");
}
}
return touched;
}
private static bool TryParseScheduleWindow(JsonElement schedules, string key, out DateTime begin, out DateTime end)
{
begin = default;
end = default;
if (!schedules.TryGetProperty(key, out var window) || window.ValueKind != JsonValueKind.Object) return false;
if (!DateTime.TryParse(GetString(window, "begin_time"), out begin)) return false;
if (!DateTime.TryParse(GetString(window, "end_time"), out end)) return false;
return true;
}
private static async Task UpsertSection<T>(SVSimDbContext context, Func<T> shippedDefaults, Action<T> mutate)
where T : class, new()
{
var sectionName = typeof(T).GetCustomAttributes(typeof(ConfigSectionAttribute), inherit: false)
.Cast<ConfigSectionAttribute>().FirstOrDefault()?.Name
?? throw new InvalidOperationException($"{typeof(T).Name} is missing [ConfigSection].");
var row = await context.GameConfigs.FirstOrDefaultAsync(s => s.SectionName == sectionName);
T value;
if (row is null)
{
value = shippedDefaults();
row = new GameConfigSection { SectionName = sectionName };
context.GameConfigs.Add(row);
}
else
{
value = JsonSerializer.Deserialize<T>(row.ValueJson) ?? shippedDefaults();
}
mutate(value);
row.ValueJson = JsonSerializer.Serialize(value);
}
// ---------- My Rotation ----------