1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 19:22:54 +08:00

Merge pull request #183 from peppy/general-fixes

Explicit disposal via using() to WorkingBeatmap.
This commit is contained in:
Thomas Müller 2016-11-22 16:56:42 +01:00 committed by GitHub
commit 420ed08119
8 changed files with 270 additions and 170 deletions

View File

@ -26,12 +26,12 @@ namespace osu.Game.Beatmaps.Drawable
/// </summary>
public Action<BeatmapGroup, BeatmapInfo> SelectionChanged;
private BeatmapSetInfo beatmapSet;
public BeatmapSetHeader Header;
private BeatmapGroupState state;
public List<BeatmapPanel> BeatmapPanels;
private WorkingBeatmap beatmap;
public BeatmapGroupState State
{
@ -65,11 +65,11 @@ namespace osu.Game.Beatmaps.Drawable
}
}
public BeatmapGroup(BeatmapSetInfo beatmapSet, WorkingBeatmap working)
public BeatmapGroup(WorkingBeatmap beatmap)
{
this.beatmapSet = beatmapSet;
this.beatmap = beatmap;
Header = new BeatmapSetHeader(beatmapSet, working)
Header = new BeatmapSetHeader(beatmap)
{
GainedSelection = headerGainedSelection,
RelativeSizeAxes = Axes.X,
@ -77,7 +77,7 @@ namespace osu.Game.Beatmaps.Drawable
Origin = Anchor.TopRight,
};
BeatmapPanels = beatmapSet.Beatmaps.Select(b => new BeatmapPanel(b)
BeatmapPanels = beatmap.BeatmapSetInfo.Beatmaps.Select(b => new BeatmapPanel(b)
{
GainedSelection = panelGainedSelection,
Anchor = Anchor.TopRight,

View File

@ -14,107 +14,27 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Game.Configuration;
using osu.Framework.Graphics.Colour;
using osu.Framework;
namespace osu.Game.Beatmaps.Drawable
{
class BeatmapSetHeader : Panel
{
public Action<BeatmapSetHeader> GainedSelection;
private BeatmapSetInfo beatmapSet;
private SpriteText title, artist;
private OsuConfigManager config;
private Bindable<bool> preferUnicode;
protected override void Selected()
{
base.Selected();
GainedSelection?.Invoke(this);
}
private Bindable<bool> preferUnicode;
private WorkingBeatmap beatmap;
protected override void Deselected()
{
base.Deselected();
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
this.config = config;
preferUnicode = config.GetBindable<bool>(OsuConfig.ShowUnicode);
preferUnicode.ValueChanged += preferUnicode_changed;
preferUnicode_changed(preferUnicode, null);
}
private void preferUnicode_changed(object sender, EventArgs e)
public BeatmapSetHeader(WorkingBeatmap beatmap)
{
title.Text = config.GetUnicodeString(beatmapSet.Metadata.Title, beatmapSet.Metadata.TitleUnicode);
artist.Text = config.GetUnicodeString(beatmapSet.Metadata.Artist, beatmapSet.Metadata.ArtistUnicode);
}
protected override void Dispose(bool isDisposing)
{
if (preferUnicode != null)
preferUnicode.ValueChanged -= preferUnicode_changed;
base.Dispose(isDisposing);
}
this.beatmap = beatmap;
public BeatmapSetHeader(BeatmapSetInfo beatmapSet, WorkingBeatmap working)
{
this.beatmapSet = beatmapSet;
Children = new Framework.Graphics.Drawable[]
{
new BufferedContainer
new PanelBackground(beatmap)
{
CacheDrawnFrameBuffer = true,
RelativeSizeAxes = Axes.Both,
Children = new Framework.Graphics.Drawable[]
{
working.Background == null ? new Box{ RelativeSizeAxes = Axes.Both, Colour = new Color4(200, 200, 200, 255) } : new Sprite
{
Texture = working.Background,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1366 / working.Background.Width * 0.6f),
},
new FlowContainer
{
Direction = FlowDirection.HorizontalOnly,
RelativeSizeAxes = Axes.Both,
// This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle
Shear = new Vector2(0.8f, 0),
Alpha = 0.5f,
Children = new[]
{
// The left half with no gradient applied
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Width = 0.4f,
},
// Piecewise-linear gradient with 3 segments to make it appear smoother
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientHorizontal(Color4.Black, new Color4(0f, 0f, 0f, 0.9f)),
Width = 0.05f,
},
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.9f), new Color4(0f, 0f, 0f, 0.1f)),
Width = 0.2f,
},
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.1f), new Color4(0, 0, 0, 0)),
Width = 0.05f,
},
}
},
}
},
new FlowContainer
{
@ -126,7 +46,7 @@ namespace osu.Game.Beatmaps.Drawable
title = new SpriteText
{
Font = @"Exo2.0-BoldItalic",
Text = beatmapSet.Metadata.Title,
Text = beatmap.BeatmapSetInfo.Metadata.Title,
TextSize = 22,
Shadow = true,
},
@ -134,7 +54,7 @@ namespace osu.Game.Beatmaps.Drawable
{
Margin = new MarginPadding { Top = -1 },
Font = @"Exo2.0-SemiBoldItalic",
Text = beatmapSet.Metadata.Artist,
Text = beatmap.BeatmapSetInfo.Metadata.Artist,
TextSize = 17,
Shadow = true,
},
@ -151,6 +71,125 @@ namespace osu.Game.Beatmaps.Drawable
}
}
};
}
protected override void Selected()
{
base.Selected();
GainedSelection?.Invoke(this);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
this.config = config;
preferUnicode = config.GetBindable<bool>(OsuConfig.ShowUnicode);
preferUnicode.ValueChanged += preferUnicode_changed;
preferUnicode_changed(preferUnicode, null);
}
private void preferUnicode_changed(object sender, EventArgs e)
{
title.Text = config.GetUnicodeString(beatmap.BeatmapSetInfo.Metadata.Title, beatmap.BeatmapSetInfo.Metadata.TitleUnicode);
artist.Text = config.GetUnicodeString(beatmap.BeatmapSetInfo.Metadata.Artist, beatmap.BeatmapSetInfo.Metadata.ArtistUnicode);
}
protected override void Dispose(bool isDisposing)
{
if (preferUnicode != null)
preferUnicode.ValueChanged -= preferUnicode_changed;
base.Dispose(isDisposing);
}
class PanelBackground : BufferedContainer
{
private readonly WorkingBeatmap working;
public PanelBackground(WorkingBeatmap working)
{
this.working = working;
CacheDrawnFrameBuffer = true;
Children = new[]
{
new FlowContainer
{
Depth = 1,
Direction = FlowDirection.HorizontalOnly,
RelativeSizeAxes = Axes.Both,
// This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle
Shear = new Vector2(0.8f, 0),
Alpha = 0.5f,
Children = new[]
{
// The left half with no gradient applied
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Width = 0.4f,
},
// Piecewise-linear gradient with 3 segments to make it appear smoother
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientHorizontal(Color4.Black, new Color4(0f, 0f, 0f, 0.9f)),
Width = 0.05f,
},
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.9f), new Color4(0f, 0f, 0f, 0.1f)),
Width = 0.2f,
},
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.1f), new Color4(0, 0, 0, 0)),
Width = 0.05f,
},
}
},
};
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
//todo: masking check
new BeatmapBackground(working)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}.Preload(game, (bg) =>
{
Add(bg);
ForceRedraw();
});
}
class BeatmapBackground : Sprite
{
private readonly WorkingBeatmap working;
public BeatmapBackground(WorkingBeatmap working)
{
this.working = working;
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
Texture = working.Background;
}
protected override void LoadComplete()
{
base.LoadComplete();
Scale = new Vector2(1366 / (Texture?.Width ?? 1) * 0.6f);
}
}
}
}
}

View File

@ -14,7 +14,7 @@ namespace osu.Game.Beatmaps.IO
public Func<BasicStorage, string, bool> Test { get; set; }
public Type Type { get; set; }
}
private static List<Reader> readers { get; } = new List<Reader>();
public static ArchiveReader GetReader(BasicStorage storage, string path)

View File

@ -18,7 +18,7 @@ namespace osu.Game.Beatmaps
public readonly BeatmapSetInfo BeatmapSetInfo;
private readonly BeatmapDatabase database;
private ArchiveReader reader => database?.GetReader(BeatmapSetInfo);
private ArchiveReader GetReader() => database?.GetReader(BeatmapSetInfo);
private Texture background;
private object backgroundLock = new object();
@ -32,7 +32,8 @@ namespace osu.Game.Beatmaps
try
{
background = new TextureStore(new RawTextureLoaderStore(reader)).Get(BeatmapInfo.Metadata.BackgroundFile);
using (var reader = GetReader())
background = new TextureStore(new RawTextureLoaderStore(reader), false).Get(BeatmapInfo.Metadata.BackgroundFile);
}
catch { }
@ -54,6 +55,7 @@ namespace osu.Game.Beatmaps
try
{
using (var reader = GetReader())
using (var stream = new StreamReader(reader.GetStream(BeatmapInfo.Path)))
beatmap = BeatmapDecoder.GetDecoder(stream)?.Decode(stream);
}
@ -77,9 +79,12 @@ namespace osu.Game.Beatmaps
try
{
var trackData = reader?.GetStream(BeatmapInfo.Metadata.AudioFile);
if (trackData != null)
track = new AudioTrackBass(trackData);
using (var reader = GetReader())
{
var trackData = reader?.GetStream(BeatmapInfo.Metadata.AudioFile);
if (trackData != null)
track = new AudioTrackBass(trackData);
}
}
catch { }
@ -89,11 +94,7 @@ namespace osu.Game.Beatmaps
set { lock (trackLock) track = value; }
}
~WorkingBeatmap()
{
Dispose(false);
}
public bool TrackLoaded => track != null;
public WorkingBeatmap(Beatmap beatmap)
{
@ -114,7 +115,7 @@ namespace osu.Game.Beatmaps
if (!isDisposed)
{
track?.Dispose();
reader?.Dispose();
background?.Dispose();
isDisposed = true;
}
}

View File

@ -136,7 +136,7 @@ namespace osu.Game.Database
public BeatmapSetInfo GetBeatmapSet(int id)
{
return Query<BeatmapSetInfo>().Where(s => s.BeatmapSetID == id).FirstOrDefault();
return Query<BeatmapSetInfo>().FirstOrDefault(s => s.BeatmapSetID == id);
}
public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null)
@ -144,7 +144,7 @@ namespace osu.Game.Database
var beatmapSetInfo = Query<BeatmapSetInfo>().FirstOrDefault(s => s.BeatmapSetID == beatmapInfo.BeatmapSetID);
//we need metadata
GetChildren(beatmapSetInfo);
GetChildren(beatmapSetInfo, false);
if (beatmapSetInfo == null)
throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetID} is not in the local database.");
@ -175,10 +175,10 @@ namespace osu.Game.Database
return connection.GetWithChildren<T>(id);
}
public List<T> GetAllWithChildren<T>(Expression<Func<T, bool>> filter = null,
bool recursive = true) where T : class
public List<T> GetAllWithChildren<T>(Expression<Func<T, bool>> filter = null, bool recursive = true)
where T : class
{
return connection.GetAllWithChildren<T>(filter, recursive);
return connection.GetAllWithChildren(filter, recursive);
}
public T GetChildren<T>(T item, bool recursive = true)
@ -199,7 +199,7 @@ namespace osu.Game.Database
public void Update<T>(T record, bool cascade = true) where T : class
{
if (!validTypes.Any(t => t == typeof(T)))
if (validTypes.All(t => t != typeof(T)))
throw new ArgumentException(nameof(T), "Must be a type managed by BeatmapDatabase");
if (cascade)
connection.UpdateWithChildren(record);

View File

@ -34,7 +34,6 @@ namespace osu.Game.Overlays
private DragBar progress;
private TextAwesome playButton, listButton;
private SpriteText title, artist;
private Texture fallbackTexture;
private List<BeatmapSetInfo> playList;
private List<BeatmapInfo> playHistory = new List<BeatmapInfo>();
@ -47,6 +46,7 @@ namespace osu.Game.Overlays
private OsuConfigManager config;
private WorkingBeatmap current;
private BeatmapDatabase beatmaps;
private BaseGame game;
public MusicController()
{
@ -208,7 +208,7 @@ namespace osu.Game.Overlays
beatmapSource = osuGame.Beatmap ?? new Bindable<WorkingBeatmap>();
playList = beatmaps.GetAllWithChildren<BeatmapSetInfo>();
backgroundSprite = new MusicControllerBackground(fallbackTexture = textures.Get(@"Backgrounds/bg4"));
backgroundSprite = new MusicControllerBackground();
AddInternal(backgroundSprite);
}
@ -222,12 +222,15 @@ namespace osu.Game.Overlays
protected override void Update()
{
base.Update();
if (current?.Track == null) return;
progress.UpdatePosition((float)(current.Track.CurrentTime / current.Track.Length));
playButton.Icon = current.Track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
if (current?.TrackLoaded ?? false)
{
if (current.Track.HasCompleted && !current.Track.Looping) next();
progress.UpdatePosition((float)(current.Track.CurrentTime / current.Track.Length));
playButton.Icon = current.Track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
if (current.Track.HasCompleted && !current.Track.Looping) next();
}
}
void preferUnicode_changed(object sender, EventArgs e)
@ -304,36 +307,49 @@ namespace osu.Game.Overlays
updateDisplay(current, isNext ? TransformDirection.Next : TransformDirection.Prev);
}
protected override void PerformLoad(BaseGame game)
{
this.game = game;
base.PerformLoad(game);
}
private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction)
{
if (beatmap.Beatmap == null)
//todo: we may need to display some default text here (currently in the constructor).
return;
BeatmapMetadata metadata = beatmap.Beatmap.BeatmapInfo.Metadata;
title.Text = config.GetUnicodeString(metadata.Title, metadata.TitleUnicode);
artist.Text = config.GetUnicodeString(metadata.Artist, metadata.ArtistUnicode);
MusicControllerBackground newBackground = new MusicControllerBackground(beatmap.Background ?? fallbackTexture);
Add(newBackground);
switch (direction)
Task.Run(() =>
{
case TransformDirection.Next:
newBackground.Position = new Vector2(400, 0);
newBackground.MoveToX(0, 500, EasingTypes.OutCubic);
backgroundSprite.MoveToX(-400, 500, EasingTypes.OutCubic);
break;
case TransformDirection.Prev:
newBackground.Position = new Vector2(-400, 0);
newBackground.MoveToX(0, 500, EasingTypes.OutCubic);
backgroundSprite.MoveToX(400, 500, EasingTypes.OutCubic);
break;
}
if (beatmap.Beatmap == null)
//todo: we may need to display some default text here (currently in the constructor).
return;
backgroundSprite.Expire();
backgroundSprite = newBackground;
BeatmapMetadata metadata = beatmap.Beatmap.BeatmapInfo.Metadata;
title.Text = config.GetUnicodeString(metadata.Title, metadata.TitleUnicode);
artist.Text = config.GetUnicodeString(metadata.Artist, metadata.ArtistUnicode);
});
MusicControllerBackground newBackground;
(newBackground = new MusicControllerBackground(beatmap)).Preload(game, delegate
{
Add(newBackground);
switch (direction)
{
case TransformDirection.Next:
newBackground.Position = new Vector2(400, 0);
newBackground.MoveToX(0, 500, EasingTypes.OutCubic);
backgroundSprite.MoveToX(-400, 500, EasingTypes.OutCubic);
break;
case TransformDirection.Prev:
newBackground.Position = new Vector2(-400, 0);
newBackground.MoveToX(0, 500, EasingTypes.OutCubic);
backgroundSprite.MoveToX(400, 500, EasingTypes.OutCubic);
break;
}
backgroundSprite.Expire();
backgroundSprite = newBackground;
});
}
private void seek(float position)
@ -363,9 +379,11 @@ namespace osu.Game.Overlays
private class MusicControllerBackground : BufferedContainer
{
private Sprite sprite;
private WorkingBeatmap beatmap;
public MusicControllerBackground(Texture backgroundTexture)
public MusicControllerBackground(WorkingBeatmap beatmap = null)
{
this.beatmap = beatmap;
CacheDrawnFrameBuffer = true;
RelativeSizeAxes = Axes.Both;
Depth = float.MinValue;
@ -374,7 +392,6 @@ namespace osu.Game.Overlays
{
sprite = new Sprite
{
Texture = backgroundTexture,
Colour = new Color4(150, 150, 150, 255)
},
new Box
@ -388,6 +405,12 @@ namespace osu.Game.Overlays
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
}
protected override void LoadComplete()
{
base.LoadComplete();

View File

@ -1,9 +1,8 @@
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Transformations;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Background;
@ -15,6 +14,7 @@ namespace osu.Game.Screens.Backgrounds
private Background background;
private WorkingBeatmap beatmap;
private Vector2 blurTarget;
public WorkingBeatmap Beatmap
{
@ -30,20 +30,26 @@ namespace osu.Game.Screens.Backgrounds
beatmap = value;
Background oldBackground = background;
addBackground(background = new Background());
background.Sprite.Texture = beatmap.Background;
if (oldBackground != null)
Schedule(() =>
{
oldBackground.Depth = 1;
oldBackground.Flush();
oldBackground.FadeOut(250);
oldBackground.Expire();
Background newBackground = new BeatmapBackground(beatmap);
background.BlurSigma = oldBackground.BlurSigma;
}
newBackground.Preload(Game, delegate
{
Background oldBackground = background;
Add(background = newBackground);
background.BlurSigma = blurTarget;
if (oldBackground != null)
{
oldBackground.Depth = 1;
oldBackground.Flush();
oldBackground.FadeOut(250);
oldBackground.Expire();
}
});
});
}
}
@ -52,20 +58,33 @@ namespace osu.Game.Screens.Backgrounds
Beatmap = beatmap;
}
private void addBackground(Background background)
{
background.CacheDrawnFrameBuffer = true;
Add(background);
}
public void BlurTo(Vector2 sigma, double duration)
{
background?.BlurTo(sigma, duration, EasingTypes.OutExpo);
blurTarget = sigma;
}
public override bool Equals(BackgroundMode other)
{
return base.Equals(other) && beatmap == ((BackgroundModeBeatmap)other).Beatmap;
}
class BeatmapBackground : Background
{
private WorkingBeatmap beatmap;
public BeatmapBackground(WorkingBeatmap beatmap)
{
this.beatmap = beatmap;
CacheDrawnFrameBuffer = true;
}
[BackgroundDependencyLoader]
private void load()
{
Sprite.Texture = beatmap.Background;
}
}
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@ -44,6 +45,7 @@ namespace osu.Game.Screens.Select
private Container wedgedBeatmapInfo;
private static readonly Vector2 BACKGROUND_BLUR = new Vector2(20);
private CancellationTokenSource initialAddSetsTask;
/// <param name="database">Optionally provide a database to use instead of the OsuGame one.</param>
public PlaySongSelect(BeatmapDatabase database = null)
@ -156,11 +158,18 @@ namespace osu.Game.Screens.Select
if (database == null)
database = beatmaps;
database.BeatmapSetAdded += s => Schedule(() => addBeatmapSet(s, game));
database.BeatmapSetAdded += onDatabaseOnBeatmapSetAdded;
trackManager = audio.Track;
Task.Factory.StartNew(() => addBeatmapSets(game));
initialAddSetsTask = new CancellationTokenSource();
Task.Factory.StartNew(() => addBeatmapSets(game, initialAddSetsTask.Token), initialAddSetsTask.Token);
}
private void onDatabaseOnBeatmapSetAdded(BeatmapSetInfo s)
{
Schedule(() => addBeatmapSet(s, Game));
}
protected override void OnEntering(GameMode last)
@ -199,6 +208,10 @@ namespace osu.Game.Screens.Select
base.Dispose(isDisposing);
if (playMode != null)
playMode.ValueChanged -= playMode_ValueChanged;
database.BeatmapSetAdded -= onDatabaseOnBeatmapSetAdded;
initialAddSetsTask.Cancel();
}
private void playMode_ValueChanged(object sender, EventArgs e)
@ -218,6 +231,7 @@ namespace osu.Game.Screens.Select
(Background as BackgroundModeBeatmap)?.BlurTo(BACKGROUND_BLUR, 1000);
}
//todo: move to own class and fix async logic (move every call on WorkingBeatmap.* to load() and use Preload to create it.
refreshWedgedBeatmapInfo(beatmap);
}
@ -332,6 +346,7 @@ namespace osu.Game.Screens.Select
{
base.OnBeatmapChanged(beatmap);
//todo: change background in selectionChanged instead; support per-difficulty backgrounds.
changeBackground(beatmap);
selectBeatmap(beatmap.BeatmapInfo);
@ -375,9 +390,9 @@ namespace osu.Game.Screens.Select
beatmapSet.Beatmaps.ForEach(b => database.GetChildren(b));
beatmapSet.Beatmaps = beatmapSet.Beatmaps.OrderBy(b => b.BaseDifficulty.OverallDifficulty).ToList();
var working = database.GetWorkingBeatmap(beatmapSet.Beatmaps.FirstOrDefault());
var beatmap = database.GetWorkingBeatmap(beatmapSet.Beatmaps.FirstOrDefault());
var group = new BeatmapGroup(beatmapSet, working) { SelectionChanged = selectionChanged };
var group = new BeatmapGroup(beatmap) { SelectionChanged = selectionChanged };
//for the time being, let's completely load the difficulty panels in the background.
//this likely won't scale so well, but allows us to completely async the loading flow.
@ -396,10 +411,13 @@ namespace osu.Game.Screens.Select
}));
}
private void addBeatmapSets(BaseGame game)
private void addBeatmapSets(BaseGame game, CancellationToken token)
{
foreach (var beatmapSet in database.Query<BeatmapSetInfo>())
{
if (token.IsCancellationRequested) return;
addBeatmapSet(beatmapSet, game);
}
}
}
}