diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index a181a6fa5e..785f915a3e 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -29,7 +29,7 @@ namespace osu.Desktop.Deploy public static string SolutionName = ConfigurationManager.AppSettings["SolutionName"]; public static string ProjectName = ConfigurationManager.AppSettings["ProjectName"]; public static string NuSpecName = ConfigurationManager.AppSettings["NuSpecName"]; - public static string TargetName = ConfigurationManager.AppSettings["TargetName"]; + public static string TargetNames = ConfigurationManager.AppSettings["TargetName"]; public static string PackageName = ConfigurationManager.AppSettings["PackageName"]; public static string IconName = ConfigurationManager.AppSettings["IconName"]; public static string CodeSigningCertificate = ConfigurationManager.AppSettings["CodeSigningCertificate"]; @@ -100,7 +100,8 @@ namespace osu.Desktop.Deploy updateAssemblyInfo(version); write("Running build process..."); - runCommand(msbuild_path, $"/v:quiet /m /t:{TargetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln"); + foreach (string targetName in TargetNames.Split(',')) + runCommand(msbuild_path, $"/v:quiet /m /t:{targetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln"); write("Creating NuGet deployment package..."); runCommand(nuget_path, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}"); diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 7cad9306c1..2efb0c0707 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -23,7 +23,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps yield return new Fruit { StartTime = obj.StartTime, - Position = ((IHasXPosition)obj).X / OsuPlayfield.BASE_SIZE.X + NewCombo = (obj as IHasCombo)?.NewCombo ?? false, + X = ((IHasXPosition)obj).X / OsuPlayfield.BASE_SIZE.X }; } } diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs new file mode 100644 index 0000000000..7fac19d135 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Beatmaps +{ + internal class CatchBeatmapProcessor : BeatmapProcessor + { + public override void PostProcess(Beatmap beatmap) + { + if (beatmap.ComboColors.Count == 0) + return; + + int comboIndex = 0; + int colourIndex = 0; + + CatchBaseHit lastObj = null; + + foreach (var obj in beatmap.HitObjects) + { + if (obj.NewCombo) + { + if (lastObj != null) lastObj.LastInCombo = true; + + comboIndex = 0; + colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count; + } + + obj.ComboIndex = comboIndex++; + obj.ComboColour = beatmap.ComboColors[colourIndex]; + + lastObj = obj; + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs b/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs index de0547580f..2f33cf1093 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs @@ -2,11 +2,23 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects { - public abstract class CatchBaseHit : HitObject + public abstract class CatchBaseHit : HitObject, IHasXPosition, IHasCombo { - public float Position { get; set; } + public float X { get; set; } + + public Color4 ComboColour { get; set; } = Color4.Gray; + public int ComboIndex { get; set; } + + public virtual bool NewCombo { get; set; } + + /// + /// The next fruit starts a new combo. Used for explodey. + /// + public virtual bool LastInCombo { get; set; } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index 3dd086fb48..e0c9f0c028 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable { public class DrawableFruit : DrawableScrollingHitObject { - private const float pulp_size = 30; + private const float pulp_size = 20; private class Pulp : Circle, IHasAccentColour { @@ -26,15 +26,26 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable { Size = new Vector2(pulp_size); - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Radius = 5, - Colour = AccentColour.Opacity(0.5f), - }; + Blending = BlendingMode.Additive; + Colour = Color4.White.Opacity(0.9f); } - public Color4 AccentColour { get; set; } = Color4.White; + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Radius = 5, + Colour = accentColour.Lighten(100), + }; + } + } } @@ -42,12 +53,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable : base(h) { Origin = Anchor.Centre; - Size = new Vector2(pulp_size * 2, pulp_size * 2.6f); + Size = new Vector2(pulp_size * 2.2f, pulp_size * 2.8f); RelativePositionAxes = Axes.Both; - X = h.Position; + X = h.X; - Colour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1); + AccentColour = HitObject.ComboColour; + + Masking = false; Rotation = (float)(RNG.NextDouble() - 0.5f) * 40; } @@ -71,6 +84,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable RelativePositionAxes = Axes.Both, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, + AccentColour = AccentColour, Scale = new Vector2(0.6f), }, new Pulp @@ -78,6 +92,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable RelativePositionAxes = Axes.Both, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, + AccentColour = AccentColour, Y = -0.08f }, new Pulp @@ -85,6 +100,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable RelativePositionAxes = Axes.Both, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, + AccentColour = AccentColour, Y = -0.08f }, new Pulp @@ -92,6 +108,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable RelativePositionAxes = Axes.Both, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, + AccentColour = AccentColour, }, } } diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 11e95622ca..16756e65f1 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -1,7 +1,10 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -9,13 +12,22 @@ namespace osu.Game.Rulesets.Catch.Scoring { internal class CatchScoreProcessor : ScoreProcessor { - public CatchScoreProcessor() - { - } - public CatchScoreProcessor(RulesetContainer rulesetContainer) : base(rulesetContainer) { } + + protected override void SimulateAutoplay(Beatmap beatmap) + { + foreach (var obj in beatmap.HitObjects) + { + var fruit = obj as Fruit; + + if (fruit != null) + AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); + } + + base.SimulateAutoplay(beatmap); + } } } diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchPlayer.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchPlayer.cs new file mode 100644 index 0000000000..8d18a712d8 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchPlayer.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer + { + protected override Beatmap CreateBeatmap() + { + var beatmap = new Beatmap(); + + for (int i = 0; i < 256; i++) + beatmap.HitObjects.Add(new Fruit { X = 0.5f, StartTime = i * 100, NewCombo = i % 8 == 0 }); + + return beatmap; + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs index 1aee75fb78..8a6ef71996 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Catch.UI public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this); + protected override BeatmapProcessor CreateBeatmapProcessor() => new CatchBeatmapProcessor(); + protected override BeatmapConverter CreateBeatmapConverter() => new CatchBeatmapConverter(); protected override Playfield CreatePlayfield() => new CatchPlayfield(); diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 5fc2cf9ef7..2930dbb7cc 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; using osu.Framework.MathUtils; using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; @@ -20,20 +19,25 @@ namespace osu.Game.Rulesets.Catch.UI public class CatcherArea : Container { private Catcher catcher; + private Container explodingFruitContainer; public void Add(DrawableHitObject fruit, Vector2 screenPosition) => catcher.AddToStack(fruit, screenPosition); - public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.Position) < catcher.DrawSize.X / DrawSize.X / 2; + public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X / DrawSize.X / 2; [BackgroundDependencyLoader] private void load() { Children = new Drawable[] { + explodingFruitContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, catcher = new Catcher { RelativePositionAxes = Axes.Both, - Anchor = Anchor.TopLeft, + ExplodingFruitTarget = explodingFruitContainer, Origin = Anchor.TopCentre, X = 0.5f, } @@ -51,18 +55,30 @@ namespace osu.Game.Rulesets.Catch.UI { private Texture texture; + private Container caughtFruit; + [BackgroundDependencyLoader] private void load(TextureStore textures) { texture = textures.Get(@"Play/Catch/fruit-catcher-idle"); - Child = createCatcherSprite(); + Children = new Drawable[] + { + createCatcherSprite(), + caughtFruit = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.BottomCentre, + } + }; } private int currentDirection; private bool dashing; + public Container ExplodingFruitTarget; + protected bool Dashing { get { return dashing; } @@ -139,16 +155,21 @@ namespace osu.Game.Rulesets.Catch.UI return false; } + /// + /// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable. + /// + private const double base_speed = 1.0 / 512; + protected override void Update() { base.Update(); if (currentDirection == 0) return; - float speed = Dashing ? 1.5f : 1; + double dashModifier = Dashing ? 1 : 0.5; Scale = new Vector2(Math.Sign(currentDirection), 1); - X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime / 1800 * speed, 0, 1); + X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * base_speed * dashModifier, 0, 1); } public void AddToStack(DrawableHitObject fruit, Vector2 absolutePosition) @@ -163,13 +184,45 @@ namespace osu.Game.Rulesets.Catch.UI float distance = fruit.DrawSize.X / 2 * fruit.Scale.X; - while (Children.OfType().Any(f => Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance)) + while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance)) { fruit.X += RNG.Next(-5, 5); fruit.Y -= RNG.Next(0, 5); } - Add(fruit); + caughtFruit.Add(fruit); + + if (((CatchBaseHit)fruit.HitObject).LastInCombo) + explode(); + } + + private void explode() + { + var fruit = caughtFruit.ToArray(); + + foreach (var f in fruit) + { + var originalX = f.X * Scale.X; + + if (ExplodingFruitTarget != null) + { + f.Anchor = Anchor.TopLeft; + f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget); + + caughtFruit.Remove(f); + + ExplodingFruitTarget.Add(f); + } + + f.MoveToY(f.Y - 50, 250, Easing.OutSine) + .Then() + .MoveToY(f.Y + 50, 500, Easing.InSine); + + f.MoveToX(f.X + originalX * 6, 1000); + f.FadeOut(750); + + f.Expire(); + } } } } diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 639e9135c8..787825d482 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -16,7 +16,7 @@ true full false - bin\Debug\ + ..\osu.Game\bin\Debug\ DEBUG;TRACE prompt 4 @@ -26,7 +26,7 @@ pdbonly true - bin\Release\ + ..\osu.Game\bin\Release\ TRACE prompt 4 @@ -47,6 +47,7 @@ + @@ -57,6 +58,7 @@ + diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 6d0e56cb0c..fa8b9d35aa 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -16,7 +16,7 @@ true full false - bin\Debug\ + ..\osu.Game\bin\Debug\ DEBUG;TRACE prompt 4 @@ -26,7 +26,7 @@ pdbonly true - bin\Release\ + ..\osu.Game\bin\Release\ TRACE prompt 4 diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index d299faaae2..50239bf16c 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -16,10 +16,6 @@ namespace osu.Game.Rulesets.Osu.Scoring { internal class OsuScoreProcessor : ScoreProcessor { - public OsuScoreProcessor() - { - } - public OsuScoreProcessor(RulesetContainer rulesetContainer) : base(rulesetContainer) { diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 338a2b9045..300000754c 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -17,7 +17,7 @@ true full false - bin\Debug\ + ..\osu.Game\bin\Debug\ DEBUG;TRACE prompt 4 @@ -27,7 +27,7 @@ pdbonly true - bin\Release\ + ..\osu.Game\bin\Release\ TRACE prompt 4 diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index d38b24f933..2c49be287b 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -16,7 +16,7 @@ true full false - bin\Debug\ + ..\osu.Game\bin\Debug\ DEBUG;TRACE prompt 4 @@ -26,7 +26,7 @@ pdbonly true - bin\Release\ + ..\osu.Game\bin\Release\ TRACE prompt 4 diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f58b3505c5..a1b678392b 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -429,6 +429,15 @@ namespace osu.Game.Beatmaps if (beatmapSet != null) { Undelete(beatmapSet); + + // ensure all files are present and accessible + foreach (var f in beatmapSet.Files) + { + if (!storage.Exists(f.FileInfo.StoragePath)) + using (Stream s = reader.GetStream(f.Filename)) + files.Add(s, false); + } + return beatmapSet; } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index bb3122489e..277846ee80 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps { BeatmapInfo = beatmapInfo; BeatmapSetInfo = beatmapInfo.BeatmapSet; - Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo.Metadata; + Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); Mods.ValueChanged += mods => applyRateAdjustments(); } diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index 1011fa3236..c3d8c1df46 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -78,33 +78,33 @@ namespace osu.Game.IO } } - public FileInfo Add(Stream data) + public FileInfo Add(Stream data, bool reference = true) { string hash = data.ComputeSHA2Hash(); var existing = Connection.Table().Where(f => f.Hash == hash).FirstOrDefault(); var info = existing ?? new FileInfo { Hash = hash }; - if (existing != null) + + string path = Path.Combine(prefix, info.StoragePath); + + // we may be re-adding a file to fix missing store entries. + if (!Storage.Exists(path)) { - info = existing; + data.Seek(0, SeekOrigin.Begin); + + using (var output = Storage.GetStream(path, FileAccess.Write)) + data.CopyTo(output); + + data.Seek(0, SeekOrigin.Begin); } - else - { - string path = Path.Combine(prefix, info.StoragePath); - - data.Seek(0, SeekOrigin.Begin); - - if (!Storage.Exists(path)) - using (var output = Storage.GetStream(path, FileAccess.Write)) - data.CopyTo(output); - - data.Seek(0, SeekOrigin.Begin); + if (existing == null) Connection.Insert(info); - } - Reference(info); + if (reference || existing == null) + Reference(info); + return info; } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 9f831b58fb..448dccdd72 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -231,7 +231,7 @@ namespace osu.Game LocalConfig.Save(); } - connection.Dispose(); + connection?.Dispose(); base.Dispose(isDisposing); } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 8d66ad8985..5eef4a8470 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using osu.Framework.Development; using osu.Game.Database; using SQLite.Net; @@ -17,7 +16,7 @@ namespace osu.Game.Rulesets /// public class RulesetStore : DatabaseBackedStore { - private readonly List instances = new List(); + private static readonly Dictionary loaded_assemblies = new Dictionary(); public IEnumerable AllRulesets => Query().Where(r => r.Available); @@ -25,11 +24,20 @@ namespace osu.Game.Rulesets { } + static RulesetStore() + { + AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve; + + foreach (string file in Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll")) + loadRulesetFromFile(file); + } + + private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) => loaded_assemblies.Keys.FirstOrDefault(a => a.FullName == args.Name); + private const string ruleset_library_prefix = "osu.Game.Rulesets"; protected override void Prepare(bool reset = false) { - instances.Clear(); Connection.CreateTable(); @@ -38,73 +46,59 @@ namespace osu.Game.Rulesets Connection.DeleteAll(); } - // todo: don't do this on deploy - var sln = DebugUtils.GetSolutionPath(); + var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())); - if (sln != null) + Connection.RunInTransaction(() => { - foreach (string dir in Directory.GetDirectories(sln, $"{ruleset_library_prefix}.*")) - foreach (string file in Directory.GetFiles(Path.Combine(dir, "bin", DebugUtils.IsDebug ? "Debug" : "Release"), $"{ruleset_library_prefix}.*.dll")) - loadRulesetFromFile(file); - } - - foreach (string file in Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll")) - loadRulesetFromFile(file); - - 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().Where(ri => ri.InstantiationInfo == us.InstantiationInfo).FirstOrDefault(); - - if (existing == null) - Connection.Insert(us); - } - - //perform a consistency check - foreach (var r in Query()) - { - try + //add all legacy modes in correct order + foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID)) { - r.CreateInstance(); - r.Available = true; - } - catch - { - r.Available = false; + Connection.InsertOrReplace(createRulesetInfo(r)); } - Connection.Update(r); - } + //add any other modes + foreach (var r in instances.Where(r => r.LegacyID < 0)) + { + var us = createRulesetInfo(r); - Connection.Commit(); + var existing = Query().Where(ri => ri.InstantiationInfo == us.InstantiationInfo).FirstOrDefault(); + + if (existing == null) + Connection.Insert(us); + } + }); + + Connection.RunInTransaction(() => + { + //perform a consistency check + foreach (var r in Query()) + { + try + { + r.CreateInstance(); + r.Available = true; + } + catch + { + r.Available = false; + } + + Connection.Update(r); + } + }); } - private void loadRulesetFromFile(string file) + private static void loadRulesetFromFile(string file) { var filename = Path.GetFileNameWithoutExtension(file); - if (instances.Any(i => i.GetType().Namespace == filename)) + if (loaded_assemblies.Values.Any(t => t.Namespace == filename)) return; try { - var assembly = Assembly.LoadFile(file); - var rulesets = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Ruleset))); - - if (rulesets.Count() != 1) - return; - - instances.Add((Ruleset)Activator.CreateInstance(rulesets.First(), new RulesetInfo())); + var assembly = Assembly.LoadFrom(file); + loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsSubclassOf(typeof(Ruleset))); } catch (Exception) { } } diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 8b272f01a4..d722f7d711 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Platform; using osu.Framework.Testing; @@ -10,7 +11,7 @@ namespace osu.Game.Tests.Visual { public override void RunTest() { - using (var host = new HeadlessGameHost(realtime: false)) + using (var host = new HeadlessGameHost(AppDomain.CurrentDomain.FriendlyName.Replace(' ', '-'), realtime: false)) host.Run(new OsuTestCaseTestRunner(this)); } diff --git a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs index 363038e2fd..feff7497d8 100644 --- a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs @@ -2,9 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Framework.Allocation; using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; @@ -14,13 +16,20 @@ namespace osu.Game.Tests.Visual { internal class TestCasePlaySongSelect : OsuTestCase { - private readonly BeatmapManager manager; + private BeatmapManager manager; public override string Description => @"with fake data"; - private readonly RulesetStore rulesets; + private RulesetStore rulesets; - public TestCasePlaySongSelect() + private DependencyContainer dependencies; + + private FileStore files; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent); + + [BackgroundDependencyLoader] + private void load() { PlaySongSelect songSelect; @@ -31,8 +40,9 @@ namespace osu.Game.Tests.Visual var backingDatabase = storage.GetDatabase(@"client"); backingDatabase.CreateTable(); - rulesets = new RulesetStore(backingDatabase); - manager = new BeatmapManager(storage, null, backingDatabase, rulesets, null); + dependencies.Cache(rulesets = new RulesetStore(backingDatabase)); + dependencies.Cache(files = new FileStore(backingDatabase, storage)); + dependencies.Cache(manager = new BeatmapManager(storage, files, backingDatabase, rulesets, null)); for (int i = 0; i < 100; i += 10) manager.Import(createTestBeatmapSet(i)); diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 49146c4173..4a25a52e36 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -16,7 +16,7 @@ using OpenTK.Graphics; namespace osu.Game.Tests.Visual { - internal class TestCasePlayer : OsuTestCase + public class TestCasePlayer : OsuTestCase { protected Player Player; private RulesetStore rulesets; @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual loadPlayerFor(rulesets.Query().First()); } - private void loadPlayerFor(RulesetInfo r) + protected virtual Beatmap CreateBeatmap() { Beatmap beatmap; @@ -53,6 +53,13 @@ namespace osu.Game.Tests.Visual using (var reader = new StreamReader(stream)) beatmap = BeatmapDecoder.GetDecoder(reader).Decode(reader); + return beatmap; + } + + private void loadPlayerFor(RulesetInfo r) + { + var beatmap = CreateBeatmap(); + beatmap.BeatmapInfo.Ruleset = r; var instance = r.CreateInstance();