Next piece of the puzzle

This probably breaks something.  I am sorry, grab me and I'll fix it
This commit is contained in:
nattthebear 2020-07-12 12:52:27 -04:00
parent 33a0954087
commit 5c5ffed5ff
14 changed files with 131 additions and 155 deletions

View File

@ -423,7 +423,50 @@ namespace BizHawk.Client.Common
throw new NotImplementedException("M3U not supported!"); throw new NotImplementedException("M3U not supported!");
} }
private void LoadOther(string path, CoreComm nextComm, bool forceAccurateCore, HawkFile file, out IEmulator nextEmulator, out RomGame rom, out GameInfo game, out bool cancel) private static string ForcedCoreToCoreName(string forcedCore)
{
// TODO: Is this yet another list of core names really needed? Let's just make the gamedb less dumb
return forcedCore switch
{
"snes9x" => CoreNames.Snes9X,
"bsnes" => CoreNames.Bsnes,
"quicknes" => CoreNames.QuickNes,
"pico" => CoreNames.PicoDrive,
_ => null
};
}
private IEmulator MakeCoreFromCoreInventory(string systemId, CoreInventoryParameters cip, string forcedCoreGameDb)
{
_config.PreferredCores.TryGetValue(systemId, out var preferredCore);
var forcedCore = ForcedCoreToCoreName(forcedCoreGameDb);
var cores = CoreInventory.Instance.GetCores(systemId)
.OrderBy(c =>
{
if (c.Name == preferredCore)
return (int)CorePriority.UserPreference;
else if (c.Name == forcedCore)
return (int)CorePriority.GameDbPreference;
else
return (int)c.Priority;
})
.ToList();
var exceptions = new List<Exception>();
foreach (var core in cores)
{
try
{
return core.Create(cip);
}
catch (Exception e)
{
exceptions.Add(e);
}
}
throw new AggregateException("No core could load the ROM", exceptions);
}
private void LoadOther(string path, CoreComm nextComm, HawkFile file, out IEmulator nextEmulator, out RomGame rom, out GameInfo game, out bool cancel)
{ {
cancel = false; cancel = false;
rom = new RomGame(file); rom = new RomGame(file);
@ -463,92 +506,17 @@ namespace BizHawk.Client.Common
game = rom.GameInfo; game = rom.GameInfo;
var isXml = false;
if (file.Extension.ToLowerInvariant() == ".xml")
{
game.System = "SNES"; // other xml has already been handled
isXml = true;
}
nextEmulator = null; nextEmulator = null;
if (game.System == null) if (game.System == null)
return; // The user picked nothing in the Core picker return; // The user picked nothing in the Core picker
CoreInventory.Core core;
switch (game.System) switch (game.System)
{ {
case "SNES":
var name = game.ForcedCore?.ToLower() switch
{
"snes9x" => CoreNames.Snes9X,
"bsnes" => CoreNames.Bsnes,
_ => _config.PreferredCores["SNES"]
};
try
{
core = CoreInventory.Instance["SNES", name];
}
catch // TODO: CoreInventory should support some sort of trygetvalue
{
// need to get rid of this hack at some point
nextEmulator = new LibsnesCore(
game,
isXml ? null : rom.FileData,
isXml ? rom.FileData : null,
Path.GetDirectoryName(path.SubstringBefore('|')),
nextComm,
GetCoreSettings<LibsnesCore, LibsnesCore.SnesSettings>(),
GetCoreSyncSettings<LibsnesCore, LibsnesCore.SnesSyncSettings>()
);
return;
}
break;
case "NES":
// apply main spur-of-the-moment switcheroo as lowest priority
var preference = _config.PreferredCores["NES"];
// if user has saw fit to override in gamedb, apply that
if (!string.IsNullOrEmpty(game.ForcedCore))
{
preference = game.ForcedCore.ToLower() switch
{
"quicknes" => CoreNames.QuickNes,
_ => CoreNames.NesHawk
};
}
// but only neshawk is accurate
if (forceAccurateCore)
{
preference = CoreNames.NesHawk;
}
core = CoreInventory.Instance["NES", preference];
break;
case "GB": case "GB":
case "GBC": case "GBC":
if (_config.GbAsSgb) if (_config.GbAsSgb)
{ {
if (_config.SgbUseBsnes) game.System = "SGB";
{
game.System = "SNES";
game.AddOption("SGB", "");
nextEmulator = new LibsnesCore(
game,
rom.FileData,
null,
null,
nextComm,
GetCoreSettings<LibsnesCore, LibsnesCore.SnesSettings>(),
GetCoreSyncSettings<LibsnesCore, LibsnesCore.SnesSyncSettings>()
);
return;
}
core = CoreInventory.Instance["SGB", CoreNames.SameBoy];
}
else
{
core = CoreInventory.Instance["GB", _config.PreferredCores["GB"]];
} }
break; break;
case "ChannelF": case "ChannelF":
@ -567,17 +535,10 @@ namespace BizHawk.Client.Common
); );
rom.GameInfo.Name = gameName; rom.GameInfo.Name = gameName;
return; return;
case "GEN":
core = CoreInventory.Instance["GEN", game.ForcedCore?.ToLower() == "pico" ? CoreNames.PicoDrive : CoreNames.Gpgx];
break;
default: default:
core = _config.PreferredCores.TryGetValue(game.System, out var coreName)
? CoreInventory.Instance[game.System, coreName]
: CoreInventory.Instance[game.System];
break; break;
} }
var cip = new CoreInventoryParameters(this)
nextEmulator = core.Create(new CoreInventoryParameters(this)
{ {
Comm = nextComm, Comm = nextComm,
Game = game, Game = game,
@ -593,7 +554,8 @@ namespace BizHawk.Client.Common
}, },
DeterministicEmulationRequested = Deterministic DeterministicEmulationRequested = Deterministic
}); };
nextEmulator = MakeCoreFromCoreInventory(game.System, cip, game.ForcedCore);
} }
private void LoadPSF(string path, CoreComm nextComm, HawkFile file, out IEmulator nextEmulator, out RomGame rom, out GameInfo game) private void LoadPSF(string path, CoreComm nextComm, HawkFile file, out IEmulator nextEmulator, out RomGame rom, out GameInfo game)
@ -747,7 +709,7 @@ namespace BizHawk.Client.Common
} }
} }
public bool LoadRom(string path, CoreComm nextComm, string launchLibretroCore, bool forceAccurateCore = false, int recursiveCount = 0) public bool LoadRom(string path, CoreComm nextComm, string launchLibretroCore, int recursiveCount = 0)
{ {
if (path == null) return false; if (path == null) return false;
@ -862,7 +824,7 @@ namespace BizHawk.Client.Common
} }
else else
{ {
LoadOther(path, nextComm, forceAccurateCore, file, out nextEmulator, out rom, out game, out cancel); // must be called after LoadXML because of SNES hacks LoadOther(path, nextComm, file, out nextEmulator, out rom, out game, out cancel); // must be called after LoadXML because of SNES hacks
} }
break; break;
} }
@ -894,7 +856,7 @@ namespace BizHawk.Client.Common
DoMessageCallback("Unable to use quicknes, using NESHawk instead"); DoMessageCallback("Unable to use quicknes, using NESHawk instead");
} }
return LoadRom(path, nextComm, launchLibretroCore, true, recursiveCount + 1); return LoadRom(path, nextComm, launchLibretroCore, recursiveCount + 1);
} }
else if (ex is MissingFirmwareException) else if (ex is MissingFirmwareException)
{ {
@ -906,7 +868,7 @@ namespace BizHawk.Client.Common
// To avoid catch-22, disable SGB mode // To avoid catch-22, disable SGB mode
_config.GbAsSgb = false; _config.GbAsSgb = false;
DoMessageCallback("Failed to load a GB rom in SGB mode. Disabling SGB Mode."); DoMessageCallback("Failed to load a GB rom in SGB mode. Disabling SGB Mode.");
return LoadRom(path, nextComm, launchLibretroCore, false, recursiveCount + 1); return LoadRom(path, nextComm, launchLibretroCore, recursiveCount + 1);
} }
else if (ex is NoAvailableCoreException) else if (ex is NoAvailableCoreException)
{ {

View File

@ -16,7 +16,8 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
{ {
private readonly LibHyperNyma _hyperNyma; private readonly LibHyperNyma _hyperNyma;
[CoreConstructor(new[] { "PCE", "SGX" })] [CoreConstructor("PCE", Priority = CorePriority.Low)]
[CoreConstructor("SGX", Priority = CorePriority.Low)]
public HyperNyma(GameInfo game, byte[] rom, CoreComm comm, string extension, public HyperNyma(GameInfo game, byte[] rom, CoreComm comm, string extension,
NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic) NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
: base(comm, "PCE", "PC Engine Controller", settings, syncSettings) : base(comm, "PCE", "PC Engine Controller", settings, syncSettings)

View File

@ -16,7 +16,8 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
{ {
private readonly LibTurboNyma _turboNyma; private readonly LibTurboNyma _turboNyma;
[CoreConstructor(new[] { "PCE", "SGX" })] [CoreConstructor("PCE")]
[CoreConstructor("SGX")]
public TurboNyma(GameInfo game, byte[] rom, CoreComm comm, string extension, public TurboNyma(GameInfo game, byte[] rom, CoreComm comm, string extension,
NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic) NymaSettings settings, NymaSyncSettings syncSettings, bool deterministic)
: base(comm, "PCE", "PC Engine Controller", settings, syncSettings) : base(comm, "PCE", "PC Engine Controller", settings, syncSettings)

View File

@ -93,7 +93,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
private static byte[] GBA_override = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 }; private static byte[] GBA_override = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 };
[CoreConstructor(new[] { "GB", "GBC" })] [CoreConstructor("GB")]
[CoreConstructor("GBC")]
public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ GBSettings settings, GBSyncSettings syncSettings) public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ GBSettings settings, GBSyncSettings syncSettings)
{ {
var ser = new BasicServiceProvider(this); var ser = new BasicServiceProvider(this);

View File

@ -23,7 +23,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
IBoardInfo, IRomInfo, IDebuggable, ISettable<Gameboy.GambatteSettings, Gameboy.GambatteSyncSettings>, IBoardInfo, IRomInfo, IDebuggable, ISettable<Gameboy.GambatteSettings, Gameboy.GambatteSyncSettings>,
IGameboyCommon, ICycleTiming, ILinkable IGameboyCommon, ICycleTiming, ILinkable
{ {
[CoreConstructor(new[] { "GB", "GBC" })] [CoreConstructor("GB")]
[CoreConstructor("GBC")]
public Gameboy(CoreComm comm, GameInfo game, byte[] file, Gameboy.GambatteSettings settings, Gameboy.GambatteSyncSettings syncSettings, bool deterministic) public Gameboy(CoreComm comm, GameInfo game, byte[] file, Gameboy.GambatteSettings settings, Gameboy.GambatteSyncSettings syncSettings, bool deterministic)
{ {
var ser = new BasicServiceProvider(this); var ser = new BasicServiceProvider(this);

View File

@ -41,6 +41,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
{ } { }
[CoreConstructor("GB")] [CoreConstructor("GB")]
[CoreConstructor("GBC")]
public Sameboy(CoreComm comm, byte[] rom, Settings settings, SyncSettings syncSettings, bool deterministic) public Sameboy(CoreComm comm, byte[] rom, Settings settings, SyncSettings syncSettings, bool deterministic)
: this(rom, comm, false, settings, syncSettings, deterministic) : this(rom, comm, false, settings, syncSettings, deterministic)
{ } { }

View File

@ -33,7 +33,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
QN.qn_setup_mappers(); QN.qn_setup_mappers();
} }
[CoreConstructor("NES")] [CoreConstructor("NES", Priority = CorePriority.Low)]
public QuickNES(byte[] file, QuickNESSettings settings, QuickNESSyncSettings syncSettings) public QuickNES(byte[] file, QuickNESSettings settings, QuickNESSyncSettings syncSettings)
{ {
FP = OSTailoredCode.IsUnixHost FP = OSTailoredCode.IsUnixHost

View File

@ -29,6 +29,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public unsafe partial class LibsnesCore : IEmulator, IVideoProvider, ISaveRam, IStatable, IInputPollable, IRegionable, ICodeDataLogger, public unsafe partial class LibsnesCore : IEmulator, IVideoProvider, ISaveRam, IStatable, IInputPollable, IRegionable, ICodeDataLogger,
IDebuggable, ISettable<LibsnesCore.SnesSettings, LibsnesCore.SnesSyncSettings> IDebuggable, ISettable<LibsnesCore.SnesSettings, LibsnesCore.SnesSyncSettings>
{ {
[CoreConstructor("SGB")]
[CoreConstructor("SNES")]
public LibsnesCore(GameInfo game, byte[] rom, CoreComm comm,
LibsnesCore.SnesSettings settings, LibsnesCore.SnesSyncSettings syncSettings)
:this(game, rom, null, null, comm, settings, syncSettings)
{}
public LibsnesCore(GameInfo game, byte[] romData, byte[] xmlData, string baseRomPath, CoreComm comm, public LibsnesCore(GameInfo game, byte[] romData, byte[] xmlData, string baseRomPath, CoreComm comm,
LibsnesCore.SnesSettings settings, LibsnesCore.SnesSyncSettings syncSettings) LibsnesCore.SnesSettings settings, LibsnesCore.SnesSyncSettings syncSettings)
{ {
@ -47,7 +54,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
CoreComm = comm; CoreComm = comm;
byte[] sgbRomData = null; byte[] sgbRomData = null;
if (game["SGB"]) if (game.System == "SGB")
{ {
if ((romData[0x143] & 0xc0) == 0xc0) if ((romData[0x143] & 0xc0) == 0xc0)
{ {
@ -112,7 +119,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
romData = newData; romData = newData;
} }
if (game["SGB"]) if (game.System == "SGB")
{ {
IsSGB = true; IsSGB = true;
SystemId = "SNES"; SystemId = "SNES";

View File

@ -14,7 +14,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SubGBHawk
public partial class SubGBHawk : IEmulator, IStatable, IInputPollable, public partial class SubGBHawk : IEmulator, IStatable, IInputPollable,
ISettable<GBHawk.GBHawk.GBSettings, GBHawk.GBHawk.GBSyncSettings>, IDebuggable ISettable<GBHawk.GBHawk.GBSettings, GBHawk.GBHawk.GBSyncSettings>, IDebuggable
{ {
[CoreConstructor(new[] { "GB", "GBC" })] [CoreConstructor("GB", Priority = CorePriority.SuperLow)]
[CoreConstructor("GBC", Priority = CorePriority.SuperLow)]
public SubGBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ GBHawk.GBHawk.GBSettings settings, GBHawk.GBHawk.GBSyncSettings syncSettings) public SubGBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ GBHawk.GBHawk.GBSettings settings, GBHawk.GBHawk.GBSyncSettings syncSettings)
{ {

View File

@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SubNESHawk
public partial class SubNESHawk : IEmulator, IStatable, IInputPollable, public partial class SubNESHawk : IEmulator, IStatable, IInputPollable,
ISettable<NES.NES.NESSettings, NES.NES.NESSyncSettings> ISettable<NES.NES.NESSettings, NES.NES.NESSyncSettings>
{ {
[CoreConstructor("NES")] [CoreConstructor("NES", Priority = CorePriority.SuperLow)]
public SubNESHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ NES.NES.NESSettings settings, NES.NES.NESSyncSettings syncSettings) public SubNESHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ NES.NES.NESSettings settings, NES.NES.NESSyncSettings syncSettings)
{ {
var subNesSettings = (NES.NES.NESSettings)settings ?? new NES.NES.NESSettings(); var subNesSettings = (NES.NES.NESSettings)settings ?? new NES.NES.NESSettings();

View File

@ -21,7 +21,8 @@ namespace BizHawk.Emulation.Cores.PCEngine
IDebuggable, ISettable<PCEngine.PCESettings, PCEngine.PCESyncSettings>, IDriveLight, ICodeDataLogger, IDebuggable, ISettable<PCEngine.PCESettings, PCEngine.PCESyncSettings>, IDriveLight, ICodeDataLogger,
IPceGpuView IPceGpuView
{ {
[CoreConstructor(new[] { "PCE", "SGX" })] [CoreConstructor("PCE", Priority = CorePriority.Low)]
[CoreConstructor("SGX", Priority = CorePriority.Low)]
public PCEngine(GameInfo game, byte[] rom, PCEngine.PCESettings settings, PCEngine.PCESyncSettings syncSettings) public PCEngine(GameInfo game, byte[] rom, PCEngine.PCESettings settings, PCEngine.PCESyncSettings syncSettings)
{ {
switch (game.System) switch (game.System)

View File

@ -19,7 +19,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.PicoDrive
private DiscSectorReader _cdReader; private DiscSectorReader _cdReader;
private bool _isPal; private bool _isPal;
[CoreConstructor(new[] { "GEN", "32X" })] [CoreConstructor("GEN", Priority = CorePriority.Low)]
[CoreConstructor("32X")]
public PicoDrive(CoreComm comm, GameInfo game, byte[] rom, bool deterministic, SyncSettings syncSettings) public PicoDrive(CoreComm comm, GameInfo game, byte[] rom, bool deterministic, SyncSettings syncSettings)
: this(comm, game, rom, null, deterministic, syncSettings) : this(comm, game, rom, null, deterministic, syncSettings)
{ } { }

View File

@ -23,7 +23,9 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
public partial class SMS : IEmulator, ISoundProvider, ISaveRam, IInputPollable, IRegionable, public partial class SMS : IEmulator, ISoundProvider, ISaveRam, IInputPollable, IRegionable,
IDebuggable, ISettable<SMS.SmsSettings, SMS.SmsSyncSettings>, ICodeDataLogger IDebuggable, ISettable<SMS.SmsSettings, SMS.SmsSyncSettings>, ICodeDataLogger
{ {
[CoreConstructor(new[] { "SMS", "SG", "GG" })] [CoreConstructor("SMS")]
[CoreConstructor("SG")]
[CoreConstructor("GG")]
public SMS(CoreComm comm, GameInfo game, byte[] rom, SmsSettings settings, SmsSyncSettings syncSettings) public SMS(CoreComm comm, GameInfo game, byte[] rom, SmsSettings settings, SmsSyncSettings syncSettings)
{ {
var ser = new BasicServiceProvider(this); var ser = new BasicServiceProvider(this);

View File

@ -28,11 +28,12 @@ namespace BizHawk.Emulation.Cores
// If true, this is a new style constructor that takes a CoreLoadParameters object // If true, this is a new style constructor that takes a CoreLoadParameters object
private readonly bool _useCoreLoadParameters; private readonly bool _useCoreLoadParameters;
public Core(string name, Type type, ConstructorInfo ctor) public Core(string name, Type type, ConstructorInfo ctor, CorePriority priority)
{ {
Name = name; Name = name;
Type = type; Type = type;
CTor = ctor; CTor = ctor;
Priority = priority;
var pp = CTor.GetParameters(); var pp = CTor.GetParameters();
if (pp.Length == 1 if (pp.Length == 1
@ -65,9 +66,14 @@ namespace BizHawk.Emulation.Cores
} }
} }
/// <summary>
/// (hopefully) a CoreNames value
/// </summary>
/// <value></value>
public string Name { get; } public string Name { get; }
public Type Type { get; } public Type Type { get; }
public ConstructorInfo CTor { get; } public ConstructorInfo CTor { get; }
public CorePriority Priority { get; }
public Type SettingsType { get; } = typeof(object); public Type SettingsType { get; } = typeof(object);
public Type SyncSettingsType { get; } = typeof(object); public Type SyncSettingsType { get; } = typeof(object);
@ -116,53 +122,22 @@ namespace BizHawk.Emulation.Cores
} }
} }
private void ProcessConstructor(Type type, string system, CoreAttribute coreAttr, ConstructorInfo cons) private void ProcessConstructor(Type type, CoreConstructorAttribute consAttr, CoreAttribute coreAttr, ConstructorInfo cons)
{ {
Core core = new Core(coreAttr.CoreName, type, cons); Core core = new Core(coreAttr.CoreName, type, cons, consAttr.Priority);
if (!_systems.TryGetValue(system, out var ss)) if (!_systems.TryGetValue(consAttr.System, out var ss))
{ {
ss = new List<Core>(); ss = new List<Core>();
_systems.Add(system, ss); _systems.Add(consAttr.System, ss);
} }
ss.Add(core); ss.Add(core);
} }
/// <summary> public IEnumerable<Core> GetCores(string system)
/// find a core matching a particular game.system
/// </summary>
public Core this[string system]
{ {
get _systems.TryGetValue(system, out var cores);
{ return cores ?? Enumerable.Empty<Core>();
List<Core> ss = _systems[system];
if (ss.Count != 1)
{
throw new InvalidOperationException("Ambiguous core selection!");
}
return ss[0];
}
}
/// <summary>
/// find a core matching a particular game.system with a particular CoreAttributes.Name
/// </summary>
public Core this[string system, string core]
{
get
{
List<Core> ss = _systems[system];
foreach (Core c in ss)
{
if (c.Name == core)
{
return c;
}
}
throw new InvalidOperationException("No such core!");
}
} }
/// <summary> /// <summary>
@ -183,9 +158,9 @@ namespace BizHawk.Emulation.Cores
.Where(c => c.GetCustomAttributes(typeof(CoreConstructorAttribute), false).Length > 0); .Where(c => c.GetCustomAttributes(typeof(CoreConstructorAttribute), false).Length > 0);
foreach(var con in cons) foreach(var con in cons)
{ {
foreach (string system in ((CoreConstructorAttribute)con.GetCustomAttributes(typeof(CoreConstructorAttribute), false)[0]).Systems) foreach (var consAttr in con.GetCustomAttributes(typeof(CoreConstructorAttribute), false).Cast<CoreConstructorAttribute>())
{ {
ProcessConstructor(typ, system, (CoreAttribute)coreAttr[0], con); ProcessConstructor(typ, consAttr, (CoreAttribute)coreAttr[0], con);
} }
} }
} }
@ -196,23 +171,45 @@ namespace BizHawk.Emulation.Cores
public static readonly CoreInventory Instance = new CoreInventory(new[] { typeof(CoreInventory).Assembly }); public static readonly CoreInventory Instance = new CoreInventory(new[] { typeof(CoreInventory).Assembly });
} }
[AttributeUsage(AttributeTargets.Constructor)] public enum CorePriority
{
/// <summary>
/// The gamedb has requested this core for this game
/// </summary>
GameDbPreference = -300,
/// <summary>
/// The user has indicated in preferences that this is their favourite core
/// </summary>
UserPreference = -200,
/// <summary>
/// A very good core that should be prefered over normal cores. Don't use this?
/// </summary>
High = -100,
/// <summary>
/// Most cores should use this
/// </summary>
Normal = 0,
/// <summary>
/// Experimental, special use, or garbage core
/// </summary>
Low = 100,
/// <summary>
/// TODO: Do we need this? Does it need a better name?
/// </summary>
SuperLow = 200,
}
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = true)]
public sealed class CoreConstructorAttribute : Attribute public sealed class CoreConstructorAttribute : Attribute
{ {
private readonly List<string> _systems = new List<string>(); public string System { get; }
/// <remarks>TODO neither array nor <see cref="IEnumerable{T}"/> is the correct collection to be using here, try <see cref="IReadOnlyList{T}"/>/<see cref="IReadOnlyCollection{T}"/> instead</remarks>
public CoreConstructorAttribute(string[] systems)
{
_systems.AddRange(systems);
}
public CoreConstructorAttribute(string system) public CoreConstructorAttribute(string system)
{ {
_systems.Add(system); System = system;
} }
public CorePriority Priority { get; set; }
public IEnumerable<string> Systems => _systems;
} }
/// <summary> /// <summary>