Merge remote-tracking branch 'upstream/master' into settings-footer-show-changelog-current-build

This commit is contained in:
Dean Herbert 2019-06-07 00:20:32 +09:00
commit e22a5896fa
92 changed files with 1605 additions and 437 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
custom: https://osu.ppy.sh/home/support

View File

@ -10,6 +10,8 @@ This project is still heavily under development, but is in a state where users a
We are accepting bug reports (please report with as much detail as possible). Feature requests are welcome as long as you read and understand the contribution guidelines listed below. We are accepting bug reports (please report with as much detail as possible). Feature requests are welcome as long as you read and understand the contribution guidelines listed below.
Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh/home/changelog).
## Requirements ## Requirements
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed. - A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
@ -20,17 +22,24 @@ We are accepting bug reports (please report with as much detail as possible). Fe
### Releases ### Releases
If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled. ![](https://puu.sh/DCmvA/f6a74f5fbb.png)
- Windows (x64) users should download and run `install.exe`. If you are not interested in developing the game, you can consume our [binary releases](https://github.com/ppy/osu/releases).
- macOS users (10.12 "Sierra" and higher) should download and run `osu.app.zip`.
- iOS users can join the [TestFlight beta program](https://t.co/xQJmHkfC18). **Latest build:***
| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) |
| ------------- | ------------- |
- **Linux** users are recommended to self-compile until we have official deployment in place.
- **iOS** users can join the [TestFlight beta program](https://t.co/xQJmHkfC18) (note that due to high demand this is reulgarly full).
- **Android** users can self-compile, and expect a public beta soon.
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below. If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
### Downloading the source code ### Downloading the source code
Clone the repository **including submodules**: Clone the repository:
```shell ```shell
git clone https://github.com/ppy/osu git clone https://github.com/ppy/osu
@ -45,7 +54,7 @@ git pull
### Building ### Building
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided below. Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided [below](#contributing).
> Visual Studio Code users must run the `Restore` task before any build attempt. > Visual Studio Code users must run the `Restore` task before any build attempt.

View File

@ -20,14 +20,14 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
} }
protected override IBeatmap CreateBeatmap(Ruleset ruleset) protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 }, BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 },
Ruleset = ruleset.RulesetInfo Ruleset = ruleset
} }
}; };

View File

@ -29,14 +29,14 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
} }
protected override IBeatmap CreateBeatmap(Ruleset ruleset) protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset.RulesetInfo Ruleset = ruleset
} }
}; };

View File

@ -16,14 +16,14 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
} }
protected override IBeatmap CreateBeatmap(Ruleset ruleset) protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset.RulesetInfo Ruleset = ruleset
} }
}; };

View File

@ -23,13 +23,13 @@ namespace osu.Game.Rulesets.Catch.Tests
AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash); AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash);
} }
protected override IBeatmap CreateBeatmap(Ruleset ruleset) protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {
BeatmapInfo = BeatmapInfo =
{ {
Ruleset = ruleset.RulesetInfo, Ruleset = ruleset,
BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f } BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f }
} }
}; };

View File

@ -17,14 +17,14 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
} }
protected override IBeatmap CreateBeatmap(Ruleset ruleset) protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset.RulesetInfo Ruleset = ruleset
} }
}; };

View File

@ -20,7 +20,6 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
using osuTK; using osuTK;
@ -299,7 +298,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
AddStep("load player", () => AddStep("load player", () =>
{ {
Beatmap.Value = new TestWorkingBeatmap(new Beatmap<OsuHitObject> Beatmap.Value = CreateWorkingBeatmap(new Beatmap<OsuHitObject>
{ {
HitObjects = HitObjects =
{ {
@ -323,7 +322,7 @@ namespace osu.Game.Rulesets.Osu.Tests
BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 3 }, BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 3 },
Ruleset = new OsuRuleset().RulesetInfo Ruleset = new OsuRuleset().RulesetInfo
}, },
}, Clock); });
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });

View File

@ -2,13 +2,10 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Primitives;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osuTK.Graphics.ES30;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
@ -19,8 +16,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private readonly SliderPath path; private readonly SliderPath path;
protected Path Path => path; protected Path Path => path;
private readonly BufferedContainer container;
public float PathRadius public float PathRadius
{ {
get => path.PathRadius; get => path.PathRadius;
@ -44,8 +39,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return; return;
path.AccentColour = value; path.AccentColour = value;
container.ForceRedraw();
} }
} }
@ -61,8 +54,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return; return;
path.BorderColour = value; path.BorderColour = value;
container.ForceRedraw();
} }
} }
@ -78,23 +69,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return; return;
path.BorderSize = value; path.BorderSize = value;
container.ForceRedraw();
} }
} }
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
protected SliderBody() protected SliderBody()
{ {
InternalChild = container = new BufferedContainer InternalChild = path = new SliderPath();
{
RelativeSizeAxes = Axes.Both,
CacheDrawnFrameBuffer = true,
Child = path = new SliderPath { Blending = BlendingMode.None }
};
container.Attach(RenderbufferInternalFormat.DepthComponent16);
} }
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
@ -103,11 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
/// Sets the vertices of the path which should be drawn by this <see cref="SliderBody"/>. /// Sets the vertices of the path which should be drawn by this <see cref="SliderBody"/>.
/// </summary> /// </summary>
/// <param name="vertices">The vertices</param> /// <param name="vertices">The vertices</param>
protected void SetVertices(IReadOnlyList<Vector2> vertices) protected void SetVertices(IReadOnlyList<Vector2> vertices) => path.Vertices = vertices;
{
path.Vertices = vertices;
container.ForceRedraw();
}
private class SliderPath : SmoothPath private class SliderPath : SmoothPath
{ {

View File

@ -210,7 +210,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
Vector2 pos = parts[i].Position; Vector2 pos = parts[i].Position;
float localTime = parts[i].Time; float localTime = parts[i].Time;
texture.DrawQuad( DrawQuad(
texture,
new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y), new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
DrawColourInfo.Colour, DrawColourInfo.Colour,
null, null,

View File

@ -18,7 +18,6 @@ using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
using osuTK; using osuTK;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -64,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
var controlPointInfo = new ControlPointInfo(); var controlPointInfo = new ControlPointInfo();
controlPointInfo.TimingPoints.Add(new TimingControlPoint()); controlPointInfo.TimingPoints.Add(new TimingControlPoint());
WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
{ {
HitObjects = new List<HitObject> { new CentreHit() }, HitObjects = new List<HitObject> { new CentreHit() },
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
@ -79,7 +78,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Ruleset = new TaikoRuleset().RulesetInfo Ruleset = new TaikoRuleset().RulesetInfo
}, },
ControlPointInfo = controlPointInfo ControlPointInfo = controlPointInfo
}, Clock); });
Add(playfieldContainer = new Container Add(playfieldContainer = new Container
{ {

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
@ -54,7 +55,7 @@ namespace osu.Game.Tests.Visual.Background
private RulesetStore rulesets; private RulesetStore rulesets;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(GameHost host) private void load(GameHost host, AudioManager audio)
{ {
factory = new DatabaseContextFactory(LocalStorage); factory = new DatabaseContextFactory(LocalStorage);
factory.ResetDatabase(); factory.ResetDatabase();
@ -68,7 +69,7 @@ namespace osu.Game.Tests.Visual.Background
usage.Migrate(); usage.Migrate();
Dependencies.Cache(rulesets = new RulesetStore(factory)); Dependencies.Cache(rulesets = new RulesetStore(factory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null, host, Beatmap.Default)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(new OsuConfigManager(LocalStorage)); Dependencies.Cache(new OsuConfigManager(LocalStorage));
manager.Import(TestResources.GetTestBeatmapForImport()); manager.Import(TestResources.GetTestBeatmapForImport());

View File

@ -111,16 +111,19 @@ namespace osu.Game.Tests.Visual.Components
private class TestPreviewTrackManager : PreviewTrackManager private class TestPreviewTrackManager : PreviewTrackManager
{ {
protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) => new TestPreviewTrack(beatmapSetInfo, trackManager); protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore);
protected class TestPreviewTrack : TrackManagerPreviewTrack protected class TestPreviewTrack : TrackManagerPreviewTrack
{ {
public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) private readonly ITrackStore trackManager;
public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackManager)
: base(beatmapSetInfo, trackManager) : base(beatmapSetInfo, trackManager)
{ {
this.trackManager = trackManager;
} }
protected override Track GetTrack() => new TrackVirtual { Length = 100000 }; protected override Track GetTrack() => trackManager.GetVirtual(100000);
} }
} }
} }

View File

@ -7,7 +7,6 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editor
{ {
@ -19,7 +18,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, Clock); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
Child = new ComposeScreen(); Child = new ComposeScreen();
} }
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -30,9 +31,9 @@ namespace osu.Game.Tests.Visual.Editor
}; };
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load(AudioManager audio)
{ {
Beatmap.Value = new WaveformTestBeatmap(); Beatmap.Value = new WaveformTestBeatmap(audio);
Children = new Drawable[] Children = new Drawable[]
{ {

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using NUnit.Framework; using NUnit.Framework;
@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -48,7 +47,7 @@ namespace osu.Game.Tests.Visual.Editor
} }
}; };
Beatmap.Value = new TestWorkingBeatmap(testBeatmap, Clock); Beatmap.Value = CreateWorkingBeatmap(testBeatmap);
Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock }; Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock };
} }

View File

@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Game.Tests.Beatmaps;
using osuTK; using osuTK;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editor
@ -21,7 +20,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
Add(new SummaryTimeline Add(new SummaryTimeline
{ {

View File

@ -18,7 +18,6 @@ using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Tests.Beatmaps;
using osuTK; using osuTK;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editor
@ -45,7 +44,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Beatmap.Value = new TestWorkingBeatmap(new Beatmap Beatmap.Value = CreateWorkingBeatmap(new Beatmap
{ {
HitObjects = new List<HitObject> HitObjects = new List<HitObject>
{ {

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using NUnit.Framework; using NUnit.Framework;
@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Edit.Components;
using osu.Game.Tests.Beatmaps;
using osuTK; using osuTK;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editor
@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Editor
Size = new Vector2(200, 100) Size = new Vector2(200, 100)
}; };
Beatmap.Value = new TestWorkingBeatmap(new Beatmap(), Clock); Beatmap.Value = CreateWorkingBeatmap(new Beatmap());
Child = playback; Child = playback;
} }

View File

@ -3,6 +3,7 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Audio;
@ -10,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Osu;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editor
@ -20,9 +22,9 @@ namespace osu.Game.Tests.Visual.Editor
private WorkingBeatmap waveformBeatmap; private WorkingBeatmap waveformBeatmap;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load(AudioManager audio)
{ {
waveformBeatmap = new WaveformTestBeatmap(); waveformBeatmap = new WaveformTestBeatmap(audio);
} }
[TestCase(1f)] [TestCase(1f)]
@ -91,7 +93,7 @@ namespace osu.Game.Tests.Visual.Editor
Child = graph = new TestWaveformGraph Child = graph = new TestWaveformGraph
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Waveform = new DummyWorkingBeatmap().Waveform, Waveform = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).Waveform,
}, },
}; };
}); });

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
@ -16,7 +16,6 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens; using osu.Game.Screens;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void Setup() => Schedule(() => public void Setup() => Schedule(() =>
{ {
InputManager.Child = stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }; InputManager.Child = stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
Beatmap.Value = new TestWorkingBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), Clock); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
}); });
[Test] [Test]

View File

@ -3,7 +3,6 @@
using System; using System;
using osu.Framework.Lists; using osu.Framework.Lists;
using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
@ -43,9 +42,9 @@ namespace osu.Game.Tests.Visual.Gameplay
}); });
} }
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock clock) protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
{ {
var working = base.CreateWorkingBeatmap(beatmap, clock); var working = base.CreateWorkingBeatmap(beatmap);
workingWeakReferences.Add(working); workingWeakReferences.Add(working);
return working; return working;
} }

View File

@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
@ -57,7 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("set name", () => Room.Name.Value = "Room name"); AddStep("set name", () => Room.Name.Value = "Room name");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value); AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo })); AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo }));
AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value); AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
AddStep("clear name", () => Room.Name.Value = ""); AddStep("clear name", () => Room.Name.Value = "");

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Direct; using osu.Game.Overlays.Direct;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Tests.Beatmaps;
using osuTK; using osuTK;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
@ -25,7 +24,7 @@ namespace osu.Game.Tests.Visual.Online
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
var beatmap = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null); var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true; beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true;
beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true; beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true;

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
@ -18,7 +19,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BeatmapDetails) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BeatmapDetails) };
public TestSceneBeatmapDetailArea() [BackgroundDependencyLoader]
private void load(OsuGameBase game)
{ {
BeatmapDetailArea detailsArea; BeatmapDetailArea detailsArea;
Add(detailsArea = new BeatmapDetailArea Add(detailsArea = new BeatmapDetailArea
@ -28,7 +30,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Size = new Vector2(550f, 450f), Size = new Vector2(550f, 450f),
}); });
AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{ {
BeatmapInfo = BeatmapInfo =
{ {
@ -56,7 +58,7 @@ namespace osu.Game.Tests.Visual.SongSelect
} }
); );
AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{ {
BeatmapInfo = BeatmapInfo =
{ {
@ -82,7 +84,7 @@ namespace osu.Game.Tests.Visual.SongSelect
} }
}); });
AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{ {
BeatmapInfo = BeatmapInfo =
{ {
@ -107,7 +109,7 @@ namespace osu.Game.Tests.Visual.SongSelect
} }
}); });
AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{ {
BeatmapInfo = BeatmapInfo =
{ {
@ -133,7 +135,7 @@ namespace osu.Game.Tests.Visual.SongSelect
} }
}); });
AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{ {
BeatmapInfo = BeatmapInfo =
{ {

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
@ -18,7 +18,6 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
using osu.Game.Tests.Beatmaps;
using osuTK; using osuTK;
namespace osu.Game.Tests.Visual.SongSelect namespace osu.Game.Tests.Visual.SongSelect
@ -136,7 +135,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () => AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () =>
{ {
infoBefore = infoWedge.Info; infoBefore = infoWedge.Info;
infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : new TestWorkingBeatmap(b); infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b);
}); });
AddUntilStep("wait for async load", () => infoWedge.Info != infoBefore); AddUntilStep("wait for async load", () => infoWedge.Info != infoBefore);

View File

@ -8,6 +8,7 @@ using System.Linq;
using System.Text; using System.Text;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
@ -79,7 +80,7 @@ namespace osu.Game.Tests.Visual.SongSelect
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(GameHost host) private void load(GameHost host, AudioManager audio)
{ {
factory = new DatabaseContextFactory(LocalStorage); factory = new DatabaseContextFactory(LocalStorage);
factory.ResetDatabase(); factory.ResetDatabase();
@ -93,7 +94,7 @@ namespace osu.Game.Tests.Visual.SongSelect
usage.Migrate(); usage.Migrate();
Dependencies.Cache(rulesets = new RulesetStore(factory)); Dependencies.Cache(rulesets = new RulesetStore(factory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null, host, defaultBeatmap = Beatmap.Default)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default));
Beatmap.SetDefault(); Beatmap.SetDefault();
} }

View File

@ -0,0 +1,50 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Tests.Visual.UserInterface
{
/// <summary>
/// An abstract test case which exposes small cells arranged in a grid.
/// Useful for displaying multiple configurations of a tested component at a glance.
/// </summary>
public abstract class OsuGridTestScene : OsuTestScene
{
private readonly Drawable[,] cells;
/// <summary>
/// The amount of rows in the grid.
/// </summary>
protected readonly int Rows;
/// <summary>
/// The amount of columns in the grid.
/// </summary>
protected readonly int Cols;
/// <summary>
/// Constructs a grid test case with the given dimensions.
/// </summary>
protected OsuGridTestScene(int rows, int cols)
{
Rows = rows;
Cols = cols;
GridContainer testContainer;
Add(testContainer = new GridContainer { RelativeSizeAxes = Axes.Both });
cells = new Drawable[rows, cols];
for (int r = 0; r < rows; r++)
for (int c = 0; c < cols; c++)
cells[r, c] = new Container { RelativeSizeAxes = Axes.Both };
testContainer.Content = cells.ToJagged();
}
protected Container Cell(int index) => (Container)cells[index / Cols, index % Cols];
protected Container Cell(int row, int col) => (Container)cells[row, col];
}
}

View File

@ -3,13 +3,12 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneLoadingAnimation : GridTestScene //todo: this should be an OsuTestScene public class TestSceneLoadingAnimation : OsuGridTestScene
{ {
public TestSceneLoadingAnimation() public TestSceneLoadingAnimation()
: base(2, 2) : base(2, 2)

View File

@ -3,14 +3,13 @@
using System; using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osuTK; using osuTK;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneOsuAnimatedButton : GridTestScene public class TestSceneOsuAnimatedButton : OsuGridTestScene
{ {
public TestSceneOsuAnimatedButton() public TestSceneOsuAnimatedButton()
: base(3, 2) : base(3, 2)

View File

@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.UserInterface
TestUpdateableBeatmapBackgroundSprite background = null; TestUpdateableBeatmapBackgroundSprite background = null;
AddStep("load null beatmap", () => Child = background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }); AddStep("load null beatmap", () => Child = background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both });
AddUntilStep("wait for load", () => background.ContentLoaded); AddUntilStep("content loaded", () => background.ContentLoaded);
} }
[Test] [Test]

View File

@ -3,6 +3,7 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -19,12 +20,14 @@ namespace osu.Game.Tests
{ {
private readonly ZipArchiveReader reader; private readonly ZipArchiveReader reader;
private readonly Stream stream; private readonly Stream stream;
private readonly ITrackStore trackStore;
public WaveformTestBeatmap() public WaveformTestBeatmap(AudioManager audioManager)
: base(new BeatmapInfo()) : base(new BeatmapInfo(), audioManager)
{ {
stream = TestResources.GetTestBeatmapStream(); stream = TestResources.GetTestBeatmapStream();
reader = new ZipArchiveReader(stream); reader = new ZipArchiveReader(stream);
trackStore = audioManager.GetTrackStore(reader);
} }
public override void Dispose() public override void Dispose()
@ -32,17 +35,19 @@ namespace osu.Game.Tests
base.Dispose(); base.Dispose();
stream?.Dispose(); stream?.Dispose();
reader?.Dispose(); reader?.Dispose();
trackStore?.Dispose();
} }
protected override IBeatmap GetBeatmap() => createTestBeatmap(); protected override IBeatmap GetBeatmap() => createTestBeatmap();
protected override Texture GetBackground() => null; protected override Texture GetBackground() => null;
protected override Waveform GetWaveform() => new Waveform(getAudioStream()); protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile));
protected override Track GetTrack() => new TrackBass(getAudioStream()); protected override Track GetTrack() => trackStore.Get(firstAudioFile);
private string firstAudioFile => reader.Filenames.First(f => f.EndsWith(".mp3"));
private Stream getAudioStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".mp3")));
private Stream getBeatmapStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu"))); private Stream getBeatmapStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu")));
private Beatmap createTestBeatmap() private Beatmap createTestBeatmap()

View File

@ -20,19 +20,18 @@ namespace osu.Game.Audio
private readonly BindableDouble muteBindable = new BindableDouble(); private readonly BindableDouble muteBindable = new BindableDouble();
private AudioManager audio; private AudioManager audio;
private TrackManager trackManager; private ITrackStore trackStore;
private TrackManagerPreviewTrack current; private TrackManagerPreviewTrack current;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio, FrameworkConfigManager config) private void load(AudioManager audio, FrameworkConfigManager config)
{ {
trackManager = new TrackManager(new OnlineStore()); trackStore = audio.GetTrackStore(new OnlineStore());
this.audio = audio; this.audio = audio;
audio.AddItem(trackManager);
config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); config.BindWith(FrameworkSetting.VolumeMusic, trackStore.Volume);
} }
/// <summary> /// <summary>
@ -42,19 +41,19 @@ namespace osu.Game.Audio
/// <returns>The playable <see cref="PreviewTrack"/>.</returns> /// <returns>The playable <see cref="PreviewTrack"/>.</returns>
public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo) public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo)
{ {
var track = CreatePreviewTrack(beatmapSetInfo, trackManager); var track = CreatePreviewTrack(beatmapSetInfo, trackStore);
track.Started += () => track.Started += () =>
{ {
current?.Stop(); current?.Stop();
current = track; current = track;
audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); audio.Tracks.AddAdjustment(AdjustableProperty.Volume, muteBindable);
}; };
track.Stopped += () => track.Stopped += () =>
{ {
current = null; current = null;
audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); audio.Tracks.RemoveAdjustment(AdjustableProperty.Volume, muteBindable);
}; };
return track; return track;
@ -81,16 +80,16 @@ namespace osu.Game.Audio
/// <summary> /// <summary>
/// Creates the <see cref="TrackManagerPreviewTrack"/>. /// Creates the <see cref="TrackManagerPreviewTrack"/>.
/// </summary> /// </summary>
protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) => new TrackManagerPreviewTrack(beatmapSetInfo, trackManager); protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TrackManagerPreviewTrack(beatmapSetInfo, trackStore);
protected class TrackManagerPreviewTrack : PreviewTrack protected class TrackManagerPreviewTrack : PreviewTrack
{ {
public IPreviewTrackOwner Owner { get; private set; } public IPreviewTrackOwner Owner { get; private set; }
private readonly BeatmapSetInfo beatmapSetInfo; private readonly BeatmapSetInfo beatmapSetInfo;
private readonly TrackManager trackManager; private readonly ITrackStore trackManager;
public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackManager)
{ {
this.beatmapSetInfo = beatmapSetInfo; this.beatmapSetInfo = beatmapSetInfo;
this.trackManager = trackManager; this.trackManager = trackManager;

View File

@ -342,6 +342,7 @@ namespace osu.Game.Beatmaps
OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID, OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID,
Beatmaps = new List<BeatmapInfo>(), Beatmaps = new List<BeatmapInfo>(),
Metadata = beatmap.Metadata, Metadata = beatmap.Metadata,
DateAdded = DateTimeOffset.UtcNow
}; };
} }
@ -429,7 +430,7 @@ namespace osu.Game.Beatmaps
private readonly IBeatmap beatmap; private readonly IBeatmap beatmap;
public DummyConversionBeatmap(IBeatmap beatmap) public DummyConversionBeatmap(IBeatmap beatmap)
: base(beatmap.BeatmapInfo) : base(beatmap.BeatmapInfo, null)
{ {
this.beatmap = beatmap; this.beatmap = beatmap;
} }

View File

@ -20,14 +20,12 @@ namespace osu.Game.Beatmaps
protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap
{ {
private readonly IResourceStore<byte[]> store; private readonly IResourceStore<byte[]> store;
private readonly AudioManager audioManager;
public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> store, TextureStore textureStore, BeatmapInfo beatmapInfo, AudioManager audioManager) public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> store, TextureStore textureStore, BeatmapInfo beatmapInfo, AudioManager audioManager)
: base(beatmapInfo) : base(beatmapInfo, audioManager)
{ {
this.store = store; this.store = store;
this.textureStore = textureStore; this.textureStore = textureStore;
this.audioManager = audioManager;
} }
protected override IBeatmap GetBeatmap() protected override IBeatmap GetBeatmap()
@ -47,6 +45,8 @@ namespace osu.Game.Beatmaps
private TextureStore textureStore; private TextureStore textureStore;
private ITrackStore trackStore;
protected override bool BackgroundStillValid(Texture b) => false; // bypass lazy logic. we want to return a new background each time for refcounting purposes. protected override bool BackgroundStillValid(Texture b) => false; // bypass lazy logic. we want to return a new background each time for refcounting purposes.
protected override Texture GetBackground() protected override Texture GetBackground()
@ -68,8 +68,7 @@ namespace osu.Game.Beatmaps
{ {
try try
{ {
var trackData = store.GetStream(getPathForFile(Metadata.AudioFile)); return (trackStore ?? (trackStore = AudioManager.GetTrackStore(store))).Get(getPathForFile(Metadata.AudioFile));
return trackData == null ? null : new TrackBass(trackData);
} }
catch catch
{ {
@ -77,6 +76,14 @@ namespace osu.Game.Beatmaps
} }
} }
public override void RecycleTrack()
{
base.RecycleTrack();
trackStore?.Dispose();
trackStore = null;
}
public override void TransferTo(WorkingBeatmap other) public override void TransferTo(WorkingBeatmap other)
{ {
base.TransferTo(other); base.TransferTo(other);
@ -135,7 +142,7 @@ namespace osu.Game.Beatmaps
try try
{ {
skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager); skin = new LegacyBeatmapSkin(BeatmapInfo, store, AudioManager);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
@ -20,6 +21,8 @@ namespace osu.Game.Beatmaps
set => onlineBeatmapSetID = value > 0 ? value : null; set => onlineBeatmapSetID = value > 0 ? value : null;
} }
public DateTimeOffset DateAdded { get; set; }
public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None; public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
public BeatmapMetadata Metadata { get; set; } public BeatmapMetadata Metadata { get; set; }

View File

@ -1,11 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics; using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
@ -16,32 +12,15 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap> public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap>
{ {
private AudioManager audioManager;
private WorkingBeatmap lastBeatmap; private WorkingBeatmap lastBeatmap;
protected BindableBeatmap(WorkingBeatmap defaultValue) protected BindableBeatmap(WorkingBeatmap defaultValue)
: base(defaultValue) : base(defaultValue)
{ {
BindValueChanged(b => updateAudioTrack(b.NewValue), true);
} }
/// <summary> private void updateAudioTrack(WorkingBeatmap beatmap)
/// Registers an <see cref="AudioManager"/> for <see cref="Track"/>s to be added to.
/// </summary>
/// <param name="audioManager">The <see cref="AudioManager"/> to register.</param>
protected void RegisterAudioManager([NotNull] AudioManager audioManager)
{
if (this.audioManager != null) throw new InvalidOperationException($"Cannot register multiple {nameof(AudioManager)}s.");
this.audioManager = audioManager;
ValueChanged += b => registerAudioTrack(b.NewValue);
// If the track has changed prior to this being called, let's register it
if (Value != Default)
registerAudioTrack(Value);
}
private void registerAudioTrack(WorkingBeatmap beatmap)
{ {
var trackLoaded = lastBeatmap?.TrackLoaded ?? false; var trackLoaded = lastBeatmap?.TrackLoaded ?? false;
@ -55,18 +34,9 @@ namespace osu.Game.Beatmaps
lastBeatmap.RecycleTrack(); lastBeatmap.RecycleTrack();
} }
audioManager.Track.AddItem(beatmap.Track);
} }
lastBeatmap = beatmap; lastBeatmap = beatmap;
} }
/// <summary>
/// Retrieve a new <see cref="BindableBeatmap"/> instance weakly bound to this <see cref="BindableBeatmap"/>.
/// If you are further binding to events of the retrieved <see cref="BindableBeatmap"/>, ensure a local reference is held.
/// </summary>
[NotNull]
public new abstract BindableBeatmap GetBoundCopy();
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -31,24 +32,8 @@ namespace osu.Game.Beatmaps.Drawables
/// </summary> /// </summary>
protected virtual double UnloadDelay => 10000; protected virtual double UnloadDelay => 10000;
private BeatmapInfo lastModel; protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad)
private bool firstLoad = true; => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, UnloadDelay);
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Drawable content, double timeBeforeLoad)
{
return new DelayedLoadUnloadWrapper(() =>
{
// If DelayedLoadUnloadWrapper is attempting to RELOAD the same content (Beatmap), that means that it was
// previously UNLOADED and thus its children have been disposed of, so we need to recreate them here.
if (!firstLoad && lastModel == Beatmap.Value)
return CreateDrawable(Beatmap.Value);
// If the model has changed since the previous unload (or if there was no load), then we can safely use the given content
lastModel = Beatmap.Value;
firstLoad = false;
return content;
}, timeBeforeLoad, UnloadDelay);
}
protected override Drawable CreateDrawable(BeatmapInfo model) protected override Drawable CreateDrawable(BeatmapInfo model)
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Audio;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
@ -16,9 +17,9 @@ namespace osu.Game.Beatmaps
{ {
public class DummyWorkingBeatmap : WorkingBeatmap public class DummyWorkingBeatmap : WorkingBeatmap
{ {
private readonly OsuGameBase game; private readonly TextureStore textures;
public DummyWorkingBeatmap(OsuGameBase game = null) public DummyWorkingBeatmap(AudioManager audio, TextureStore textures)
: base(new BeatmapInfo : base(new BeatmapInfo
{ {
Metadata = new BeatmapMetadata Metadata = new BeatmapMetadata
@ -34,16 +35,16 @@ namespace osu.Game.Beatmaps
OverallDifficulty = 0, OverallDifficulty = 0,
}, },
Ruleset = new DummyRulesetInfo() Ruleset = new DummyRulesetInfo()
}) }, audio)
{ {
this.game = game; this.textures = textures;
} }
protected override IBeatmap GetBeatmap() => new Beatmap(); protected override IBeatmap GetBeatmap() => new Beatmap();
protected override Texture GetBackground() => game?.Textures.Get(@"Backgrounds/bg4"); protected override Texture GetBackground() => textures?.Get(@"Backgrounds/bg4");
protected override Track GetTrack() => new TrackVirtual { Length = 1000 }; protected override Track GetTrack() => GetVirtualTrack();
private class DummyRulesetInfo : RulesetInfo private class DummyRulesetInfo : RulesetInfo
{ {

View File

@ -11,15 +11,17 @@ using osu.Framework.IO.File;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using osu.Framework.Audio;
using osu.Game.IO.Serialization; using osu.Game.IO.Serialization;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
public abstract partial class WorkingBeatmap : IDisposable public abstract class WorkingBeatmap : IDisposable
{ {
public readonly BeatmapInfo BeatmapInfo; public readonly BeatmapInfo BeatmapInfo;
@ -27,8 +29,11 @@ namespace osu.Game.Beatmaps
public readonly BeatmapMetadata Metadata; public readonly BeatmapMetadata Metadata;
protected WorkingBeatmap(BeatmapInfo beatmapInfo) protected AudioManager AudioManager { get; }
protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager)
{ {
AudioManager = audioManager;
BeatmapInfo = beatmapInfo; BeatmapInfo = beatmapInfo;
BeatmapSetInfo = beatmapInfo.BeatmapSet; BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
@ -46,13 +51,39 @@ namespace osu.Game.Beatmaps
return b; return b;
}); });
track = new RecyclableLazy<Track>(() => GetTrack() ?? new VirtualBeatmapTrack(Beatmap)); track = new RecyclableLazy<Track>(() => GetTrack() ?? GetVirtualTrack());
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid); background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
waveform = new RecyclableLazy<Waveform>(GetWaveform); waveform = new RecyclableLazy<Waveform>(GetWaveform);
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard); storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
skin = new RecyclableLazy<Skin>(GetSkin); skin = new RecyclableLazy<Skin>(GetSkin);
} }
protected virtual Track GetVirtualTrack()
{
const double excess_length = 1000;
var lastObject = Beatmap.HitObjects.LastOrDefault();
double length;
switch (lastObject)
{
case null:
length = excess_length;
break;
case IHasEndTime endTime:
length = endTime.EndTime + excess_length;
break;
default:
length = lastObject.StartTime + excess_length;
break;
}
return AudioManager.Tracks.GetVirtual(length);
}
/// <summary> /// <summary>
/// Saves the <see cref="Beatmaps.Beatmap"/>. /// Saves the <see cref="Beatmaps.Beatmap"/>.
/// </summary> /// </summary>
@ -150,6 +181,7 @@ namespace osu.Game.Beatmaps
public bool SkinLoaded => skin.IsResultAvailable; public bool SkinLoaded => skin.IsResultAvailable;
public Skin Skin => skin.Value; public Skin Skin => skin.Value;
protected virtual Skin GetSkin() => new DefaultSkin(); protected virtual Skin GetSkin() => new DefaultSkin();
private readonly RecyclableLazy<Skin> skin; private readonly RecyclableLazy<Skin> skin;
@ -175,7 +207,7 @@ namespace osu.Game.Beatmaps
/// Eagerly dispose of the audio track associated with this <see cref="WorkingBeatmap"/> (if any). /// Eagerly dispose of the audio track associated with this <see cref="WorkingBeatmap"/> (if any).
/// Accessing track again will load a fresh instance. /// Accessing track again will load a fresh instance.
/// </summary> /// </summary>
public void RecycleTrack() => track.Recycle(); public virtual void RecycleTrack() => track.Recycle();
public class RecyclableLazy<T> public class RecyclableLazy<T>
{ {

View File

@ -1,41 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Audio.Track;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Beatmaps
{
public partial class WorkingBeatmap
{
/// <summary>
/// A type of <see cref="TrackVirtual"/> which provides a valid length based on the <see cref="HitObject"/>s of an <see cref="IBeatmap"/>.
/// </summary>
protected class VirtualBeatmapTrack : TrackVirtual
{
private const double excess_length = 1000;
public VirtualBeatmapTrack(IBeatmap beatmap)
{
var lastObject = beatmap.HitObjects.LastOrDefault();
switch (lastObject)
{
case null:
Length = excess_length;
break;
case IHasEndTime endTime:
Length = endTime.EndTime + excess_length;
break;
default:
Length = lastObject.StartTime + excess_length;
break;
}
}
}
}
}

View File

@ -15,6 +15,8 @@ namespace osu.Game.Configuration
public int? Variant { get; set; } public int? Variant { get; set; }
public int? SkinInfoID { get; set; }
[Column("Key")] [Column("Key")]
public string Key { get; set; } public string Key { get; set; }

View File

@ -214,7 +214,6 @@ namespace osu.Game.Graphics.Backgrounds
base.Draw(vertexAction); base.Draw(vertexAction);
shader.Bind(); shader.Bind();
texture.TextureGL.Bind();
Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy; Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy;
@ -231,7 +230,8 @@ namespace osu.Game.Graphics.Backgrounds
ColourInfo colourInfo = DrawColourInfo.Colour; ColourInfo colourInfo = DrawColourInfo.Colour;
colourInfo.ApplyChild(particle.Colour); colourInfo.ApplyChild(particle.Colour);
texture.DrawTriangle( DrawTriangle(
texture,
triangle, triangle,
colourInfo, colourInfo,
null, null,

View File

@ -51,8 +51,8 @@ namespace osu.Game.Graphics.Containers
if (osuGame != null) if (osuGame != null)
OverlayActivationMode.BindTo(osuGame.OverlayActivationMode); OverlayActivationMode.BindTo(osuGame.OverlayActivationMode);
samplePopIn = audio.Sample.Get(@"UI/overlay-pop-in"); samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in");
samplePopOut = audio.Sample.Get(@"UI/overlay-pop-out"); samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out");
StateChanged += onStateChanged; StateChanged += onStateChanged;
} }

View File

@ -51,7 +51,7 @@ namespace osu.Game.Graphics
screenshotFormat = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat); screenshotFormat = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat);
captureMenuCursor = config.GetBindable<bool>(OsuSetting.ScreenshotCaptureMenuCursor); captureMenuCursor = config.GetBindable<bool>(OsuSetting.ScreenshotCaptureMenuCursor);
shutter = audio.Sample.Get("UI/shutter"); shutter = audio.Samples.Get("UI/shutter");
} }
public bool OnPressed(GlobalAction action) public bool OnPressed(GlobalAction action)

View File

@ -31,7 +31,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio) private void load(AudioManager audio)
{ {
sampleClick = audio.Sample.Get($@"UI/generic-select{SampleSet.GetDescription()}"); sampleClick = audio.Samples.Get($@"UI/generic-select{SampleSet.GetDescription()}");
} }
} }
} }

View File

@ -37,7 +37,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio) private void load(AudioManager audio)
{ {
sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); sampleHover = audio.Samples.Get($@"UI/generic-hover{SampleSet.GetDescription()}");
} }
} }

View File

@ -66,6 +66,7 @@ namespace osu.Game.Graphics.UserInterface
set set
{ {
Content.RelativeSizeAxes = Axes.None; Content.RelativeSizeAxes = Axes.None;
Content.AutoSizeAxes = Axes.None;
Content.Size = value; Content.Size = value;
} }
} }

View File

@ -17,11 +17,11 @@ namespace osu.Game.Graphics.UserInterface
/// <summary> /// <summary>
/// A button with added default sound effects. /// A button with added default sound effects.
/// </summary> /// </summary>
public class OsuButton : Button public abstract class OsuButton : Button
{ {
private Box hover; private Box hover;
public OsuButton() protected OsuButton()
{ {
Height = 40; Height = 40;

View File

@ -112,8 +112,8 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio) private void load(AudioManager audio)
{ {
sampleChecked = audio.Sample.Get(@"UI/check-on"); sampleChecked = audio.Samples.Get(@"UI/check-on");
sampleUnchecked = audio.Sample.Get(@"UI/check-off"); sampleUnchecked = audio.Samples.Get(@"UI/check-off");
} }
} }
} }

View File

@ -71,8 +71,8 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio) private void load(AudioManager audio)
{ {
sampleHover = audio.Sample.Get(@"UI/generic-hover"); sampleHover = audio.Samples.Get(@"UI/generic-hover");
sampleClick = audio.Sample.Get(@"UI/generic-select"); sampleClick = audio.Samples.Get(@"UI/generic-select");
BackgroundColour = Color4.Transparent; BackgroundColour = Color4.Transparent;
BackgroundColourHover = OsuColour.FromHex(@"172023"); BackgroundColourHover = OsuColour.FromHex(@"172023");

View File

@ -86,7 +86,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio, OsuColour colours) private void load(AudioManager audio, OsuColour colours)
{ {
sample = audio.Sample.Get(@"UI/sliderbar-notch"); sample = audio.Samples.Get(@"UI/sliderbar-notch");
AccentColour = colours.Pink; AccentColour = colours.Pink;
} }

View File

@ -15,6 +15,8 @@ namespace osu.Game.IO.Archives
/// </summary> /// </summary>
public abstract Stream GetStream(string name); public abstract Stream GetStream(string name);
public IEnumerable<string> GetAvailableResources() => Filenames;
public abstract void Dispose(); public abstract void Dispose();
/// <summary> /// <summary>

View File

@ -0,0 +1,498 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20190525060824_SkinSettings")]
partial class SkinSettings
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<double>("SliderMultiplier");
b.Property<double>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Key")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<int?>("SkinInfoID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("SkinInfoID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int?>("ScoreInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("ScoreInfoID");
b.ToTable("ScoreFileInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("Accuracy")
.HasColumnType("DECIMAL(1,4)");
b.Property<int>("BeatmapInfoID");
b.Property<int>("Combo");
b.Property<DateTimeOffset>("Date");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int>("MaxCombo");
b.Property<string>("ModsJson")
.HasColumnName("Mods");
b.Property<long?>("OnlineScoreID");
b.Property<double?>("PP");
b.Property<int>("Rank");
b.Property<int>("RulesetID");
b.Property<string>("StatisticsJson")
.HasColumnName("Statistics");
b.Property<long>("TotalScore");
b.Property<long?>("UserID")
.HasColumnName("UserID");
b.Property<string>("UserString")
.HasColumnName("User");
b.HasKey("ID");
b.HasIndex("BeatmapInfoID");
b.HasIndex("OnlineScoreID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("ScoreInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Settings")
.HasForeignKey("SkinInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Scoring.ScoreInfo")
.WithMany("Files")
.HasForeignKey("ScoreInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany("Scores")
.HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,54 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class SkinSettings : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"create table Settings_dg_tmp
(
ID INTEGER not null
constraint PK_Settings
primary key autoincrement,
Key TEXT not null,
RulesetID INTEGER,
Value TEXT,
Variant INTEGER,
SkinInfoID int
constraint Settings_SkinInfo_ID_fk
references SkinInfo
on delete restrict
);
insert into Settings_dg_tmp(ID, Key, RulesetID, Value, Variant) select ID, Key, RulesetID, Value, Variant from Settings;
drop table Settings;
alter table Settings_dg_tmp rename to Settings;
create index IX_Settings_RulesetID_Variant
on Settings (RulesetID, Variant);
create index Settings_SkinInfoID_index
on Settings (SkinInfoID);
");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Settings_SkinInfo_SkinInfoID",
table: "Settings");
migrationBuilder.DropIndex(
name: "IX_Settings_SkinInfoID",
table: "Settings");
migrationBuilder.DropColumn(
name: "SkinInfoID",
table: "Settings");
}
}
}

View File

@ -0,0 +1,489 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20190605091246_AddDateAddedColumnToBeatmapSet")]
partial class AddDateAddedColumnToBeatmapSet
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<double>("SliderMultiplier");
b.Property<double>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset>("DateAdded");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Key")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int?>("ScoreInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("ScoreInfoID");
b.ToTable("ScoreFileInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("Accuracy")
.HasColumnType("DECIMAL(1,4)");
b.Property<int>("BeatmapInfoID");
b.Property<int>("Combo");
b.Property<DateTimeOffset>("Date");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int>("MaxCombo");
b.Property<string>("ModsJson")
.HasColumnName("Mods");
b.Property<long?>("OnlineScoreID");
b.Property<double?>("PP");
b.Property<int>("Rank");
b.Property<int>("RulesetID");
b.Property<string>("StatisticsJson")
.HasColumnName("Statistics");
b.Property<long>("TotalScore");
b.Property<long?>("UserID")
.HasColumnName("UserID");
b.Property<string>("UserString")
.HasColumnName("User");
b.HasKey("ID");
b.HasIndex("BeatmapInfoID");
b.HasIndex("OnlineScoreID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("ScoreInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Scoring.ScoreInfo")
.WithMany("Files")
.HasForeignKey("ScoreInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany("Scores")
.HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class AddDateAddedColumnToBeatmapSet : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTimeOffset>(
name: "DateAdded",
table: "BeatmapSetInfo",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DateAdded",
table: "BeatmapSetInfo");
}
}
}

View File

@ -166,6 +166,8 @@ namespace osu.Game.Migrations
b.Property<int>("ID") b.Property<int>("ID")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<DateTimeOffset>("DateAdded");
b.Property<bool>("DeletePending"); b.Property<bool>("DeletePending");
b.Property<string>("Hash"); b.Property<string>("Hash");
@ -203,6 +205,8 @@ namespace osu.Game.Migrations
b.Property<int?>("RulesetID"); b.Property<int?>("RulesetID");
b.Property<int?>("SkinInfoID");
b.Property<string>("StringValue") b.Property<string>("StringValue")
.HasColumnName("Value"); .HasColumnName("Value");
@ -210,6 +214,8 @@ namespace osu.Game.Migrations
b.HasKey("ID"); b.HasKey("ID");
b.HasIndex("SkinInfoID");
b.HasIndex("RulesetID", "Variant"); b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings"); b.ToTable("Settings");
@ -442,6 +448,13 @@ namespace osu.Game.Migrations
.HasForeignKey("MetadataID"); .HasForeignKey("MetadataID");
}); });
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Settings")
.HasForeignKey("SkinInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{ {
b.HasOne("osu.Game.IO.FileInfo", "FileInfo") b.HasOne("osu.Game.IO.FileInfo", "FileInfo")

View File

@ -163,7 +163,7 @@ namespace osu.Game
dependencies.CacheAs<IAPIProvider>(API); dependencies.CacheAs<IAPIProvider>(API);
var defaultBeatmap = new DummyWorkingBeatmap(this); var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures);
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
@ -195,9 +195,9 @@ namespace osu.Game
// tracks play so loud our samples can't keep up. // tracks play so loud our samples can't keep up.
// this adds a global reduction of track volume for the time being. // this adds a global reduction of track volume for the time being.
Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8)); Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio); beatmap = new OsuBindableBeatmap(defaultBeatmap);
dependencies.CacheAs<IBindable<WorkingBeatmap>>(beatmap); dependencies.CacheAs<IBindable<WorkingBeatmap>>(beatmap);
dependencies.CacheAs(beatmap); dependencies.CacheAs(beatmap);
@ -283,23 +283,10 @@ namespace osu.Game
private class OsuBindableBeatmap : BindableBeatmap private class OsuBindableBeatmap : BindableBeatmap
{ {
public OsuBindableBeatmap(WorkingBeatmap defaultValue, AudioManager audioManager)
: this(defaultValue)
{
RegisterAudioManager(audioManager);
}
public OsuBindableBeatmap(WorkingBeatmap defaultValue) public OsuBindableBeatmap(WorkingBeatmap defaultValue)
: base(defaultValue) : base(defaultValue)
{ {
} }
public override BindableBeatmap GetBoundCopy()
{
var copy = new OsuBindableBeatmap(Default);
copy.BindTo(this);
return copy;
}
} }
private class OsuUserInputManager : UserInputManager private class OsuUserInputManager : UserInputManager

View File

@ -90,8 +90,8 @@ namespace osu.Game.Overlays.Changelog
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio) private void load(AudioManager audio)
{ {
sampleClick = audio.Sample.Get(@"UI/generic-select-soft"); sampleClick = audio.Samples.Get(@"UI/generic-select-soft");
sampleHover = audio.Sample.Get(@"UI/generic-hover-soft"); sampleHover = audio.Samples.Get(@"UI/generic-hover-soft");
} }
protected override void OnActivated() => updateState(); protected override void OnActivated() => updateState();

View File

@ -76,7 +76,7 @@ namespace osu.Game.Overlays
}, },
}; };
sampleBack = audio.Sample.Get(@"UI/generic-select-soft"); sampleBack = audio.Samples.Get(@"UI/generic-select-soft");
header.Current.BindTo(Current); header.Current.BindTo(Current);

View File

@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
@ -17,6 +19,11 @@ namespace osu.Game.Overlays
{ {
private Box overlay; private Box overlay;
private readonly BindableDouble audioVolume = new BindableDouble(1);
[Resolved]
private AudioManager audio { get; set; }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
@ -33,7 +40,19 @@ namespace osu.Game.Overlays
} }
}; };
Progress.ValueChanged += p => overlay.Alpha = (float)p.NewValue; Progress.ValueChanged += p =>
{
audioVolume.Value = 1 - p.NewValue;
overlay.Alpha = (float)p.NewValue;
};
audio.Tracks.AddAdjustment(AdjustableProperty.Volume, audioVolume);
}
protected override void Dispose(bool isDisposing)
{
audio.Tracks.RemoveAdjustment(AdjustableProperty.Volume, audioVolume);
base.Dispose(isDisposing);
} }
} }
} }

View File

@ -145,7 +145,7 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours, TextureStore textures, AudioManager audio) private void load(OsuColour colours, TextureStore textures, AudioManager audio)
{ {
getSample = audio.Sample.Get(@"MedalSplash/medal-get"); getSample = audio.Samples.Get(@"MedalSplash/medal-get");
innerSpin.Texture = outerSpin.Texture = textures.Get(@"MedalSplash/disc-spin"); innerSpin.Texture = outerSpin.Texture = textures.Get(@"MedalSplash/disc-spin");
disc.EdgeEffect = leftStrip.EdgeEffect = rightStrip.EdgeEffect = new EdgeEffectParameters disc.EdgeEffect = leftStrip.EdgeEffect = rightStrip.EdgeEffect = new EdgeEffectParameters

View File

@ -56,8 +56,8 @@ namespace osu.Game.Overlays.Mods
Ruleset.BindTo(ruleset); Ruleset.BindTo(ruleset);
if (mods != null) SelectedMods.BindTo(mods); if (mods != null) SelectedMods.BindTo(mods);
sampleOn = audio.Sample.Get(@"UI/check-on"); sampleOn = audio.Samples.Get(@"UI/check-on");
sampleOff = audio.Sample.Get(@"UI/check-off"); sampleOff = audio.Samples.Get(@"UI/check-off");
} }
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -350,7 +350,7 @@ namespace osu.Game.Overlays
direction = last > next ? TransformDirection.Prev : TransformDirection.Next; direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
} }
current.Track.Completed -= currentTrackCompleted; //current.Track.Completed -= currentTrackCompleted;
} }
current = beatmap.NewValue; current = beatmap.NewValue;

View File

@ -31,6 +31,11 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
LabelText = "Bypass caching (slow)", LabelText = "Bypass caching (slow)",
Bindable = config.GetBindable<bool>(DebugSetting.BypassCaching) Bindable = config.GetBindable<bool>(DebugSetting.BypassCaching)
}, },
new SettingsCheckbox
{
LabelText = "Bypass front-to-back render pass",
Bindable = config.GetBindable<bool>(DebugSetting.BypassFrontToBackPass)
}
}; };
} }
} }

View File

@ -179,7 +179,7 @@ namespace osu.Game.Rulesets.Mods
shader.GetUniform<Vector2>("flashlightSize").UpdateValue(ref flashlightSize); shader.GetUniform<Vector2>("flashlightSize").UpdateValue(ref flashlightSize);
shader.GetUniform<float>("flashlightDim").UpdateValue(ref flashlightDim); shader.GetUniform<float>("flashlightDim").UpdateValue(ref flashlightDim);
Texture.WhitePixel.DrawQuad(screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction); DrawQuad(Texture.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction);
shader.Unbind(); shader.Unbind();
} }

View File

@ -182,9 +182,9 @@ namespace osu.Game.Screens.Menu
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio) private void load(AudioManager audio)
{ {
sampleHover = audio.Sample.Get(@"Menu/button-hover"); sampleHover = audio.Samples.Get(@"Menu/button-hover");
if (!string.IsNullOrEmpty(sampleName)) if (!string.IsNullOrEmpty(sampleName))
sampleClick = audio.Sample.Get($@"Menu/{sampleName}"); sampleClick = audio.Samples.Get($@"Menu/{sampleName}");
} }
protected override bool OnMouseDown(MouseDownEvent e) protected override bool OnMouseDown(MouseDownEvent e)

View File

@ -150,7 +150,7 @@ namespace osu.Game.Screens.Menu
if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle);
sampleBack = audio.Sample.Get(@"Menu/button-back-select"); sampleBack = audio.Samples.Get(@"Menu/button-back-select");
} }
private void onMulti() private void onMulti()

View File

@ -76,8 +76,8 @@ namespace osu.Game.Screens.Menu
introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
track = introBeatmap.Track; track = introBeatmap.Track;
welcome = audio.Sample.Get(@"welcome"); welcome = audio.Samples.Get(@"welcome");
seeya = audio.Sample.Get(@"seeya"); seeya = audio.Samples.Get(@"seeya");
} }
private const double delay_step_one = 2300; private const double delay_step_one = 2300;

View File

@ -189,7 +189,6 @@ namespace osu.Game.Screens.Menu
base.Draw(vertexAction); base.Draw(vertexAction);
shader.Bind(); shader.Bind();
texture.TextureGL.Bind();
Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy;
@ -224,7 +223,8 @@ namespace osu.Game.Screens.Menu
Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix) Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix)
); );
texture.DrawQuad( DrawQuad(
texture,
rectangle, rectangle,
colourInfo, colourInfo,
null, null,

View File

@ -255,8 +255,8 @@ namespace osu.Game.Screens.Menu
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(TextureStore textures, AudioManager audio) private void load(TextureStore textures, AudioManager audio)
{ {
sampleClick = audio.Sample.Get(@"Menu/osu-logo-select"); sampleClick = audio.Samples.Get(@"Menu/osu-logo-select");
sampleBeat = audio.Sample.Get(@"Menu/osu-logo-heartbeat"); sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat");
logo.Texture = textures.Get(@"Menu/logo"); logo.Texture = textures.Get(@"Menu/logo");
ripple.Texture = textures.Get(@"Menu/logo"); ripple.Texture = textures.Get(@"Menu/logo");

View File

@ -137,7 +137,7 @@ namespace osu.Game.Screens.Multi.Match.Components
private class BackgroundSprite : UpdateableBeatmapBackgroundSprite private class BackgroundSprite : UpdateableBeatmapBackgroundSprite
{ {
protected override double FadeDuration => 200; protected override double TransformDuration => 200;
} }
} }
} }

View File

@ -255,7 +255,7 @@ namespace osu.Game.Screens.Multi
if (!track.IsRunning) if (!track.IsRunning)
{ {
game.Audio.AddItemToList(track); game.Audio.AddItem(track);
track.Seek(Beatmap.Value.Metadata.PreviewTime); track.Seek(Beatmap.Value.Metadata.PreviewTime);
track.Start(); track.Start();
} }

View File

@ -99,7 +99,7 @@ namespace osu.Game.Screens
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(OsuGame osu, AudioManager audio) private void load(OsuGame osu, AudioManager audio)
{ {
sampleExit = audio.Sample.Get(@"UI/screen-back"); sampleExit = audio.Samples.Get(@"UI/screen-back");
} }
public virtual bool OnPressed(GlobalAction action) public virtual bool OnPressed(GlobalAction action)

View File

@ -103,7 +103,7 @@ namespace osu.Game.Screens.Play
if (working == null) if (working == null)
return; return;
sampleRestart = audio.Sample.Get(@"Gameplay/restart"); sampleRestart = audio.Samples.Get(@"Gameplay/restart");
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel); mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
showStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard); showStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);

View File

@ -234,7 +234,7 @@ namespace osu.Game.Screens.Play
colourNormal = colours.Yellow; colourNormal = colours.Yellow;
colourHover = colours.YellowDark; colourHover = colours.YellowDark;
sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection"); sampleConfirm = audio.Samples.Get(@"SongSelect/confirm-selection");
Children = new Drawable[] Children = new Drawable[]
{ {

View File

@ -45,6 +45,9 @@ namespace osu.Game.Screens.Select.Carousel
case SortMode.Author: case SortMode.Author:
return string.Compare(BeatmapSet.Metadata.Author.Username, otherSet.BeatmapSet.Metadata.Author.Username, StringComparison.InvariantCultureIgnoreCase); return string.Compare(BeatmapSet.Metadata.Author.Username, otherSet.BeatmapSet.Metadata.Author.Username, StringComparison.InvariantCultureIgnoreCase);
case SortMode.DateAdded:
return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded);
case SortMode.Difficulty: case SortMode.Difficulty:
return BeatmapSet.MaxStarDifficulty.CompareTo(otherSet.BeatmapSet.MaxStarDifficulty); return BeatmapSet.MaxStarDifficulty.CompareTo(otherSet.BeatmapSet.MaxStarDifficulty);
} }

View File

@ -69,7 +69,7 @@ namespace osu.Game.Screens.Select.Carousel
} }
}; };
sampleHover = audio.Sample.Get($@"SongSelect/song-ping-variation-{RNG.Next(1, 5)}"); sampleHover = audio.Samples.Get($@"SongSelect/song-ping-variation-{RNG.Next(1, 5)}");
hoverLayer.Colour = colours.Blue.Opacity(0.1f); hoverLayer.Colour = colours.Blue.Opacity(0.1f);
} }

View File

@ -242,9 +242,9 @@ namespace osu.Game.Screens.Select
dialogOverlay = dialog; dialogOverlay = dialog;
sampleChangeDifficulty = audio.Sample.Get(@"SongSelect/select-difficulty"); sampleChangeDifficulty = audio.Samples.Get(@"SongSelect/select-difficulty");
sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand"); sampleChangeBeatmap = audio.Samples.Get(@"SongSelect/select-expand");
SampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection"); SampleConfirm = audio.Samples.Get(@"SongSelect/confirm-selection");
Carousel.LoadBeatmapSetsFromManager(this.beatmaps); Carousel.LoadBeatmapSetsFromManager(this.beatmaps);
@ -580,9 +580,6 @@ namespace osu.Game.Screens.Select
if (!track.IsRunning || restart) if (!track.IsRunning || restart)
{ {
// Ensure the track is added to the TrackManager, since it is removed after the player finishes the map.
// Using AddItemToList rather than AddItem so that it doesn't attempt to register adjustment dependencies more than once.
Game.Audio.Track.AddItemToList(track);
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
track.Restart(); track.Restart();
} }

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -21,7 +22,7 @@ namespace osu.Game.Skinning
{ {
protected TextureStore Textures; protected TextureStore Textures;
protected SampleManager Samples; protected IResourceStore<SampleChannel> Samples;
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager) public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
: this(skin, new LegacySkinResourceStore<SkinFileInfo>(skin, storage), audioManager, "skin.ini") : this(skin, new LegacySkinResourceStore<SkinFileInfo>(skin, storage), audioManager, "skin.ini")
@ -38,10 +39,17 @@ namespace osu.Game.Skinning
else else
Configuration = new SkinConfiguration(); Configuration = new SkinConfiguration();
Samples = audioManager.GetSampleManager(storage); Samples = audioManager.GetSampleStore(storage);
Textures = new TextureStore(new TextureLoaderStore(storage)); Textures = new TextureStore(new TextureLoaderStore(storage));
} }
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Textures?.Dispose();
Samples?.Dispose();
}
public override Drawable GetDrawableComponent(string componentName) public override Drawable GetDrawableComponent(string componentName)
{ {
switch (componentName) switch (componentName)
@ -133,6 +141,8 @@ namespace osu.Game.Skinning
return path == null ? null : underlyingStore.GetStream(path); return path == null ? null : underlyingStore.GetStream(path);
} }
public IEnumerable<string> GetAvailableResources() => source.Files.Select(f => f.Filename);
byte[] IResourceStore<byte[]>.Get(string name) => GetAsync(name).Result; byte[] IResourceStore<byte[]>.Get(string name) => GetAsync(name).Result;
public Task<byte[]> GetAsync(string name) public Task<byte[]> GetAsync(string name)

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Configuration;
using osu.Game.Database; using osu.Game.Database;
namespace osu.Game.Skinning namespace osu.Game.Skinning
@ -19,6 +20,8 @@ namespace osu.Game.Skinning
public List<SkinFileInfo> Files { get; set; } public List<SkinFileInfo> Files { get; set; }
public List<DatabasedSetting> Settings { get; set; }
public bool DeletePending { get; set; } public bool DeletePending { get; set; }
public string FullName => $"\"{Name}\" by {Creator}"; public string FullName => $"\"{Name}\" by {Creator}";

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Linq;
using Microsoft.EntityFrameworkCore;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Database; using osu.Game.Database;
@ -12,5 +14,9 @@ namespace osu.Game.Skinning
: base(contextFactory, storage) : base(contextFactory, storage)
{ {
} }
protected override IQueryable<SkinInfo> AddIncludesForDeletion(IQueryable<SkinInfo> query) =>
base.AddIncludesForDeletion(query)
.Include(s => s.Settings); // don't include FileInfo. these are handled by the FileStore itself.
} }
} }

View File

@ -39,7 +39,7 @@ namespace osu.Game.Skinning
{ {
var ch = loadChannel(s, skin.GetSample); var ch = loadChannel(s, skin.GetSample);
if (ch == null && allowFallback) if (ch == null && allowFallback)
ch = loadChannel(s, audio.Sample.Get); ch = loadChannel(s, audio.Samples.Get);
return ch; return ch;
}).Where(c => c != null).ToArray(); }).Where(c => c != null).ToArray();
} }
@ -58,5 +58,13 @@ namespace osu.Game.Skinning
return null; return null;
} }
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
foreach (var c in channels)
c.Dispose();
}
} }
} }

View File

@ -1,134 +1,30 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osuTK;
namespace osu.Game.Tests.Beatmaps namespace osu.Game.Tests.Beatmaps
{ {
public class TestWorkingBeatmap : WorkingBeatmap public class TestWorkingBeatmap : WorkingBeatmap
{ {
private readonly TrackVirtualManual track;
private readonly IBeatmap beatmap; private readonly IBeatmap beatmap;
/// <summary>
/// Create an instance which creates a <see cref="TestBeatmap"/> for the provided ruleset when requested.
/// </summary>
/// <param name="ruleset">The target ruleset.</param>
/// <param name="referenceClock">A clock which should be used instead of a stopwatch for virtual time progression.</param>
public TestWorkingBeatmap(RulesetInfo ruleset, IFrameBasedClock referenceClock)
: this(new TestBeatmap(ruleset), referenceClock)
{
}
/// <summary> /// <summary>
/// Create an instance which provides the <see cref="IBeatmap"/> when requested. /// Create an instance which provides the <see cref="IBeatmap"/> when requested.
/// </summary> /// </summary>
/// <param name="beatmap">The beatmap</param> /// <param name="beatmap">The beatmap</param>
/// <param name="referenceClock">An optional clock which should be used instead of a stopwatch for virtual time progression.</param> public TestWorkingBeatmap(IBeatmap beatmap)
public TestWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock referenceClock = null) : base(beatmap.BeatmapInfo, null)
: base(beatmap.BeatmapInfo)
{ {
this.beatmap = beatmap; this.beatmap = beatmap;
if (referenceClock != null)
track = new TrackVirtualManual(referenceClock);
} }
protected override IBeatmap GetBeatmap() => beatmap; protected override IBeatmap GetBeatmap() => beatmap;
protected override Texture GetBackground() => null; protected override Texture GetBackground() => null;
protected override Track GetTrack() => track;
/// <summary> protected override Track GetTrack() => null;
/// A virtual track which tracks a reference clock.
/// </summary>
public class TrackVirtualManual : Track
{
private readonly IFrameBasedClock referenceClock;
private readonly ManualClock clock = new ManualClock();
private bool running;
/// <summary>
/// Local offset added to the reference clock to resolve correct time.
/// </summary>
private double offset;
public TrackVirtualManual(IFrameBasedClock referenceClock)
{
this.referenceClock = referenceClock;
Length = double.PositiveInfinity;
}
public override bool Seek(double seek)
{
offset = MathHelper.Clamp(seek, 0, Length);
lastReferenceTime = null;
return offset == seek;
}
public override void Start()
{
running = true;
}
public override void Reset()
{
Seek(0);
base.Reset();
}
public override void Stop()
{
if (running)
{
running = false;
// on stopping, the current value should be transferred out of the clock, as we can no longer rely on
// the referenceClock (which will still be counting time).
offset = clock.CurrentTime;
lastReferenceTime = null;
}
}
public override bool IsRunning => running;
private double? lastReferenceTime;
public override double CurrentTime => clock.CurrentTime;
protected override void UpdateState()
{
base.UpdateState();
if (running)
{
double refTime = referenceClock.CurrentTime;
if (!lastReferenceTime.HasValue)
{
// if the clock just started running, the current value should be transferred to the offset
// (to zero the progression of time).
offset -= refTime;
}
lastReferenceTime = refTime;
}
clock.CurrentTime = Math.Min((lastReferenceTime ?? 0) + offset, Length);
if (CurrentTime >= Length)
{
Stop();
RaiseCompleted();
}
}
}
} }
} }

View File

@ -4,13 +4,10 @@
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
@ -50,26 +47,20 @@ namespace osu.Game.Tests.Visual
protected abstract void AddCheckSteps(); protected abstract void AddCheckSteps();
protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo); private Player loadPlayerFor(RulesetInfo rulesetInfo)
protected virtual WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock clock) =>
new TestWorkingBeatmap(beatmap, Clock);
private Player loadPlayerFor(RulesetInfo ri)
{ {
Ruleset.Value = ri; Ruleset.Value = rulesetInfo;
var r = ri.CreateInstance(); var ruleset = rulesetInfo.CreateInstance();
var beatmap = CreateBeatmap(r); var working = CreateWorkingBeatmap(rulesetInfo);
var working = CreateWorkingBeatmap(beatmap, Clock);
Beatmap.Value = working; Beatmap.Value = working;
Mods.Value = new[] { r.GetAllMods().First(m => m is ModNoFail) }; Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) };
Player?.Exit(); Player?.Exit();
Player = null; Player = null;
Player = CreatePlayer(r); Player = CreatePlayer(ruleset);
LoadScreen(Player); LoadScreen(Player);

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
@ -6,7 +6,6 @@ using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
@ -24,7 +23,7 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Beatmap.Value = new TestWorkingBeatmap(ruleset.RulesetInfo, null); Beatmap.Value = CreateWorkingBeatmap(ruleset.RulesetInfo);
LoadScreen(new Editor()); LoadScreen(new Editor());
} }

View File

@ -3,15 +3,22 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Textures;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
@ -19,7 +26,7 @@ namespace osu.Game.Tests.Visual
{ {
[Cached(typeof(Bindable<WorkingBeatmap>))] [Cached(typeof(Bindable<WorkingBeatmap>))]
[Cached(typeof(IBindable<WorkingBeatmap>))] [Cached(typeof(IBindable<WorkingBeatmap>))]
private readonly OsuTestBeatmap beatmap = new OsuTestBeatmap(new DummyWorkingBeatmap()); private OsuTestBeatmap beatmap;
protected BindableBeatmap Beatmap => beatmap; protected BindableBeatmap Beatmap => beatmap;
@ -39,7 +46,12 @@ namespace osu.Game.Tests.Visual
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{ {
// This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures
beatmap.Default = new DummyWorkingBeatmap(parent.Get<OsuGameBase>()); var working = new DummyWorkingBeatmap(parent.Get<AudioManager>(), parent.Get<TextureStore>());
beatmap = new OsuTestBeatmap(working)
{
Default = working
};
return Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); return Dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
} }
@ -49,11 +61,20 @@ namespace osu.Game.Tests.Visual
localStorage = new Lazy<Storage>(() => new NativeStorage($"{GetType().Name}-{Guid.NewGuid()}")); localStorage = new Lazy<Storage>(() => new NativeStorage($"{GetType().Name}-{Guid.NewGuid()}"));
} }
[BackgroundDependencyLoader] [Resolved]
private void load(AudioManager audioManager, RulesetStore rulesets) private AudioManager audio { get; set; }
{
beatmap.SetAudioManager(audioManager);
protected virtual IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset);
protected WorkingBeatmap CreateWorkingBeatmap(RulesetInfo ruleset) =>
CreateWorkingBeatmap(CreateBeatmap(ruleset));
protected virtual WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) =>
new ClockBackedTestWorkingBeatmap(beatmap, Clock, audio);
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
Ruleset.Value = rulesets.AvailableRulesets.First(); Ruleset.Value = rulesets.AvailableRulesets.First();
} }
@ -61,7 +82,8 @@ namespace osu.Game.Tests.Visual
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);
beatmap?.Value.Track.Stop(); if (beatmap?.Value.TrackLoaded == true)
beatmap.Value.Track.Stop();
if (localStorage.IsValueCreated) if (localStorage.IsValueCreated)
{ {
@ -78,6 +100,164 @@ namespace osu.Game.Tests.Visual
protected override ITestSceneTestRunner CreateRunner() => new OsuTestSceneTestRunner(); protected override ITestSceneTestRunner CreateRunner() => new OsuTestSceneTestRunner();
public class ClockBackedTestWorkingBeatmap : TestWorkingBeatmap
{
private readonly Track track;
private readonly TrackVirtualStore store;
/// <summary>
/// Create an instance which creates a <see cref="TestBeatmap"/> for the provided ruleset when requested.
/// </summary>
/// <param name="ruleset">The target ruleset.</param>
/// <param name="referenceClock">A clock which should be used instead of a stopwatch for virtual time progression.</param>
/// <param name="audio">Audio manager. Required if a reference clock isn't provided.</param>
public ClockBackedTestWorkingBeatmap(RulesetInfo ruleset, IFrameBasedClock referenceClock, AudioManager audio)
: this(new TestBeatmap(ruleset), referenceClock, audio)
{
}
/// <summary>
/// Create an instance which provides the <see cref="IBeatmap"/> when requested.
/// </summary>
/// <param name="beatmap">The beatmap</param>
/// <param name="referenceClock">An optional clock which should be used instead of a stopwatch for virtual time progression.</param>
/// <param name="audio">Audio manager. Required if a reference clock isn't provided.</param>
/// <param name="length">The length of the returned virtual track.</param>
public ClockBackedTestWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock referenceClock, AudioManager audio, double length = 60000)
: base(beatmap)
{
if (referenceClock != null)
{
store = new TrackVirtualStore(referenceClock);
audio.AddItem(store);
track = store.GetVirtual(length);
}
else
track = audio?.Tracks.GetVirtual(length);
}
public override void Dispose()
{
base.Dispose();
store?.Dispose();
}
protected override Track GetTrack() => track;
public class TrackVirtualStore : AudioCollectionManager<Track>, ITrackStore
{
private readonly IFrameBasedClock referenceClock;
public TrackVirtualStore(IFrameBasedClock referenceClock)
{
this.referenceClock = referenceClock;
}
public Track Get(string name) => throw new NotImplementedException();
public Task<Track> GetAsync(string name) => throw new NotImplementedException();
public Stream GetStream(string name) => throw new NotImplementedException();
public IEnumerable<string> GetAvailableResources() => throw new NotImplementedException();
public Track GetVirtual(double length = Double.PositiveInfinity)
{
var track = new TrackVirtualManual(referenceClock) { Length = length };
AddItem(track);
return track;
}
}
/// <summary>
/// A virtual track which tracks a reference clock.
/// </summary>
public class TrackVirtualManual : Track
{
private readonly IFrameBasedClock referenceClock;
private readonly ManualClock clock = new ManualClock();
private bool running;
/// <summary>
/// Local offset added to the reference clock to resolve correct time.
/// </summary>
private double offset;
public TrackVirtualManual(IFrameBasedClock referenceClock)
{
this.referenceClock = referenceClock;
Length = double.PositiveInfinity;
}
public override bool Seek(double seek)
{
offset = MathHelper.Clamp(seek, 0, Length);
lastReferenceTime = null;
return offset == seek;
}
public override void Start()
{
running = true;
}
public override void Reset()
{
Seek(0);
base.Reset();
}
public override void Stop()
{
if (running)
{
running = false;
// on stopping, the current value should be transferred out of the clock, as we can no longer rely on
// the referenceClock (which will still be counting time).
offset = clock.CurrentTime;
lastReferenceTime = null;
}
}
public override bool IsRunning => running;
private double? lastReferenceTime;
public override double CurrentTime => clock.CurrentTime;
protected override void UpdateState()
{
base.UpdateState();
if (running)
{
double refTime = referenceClock.CurrentTime;
if (!lastReferenceTime.HasValue)
{
// if the clock just started running, the current value should be transferred to the offset
// (to zero the progression of time).
offset -= refTime;
}
lastReferenceTime = refTime;
}
clock.CurrentTime = Math.Min((lastReferenceTime ?? 0) + offset, Length);
if (CurrentTime >= Length)
{
Stop();
RaiseCompleted();
}
}
}
}
public class OsuTestSceneTestRunner : OsuGameBase, ITestSceneTestRunner public class OsuTestSceneTestRunner : OsuGameBase, ITestSceneTestRunner
{ {
private TestSceneTestRunner.TestRunner runner; private TestSceneTestRunner.TestRunner runner;
@ -99,15 +279,6 @@ namespace osu.Game.Tests.Visual
: base(defaultValue) : base(defaultValue)
{ {
} }
public void SetAudioManager(AudioManager audioManager) => RegisterAudioManager(audioManager);
public override BindableBeatmap GetBoundCopy()
{
var copy = new OsuTestBeatmap(Default);
copy.BindTo(this);
return copy;
}
} }
} }
} }

View File

@ -15,19 +15,18 @@ namespace osu.Game.Tests.Visual
[Cached(Type = typeof(IPlacementHandler))] [Cached(Type = typeof(IPlacementHandler))]
public abstract class PlacementBlueprintTestScene : OsuTestScene, IPlacementHandler public abstract class PlacementBlueprintTestScene : OsuTestScene, IPlacementHandler
{ {
protected readonly Container HitObjectContainer; protected Container HitObjectContainer;
private PlacementBlueprint currentBlueprint; private PlacementBlueprint currentBlueprint;
protected PlacementBlueprintTestScene() protected PlacementBlueprintTestScene()
{ {
Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = 2;
Add(HitObjectContainer = CreateHitObjectContainer()); Add(HitObjectContainer = CreateHitObjectContainer());
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = 2;
Add(currentBlueprint = CreateBlueprint()); Add(currentBlueprint = CreateBlueprint());
} }

View File

@ -4,12 +4,10 @@
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
@ -39,15 +37,13 @@ namespace osu.Game.Tests.Visual
AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1); AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1);
} }
protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo);
protected virtual bool AllowFail => false; protected virtual bool AllowFail => false;
private void loadPlayer() private void loadPlayer()
{ {
var beatmap = CreateBeatmap(ruleset); var beatmap = CreateBeatmap(ruleset.RulesetInfo);
Beatmap.Value = new TestWorkingBeatmap(beatmap, Clock); Beatmap.Value = CreateWorkingBeatmap(beatmap);
if (!AllowFail) if (!AllowFail)
Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) };

View File

@ -15,7 +15,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.4" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.518.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.518.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.523.0" /> <PackageReference Include="ppy.osu.Framework" Version="2019.606.1" />
<PackageReference Include="SharpCompress" Version="0.23.0" /> <PackageReference Include="SharpCompress" Version="0.23.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />

View File

@ -105,8 +105,8 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.128.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.128.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.523.0" /> <PackageReference Include="ppy.osu.Framework" Version="2019.606.1" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.523.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2019.606.1" />
<PackageReference Include="SharpCompress" Version="0.22.0" /> <PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />