diff --git a/ALttPRandomizer/Model/SeedSettings.cs b/ALttPRandomizer/Model/SeedSettings.cs index 51508d5..83f34ae 100644 --- a/ALttPRandomizer/Model/SeedSettings.cs +++ b/ALttPRandomizer/Model/SeedSettings.cs @@ -42,6 +42,10 @@ [ForbiddenSetting([Apr2025], EntranceShuffle.Swapped)] public EntranceShuffle EntranceShuffle { get; set; } = EntranceShuffle.Vanilla; + [NoSettingName] + [RequiredSetting([Apr2025], LinksHouse.Vanilla)] + public LinksHouse LinksHouse { get; set; } = LinksHouse.Vanilla; + [SettingName("skullwoods")] [RequiredSetting([Apr2025], SkullWoodsShuffle.Original)] [NoSettingName([Apr2025])] @@ -250,6 +254,11 @@ [SettingName("insanity")] Decoupled, } + public enum LinksHouse { + Vanilla, + [AdditionalSetting("--shufflelinks")] Shuffled, + } + public enum SkullWoodsShuffle { Original, Restricted, diff --git a/ALttPRandomizer/Program.cs b/ALttPRandomizer/Program.cs index 7a0c9c8..de27c27 100644 --- a/ALttPRandomizer/Program.cs +++ b/ALttPRandomizer/Program.cs @@ -69,6 +69,7 @@ builder.Services.AddSingleton(sp => sp); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddKeyedScoped(BaseRandomizer.Name); builder.Services.AddKeyedScoped(BaseRandomizer.DungeonMapName); @@ -90,7 +91,11 @@ c.EnableValidator(); }); + var sh = app.Services.GetService(); + app.Run(); + + sh?.HandleShutdown().Wait(); } } } diff --git a/ALttPRandomizer/Randomizers/BaseRandomizer.cs b/ALttPRandomizer/Randomizers/BaseRandomizer.cs index 1f82d87..8165007 100644 --- a/ALttPRandomizer/Randomizers/BaseRandomizer.cs +++ b/ALttPRandomizer/Randomizers/BaseRandomizer.cs @@ -18,18 +18,20 @@ public const string Name = "base"; public const string DungeonMapName = "dungeon_map"; - public const int MULTI_TRIES = 20; - public const int SINGLE_TRIES = 20; + public const int MULTI_TRIES = 80; + public const int SINGLE_TRIES = 5; public BaseRandomizer( AzureStorage azureStorage, CommonSettingsProcessor settingsProcessor, IdGenerator idGenerator, + ShutdownHandler shutdownHandler, IOptionsMonitor optionsMonitor, ILogger logger) { this.AzureStorage = azureStorage; this.SettingsProcessor = settingsProcessor; this.IdGenerator = idGenerator; + this.ShutdownHandler = shutdownHandler; this.OptionsMonitor = optionsMonitor; this.Logger = logger; } @@ -40,6 +42,7 @@ private IOptionsMonitor OptionsMonitor { get; } private ILogger Logger { get; } private ServiceOptions Configuration => OptionsMonitor.CurrentValue; + private ShutdownHandler ShutdownHandler { get; } public void Validate(SeedSettings settings) { this.SettingsProcessor.ValidateSettings(settings.Randomizer, settings); @@ -58,7 +61,6 @@ var args = new List() { "--reduce_flashing", "--quickswap", - "--shufflelinks", "--shuffletavern", }; @@ -100,13 +102,11 @@ args.Add("--spoiler=json"); - args.Add(string.Format("--tries={0}", SINGLE_TRIES)); - foreach (var arg in settings) { args.Add(arg); } - Logger.LogInformation("Randomizing with args: {args}", string.Join(" ", args)); + Logger.LogInformation("Randomizing {id} with args: {args}", id, string.Join(" ", args.Select(arg => $"\"{arg}\""))); var generating = string.Format("{0}/generating", id); await AzureStorage.UploadFile(generating, BinaryData.Empty); @@ -114,14 +114,17 @@ var process = Process.Start(start) ?? throw new GenerationFailedException("Process failed to start."); process.EnableRaisingEvents = true; - process.OutputDataReceived += (_, args) => Logger.LogInformation("Randomizer STDOUT: {output}", args.Data); - process.ErrorDataReceived += (_, args) => Logger.LogInformation("Randomizer STDERR: {output}", args.Data); + process.OutputDataReceived += (_, args) => Logger.LogInformation("Randomizer {id} STDOUT: {output}", id, args.Data); + process.ErrorDataReceived += (_, args) => Logger.LogInformation("Randomizer {id} STDERR: {output}", id, args.Data); process.BeginOutputReadLine(); process.BeginErrorReadLine(); + this.ShutdownHandler.AddId(id); + process.Exited += async (sender, args) => { try { + this.ShutdownHandler.RemoveId(id); await completed.Invoke(process.ExitCode); } catch (Exception ex) { this.Logger.LogError(ex, "Error while invoking completion of randomizer generation."); @@ -132,7 +135,9 @@ public async Task Randomize(string id, SeedSettings settings) { Logger.LogDebug("Recieved request for id {id} to randomize settings {@settings}", id, settings); - await StartProcess(this.SettingsProcessor.GetRandomizerName(settings.Randomizer), id, this.GetArgs(settings), async exitcode => { + var args = this.GetArgs(settings).Append(string.Format("--tries={0}", SINGLE_TRIES)); + + await StartProcess(this.SettingsProcessor.GetRandomizerName(settings.Randomizer), id, args, async exitcode => { if (exitcode != 0) { await GenerationFailed(id, exitcode); } else { diff --git a/ALttPRandomizer/SeedController.cs b/ALttPRandomizer/SeedController.cs index 5d6d38e..0d27c2d 100644 --- a/ALttPRandomizer/SeedController.cs +++ b/ALttPRandomizer/SeedController.cs @@ -66,6 +66,12 @@ return ResolveResult(await this.SeedService.GetMulti(id)); } + [Route("/multi/{id}")] + [HttpPost] + public async Task RetryMulti(string id) { + return ResolveResult(await this.RandomizeService.RetryMulti(id)); + } + private ActionResult ResolveResult(IDictionary result) { if (result.TryGetValue("status", out var responseCode)) { if (responseCode is int code) { diff --git a/ALttPRandomizer/Service/RandomizeService.cs b/ALttPRandomizer/Service/RandomizeService.cs index ac114a4..c4315a9 100644 --- a/ALttPRandomizer/Service/RandomizeService.cs +++ b/ALttPRandomizer/Service/RandomizeService.cs @@ -95,5 +95,46 @@ result["status"] = 202; return result; } + + public async Task> RetryMulti(string multiId) { + var files = await this.AzureStorage.GetFiles(multiId); + + this.Logger.LogDebug("Found files: {@files}", files.Keys); + + var result = new Dictionary(); + + if (!files.TryGetValue("settings.json", out var settingsData)) { + result["status"] = 404; + result["error"] = "multi settings not found"; + return result; + } + + var settingsJson = JsonDocument.Parse(settingsData.ToString()); + + var settings = settingsJson.Deserialize>(JsonOptions.Default); + + if (settings == null) { + result["status"] = 404; + result["error"] = "multi settings not found"; + return result; + } + + if (files.TryGetValue("patch.bps", out var patchData)) { + result["status"] = 409; + result["error"] = "generation already successful"; + return result; + } + + if (files.ContainsKey("generating")) { + result["status"] = 409; + result["error"] = "generation still in progress"; + return result; + } + + await this.RandomizeMultiworld(settings, multiId); + + result["status"] = 202; + return result; + } } } diff --git a/ALttPRandomizer/Service/SeedService.cs b/ALttPRandomizer/Service/SeedService.cs index 7cd32d9..c04fbf3 100644 --- a/ALttPRandomizer/Service/SeedService.cs +++ b/ALttPRandomizer/Service/SeedService.cs @@ -102,6 +102,7 @@ } else { result["status"] = 404; result["error"] = "generation failed"; + result["retry"] = true; return result; } } diff --git a/ALttPRandomizer/ShutdownHandler.cs b/ALttPRandomizer/ShutdownHandler.cs new file mode 100644 index 0000000..a44b956 --- /dev/null +++ b/ALttPRandomizer/ShutdownHandler.cs @@ -0,0 +1,40 @@ +namespace ALttPRandomizer { + using System.Collections.Generic; + using System.Threading.Tasks; + using ALttPRandomizer.Azure; + using Microsoft.Extensions.Logging; + + public class ShutdownHandler { + public ShutdownHandler(AzureStorage azureStorage, ILogger logger) { + this.AzureStorage = azureStorage; + this.Logger = logger; + } + + private AzureStorage AzureStorage { get; } + private ILogger Logger { get; } + + private HashSet IdsInProgress { get; } = new(); + + public void AddId(string id) { + this.IdsInProgress.Add(id); + } + + public void RemoveId(string id) { + this.IdsInProgress.Remove(id); + } + + public async Task HandleShutdown() { + this.Logger.LogWarning("Shutdown Received."); + + List tasks = []; + + foreach (var id in this.IdsInProgress) { + tasks.Add(AzureStorage.DeleteFile($"{id}/generating")); + } + + this.Logger.LogInformation("Ending {count} in-progress generations.", tasks.Count); + + await Task.WhenAll(tasks); + } + } +} diff --git a/DungeonMapRandomizer b/DungeonMapRandomizer index 5e0dead..d424505 160000 --- a/DungeonMapRandomizer +++ b/DungeonMapRandomizer @@ -1 +1 @@ -Subproject commit 5e0deadf55c355e2df7b94d6c65373c7d72f54cc +Subproject commit d424505677d5a05a6b2bc161e603671a85edfc6d