1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-16 01:42:58 +08:00

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.
Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh/home/changelog).
## Requirements
- 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
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`.
- 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).
If you are not interested in developing the game, you can consume our [binary releases](https://github.com/ppy/osu/releases).
**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.
### Downloading the source code
Clone the repository **including submodules**:
Clone the repository:
```shell
git clone https://github.com/ppy/osu
@ -45,7 +54,7 @@ git pull
### 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.

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
{
BeatmapInfo = new BeatmapInfo
{
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
{
BeatmapInfo = new BeatmapInfo
{
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
{
BeatmapInfo = new BeatmapInfo
{
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);
}
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo =
{
Ruleset = ruleset.RulesetInfo,
Ruleset = ruleset,
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
{
BeatmapInfo = new BeatmapInfo
{
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.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual;
using osuTK;
@ -299,7 +298,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
AddStep("load player", () =>
{
Beatmap.Value = new TestWorkingBeatmap(new Beatmap<OsuHitObject>
Beatmap.Value = CreateWorkingBeatmap(new Beatmap<OsuHitObject>
{
HitObjects =
{
@ -323,7 +322,7 @@ namespace osu.Game.Rulesets.Osu.Tests
BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 3 },
Ruleset = new OsuRuleset().RulesetInfo
},
}, Clock);
});
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.
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Primitives;
using osuTK;
using osuTK.Graphics;
using osuTK.Graphics.ES30;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@ -19,8 +16,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private readonly SliderPath path;
protected Path Path => path;
private readonly BufferedContainer container;
public float PathRadius
{
get => path.PathRadius;
@ -44,8 +39,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
path.AccentColour = value;
container.ForceRedraw();
}
}
@ -61,8 +54,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
path.BorderColour = value;
container.ForceRedraw();
}
}
@ -78,23 +69,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
path.BorderSize = value;
container.ForceRedraw();
}
}
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
protected SliderBody()
{
InternalChild = container = new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
CacheDrawnFrameBuffer = true,
Child = path = new SliderPath { Blending = BlendingMode.None }
};
container.Attach(RenderbufferInternalFormat.DepthComponent16);
InternalChild = path = new SliderPath();
}
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"/>.
/// </summary>
/// <param name="vertices">The vertices</param>
protected void SetVertices(IReadOnlyList<Vector2> vertices)
{
path.Vertices = vertices;
container.ForceRedraw();
}
protected void SetVertices(IReadOnlyList<Vector2> vertices) => path.Vertices = vertices;
private class SliderPath : SmoothPath
{

View File

@ -210,7 +210,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
Vector2 pos = parts[i].Position;
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),
DrawColourInfo.Colour,
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.Drawables;
using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual;
using osuTK;
using osu.Game.Rulesets.Scoring;
@ -64,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
var controlPointInfo = new ControlPointInfo();
controlPointInfo.TimingPoints.Add(new TimingControlPoint());
WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
{
HitObjects = new List<HitObject> { new CentreHit() },
BeatmapInfo = new BeatmapInfo
@ -79,7 +78,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Ruleset = new TaikoRuleset().RulesetInfo
},
ControlPointInfo = controlPointInfo
}, Clock);
});
Add(playfieldContainer = new Container
{

View File

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

View File

@ -111,16 +111,19 @@ namespace osu.Game.Tests.Visual.Components
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
{
public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager)
private readonly ITrackStore trackManager;
public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore 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.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual.Editor
{
@ -19,7 +18,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, Clock);
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
Child = new ComposeScreen();
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -30,9 +31,9 @@ namespace osu.Game.Tests.Visual.Editor
};
[BackgroundDependencyLoader]
private void load()
private void load(AudioManager audio)
{
Beatmap.Value = new WaveformTestBeatmap();
Beatmap.Value = new WaveformTestBeatmap(audio);
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.
using NUnit.Framework;
@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
using osuTK;
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 };
}

View File

@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
@ -21,7 +20,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null);
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
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.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
@ -45,7 +44,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = new TestWorkingBeatmap(new Beatmap
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
{
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.
using NUnit.Framework;
@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Screens.Edit.Components;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Editor
Size = new Vector2(200, 100)
};
Beatmap.Value = new TestWorkingBeatmap(new Beatmap(), Clock);
Beatmap.Value = CreateWorkingBeatmap(new Beatmap());
Child = playback;
}

View File

@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio;
@ -10,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Osu;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
@ -20,9 +22,9 @@ namespace osu.Game.Tests.Visual.Editor
private WorkingBeatmap waveformBeatmap;
[BackgroundDependencyLoader]
private void load()
private void load(AudioManager audio)
{
waveformBeatmap = new WaveformTestBeatmap();
waveformBeatmap = new WaveformTestBeatmap(audio);
}
[TestCase(1f)]
@ -91,7 +93,7 @@ namespace osu.Game.Tests.Visual.Editor
Child = graph = new TestWaveformGraph
{
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.
using System;
@ -16,7 +16,6 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual.Gameplay
{
@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void Setup() => Schedule(() =>
{
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]

View File

@ -3,7 +3,6 @@
using System;
using osu.Framework.Lists;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
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);
return working;
}

View File

@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
@ -57,7 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("set name", () => Room.Name.Value = "Room name");
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);
AddStep("clear name", () => Room.Name.Value = "");

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets.Osu;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.Online
@ -25,7 +24,7 @@ namespace osu.Game.Tests.Visual.Online
[BackgroundDependencyLoader]
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.HasStoryboard = true;

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select;
@ -18,7 +19,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BeatmapDetails) };
public TestSceneBeatmapDetailArea()
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
BeatmapDetailArea detailsArea;
Add(detailsArea = new BeatmapDetailArea
@ -28,7 +30,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Size = new Vector2(550f, 450f),
});
AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap
AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{
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 =
{
@ -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 =
{
@ -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 =
{
@ -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 =
{

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.
using System.Collections.Generic;
@ -18,7 +18,6 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.SongSelect
@ -136,7 +135,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () =>
{
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);

View File

@ -8,6 +8,7 @@ using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.MathUtils;
@ -79,7 +80,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}
[BackgroundDependencyLoader]
private void load(GameHost host)
private void load(GameHost host, AudioManager audio)
{
factory = new DatabaseContextFactory(LocalStorage);
factory.ResetDatabase();
@ -93,7 +94,7 @@ namespace osu.Game.Tests.Visual.SongSelect
usage.Migrate();
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();
}

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.Shapes;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneLoadingAnimation : GridTestScene //todo: this should be an OsuTestScene
public class TestSceneLoadingAnimation : OsuGridTestScene
{
public TestSceneLoadingAnimation()
: base(2, 2)

View File

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

View File

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

View File

@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
@ -19,12 +20,14 @@ namespace osu.Game.Tests
{
private readonly ZipArchiveReader reader;
private readonly Stream stream;
private readonly ITrackStore trackStore;
public WaveformTestBeatmap()
: base(new BeatmapInfo())
public WaveformTestBeatmap(AudioManager audioManager)
: base(new BeatmapInfo(), audioManager)
{
stream = TestResources.GetTestBeatmapStream();
reader = new ZipArchiveReader(stream);
trackStore = audioManager.GetTrackStore(reader);
}
public override void Dispose()
@ -32,17 +35,19 @@ namespace osu.Game.Tests
base.Dispose();
stream?.Dispose();
reader?.Dispose();
trackStore?.Dispose();
}
protected override IBeatmap GetBeatmap() => createTestBeatmap();
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 Beatmap createTestBeatmap()

View File

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

View File

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

View File

@ -20,14 +20,12 @@ namespace osu.Game.Beatmaps
protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap
{
private readonly IResourceStore<byte[]> store;
private readonly AudioManager audioManager;
public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> store, TextureStore textureStore, BeatmapInfo beatmapInfo, AudioManager audioManager)
: base(beatmapInfo)
: base(beatmapInfo, audioManager)
{
this.store = store;
this.textureStore = textureStore;
this.audioManager = audioManager;
}
protected override IBeatmap GetBeatmap()
@ -47,6 +45,8 @@ namespace osu.Game.Beatmaps
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 Texture GetBackground()
@ -68,8 +68,7 @@ namespace osu.Game.Beatmaps
{
try
{
var trackData = store.GetStream(getPathForFile(Metadata.AudioFile));
return trackData == null ? null : new TrackBass(trackData);
return (trackStore ?? (trackStore = AudioManager.GetTrackStore(store))).Get(getPathForFile(Metadata.AudioFile));
}
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)
{
base.TransferTo(other);
@ -135,7 +142,7 @@ namespace osu.Game.Beatmaps
try
{
skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager);
skin = new LegacyBeatmapSkin(BeatmapInfo, store, AudioManager);
}
catch (Exception e)
{

View File

@ -1,6 +1,7 @@
// 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;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
@ -20,6 +21,8 @@ namespace osu.Game.Beatmaps
set => onlineBeatmapSetID = value > 0 ? value : null;
}
public DateTimeOffset DateAdded { get; set; }
public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
public BeatmapMetadata Metadata { get; set; }

View File

@ -1,11 +1,7 @@
// 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;
using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
namespace osu.Game.Beatmaps
@ -16,32 +12,15 @@ namespace osu.Game.Beatmaps
/// </summary>
public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap>
{
private AudioManager audioManager;
private WorkingBeatmap lastBeatmap;
protected BindableBeatmap(WorkingBeatmap defaultValue)
: base(defaultValue)
{
BindValueChanged(b => updateAudioTrack(b.NewValue), true);
}
/// <summary>
/// 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)
private void updateAudioTrack(WorkingBeatmap beatmap)
{
var trackLoaded = lastBeatmap?.TrackLoaded ?? false;
@ -55,18 +34,9 @@ namespace osu.Game.Beatmaps
lastBeatmap.RecycleTrack();
}
audioManager.Track.AddItem(beatmap.Track);
}
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.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -31,24 +32,8 @@ namespace osu.Game.Beatmaps.Drawables
/// </summary>
protected virtual double UnloadDelay => 10000;
private BeatmapInfo lastModel;
private bool firstLoad = true;
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 DelayedLoadWrapper CreateDelayedLoadWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad)
=> new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, UnloadDelay);
protected override Drawable CreateDrawable(BeatmapInfo model)
{

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Textures;
@ -16,9 +17,9 @@ namespace osu.Game.Beatmaps
{
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
{
Metadata = new BeatmapMetadata
@ -34,16 +35,16 @@ namespace osu.Game.Beatmaps
OverallDifficulty = 0,
},
Ruleset = new DummyRulesetInfo()
})
}, audio)
{
this.game = game;
this.textures = textures;
}
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
{

View File

@ -11,15 +11,17 @@ using osu.Framework.IO.File;
using System.IO;
using System.Linq;
using System.Threading;
using osu.Framework.Audio;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
namespace osu.Game.Beatmaps
{
public abstract partial class WorkingBeatmap : IDisposable
public abstract class WorkingBeatmap : IDisposable
{
public readonly BeatmapInfo BeatmapInfo;
@ -27,8 +29,11 @@ namespace osu.Game.Beatmaps
public readonly BeatmapMetadata Metadata;
protected WorkingBeatmap(BeatmapInfo beatmapInfo)
protected AudioManager AudioManager { get; }
protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager)
{
AudioManager = audioManager;
BeatmapInfo = beatmapInfo;
BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
@ -46,13 +51,39 @@ namespace osu.Game.Beatmaps
return b;
});
track = new RecyclableLazy<Track>(() => GetTrack() ?? new VirtualBeatmapTrack(Beatmap));
track = new RecyclableLazy<Track>(() => GetTrack() ?? GetVirtualTrack());
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
waveform = new RecyclableLazy<Waveform>(GetWaveform);
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
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>
/// Saves the <see cref="Beatmaps.Beatmap"/>.
/// </summary>
@ -150,6 +181,7 @@ namespace osu.Game.Beatmaps
public bool SkinLoaded => skin.IsResultAvailable;
public Skin Skin => skin.Value;
protected virtual Skin GetSkin() => new DefaultSkin();
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).
/// Accessing track again will load a fresh instance.
/// </summary>
public void RecycleTrack() => track.Recycle();
public virtual void RecycleTrack() => track.Recycle();
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? SkinInfoID { get; set; }
[Column("Key")]
public string Key { get; set; }

View File

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

View File

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

View File

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

View File

@ -31,7 +31,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
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]
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
{
Content.RelativeSizeAxes = Axes.None;
Content.AutoSizeAxes = Axes.None;
Content.Size = value;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -15,6 +15,8 @@ namespace osu.Game.IO.Archives
/// </summary>
public abstract Stream GetStream(string name);
public IEnumerable<string> GetAvailableResources() => Filenames;
public abstract void Dispose();
/// <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")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset>("DateAdded");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
@ -203,6 +205,8 @@ namespace osu.Game.Migrations
b.Property<int?>("RulesetID");
b.Property<int?>("SkinInfoID");
b.Property<string>("StringValue")
.HasColumnName("Value");
@ -210,6 +214,8 @@ namespace osu.Game.Migrations
b.HasKey("ID");
b.HasIndex("SkinInfoID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
@ -442,6 +448,13 @@ namespace osu.Game.Migrations
.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")

View File

@ -163,7 +163,7 @@ namespace osu.Game
dependencies.CacheAs<IAPIProvider>(API);
var defaultBeatmap = new DummyWorkingBeatmap(this);
var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures);
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
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.
// 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(beatmap);
@ -283,23 +283,10 @@ namespace osu.Game
private class OsuBindableBeatmap : BindableBeatmap
{
public OsuBindableBeatmap(WorkingBeatmap defaultValue, AudioManager audioManager)
: this(defaultValue)
{
RegisterAudioManager(audioManager);
}
public OsuBindableBeatmap(WorkingBeatmap defaultValue)
: base(defaultValue)
{
}
public override BindableBeatmap GetBoundCopy()
{
var copy = new OsuBindableBeatmap(Default);
copy.BindTo(this);
return copy;
}
}
private class OsuUserInputManager : UserInputManager

View File

@ -90,8 +90,8 @@ namespace osu.Game.Overlays.Changelog
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleClick = audio.Sample.Get(@"UI/generic-select-soft");
sampleHover = audio.Sample.Get(@"UI/generic-hover-soft");
sampleClick = audio.Samples.Get(@"UI/generic-select-soft");
sampleHover = audio.Samples.Get(@"UI/generic-hover-soft");
}
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);

View File

@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
@ -17,6 +19,11 @@ namespace osu.Game.Overlays
{
private Box overlay;
private readonly BindableDouble audioVolume = new BindableDouble(1);
[Resolved]
private AudioManager audio { get; set; }
[BackgroundDependencyLoader]
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]
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");
disc.EdgeEffect = leftStrip.EdgeEffect = rightStrip.EdgeEffect = new EdgeEffectParameters

View File

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

View File

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

View File

@ -31,6 +31,11 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
LabelText = "Bypass caching (slow)",
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<float>("flashlightDim").UpdateValue(ref flashlightDim);
Texture.WhitePixel.DrawQuad(screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction);
DrawQuad(Texture.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction);
shader.Unbind();
}

View File

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

View File

@ -150,7 +150,7 @@ namespace osu.Game.Screens.Menu
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()

View File

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

View File

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

View File

@ -255,8 +255,8 @@ namespace osu.Game.Screens.Menu
[BackgroundDependencyLoader]
private void load(TextureStore textures, AudioManager audio)
{
sampleClick = audio.Sample.Get(@"Menu/osu-logo-select");
sampleBeat = audio.Sample.Get(@"Menu/osu-logo-heartbeat");
sampleClick = audio.Samples.Get(@"Menu/osu-logo-select");
sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat");
logo.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
{
protected override double FadeDuration => 200;
protected override double TransformDuration => 200;
}
}
}

View File

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

View File

@ -99,7 +99,7 @@ namespace osu.Game.Screens
[BackgroundDependencyLoader(true)]
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)

View File

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

View File

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

View File

@ -45,6 +45,9 @@ namespace osu.Game.Screens.Select.Carousel
case SortMode.Author:
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:
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);
}

View File

@ -242,9 +242,9 @@ namespace osu.Game.Screens.Select
dialogOverlay = dialog;
sampleChangeDifficulty = audio.Sample.Get(@"SongSelect/select-difficulty");
sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand");
SampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection");
sampleChangeDifficulty = audio.Samples.Get(@"SongSelect/select-difficulty");
sampleChangeBeatmap = audio.Samples.Get(@"SongSelect/select-expand");
SampleConfirm = audio.Samples.Get(@"SongSelect/confirm-selection");
Carousel.LoadBeatmapSetsFromManager(this.beatmaps);
@ -580,9 +580,6 @@ namespace osu.Game.Screens.Select
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.Restart();
}

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@ -21,7 +22,7 @@ namespace osu.Game.Skinning
{
protected TextureStore Textures;
protected SampleManager Samples;
protected IResourceStore<SampleChannel> Samples;
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
: this(skin, new LegacySkinResourceStore<SkinFileInfo>(skin, storage), audioManager, "skin.ini")
@ -38,10 +39,17 @@ namespace osu.Game.Skinning
else
Configuration = new SkinConfiguration();
Samples = audioManager.GetSampleManager(storage);
Samples = audioManager.GetSampleStore(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)
{
switch (componentName)
@ -133,6 +141,8 @@ namespace osu.Game.Skinning
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;
public Task<byte[]> GetAsync(string name)

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using osu.Game.Configuration;
using osu.Game.Database;
namespace osu.Game.Skinning
@ -19,6 +20,8 @@ namespace osu.Game.Skinning
public List<SkinFileInfo> Files { get; set; }
public List<DatabasedSetting> Settings { get; set; }
public bool DeletePending { get; set; }
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.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using Microsoft.EntityFrameworkCore;
using osu.Framework.Platform;
using osu.Game.Database;
@ -12,5 +14,9 @@ namespace osu.Game.Skinning
: 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);
if (ch == null && allowFallback)
ch = loadChannel(s, audio.Sample.Get);
ch = loadChannel(s, audio.Samples.Get);
return ch;
}).Where(c => c != null).ToArray();
}
@ -58,5 +58,13 @@ namespace osu.Game.Skinning
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.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osuTK;
namespace osu.Game.Tests.Beatmaps
{
public class TestWorkingBeatmap : WorkingBeatmap
{
private readonly TrackVirtualManual track;
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>
/// 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>
public TestWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock referenceClock = null)
: base(beatmap.BeatmapInfo)
public TestWorkingBeatmap(IBeatmap beatmap)
: base(beatmap.BeatmapInfo, null)
{
this.beatmap = beatmap;
if (referenceClock != null)
track = new TrackVirtualManual(referenceClock);
}
protected override IBeatmap GetBeatmap() => beatmap;
protected override Texture GetBackground() => null;
protected override Track GetTrack() => 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();
}
}
}
protected override Track GetTrack() => null;
}
}

View File

@ -4,13 +4,10 @@
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Screens;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual
{
@ -50,26 +47,20 @@ namespace osu.Game.Tests.Visual
protected abstract void AddCheckSteps();
protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo);
protected virtual WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock clock) =>
new TestWorkingBeatmap(beatmap, Clock);
private Player loadPlayerFor(RulesetInfo ri)
private Player loadPlayerFor(RulesetInfo rulesetInfo)
{
Ruleset.Value = ri;
var r = ri.CreateInstance();
Ruleset.Value = rulesetInfo;
var ruleset = rulesetInfo.CreateInstance();
var beatmap = CreateBeatmap(r);
var working = CreateWorkingBeatmap(beatmap, Clock);
var working = CreateWorkingBeatmap(rulesetInfo);
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 = null;
Player = CreatePlayer(r);
Player = CreatePlayer(ruleset);
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.
using System;
@ -6,7 +6,6 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets;
using osu.Game.Screens.Edit;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual
{
@ -24,7 +23,7 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = new TestWorkingBeatmap(ruleset.RulesetInfo, null);
Beatmap.Value = CreateWorkingBeatmap(ruleset.RulesetInfo);
LoadScreen(new Editor());
}

View File

@ -3,15 +3,22 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Textures;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual
{
@ -19,7 +26,7 @@ namespace osu.Game.Tests.Visual
{
[Cached(typeof(Bindable<WorkingBeatmap>))]
[Cached(typeof(IBindable<WorkingBeatmap>))]
private readonly OsuTestBeatmap beatmap = new OsuTestBeatmap(new DummyWorkingBeatmap());
private OsuTestBeatmap beatmap;
protected BindableBeatmap Beatmap => beatmap;
@ -39,7 +46,12 @@ namespace osu.Game.Tests.Visual
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
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));
}
@ -49,11 +61,20 @@ namespace osu.Game.Tests.Visual
localStorage = new Lazy<Storage>(() => new NativeStorage($"{GetType().Name}-{Guid.NewGuid()}"));
}
[BackgroundDependencyLoader]
private void load(AudioManager audioManager, RulesetStore rulesets)
{
beatmap.SetAudioManager(audioManager);
[Resolved]
private AudioManager audio { get; set; }
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();
}
@ -61,7 +82,8 @@ namespace osu.Game.Tests.Visual
{
base.Dispose(isDisposing);
beatmap?.Value.Track.Stop();
if (beatmap?.Value.TrackLoaded == true)
beatmap.Value.Track.Stop();
if (localStorage.IsValueCreated)
{
@ -78,6 +100,164 @@ namespace osu.Game.Tests.Visual
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
{
private TestSceneTestRunner.TestRunner runner;
@ -99,15 +279,6 @@ namespace osu.Game.Tests.Visual
: 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))]
public abstract class PlacementBlueprintTestScene : OsuTestScene, IPlacementHandler
{
protected readonly Container HitObjectContainer;
protected Container HitObjectContainer;
private PlacementBlueprint currentBlueprint;
protected PlacementBlueprintTestScene()
{
Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = 2;
Add(HitObjectContainer = CreateHitObjectContainer());
}
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = 2;
Add(currentBlueprint = CreateBlueprint());
}

View File

@ -4,12 +4,10 @@
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual
{
@ -39,15 +37,13 @@ namespace osu.Game.Tests.Visual
AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1);
}
protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo);
protected virtual bool AllowFail => false;
private void loadPlayer()
{
var beatmap = CreateBeatmap(ruleset);
var beatmap = CreateBeatmap(ruleset.RulesetInfo);
Beatmap.Value = new TestWorkingBeatmap(beatmap, Clock);
Beatmap.Value = CreateWorkingBeatmap(beatmap);
if (!AllowFail)
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="Newtonsoft.Json" Version="12.0.2" />
<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="NUnit" Version="3.12.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="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.128.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.523.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.523.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.606.1" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.606.1" />
<PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />