Compare commits

...
9 Commits
12 changed files with 110 additions and 40 deletions
+3 -2
View File
@@ -24,8 +24,9 @@
await BlobClient.UploadBlobAsync(name, data);
}
public async Task UploadFile(string name, BinaryData data) {
await BlobClient.UploadBlobAsync(name, data);
public async Task UploadFile(string name, BinaryData data, bool overwrite = false) {
var client = BlobClient.GetBlobClient(name);
await client.UploadAsync(data, overwrite);
}
public async Task UploadFileFromSource(string name, string filepath) {
+1 -1
View File
@@ -48,7 +48,7 @@
public RandomizableWeights<DungeonItemLocations> Compasses { get; set; } = new();
public RandomizableWeights<ShowLoot> ShowLoot { get; set; } = new();
public RandomizableWeights<ShowLootMap> ShowLootMap { get; set; } = new();
public RandomizableWeights<ShowLootHud> ShowLootHud { get; set; } = new();
+33 -6
View File
@@ -1,19 +1,24 @@
namespace ALttPRandomizer.Model {
using ALttPRandomizer.Settings;
using System.Text.Json.Serialization;
using YamlDotNet.Serialization;
using static ALttPRandomizer.Model.RandomizerInstance;
public class SeedSettings {
[NoSettingName]
public RandomizerInstance Randomizer { get; set; } = RandomizerInstance.Base;
[NoSettingName]
[IgnoreSetting]
public string PlayerName { get; set; } = string.Empty;
[IgnoreSetting]
public int PlayerSlot { get; set; } = 1;
[NoSettingName]
public RaceMode Race { get; set; } = RaceMode.Normal;
[NoSettingName]
public MysteryMode Mystery { get; set; } = MysteryMode.Normal;
[ForbiddenSetting([Apr2025], Mode.Inverted)]
[RequiredSetting([Pikit], Mode.Open)]
public Mode Mode { get; set; } = Mode.Open;
@@ -46,6 +51,10 @@
[IgnoreSetting(Apr2025)]
public GanonItem GanonItem { get; set; } = GanonItem.Silver;
[NoSettingName]
[IgnoreSetting(Apr2025)]
public RequireGanonItem RequireGanonItem { get; set; } = RequireGanonItem.SilverlessAllowed;
[SettingName("shuffle")]
[ForbiddenSetting([Apr2025], EntranceShuffle.Swapped)]
public EntranceShuffle EntranceShuffle { get; set; } = EntranceShuffle.Vanilla;
@@ -97,10 +106,10 @@
[SettingName("showloot")]
[IgnoreSetting(Apr2025)]
public ShowLoot ShowLoot { get; set; } = ShowLoot.Never;
public ShowLootMap ShowLootMap { get; set; } = ShowLootMap.Never;
[SettingName("loothud")]
[IgnoreSetting(Apr2025, Base)]
[IgnoreSetting(Apr2025)]
public ShowLootHud ShowLootHud { get; set; } = ShowLootHud.Never;
[SettingName("showmap")]
@@ -163,7 +172,7 @@
public TrapDoorMode TrapDoorMode { get; set; } = TrapDoorMode.Some;
[SettingName("extra_keys")]
[IgnoreSetting(Apr2025, Base)]
[IgnoreSetting(Apr2025)]
public ExtraKeysMode ExtraKeys { get; set; } = ExtraKeysMode.None;
[NoSettingName]
@@ -204,6 +213,12 @@
[NoSettingName]
public Hints Hints { get; set; } = Hints.Off;
public SeedSettings Clone() {
var newSettings = (SeedSettings) this.MemberwiseClone();
return newSettings;
}
}
public enum RandomizerInstance {
@@ -218,6 +233,11 @@
[AdditionalSetting("--securerandom")] Race,
}
public enum MysteryMode {
Normal,
[AdditionalSetting("--mystery")] Mystery,
}
public enum Mode {
Open,
Standard,
@@ -311,6 +331,11 @@
None,
}
public enum RequireGanonItem {
SilverlessAllowed,
[AdditionalSetting("--require_ganon_item")] SpecialItemRequired,
}
public enum EntranceShuffle {
Vanilla,
Full,
@@ -405,7 +430,7 @@
Wild,
}
public enum ShowLoot {
public enum ShowLootMap {
Never,
Presence,
Compass,
@@ -417,6 +442,7 @@
Presence,
Value,
[SettingName("dungeon_value")] DungeonValue,
[SettingName("cave_value")] CaveValue,
}
public enum ShowMap {
@@ -455,6 +481,7 @@
public enum BombSettings {
Normal,
[AdditionalSetting("--bombbag")] BombBagRequired,
[AddStartingItems("Bombs_(10)")] Starting,
}
public enum BookSettings {
+24 -2
View File
@@ -18,6 +18,8 @@
public const int MULTI_TRIES = 100;
public const int SINGLE_TRIES = 5;
private static readonly Random random = new();
public BaseRandomizer(
AzureStorage azureStorage,
CommonSettingsProcessor settingsProcessor,
@@ -64,10 +66,20 @@
args.Add("--shuffletavern");
}
if (settings.Randomizer == RandomizerInstance.Beta) {
if (random.NextDouble() < 0.25) {
args.Add("--orange_potion");
}
}
if (settings.DoorShuffle == DoorShuffle.Vanilla) {
settings.DoorTypeMode = DoorTypeMode.Original;
}
if (settings.GanonItem == GanonItem.None) {
settings.RequireGanonItem = RequireGanonItem.SilverlessAllowed;
}
foreach (var arg in SettingsProcessor.GetSettings(settings.Randomizer, settings)) {
args.Add(arg);
}
@@ -113,7 +125,7 @@
};
}
public async Task Randomize(string id, SeedSettings settings, bool uploadSettings = true) {
public async Task Randomize(string id, SeedSettings settings, bool uploadSettings = true, MysterySettings? mysterySettings = null) {
Logger.LogDebug("Recieved request for id {id} to randomize settings {@settings}", id, settings);
var args = this.GetArgs(settings);
@@ -133,16 +145,26 @@
}
});
if (mysterySettings != null) {
var mysteryJson = JsonSerializer.SerializeToDocument(mysterySettings, JsonOptions.Default);
var mysteryOut = string.Format("{0}/mystery.json", id);
await AzureStorage.UploadFile(mysteryOut, new BinaryData(mysteryJson));
}
if (uploadSettings) {
var settingsJson = JsonSerializer.SerializeToDocument(settings, JsonOptions.Default);
var settingsOut = string.Format("{0}/settings.json", id);
await AzureStorage.UploadFile(settingsOut, new BinaryData(settingsJson));
await AzureStorage.UploadFile(settingsOut, new BinaryData(settingsJson), true);
}
}
public async Task RandomizeMultiworld(string id, IList<SeedSettings> settings, bool uploadSettings = true) {
var generatorSettings = this.SettingsProcessor.GetGeneratorSettings(settings[0].Randomizer);
for (var i = 0; i < settings.Count; i++) {
settings[i].PlayerSlot = i + 1;
}
Logger.LogDebug("Recieved request for id {id} to randomize multiworld settings {@settings}", id, settings);
var names = settings.Select(s => s.PlayerName.Replace(' ', '_')).ToList();
+1 -1
View File
@@ -5,6 +5,6 @@
public interface IRandomizer {
public void Validate(SeedSettings settings);
public Task Randomize(string id, SeedSettings settings, bool uploadSettings = true);
public Task Randomize(string id, SeedSettings settings, bool uploadSettings = true, MysterySettings? mysterySettings = null);
}
}
+14 -20
View File
@@ -1,41 +1,25 @@
namespace ALttPRandomizer {
using ALttPRandomizer.Serialization;
using ALttPRandomizer.Model;
using ALttPRandomizer.Randomizers;
using ALttPRandomizer.Service;
using ALttPRandomizer.Settings;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
public class SeedController : Controller {
public SeedController(
RandomizeService randomizeService,
MysteryRandomizer mysteryRandomizer,
SeedService seedService,
ILogger<SeedController> logger) {
this.RandomizeService = randomizeService;
this.MysteryRandomizer = mysteryRandomizer;
this.SeedService = seedService;
this.Logger = logger;
this.YamlSerializer =
new SerializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.WithTypeConverter(new YamlStringEnumConverter())
.WithTypeConverter(new RandomizableWeightsYamlConverters())
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull | DefaultValuesHandling.OmitEmptyCollections)
.Build();
}
private RandomizeService RandomizeService { get; }
private MysteryRandomizer MysteryRandomizer { get; }
private SeedService SeedService { get; }
private ILogger<SeedController> Logger { get; }
private ISerializer YamlSerializer { get; }
[Route("/generate")]
[HttpPost]
@@ -43,6 +27,7 @@
if (!ModelState.IsValid) {
return BadRequest(ModelState);
}
try {
var id = await this.RandomizeService.RandomizeSeed(settings);
var url = string.Format("/seed/{0}", id);
@@ -54,15 +39,24 @@
[Route("/mystery/{randomizer}")]
[HttpPost]
public ActionResult RollMystery([FromBody] MysterySettings mysterySettings, RandomizerInstance randomizer) {
public async Task<ObjectResult> RollMystery([FromBody] MysterySettings mysterySettings, RandomizerInstance randomizer, bool race) {
if (!ModelState.IsValid) {
return BadRequest(ModelState);
}
var seedSettings = new SeedSettings() { Randomizer = randomizer };
this.MysteryRandomizer.Roll(mysterySettings, seedSettings);
var baseSettings = new SeedSettings() {
Randomizer = randomizer,
Race = race ? RaceMode.Race : RaceMode.Normal,
Mystery = MysteryMode.Mystery,
};
return StatusCode(200, this.YamlSerializer.Serialize(mysterySettings));
try {
var id = await this.RandomizeService.RandomizeMystery(mysterySettings, baseSettings);
var url = string.Format("/seed/{0}", id);
return Accepted(url, id);
} catch (InvalidSettingsException ex) {
return BadRequest(ex.Message);
}
}
[Route("/multiworld")]
@@ -2,6 +2,7 @@
using ALttPRandomizer.Azure;
using ALttPRandomizer.Model;
using ALttPRandomizer.Randomizers;
using ALttPRandomizer.Settings;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Text.Json;
@@ -11,10 +12,12 @@
public RandomizeService(
IdGenerator idGenerator,
BaseRandomizer baseRandomizer,
MysteryRandomizer mysteryRandomizer,
AzureStorage azureStorage,
ILogger<RandomizeService> logger) {
this.IdGenerator = idGenerator;
this.BaseRandomizer = baseRandomizer;
this.MysteryRandomizer = mysteryRandomizer;
this.AzureStorage = azureStorage;
this.Logger = logger;
}
@@ -23,6 +26,7 @@
private IdGenerator IdGenerator { get; }
private BaseRandomizer BaseRandomizer { get; }
private MysteryRandomizer MysteryRandomizer { get; }
private AzureStorage AzureStorage { get; }
public async Task<string> RandomizeSeed(SeedSettings settings, string? seedId = null) {
@@ -35,6 +39,31 @@
return id;
}
public async Task<string> RandomizeMystery(MysterySettings mysterySettings, SeedSettings baseSettings, string? seedId = null) {
var id = seedId ?? this.IdGenerator.GenerateId();
this.Logger.LogDebug("Rolling mystery seed {seedId} with mystery {@settings}", id, mysterySettings);
SeedSettings seedSettings;
for (var i = 0; i < 10; i++) {
seedSettings = baseSettings.Clone();
this.MysteryRandomizer.Roll(mysterySettings, seedSettings);
this.Logger.LogInformation("Mystery seed {seedId} rolled settings {@settings}", id, seedSettings);
try {
this.BaseRandomizer.Validate(seedSettings);
} catch (InvalidSettingsException) {
this.Logger.LogWarning("Mystery seed {seedId} settings {@settings} failed validation; retrying", id, seedSettings);
continue;
}
await this.BaseRandomizer.Randomize(id, seedSettings, true, seedId == null ? mysterySettings : null);
return id;
}
throw new InvalidSettingsException("Could not roll settings that pass validation");
}
public async Task<string> RandomizeMultiworld(IList<SeedSettings> settings, string? multiId = null) {
var id = multiId ?? this.IdGenerator.GenerateId();
this.Logger.LogInformation("Generating multiworld {seedId} with settings {@settings}", id, settings);
+1
View File
@@ -59,6 +59,7 @@
}
internal class IgnoreSettingAttribute : RandomizerSpecificAttribute {
public IgnoreSettingAttribute() : base(null) { }
public IgnoreSettingAttribute(params RandomizerInstance[] randomizers) : base(randomizers) { }
}
@@ -10,7 +10,7 @@
var props = typeof(SeedSettings).GetProperties(BindingFlags.Instance | BindingFlags.Public);
var starting = new List<string>();
foreach (var prop in props) {
if (prop.Name == nameof(SeedSettings.PlayerName)) {
if (prop.GetCustomAttributes<IgnoreSettingAttribute>().Any(att => att.HasRandomizer(randomizer))) {
continue;
}
@@ -19,10 +19,6 @@
var fi = prop.PropertyType.GetField(valueFieldName, BindingFlags.Static | BindingFlags.Public)
?? throw new SettingsLookupException("Could not get field info for value {0}.{1}", prop.PropertyType, valueFieldName);
if (prop.GetCustomAttributes<IgnoreSettingAttribute>().Any(att => att.HasRandomizer(randomizer))) {
continue;
}
if (!prop.GetCustomAttributes<NoSettingNameAttribute>().Any(att => att.HasRandomizer(randomizer))) {
var settingName =
prop.GetCustomAttributes<SettingNameAttribute>()