1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 17:35:10 +08:00

Merge branch 'master' into update-framework

This commit is contained in:
Dean Herbert 2021-02-19 10:39:50 +09:00
commit ce0011f710
14 changed files with 171 additions and 84 deletions

View File

@ -152,7 +152,7 @@ namespace osu.Game.Tournament
{ {
if (string.IsNullOrEmpty(p.Username) || p.Statistics == null) if (string.IsNullOrEmpty(p.Username) || p.Statistics == null)
{ {
PopulateUser(p); PopulateUser(p, immediate: true);
addedInfo = true; addedInfo = true;
} }
} }
@ -211,12 +211,14 @@ namespace osu.Game.Tournament
return addedInfo; return addedInfo;
} }
public void PopulateUser(User user, Action success = null, Action failure = null) public void PopulateUser(User user, Action success = null, Action failure = null, bool immediate = false)
{ {
var req = new GetUserRequest(user.Id, Ruleset.Value); var req = new GetUserRequest(user.Id, Ruleset.Value);
req.Success += res => req.Success += res =>
{ {
user.Id = res.Id;
user.Username = res.Username; user.Username = res.Username;
user.Statistics = res.Statistics; user.Statistics = res.Statistics;
user.Country = res.Country; user.Country = res.Country;
@ -231,7 +233,10 @@ namespace osu.Game.Tournament
failure?.Invoke(); failure?.Invoke();
}; };
API.Queue(req); if (immediate)
API.Perform(req);
else
API.Queue(req);
} }
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -51,7 +51,12 @@ namespace osu.Game.Beatmaps
[JsonProperty(@"tags")] [JsonProperty(@"tags")]
public string Tags { get; set; } public string Tags { get; set; }
/// <summary>
/// The time in milliseconds to begin playing the track for preview purposes.
/// If -1, the track should begin playing at 40% of its length.
/// </summary>
public int PreviewTime { get; set; } public int PreviewTime { get; set; }
public string AudioFile { get; set; } public string AudioFile { get; set; }
public string BackgroundFile { get; set; } public string BackgroundFile { get; set; }

View File

@ -266,6 +266,26 @@ namespace osu.Game.Beatmaps
[NotNull] [NotNull]
public Track LoadTrack() => loadedTrack = GetBeatmapTrack() ?? GetVirtualTrack(1000); public Track LoadTrack() => loadedTrack = GetBeatmapTrack() ?? GetVirtualTrack(1000);
/// <summary>
/// Reads the correct track restart point from beatmap metadata and sets looping to enabled.
/// </summary>
public void PrepareTrackForPreviewLooping()
{
Track.Looping = true;
Track.RestartPoint = Metadata.PreviewTime;
if (Track.RestartPoint == -1)
{
if (!Track.IsLoaded)
{
// force length to be populated (https://github.com/ppy/osu-framework/issues/4202)
Track.Seek(Track.CurrentTime);
}
Track.RestartPoint = 0.4f * Track.Length;
}
}
/// <summary> /// <summary>
/// Transfer a valid audio track into this working beatmap. Used as an optimisation to avoid reload / track swap /// Transfer a valid audio track into this working beatmap. Used as an optimisation to avoid reload / track swap
/// across difficulties in the same beatmap set. /// across difficulties in the same beatmap set.

View File

@ -29,6 +29,14 @@ namespace osu.Game.Collections
/// </summary> /// </summary>
protected virtual bool ShowManageCollectionsItem => true; protected virtual bool ShowManageCollectionsItem => true;
private readonly BindableWithCurrent<CollectionFilterMenuItem> current = new BindableWithCurrent<CollectionFilterMenuItem>();
public new Bindable<CollectionFilterMenuItem> Current
{
get => current.Current;
set => current.Current = value;
}
private readonly IBindableList<BeatmapCollection> collections = new BindableList<BeatmapCollection>(); private readonly IBindableList<BeatmapCollection> collections = new BindableList<BeatmapCollection>();
private readonly IBindableList<BeatmapInfo> beatmaps = new BindableList<BeatmapInfo>(); private readonly IBindableList<BeatmapInfo> beatmaps = new BindableList<BeatmapInfo>();
private readonly BindableList<CollectionFilterMenuItem> filters = new BindableList<CollectionFilterMenuItem>(); private readonly BindableList<CollectionFilterMenuItem> filters = new BindableList<CollectionFilterMenuItem>();
@ -36,25 +44,28 @@ namespace osu.Game.Collections
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private ManageCollectionsDialog manageCollectionsDialog { get; set; } private ManageCollectionsDialog manageCollectionsDialog { get; set; }
[Resolved(CanBeNull = true)]
private CollectionManager collectionManager { get; set; }
public CollectionFilterDropdown() public CollectionFilterDropdown()
{ {
ItemSource = filters; ItemSource = filters;
} Current.Value = new AllBeatmapsCollectionFilterMenuItem();
[BackgroundDependencyLoader(permitNulls: true)]
private void load([CanBeNull] CollectionManager collectionManager)
{
if (collectionManager != null)
collections.BindTo(collectionManager.Collections);
collections.CollectionChanged += (_, __) => collectionsChanged();
collectionsChanged();
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
if (collectionManager != null)
collections.BindTo(collectionManager.Collections);
// Dropdown has logic which triggers a change on the bindable with every change to the contained items.
// This is not desirable here, as it leads to multiple filter operations running even though nothing has changed.
// An extra bindable is enough to subvert this behaviour.
base.Current = Current;
collections.BindCollectionChanged((_, __) => collectionsChanged(), true);
Current.BindValueChanged(filterChanged, true); Current.BindValueChanged(filterChanged, true);
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -9,7 +10,7 @@ namespace osu.Game.Collections
/// <summary> /// <summary>
/// A <see cref="BeatmapCollection"/> filter. /// A <see cref="BeatmapCollection"/> filter.
/// </summary> /// </summary>
public class CollectionFilterMenuItem public class CollectionFilterMenuItem : IEquatable<CollectionFilterMenuItem>
{ {
/// <summary> /// <summary>
/// The collection to filter beatmaps from. /// The collection to filter beatmaps from.
@ -33,6 +34,11 @@ namespace osu.Game.Collections
Collection = collection; Collection = collection;
CollectionName = Collection?.Name.GetBoundCopy() ?? new Bindable<string>("All beatmaps"); CollectionName = Collection?.Name.GetBoundCopy() ?? new Bindable<string>("All beatmaps");
} }
public bool Equals(CollectionFilterMenuItem other)
=> other != null && CollectionName.Value == other.CollectionName.Value;
public override int GetHashCode() => CollectionName.Value.GetHashCode();
} }
public class AllBeatmapsCollectionFilterMenuItem : CollectionFilterMenuItem public class AllBeatmapsCollectionFilterMenuItem : CollectionFilterMenuItem

View File

@ -139,35 +139,43 @@ namespace osu.Game.Collections
PostNotification?.Invoke(notification); PostNotification?.Invoke(notification);
var collection = readCollections(stream, notification); var collection = readCollections(stream, notification);
bool importCompleted = false; await importCollections(collection);
Schedule(() =>
{
importCollections(collection);
importCompleted = true;
});
while (!IsDisposed && !importCompleted)
await Task.Delay(10);
notification.CompletionText = $"Imported {collection.Count} collections"; notification.CompletionText = $"Imported {collection.Count} collections";
notification.State = ProgressNotificationState.Completed; notification.State = ProgressNotificationState.Completed;
} }
private void importCollections(List<BeatmapCollection> newCollections) private Task importCollections(List<BeatmapCollection> newCollections)
{ {
foreach (var newCol in newCollections) var tcs = new TaskCompletionSource<bool>();
{
var existing = Collections.FirstOrDefault(c => c.Name == newCol.Name);
if (existing == null)
Collections.Add(existing = new BeatmapCollection { Name = { Value = newCol.Name.Value } });
foreach (var newBeatmap in newCol.Beatmaps) Schedule(() =>
{
try
{ {
if (!existing.Beatmaps.Contains(newBeatmap)) foreach (var newCol in newCollections)
existing.Beatmaps.Add(newBeatmap); {
var existing = Collections.FirstOrDefault(c => c.Name == newCol.Name);
if (existing == null)
Collections.Add(existing = new BeatmapCollection { Name = { Value = newCol.Name.Value } });
foreach (var newBeatmap in newCol.Beatmaps)
{
if (!existing.Beatmaps.Contains(newBeatmap))
existing.Beatmaps.Add(newBeatmap);
}
}
tcs.SetResult(true);
} }
} catch (Exception e)
{
Logger.Error(e, "Failed to import collection.");
tcs.SetException(e);
}
});
return tcs.Task;
} }
private List<BeatmapCollection> readCollections(Stream stream, ProgressNotification notification = null) private List<BeatmapCollection> readCollections(Stream stream, ProgressNotification notification = null)

View File

@ -15,7 +15,7 @@ namespace osu.Game.Online
/// A <see cref="Container"/> for displaying online content which require a local user to be logged in. /// A <see cref="Container"/> for displaying online content which require a local user to be logged in.
/// Shows its children only when the local user is logged in and supports displaying a placeholder if not. /// Shows its children only when the local user is logged in and supports displaying a placeholder if not.
/// </summary> /// </summary>
public abstract class OnlineViewContainer : Container public class OnlineViewContainer : Container
{ {
protected LoadingSpinner LoadingSpinner { get; private set; } protected LoadingSpinner LoadingSpinner { get; private set; }
@ -30,7 +30,7 @@ namespace osu.Game.Online
[Resolved] [Resolved]
protected IAPIProvider API { get; private set; } protected IAPIProvider API { get; private set; }
protected OnlineViewContainer(string placeholderMessage) public OnlineViewContainer(string placeholderMessage)
{ {
this.placeholderMessage = placeholderMessage; this.placeholderMessage = placeholderMessage;
} }

View File

@ -24,6 +24,7 @@ using osu.Game.Overlays.Chat.Tabs;
using osuTK.Input; using osuTK.Input;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Online;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
@ -118,40 +119,47 @@ namespace osu.Game.Overlays
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
currentChannelContainer = new Container<DrawableChannel> new OnlineViewContainer("Sign in to chat")
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Bottom = textbox_height
},
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = textbox_height,
Padding = new MarginPadding
{
Top = padding * 2,
Bottom = padding * 2,
Left = ChatLine.LEFT_PADDING + padding * 2,
Right = padding * 2,
},
Children = new Drawable[] Children = new Drawable[]
{ {
textbox = new FocusedTextBox currentChannelContainer = new Container<DrawableChannel>
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Height = 1, Padding = new MarginPadding
PlaceholderText = "type your message", {
ReleaseFocusOnCommit = false, Bottom = textbox_height
HoldFocus = true, },
} },
} new Container
}, {
loading = new LoadingSpinner(), Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = textbox_height,
Padding = new MarginPadding
{
Top = padding * 2,
Bottom = padding * 2,
Left = ChatLine.LEFT_PADDING + padding * 2,
Right = padding * 2,
},
Children = new Drawable[]
{
textbox = new FocusedTextBox
{
RelativeSizeAxes = Axes.Both,
Height = 1,
PlaceholderText = "type your message",
ReleaseFocusOnCommit = false,
HoldFocus = true,
}
}
},
loading = new LoadingSpinner(),
},
}
} }
}, },
tabsArea = new TabsArea tabsArea = new TabsArea

View File

@ -168,7 +168,7 @@ namespace osu.Game.Screens.Menu
{ {
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu. // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu.
if (UsingThemedIntro) if (UsingThemedIntro)
Track.Restart(); Track.Start();
} }
protected override void LogoArriving(OsuLogo logo, bool resuming) protected override void LogoArriving(OsuLogo logo, bool resuming)

View File

@ -179,14 +179,15 @@ namespace osu.Game.Screens.Menu
base.OnEntering(last); base.OnEntering(last);
buttons.FadeInFromZero(500); buttons.FadeInFromZero(500);
var metadata = Beatmap.Value.Metadata;
if (last is IntroScreen && musicController.TrackLoaded) if (last is IntroScreen && musicController.TrackLoaded)
{ {
if (!musicController.CurrentTrack.IsRunning) var track = musicController.CurrentTrack;
// presume the track is the current beatmap's track. not sure how correct this assumption is but it has worked until now.
if (!track.IsRunning)
{ {
musicController.CurrentTrack.Seek(metadata.PreviewTime != -1 ? metadata.PreviewTime : 0.4f * musicController.CurrentTrack.Length); Beatmap.Value.PrepareTrackForPreviewLooping();
musicController.CurrentTrack.Start(); track.Restart();
} }
} }

View File

@ -173,9 +173,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
if (track != null) if (track != null)
{ {
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; Beatmap.Value.PrepareTrackForPreviewLooping();
track.Looping = true;
music?.EnsurePlayingSomething(); music?.EnsurePlayingSomething();
} }
} }
@ -185,10 +183,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
var track = Beatmap?.Value?.Track; var track = Beatmap?.Value?.Track;
if (track != null) if (track != null)
{
track.Looping = false; track.Looping = false;
track.RestartPoint = 0;
}
} }
} }
} }

View File

@ -25,8 +25,13 @@ namespace osu.Game.Screens.Select.Carousel
public readonly Bindable<CarouselItemState> State = new Bindable<CarouselItemState>(CarouselItemState.NotSelected); public readonly Bindable<CarouselItemState> State = new Bindable<CarouselItemState>(CarouselItemState.NotSelected);
private readonly HoverLayer hoverLayer;
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
private const float corner_radius = 10;
private const float border_thickness = 2.5f;
public CarouselHeader() public CarouselHeader()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
@ -36,12 +41,12 @@ namespace osu.Game.Screens.Select.Carousel
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Masking = true, Masking = true,
CornerRadius = 10, CornerRadius = corner_radius,
BorderColour = new Color4(221, 255, 255, 255), BorderColour = new Color4(221, 255, 255, 255),
Children = new Drawable[] Children = new Drawable[]
{ {
Content, Content,
new HoverLayer() hoverLayer = new HoverLayer()
} }
}; };
} }
@ -59,6 +64,8 @@ namespace osu.Game.Screens.Select.Carousel
{ {
case CarouselItemState.Collapsed: case CarouselItemState.Collapsed:
case CarouselItemState.NotSelected: case CarouselItemState.NotSelected:
hoverLayer.InsetForBorder = false;
BorderContainer.BorderThickness = 0; BorderContainer.BorderThickness = 0;
BorderContainer.EdgeEffect = new EdgeEffectParameters BorderContainer.EdgeEffect = new EdgeEffectParameters
{ {
@ -70,7 +77,9 @@ namespace osu.Game.Screens.Select.Carousel
break; break;
case CarouselItemState.Selected: case CarouselItemState.Selected:
BorderContainer.BorderThickness = 2.5f; hoverLayer.InsetForBorder = true;
BorderContainer.BorderThickness = border_thickness;
BorderContainer.EdgeEffect = new EdgeEffectParameters BorderContainer.EdgeEffect = new EdgeEffectParameters
{ {
Type = EdgeEffectType.Glow, Type = EdgeEffectType.Glow,
@ -107,6 +116,26 @@ namespace osu.Game.Screens.Select.Carousel
sampleHover = audio.Samples.Get("SongSelect/song-ping"); sampleHover = audio.Samples.Get("SongSelect/song-ping");
} }
public bool InsetForBorder
{
set
{
if (value)
{
// apply same border as above to avoid applying additive overlay to it (and blowing out the colour).
Masking = true;
CornerRadius = corner_radius;
BorderThickness = border_thickness;
}
else
{
BorderThickness = 0;
CornerRadius = 0;
Masking = false;
}
}
}
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)
{ {
box.FadeIn(100, Easing.OutQuint); box.FadeIn(100, Easing.OutQuint);

View File

@ -44,7 +44,7 @@ namespace osu.Game.Screens.Select
Sort = sortMode.Value, Sort = sortMode.Value,
AllowConvertedBeatmaps = showConverted.Value, AllowConvertedBeatmaps = showConverted.Value,
Ruleset = ruleset.Value, Ruleset = ruleset.Value,
Collection = collectionDropdown?.Current.Value.Collection Collection = collectionDropdown?.Current.Value?.Collection
}; };
if (!minimumStars.IsDefault) if (!minimumStars.IsDefault)

View File

@ -648,8 +648,9 @@ namespace osu.Game.Screens.Select
{ {
Debug.Assert(!isHandlingLooping); Debug.Assert(!isHandlingLooping);
music.CurrentTrack.Looping = isHandlingLooping = true; isHandlingLooping = true;
ensureTrackLooping(Beatmap.Value, TrackChangeDirection.None);
music.TrackChanged += ensureTrackLooping; music.TrackChanged += ensureTrackLooping;
} }
@ -665,7 +666,7 @@ namespace osu.Game.Screens.Select
} }
private void ensureTrackLooping(WorkingBeatmap beatmap, TrackChangeDirection changeDirection) private void ensureTrackLooping(WorkingBeatmap beatmap, TrackChangeDirection changeDirection)
=> music.CurrentTrack.Looping = true; => beatmap.PrepareTrackForPreviewLooping();
public override bool OnBackButton() public override bool OnBackButton()
{ {
@ -719,8 +720,6 @@ namespace osu.Game.Screens.Select
bool isNewTrack = !lastTrack.TryGetTarget(out var last) || last != track; bool isNewTrack = !lastTrack.TryGetTarget(out var last) || last != track;
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
if (!track.IsRunning && (music.UserPauseRequested != true || isNewTrack)) if (!track.IsRunning && (music.UserPauseRequested != true || isNewTrack))
music.Play(true); music.Play(true);