diff --git a/osu.Desktop.Tests/VisualTests.cs b/osu.Desktop.Tests/VisualTests.cs index 36dce18b05..6ef924e873 100644 --- a/osu.Desktop.Tests/VisualTests.cs +++ b/osu.Desktop.Tests/VisualTests.cs @@ -4,11 +4,6 @@ using NUnit.Framework; using osu.Desktop.VisualTests; using osu.Framework.Desktop.Platform; -using osu.Game.Modes; -using osu.Game.Modes.Catch; -using osu.Game.Modes.Mania; -using osu.Game.Modes.Osu; -using osu.Game.Modes.Taiko; namespace osu.Desktop.Tests { @@ -20,11 +15,6 @@ namespace osu.Desktop.Tests { using (var host = new HeadlessGameHost()) { - RulesetCollection.Register(typeof(OsuRuleset)); - RulesetCollection.Register(typeof(TaikoRuleset)); - RulesetCollection.Register(typeof(ManiaRuleset)); - RulesetCollection.Register(typeof(CatchRuleset)); - host.Run(new AutomatedVisualTestGame()); } } diff --git a/osu.Desktop.VisualTests/Program.cs b/osu.Desktop.VisualTests/Program.cs index 912034a927..03d1588b78 100644 --- a/osu.Desktop.VisualTests/Program.cs +++ b/osu.Desktop.VisualTests/Program.cs @@ -4,11 +4,6 @@ using System; using osu.Framework.Desktop; using osu.Framework.Platform; -using osu.Game.Modes; -using osu.Game.Modes.Catch; -using osu.Game.Modes.Mania; -using osu.Game.Modes.Osu; -using osu.Game.Modes.Taiko; namespace osu.Desktop.VisualTests { @@ -21,11 +16,6 @@ namespace osu.Desktop.VisualTests using (GameHost host = Host.GetSuitableHost(@"osu")) { - RulesetCollection.Register(typeof(OsuRuleset)); - RulesetCollection.Register(typeof(TaikoRuleset)); - RulesetCollection.Register(typeof(ManiaRuleset)); - RulesetCollection.Register(typeof(CatchRuleset)); - if (benchmark) host.Run(new AutomatedVisualTestGame()); else diff --git a/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs index c569f5f64a..d1c137191f 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs @@ -1,10 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Overlays.Mods; using osu.Framework.Testing; -using osu.Game.Modes; +using osu.Game.Database; namespace osu.Desktop.VisualTests.Tests { @@ -13,6 +14,13 @@ namespace osu.Desktop.VisualTests.Tests public override string Description => @"Tests the mod select overlay"; private ModSelectOverlay modSelect; + private RulesetDatabase rulesets; + + [BackgroundDependencyLoader] + private void load(RulesetDatabase rulesets) + { + this.rulesets = rulesets; + } public override void Reset() { @@ -27,8 +35,8 @@ namespace osu.Desktop.VisualTests.Tests AddStep("Toggle", modSelect.ToggleVisibility); - foreach (var ruleset in RulesetCollection.AllRulesets) - AddStep(ruleset.Description, () => modSelect.Ruleset.Value = ruleset); + foreach (var ruleset in rulesets.AllRulesets) + AddStep(ruleset.CreateInstance().Description, () => modSelect.Ruleset.Value = ruleset); } } } diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index dd17d62739..85ccebef77 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -3,12 +3,12 @@ using System.Collections.Generic; using osu.Desktop.VisualTests.Platform; +using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Framework.MathUtils; using osu.Game.Database; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; -using osu.Game.Modes; namespace osu.Desktop.VisualTests.Tests { @@ -20,6 +20,14 @@ namespace osu.Desktop.VisualTests.Tests public override string Description => @"with fake data"; + private RulesetDatabase rulesets; + + [BackgroundDependencyLoader] + private void load(RulesetDatabase rulesets) + { + this.rulesets = rulesets; + } + public override void Reset() { base.Reset(); @@ -72,7 +80,7 @@ namespace osu.Desktop.VisualTests.Tests new BeatmapInfo { OnlineBeatmapID = 1234 + i, - Ruleset = RulesetCollection.GetRuleset(0), + Ruleset = rulesets.Query().First(), Path = "normal.osu", Version = "Normal", Difficulty = new BeatmapDifficulty @@ -83,7 +91,7 @@ namespace osu.Desktop.VisualTests.Tests new BeatmapInfo { OnlineBeatmapID = 1235 + i, - Ruleset = RulesetCollection.GetRuleset(0), + Ruleset = rulesets.Query().First(), Path = "hard.osu", Version = "Hard", Difficulty = new BeatmapDifficulty @@ -94,7 +102,7 @@ namespace osu.Desktop.VisualTests.Tests new BeatmapInfo { OnlineBeatmapID = 1236 + i, - Ruleset = RulesetCollection.GetRuleset(0), + Ruleset = rulesets.Query().First(), Path = "insane.osu", Version = "Insane", Difficulty = new BeatmapDifficulty diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs index 2a9c31b2e9..924e753e2d 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs @@ -37,7 +37,7 @@ namespace osu.Desktop.VisualTests.Tests WorkingBeatmap beatmap = null; - var beatmapInfo = db.Query().FirstOrDefault(b => b.Ruleset is OsuRuleset); + var beatmapInfo = db.Query().FirstOrDefault(b => b.Ruleset.CreateInstance() is OsuRuleset); if (beatmapInfo != null) beatmap = db.GetWorkingBeatmap(beatmapInfo); diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index e9117cf533..210f780078 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -7,11 +7,6 @@ using osu.Desktop.Beatmaps.IO; using osu.Framework.Desktop; using osu.Framework.Desktop.Platform; using osu.Game.IPC; -using osu.Game.Modes; -using osu.Game.Modes.Catch; -using osu.Game.Modes.Mania; -using osu.Game.Modes.Osu; -using osu.Game.Modes.Taiko; namespace osu.Desktop { @@ -41,11 +36,6 @@ namespace osu.Desktop } else { - RulesetCollection.Register(typeof(OsuRuleset)); - RulesetCollection.Register(typeof(TaikoRuleset)); - RulesetCollection.Register(typeof(ManiaRuleset)); - RulesetCollection.Register(typeof(CatchRuleset)); - host.Run(new OsuGameDesktop(args)); } return 0; diff --git a/osu.Game.Modes.Catch/CatchRuleset.cs b/osu.Game.Modes.Catch/CatchRuleset.cs index 02c170d0ac..6aafb2a3c6 100644 --- a/osu.Game.Modes.Catch/CatchRuleset.cs +++ b/osu.Game.Modes.Catch/CatchRuleset.cs @@ -90,5 +90,7 @@ namespace osu.Game.Modes.Catch public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new CatchDifficultyCalculator(beatmap); public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(); + + public override int LegacyID => 2; } } diff --git a/osu.Game.Modes.Mania/ManiaRuleset.cs b/osu.Game.Modes.Mania/ManiaRuleset.cs index 7d25ea93c9..030cea7344 100644 --- a/osu.Game.Modes.Mania/ManiaRuleset.cs +++ b/osu.Game.Modes.Mania/ManiaRuleset.cs @@ -105,5 +105,7 @@ namespace osu.Game.Modes.Mania public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap); public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(); + + public override int LegacyID => 3; } } diff --git a/osu.Game.Modes.Osu/OsuRuleset.cs b/osu.Game.Modes.Osu/OsuRuleset.cs index 6ca19bb5bb..4de890ac5f 100644 --- a/osu.Game.Modes.Osu/OsuRuleset.cs +++ b/osu.Game.Modes.Osu/OsuRuleset.cs @@ -110,5 +110,7 @@ namespace osu.Game.Modes.Osu }; public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(); + + public override int LegacyID => 0; } } diff --git a/osu.Game.Modes.Taiko/TaikoRuleset.cs b/osu.Game.Modes.Taiko/TaikoRuleset.cs index e88b50bb95..b93c25c55d 100644 --- a/osu.Game.Modes.Taiko/TaikoRuleset.cs +++ b/osu.Game.Modes.Taiko/TaikoRuleset.cs @@ -91,5 +91,7 @@ namespace osu.Game.Modes.Taiko public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new TaikoDifficultyCalculator(beatmap); public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(); + + public override int LegacyID => 1; } } diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs index 9d783c7e91..91b673dc4c 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs @@ -6,7 +6,6 @@ using NUnit.Framework; using OpenTK; using OpenTK.Graphics; using osu.Game.Beatmaps.Formats; -using osu.Game.Modes; using osu.Game.Tests.Resources; using osu.Game.Modes.Osu; using osu.Game.Modes.Objects.Legacy; @@ -22,7 +21,6 @@ namespace osu.Game.Tests.Beatmaps.Formats public void SetUp() { OsuLegacyDecoder.Register(); - RulesetCollection.Register(typeof(OsuRuleset)); } [Test] @@ -58,7 +56,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(false, beatmapInfo.Countdown); Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); Assert.AreEqual(false, beatmapInfo.SpecialStyle); - Assert.IsTrue(beatmapInfo.Ruleset is OsuRuleset); + Assert.IsTrue(beatmapInfo.Ruleset.CreateInstance() is OsuRuleset); Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks); Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard); } diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index e110da59b9..0d27a6a650 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -12,11 +12,7 @@ using osu.Framework.Desktop.Platform; using osu.Framework.Platform; using osu.Game.Database; using osu.Game.IPC; -using osu.Game.Modes; -using osu.Game.Modes.Catch; -using osu.Game.Modes.Mania; using osu.Game.Modes.Osu; -using osu.Game.Modes.Taiko; namespace osu.Game.Tests.Beatmaps.IO { @@ -25,15 +21,6 @@ namespace osu.Game.Tests.Beatmaps.IO { private const string osz_path = @"../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; - [OneTimeSetUp] - public void SetUp() - { - RulesetCollection.Register(typeof(OsuRuleset)); - RulesetCollection.Register(typeof(TaikoRuleset)); - RulesetCollection.Register(typeof(ManiaRuleset)); - RulesetCollection.Register(typeof(CatchRuleset)); - } - [Test] public void TestImportWhenClosed() { @@ -166,7 +153,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(set.Beatmaps.Count > 0); - var beatmap = osu.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.Ruleset is OsuRuleset))?.Beatmap; + var beatmap = osu.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.Ruleset.CreateInstance() is OsuRuleset))?.Beatmap; Assert.IsTrue(beatmap?.HitObjects.Count > 0); } diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index add00d8f4d..03d09e24e0 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -4,8 +4,6 @@ using System.IO; using NUnit.Framework; using osu.Game.Beatmaps.IO; -using osu.Game.Modes; -using osu.Game.Modes.Osu; using osu.Game.Tests.Resources; using osu.Game.Beatmaps.Formats; using osu.Game.Database; @@ -19,7 +17,6 @@ namespace osu.Game.Tests.Beatmaps.IO public void SetUp() { OszArchiveReader.Register(); - RulesetCollection.Register(typeof(OsuRuleset)); } [Test] diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 6099b1e115..3b57d4e7fe 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -53,7 +53,7 @@ namespace osu.Game.Beatmaps /// Calculates the star difficulty for this Beatmap. /// /// The star difficulty. - public double CalculateStarDifficulty() => BeatmapInfo.Ruleset.CreateDifficultyCalculator(this).Calculate(); + public double CalculateStarDifficulty() => BeatmapInfo.Ruleset.CreateInstance().CreateDifficultyCalculator(this).Calculate(); /// /// Constructs a new beatmap. diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 02595df77a..8a9183819c 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -44,7 +44,7 @@ namespace osu.Game.Beatmaps.Drawables Origin = Anchor.Centre, TextSize = Size.X, Colour = Color4.White, - Icon = beatmap.Ruleset.Icon + Icon = beatmap.Ruleset.CreateInstance().Icon } }; } diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 36f025d1ff..8ad5f8e7c0 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -83,7 +83,7 @@ namespace osu.Game.Beatmaps.Formats beatmap.BeatmapInfo.StackLeniency = float.Parse(val, NumberFormatInfo.InvariantInfo); break; case @"Mode": - beatmap.BeatmapInfo.Mode = int.Parse(val); + beatmap.BeatmapInfo.RulesetID = int.Parse(val); break; case @"LetterboxInBreaks": beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(val) == 1; diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index 9695ba8bd0..f3f58ce8b0 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Linq.Expressions; using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Framework.Platform; @@ -77,6 +76,8 @@ namespace osu.Game.Database public override void Reset() { + Storage.DeleteDatabase(@"beatmaps"); + foreach (var setInfo in Query()) { if (Storage.Exists(setInfo.Path)) @@ -89,6 +90,13 @@ namespace osu.Game.Database Connection.DeleteAll(); } + protected override Type[] ValidTypes => new[] { + typeof(BeatmapSetInfo), + typeof(BeatmapInfo), + typeof(BeatmapMetadata), + typeof(BeatmapDifficulty), + }; + /// /// Import multiple from . /// @@ -275,47 +283,6 @@ namespace osu.Game.Database return working; } - public TableQuery Query() where T : class - { - return Connection.Table(); - } - - public T GetWithChildren(object id) where T : class - { - return Connection.GetWithChildren(id); - } - - public List GetAllWithChildren(Expression> filter = null, bool recursive = true) - where T : class - { - return Connection.GetAllWithChildren(filter, recursive); - } - - public T GetChildren(T item, bool recursive = false) - { - if (item == null) return default(T); - - Connection.GetChildren(item, recursive); - return item; - } - - private readonly Type[] validTypes = { - typeof(BeatmapSetInfo), - typeof(BeatmapInfo), - typeof(BeatmapMetadata), - typeof(BeatmapDifficulty), - }; - - public void Update(T record, bool cascade = true) where T : class - { - if (validTypes.All(t => t != typeof(T))) - throw new ArgumentException("Must be a type managed by BeatmapDatabase", nameof(T)); - if (cascade) - Connection.UpdateWithChildren(record); - else - Connection.Update(record); - } - public bool Exists(BeatmapSetInfo beatmapSet) => Storage.Exists(beatmapSet.Path); } } diff --git a/osu.Game/Database/BeatmapInfo.cs b/osu.Game/Database/BeatmapInfo.cs index be4faebde2..5097622deb 100644 --- a/osu.Game/Database/BeatmapInfo.cs +++ b/osu.Game/Database/BeatmapInfo.cs @@ -3,7 +3,6 @@ using Newtonsoft.Json; using osu.Game.IO.Serialization; -using osu.Game.Modes; using SQLite.Net.Attributes; using SQLiteNetExtensions.Attributes; using System; @@ -55,15 +54,12 @@ namespace osu.Game.Database public float StackLeniency { get; set; } public bool SpecialStyle { get; set; } - public int Mode { get; set; } + [ForeignKey(typeof(RulesetInfo))] + public int RulesetID { get; set; } + + [OneToOne(CascadeOperations = CascadeOperation.All)] + public RulesetInfo Ruleset { get; set; } - [Ignore] - public Ruleset Ruleset - { - get { return RulesetCollection.GetRuleset(Mode); } - set { Mode = RulesetCollection.GetId(value); } - } - public bool LetterboxInBreaks { get; set; } public bool WidescreenStoryboard { get; set; } diff --git a/osu.Game/Database/Database.cs b/osu.Game/Database/Database.cs index 6f8e902663..19bd78b39c 100644 --- a/osu.Game/Database/Database.cs +++ b/osu.Game/Database/Database.cs @@ -2,9 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; using osu.Framework.Logging; using osu.Framework.Platform; using SQLite.Net; +using SQLiteNetExtensions.Extensions; namespace osu.Game.Database { @@ -24,8 +28,7 @@ namespace osu.Game.Database } catch (Exception e) { - Logger.Error(e, @"Failed to initialise the beatmap database! Trying again with a clean database..."); - storage.DeleteDatabase(@"beatmaps"); + Logger.Error(e, $@"Failed to initialise the {GetType()}! Trying again with a clean database..."); Reset(); Prepare(); } @@ -40,5 +43,41 @@ namespace osu.Game.Database /// Reset this database to a default state. Undo all changes to database and storage backings. /// public abstract void Reset(); + + public TableQuery Query() where T : class + { + return Connection.Table(); + } + + public T GetWithChildren(object id) where T : class + { + return Connection.GetWithChildren(id); + } + + public List GetAllWithChildren(Expression> filter = null, bool recursive = true) + where T : class + { + return Connection.GetAllWithChildren(filter, recursive); + } + + public T GetChildren(T item, bool recursive = false) + { + if (item == null) return default(T); + + Connection.GetChildren(item, recursive); + return item; + } + + protected abstract Type[] ValidTypes { get; } + + public void Update(T record, bool cascade = true) where T : class + { + if (ValidTypes.All(t => t != typeof(T))) + throw new ArgumentException("Must be a type managed by BeatmapDatabase", nameof(T)); + if (cascade) + Connection.UpdateWithChildren(record); + else + Connection.Update(record); + } } } \ No newline at end of file diff --git a/osu.Game/Database/RulesetDatabase.cs b/osu.Game/Database/RulesetDatabase.cs new file mode 100644 index 0000000000..45db5df26f --- /dev/null +++ b/osu.Game/Database/RulesetDatabase.cs @@ -0,0 +1,107 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using osu.Framework.Platform; +using osu.Game.Modes; +using SQLite.Net; + +namespace osu.Game.Database +{ + /// + /// Todo: All of this needs to be moved to a RulesetDatabase. + /// + public class RulesetDatabase : Database + { + public IEnumerable AllRulesets => Query().Where(r => r.Available); + + public RulesetDatabase(Storage storage, SQLiteConnection connection) + : base(storage, connection) + { + } + + protected override void Prepare() + { + Connection.CreateTable(); + + List instances = new List(); + + foreach (string file in Directory.GetFiles(Environment.CurrentDirectory, @"osu.Game.Modes.*.dll")) + { + try + { + var assembly = Assembly.LoadFile(file); + var rulesets = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Ruleset))); + + if (rulesets.Count() != 1) + continue; + + Assembly.LoadFile(file); + + foreach (Type rulesetType in rulesets) + instances.Add((Ruleset)Activator.CreateInstance(rulesetType)); + } + catch (Exception) { } + } + + Connection.BeginTransaction(); + + //add all legacy modes in correct order + foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID)) + { + Connection.InsertOrReplace(createRulesetInfo(r)); + } + + //add any other modes + foreach (var r in instances.Where(r => r.LegacyID < 0)) + { + var us = createRulesetInfo(r); + + var existing = Query().FirstOrDefault(ri => ri.InstantiationInfo == us.InstantiationInfo); + + if (existing == null) + Connection.Insert(us); + } + + //perform a consistency check + foreach (var r in Query()) + { + try + { + r.CreateInstance(); + r.Available = true; + } + catch + { + r.Available = false; + } + + Connection.Update(r); + } + + Connection.Commit(); + + + } + + private RulesetInfo createRulesetInfo(Ruleset ruleset) => new RulesetInfo + { + Name = ruleset.Description, + InstantiationInfo = ruleset.GetType().AssemblyQualifiedName, + ID = ruleset.LegacyID + }; + + public override void Reset() + { + Connection.DeleteAll(); + } + + protected override Type[] ValidTypes => new[] { typeof(RulesetInfo) }; + + public RulesetInfo GetRuleset(int id) => Query().First(r => r.ID == id); + } +} diff --git a/osu.Game/Database/RulesetInfo.cs b/osu.Game/Database/RulesetInfo.cs new file mode 100644 index 0000000000..6a0ee13e41 --- /dev/null +++ b/osu.Game/Database/RulesetInfo.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Modes; +using SQLite.Net.Attributes; + +namespace osu.Game.Database +{ + public class RulesetInfo + { + [PrimaryKey, AutoIncrement] + public int ID { get; set; } + + public string Name { get; set; } + + public string InstantiationInfo { get; set; } + + [Indexed] + public bool Available { get; set; } + + public Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo)); + } +} \ No newline at end of file diff --git a/osu.Game/Database/ScoreDatabase.cs b/osu.Game/Database/ScoreDatabase.cs index 240b4fa8e6..91a52fba4a 100644 --- a/osu.Game/Database/ScoreDatabase.cs +++ b/osu.Game/Database/ScoreDatabase.cs @@ -7,7 +7,6 @@ using System.Linq; using osu.Framework.Platform; using osu.Game.IO.Legacy; using osu.Game.IPC; -using osu.Game.Modes; using osu.Game.Modes.Scoring; using SharpCompress.Compressors.LZMA; using SQLite.Net; @@ -17,17 +16,20 @@ namespace osu.Game.Database public class ScoreDatabase : Database { private readonly Storage storage; + private readonly BeatmapDatabase beatmaps; + private readonly RulesetDatabase rulesets; private const string replay_folder = @"replays"; // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ScoreIPCChannel ipc; - public ScoreDatabase(Storage storage, SQLiteConnection connection, IIpcHost importHost = null, BeatmapDatabase beatmaps = null) : base(storage, connection) + public ScoreDatabase(Storage storage, SQLiteConnection connection, IIpcHost importHost = null, BeatmapDatabase beatmaps = null, RulesetDatabase rulesets = null) : base(storage, connection) { this.storage = storage; this.beatmaps = beatmaps; + this.rulesets = rulesets; if (importHost != null) ipc = new ScoreIPCChannel(importHost, this); @@ -40,7 +42,7 @@ namespace osu.Game.Database using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename))) using (SerializationReader sr = new SerializationReader(s)) { - var ruleset = RulesetCollection.GetRuleset(sr.ReadByte()); + var ruleset = rulesets.GetRuleset(sr.ReadByte()).CreateInstance(); score = ruleset.CreateScoreProcessor().CreateScore(); /* score.Pass = true;*/ @@ -116,5 +118,7 @@ namespace osu.Game.Database public override void Reset() { } + + protected override Type[] ValidTypes => new[] { typeof(Score) }; } } diff --git a/osu.Game/Modes/BeatmapStatistic.cs b/osu.Game/Modes/BeatmapStatistic.cs new file mode 100644 index 0000000000..d598b81ff4 --- /dev/null +++ b/osu.Game/Modes/BeatmapStatistic.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; + +namespace osu.Game.Modes +{ + public class BeatmapStatistic + { + public FontAwesome Icon; + public string Content; + public string Name; + } +} \ No newline at end of file diff --git a/osu.Game/Modes/Ruleset.cs b/osu.Game/Modes/Ruleset.cs index 284a71d518..cf0fbe5b6a 100644 --- a/osu.Game/Modes/Ruleset.cs +++ b/osu.Game/Modes/Ruleset.cs @@ -11,13 +11,6 @@ using osu.Game.Modes.Scoring; namespace osu.Game.Modes { - public class BeatmapStatistic - { - public FontAwesome Icon; - public string Content; - public string Name; - } - public abstract class Ruleset { public virtual IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { }; @@ -35,5 +28,10 @@ namespace osu.Game.Modes public abstract string Description { get; } public abstract IEnumerable CreateGameplayKeys(); + + /// + /// Do not override this unless you are a legacy mode. + /// + public virtual int LegacyID => -1; } } diff --git a/osu.Game/Modes/RulesetCollection.cs b/osu.Game/Modes/RulesetCollection.cs deleted file mode 100644 index 221fa6f5ba..0000000000 --- a/osu.Game/Modes/RulesetCollection.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Modes -{ - /// - /// Todo: All of this needs to be moved to a RulesetDatabase. - /// - public static class RulesetCollection - { - private static readonly ConcurrentDictionary available_rulesets = new ConcurrentDictionary(); - - public static void Register(Type type) - { - var ruleset = Activator.CreateInstance(type) as Ruleset; - - if (ruleset == null) - return; - - available_rulesets.TryAdd(available_rulesets.Count, ruleset); - } - - public static Ruleset GetRuleset(int rulesetId) - { - Ruleset ruleset; - - if (!available_rulesets.TryGetValue(rulesetId, out ruleset)) - return null; - - return ruleset; - } - - public static int GetId(Ruleset ruleset) => available_rulesets.First(kvp => kvp.Value == ruleset).Key; - - public static IEnumerable AllRulesets => available_rulesets.Values; - } -} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 3379787d6d..6062f3fd59 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -15,7 +15,6 @@ using osu.Framework.Logging; using osu.Game.Graphics.UserInterface.Volume; using osu.Framework.Allocation; using osu.Framework.Timing; -using osu.Game.Modes; using osu.Game.Overlays.Toolbar; using osu.Game.Screens; using osu.Game.Screens.Menu; @@ -24,6 +23,7 @@ using System.Linq; using osu.Framework.Graphics.Primitives; using System.Threading.Tasks; using osu.Framework.Threading; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Modes.Scoring; using osu.Game.Overlays.Notifications; @@ -59,7 +59,7 @@ namespace osu.Game private VolumeControl volume; private Bindable configRuleset; - public Bindable Ruleset = new Bindable(); + public Bindable Ruleset = new Bindable(); private readonly string[] args; @@ -90,8 +90,8 @@ namespace osu.Game Dependencies.Cache(this); configRuleset = LocalConfig.GetBindable(OsuConfig.Ruleset); - Ruleset.Value = RulesetCollection.GetRuleset(configRuleset.Value); - Ruleset.ValueChanged += r => configRuleset.Value = RulesetCollection.GetId(r); + Ruleset.Value = RulesetDatabase.GetRuleset(configRuleset.Value); + Ruleset.ValueChanged += r => configRuleset.Value = r.ID; } private ScheduledDelegate scoreLoad; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index c37d2b83eb..c6a5cbef1b 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -28,6 +28,8 @@ namespace osu.Game protected BeatmapDatabase BeatmapDatabase; + protected RulesetDatabase RulesetDatabase; + protected ScoreDatabase ScoreDatabase; protected override string MainResourceFile => @"osu.Game.Resources.dll"; @@ -85,6 +87,7 @@ namespace osu.Game SQLiteConnection connection = Host.Storage.GetDatabase(@"client"); Dependencies.Cache(BeatmapDatabase = new BeatmapDatabase(Host.Storage, connection, Host)); + Dependencies.Cache(RulesetDatabase = new RulesetDatabase(Host.Storage, connection)); Dependencies.Cache(ScoreDatabase = new ScoreDatabase(Host.Storage, connection, Host, BeatmapDatabase)); Dependencies.Cache(new OsuColour()); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 02df95ff8a..30ccb2d005 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -13,11 +13,11 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; -using osu.Game.Modes; using osu.Game.Modes.Mods; using System; using System.Collections.Generic; using System.Linq; +using osu.Game.Database; namespace osu.Game.Overlays.Mods { @@ -37,12 +37,14 @@ namespace osu.Game.Overlays.Mods public readonly Bindable> SelectedMods = new Bindable>(); - public readonly Bindable Ruleset = new Bindable(RulesetCollection.GetRuleset(0)); + public readonly Bindable Ruleset = new Bindable(); - private void rulesetChanged(Ruleset newRuleset) + private void rulesetChanged(RulesetInfo newRuleset) { + var instance = newRuleset.CreateInstance(); + foreach (ModSection section in modSectionsContainer.Children) - section.Buttons = newRuleset.GetModsFor(section.ModType).Select(m => new ModButton(m)).ToArray(); + section.Buttons = instance.GetModsFor(section.ModType).Select(m => new ModButton(m)).ToArray(); refreshSelectedMods(); } diff --git a/osu.Game/Overlays/Options/OptionsFooter.cs b/osu.Game/Overlays/Options/OptionsFooter.cs index ad184c8838..c785f2d0c0 100644 --- a/osu.Game/Overlays/Options/OptionsFooter.cs +++ b/osu.Game/Overlays/Options/OptionsFooter.cs @@ -6,9 +6,9 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Modes; using OpenTK; using OpenTK.Graphics; @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Options public class OptionsFooter : FillFlowContainer { [BackgroundDependencyLoader] - private void load(OsuGameBase game, OsuColour colours) + private void load(OsuGameBase game, OsuColour colours, RulesetDatabase rulesets) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -26,11 +26,11 @@ namespace osu.Game.Overlays.Options var modes = new List(); - foreach (var ruleset in RulesetCollection.AllRulesets) + foreach (var ruleset in rulesets.AllRulesets) { modes.Add(new TextAwesome { - Icon = ruleset.Icon, + Icon = ruleset.CreateInstance().Icon, Colour = Color4.Gray, TextSize = 20 }); diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index ec76a37e6e..4632b55775 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -8,8 +8,8 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; +using osu.Game.Database; using osu.Game.Graphics; -using osu.Game.Modes; using OpenTK; namespace osu.Game.Overlays.Toolbar @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Toolbar public const float TOOLTIP_HEIGHT = 30; public Action OnHome; - public Action OnRulesetChange; + public Action OnRulesetChange; private readonly ToolbarModeSelector modeSelector; private readonly ToolbarUserArea userArea; @@ -129,7 +129,7 @@ namespace osu.Game.Overlays.Toolbar } } - public void SetGameMode(Ruleset ruleset) => modeSelector.SetGameMode(ruleset); + public void SetGameMode(RulesetInfo ruleset) => modeSelector.SetGameMode(ruleset); protected override void PopIn() { diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs index 6ace90bcb4..dd70289f7d 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs @@ -2,23 +2,26 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics.Containers; -using osu.Game.Modes; +using osu.Game.Database; using OpenTK.Graphics; namespace osu.Game.Overlays.Toolbar { public class ToolbarModeButton : ToolbarButton { - private Ruleset ruleset; - public Ruleset Ruleset + private RulesetInfo ruleset; + public RulesetInfo Ruleset { get { return ruleset; } set { ruleset = value; - TooltipMain = ruleset.Description; - TooltipSub = $"Play some {ruleset.Description}"; - Icon = ruleset.Icon; + + var rInstance = ruleset.CreateInstance(); + + TooltipMain = rInstance.Description; + TooltipSub = $"Play some {rInstance.Description}"; + Icon = rInstance.Icon; } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index 0d673eb3fe..e4c9db7c5d 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -3,12 +3,13 @@ using System; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Sprites; -using osu.Game.Modes; +using osu.Game.Database; using OpenTK; using OpenTK.Graphics; @@ -22,7 +23,7 @@ namespace osu.Game.Overlays.Toolbar private readonly Drawable modeButtonLine; private ToolbarModeButton activeButton; - public Action OnRulesetChange; + public Action OnRulesetChange; public ToolbarModeSelector() { @@ -62,8 +63,12 @@ namespace osu.Game.Overlays.Toolbar } } }; + } - foreach (var ruleset in RulesetCollection.AllRulesets) + [BackgroundDependencyLoader] + private void load(RulesetDatabase rulesets) + { + foreach (var ruleset in rulesets.AllRulesets) { modeButtons.Add(new ToolbarModeButton { @@ -84,11 +89,11 @@ namespace osu.Game.Overlays.Toolbar Size = new Vector2(modeButtons.DrawSize.X, 1); } - public void SetGameMode(Ruleset ruleset) + public void SetGameMode(RulesetInfo ruleset) { foreach (ToolbarModeButton m in modeButtons.Children.Cast()) { - bool isActive = m.Ruleset == ruleset; + bool isActive = m.Ruleset.ID == ruleset.ID; m.Active = isActive; if (isActive) activeButton = m; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b59b3bf0c1..b32548c31a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -102,7 +102,7 @@ namespace osu.Game.Screens.Play sourceClock.Reset(); }); - ruleset = Beatmap.BeatmapInfo.Ruleset; + ruleset = Beatmap.BeatmapInfo.Ruleset.CreateInstance(); // Todo: This should be done as early as possible, and should check if the hit renderer // can actually convert the hit objects... Somehow... diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 775efd5a40..2d873d10ab 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Select })); //get statistics fromt he current ruleset. - labels.AddRange(beatmap.BeatmapInfo.Ruleset.GetBeatmapStatistics(beatmap).Select(s => new InfoLabel(s))); + labels.AddRange(beatmap.BeatmapInfo.Ruleset.CreateInstance().GetBeatmapStatistics(beatmap).Select(s => new InfoLabel(s))); } AlwaysPresent = true; diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 1e2f074939..e0b197e9ca 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -16,7 +16,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Select.Filter; using Container = osu.Framework.Graphics.Containers.Container; using osu.Framework.Input; -using osu.Game.Modes; +using osu.Game.Database; namespace osu.Game.Screens.Select { @@ -163,7 +163,7 @@ namespace osu.Game.Screens.Select searchTextBox.HoldFocus = true; } - private readonly Bindable ruleset = new Bindable(RulesetCollection.GetRuleset(0)); + private readonly Bindable ruleset = new Bindable(); [BackgroundDependencyLoader(permitNulls:true)] private void load(OsuColour colours, OsuGame osu) diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 0b750ef8d3..1a32244deb 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps.Drawables; -using osu.Game.Modes; +using osu.Game.Database; using osu.Game.Screens.Select.Filter; namespace osu.Game.Screens.Select @@ -15,7 +15,7 @@ namespace osu.Game.Screens.Select public GroupMode Group; public SortMode Sort; public string SearchText; - public Ruleset Ruleset; + public RulesetInfo Ruleset; public void Filter(List groups) { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 183eb7de32..182158fa5d 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -20,7 +20,6 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Modes; using osu.Game.Overlays; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Select.Options; @@ -29,7 +28,7 @@ namespace osu.Game.Screens.Select { public abstract class SongSelect : OsuScreen { - private readonly Bindable ruleset = new Bindable(RulesetCollection.GetRuleset(0)); + private readonly Bindable ruleset = new Bindable(); private BeatmapDatabase database; protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a450125e48..193c2c1179 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -79,6 +79,7 @@ + @@ -102,6 +103,7 @@ + @@ -133,7 +135,7 @@ - +