From 6560dc2d1f579947218d1fa88853775efb0d1792 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Apr 2021 20:46:44 +0900 Subject: [PATCH 01/40] Fix exported replays being wrapped in zip packages --- osu.Game/Database/ArchiveModelManager.cs | 20 +++++++++++++++----- osu.Game/Scoring/ScoreManager.cs | 10 ++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index d809dbcb01..6719351530 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -422,15 +422,25 @@ namespace osu.Game.Database if (retrievedItem == null) throw new ArgumentException("Specified model could not be found", nameof(item)); + using (var outputStream = exportStorage.GetStream($"{getValidFilename(item.ToString())}{HandledExtensions.First()}", FileAccess.Write, FileMode.Create)) + ExportModelTo(retrievedItem, outputStream); + + exportStorage.OpenInNativeExplorer(); + } + + /// + /// Exports an item to the given output stream. + /// + /// The item to export. + /// The output stream to export to. + protected virtual void ExportModelTo(TModel model, Stream outputStream) + { using (var archive = ZipArchive.Create()) { - foreach (var file in retrievedItem.Files) + foreach (var file in model.Files) archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath)); - using (var outputStream = exportStorage.GetStream($"{getValidFilename(item.ToString())}{HandledExtensions.First()}", FileAccess.Write, FileMode.Create)) - archive.SaveTo(outputStream); - - exportStorage.OpenInNativeExplorer(); + archive.SaveTo(outputStream); } } diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index c7ee26c248..9d3b952ada 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -72,6 +72,16 @@ namespace osu.Game.Scoring } } + protected override void ExportModelTo(ScoreInfo model, Stream outputStream) + { + var file = model.Files.SingleOrDefault(); + if (file == null) + return; + + using (var inputStream = Files.Storage.GetStream(file.FileInfo.StoragePath)) + inputStream.CopyTo(outputStream); + } + protected override IEnumerable GetStableImportPaths(Storage storage) => storage.GetFiles(ImportFromStablePath).Where(p => HandledExtensions.Any(ext => Path.GetExtension(p)?.Equals(ext, StringComparison.OrdinalIgnoreCase) ?? false)) .Select(path => storage.GetFullPath(path)); From 213ac88a8b34b7d1caa57183945b501b3c828e1a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Apr 2021 20:52:20 +0900 Subject: [PATCH 02/40] Fix exported scores not being compatible with osu-stable --- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index db7e51e833..56c4e75864 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -95,7 +95,7 @@ namespace osu.Game.Scoring.Legacy foreach (var f in score.Replay.Frames.OfType().Select(f => f.ToLegacy(beatmap))) { - replayData.Append(FormattableString.Invariant($"{f.Time - lastF.Time}|{f.MouseX ?? 0}|{f.MouseY ?? 0}|{(int)f.ButtonState},")); + replayData.Append(FormattableString.Invariant($"{(int)Math.Round(f.Time - lastF.Time)}|{f.MouseX ?? 0}|{f.MouseY ?? 0}|{(int)f.ButtonState},")); lastF = f; } } From 08a232f7fad662384426f21b3131e36828b9de62 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Mon, 26 Apr 2021 20:08:40 +0200 Subject: [PATCH 03/40] Add method to safely refresh DrawableHitObject transforms --- .../Rulesets/Objects/Drawables/DrawableHitObject.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 7739994527..679c86072b 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -14,13 +14,13 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Framework.Threading; using osu.Game.Audio; +using osu.Game.Configuration; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Pooling; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; -using osu.Game.Configuration; -using osu.Game.Rulesets.Objects.Pooling; using osu.Game.Rulesets.UI; +using osu.Game.Skinning; using osuTK.Graphics; namespace osu.Game.Rulesets.Objects.Drawables @@ -429,6 +429,13 @@ namespace osu.Game.Rulesets.Objects.Drawables base.ClearTransformsAfter(double.MinValue, true); } + /// + /// Removes all previously applied transforms, then reapplies a new set of transforms with potentially different parameters. + /// The transforms will use the current , and they will use the appropriate start times. + /// This also takes in account potential overrides defined in . + /// + protected void RefreshStateTransforms() => updateState(State.Value, true); + /// /// Apply (generally fade-in) transforms leading into the start time. /// The local drawable hierarchy is recursively delayed to for convenience. From 9ad30da72943d4bae44dc82e7438d8bb84e14ce0 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 26 Apr 2021 16:41:26 -0400 Subject: [PATCH 04/40] Show a notification if game is run as administrator --- osu.Desktop/OsuGameDesktop.cs | 9 ++++++ osu.Desktop/osu.Desktop.csproj | 1 + osu.Game/Admin/AdminChecker.cs | 57 ++++++++++++++++++++++++++++++++++ osu.Game/OsuGame.cs | 5 +++ 4 files changed, 72 insertions(+) create mode 100644 osu.Game/Admin/AdminChecker.cs diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 0c21c75290..1ce79b3b49 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Versioning; +using System.Security.Principal; using System.Threading.Tasks; using Microsoft.Win32; using osu.Desktop.Overlays; @@ -20,6 +21,7 @@ using osu.Game.Screens.Menu; using osu.Game.Updater; using osu.Desktop.Windows; using osu.Framework.Threading; +using osu.Game.Admin; using osu.Game.IO; namespace osu.Desktop @@ -102,6 +104,8 @@ namespace osu.Desktop } } + protected override AdminChecker CreateAdminChecker() => new DesktopAdminChecker(); + protected override void LoadComplete() { base.LoadComplete(); @@ -180,5 +184,10 @@ namespace osu.Desktop Task.Factory.StartNew(() => Import(paths), TaskCreationOptions.LongRunning); } } + + private class DesktopAdminChecker : AdminChecker + { + protected override bool IsAdmin() => OperatingSystem.IsWindows() ? new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator) : Mono.Unix.Native.Syscall.geteuid() == 0; + } } } diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 3e0f0cb7f6..6bd0f64218 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -25,6 +25,7 @@ + diff --git a/osu.Game/Admin/AdminChecker.cs b/osu.Game/Admin/AdminChecker.cs new file mode 100644 index 0000000000..b81e34653e --- /dev/null +++ b/osu.Game/Admin/AdminChecker.cs @@ -0,0 +1,57 @@ +// 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; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; + +namespace osu.Game.Admin +{ + /// + /// Checks if the game is running with elevated privileges (as admin in Windows, root in Unix) and displays a warning notification if so. + /// + public class AdminChecker : CompositeDrawable + { + [Resolved] + protected NotificationOverlay Notifications { get; private set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + if (IsAdmin()) + Notifications.Post(new AdminNotification()); + } + + protected virtual bool IsAdmin() => false; + + private class AdminNotification : SimpleNotification + { + public override bool IsImportant => true; + + public AdminNotification() + { + bool isUnix = RuntimeInfo.IsUnix; + Text = $"Running osu! as {(isUnix ? "root" : "administrator")} does not improve performance and poses a security risk. Please run the game normally."; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, NotificationOverlay notificationOverlay) + { + Icon = FontAwesome.Solid.ShieldAlt; + IconBackgound.Colour = colours.YellowDark; + + Activated = delegate + { + notificationOverlay.Hide(); + return true; + }; + } + } + } + + +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 28f32ba455..358e85b247 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -28,6 +28,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Threading; +using osu.Game.Admin; using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.Graphics; @@ -450,6 +451,8 @@ namespace osu.Game protected virtual UpdateManager CreateUpdateManager() => new UpdateManager(); + protected virtual AdminChecker CreateAdminChecker() => new AdminChecker(); + protected override Container CreateScalingContainer() => new ScalingContainer(ScalingMode.Everything); #region Beatmap progression @@ -673,6 +676,8 @@ namespace osu.Game // dependency on notification overlay, dependent by settings overlay loadComponentSingleFile(CreateUpdateManager(), Add, true); + loadComponentSingleFile(CreateAdminChecker(), Add, false); + // overlay elements loadComponentSingleFile(new ManageCollectionsDialog(), overlayContent.Add, true); loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true); From 260dd06f4707f378ae2fd7a6fda4852d6160105a Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 26 Apr 2021 17:41:04 -0400 Subject: [PATCH 05/40] Move AdminChecker to osu.Desktop.Admin --- {osu.Game => osu.Desktop}/Admin/AdminChecker.cs | 13 ++++++------- osu.Desktop/OsuGameDesktop.cs | 12 +++--------- osu.Game/OsuGame.cs | 5 ----- 3 files changed, 9 insertions(+), 21 deletions(-) rename {osu.Game => osu.Desktop}/Admin/AdminChecker.cs (75%) diff --git a/osu.Game/Admin/AdminChecker.cs b/osu.Desktop/Admin/AdminChecker.cs similarity index 75% rename from osu.Game/Admin/AdminChecker.cs rename to osu.Desktop/Admin/AdminChecker.cs index b81e34653e..b9f0d68694 100644 --- a/osu.Game/Admin/AdminChecker.cs +++ b/osu.Desktop/Admin/AdminChecker.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Security.Principal; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; @@ -9,7 +11,7 @@ using osu.Game.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; -namespace osu.Game.Admin +namespace osu.Desktop.Admin { /// /// Checks if the game is running with elevated privileges (as admin in Windows, root in Unix) and displays a warning notification if so. @@ -22,11 +24,11 @@ namespace osu.Game.Admin protected override void LoadComplete() { base.LoadComplete(); - if (IsAdmin()) + if (isAdmin()) Notifications.Post(new AdminNotification()); } - protected virtual bool IsAdmin() => false; + private bool isAdmin() => OperatingSystem.IsWindows() ? new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator) : Mono.Unix.Native.Syscall.geteuid() == 0; private class AdminNotification : SimpleNotification { @@ -34,8 +36,7 @@ namespace osu.Game.Admin public AdminNotification() { - bool isUnix = RuntimeInfo.IsUnix; - Text = $"Running osu! as {(isUnix ? "root" : "administrator")} does not improve performance and poses a security risk. Please run the game normally."; + Text = $"Running osu! as {(RuntimeInfo.IsUnix ? "root" : "administrator")} does not improve performance and poses a security risk. Please run the game normally."; } [BackgroundDependencyLoader] @@ -52,6 +53,4 @@ namespace osu.Game.Admin } } } - - } diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 1ce79b3b49..77f968e1b6 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -7,9 +7,9 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Versioning; -using System.Security.Principal; using System.Threading.Tasks; using Microsoft.Win32; +using osu.Desktop.Admin; using osu.Desktop.Overlays; using osu.Framework.Platform; using osu.Game; @@ -21,7 +21,6 @@ using osu.Game.Screens.Menu; using osu.Game.Updater; using osu.Desktop.Windows; using osu.Framework.Threading; -using osu.Game.Admin; using osu.Game.IO; namespace osu.Desktop @@ -104,8 +103,6 @@ namespace osu.Desktop } } - protected override AdminChecker CreateAdminChecker() => new DesktopAdminChecker(); - protected override void LoadComplete() { base.LoadComplete(); @@ -117,6 +114,8 @@ namespace osu.Desktop if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows) LoadComponentAsync(new GameplayWinKeyBlocker(), Add); + + LoadComponentAsync(new AdminChecker(), Add); } protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen) @@ -184,10 +183,5 @@ namespace osu.Desktop Task.Factory.StartNew(() => Import(paths), TaskCreationOptions.LongRunning); } } - - private class DesktopAdminChecker : AdminChecker - { - protected override bool IsAdmin() => OperatingSystem.IsWindows() ? new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator) : Mono.Unix.Native.Syscall.geteuid() == 0; - } } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 358e85b247..28f32ba455 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -28,7 +28,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Threading; -using osu.Game.Admin; using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.Graphics; @@ -451,8 +450,6 @@ namespace osu.Game protected virtual UpdateManager CreateUpdateManager() => new UpdateManager(); - protected virtual AdminChecker CreateAdminChecker() => new AdminChecker(); - protected override Container CreateScalingContainer() => new ScalingContainer(ScalingMode.Everything); #region Beatmap progression @@ -676,8 +673,6 @@ namespace osu.Game // dependency on notification overlay, dependent by settings overlay loadComponentSingleFile(CreateUpdateManager(), Add, true); - loadComponentSingleFile(CreateAdminChecker(), Add, false); - // overlay elements loadComponentSingleFile(new ManageCollectionsDialog(), overlayContent.Add, true); loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true); From c3bad1d4c599e43846c8f0e596b370bc082ef5ed Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 26 Apr 2021 21:05:18 -0400 Subject: [PATCH 06/40] Rename AdminChecker to ElevatedPrivilegesChecker, refactor elevated check --- ...hecker.cs => ElevatedPrivilegesChecker.cs} | 30 ++++++++++++++----- osu.Desktop/OsuGameDesktop.cs | 2 +- 2 files changed, 23 insertions(+), 9 deletions(-) rename osu.Desktop/Admin/{AdminChecker.cs => ElevatedPrivilegesChecker.cs} (64%) diff --git a/osu.Desktop/Admin/AdminChecker.cs b/osu.Desktop/Admin/ElevatedPrivilegesChecker.cs similarity index 64% rename from osu.Desktop/Admin/AdminChecker.cs rename to osu.Desktop/Admin/ElevatedPrivilegesChecker.cs index b9f0d68694..ea4aab0f85 100644 --- a/osu.Desktop/Admin/AdminChecker.cs +++ b/osu.Desktop/Admin/ElevatedPrivilegesChecker.cs @@ -5,7 +5,7 @@ using System; using System.Security.Principal; using osu.Framework; using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Overlays; @@ -16,7 +16,7 @@ namespace osu.Desktop.Admin /// /// Checks if the game is running with elevated privileges (as admin in Windows, root in Unix) and displays a warning notification if so. /// - public class AdminChecker : CompositeDrawable + public class ElevatedPrivilegesChecker : Component { [Resolved] protected NotificationOverlay Notifications { get; private set; } @@ -24,17 +24,31 @@ namespace osu.Desktop.Admin protected override void LoadComplete() { base.LoadComplete(); - if (isAdmin()) - Notifications.Post(new AdminNotification()); + + bool elevated = false; + + if (OperatingSystem.IsWindows()) + { + var windowsIdentity = WindowsIdentity.GetCurrent(); + var windowsPrincipal = new WindowsPrincipal(windowsIdentity); + elevated = windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator); + } + else if (RuntimeInfo.IsUnix) + { + elevated = Mono.Unix.Native.Syscall.geteuid() == 0; + } + + if (!elevated) + return; + + Notifications.Post(new ElevatedPrivilegesNotification()); } - private bool isAdmin() => OperatingSystem.IsWindows() ? new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator) : Mono.Unix.Native.Syscall.geteuid() == 0; - - private class AdminNotification : SimpleNotification + private class ElevatedPrivilegesNotification : SimpleNotification { public override bool IsImportant => true; - public AdminNotification() + public ElevatedPrivilegesNotification() { Text = $"Running osu! as {(RuntimeInfo.IsUnix ? "root" : "administrator")} does not improve performance and poses a security risk. Please run the game normally."; } diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 77f968e1b6..9f7854779e 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -115,7 +115,7 @@ namespace osu.Desktop if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows) LoadComponentAsync(new GameplayWinKeyBlocker(), Add); - LoadComponentAsync(new AdminChecker(), Add); + LoadComponentAsync(new ElevatedPrivilegesChecker(), Add); } protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen) From a2723f3f579565b2aca0e1c6159362838c9c559b Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 26 Apr 2021 22:37:08 -0400 Subject: [PATCH 07/40] Perform elevated check asynchronously, use a separate function w/ switch statement --- osu.Desktop/OsuGameDesktop.cs | 2 +- .../ElevatedPrivilegesChecker.cs | 48 +++++++++++++------ osu.Desktop/osu.Desktop.csproj | 2 +- 3 files changed, 35 insertions(+), 17 deletions(-) rename osu.Desktop/{Admin => Security}/ElevatedPrivilegesChecker.cs (66%) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 9f7854779e..4a28ab3722 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -9,7 +9,7 @@ using System.Reflection; using System.Runtime.Versioning; using System.Threading.Tasks; using Microsoft.Win32; -using osu.Desktop.Admin; +using osu.Desktop.Security; using osu.Desktop.Overlays; using osu.Framework.Platform; using osu.Game; diff --git a/osu.Desktop/Admin/ElevatedPrivilegesChecker.cs b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs similarity index 66% rename from osu.Desktop/Admin/ElevatedPrivilegesChecker.cs rename to osu.Desktop/Security/ElevatedPrivilegesChecker.cs index ea4aab0f85..a719dbf952 100644 --- a/osu.Desktop/Admin/ElevatedPrivilegesChecker.cs +++ b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; -namespace osu.Desktop.Admin +namespace osu.Desktop.Security { /// /// Checks if the game is running with elevated privileges (as admin in Windows, root in Unix) and displays a warning notification if so. @@ -21,36 +21,54 @@ namespace osu.Desktop.Admin [Resolved] protected NotificationOverlay Notifications { get; private set; } + private bool elevated; + protected override void LoadComplete() { base.LoadComplete(); - bool elevated = false; - - if (OperatingSystem.IsWindows()) - { - var windowsIdentity = WindowsIdentity.GetCurrent(); - var windowsPrincipal = new WindowsPrincipal(windowsIdentity); - elevated = windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator); - } - else if (RuntimeInfo.IsUnix) - { - elevated = Mono.Unix.Native.Syscall.geteuid() == 0; - } - if (!elevated) return; Notifications.Post(new ElevatedPrivilegesNotification()); } + [BackgroundDependencyLoader] + private void load() + { + elevated = isElevated(); + } + + private bool isElevated() + { + switch (RuntimeInfo.OS) + { + case RuntimeInfo.Platform.Windows: + { + if (!OperatingSystem.IsWindows()) return false; + + var windowsIdentity = WindowsIdentity.GetCurrent(); + var windowsPrincipal = new WindowsPrincipal(windowsIdentity); + + return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator); + } + + case RuntimeInfo.Platform.macOS: + case RuntimeInfo.Platform.Linux: + return Mono.Unix.Native.Syscall.geteuid() == 0; + + default: + return false; + } + } + private class ElevatedPrivilegesNotification : SimpleNotification { public override bool IsImportant => true; public ElevatedPrivilegesNotification() { - Text = $"Running osu! as {(RuntimeInfo.IsUnix ? "root" : "administrator")} does not improve performance and poses a security risk. Please run the game normally."; + Text = $"Running osu! as {(RuntimeInfo.IsUnix ? "root" : "administrator")} does not improve performance and poses a security risk. Please run the game as a normal user."; } [BackgroundDependencyLoader] diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 6bd0f64218..ad5c323e9b 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -25,7 +25,7 @@ - + From e0f54f58424f6fc77d79fe12ef6f6a511e4b6f71 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 26 Apr 2021 22:51:03 -0400 Subject: [PATCH 08/40] Move load() before LoadComplete() --- osu.Desktop/Security/ElevatedPrivilegesChecker.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs index a719dbf952..5fb8263516 100644 --- a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs +++ b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs @@ -23,6 +23,12 @@ namespace osu.Desktop.Security private bool elevated; + [BackgroundDependencyLoader] + private void load() + { + elevated = isElevated(); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -33,12 +39,6 @@ namespace osu.Desktop.Security Notifications.Post(new ElevatedPrivilegesNotification()); } - [BackgroundDependencyLoader] - private void load() - { - elevated = isElevated(); - } - private bool isElevated() { switch (RuntimeInfo.OS) From 5a3fbef5ace66e56b2f5312aacf74a4e2ff26793 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Tue, 27 Apr 2021 00:23:08 -0400 Subject: [PATCH 09/40] Use a try-catch, notification activation does nothing --- .../Security/ElevatedPrivilegesChecker.cs | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs index 5fb8263516..47705eb929 100644 --- a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs +++ b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs @@ -19,7 +19,7 @@ namespace osu.Desktop.Security public class ElevatedPrivilegesChecker : Component { [Resolved] - protected NotificationOverlay Notifications { get; private set; } + private NotificationOverlay notifications { get; set; } private bool elevated; @@ -33,32 +33,35 @@ namespace osu.Desktop.Security { base.LoadComplete(); - if (!elevated) - return; - - Notifications.Post(new ElevatedPrivilegesNotification()); + if (elevated) + notifications.Post(new ElevatedPrivilegesNotification()); } private bool isElevated() { - switch (RuntimeInfo.OS) + try { - case RuntimeInfo.Platform.Windows: + switch (RuntimeInfo.OS) { - if (!OperatingSystem.IsWindows()) return false; + case RuntimeInfo.Platform.Windows: + if (!OperatingSystem.IsWindows()) return false; - var windowsIdentity = WindowsIdentity.GetCurrent(); - var windowsPrincipal = new WindowsPrincipal(windowsIdentity); + var windowsIdentity = WindowsIdentity.GetCurrent(); + var windowsPrincipal = new WindowsPrincipal(windowsIdentity); - return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator); + return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator); + + case RuntimeInfo.Platform.macOS: + case RuntimeInfo.Platform.Linux: + return Mono.Unix.Native.Syscall.geteuid() == 0; + + default: + return false; } - - case RuntimeInfo.Platform.macOS: - case RuntimeInfo.Platform.Linux: - return Mono.Unix.Native.Syscall.geteuid() == 0; - - default: - return false; + } + catch + { + return false; } } @@ -77,11 +80,7 @@ namespace osu.Desktop.Security Icon = FontAwesome.Solid.ShieldAlt; IconBackgound.Colour = colours.YellowDark; - Activated = delegate - { - notificationOverlay.Hide(); - return true; - }; + Activated = () => true; } } } From 7980d16b4c202bcb063c0569112ad76463e8a9c6 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Apr 2021 14:26:12 +0900 Subject: [PATCH 10/40] Add failing test showing the issue of DHO lifetime --- .../Gameplay/TestSceneDrawableHitObject.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs new file mode 100644 index 0000000000..d42802db91 --- /dev/null +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -0,0 +1,62 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.Gameplay +{ + [HeadlessTest] + public class TestSceneDrawableHitObject : OsuTestScene + { + [Test] + public void TestEntryLifetime() + { + TestDrawableHitObject dho = null; + var initialHitObject = new HitObject + { + StartTime = 1000 + }; + var entry = new TestLifetimeEntry(new HitObject + { + StartTime = 2000 + }); + + AddStep("Create DHO", () => Child = dho = new TestDrawableHitObject(initialHitObject)); + + AddAssert("Correct initial lifetime", () => dho.LifetimeStart == initialHitObject.StartTime - TestDrawableHitObject.INITIAL_LIFETIME_OFFSET); + + AddStep("Apply entry", () => dho.Apply(entry)); + + AddAssert("Correct initial lifetime", () => dho.LifetimeStart == entry.HitObject.StartTime - TestLifetimeEntry.INITIAL_LIFETIME_OFFSET); + + AddStep("Set lifetime", () => dho.LifetimeEnd = 3000); + AddAssert("Entry lifetime is updated", () => entry.LifetimeEnd == 3000); + } + + private class TestDrawableHitObject : DrawableHitObject + { + public const double INITIAL_LIFETIME_OFFSET = 100; + protected override double InitialLifetimeOffset => INITIAL_LIFETIME_OFFSET; + + public TestDrawableHitObject(HitObject hitObject) + : base(hitObject) + { + } + } + + private class TestLifetimeEntry : HitObjectLifetimeEntry + { + public const double INITIAL_LIFETIME_OFFSET = 200; + protected override double InitialLifetimeOffset => INITIAL_LIFETIME_OFFSET; + + public TestLifetimeEntry(HitObject hitObject) + : base(hitObject) + { + } + } + } +} From 2303d108bb56b8214c788c18b59424d3fe69858e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Apr 2021 14:35:14 +0900 Subject: [PATCH 11/40] Simplify false return path --- osu.Desktop/Security/ElevatedPrivilegesChecker.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs index 47705eb929..92885ad855 100644 --- a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs +++ b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs @@ -54,15 +54,13 @@ namespace osu.Desktop.Security case RuntimeInfo.Platform.macOS: case RuntimeInfo.Platform.Linux: return Mono.Unix.Native.Syscall.geteuid() == 0; - - default: - return false; } } catch { - return false; } + + return false; } private class ElevatedPrivilegesNotification : SimpleNotification From 13de571b3c690b2def91c4331b3b70bff00c4719 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Apr 2021 14:35:57 +0900 Subject: [PATCH 12/40] Rename private method --- osu.Desktop/Security/ElevatedPrivilegesChecker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs index 92885ad855..82e7d2a141 100644 --- a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs +++ b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs @@ -26,7 +26,7 @@ namespace osu.Desktop.Security [BackgroundDependencyLoader] private void load() { - elevated = isElevated(); + elevated = checkElevated(); } protected override void LoadComplete() @@ -37,7 +37,7 @@ namespace osu.Desktop.Security notifications.Post(new ElevatedPrivilegesNotification()); } - private bool isElevated() + private bool checkElevated() { try { From 2673cd3d9935025232453d9b371d516bc6b7de8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Apr 2021 14:36:11 +0900 Subject: [PATCH 13/40] Remove unnecessary noop action --- osu.Desktop/Security/ElevatedPrivilegesChecker.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs index 82e7d2a141..edd4906421 100644 --- a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs +++ b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs @@ -77,8 +77,6 @@ namespace osu.Desktop.Security { Icon = FontAwesome.Solid.ShieldAlt; IconBackgound.Colour = colours.YellowDark; - - Activated = () => true; } } } From dbcb1259e2c07a726b8be0b1d12aca362c43df7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Apr 2021 14:38:19 +0900 Subject: [PATCH 14/40] Add a note about elevated privileges also breaking integrations --- osu.Desktop/Security/ElevatedPrivilegesChecker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs index edd4906421..01458b4c37 100644 --- a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs +++ b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs @@ -69,7 +69,7 @@ namespace osu.Desktop.Security public ElevatedPrivilegesNotification() { - Text = $"Running osu! as {(RuntimeInfo.IsUnix ? "root" : "administrator")} does not improve performance and poses a security risk. Please run the game as a normal user."; + Text = $"Running osu! as {(RuntimeInfo.IsUnix ? "root" : "administrator")} does not improve performance, may break integrations and poses a security risk. Please run the game as a normal user."; } [BackgroundDependencyLoader] From a2c0951d94102cee2660e65412ffb4c4850114f1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Apr 2021 15:23:33 +0900 Subject: [PATCH 15/40] Use overriding instead of hiding in HitObjectLifetimeEntry Hidden properties are used when the type is the base class. It caused issues when `DrawableHitObject` logic is factored out to `PoolableDrawableWithLifetime` because it is using the base `LifetimeEntry`, not `HitObjectLifetimeEntry`. --- .../Objects/Drawables/DrawableHitObject.cs | 5 ++- .../Objects/HitObjectLifetimeEntry.cs | 41 +++++-------------- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 7739994527..6431ec8980 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Primitives; using osu.Framework.Threading; using osu.Game.Audio; @@ -431,7 +432,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Apply (generally fade-in) transforms leading into the start time. - /// The local drawable hierarchy is recursively delayed to for convenience. + /// The local drawable hierarchy is recursively delayed to for convenience. /// /// By default this will fade in the object from zero with no duration. /// @@ -613,7 +614,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// This is only used as an optimisation to delay the initial update of this and may be tuned more aggressively if required. /// It is indirectly used to decide the automatic transform offset provided to . - /// A more accurate should be set for further optimisation (in , for example). + /// A more accurate should be set for further optimisation (in , for example). /// /// Only has an effect if this is not being pooled. /// For pooled s, use instead. diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index 1954d7e6d2..23e991d00e 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -38,40 +38,19 @@ namespace osu.Game.Rulesets.Objects startTimeBindable.BindValueChanged(onStartTimeChanged, true); } - // The lifetime start, as set by the hitobject. + // The lifetime, as set by the hitobject. private double realLifetimeStart = double.MinValue; - - /// - /// The time at which the should become alive. - /// - public new double LifetimeStart - { - get => realLifetimeStart; - set => setLifetime(realLifetimeStart = value, LifetimeEnd); - } - - // The lifetime end, as set by the hitobject. private double realLifetimeEnd = double.MaxValue; - /// - /// The time at which the should become dead. - /// - public new double LifetimeEnd + public override void SetLifetime(double start, double end) { - get => realLifetimeEnd; - set => setLifetime(LifetimeStart, realLifetimeEnd = value); - } + realLifetimeStart = start; + realLifetimeEnd = end; - private void setLifetime(double start, double end) - { if (keepAlive) - { - start = double.MinValue; - end = double.MaxValue; - } - - base.LifetimeStart = start; - base.LifetimeEnd = end; + base.SetLifetime(double.MinValue, double.MaxValue); + else + base.SetLifetime(start, end); } private bool keepAlive; @@ -87,7 +66,7 @@ namespace osu.Game.Rulesets.Objects return; keepAlive = value; - setLifetime(realLifetimeStart, realLifetimeEnd); + SetLifetime(realLifetimeStart, realLifetimeEnd); } } @@ -98,12 +77,12 @@ namespace osu.Game.Rulesets.Objects /// /// This is only used as an optimisation to delay the initial update of the and may be tuned more aggressively if required. /// It is indirectly used to decide the automatic transform offset provided to . - /// A more accurate should be set for further optimisation (in , for example). + /// A more accurate should be set for further optimisation (in , for example). /// protected virtual double InitialLifetimeOffset => 10000; /// - /// Resets according to the change in start time of the . + /// Resets according to the change in start time of the . /// private void onStartTimeChanged(ValueChangedEvent startTime) => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; } From c9e6ca537896fd7a099d2b4a4338b1ecfcda9001 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Apr 2021 15:24:52 +0900 Subject: [PATCH 16/40] Use now-public Entry.SetLifetime method --- .../Objects/Pooling/PoolableDrawableWithLifetime.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index 93e476be76..049ff9d446 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -100,11 +100,7 @@ namespace osu.Game.Rulesets.Objects.Pooling base.LifetimeStart = start; base.LifetimeEnd = end; - if (Entry != null) - { - Entry.LifetimeStart = start; - Entry.LifetimeEnd = end; - } + Entry?.SetLifetime(start, end); } private void free() From 3899e500d3edf2b15622ed4e5ace339ab68fbdc0 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Apr 2021 17:54:18 +0900 Subject: [PATCH 17/40] Adopt framework change of LifetimeEntry Override SetLifetimeStart/SetLifetimeEnd separately to track individual assignment. It is necessary to ensure real lifetime is not lost when lifetime is partially updated. --- .../Rulesets/Objects/HitObjectLifetimeEntry.cs | 15 +++++++++++++-- .../Pooling/PoolableDrawableWithLifetime.cs | 6 +++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index 23e991d00e..0181d6241b 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -42,11 +42,22 @@ namespace osu.Game.Rulesets.Objects private double realLifetimeStart = double.MinValue; private double realLifetimeEnd = double.MaxValue; - public override void SetLifetime(double start, double end) + // This method is called even if `start == LifetimeStart` when `KeepAlive` is true (necessary to update `realLifetimeStart`). + protected override void SetLifetimeStart(double start) { + // This assignment cannot be done in `SetLifetime` because otherwise setting only `LifetimeStart` will make `realLifetimeEnd` to be lost. realLifetimeStart = start; - realLifetimeEnd = end; + base.SetLifetimeStart(start); + } + protected override void SetLifetimeEnd(double end) + { + realLifetimeEnd = end; + base.SetLifetimeEnd(end); + } + + protected override void SetLifetime(double start, double end) + { if (keepAlive) base.SetLifetime(double.MinValue, double.MaxValue); else diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index 049ff9d446..93e476be76 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -100,7 +100,11 @@ namespace osu.Game.Rulesets.Objects.Pooling base.LifetimeStart = start; base.LifetimeEnd = end; - Entry?.SetLifetime(start, end); + if (Entry != null) + { + Entry.LifetimeStart = start; + Entry.LifetimeEnd = end; + } } private void free() From 003553aba32d5af6a9decf2a7f2b8c307857ac9b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Apr 2021 18:10:44 +0900 Subject: [PATCH 18/40] Add test of HitObjectLifetimeEntry.KeepAlive behavior --- .../Gameplay/TestSceneDrawableHitObject.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index d42802db91..2e3f192f1b 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -37,6 +37,38 @@ namespace osu.Game.Tests.Gameplay AddAssert("Entry lifetime is updated", () => entry.LifetimeEnd == 3000); } + [Test] + public void TestKeepAlive() + { + TestDrawableHitObject dho = null; + TestLifetimeEntry entry = null; + AddStep("Create DHO", () => + { + dho = new TestDrawableHitObject(null); + dho.Apply(entry = new TestLifetimeEntry(new HitObject()) + { + LifetimeStart = 0, + LifetimeEnd = 1000, + }); + Child = dho; + }); + + AddStep("KeepAlive = true", () => entry.KeepAlive = true); + AddAssert("Lifetime is overriden", () => entry.LifetimeStart == double.MinValue && entry.LifetimeEnd == double.MaxValue); + + AddStep("Set LifetimeStart", () => dho.LifetimeStart = 500); + AddStep("KeepAlive = false", () => entry.KeepAlive = false); + AddAssert("Lifetime is correct", () => entry.LifetimeStart == 500 && entry.LifetimeEnd == 1000); + + AddStep("Set LifetimeStart while KeepAlive", () => + { + entry.KeepAlive = true; + dho.LifetimeStart = double.MinValue; + entry.KeepAlive = false; + }); + AddAssert("Lifetime is changed", () => entry.LifetimeStart == double.MinValue && entry.LifetimeEnd == 1000); + } + private class TestDrawableHitObject : DrawableHitObject { public const double INITIAL_LIFETIME_OFFSET = 100; From 3ea55314f2f11c53dc2232e4152beacf7dc7f779 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Tue, 27 Apr 2021 11:29:16 +0200 Subject: [PATCH 19/40] Update osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs Co-authored-by: Dan Balasescu --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 679c86072b..e715e4aac6 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -430,9 +430,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } /// - /// Removes all previously applied transforms, then reapplies a new set of transforms with potentially different parameters. - /// The transforms will use the current , and they will use the appropriate start times. - /// This also takes in account potential overrides defined in . + /// Reapplies the current . /// protected void RefreshStateTransforms() => updateState(State.Value, true); From b87446a5778ee76c96f02cd163beebdc44476e24 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Apr 2021 19:37:01 +0900 Subject: [PATCH 20/40] Simplify HitObjectLifetimeEntry logic a bit --- .../Objects/HitObjectLifetimeEntry.cs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index 0181d6241b..0d1eb68f07 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -45,23 +45,16 @@ namespace osu.Game.Rulesets.Objects // This method is called even if `start == LifetimeStart` when `KeepAlive` is true (necessary to update `realLifetimeStart`). protected override void SetLifetimeStart(double start) { - // This assignment cannot be done in `SetLifetime` because otherwise setting only `LifetimeStart` will make `realLifetimeEnd` to be lost. realLifetimeStart = start; - base.SetLifetimeStart(start); + if (!keepAlive) + base.SetLifetimeStart(start); } protected override void SetLifetimeEnd(double end) { realLifetimeEnd = end; - base.SetLifetimeEnd(end); - } - - protected override void SetLifetime(double start, double end) - { - if (keepAlive) - base.SetLifetime(double.MinValue, double.MaxValue); - else - base.SetLifetime(start, end); + if (!keepAlive) + base.SetLifetimeEnd(end); } private bool keepAlive; @@ -77,7 +70,10 @@ namespace osu.Game.Rulesets.Objects return; keepAlive = value; - SetLifetime(realLifetimeStart, realLifetimeEnd); + if (keepAlive) + SetLifetime(double.MinValue, double.MaxValue); + else + SetLifetime(realLifetimeStart, realLifetimeEnd); } } From 5dadfd04e7c11f0e69982401fe5647ba67621067 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Apr 2021 22:36:25 +0900 Subject: [PATCH 21/40] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index e0b07549f4..5aee9e15cc 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9a43d5f031..986bd8e7ba 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 2c41b3ef26..c32109d6db 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 3b04aed491abef2278c34f497ead995a839c6f52 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Apr 2021 22:31:23 +0900 Subject: [PATCH 22/40] Add failing test --- .../Rulesets/Scoring/ScoreProcessorTest.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index 20fa0732b9..2c862fe8a8 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -287,6 +287,23 @@ namespace osu.Game.Tests.Rulesets.Scoring Assert.AreEqual(expectedReturnValue, hitResult.IsScorable()); } + [TestCase(HitResult.Perfect, 1_000_000)] + [TestCase(HitResult.SmallTickHit, 1_000_000)] + [TestCase(HitResult.LargeTickHit, 1_000_000)] + [TestCase(HitResult.SmallBonus, 700_000 + Judgement.SMALL_BONUS_SCORE)] + [TestCase(HitResult.LargeBonus, 700_000 + Judgement.LARGE_BONUS_SCORE)] + public void TestGetScoreWithExternalStatistics(HitResult result, int expectedScore) + { + var statistic = new Dictionary { { result, 1 } }; + + scoreProcessor.ApplyBeatmap(new Beatmap + { + HitObjects = { new TestHitObject(result) } + }); + + Assert.That(scoreProcessor.GetImmediateScore(ScoringMode.Standardised, result.AffectsCombo() ? 1 : 0, statistic), Is.EqualTo(expectedScore).Within(0.5d)); + } + private class TestJudgement : Judgement { public override HitResult MaxResult { get; } From 1281993f1f9b106c6e77c05cf59b96e85590feda Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Apr 2021 22:35:31 +0900 Subject: [PATCH 23/40] Fix bonus score not calculated from the correct statistics --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 0fb5c2f4b5..a16a6a6c9b 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -252,7 +252,7 @@ namespace osu.Game.Rulesets.Scoring computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value; } - return GetScore(mode, maxAchievableCombo, calculateAccuracyRatio(computedBaseScore), calculateComboRatio(maxCombo), scoreResultCounts); + return GetScore(mode, maxAchievableCombo, calculateAccuracyRatio(computedBaseScore), calculateComboRatio(maxCombo), statistics); } /// From baa6e845aa812d91a15c655b459de4fbaa203c09 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Apr 2021 22:38:37 +0900 Subject: [PATCH 24/40] Change to fluent assertions --- osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index 2c862fe8a8..ac26f4723e 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; @@ -51,7 +50,7 @@ namespace osu.Game.Tests.Rulesets.Scoring }; scoreProcessor.ApplyResult(judgementResult); - Assert.IsTrue(Precision.AlmostEquals(expectedScore, scoreProcessor.TotalScore.Value)); + Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d)); } /// @@ -118,7 +117,7 @@ namespace osu.Game.Tests.Rulesets.Scoring scoreProcessor.ApplyResult(judgementResult); } - Assert.IsTrue(Precision.AlmostEquals(expectedScore, scoreProcessor.TotalScore.Value, 0.5)); + Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d)); } /// @@ -158,7 +157,7 @@ namespace osu.Game.Tests.Rulesets.Scoring }; scoreProcessor.ApplyResult(lastJudgementResult); - Assert.IsTrue(Precision.AlmostEquals(expectedScore, scoreProcessor.TotalScore.Value, 0.5)); + Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d)); } [Test] @@ -169,7 +168,7 @@ namespace osu.Game.Tests.Rulesets.Scoring scoreProcessor.Mode.Value = scoringMode; scoreProcessor.ApplyBeatmap(new TestBeatmap(new RulesetInfo())); - Assert.IsTrue(Precision.AlmostEquals(0, scoreProcessor.TotalScore.Value)); + Assert.That(scoreProcessor.TotalScore.Value, Is.Zero); } [TestCase(HitResult.IgnoreHit, HitResult.IgnoreMiss)] From 4e3ee773968e52788ccf73bca74fe883d72da113 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 28 Apr 2021 02:46:34 +0900 Subject: [PATCH 25/40] Add support for custom controls to SettingSourceAttribute --- .../Mods/SettingsSourceAttributeTest.cs | 29 +++++++++++++++++++ .../Configuration/SettingSourceAttribute.cs | 29 ++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Mods/SettingsSourceAttributeTest.cs b/osu.Game.Tests/Mods/SettingsSourceAttributeTest.cs index 883c9d1ac2..dd105787fa 100644 --- a/osu.Game.Tests/Mods/SettingsSourceAttributeTest.cs +++ b/osu.Game.Tests/Mods/SettingsSourceAttributeTest.cs @@ -4,7 +4,10 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; using osu.Game.Configuration; +using osu.Game.Overlays.Settings; namespace osu.Game.Tests.Mods { @@ -26,6 +29,16 @@ namespace osu.Game.Tests.Mods Assert.That(orderedSettings[3].Item2.Name, Is.EqualTo(nameof(ClassWithSettings.UnorderedSetting))); } + [Test] + public void TestCustomControl() + { + var objectWithCustomSettingControl = new ClassWithCustomSettingControl(); + var settings = objectWithCustomSettingControl.CreateSettingsControls().ToArray(); + + Assert.That(settings, Has.Length.EqualTo(1)); + Assert.That(settings[0], Is.TypeOf()); + } + private class ClassWithSettings { [SettingSource("Unordered setting", "Should be last")] @@ -40,5 +53,21 @@ namespace osu.Game.Tests.Mods [SettingSource("Third setting", "Yet another description", 3)] public BindableInt ThirdSetting { get; set; } = new BindableInt(); } + + private class ClassWithCustomSettingControl + { + [SettingSource("Custom setting", "Should be a custom control", SettingControlType = typeof(CustomSettingsControl))] + public BindableInt UnorderedSetting { get; set; } = new BindableInt(); + } + + private class CustomSettingsControl : SettingsItem + { + protected override Drawable CreateControl() => new CustomControl(); + + private class CustomControl : Drawable, IHasCurrentValue + { + public Bindable Current { get; set; } = new Bindable(); + } + } } } diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index cfce615130..13cf0a9f0c 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -1,12 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using JetBrains.Annotations; using osu.Framework.Bindables; +using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Overlays.Settings; @@ -31,7 +34,15 @@ namespace osu.Game.Configuration public int? OrderPosition { get; } - public SettingSourceAttribute(string label, string description = null) + /// + /// The type of the settings control which handles this setting source. + /// + /// + /// Must be a type deriving with a public constructor. + /// + public Type? SettingControlType { get; set; } + + public SettingSourceAttribute(string? label, string? description = null) { Label = label ?? string.Empty; Description = description ?? string.Empty; @@ -67,6 +78,22 @@ namespace osu.Game.Configuration { object value = property.GetValue(obj); + if (attr.SettingControlType != null) + { + var controlType = attr.SettingControlType; + if (controlType.EnumerateBaseTypes().All(t => !t.IsGenericType || t.GetGenericTypeDefinition() != typeof(SettingsItem<>))) + throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} had an unsupported custom control type ({controlType.ReadableName()})"); + + var control = (Drawable)Activator.CreateInstance(controlType); + controlType.GetProperty(nameof(SettingsItem.LabelText))?.SetValue(control, attr.Label); + controlType.GetProperty(nameof(SettingsItem.TooltipText))?.SetValue(control, attr.Description); + controlType.GetProperty(nameof(SettingsItem.Current))?.SetValue(control, value); + + yield return control; + + continue; + } + switch (value) { case BindableNumber bNumber: From 61b7dc1e0601feea555c2841249714a9bc684f68 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 28 Apr 2021 03:42:29 +0900 Subject: [PATCH 26/40] Fix bonus-only maps having 700K base score --- .../Rulesets/Scoring/ScoreProcessorTest.cs | 12 ++++++------ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index ac26f4723e..baa10663d5 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -83,8 +83,8 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, HitResult.SmallTickHit, 925_000)] // (3 * 10) / (4 * 10) * 300_000 + 700_000 (max combo 0) [TestCase(ScoringMode.Standardised, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] // (3 * 0) / (4 * 30) * 300_000 + (0 / 4) * 700_000 [TestCase(ScoringMode.Standardised, HitResult.LargeTickHit, HitResult.LargeTickHit, 575_000)] // (3 * 30) / (4 * 30) * 300_000 + (0 / 4) * 700_000 - [TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 700_030)] // 0 * 300_000 + 700_000 (max combo 0) + 3 * 10 (bonus points) - [TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 700_150)] // 0 * 300_000 + 700_000 (max combo 0) + 3 * 50 (bonus points) + [TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)] // 0 * 300_000 + 700_000 (max combo 0) + 3 * 10 (bonus points) + [TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)] // 0 * 300_000 + 700_000 (max combo 0) + 3 * 50 (bonus points) [TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)] // (0 * 4 * 300) * (1 + 0 / 25) [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 156)] // (((3 * 50) / (4 * 300)) * 4 * 300) * (1 + 1 / 25) [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 312)] // (((3 * 100) / (4 * 300)) * 4 * 300) * (1 + 1 / 25) @@ -95,8 +95,8 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 225)] // (((3 * 10) / (4 * 10)) * 1 * 300) * (1 + 0 / 25) [TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] // (0 * 4 * 300) * (1 + 0 / 25) [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 936)] // (((3 * 50) / (4 * 50)) * 4 * 300) * (1 + 1 / 25) - [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 30)] // (0 * 1 * 300) * (1 + 0 / 25) + 3 * 10 (bonus points) - [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 150)] // (0 * 1 * 300) * (1 + 0 / 25) * 3 * 50 (bonus points) + [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 330)] // (0 * 1 * 300) * (1 + 0 / 25) + 3 * 10 (bonus points) + [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 450)] // (1 * 1 * 300) * (1 + 0 / 25) * 3 * 50 (bonus points) public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore) { var minResult = new TestJudgement(hitResult).MinResult; @@ -289,8 +289,8 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(HitResult.Perfect, 1_000_000)] [TestCase(HitResult.SmallTickHit, 1_000_000)] [TestCase(HitResult.LargeTickHit, 1_000_000)] - [TestCase(HitResult.SmallBonus, 700_000 + Judgement.SMALL_BONUS_SCORE)] - [TestCase(HitResult.LargeBonus, 700_000 + Judgement.LARGE_BONUS_SCORE)] + [TestCase(HitResult.SmallBonus, 1_000_000 + Judgement.SMALL_BONUS_SCORE)] + [TestCase(HitResult.LargeBonus, 1_000_000 + Judgement.LARGE_BONUS_SCORE)] public void TestGetScoreWithExternalStatistics(HitResult result, int expectedScore) { var statistic = new Dictionary { { result, 1 } }; diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index a16a6a6c9b..f32f70d4ba 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -266,7 +266,7 @@ namespace osu.Game.Rulesets.Scoring if (preferRolling && rollingMaxBaseScore != 0) return baseScore / rollingMaxBaseScore; - return maxBaseScore > 0 ? baseScore / maxBaseScore : 0; + return maxBaseScore > 0 ? baseScore / maxBaseScore : 1; } private double calculateComboRatio(int maxCombo) => maxAchievableCombo > 0 ? (double)maxCombo / maxAchievableCombo : 1; From 04454062b7dad5cd47b6bd066c23a018e5290f06 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 28 Apr 2021 03:52:59 +0900 Subject: [PATCH 27/40] Fix up xmldoc --- osu.Game/Configuration/SettingSourceAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index 13cf0a9f0c..a9436aac89 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -38,7 +38,7 @@ namespace osu.Game.Configuration /// The type of the settings control which handles this setting source. /// /// - /// Must be a type deriving with a public constructor. + /// Must be a type deriving . /// public Type? SettingControlType { get; set; } From 05bd6ee50cc20a014c958c86752152ca80e3e553 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 28 Apr 2021 03:54:42 +0900 Subject: [PATCH 28/40] Add back ctor doc --- osu.Game/Configuration/SettingSourceAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index a9436aac89..3e50613093 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -38,7 +38,7 @@ namespace osu.Game.Configuration /// The type of the settings control which handles this setting source. /// /// - /// Must be a type deriving . + /// Must be a type deriving with a public parameterless constructor. /// public Type? SettingControlType { get; set; } From ac1534cda2fb51afad43bcc4e4f4fe945a232cde Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 15:54:40 +0900 Subject: [PATCH 29/40] Add test covering existing button actually changing to `LocallyAvailable` state --- osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs index 0c199bfb62..3fc894da0d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs @@ -43,7 +43,10 @@ namespace osu.Game.Tests.Visual.Online createButtonWithBeatmap(createSoleily()); AddAssert("button state not downloaded", () => downloadButton.DownloadState == DownloadState.NotDownloaded); AddStep("import soleily", () => beatmaps.Import(TestResources.GetQuickTestBeatmapForImport())); + AddUntilStep("wait for beatmap import", () => beatmaps.GetAllUsableBeatmapSets().Any(b => b.OnlineBeatmapSetID == 241526)); + AddAssert("button state downloaded", () => downloadButton.DownloadState == DownloadState.LocallyAvailable); + createButtonWithBeatmap(createSoleily()); AddAssert("button state downloaded", () => downloadButton.DownloadState == DownloadState.LocallyAvailable); ensureSoleilyRemoved(); From 05e3a73a7d24acf00431405b96234498f830ddcb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 15:54:58 +0900 Subject: [PATCH 30/40] Fix import cancellation not correctly being forwarded to import notification --- osu.Game/Database/ArchiveModelManager.cs | 49 +++++++++++++++--------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 6719351530..dbeaebb1cd 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -156,33 +156,44 @@ namespace osu.Game.Database bool isLowPriorityImport = tasks.Length > low_priority_import_batch_size; - await Task.WhenAll(tasks.Select(async task => + try { - notification.CancellationToken.ThrowIfCancellationRequested(); - - try + await Task.WhenAll(tasks.Select(async task => { - var model = await Import(task, isLowPriorityImport, notification.CancellationToken).ConfigureAwait(false); + notification.CancellationToken.ThrowIfCancellationRequested(); - lock (imported) + try { - if (model != null) - imported.Add(model); - current++; + var model = await Import(task, isLowPriorityImport, notification.CancellationToken).ConfigureAwait(false); - notification.Text = $"Imported {current} of {tasks.Length} {HumanisedModelName}s"; - notification.Progress = (float)current / tasks.Length; + lock (imported) + { + if (model != null) + imported.Add(model); + current++; + + notification.Text = $"Imported {current} of {tasks.Length} {HumanisedModelName}s"; + notification.Progress = (float)current / tasks.Length; + } } - } - catch (TaskCanceledException) + catch (TaskCanceledException) + { + throw; + } + catch (Exception e) + { + Logger.Error(e, $@"Could not import ({task})", LoggingTarget.Database); + } + })).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + if (imported.Count == 0) { - throw; + notification.State = ProgressNotificationState.Cancelled; + return imported; } - catch (Exception e) - { - Logger.Error(e, $@"Could not import ({task})", LoggingTarget.Database); - } - })).ConfigureAwait(false); + } if (imported.Count == 0) { From 8598a0968f17c0733ec38e030abceeddc8dca37a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 16:29:23 +0900 Subject: [PATCH 31/40] Update calculations in comments to match new logic Mostly look to be errors that existed before this PR. Co-authored-by: Endrik --- osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index baa10663d5..41e16ebeaf 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -83,8 +83,8 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, HitResult.SmallTickHit, 925_000)] // (3 * 10) / (4 * 10) * 300_000 + 700_000 (max combo 0) [TestCase(ScoringMode.Standardised, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] // (3 * 0) / (4 * 30) * 300_000 + (0 / 4) * 700_000 [TestCase(ScoringMode.Standardised, HitResult.LargeTickHit, HitResult.LargeTickHit, 575_000)] // (3 * 30) / (4 * 30) * 300_000 + (0 / 4) * 700_000 - [TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)] // 0 * 300_000 + 700_000 (max combo 0) + 3 * 10 (bonus points) - [TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)] // 0 * 300_000 + 700_000 (max combo 0) + 3 * 50 (bonus points) + [TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 10 (bonus points) + [TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 50 (bonus points) [TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)] // (0 * 4 * 300) * (1 + 0 / 25) [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 156)] // (((3 * 50) / (4 * 300)) * 4 * 300) * (1 + 1 / 25) [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 312)] // (((3 * 100) / (4 * 300)) * 4 * 300) * (1 + 1 / 25) @@ -95,8 +95,8 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 225)] // (((3 * 10) / (4 * 10)) * 1 * 300) * (1 + 0 / 25) [TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] // (0 * 4 * 300) * (1 + 0 / 25) [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 936)] // (((3 * 50) / (4 * 50)) * 4 * 300) * (1 + 1 / 25) - [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 330)] // (0 * 1 * 300) * (1 + 0 / 25) + 3 * 10 (bonus points) - [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 450)] // (1 * 1 * 300) * (1 + 0 / 25) * 3 * 50 (bonus points) + [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 330)] // (1 * 1 * 300) * (1 + 0 / 25) + 3 * 10 (bonus points) + [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 450)] // (1 * 1 * 300) * (1 + 0 / 25) + 3 * 50 (bonus points) public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore) { var minResult = new TestJudgement(hitResult).MinResult; From e71dbfd730df6a2909deb9b3b53ba027f9f45e29 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 16:37:48 +0900 Subject: [PATCH 32/40] Add inline comment regarding remaining issues with classic scoring --- osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index 41e16ebeaf..184a94912a 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -95,6 +95,7 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 225)] // (((3 * 10) / (4 * 10)) * 1 * 300) * (1 + 0 / 25) [TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] // (0 * 4 * 300) * (1 + 0 / 25) [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 936)] // (((3 * 50) / (4 * 50)) * 4 * 300) * (1 + 1 / 25) + // TODO: The following two cases don't match expectations currently (a single hit is registered in acc portion when it shouldn't be). See https://github.com/ppy/osu/issues/12604. [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 330)] // (1 * 1 * 300) * (1 + 0 / 25) + 3 * 10 (bonus points) [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 450)] // (1 * 1 * 300) * (1 + 0 / 25) + 3 * 50 (bonus points) public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore) From 6d7eef3f5a437e1886908f3440138700e0dcd9e2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 28 Apr 2021 16:45:57 +0000 Subject: [PATCH 33/40] Upgrade to GitHub-native Dependabot --- .github/dependabot.yml | 46 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..9e9af23b27 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,46 @@ +version: 2 +updates: +- package-ecosystem: nuget + directory: "/" + schedule: + interval: monthly + time: "17:00" + open-pull-requests-limit: 99 + ignore: + - dependency-name: Microsoft.EntityFrameworkCore.Design + versions: + - "> 2.2.6" + - dependency-name: Microsoft.EntityFrameworkCore.Sqlite + versions: + - "> 2.2.6" + - dependency-name: Microsoft.EntityFrameworkCore.Sqlite.Core + versions: + - "> 2.2.6" + - dependency-name: Microsoft.Extensions.DependencyInjection + versions: + - ">= 5.a, < 6" + - dependency-name: NUnit3TestAdapter + versions: + - ">= 3.16.a, < 3.17" + - dependency-name: Microsoft.NET.Test.Sdk + versions: + - 16.9.1 + - dependency-name: Microsoft.Extensions.DependencyInjection + versions: + - 3.1.11 + - 3.1.12 + - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson + versions: + - 3.1.11 + - dependency-name: Microsoft.NETCore.Targets + versions: + - 5.0.0 + - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack + versions: + - 5.0.2 + - dependency-name: NUnit + versions: + - 3.13.1 + - dependency-name: Microsoft.AspNetCore.SignalR.Client + versions: + - 3.1.11 From 921d4510478943f5b6c195044d30102f2a9f2fe2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 03:50:49 +0000 Subject: [PATCH 34/40] Bump Microsoft.AspNetCore.SignalR.Client from 5.0.4 to 5.0.5 Bumps [Microsoft.AspNetCore.SignalR.Client](https://github.com/dotnet/aspnetcore) from 5.0.4 to 5.0.5. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.4...v5.0.5) Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 986bd8e7ba..c61c59e589 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -21,7 +21,7 @@ - + From 5720e255c9b3ed3c1d466db57ca4eac65fae0a09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 03:50:53 +0000 Subject: [PATCH 35/40] Bump Sentry from 3.2.0 to 3.3.4 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 3.2.0 to 3.3.4. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Changelog](https://github.com/getsentry/sentry-dotnet/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/3.2.0...3.3.4) Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 986bd8e7ba..abb1e40b64 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -31,7 +31,7 @@ - + From 6c51bf523ca0740feae42ade648b7bbffa49192d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 03:50:56 +0000 Subject: [PATCH 36/40] Bump SharpCompress from 0.28.1 to 0.28.2 Bumps [SharpCompress](https://github.com/adamhathcock/sharpcompress) from 0.28.1 to 0.28.2. - [Release notes](https://github.com/adamhathcock/sharpcompress/releases) - [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.28.1...0.28.2) Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 986bd8e7ba..189bc724fc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -32,7 +32,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c32109d6db..1a9f945ec8 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -94,7 +94,7 @@ - + From 437e9201ba844fb6d7baac65ea679647d439b370 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 03:51:00 +0000 Subject: [PATCH 37/40] Bump Microsoft.AspNetCore.SignalR.Protocols.MessagePack Bumps [Microsoft.AspNetCore.SignalR.Protocols.MessagePack](https://github.com/dotnet/aspnetcore) from 5.0.4 to 5.0.5. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.4...v5.0.5) Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 986bd8e7ba..e2c8e2cf1b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,7 +22,7 @@ - + From 5b071c68e7ab85f7e7faab2e2ae79762140ba6ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 03:51:04 +0000 Subject: [PATCH 38/40] Bump Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson Bumps [Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson](https://github.com/dotnet/aspnetcore) from 5.0.4 to 5.0.5. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.4...v5.0.5) Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 986bd8e7ba..c9de4dd28a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + From 1e7feff49d57fc2f0575a8a423083e0ff9a44d65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 03:51:08 +0000 Subject: [PATCH 39/40] Bump Humanizer from 2.8.26 to 2.9.9 Bumps [Humanizer](https://github.com/Humanizr/Humanizer) from 2.8.26 to 2.9.9. - [Release notes](https://github.com/Humanizr/Humanizer/releases) - [Changelog](https://github.com/Humanizr/Humanizer/blob/main/release_notes.md) - [Commits](https://github.com/Humanizr/Humanizer/compare/v2.8.26...v2.9.9) Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 986bd8e7ba..aa1599e69a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -19,7 +19,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c32109d6db..7061bb5e84 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -89,7 +89,7 @@ - + From 1b3b07d6a9f0ccb9e595a1c6ae69456254356b96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 03:51:12 +0000 Subject: [PATCH 40/40] Bump NUnit from 3.13.1 to 3.13.2 Bumps [NUnit](https://github.com/nunit/nunit) from 3.13.1 to 3.13.2. - [Release notes](https://github.com/nunit/nunit/releases) - [Changelog](https://github.com/nunit/nunit/blob/v3.13.2/CHANGES.md) - [Commits](https://github.com/nunit/nunit/compare/v3.13.1...v3.13.2) Signed-off-by: dependabot[bot] --- .../osu.Game.Rulesets.EmptyFreeform.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- .../osu.Game.Rulesets.EmptyScrolling.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- osu.Game.Benchmarks/osu.Game.Benchmarks.csproj | 2 +- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj index 98a32f9b3a..992f954a3a 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index afa7b03536..7571d1827a 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj index c9f87a8551..1c8ed54440 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index afa7b03536..7571d1827a 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj index ea43d9a54c..bfcf4ef35e 100644 --- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -8,7 +8,7 @@ - + diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index c2d9a923d9..77e9d672e3 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index 64e934efd2..8f8b99b092 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index f743d65db3..e01e858873 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index eab144592f..2dfa1dfbb7 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index df6d17f615..895518e1b9 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index a4e52f8cd4..d5dda39aa5 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 986bd8e7ba..b242811939 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -33,7 +33,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c32109d6db..423f87923b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -95,7 +95,7 @@ - +