diff --git a/osu.Android.props b/osu.Android.props index 7b22b76e0e..5ee0573c58 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ Off True Xamarin.Android.Net.AndroidClientHandler - v8.1 + v9.0 false diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 326d32f7ab..acd21f9587 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -1,6 +1,6 @@  - - + + diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 42a3185cd1..ac3905a372 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -14,6 +14,11 @@ Properties\AndroidManifest.xml armeabi-v7a;x86;arm64-v8a + + cjk;mideast;other;rare;west + d8 + r8 + diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml index b04b0718f5..db95e18f13 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml index c315581606..e6728c801d 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml index dac9c19477..aad907b241 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml index f731042a4c..cd4b74aa16 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Tests.Android/Properties/AndroidManifest.xml index 146f96c2a3..bb996dc5ca 100644 --- a/osu.Game.Tests.Android/Properties/AndroidManifest.xml +++ b/osu.Game.Tests.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 860c7fc0fa..7ef50da7d3 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -82,6 +82,8 @@ namespace osu.Game.Beatmaps protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => new DownloadBeatmapSetRequest(set, minimiseDownloadSize); + protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz"; + protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default) { if (archive != null) @@ -176,20 +178,23 @@ namespace osu.Game.Beatmaps if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; - var cached = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); + lock (workingCache) + { + var cached = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); - if (cached != null) - return cached; + if (cached != null) + return cached; - if (beatmapInfo.Metadata == null) - beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; + if (beatmapInfo.Metadata == null) + beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; - WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager); + WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager); - previous?.TransferTo(working); - workingCache.Add(working); + previous?.TransferTo(working); + workingCache.Add(working); - return working; + return working; + } } /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 01455e7d50..ed65bdc069 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -114,7 +114,8 @@ namespace osu.Game.Database lock (imported) { - imported.Add(model); + if (model != null) + imported.Add(model); current++; notification.Text = $"Imported {current} of {paths.Length} {HumanisedModelName}s"; @@ -140,7 +141,7 @@ namespace osu.Game.Database { notification.CompletionText = imported.Count == 1 ? $"Imported {imported.First()}!" - : $"Imported {current} {HumanisedModelName}s!"; + : $"Imported {imported.Count} {HumanisedModelName}s!"; if (imported.Count > 0 && PresentImport != null) { @@ -176,7 +177,7 @@ namespace osu.Game.Database // TODO: Add a check to prevent files from storage to be deleted. try { - if (import != null && File.Exists(path)) + if (import != null && File.Exists(path) && ShouldDeleteArchive(path)) File.Delete(path); } catch (Exception e) @@ -207,7 +208,7 @@ namespace osu.Game.Database { model = CreateModel(archive); - if (model == null) return null; + if (model == null) return Task.FromResult(null); model.Hash = computeHash(archive); } @@ -498,6 +499,18 @@ namespace osu.Game.Database /// protected virtual string ImportFromStablePath => null; + /// + /// Select paths to import from stable. Default implementation iterates all directories in . + /// + protected virtual IEnumerable GetStableImportPaths(Storage stableStoage) => stableStoage.GetDirectories(ImportFromStablePath); + + /// + /// Whether this specified path should be removed after successful import. + /// + /// The path for consideration. May be a file or a directory. + /// Whether to perform deletion. + protected virtual bool ShouldDeleteArchive(string path) => false; + /// /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. /// @@ -518,7 +531,7 @@ namespace osu.Game.Database return Task.CompletedTask; } - return Task.Run(async () => await Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray())); + return Task.Run(async () => await Import(GetStableImportPaths(GetStableStorage()).Select(f => stable.GetFullPath(f)).ToArray())); } #endregion diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs index 869005d05c..8134cfb42d 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs @@ -37,6 +37,8 @@ namespace osu.Game.Graphics.UserInterface text.Colour = AccentColour; icon.Colour = AccentColour; } + + updateFade(); } } @@ -48,39 +50,6 @@ namespace osu.Game.Graphics.UserInterface private const float transition_length = 500; - private void fadeIn() - { - box.FadeIn(transition_length, Easing.OutQuint); - text.FadeColour(Color4.White, transition_length, Easing.OutQuint); - } - - private void fadeOut() - { - box.FadeOut(transition_length, Easing.OutQuint); - text.FadeColour(AccentColour, transition_length, Easing.OutQuint); - } - - protected override bool OnHover(HoverEvent e) - { - fadeIn(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - if (!Current.Value) - fadeOut(); - - base.OnHoverLost(e); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - if (accentColour == null) - AccentColour = colours.Blue; - } - public OsuTabControlCheckbox() { AutoSizeAxes = Axes.Both; @@ -115,19 +84,34 @@ namespace osu.Game.Graphics.UserInterface } }; - Current.ValueChanged += selected => - { - if (selected.NewValue) - { - fadeIn(); - icon.Icon = FontAwesome.Regular.CheckCircle; - } - else - { - fadeOut(); - icon.Icon = FontAwesome.Regular.Circle; - } - }; + Current.ValueChanged += selected => { icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle; }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (accentColour == null) + AccentColour = colours.Blue; + } + + protected override bool OnHover(HoverEvent e) + { + updateFade(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + if (!Current.Value) + updateFade(); + + base.OnHoverLost(e); + } + + private void updateFade() + { + box.FadeTo(IsHovered ? 1 : 0, transition_length, Easing.OutQuint); + text.FadeColour(IsHovered ? Color4.White : AccentColour, transition_length, Easing.OutQuint); } } } diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index 786afdf450..62d6efcb6f 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -11,7 +11,7 @@ using osu.Game.Online.API; namespace osu.Game.Online { /// - /// A component which tracks a beatmap through potential download/import/deletion. + /// A component which tracks a through potential download/import/deletion. /// public abstract class DownloadTrackingComposite : CompositeDrawable where TModel : class, IEquatable @@ -22,7 +22,7 @@ namespace osu.Game.Online private TModelManager manager; /// - /// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded. + /// Holds the current download state of the , whether is has already been downloaded, is in progress, or is not downloaded. /// protected readonly Bindable State = new Bindable(); diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index b91de93a4a..dea2ff1a21 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -231,12 +231,6 @@ namespace osu.Game.Online.Leaderboards if (getScoresRequest == null) return; - if (api?.IsLoggedIn != true) - { - PlaceholderState = PlaceholderState.NotLoggedIn; - return; - } - getScoresRequest.Failure += e => Schedule(() => { if (e is OperationCanceledException) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7087df83d7..0a472d4dc1 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -387,6 +387,7 @@ namespace osu.Game BeatmapManager.PresentImport = items => PresentBeatmap(items.First()); ScoreManager.PostNotification = n => notifications?.Post(n); + ScoreManager.GetStableStorage = GetStorageForStableInstall; ScoreManager.PresentImport = items => PresentScore(items.First()); Container logoContainer; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 398a091486..832673703b 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Scoring; using osu.Game.Skinning; namespace osu.Game.Overlays.Settings.Sections.Maintenance @@ -16,14 +17,16 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override string Header => "General"; private TriangleButton importBeatmapsButton; + private TriangleButton importScoresButton; private TriangleButton importSkinsButton; - private TriangleButton deleteSkinsButton; private TriangleButton deleteBeatmapsButton; + private TriangleButton deleteScoresButton; + private TriangleButton deleteSkinsButton; private TriangleButton restoreButton; private TriangleButton undeleteButton; [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps, SkinManager skins, DialogOverlay dialogOverlay) + private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, DialogOverlay dialogOverlay) { if (beatmaps.SupportsImportFromStable) { @@ -51,6 +54,32 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); + if (scores.SupportsImportFromStable) + { + Add(importScoresButton = new SettingsButton + { + Text = "Import scores from stable", + Action = () => + { + importScoresButton.Enabled.Value = false; + scores.ImportFromStableAsync().ContinueWith(t => Schedule(() => importScoresButton.Enabled.Value = true)); + } + }); + } + + Add(deleteScoresButton = new DangerousSettingsButton + { + Text = "Delete ALL scores", + Action = () => + { + dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() => + { + deleteScoresButton.Enabled.Value = false; + Task.Run(() => scores.Delete(scores.GetAllUsableScores())).ContinueWith(t => Schedule(() => deleteScoresButton.Enabled.Value = true)); + })); + } + }); + if (skins.SupportsImportFromStable) { Add(importSkinsButton = new SettingsButton diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 2d82987da0..8475158c78 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; @@ -24,7 +25,7 @@ namespace osu.Game.Scoring protected override string[] HashableFileTypes => new[] { ".osr" }; - protected override string ImportFromStablePath => "Replays"; + protected override string ImportFromStablePath => Path.Combine("Data", "r"); private readonly RulesetStore rulesets; private readonly Func beatmaps; @@ -55,6 +56,9 @@ namespace osu.Game.Scoring } } + protected override IEnumerable GetStableImportPaths(Storage stableStorage) + => stableStorage.GetFiles(ImportFromStablePath).Where(p => HandledExtensions.Any(ext => Path.GetExtension(p)?.Equals(ext, StringComparison.InvariantCultureIgnoreCase) ?? false)); + public Score GetScore(ScoreInfo score) => new LegacyDatabasedScore(score, rulesets, beatmaps(), Files.Store); public List GetAllUsableScores() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); @@ -65,6 +69,6 @@ namespace osu.Game.Scoring protected override ArchiveDownloadRequest CreateDownloadRequest(ScoreInfo score, bool minimiseDownload) => new DownloadReplayRequest(score); - protected override bool CheckLocalAvailability(ScoreInfo model, IQueryable items) => items.Any(s => s.OnlineScoreID == model.OnlineScoreID); + protected override bool CheckLocalAvailability(ScoreInfo model, IQueryable items) => items.Any(s => s.OnlineScoreID == model.OnlineScoreID && s.Files.Any()); } } diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index dab5066c52..f6fbcf6498 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -33,13 +33,18 @@ namespace osu.Game.Screens.Menu protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack(); + private readonly BindableDouble exitingVolumeFade = new BindableDouble(1); + + [Resolved] + private AudioManager audio { get; set; } + private Bindable menuVoice; private Bindable menuMusic; private Track track; private WorkingBeatmap introBeatmap; [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) + private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) { menuVoice = config.GetBindable(OsuSetting.MenuVoice); menuMusic = config.GetBindable(OsuSetting.MenuMusic); @@ -161,7 +166,8 @@ namespace osu.Game.Screens.Menu else fadeOutTime = 500; - Scheduler.AddDelayed(this.Exit, fadeOutTime); + audio.AddAdjustment(AdjustableProperty.Volume, exitingVolumeFade); + this.TransformBindableTo(exitingVolumeFade, 0, fadeOutTime).OnComplete(_ => this.Exit()); //don't want to fade out completely else we will stop running updates. Game.FadeTo(0.01f, fadeOutTime); diff --git a/osu.Game/Screens/Play/ReplayDownloadButton.cs b/osu.Game/Screens/Play/ReplayDownloadButton.cs index 748fe8cc90..290e00f287 100644 --- a/osu.Game/Screens/Play/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Play/ReplayDownloadButton.cs @@ -86,11 +86,7 @@ namespace osu.Game.Screens.Play } }, true); - if (replayAvailability == ReplayAvailability.NotAvailable) - { - button.Enabled.Value = false; - button.Alpha = 0.6f; - } + button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable; } private enum ReplayAvailability diff --git a/osu.Game/Screens/Select/EditSongSelect.cs b/osu.Game/Screens/Select/EditSongSelect.cs deleted file mode 100644 index bdf5f905fe..0000000000 --- a/osu.Game/Screens/Select/EditSongSelect.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Screens; - -namespace osu.Game.Screens.Select -{ - public class EditSongSelect : SongSelect - { - protected override bool ShowFooter => false; - - protected override bool OnStart() - { - this.Exit(); - return true; - } - } -} diff --git a/osu.Game/Screens/Select/ImportFromStablePopup.cs b/osu.Game/Screens/Select/ImportFromStablePopup.cs index 54e4c096f6..20494829ae 100644 --- a/osu.Game/Screens/Select/ImportFromStablePopup.cs +++ b/osu.Game/Screens/Select/ImportFromStablePopup.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Select public ImportFromStablePopup(Action importFromStable) { HeaderText = @"You have no beatmaps!"; - BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps (and skins)?"; + BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps, skins and scores?"; Icon = FontAwesome.Solid.Plane; diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 62f93afbbb..68f153a97d 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -62,6 +62,12 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } + if (api?.IsLoggedIn != true) + { + PlaceholderState = PlaceholderState.NotLoggedIn; + return null; + } + if (Beatmap?.OnlineBeatmapID == null) { PlaceholderState = PlaceholderState.Unavailable; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 3581ed5534..bf5857f725 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -35,6 +35,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; +using osu.Game.Scoring; namespace osu.Game.Screens.Select { @@ -215,7 +216,7 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(true)] - private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins) + private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores) { mods.BindTo(Mods); @@ -252,7 +253,7 @@ namespace osu.Game.Screens.Select if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable) dialogOverlay.Push(new ImportFromStablePopup(() => { - Task.Run(beatmaps.ImportFromStableAsync); + Task.Run(beatmaps.ImportFromStableAsync).ContinueWith(_ => scores.ImportFromStableAsync(), TaskContinuationOptions.OnlyOnRanToCompletion); Task.Run(skins.ImportFromStableAsync); })); }); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 73cc47ea47..70abfac501 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Linq.Expressions; using System.Threading; @@ -54,6 +55,8 @@ namespace osu.Game.Skinning }; } + protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osk"; + /// /// Returns a list of all usable s. Includes the special default skin plus all skins from . ///