Seeding reorg
This commit is contained in:
@@ -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 ----------
|
||||
|
||||
Reference in New Issue
Block a user