From 7b1ff38df7652b3eee6c159f50125f7809cefb4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 9 Sep 2019 23:41:51 +0200 Subject: [PATCH 01/85] Implement line-buffered reader Add a line-buffered reader decorator operating on StreamReader instances. The decorator has two main operations - PeekLine(), which allows to see the next line in the stream without consuming it, ReadLine(), which consumes and returns the next line in the stream, and ReadToEnd() which reads all the remaining text in the stream (including the unconsumed peeked line). Peeking line-per-line uses an internal queue of lines that have been read ahead from the underlying stream. The addition of the line-buffered reader is a workaround solution to a problem with decoding. At current selecting a decoder works by irreversibly reading the first line from the stream and looking for a magic string that indicates the type of decoder to use. It might however be possible for a file to be valid in format, just missing a header. In such a case a lack of a line-buffered reader makes it impossible to reparse the content of that first line. Introducing it will however allow to peek the first line for magic first. - If magic is found in the first line, GetDecoder() will peek it and use it to return the correct Decoder instance. Note that in the case of JsonBeatmapDecoder the magic is the opening JSON object brace, and therefore must not be consumed. - If magic is not found, the fallback decoder will be able to consume it using ReadLine() in Decode(). This commit additionally contains basic unit tests for the reader. Suggested-by: Aergwyn --- .../Beatmaps/IO/LineBufferedReaderTest.cs | 133 ++++++++++++++++++ osu.Game/IO/LineBufferedReader.cs | 70 +++++++++ 2 files changed, 203 insertions(+) create mode 100644 osu.Game.Tests/Beatmaps/IO/LineBufferedReaderTest.cs create mode 100644 osu.Game/IO/LineBufferedReader.cs diff --git a/osu.Game.Tests/Beatmaps/IO/LineBufferedReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/LineBufferedReaderTest.cs new file mode 100644 index 0000000000..b582ca0a6f --- /dev/null +++ b/osu.Game.Tests/Beatmaps/IO/LineBufferedReaderTest.cs @@ -0,0 +1,133 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using System.Text; +using NUnit.Framework; +using osu.Game.IO; + +namespace osu.Game.Tests.Beatmaps.IO +{ + [TestFixture] + public class LineBufferedReaderTest + { + [Test] + public void TestReadLineByLine() + { + const string contents = @"line 1 +line 2 +line 3"; + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents))) + using (var bufferedReader = new LineBufferedReader(stream)) + { + Assert.AreEqual("line 1", bufferedReader.ReadLine()); + Assert.AreEqual("line 2", bufferedReader.ReadLine()); + Assert.AreEqual("line 3", bufferedReader.ReadLine()); + Assert.IsNull(bufferedReader.ReadLine()); + } + } + + [Test] + public void TestPeekLineOnce() + { + const string contents = @"line 1 +peek this +line 3"; + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents))) + using (var bufferedReader = new LineBufferedReader(stream)) + { + Assert.AreEqual("line 1", bufferedReader.ReadLine()); + Assert.AreEqual("peek this", bufferedReader.PeekLine()); + Assert.AreEqual("peek this", bufferedReader.ReadLine()); + Assert.AreEqual("line 3", bufferedReader.ReadLine()); + Assert.IsNull(bufferedReader.ReadLine()); + } + } + + [Test] + public void TestPeekLineMultipleTimes() + { + const string contents = @"peek this once +line 2 +peek this a lot"; + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents))) + using (var bufferedReader = new LineBufferedReader(stream)) + { + Assert.AreEqual("peek this once", bufferedReader.PeekLine()); + Assert.AreEqual("peek this once", bufferedReader.ReadLine()); + Assert.AreEqual("line 2", bufferedReader.ReadLine()); + Assert.AreEqual("peek this a lot", bufferedReader.PeekLine()); + Assert.AreEqual("peek this a lot", bufferedReader.PeekLine()); + Assert.AreEqual("peek this a lot", bufferedReader.PeekLine()); + Assert.AreEqual("peek this a lot", bufferedReader.ReadLine()); + Assert.IsNull(bufferedReader.ReadLine()); + } + } + + [Test] + public void TestPeekLineAtEndOfStream() + { + const string contents = @"first line +second line"; + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents))) + using (var bufferedReader = new LineBufferedReader(stream)) + { + Assert.AreEqual("first line", bufferedReader.ReadLine()); + Assert.AreEqual("second line", bufferedReader.ReadLine()); + Assert.IsNull(bufferedReader.PeekLine()); + Assert.IsNull(bufferedReader.ReadLine()); + Assert.IsNull(bufferedReader.PeekLine()); + } + } + + [Test] + public void TestPeekReadLineOnEmptyStream() + { + using (var stream = new MemoryStream()) + using (var bufferedReader = new LineBufferedReader(stream)) + { + Assert.IsNull(bufferedReader.PeekLine()); + Assert.IsNull(bufferedReader.ReadLine()); + Assert.IsNull(bufferedReader.ReadLine()); + Assert.IsNull(bufferedReader.PeekLine()); + } + } + + [Test] + public void TestReadToEndNoPeeks() + { + const string contents = @"first line +second line"; + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents))) + using (var bufferedReader = new LineBufferedReader(stream)) + { + Assert.AreEqual(contents, bufferedReader.ReadToEnd()); + } + } + + [Test] + public void TestReadToEndAfterReadsAndPeeks() + { + const string contents = @"this line is gone +this one shouldn't be +these ones +definitely not"; + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents))) + using (var bufferedReader = new LineBufferedReader(stream)) + { + Assert.AreEqual("this line is gone", bufferedReader.ReadLine()); + Assert.AreEqual("this one shouldn't be", bufferedReader.PeekLine()); + const string ending = @"this one shouldn't be +these ones +definitely not"; + Assert.AreEqual(ending, bufferedReader.ReadToEnd()); + } + } + } +} diff --git a/osu.Game/IO/LineBufferedReader.cs b/osu.Game/IO/LineBufferedReader.cs new file mode 100644 index 0000000000..66dee1181f --- /dev/null +++ b/osu.Game/IO/LineBufferedReader.cs @@ -0,0 +1,70 @@ +// 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.Collections.Generic; +using System.IO; +using System.Text; + +namespace osu.Game.IO +{ + /// + /// A -like decorator (with more limited API) for s + /// that allows lines to be peeked without consuming. + /// + public class LineBufferedReader : IDisposable + { + private readonly StreamReader streamReader; + private readonly Queue lineBuffer; + + public LineBufferedReader(Stream stream) + { + streamReader = new StreamReader(stream); + lineBuffer = new Queue(); + } + + /// + /// Reads the next line from the stream without consuming it. + /// + public string PeekLine() + { + if (lineBuffer.Count > 0) + return lineBuffer.Peek(); + + var line = streamReader.ReadLine(); + if (line != null) + lineBuffer.Enqueue(line); + return line; + } + + /// + /// Reads the next line from the stream and consumes it. + /// + public string ReadLine() => lineBuffer.Count > 0 ? lineBuffer.Dequeue() : streamReader.ReadLine(); + + /// + /// Reads the stream to its end and returns the text read. + /// This includes any peeked but unconsumed lines. + /// + public string ReadToEnd() + { + var remainingText = streamReader.ReadToEnd(); + if (lineBuffer.Count == 0) + return remainingText; + + var builder = new StringBuilder(); + + // this might not be completely correct due to varying platform line endings + while (lineBuffer.Count > 0) + builder.AppendLine(lineBuffer.Dequeue()); + builder.Append(remainingText); + + return builder.ToString(); + } + + public void Dispose() + { + streamReader?.Dispose(); + } + } +} From 11eda44d3451c889469af4e8e30d522b04d2dd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Sep 2019 00:43:30 +0200 Subject: [PATCH 02/85] Migrate decoding to line-buffered reader Migrate all usages of StreamReader in the context of decoding beatmaps, storyboards or skins to the new LineBufferedReader. --- osu.Game.Rulesets.Osu.Tests/StackingTest.cs | 3 +- .../Formats/LegacyBeatmapDecoderTest.cs | 42 +++++++++---------- .../Beatmaps/Formats/LegacyDecoderTest.cs | 4 +- .../Formats/LegacyStoryboardDecoderTest.cs | 6 +-- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 5 ++- .../Beatmaps/IO/OszArchiveReaderTest.cs | 3 +- osu.Game.Tests/Skins/LegacySkinDecoderTest.cs | 6 +-- osu.Game.Tests/WaveformTestBeatmap.cs | 3 +- osu.Game/Beatmaps/BeatmapManager.cs | 5 ++- .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 8 ++-- osu.Game/Beatmaps/Formats/Decoder.cs | 9 ++-- .../Beatmaps/Formats/JsonBeatmapDecoder.cs | 7 +--- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 3 +- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 4 +- .../Formats/LegacyStoryboardDecoder.cs | 3 +- osu.Game/Skinning/LegacySkin.cs | 3 +- .../Tests/Beatmaps/BeatmapConversionTest.cs | 3 +- .../Beatmaps/DifficultyCalculatorTest.cs | 3 +- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 3 +- 19 files changed, 64 insertions(+), 59 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/StackingTest.cs b/osu.Game.Rulesets.Osu.Tests/StackingTest.cs index e8b99e86f9..871afdb09d 100644 --- a/osu.Game.Rulesets.Osu.Tests/StackingTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/StackingTest.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using NUnit.Framework; using osu.Game.Beatmaps; +using osu.Game.IO; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Tests.Beatmaps; @@ -21,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestStacking() { using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(beatmap_data))) - using (var reader = new StreamReader(stream)) + using (var reader = new LineBufferedReader(stream)) { var beatmap = Decoder.GetDecoder(reader).Decode(reader); var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty()); diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 535320530d..6a77c6ca96 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.IO; using NUnit.Framework; using osuTK; using osuTK.Graphics; @@ -13,6 +12,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Timing; +using osu.Game.IO; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -30,13 +30,9 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeBeatmapVersion() { using (var resStream = TestResources.OpenResource("beatmap-version.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var decoder = Decoder.GetDecoder(stream); - - stream.BaseStream.Position = 0; - stream.DiscardBufferedData(); - var working = new TestWorkingBeatmap(decoder.Decode(stream)); Assert.AreEqual(6, working.BeatmapInfo.BeatmapVersion); @@ -51,7 +47,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var beatmap = decoder.Decode(stream); var beatmapInfo = beatmap.BeatmapInfo; @@ -75,7 +71,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder(); using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var beatmapInfo = decoder.Decode(stream).BeatmapInfo; @@ -101,7 +97,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder(); using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var beatmap = decoder.Decode(stream); var beatmapInfo = beatmap.BeatmapInfo; @@ -126,7 +122,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder(); using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var difficulty = decoder.Decode(stream).BeatmapInfo.BaseDifficulty; @@ -145,7 +141,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var beatmap = decoder.Decode(stream); var metadata = beatmap.Metadata; @@ -164,7 +160,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var beatmap = decoder.Decode(stream); var controlPoints = beatmap.ControlPointInfo; @@ -239,7 +235,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = TestResources.OpenResource("overlapping-control-points.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var controlPoints = decoder.Decode(stream).ControlPointInfo; @@ -271,7 +267,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacySkinDecoder(); using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var comboColors = decoder.Decode(stream).ComboColours; @@ -297,7 +293,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder(); using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var beatmap = decoder.Decode(stream); @@ -320,7 +316,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder(); using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var beatmap = decoder.Decode(stream); @@ -343,7 +339,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; @@ -371,7 +367,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = TestResources.OpenResource("controlpoint-custom-samplebank.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; @@ -393,7 +389,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = TestResources.OpenResource("hitobject-custom-samplebank.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; @@ -411,7 +407,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = TestResources.OpenResource("hitobject-file-samples.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; @@ -431,7 +427,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = TestResources.OpenResource("slider-samples.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; @@ -475,7 +471,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var resStream = TestResources.OpenResource("hitobject-no-addition-bank.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var hitObjects = decoder.Decode(stream).HitObjects; @@ -489,7 +485,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; using (var badResStream = TestResources.OpenResource("invalid-events.osu")) - using (var badStream = new StreamReader(badResStream)) + using (var badStream = new LineBufferedReader(badResStream)) { Assert.DoesNotThrow(() => decoder.Decode(badStream)); } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs index b4d219456c..335a6aeeb0 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs @@ -2,9 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.IO; using NUnit.Framework; using osu.Game.Beatmaps.Formats; +using osu.Game.IO; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Beatmaps.Formats @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LineLoggingDecoder(14); using (var resStream = TestResources.OpenResource("comments.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { decoder.Decode(stream); diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 953763c95d..66d53d7e7b 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -1,12 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.IO; using System.Linq; using NUnit.Framework; using osuTK; using osu.Framework.Graphics; using osu.Game.Beatmaps.Formats; +using osu.Game.IO; using osu.Game.Storyboards; using osu.Game.Tests.Resources; @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyStoryboardDecoder(); using (var resStream = TestResources.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var storyboard = decoder.Decode(stream); @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new LegacyStoryboardDecoder(); using (var resStream = TestResources.OpenResource("variable-with-suffix.osb")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var storyboard = decoder.Decode(stream); diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 4859abbb8e..63346b8c9d 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; +using osu.Game.IO; using osu.Game.IO.Serialization; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; @@ -148,13 +149,13 @@ namespace osu.Game.Tests.Beatmaps.Formats private Beatmap decode(string filename, out Beatmap jsonDecoded) { using (var stream = TestResources.OpenResource(filename)) - using (var sr = new StreamReader(stream)) + using (var sr = new LineBufferedReader(stream)) { var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); using (var ms = new MemoryStream()) using (var sw = new StreamWriter(ms)) - using (var sr2 = new StreamReader(ms)) + using (var sr2 = new LineBufferedReader(ms)) { sw.Write(legacyDecoded.Serialize()); sw.Flush(); diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 37e0565df0..022b2c1a59 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Tests.Resources; using osu.Game.Beatmaps.Formats; +using osu.Game.IO; using osu.Game.IO.Archives; namespace osu.Game.Tests.Beatmaps.IO @@ -50,7 +51,7 @@ namespace osu.Game.Tests.Beatmaps.IO Beatmap beatmap; - using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) + using (var stream = new LineBufferedReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) beatmap = Decoder.GetDecoder(stream).Decode(stream); var meta = beatmap.Metadata; diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs index 8bd846518b..0d96dd08da 100644 --- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs +++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs @@ -2,8 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.IO; using NUnit.Framework; +using osu.Game.IO; using osu.Game.Skinning; using osu.Game.Tests.Resources; using osuTK.Graphics; @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Skins var decoder = new LegacySkinDecoder(); using (var resStream = TestResources.OpenResource(hasColours ? "skin.ini" : "skin-empty.ini")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var comboColors = decoder.Decode(stream).ComboColours; @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Skins var decoder = new LegacySkinDecoder(); using (var resStream = TestResources.OpenResource("skin.ini")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var config = decoder.Decode(stream); diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index db9576b5fa..0d16a78f75 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Video; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; +using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Tests.Resources; @@ -56,7 +57,7 @@ namespace osu.Game.Tests private Beatmap createTestBeatmap() { using (var beatmapStream = getBeatmapStream()) - using (var beatmapReader = new StreamReader(beatmapStream)) + using (var beatmapReader = new LineBufferedReader(beatmapStream)) return Decoder.GetDecoder(beatmapReader).Decode(beatmapReader); } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b9ed3664ef..372eec50fd 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -20,6 +20,7 @@ using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Beatmaps.Formats; using osu.Game.Database; +using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -264,7 +265,7 @@ namespace osu.Game.Beatmaps } Beatmap beatmap; - using (var stream = new StreamReader(reader.GetStream(mapName))) + using (var stream = new LineBufferedReader(reader.GetStream(mapName))) beatmap = Decoder.GetDecoder(stream).Decode(stream); return new BeatmapSetInfo @@ -287,7 +288,7 @@ namespace osu.Game.Beatmaps { using (var raw = reader.GetStream(name)) using (var ms = new MemoryStream()) //we need a memory stream so we can seek - using (var sr = new StreamReader(ms)) + using (var sr = new LineBufferedReader(ms)) { raw.CopyTo(ms); ms.Position = 0; diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 1d00c94ef2..b879b92f01 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.IO; using System.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Track; @@ -11,6 +10,7 @@ using osu.Framework.Graphics.Video; using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Game.Beatmaps.Formats; +using osu.Game.IO; using osu.Game.Skinning; using osu.Game.Storyboards; @@ -33,7 +33,7 @@ namespace osu.Game.Beatmaps { try { - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + using (var stream = new LineBufferedReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) return Decoder.GetDecoder(stream).Decode(stream); } catch @@ -127,7 +127,7 @@ namespace osu.Game.Beatmaps try { - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + using (var stream = new LineBufferedReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) { var decoder = Decoder.GetDecoder(stream); @@ -136,7 +136,7 @@ namespace osu.Game.Beatmaps storyboard = decoder.Decode(stream); else { - using (var secondaryStream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) + using (var secondaryStream = new LineBufferedReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) storyboard = decoder.Decode(stream, secondaryStream); } } diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index 953e50eadc..0f1b617809 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using osu.Game.IO; namespace osu.Game.Beatmaps.Formats { @@ -13,15 +14,15 @@ namespace osu.Game.Beatmaps.Formats { protected virtual TOutput CreateTemplateObject() => new TOutput(); - public TOutput Decode(StreamReader primaryStream, params StreamReader[] otherStreams) + public TOutput Decode(LineBufferedReader primaryStream, params LineBufferedReader[] otherStreams) { var output = CreateTemplateObject(); - foreach (StreamReader stream in otherStreams.Prepend(primaryStream)) + foreach (LineBufferedReader stream in otherStreams.Prepend(primaryStream)) ParseStreamInto(stream, output); return output; } - protected abstract void ParseStreamInto(StreamReader stream, TOutput output); + protected abstract void ParseStreamInto(LineBufferedReader stream, TOutput output); } public abstract class Decoder @@ -39,7 +40,7 @@ namespace osu.Game.Beatmaps.Formats /// Retrieves a to parse a . /// /// A stream pointing to the . - public static Decoder GetDecoder(StreamReader stream) + public static Decoder GetDecoder(LineBufferedReader stream) where T : new() { if (stream == null) diff --git a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs index d8482b200f..988968fa42 100644 --- a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.IO; +using osu.Game.IO; using osu.Game.IO.Serialization; namespace osu.Game.Beatmaps.Formats @@ -13,11 +13,8 @@ namespace osu.Game.Beatmaps.Formats AddDecoder("{", m => new JsonBeatmapDecoder()); } - protected override void ParseStreamInto(StreamReader stream, Beatmap output) + protected override void ParseStreamInto(LineBufferedReader stream, Beatmap output) { - stream.BaseStream.Position = 0; - stream.DiscardBufferedData(); - stream.ReadToEnd().DeserializeInto(output); foreach (var hitObject in output.HitObjects) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 0532790f0a..6e34fd8e90 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -8,6 +8,7 @@ using osu.Framework.IO.File; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.IO; namespace osu.Game.Beatmaps.Formats { @@ -41,7 +42,7 @@ namespace osu.Game.Beatmaps.Formats offset = FormatVersion < 5 ? 24 : 0; } - protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap) + protected override void ParseStreamInto(LineBufferedReader stream, Beatmap beatmap) { this.beatmap = beatmap; this.beatmap.BeatmapInfo.BeatmapVersion = FormatVersion; diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 9a8197ad82..83d20da458 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -3,10 +3,10 @@ using System; using System.Collections.Generic; -using System.IO; using osu.Framework.Logging; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.IO; using osuTK.Graphics; namespace osu.Game.Beatmaps.Formats @@ -21,7 +21,7 @@ namespace osu.Game.Beatmaps.Formats FormatVersion = version; } - protected override void ParseStreamInto(StreamReader stream, T output) + protected override void ParseStreamInto(LineBufferedReader stream, T output) { Section section = Section.None; diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 14c6ea5c8e..a8f8dbf69d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -10,6 +10,7 @@ using osuTK; using osuTK.Graphics; using osu.Framework.Graphics; using osu.Framework.IO.File; +using osu.Game.IO; using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats @@ -35,7 +36,7 @@ namespace osu.Game.Beatmaps.Formats AddDecoder(@"[Events]", m => new LegacyStoryboardDecoder()); } - protected override void ParseStreamInto(StreamReader stream, Storyboard storyboard) + protected override void ParseStreamInto(LineBufferedReader stream, Storyboard storyboard) { this.storyboard = storyboard; base.ParseStreamInto(stream, storyboard); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 0b1076be01..fea15458e4 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Audio; +using osu.Game.IO; using osu.Game.Rulesets.Scoring; using osuTK.Graphics; @@ -35,7 +36,7 @@ namespace osu.Game.Skinning { Stream stream = storage?.GetStream(filename); if (stream != null) - using (StreamReader reader = new StreamReader(stream)) + using (LineBufferedReader reader = new LineBufferedReader(stream)) Configuration = new LegacySkinDecoder().Decode(reader); else Configuration = new DefaultSkinConfiguration(); diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 3fc9662b17..e99b5fc5fb 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Video; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; +using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -142,7 +143,7 @@ namespace osu.Game.Tests.Beatmaps private IBeatmap getBeatmap(string name) { using (var resStream = openResource($"{resource_namespace}.{name}.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var decoder = Decoder.GetDecoder(stream); ((LegacyBeatmapDecoder)decoder).ApplyOffsets = false; diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index 108fa8ff71..748a52d1c5 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -7,6 +7,7 @@ using System.Reflection; using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; +using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; @@ -26,7 +27,7 @@ namespace osu.Game.Tests.Beatmaps private WorkingBeatmap getBeatmap(string name) { using (var resStream = openResource($"{resource_namespace}.{name}.osu")) - using (var stream = new StreamReader(resStream)) + using (var stream = new LineBufferedReader(resStream)) { var decoder = Decoder.GetDecoder(stream); ((LegacyBeatmapDecoder)decoder).ApplyOffsets = false; diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index b77a8508ad..d6f92ba086 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Text; using osu.Game.Beatmaps; +using osu.Game.IO; using osu.Game.Rulesets; using Decoder = osu.Game.Beatmaps.Formats.Decoder; @@ -39,7 +40,7 @@ namespace osu.Game.Tests.Beatmaps private static Beatmap createTestBeatmap() { using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) - using (var reader = new StreamReader(stream)) + using (var reader = new LineBufferedReader(stream)) return Decoder.GetDecoder(reader).Decode(reader); } From 86588778b170b363b2e7156a9ffa08ba32e15f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Sep 2019 22:06:10 +0200 Subject: [PATCH 03/85] Implement fallback decoder registration After the preparatory introduction of LineBufferedReader, it is now possible to introduce registration of fallback decoders that won't drop input supplied in the first line of the file. A fallback decoder is used when the magic in the first line of the file does not match any of the other known decoders. In such a case, the fallback decoder is constructed and provided a LineBufferedReader instance. The process of matching magic only peeks the first non-empty line, so it is available for re-reading in Decode() using ReadLine(). There can be only one fallback decoder per type; a second attempt of registering a fallback will result in an exception to avoid bugs. To address the issue of parsing failing on badly or non-headered files, set the legacy decoders for Beatmaps and Storyboards as the fallbacks. Due to non-trivial logic, several new, passing unit tests with possible edge cases also included. --- .../Formats/LegacyBeatmapDecoderTest.cs | 101 ++++++++++++++++++ .../Beatmaps/IO/ImportBeatmapTest.cs | 2 +- osu.Game.Tests/Resources/corrupted-header.osu | 5 + .../empty-line-instead-of-header.osu | 5 + .../Resources/empty-lines-at-start.osu | 8 ++ osu.Game.Tests/Resources/missing-header.osu | 4 + .../Resources/no-empty-line-after-header.osu | 4 + osu.Game.Tests/osu.Game.Tests.csproj | 7 ++ osu.Game/Beatmaps/Formats/Decoder.cs | 41 +++++-- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 1 + .../Formats/LegacyStoryboardDecoder.cs | 1 + osu.Game/IO/LineBufferedReader.cs | 2 + 12 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 osu.Game.Tests/Resources/corrupted-header.osu create mode 100644 osu.Game.Tests/Resources/empty-line-instead-of-header.osu create mode 100644 osu.Game.Tests/Resources/empty-lines-at-start.osu create mode 100644 osu.Game.Tests/Resources/missing-header.osu create mode 100644 osu.Game.Tests/Resources/no-empty-line-after-header.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 6a77c6ca96..f6c0dbbecf 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.IO; using NUnit.Framework; using osuTK; using osuTK.Graphics; @@ -490,5 +491,105 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.DoesNotThrow(() => decoder.Decode(badStream)); } } + + [Test] + public void TestFallbackDecoderForCorruptedHeader() + { + Decoder decoder = null; + Beatmap beatmap = null; + + using (var resStream = TestResources.OpenResource("corrupted-header.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + Assert.DoesNotThrow(() => decoder = Decoder.GetDecoder(stream)); + Assert.IsInstanceOf(decoder); + Assert.DoesNotThrow(() => beatmap = decoder.Decode(stream)); + Assert.IsNotNull(beatmap); + Assert.AreEqual("Beatmap with corrupted header", beatmap.Metadata.Title); + Assert.AreEqual("Evil Hacker", beatmap.Metadata.AuthorString); + } + } + + [Test] + public void TestFallbackDecoderForMissingHeader() + { + Decoder decoder = null; + Beatmap beatmap = null; + + using (var resStream = TestResources.OpenResource("missing-header.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + Assert.DoesNotThrow(() => decoder = Decoder.GetDecoder(stream)); + Assert.IsInstanceOf(decoder); + Assert.DoesNotThrow(() => beatmap = decoder.Decode(stream)); + Assert.IsNotNull(beatmap); + Assert.AreEqual("Beatmap with no header", beatmap.Metadata.Title); + Assert.AreEqual("Incredibly Evil Hacker", beatmap.Metadata.AuthorString); + } + } + + [Test] + public void TestDecodeFileWithEmptyLinesAtStart() + { + Decoder decoder = null; + Beatmap beatmap = null; + + using (var resStream = TestResources.OpenResource("empty-lines-at-start.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + Assert.DoesNotThrow(() => decoder = Decoder.GetDecoder(stream)); + Assert.IsInstanceOf(decoder); + Assert.DoesNotThrow(() => beatmap = decoder.Decode(stream)); + Assert.IsNotNull(beatmap); + Assert.AreEqual("Empty lines at start", beatmap.Metadata.Title); + Assert.AreEqual("Edge Case Hunter", beatmap.Metadata.AuthorString); + } + } + + [Test] + public void TestDecodeFileWithEmptyLinesAndNoHeader() + { + Decoder decoder = null; + Beatmap beatmap = null; + + using (var resStream = TestResources.OpenResource("empty-line-instead-of-header.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + Assert.DoesNotThrow(() => decoder = Decoder.GetDecoder(stream)); + Assert.IsInstanceOf(decoder); + Assert.DoesNotThrow(() => beatmap = decoder.Decode(stream)); + Assert.IsNotNull(beatmap); + Assert.AreEqual("The dog ate the file header", beatmap.Metadata.Title); + Assert.AreEqual("Why does this keep happening", beatmap.Metadata.AuthorString); + } + } + + [Test] + public void TestDecodeFileWithContentImmediatelyAfterHeader() + { + Decoder decoder = null; + Beatmap beatmap = null; + + using (var resStream = TestResources.OpenResource("no-empty-line-after-header.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + Assert.DoesNotThrow(() => decoder = Decoder.GetDecoder(stream)); + Assert.IsInstanceOf(decoder); + Assert.DoesNotThrow(() => beatmap = decoder.Decode(stream)); + Assert.IsNotNull(beatmap); + Assert.AreEqual("No empty line delimiting header from contents", beatmap.Metadata.Title); + Assert.AreEqual("Edge Case Hunter", beatmap.Metadata.AuthorString); + } + } + + [Test] + public void TestDecodeEmptyFile() + { + using (var resStream = new MemoryStream()) + using (var stream = new LineBufferedReader(resStream)) + { + Assert.Throws(() => Decoder.GetDecoder(stream)); + } + } } } diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index ad0ed00989..0686073292 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -126,7 +126,7 @@ namespace osu.Game.Tests.Beatmaps.IO var breakTemp = TestResources.GetTestBeatmapForImport(); - MemoryStream brokenOsu = new MemoryStream(new byte[] { 1, 3, 3, 7 }); + MemoryStream brokenOsu = new MemoryStream(); MemoryStream brokenOsz = new MemoryStream(File.ReadAllBytes(breakTemp)); File.Delete(breakTemp); diff --git a/osu.Game.Tests/Resources/corrupted-header.osu b/osu.Game.Tests/Resources/corrupted-header.osu new file mode 100644 index 0000000000..92701a4a7d --- /dev/null +++ b/osu.Game.Tests/Resources/corrupted-header.osu @@ -0,0 +1,5 @@ +ow computerosu file format v14 + +[Metadata] +Title: Beatmap with corrupted header +Creator: Evil Hacker diff --git a/osu.Game.Tests/Resources/empty-line-instead-of-header.osu b/osu.Game.Tests/Resources/empty-line-instead-of-header.osu new file mode 100644 index 0000000000..91ecf8d84a --- /dev/null +++ b/osu.Game.Tests/Resources/empty-line-instead-of-header.osu @@ -0,0 +1,5 @@ + + +[Metadata] +Title: The dog ate the file header +Creator: Why does this keep happening \ No newline at end of file diff --git a/osu.Game.Tests/Resources/empty-lines-at-start.osu b/osu.Game.Tests/Resources/empty-lines-at-start.osu new file mode 100644 index 0000000000..cb3b1761a2 --- /dev/null +++ b/osu.Game.Tests/Resources/empty-lines-at-start.osu @@ -0,0 +1,8 @@ + + + +osu file format v14 + +[Metadata] +Title: Empty lines at start +Creator: Edge Case Hunter \ No newline at end of file diff --git a/osu.Game.Tests/Resources/missing-header.osu b/osu.Game.Tests/Resources/missing-header.osu new file mode 100644 index 0000000000..95fac0d79b --- /dev/null +++ b/osu.Game.Tests/Resources/missing-header.osu @@ -0,0 +1,4 @@ +[Metadata] + +Title: Beatmap with no header +Creator: Incredibly Evil Hacker diff --git a/osu.Game.Tests/Resources/no-empty-line-after-header.osu b/osu.Game.Tests/Resources/no-empty-line-after-header.osu new file mode 100644 index 0000000000..9db2b7c01c --- /dev/null +++ b/osu.Game.Tests/Resources/no-empty-line-after-header.osu @@ -0,0 +1,4 @@ +osu file format v14 +[Metadata] +Title: No empty line delimiting header from contents +Creator: Edge Case Hunter \ No newline at end of file diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 84f67c9319..9dc3e85e1b 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,5 +1,12 @@  + + + + + + + diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index 0f1b617809..4a539cc33f 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -28,6 +28,7 @@ namespace osu.Game.Beatmaps.Formats public abstract class Decoder { private static readonly Dictionary>> decoders = new Dictionary>>(); + private static readonly Dictionary> fallback_decoders = new Dictionary>(); static Decoder() { @@ -49,21 +50,31 @@ namespace osu.Game.Beatmaps.Formats if (!decoders.TryGetValue(typeof(T), out var typedDecoders)) throw new IOException(@"Unknown decoder type"); - string line; + // start off with the first line of the file + string line = stream.PeekLine()?.Trim(); - do + while (line != null && line.Length == 0) { - line = stream.ReadLine()?.Trim(); - } while (line != null && line.Length == 0); + // consume the previously peeked empty line and advance to the next one + stream.ReadLine(); + line = stream.PeekLine()?.Trim(); + } if (line == null) - throw new IOException(@"Unknown file format (null)"); + throw new IOException("Unknown file format (null)"); var decoder = typedDecoders.Select(d => line.StartsWith(d.Key, StringComparison.InvariantCulture) ? d.Value : null).FirstOrDefault(); - if (decoder == null) - throw new IOException($@"Unknown file format ({line})"); - return (Decoder)decoder.Invoke(line); + // it's important the magic does NOT get consumed here, since sometimes it's part of the structure + // (see JsonBeatmapDecoder - the magic string is the opening brace) + // decoder implementations should therefore not die on receiving their own magic + if (decoder != null) + return (Decoder)decoder.Invoke(line); + + if (!fallback_decoders.TryGetValue(typeof(T), out var fallbackDecoder)) + throw new IOException($"Unknown file format ({line})"); + + return (Decoder)fallbackDecoder.Invoke(); } /// @@ -78,5 +89,19 @@ namespace osu.Game.Beatmaps.Formats typedDecoders[magic] = constructor; } + + /// + /// Registers a fallback decoder instantiation function. + /// The fallback will be returned if the first line of the decoded stream does not match any known magic. + /// + /// Type of object being decoded. + /// A function that constructs the fallback. + protected static void SetFallbackDecoder(Func constructor) + { + if (fallback_decoders.ContainsKey(typeof(T))) + throw new InvalidOperationException($"A fallback decoder was already added for type {typeof(T)}."); + + fallback_decoders[typeof(T)] = constructor; + } } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 6e34fd8e90..786b7611b5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -26,6 +26,7 @@ namespace osu.Game.Beatmaps.Formats public static void Register() { AddDecoder(@"osu file format v", m => new LegacyBeatmapDecoder(Parsing.ParseInt(m.Split('v').Last()))); + SetFallbackDecoder(() => new LegacyBeatmapDecoder()); } /// diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index a8f8dbf69d..5dbd67d304 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -34,6 +34,7 @@ namespace osu.Game.Beatmaps.Formats // note that this isn't completely correct AddDecoder(@"osu file format v", m => new LegacyStoryboardDecoder()); AddDecoder(@"[Events]", m => new LegacyStoryboardDecoder()); + SetFallbackDecoder(() => new LegacyStoryboardDecoder()); } protected override void ParseStreamInto(LineBufferedReader stream, Storyboard storyboard) diff --git a/osu.Game/IO/LineBufferedReader.cs b/osu.Game/IO/LineBufferedReader.cs index 66dee1181f..aab761afd8 100644 --- a/osu.Game/IO/LineBufferedReader.cs +++ b/osu.Game/IO/LineBufferedReader.cs @@ -25,6 +25,7 @@ namespace osu.Game.IO /// /// Reads the next line from the stream without consuming it. + /// Subsequent calls to without a will return the same string. /// public string PeekLine() { @@ -39,6 +40,7 @@ namespace osu.Game.IO /// /// Reads the next line from the stream and consumes it. + /// If a line was peeked, that same line will then be consumed and returned. /// public string ReadLine() => lineBuffer.Count > 0 ? lineBuffer.Dequeue() : streamReader.ReadLine(); From 29fcab65f9a2e553dd9c9cd550173612f1b74303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Sep 2019 22:40:52 +0200 Subject: [PATCH 04/85] Remove superfluous csproj entries --- osu.Game.Tests/osu.Game.Tests.csproj | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 9dc3e85e1b..84f67c9319 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,12 +1,5 @@  - - - - - - - From dd9f620c2364e35c1f1185f2f4b5a43893b5874f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Sep 2019 22:45:34 +0200 Subject: [PATCH 05/85] Fix misleading xmldoc --- osu.Game/Beatmaps/Formats/Decoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index 4a539cc33f..045eb2d14d 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -92,7 +92,7 @@ namespace osu.Game.Beatmaps.Formats /// /// Registers a fallback decoder instantiation function. - /// The fallback will be returned if the first line of the decoded stream does not match any known magic. + /// The fallback will be returned if the first non-empty line of the decoded stream does not match any known magic. /// /// Type of object being decoded. /// A function that constructs the fallback. From 78931c8e23f304e0b39387b6d8d355ff64c01555 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Sun, 15 Sep 2019 15:59:46 +0200 Subject: [PATCH 06/85] Implement notification when user has track or master volume muted This took around under a hour to implement, it has the same behavior and content from osu!stable. A notification will show up when the user has either its master or track volume set to the minimum, clicking it will set it to the default values. --- osu.Game/Screens/Play/PlayerLoader.cs | 46 ++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 5396321160..b8d5fc7bda 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -19,6 +20,8 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Menu; using osu.Game.Screens.Play.HUD; @@ -54,6 +57,8 @@ namespace osu.Game.Screens.Play private InputManager inputManager; + private NotificationOverlay notificationOverlay; + private IdleTracker idleTracker; public PlayerLoader(Func createPlayer) @@ -68,7 +73,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audioManager, NotificationOverlay notificationOverlay) { InternalChild = (content = new LogoTrackingContainer { @@ -101,6 +106,10 @@ namespace osu.Game.Screens.Play }); loadNewPlayer(); + + this.notificationOverlay = notificationOverlay; + + checkVolume(audioManager); } private void playerLoaded(Player player) => info.Loading = false; @@ -145,6 +154,12 @@ namespace osu.Game.Screens.Play content.FadeOut(250); } + private void checkVolume(AudioManager audio) + { + if (audio.Volume.Value <= audio.Volume.MinValue || audio.VolumeTrack.Value <= audio.VolumeTrack.MinValue) + notificationOverlay.Post(new MutedNotification()); + } + public override void OnEntering(IScreen last) { base.OnEntering(last); @@ -473,5 +488,34 @@ namespace osu.Game.Screens.Play Loading = true; } } + + private class MutedNotification : SimpleNotification + { + public MutedNotification() + { + this.Text = "Your music volume is set to 0%! Click here to restore it."; + } + + public override bool IsImportant => true; + + public override bool RequestsFocus => true; + + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audioManager, NotificationOverlay notificationOverlay) + { + Icon = FontAwesome.Solid.VolumeMute; + IconBackgound.Colour = colours.RedDark; + + Activated = delegate + { + notificationOverlay.Hide(); + + audioManager.Volume.SetDefault(); + audioManager.VolumeTrack.SetDefault(); + + return true; + }; + } + } } } From 4a10e6c44e767012cc93266b5c0a9041e6a8cf82 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Sun, 15 Sep 2019 16:11:45 +0200 Subject: [PATCH 07/85] Use ResolvedAttribute for NotificationOverlay --- osu.Game/Screens/Play/PlayerLoader.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index b8d5fc7bda..aaf750b9f7 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -57,7 +57,8 @@ namespace osu.Game.Screens.Play private InputManager inputManager; - private NotificationOverlay notificationOverlay; + [Resolved] + private NotificationOverlay notificationOverlay { get; set; } private IdleTracker idleTracker; @@ -107,8 +108,6 @@ namespace osu.Game.Screens.Play loadNewPlayer(); - this.notificationOverlay = notificationOverlay; - checkVolume(audioManager); } From 72af640df7316e646fe0c80ffcf2b9fa7b73f2d3 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Sun, 15 Sep 2019 16:31:40 +0200 Subject: [PATCH 08/85] Expose VolumeOverlay as available dependencies --- osu.Game/OsuGame.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8fa8ffaf9b..b07cd84bc3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -485,7 +485,8 @@ namespace osu.Game toolbarElements.Add(d); }); - loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add); + dependencies.Cache(loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add)); + loadComponentSingleFile(new OnScreenDisplay(), Add, true); loadComponentSingleFile(musicController = new MusicController(), Add, true); From 0afb5c5bb05f4a9b9e54f43b6d97f4b166a10b3d Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Sun, 15 Sep 2019 16:31:57 +0200 Subject: [PATCH 09/85] Expose muted state from VolumeOverlay --- osu.Game/Overlays/VolumeOverlay.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index e6204a3179..ff3cbcd575 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -32,6 +32,12 @@ namespace osu.Game.Overlays private readonly BindableDouble muteAdjustment = new BindableDouble(); + public bool IsMuted + { + get => muteButton.Current.Value; + set => muteButton.Current.Value = value; + } + [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colours) { From 00e46fdefed6be79e6542464342a0e8a345c32e0 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Sun, 15 Sep 2019 16:32:23 +0200 Subject: [PATCH 10/85] Add check if game is muted by MuteButton --- osu.Game/Screens/Play/PlayerLoader.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index aaf750b9f7..a00f005827 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -57,10 +58,13 @@ namespace osu.Game.Screens.Play private InputManager inputManager; + private IdleTracker idleTracker; + [Resolved] private NotificationOverlay notificationOverlay { get; set; } - private IdleTracker idleTracker; + [Resolved] + private VolumeOverlay volumeOverlay { get; set; } public PlayerLoader(Func createPlayer) { @@ -155,7 +159,7 @@ namespace osu.Game.Screens.Play private void checkVolume(AudioManager audio) { - if (audio.Volume.Value <= audio.Volume.MinValue || audio.VolumeTrack.Value <= audio.VolumeTrack.MinValue) + if (volumeOverlay.IsMuted || audio.Volume.Value <= audio.Volume.MinValue || audio.VolumeTrack.Value <= audio.VolumeTrack.MinValue) notificationOverlay.Post(new MutedNotification()); } @@ -500,7 +504,7 @@ namespace osu.Game.Screens.Play public override bool RequestsFocus => true; [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audioManager, NotificationOverlay notificationOverlay) + private void load(OsuColour colours, AudioManager audioManager, NotificationOverlay notificationOverlay, VolumeOverlay volumeOverlay) { Icon = FontAwesome.Solid.VolumeMute; IconBackgound.Colour = colours.RedDark; @@ -509,6 +513,7 @@ namespace osu.Game.Screens.Play { notificationOverlay.Hide(); + volumeOverlay.IsMuted = false; audioManager.Volume.SetDefault(); audioManager.VolumeTrack.SetDefault(); From 811a08d18ff2736b52382b95ba48b80544ff27f0 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Sun, 15 Sep 2019 16:50:01 +0200 Subject: [PATCH 11/85] Use Bindable instead of bool --- osu.Game/Overlays/VolumeOverlay.cs | 6 +----- osu.Game/Screens/Play/PlayerLoader.cs | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index ff3cbcd575..0c08e0eb6e 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -32,11 +32,7 @@ namespace osu.Game.Overlays private readonly BindableDouble muteAdjustment = new BindableDouble(); - public bool IsMuted - { - get => muteButton.Current.Value; - set => muteButton.Current.Value = value; - } + public Bindable IsMuted => muteButton.Current; [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colours) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index a00f005827..baeec27412 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -159,7 +159,7 @@ namespace osu.Game.Screens.Play private void checkVolume(AudioManager audio) { - if (volumeOverlay.IsMuted || audio.Volume.Value <= audio.Volume.MinValue || audio.VolumeTrack.Value <= audio.VolumeTrack.MinValue) + if (volumeOverlay.IsMuted.Value || audio.Volume.Value <= audio.Volume.MinValue || audio.VolumeTrack.Value <= audio.VolumeTrack.MinValue) notificationOverlay.Post(new MutedNotification()); } @@ -513,7 +513,7 @@ namespace osu.Game.Screens.Play { notificationOverlay.Hide(); - volumeOverlay.IsMuted = false; + volumeOverlay.IsMuted.Value = false; audioManager.Volume.SetDefault(); audioManager.VolumeTrack.SetDefault(); From 08ab4e508f07b774f83c35063cb7d5216aecb421 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Sun, 15 Sep 2019 17:15:52 +0200 Subject: [PATCH 12/85] Use loadComponentSingleFile for caching --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b07cd84bc3..d539fe0f50 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -485,7 +485,7 @@ namespace osu.Game toolbarElements.Add(d); }); - dependencies.Cache(loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add)); + loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add, true); loadComponentSingleFile(new OnScreenDisplay(), Add, true); From ec788ac09d8ac8343e374187725fbb8a7c4cef24 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Sun, 15 Sep 2019 17:20:07 +0200 Subject: [PATCH 13/85] Make notification only show up once per session --- osu.Game/Screens/Play/PlayerLoader.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index baeec27412..3bce6fa1ed 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -66,6 +66,8 @@ namespace osu.Game.Screens.Play [Resolved] private VolumeOverlay volumeOverlay { get; set; } + private bool muteWarningShownOnce = false; + public PlayerLoader(Func createPlayer) { this.createPlayer = createPlayer; @@ -159,8 +161,12 @@ namespace osu.Game.Screens.Play private void checkVolume(AudioManager audio) { - if (volumeOverlay.IsMuted.Value || audio.Volume.Value <= audio.Volume.MinValue || audio.VolumeTrack.Value <= audio.VolumeTrack.MinValue) + //Checks if the notification has not been shown yet and also if master volume is muted, track/music volume is muted or if the whole game is muted. + if (!muteWarningShownOnce && (volumeOverlay.IsMuted.Value || audio.Volume.Value <= audio.Volume.MinValue || audio.VolumeTrack.Value <= audio.VolumeTrack.MinValue)) + { notificationOverlay.Post(new MutedNotification()); + muteWarningShownOnce = true; + } } public override void OnEntering(IScreen last) From e3884658af0be36a364e8637b70fea1f2b20ff08 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Sun, 15 Sep 2019 17:36:53 +0200 Subject: [PATCH 14/85] Resolve code inspection errors --- osu.Game/Screens/Play/PlayerLoader.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 3bce6fa1ed..d78f36f6de 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -66,7 +65,7 @@ namespace osu.Game.Screens.Play [Resolved] private VolumeOverlay volumeOverlay { get; set; } - private bool muteWarningShownOnce = false; + private bool muteWarningShownOnce; public PlayerLoader(Func createPlayer) { @@ -500,10 +499,7 @@ namespace osu.Game.Screens.Play private class MutedNotification : SimpleNotification { - public MutedNotification() - { - this.Text = "Your music volume is set to 0%! Click here to restore it."; - } + public MutedNotification() => Text = "Your music volume is set to 0%! Click here to restore it."; public override bool IsImportant => true; From ecce12981e7bea177c784912d6adc360805be57e Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Sun, 15 Sep 2019 17:47:44 +0200 Subject: [PATCH 15/85] Use block body for constructor to fix remaining code inspection issue --- osu.Game/Screens/Play/PlayerLoader.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index d78f36f6de..df55b738e1 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -499,7 +499,10 @@ namespace osu.Game.Screens.Play private class MutedNotification : SimpleNotification { - public MutedNotification() => Text = "Your music volume is set to 0%! Click here to restore it."; + public MutedNotification() + { + Text = "Your music volume is set to 0%! Click here to restore it."; + } public override bool IsImportant => true; From 220fdd0a044105e268cccfe9c113c1dce6fcf668 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 16 Sep 2019 06:56:52 +0200 Subject: [PATCH 16/85] Make muteWarningShownOnce static ... so it will actually get an instance per osu session. --- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index df55b738e1..dbb6c47943 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens.Play [Resolved] private VolumeOverlay volumeOverlay { get; set; } - private bool muteWarningShownOnce; + private static bool muteWarningShownOnce; public PlayerLoader(Func createPlayer) { From c3221e1a3623d3c0cc5134aef738fbed3acbd24a Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Wed, 18 Sep 2019 23:27:26 +0200 Subject: [PATCH 17/85] Prepare classes for player loader test scene --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 90 +++++++++++++++++-- osu.Game/Screens/Play/PlayerLoader.cs | 16 +++- 2 files changed, 96 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index ab519360ac..dc099b14b9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -3,14 +3,20 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; using osu.Framework.Screens; +using osu.Game.Graphics.Containers; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; @@ -24,19 +30,35 @@ namespace osu.Game.Tests.Visual.Gameplay public class TestScenePlayerLoader : ManualInputManagerTestScene { private TestPlayerLoader loader; - private OsuScreenStack stack; + private TestPlayerLoaderContainer container; + private TestPlayer player; + + [Resolved] + private AudioManager audioManager { get; set; } [SetUp] public void Setup() => Schedule(() => { - InputManager.Child = stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }; + ResetPlayer(false); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); }); + /// + /// Sets the input manager child to a new test player loader container instance. + /// + /// If the test player should behave like the production one. + public void ResetPlayer(bool interactive) + { + player = new TestPlayer(interactive, interactive); + loader = new TestPlayerLoader(() => player); + container = new TestPlayerLoaderContainer(loader); + InputManager.Child = container; + } + [Test] public void TestBlockLoadViaMouseMovement() { - AddStep("load dummy beatmap", () => stack.Push(loader = new TestPlayerLoader(() => new TestPlayer(false, false)))); + AddStep("load dummy beatmap", () => ResetPlayer(false)); AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddRepeatStep("move mouse", () => InputManager.MoveMouseTo(loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft + (loader.VisualSettings.ScreenSpaceDrawQuad.BottomRight - loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft) * RNG.NextSingle()), 20); AddAssert("loader still active", () => loader.IsCurrentScreen()); @@ -49,13 +71,13 @@ namespace osu.Game.Tests.Visual.Gameplay Player player = null; SlowLoadPlayer slowPlayer = null; - AddStep("load dummy beatmap", () => stack.Push(loader = new TestPlayerLoader(() => player = new TestPlayer(false, false)))); + AddStep("load dummy beatmap", () => ResetPlayer(false)); AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); AddUntilStep("wait for player to be current", () => player.IsCurrentScreen()); AddStep("load slow dummy beatmap", () => { - stack.Push(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); + InputManager.Children = container = new TestPlayerLoaderContainer(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000); }); @@ -65,7 +87,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestModReinstantiation() { - TestPlayer player = null; TestMod gameMod = null; TestMod playerMod1 = null; TestMod playerMod2 = null; @@ -73,7 +94,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("load player", () => { Mods.Value = new[] { gameMod = new TestMod() }; - stack.Push(loader = new TestPlayerLoader(() => player = new TestPlayer())); + ResetPlayer(true); }); AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen()); @@ -97,6 +118,61 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("player mods applied", () => playerMod2.Applied); } + [Test] + public void TestMutedNotification() + { + AddStep("set master volume to 0%", () => audioManager.Volume.Value = 0); + AddStep("reset notification lock", () => PlayerLoader.ResetNotificationLock()); + //AddStep("reset notification overlay", () => notificationOverlay); + AddStep("load player", () => ResetPlayer(false)); + AddUntilStep("wait for player", () => player.IsLoaded); + + AddAssert("check for notification", () => container.NotificationOverlay.UnreadCount.Value == 1); + AddAssert("click notification", () => + { + var scrollContainer = container.NotificationOverlay.Children.Last() as OsuScrollContainer; + var flowContainer = scrollContainer.Children.First() as FillFlowContainer; + return flowContainer.Children.First().First().Click(); + }); + AddAssert("check master volume", () => audioManager.Volume.IsDefault); + + AddStep("restart player", () => + { + var lastPlayer = player; + player = null; + lastPlayer.Restart(); + }); + } + + private class TestPlayerLoaderContainer : Container + { + private TestPlayerLoader loader; + + [Cached] + public NotificationOverlay NotificationOverlay { get; } = new NotificationOverlay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }; + + [Cached] + public VolumeOverlay VolumeOverlay { get; } = new VolumeOverlay + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }; + + public TestPlayerLoaderContainer(TestPlayerLoader testPlayerLoader) + { + Children = new Drawable[] + { + loader = testPlayerLoader, + NotificationOverlay, + VolumeOverlay + }; + } + } + private class TestPlayerLoader : PlayerLoader { public new VisualSettings VisualSettings => base.VisualSettings; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index dbb6c47943..8f2435d2f7 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -65,6 +65,9 @@ namespace osu.Game.Screens.Play [Resolved] private VolumeOverlay volumeOverlay { get; set; } + [Resolved] + private AudioManager audioManager { get; set; } + private static bool muteWarningShownOnce; public PlayerLoader(Func createPlayer) @@ -79,7 +82,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(AudioManager audioManager, NotificationOverlay notificationOverlay) + private void load() { InternalChild = (content = new LogoTrackingContainer { @@ -112,8 +115,6 @@ namespace osu.Game.Screens.Play }); loadNewPlayer(); - - checkVolume(audioManager); } private void playerLoaded(Player player) => info.Loading = false; @@ -212,6 +213,7 @@ namespace osu.Game.Screens.Play { inputManager = GetContainingInputManager(); base.LoadComplete(); + checkVolume(audioManager); } private ScheduledDelegate pushDebounce; @@ -526,5 +528,13 @@ namespace osu.Game.Screens.Play }; } } + + /// + /// Sets to , reserved for testing. + /// + public static void ResetNotificationLock() + { + muteWarningShownOnce = false; + } } } From c92545e294d61ad151aac2197d1bb0882bf8d73d Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 23 Sep 2019 01:41:59 +0200 Subject: [PATCH 18/85] Update osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs Co-Authored-By: Salman Ahmed --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index dc099b14b9..36c8ecf020 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Gameplay /// Sets the input manager child to a new test player loader container instance. /// /// If the test player should behave like the production one. - public void ResetPlayer(bool interactive) + private void resetPlayer(bool interactive) { player = new TestPlayer(interactive, interactive); loader = new TestPlayerLoader(() => player); From 8df77ffe924a36c750cc1a3d30216e038b57d06c Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Mon, 23 Sep 2019 16:48:30 +0200 Subject: [PATCH 19/85] Revert test scene changes --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 90 ++----------------- 1 file changed, 7 insertions(+), 83 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index dc099b14b9..ab519360ac 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -3,20 +3,14 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; using osu.Framework.Screens; -using osu.Game.Graphics.Containers; -using osu.Game.Overlays; -using osu.Game.Overlays.Notifications; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; @@ -30,35 +24,19 @@ namespace osu.Game.Tests.Visual.Gameplay public class TestScenePlayerLoader : ManualInputManagerTestScene { private TestPlayerLoader loader; - private TestPlayerLoaderContainer container; - private TestPlayer player; - - [Resolved] - private AudioManager audioManager { get; set; } + private OsuScreenStack stack; [SetUp] public void Setup() => Schedule(() => { - ResetPlayer(false); + InputManager.Child = stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }; Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); }); - /// - /// Sets the input manager child to a new test player loader container instance. - /// - /// If the test player should behave like the production one. - public void ResetPlayer(bool interactive) - { - player = new TestPlayer(interactive, interactive); - loader = new TestPlayerLoader(() => player); - container = new TestPlayerLoaderContainer(loader); - InputManager.Child = container; - } - [Test] public void TestBlockLoadViaMouseMovement() { - AddStep("load dummy beatmap", () => ResetPlayer(false)); + AddStep("load dummy beatmap", () => stack.Push(loader = new TestPlayerLoader(() => new TestPlayer(false, false)))); AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddRepeatStep("move mouse", () => InputManager.MoveMouseTo(loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft + (loader.VisualSettings.ScreenSpaceDrawQuad.BottomRight - loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft) * RNG.NextSingle()), 20); AddAssert("loader still active", () => loader.IsCurrentScreen()); @@ -71,13 +49,13 @@ namespace osu.Game.Tests.Visual.Gameplay Player player = null; SlowLoadPlayer slowPlayer = null; - AddStep("load dummy beatmap", () => ResetPlayer(false)); + AddStep("load dummy beatmap", () => stack.Push(loader = new TestPlayerLoader(() => player = new TestPlayer(false, false)))); AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); AddUntilStep("wait for player to be current", () => player.IsCurrentScreen()); AddStep("load slow dummy beatmap", () => { - InputManager.Children = container = new TestPlayerLoaderContainer(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); + stack.Push(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000); }); @@ -87,6 +65,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestModReinstantiation() { + TestPlayer player = null; TestMod gameMod = null; TestMod playerMod1 = null; TestMod playerMod2 = null; @@ -94,7 +73,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("load player", () => { Mods.Value = new[] { gameMod = new TestMod() }; - ResetPlayer(true); + stack.Push(loader = new TestPlayerLoader(() => player = new TestPlayer())); }); AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen()); @@ -118,61 +97,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("player mods applied", () => playerMod2.Applied); } - [Test] - public void TestMutedNotification() - { - AddStep("set master volume to 0%", () => audioManager.Volume.Value = 0); - AddStep("reset notification lock", () => PlayerLoader.ResetNotificationLock()); - //AddStep("reset notification overlay", () => notificationOverlay); - AddStep("load player", () => ResetPlayer(false)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("check for notification", () => container.NotificationOverlay.UnreadCount.Value == 1); - AddAssert("click notification", () => - { - var scrollContainer = container.NotificationOverlay.Children.Last() as OsuScrollContainer; - var flowContainer = scrollContainer.Children.First() as FillFlowContainer; - return flowContainer.Children.First().First().Click(); - }); - AddAssert("check master volume", () => audioManager.Volume.IsDefault); - - AddStep("restart player", () => - { - var lastPlayer = player; - player = null; - lastPlayer.Restart(); - }); - } - - private class TestPlayerLoaderContainer : Container - { - private TestPlayerLoader loader; - - [Cached] - public NotificationOverlay NotificationOverlay { get; } = new NotificationOverlay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }; - - [Cached] - public VolumeOverlay VolumeOverlay { get; } = new VolumeOverlay - { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - }; - - public TestPlayerLoaderContainer(TestPlayerLoader testPlayerLoader) - { - Children = new Drawable[] - { - loader = testPlayerLoader, - NotificationOverlay, - VolumeOverlay - }; - } - } - private class TestPlayerLoader : PlayerLoader { public new VisualSettings VisualSettings => base.VisualSettings; From 028c958431728deb42811a92a3993f2382ee49a6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Sep 2019 19:19:43 +0900 Subject: [PATCH 20/85] Initial implementation of a switch button --- .../UserInterface/TestSceneSwitchButton.cs | 20 +++ .../Graphics/UserInterface/SwitchButton.cs | 117 ++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs create mode 100644 osu.Game/Graphics/UserInterface/SwitchButton.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs new file mode 100644 index 0000000000..8fe381f141 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs @@ -0,0 +1,20 @@ +// 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.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneSwitchButton : OsuTestScene + { + public TestSceneSwitchButton() + { + Child = new SwitchButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/SwitchButton.cs b/osu.Game/Graphics/UserInterface/SwitchButton.cs new file mode 100644 index 0000000000..21712051ef --- /dev/null +++ b/osu.Game/Graphics/UserInterface/SwitchButton.cs @@ -0,0 +1,117 @@ +// 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.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Graphics.UserInterface +{ + public class SwitchButton : Checkbox + { + private const float border_thickness = 4.5f; + private const float padding = 1.25f; + + private readonly Box fill; + private readonly Container switchContainer; + private readonly Drawable switchCircle; + private readonly CircularBorderContainer circularContainer; + + private Color4 enabledColour; + private Color4 disabledColour; + + public SwitchButton() + { + Size = new Vector2(45, 20); + + InternalChild = circularContainer = new CircularBorderContainer + { + RelativeSizeAxes = Axes.Both, + BorderColour = Color4.White, + BorderThickness = border_thickness, + Masking = true, + Children = new Drawable[] + { + fill = new Box + { + RelativeSizeAxes = Axes.Both, + AlwaysPresent = true, + Alpha = 0 + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(border_thickness + padding), + Child = switchContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Child = switchCircle = new CircularContainer + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Masking = true, + Child = new Box { RelativeSizeAxes = Axes.Both } + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + enabledColour = colours.BlueDark; + disabledColour = colours.Gray3; + + switchContainer.Colour = enabledColour; + fill.Colour = disabledColour; + + updateBorder(); + } + + protected override void OnUserChange(bool value) + { + base.OnUserChange(value); + + if (value) + switchCircle.MoveToX(switchContainer.DrawWidth - switchCircle.DrawWidth, 200, Easing.OutQuint); + else + switchCircle.MoveToX(0, 200, Easing.OutQuint); + + fill.FadeTo(value ? 1 : 0, 250, Easing.OutQuint); + + updateBorder(); + } + + protected override bool OnHover(HoverEvent e) + { + updateBorder(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateBorder(); + base.OnHoverLost(e); + } + + private void updateBorder() + { + circularContainer.TransformBorderTo((Current.Value ? enabledColour : disabledColour).Lighten(IsHovered ? 0.3f : 0)); + } + + private class CircularBorderContainer : CircularContainer + { + public void TransformBorderTo(SRGBColour colour) + => this.TransformTo(nameof(BorderColour), colour, 250, Easing.OutQuint); + } + } +} From 7fab1a4337c4db0094e4179fb34037984f1d322b Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 24 Sep 2019 16:06:33 -0700 Subject: [PATCH 21/85] Truncate long metadata on beatmap info wedge --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 22 +++++++++++++++------ osu.Game/Screens/Select/SongSelect.cs | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 65ecd7b812..6c3eed8610 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -31,7 +31,9 @@ namespace osu.Game.Screens.Select { public class BeatmapInfoWedge : OverlayContainer { - private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0); + private const float shear_width = 36.75f; + + private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.wedged_container_size.Y, 0); private readonly IBindable ruleset = new Bindable(); @@ -200,14 +202,17 @@ namespace osu.Game.Screens.Select Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, Direction = FillDirection.Vertical, - Margin = new MarginPadding { Top = 10, Left = 25, Right = 10, Bottom = 20 }, - AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 10, Left = 25, Right = shear_width * 2.5f, Bottom = 20 }, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, Children = new Drawable[] { VersionLabel = new OsuSpriteText { Text = beatmapInfo.Version, Font = OsuFont.GetFont(size: 24, italics: true), + RelativeSizeAxes = Axes.X, + Truncate = true, }, } }, @@ -217,7 +222,7 @@ namespace osu.Game.Screens.Select Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Direction = FillDirection.Vertical, - Margin = new MarginPadding { Top = 14, Left = 10, Right = 18, Bottom = 20 }, + Padding = new MarginPadding { Top = 14, Left = 10, Right = shear_width / 2, Bottom = 20 }, AutoSizeAxes = Axes.Both, Children = new Drawable[] { @@ -236,17 +241,22 @@ namespace osu.Game.Screens.Select Origin = Anchor.TopLeft, Y = -22, Direction = FillDirection.Vertical, - Margin = new MarginPadding { Top = 15, Left = 25, Right = 10, Bottom = 20 }, - AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 15, Left = 25, Right = shear_width, Bottom = 20 }, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, Children = new Drawable[] { TitleLabel = new OsuSpriteText { Font = OsuFont.GetFont(size: 28, italics: true), + RelativeSizeAxes = Axes.X, + Truncate = true, }, ArtistLabel = new OsuSpriteText { Font = OsuFont.GetFont(size: 17, italics: true), + RelativeSizeAxes = Axes.X, + Truncate = true, }, MapperContainer = new FillFlowContainer { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index fca801ce78..6c0d8a0f36 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Select { public abstract class SongSelect : OsuScreen, IKeyBindingHandler { - private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245); + public static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245); protected const float BACKGROUND_BLUR = 20; private const float left_area_padding = 20; From 8efba255c383acd4114b1625939eaf745bb59cbd Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 24 Sep 2019 16:21:08 -0700 Subject: [PATCH 22/85] Add truncation test --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 932e114580..cda6bedf5d 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -127,6 +127,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("check no info labels", () => !infoWedge.Info.InfoLabelContainer.Children.Any()); } + [Test] + public void testTruncation() + { + selectBeatmap(createLongMetadata()); + } + private void selectBeatmap([CanBeNull] IBeatmap b) { BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null; @@ -166,6 +172,25 @@ namespace osu.Game.Tests.Visual.SongSelect }; } + private IBeatmap createLongMetadata() + { + return new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + AuthorString = $"WWWWWWWWWWWWWWW", + Artist = $"Verrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Artist", + Source = $"Verrrrry long Source", + Title = $"Verrrrry long Title" + }, + Version = $"Verrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Version", + Status = BeatmapSetOnlineStatus.Graveyard, + }, + }; + } + private class TestBeatmapInfoWedge : BeatmapInfoWedge { public new BufferedWedgeInfo Info => base.Info; From cc6030ca14d3f6377c657afeb44faa96bca3bbd2 Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 24 Sep 2019 16:23:36 -0700 Subject: [PATCH 23/85] Update beatmap info wedge tests --- .../Visual/SongSelect/TestSceneBeatmapInfoWedge.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index cda6bedf5d..017e9c527b 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -75,7 +75,6 @@ namespace osu.Game.Tests.Visual.SongSelect testBeatmapLabels(instance); - // TODO: adjust cases once more info is shown for other gamemodes switch (instance) { case OsuRuleset _: @@ -99,8 +98,6 @@ namespace osu.Game.Tests.Visual.SongSelect break; } } - - testNullBeatmap(); } private void testBeatmapLabels(Ruleset ruleset) @@ -117,7 +114,8 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("check info labels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount); } - private void testNullBeatmap() + [Test] + public void testNullBeatmap() { selectBeatmap(null); AddAssert("check empty version", () => string.IsNullOrEmpty(infoWedge.Info.VersionLabel.Text)); From 9861b214407a5e4861722b2cfb876bd524db76de Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 24 Sep 2019 16:28:40 -0700 Subject: [PATCH 24/85] Remove unnecessary padding/margin --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 6c3eed8610..33060e0735 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -202,7 +202,7 @@ namespace osu.Game.Screens.Select Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, Direction = FillDirection.Vertical, - Padding = new MarginPadding { Top = 10, Left = 25, Right = shear_width * 2.5f, Bottom = 20 }, + Padding = new MarginPadding { Top = 10, Left = 25, Right = shear_width * 2.5f }, AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Children = new Drawable[] @@ -222,7 +222,7 @@ namespace osu.Game.Screens.Select Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Direction = FillDirection.Vertical, - Padding = new MarginPadding { Top = 14, Left = 10, Right = shear_width / 2, Bottom = 20 }, + Padding = new MarginPadding { Top = 14, Right = shear_width / 2 }, AutoSizeAxes = Axes.Both, Children = new Drawable[] { @@ -239,9 +239,9 @@ namespace osu.Game.Screens.Select Name = "Centre-aligned metadata", Anchor = Anchor.CentreLeft, Origin = Anchor.TopLeft, - Y = -22, + Y = -7, Direction = FillDirection.Vertical, - Padding = new MarginPadding { Top = 15, Left = 25, Right = shear_width, Bottom = 20 }, + Padding = new MarginPadding { Left = 25, Right = shear_width }, AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Children = new Drawable[] From 102dbd85bdd4fd4f4589314e3164512502860b51 Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 24 Sep 2019 16:48:22 -0700 Subject: [PATCH 25/85] Fix CI errors --- .../Visual/SongSelect/TestSceneBeatmapInfoWedge.cs | 14 +++++++------- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 017e9c527b..f49d7a14a6 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.SongSelect } [Test] - public void testNullBeatmap() + public void TestNullBeatmap() { selectBeatmap(null); AddAssert("check empty version", () => string.IsNullOrEmpty(infoWedge.Info.VersionLabel.Text)); @@ -126,7 +126,7 @@ namespace osu.Game.Tests.Visual.SongSelect } [Test] - public void testTruncation() + public void TestTruncation() { selectBeatmap(createLongMetadata()); } @@ -178,12 +178,12 @@ namespace osu.Game.Tests.Visual.SongSelect { Metadata = new BeatmapMetadata { - AuthorString = $"WWWWWWWWWWWWWWW", - Artist = $"Verrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Artist", - Source = $"Verrrrry long Source", - Title = $"Verrrrry long Title" + AuthorString = "WWWWWWWWWWWWWWW", + Artist = "Verrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Artist", + Source = "Verrrrry long Source", + Title = "Verrrrry long Title" }, - Version = $"Verrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Version", + Version = "Verrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Version", Status = BeatmapSetOnlineStatus.Graveyard, }, }; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 33060e0735..8b360d4a86 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Select { private const float shear_width = 36.75f; - private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.wedged_container_size.Y, 0); + private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGED_CONTAINER_SIZE.Y, 0); private readonly IBindable ruleset = new Bindable(); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 6c0d8a0f36..653f62dd15 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Select { public abstract class SongSelect : OsuScreen, IKeyBindingHandler { - public static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245); + public static readonly Vector2 WEDGED_CONTAINER_SIZE = new Vector2(0.5f, 245); protected const float BACKGROUND_BLUR = 20; private const float left_area_padding = 20; @@ -109,7 +109,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = -150 }, - Size = new Vector2(wedged_container_size.X, 1), + Size = new Vector2(WEDGED_CONTAINER_SIZE.X, 1), } } }, @@ -118,11 +118,11 @@ namespace osu.Game.Screens.Select Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, - Size = new Vector2(wedged_container_size.X, 1), + Size = new Vector2(WEDGED_CONTAINER_SIZE.X, 1), Padding = new MarginPadding { Bottom = Footer.HEIGHT, - Top = wedged_container_size.Y + left_area_padding, + Top = WEDGED_CONTAINER_SIZE.Y + left_area_padding, Left = left_area_padding, Right = left_area_padding * 2, }, @@ -158,7 +158,7 @@ namespace osu.Game.Screens.Select Child = Carousel = new BeatmapCarousel { RelativeSizeAxes = Axes.Both, - Size = new Vector2(1 - wedged_container_size.X, 1), + Size = new Vector2(1 - WEDGED_CONTAINER_SIZE.X, 1), Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, SelectionChanged = updateSelectedBeatmap, @@ -182,7 +182,7 @@ namespace osu.Game.Screens.Select }, beatmapInfoWedge = new BeatmapInfoWedge { - Size = wedged_container_size, + Size = WEDGED_CONTAINER_SIZE, RelativeSizeAxes = Axes.X, Margin = new MarginPadding { From 261ba5c80ac4b82d5d73ce02c9609fe8bc829783 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 25 Sep 2019 17:42:27 +0900 Subject: [PATCH 26/85] Fix button not transforming correctly in some cases --- .../UserInterface/TestSceneSwitchButton.cs | 30 +++++++++++++++++-- .../Graphics/UserInterface/SwitchButton.cs | 19 ++++++------ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs index 8fe381f141..bf9071b812 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs @@ -1,20 +1,44 @@ // 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.Bindables; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneSwitchButton : OsuTestScene + public class TestSceneSwitchButton : ManualInputManagerTestScene { - public TestSceneSwitchButton() + private SwitchButton switchButton; + + [SetUp] + public void Setup() => Schedule(() => { - Child = new SwitchButton + Child = switchButton = new SwitchButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, }; + }); + + [Test] + public void TestChangeThroughInput() + { + AddStep("move to switch button", () => InputManager.MoveMouseTo(switchButton)); + AddStep("click on", () => InputManager.Click(MouseButton.Left)); + AddStep("click off", () => InputManager.Click(MouseButton.Left)); + } + + [Test] + public void TestChangeThroughBindable() + { + BindableBool bindable = null; + + AddStep("bind bindable", () => switchButton.Current.BindTo(bindable = new BindableBool())); + AddStep("toggle bindable", () => bindable.Toggle()); + AddStep("toggle bindable", () => bindable.Toggle()); } } } diff --git a/osu.Game/Graphics/UserInterface/SwitchButton.cs b/osu.Game/Graphics/UserInterface/SwitchButton.cs index 21712051ef..9964af91d5 100644 --- a/osu.Game/Graphics/UserInterface/SwitchButton.cs +++ b/osu.Game/Graphics/UserInterface/SwitchButton.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -73,20 +74,20 @@ namespace osu.Game.Graphics.UserInterface switchContainer.Colour = enabledColour; fill.Colour = disabledColour; - - updateBorder(); } - protected override void OnUserChange(bool value) + protected override void LoadComplete() { - base.OnUserChange(value); + base.LoadComplete(); - if (value) - switchCircle.MoveToX(switchContainer.DrawWidth - switchCircle.DrawWidth, 200, Easing.OutQuint); - else - switchCircle.MoveToX(0, 200, Easing.OutQuint); + Current.BindValueChanged(updateState, true); + FinishTransforms(true); + } - fill.FadeTo(value ? 1 : 0, 250, Easing.OutQuint); + private void updateState(ValueChangedEvent state) + { + switchCircle.MoveToX(state.NewValue ? switchContainer.DrawWidth - switchCircle.DrawWidth : 0, 200, Easing.OutQuint); + fill.FadeTo(state.NewValue ? 1 : 0, 250, Easing.OutQuint); updateBorder(); } From c9e39c124e5817bce9909acc8e057606701ea0ce Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 25 Sep 2019 17:42:35 +0900 Subject: [PATCH 27/85] Add a labelled switch button --- .../TestSceneLabelledSwitchButton.cs | 50 +++++++++++++++++++ .../LabelledSwitchButton.cs | 17 +++++++ 2 files changed, 67 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs create mode 100644 osu.Game/Screens/Edit/Setup/Components/LabelledComponents/LabelledSwitchButton.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs new file mode 100644 index 0000000000..dbce08c898 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs @@ -0,0 +1,50 @@ +// 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.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Edit.Setup.Components.LabelledComponents; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneLabelledSwitchButton : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(LabelledSwitchButton), + typeof(SwitchButton) + }; + + [TestCase(false)] + [TestCase(true)] + public void TestSwitchButton(bool hasDescription) => createSwitchButton(hasDescription); + + private void createSwitchButton(bool hasDescription = false) + { + AddStep("create component", () => + { + LabelledSwitchButton component; + + Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 500, + AutoSizeAxes = Axes.Y, + Child = component = new LabelledSwitchButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + + component.Label = "a sample component"; + component.Description = hasDescription ? "this text describes the component" : string.Empty; + }); + } + } +} diff --git a/osu.Game/Screens/Edit/Setup/Components/LabelledComponents/LabelledSwitchButton.cs b/osu.Game/Screens/Edit/Setup/Components/LabelledComponents/LabelledSwitchButton.cs new file mode 100644 index 0000000000..54f6184578 --- /dev/null +++ b/osu.Game/Screens/Edit/Setup/Components/LabelledComponents/LabelledSwitchButton.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Edit.Setup.Components.LabelledComponents +{ + public class LabelledSwitchButton : LabelledComponent + { + public LabelledSwitchButton() + : base(true) + { + } + + protected override SwitchButton CreateComponent() => new SwitchButton(); + } +} From 9f77a1ef35c081db6812bbd40e38185101ae4fcc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 25 Sep 2019 17:53:08 +0900 Subject: [PATCH 28/85] Adjust namespaces --- .../Visual/UserInterface/TestSceneLabelledSwitchButton.cs | 3 +-- osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs | 2 +- osu.Game/Graphics/UserInterfaceV2/LabelledSwitchButton.cs | 4 +--- .../{UserInterface => UserInterfaceV2}/SwitchButton.cs | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) rename osu.Game/Graphics/{UserInterface => UserInterfaceV2}/SwitchButton.cs (98%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs index dbce08c898..6ca4d9fa4c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs @@ -6,8 +6,7 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Edit.Setup.Components.LabelledComponents; +using osu.Game.Graphics.UserInterfaceV2; namespace osu.Game.Tests.Visual.UserInterface { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs index bf9071b812..4a104b4a41 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs @@ -4,7 +4,7 @@ using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledSwitchButton.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledSwitchButton.cs index 54f6184578..c973f1d13e 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledSwitchButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledSwitchButton.cs @@ -1,9 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Screens.Edit.Setup.Components.LabelledComponents +namespace osu.Game.Graphics.UserInterfaceV2 { public class LabelledSwitchButton : LabelledComponent { diff --git a/osu.Game/Graphics/UserInterface/SwitchButton.cs b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs similarity index 98% rename from osu.Game/Graphics/UserInterface/SwitchButton.cs rename to osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs index 9964af91d5..a7fd25b554 100644 --- a/osu.Game/Graphics/UserInterface/SwitchButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs @@ -13,7 +13,7 @@ using osu.Framework.Input.Events; using osuTK; using osuTK.Graphics; -namespace osu.Game.Graphics.UserInterface +namespace osu.Game.Graphics.UserInterfaceV2 { public class SwitchButton : Checkbox { From f11156c2dc644e34f68c883b2b7d9bdf3a63c380 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Sep 2019 19:24:05 +0900 Subject: [PATCH 29/85] Fix tests not working correctly --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 107 +++++++++++++++--- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- 2 files changed, 93 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index ab519360ac..eb54eca0cb 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -7,10 +7,15 @@ using System.Linq; using System.Threading; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; using osu.Framework.Screens; +using osu.Game.Graphics.Containers; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; @@ -18,25 +23,41 @@ using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Play; using osu.Game.Screens.Play.PlayerSettings; +using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { public class TestScenePlayerLoader : ManualInputManagerTestScene { private TestPlayerLoader loader; - private OsuScreenStack stack; + private TestPlayerLoaderContainer container; + private TestPlayer player; - [SetUp] - public void Setup() => Schedule(() => + [Resolved] + private AudioManager audioManager { get; set; } + + /// + /// Sets the input manager child to a new test player loader container instance. + /// + /// If the test player should behave like the production one. + /// An action to run before player load but after bindable leases are returned. + public void ResetPlayer(bool interactive, Action beforeLoadAction = null) { - InputManager.Child = stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }; + audioManager.Volume.SetDefault(); + + InputManager.Clear(); + + beforeLoadAction?.Invoke(); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - }); + + InputManager.Child = container = new TestPlayerLoaderContainer( + loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive))); + } [Test] public void TestBlockLoadViaMouseMovement() { - AddStep("load dummy beatmap", () => stack.Push(loader = new TestPlayerLoader(() => new TestPlayer(false, false)))); + AddStep("load dummy beatmap", () => ResetPlayer(false)); AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddRepeatStep("move mouse", () => InputManager.MoveMouseTo(loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft + (loader.VisualSettings.ScreenSpaceDrawQuad.BottomRight - loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft) * RNG.NextSingle()), 20); AddAssert("loader still active", () => loader.IsCurrentScreen()); @@ -46,16 +67,17 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLoadContinuation() { - Player player = null; SlowLoadPlayer slowPlayer = null; - AddStep("load dummy beatmap", () => stack.Push(loader = new TestPlayerLoader(() => player = new TestPlayer(false, false)))); + AddStep("load dummy beatmap", () => ResetPlayer(false)); AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); AddUntilStep("wait for player to be current", () => player.IsCurrentScreen()); AddStep("load slow dummy beatmap", () => { - stack.Push(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); + InputManager.Child = container = new TestPlayerLoaderContainer( + loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); + Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000); }); @@ -65,16 +87,11 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestModReinstantiation() { - TestPlayer player = null; TestMod gameMod = null; TestMod playerMod1 = null; TestMod playerMod2 = null; - AddStep("load player", () => - { - Mods.Value = new[] { gameMod = new TestMod() }; - stack.Push(loader = new TestPlayerLoader(() => player = new TestPlayer())); - }); + AddStep("load player", () => { ResetPlayer(true, () => Mods.Value = new[] { gameMod = new TestMod() }); }); AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen()); AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); @@ -97,6 +114,66 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("player mods applied", () => playerMod2.Applied); } + [Test] + public void TestMutedNotification() + { + AddStep("reset notification", PlayerLoader.ResetNotificationLock); + + AddStep("load player", () => ResetPlayer(false, () => audioManager.Volume.Value = 0)); + AddUntilStep("wait for player", () => player.IsLoaded); + + AddAssert("check for notification", () => container.NotificationOverlay.UnreadCount.Value == 1); + AddStep("click notification", () => + { + var scrollContainer = (OsuScrollContainer)container.NotificationOverlay.Children.Last(); + var flowContainer = scrollContainer.Children.OfType>().First(); + var notification = flowContainer.First(); + + InputManager.MoveMouseTo(notification); + InputManager.Click(MouseButton.Left); + }); + AddAssert("check master volume", () => audioManager.Volume.IsDefault); + + AddStep("restart player", () => + { + var lastPlayer = player; + player = null; + lastPlayer.Restart(); + }); + } + + private class TestPlayerLoaderContainer : Container + { + [Cached] + public readonly NotificationOverlay NotificationOverlay; + + [Cached] + public readonly VolumeOverlay VolumeOverlay; + + public TestPlayerLoaderContainer(IScreen screen) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new OsuScreenStack(screen) + { + RelativeSizeAxes = Axes.Both, + }, + NotificationOverlay = new NotificationOverlay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, + VolumeOverlay = new VolumeOverlay + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + } + }; + } + } + private class TestPlayerLoader : PlayerLoader { public new VisualSettings VisualSettings => base.VisualSettings; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 8f2435d2f7..053e11104f 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -530,7 +530,7 @@ namespace osu.Game.Screens.Play } /// - /// Sets to , reserved for testing. + /// Sets to false, reserved for testing. /// public static void ResetNotificationLock() { From 9a31ccd2e34bc56960e714db87bda72c5fb6282a Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Thu, 26 Sep 2019 14:05:43 +0200 Subject: [PATCH 30/85] Add missing test cases for master, track and mute button This also modifies the reset player method to make it possible to set something before the player is loaded but after the container has loaded. --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index eb54eca0cb..e1a2cdcca3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -41,7 +41,8 @@ namespace osu.Game.Tests.Visual.Gameplay /// /// If the test player should behave like the production one. /// An action to run before player load but after bindable leases are returned. - public void ResetPlayer(bool interactive, Action beforeLoadAction = null) + /// An action to run after container load. + public void ResetPlayer(bool interactive, Action beforeLoadAction = null, Action afterLoadAction = null) { audioManager.Volume.SetDefault(); @@ -51,7 +52,11 @@ namespace osu.Game.Tests.Visual.Gameplay Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); InputManager.Child = container = new TestPlayerLoaderContainer( - loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive))); + loader = new TestPlayerLoader(() => + { + afterLoadAction?.Invoke(); + return player = new TestPlayer(interactive, interactive); + })); } [Test] @@ -115,11 +120,26 @@ namespace osu.Game.Tests.Visual.Gameplay } [Test] - public void TestMutedNotification() - { - AddStep("reset notification", PlayerLoader.ResetNotificationLock); + public void TestMutedNotificationMasterVolume() => addVolumeSteps("master volume", () => audioManager.Volume.Value = 0, null, () => audioManager.Volume.IsDefault); - AddStep("load player", () => ResetPlayer(false, () => audioManager.Volume.Value = 0)); + [Test] + public void TestMutedNotificationTrackVolume() => addVolumeSteps("music volume", () => audioManager.Volume.Value = 0, null, () => audioManager.Volume.IsDefault); + + [Test] + public void TestMutedNotificationMuteButton() => addVolumeSteps("mute button", null, () => container.VolumeOverlay.IsMuted.Value = true, () => !container.VolumeOverlay.IsMuted.Value); + + /// + /// Created for avoiding copy pasting code for the same steps. + /// + /// What part of the volume system is checked + /// The action to be invoked to set the volume before loading + /// The action to be invoked to set the volume after loading + /// The function to be invoked and checked + private void addVolumeSteps(string volumeName, Action beforeLoad, Action afterLoad, Func assert) + { + AddStep("reset notification lock", PlayerLoader.ResetNotificationLock); + + AddStep("load player", () => ResetPlayer(false, beforeLoad, afterLoad)); AddUntilStep("wait for player", () => player.IsLoaded); AddAssert("check for notification", () => container.NotificationOverlay.UnreadCount.Value == 1); @@ -132,14 +152,8 @@ namespace osu.Game.Tests.Visual.Gameplay InputManager.MoveMouseTo(notification); InputManager.Click(MouseButton.Left); }); - AddAssert("check master volume", () => audioManager.Volume.IsDefault); - AddStep("restart player", () => - { - var lastPlayer = player; - player = null; - lastPlayer.Restart(); - }); + AddAssert("check " + volumeName, assert); } private class TestPlayerLoaderContainer : Container From b50ef8ffa4855cecb9ae195accd455669b279feb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 27 Sep 2019 13:15:33 +0800 Subject: [PATCH 31/85] Allow null NotificationManager --- osu.Game/Screens/Play/PlayerLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 053e11104f..6cde7522d4 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Play private IdleTracker idleTracker; - [Resolved] + [Resolved(CanBeNull = true)] private NotificationOverlay notificationOverlay { get; set; } [Resolved] @@ -164,7 +164,7 @@ namespace osu.Game.Screens.Play //Checks if the notification has not been shown yet and also if master volume is muted, track/music volume is muted or if the whole game is muted. if (!muteWarningShownOnce && (volumeOverlay.IsMuted.Value || audio.Volume.Value <= audio.Volume.MinValue || audio.VolumeTrack.Value <= audio.VolumeTrack.MinValue)) { - notificationOverlay.Post(new MutedNotification()); + notificationOverlay?.Post(new MutedNotification()); muteWarningShownOnce = true; } } From f4f5a7e9c8be2b2d0f48a9b5be9a8761d4b856b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 27 Sep 2019 13:20:17 +0800 Subject: [PATCH 32/85] Fix test regressions --- osu.Game/Screens/Play/PlayerLoader.cs | 32 +++++++++++++++------------ 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 6cde7522d4..157ff8fcd4 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -59,17 +59,6 @@ namespace osu.Game.Screens.Play private IdleTracker idleTracker; - [Resolved(CanBeNull = true)] - private NotificationOverlay notificationOverlay { get; set; } - - [Resolved] - private VolumeOverlay volumeOverlay { get; set; } - - [Resolved] - private AudioManager audioManager { get; set; } - - private static bool muteWarningShownOnce; - public PlayerLoader(Func createPlayer) { this.createPlayer = createPlayer; @@ -159,10 +148,24 @@ namespace osu.Game.Screens.Play content.FadeOut(250); } - private void checkVolume(AudioManager audio) + [Resolved(CanBeNull = true)] + private NotificationOverlay notificationOverlay { get; set; } + + [Resolved(CanBeNull = true)] + private VolumeOverlay volumeOverlay { get; set; } + + [Resolved] + private AudioManager audioManager { get; set; } + + private static bool muteWarningShownOnce; + + private void checkVolume() { + if (muteWarningShownOnce) + return; + //Checks if the notification has not been shown yet and also if master volume is muted, track/music volume is muted or if the whole game is muted. - if (!muteWarningShownOnce && (volumeOverlay.IsMuted.Value || audio.Volume.Value <= audio.Volume.MinValue || audio.VolumeTrack.Value <= audio.VolumeTrack.MinValue)) + if (volumeOverlay?.IsMuted.Value == true || audioManager.Volume.Value <= audioManager.Volume.MinValue || audioManager.VolumeTrack.Value <= audioManager.VolumeTrack.MinValue) { notificationOverlay?.Post(new MutedNotification()); muteWarningShownOnce = true; @@ -213,7 +216,8 @@ namespace osu.Game.Screens.Play { inputManager = GetContainingInputManager(); base.LoadComplete(); - checkVolume(audioManager); + + checkVolume(); } private ScheduledDelegate pushDebounce; From 06c32d52dca8e465d34546af08505f234f7183a5 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Fri, 27 Sep 2019 09:19:39 +0200 Subject: [PATCH 33/85] Change wrong volume bindable used in test --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index e1a2cdcca3..6866fd4c62 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestMutedNotificationMasterVolume() => addVolumeSteps("master volume", () => audioManager.Volume.Value = 0, null, () => audioManager.Volume.IsDefault); [Test] - public void TestMutedNotificationTrackVolume() => addVolumeSteps("music volume", () => audioManager.Volume.Value = 0, null, () => audioManager.Volume.IsDefault); + public void TestMutedNotificationTrackVolume() => addVolumeSteps("music volume", () => audioManager.VolumeTrack.Value = 0, null, () => audioManager.VolumeTrack.IsDefault); [Test] public void TestMutedNotificationMuteButton() => addVolumeSteps("mute button", null, () => container.VolumeOverlay.IsMuted.Value = true, () => !container.VolumeOverlay.IsMuted.Value); From 04ac414249a30de3ea34a734a2c0e08183a27af7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 1 Oct 2019 13:48:56 +0900 Subject: [PATCH 34/85] Fix memory leaks due to audio track recycle order --- osu.Game/Beatmaps/BindableBeatmap.cs | 23 ----------------------- osu.Game/OsuGameBase.cs | 6 ++++++ osu.Game/Tests/Visual/OsuTestScene.cs | 9 ++++++--- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/osu.Game/Beatmaps/BindableBeatmap.cs b/osu.Game/Beatmaps/BindableBeatmap.cs index af627cc6a9..39c633e282 100644 --- a/osu.Game/Beatmaps/BindableBeatmap.cs +++ b/osu.Game/Beatmaps/BindableBeatmap.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Diagnostics; using osu.Framework.Bindables; namespace osu.Game.Beatmaps @@ -12,31 +11,9 @@ namespace osu.Game.Beatmaps /// public abstract class BindableBeatmap : NonNullableBindable { - private WorkingBeatmap lastBeatmap; - protected BindableBeatmap(WorkingBeatmap defaultValue) : base(defaultValue) { - BindValueChanged(b => updateAudioTrack(b.NewValue), true); - } - - private void updateAudioTrack(WorkingBeatmap beatmap) - { - var trackLoaded = lastBeatmap?.TrackLoaded ?? false; - - // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) - if (!trackLoaded || lastBeatmap?.Track != beatmap.Track) - { - if (trackLoaded) - { - Debug.Assert(lastBeatmap != null); - Debug.Assert(lastBeatmap.Track != null); - - lastBeatmap.RecycleTrack(); - } - } - - lastBeatmap = beatmap; } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 59a5e38b2c..a9a9693c76 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -203,6 +203,12 @@ namespace osu.Game Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8)); beatmap = new OsuBindableBeatmap(defaultBeatmap); + beatmap.BindValueChanged(b => ScheduleAfterChildren(() => + { + // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) + if (b.OldValue?.TrackLoaded == true && b.OldValue?.Track != b.NewValue?.Track) + b.OldValue.RecycleTrack(); + })); dependencies.CacheAs>(beatmap); dependencies.CacheAs(beatmap); diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 8e98d51962..cf128e058f 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -73,10 +73,13 @@ namespace osu.Game.Tests.Visual // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures var working = new DummyWorkingBeatmap(parent.Get(), parent.Get()); - beatmap = new OsuTestBeatmap(working) + beatmap = new OsuTestBeatmap(working) { Default = working }; + beatmap.BindValueChanged(b => ScheduleAfterChildren(() => { - Default = working - }; + // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) + if (b.OldValue?.TrackLoaded == true && b.OldValue?.Track != b.NewValue?.Track) + b.OldValue.RecycleTrack(); + })); Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); From cc533e8fe471c69bb0db52915a803ee1f7ad2a2f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 1 Oct 2019 17:24:47 +0900 Subject: [PATCH 35/85] Remove BindableBeatmap --- osu.Game/Beatmaps/BindableBeatmap.cs | 19 ------------------- osu.Game/OsuGameBase.cs | 10 +--------- osu.Game/Tests/Visual/OsuTestScene.cs | 14 +++----------- 3 files changed, 4 insertions(+), 39 deletions(-) delete mode 100644 osu.Game/Beatmaps/BindableBeatmap.cs diff --git a/osu.Game/Beatmaps/BindableBeatmap.cs b/osu.Game/Beatmaps/BindableBeatmap.cs deleted file mode 100644 index 39c633e282..0000000000 --- a/osu.Game/Beatmaps/BindableBeatmap.cs +++ /dev/null @@ -1,19 +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.Bindables; - -namespace osu.Game.Beatmaps -{ - /// - /// A for the beatmap. - /// This should be used sparingly in-favour of . - /// - public abstract class BindableBeatmap : NonNullableBindable - { - protected BindableBeatmap(WorkingBeatmap defaultValue) - : base(defaultValue) - { - } - } -} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index a9a9693c76..8578517a17 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -202,7 +202,7 @@ namespace osu.Game // this adds a global reduction of track volume for the time being. Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8)); - beatmap = new OsuBindableBeatmap(defaultBeatmap); + beatmap = new NonNullableBindable(defaultBeatmap); beatmap.BindValueChanged(b => ScheduleAfterChildren(() => { // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) @@ -298,14 +298,6 @@ namespace osu.Game public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray(); - private class OsuBindableBeatmap : BindableBeatmap - { - public OsuBindableBeatmap(WorkingBeatmap defaultValue) - : base(defaultValue) - { - } - } - private class OsuUserInputManager : UserInputManager { protected override MouseButtonEventManager CreateButtonManagerFor(MouseButton button) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index cf128e058f..96b39b303e 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -28,9 +28,9 @@ namespace osu.Game.Tests.Visual { [Cached(typeof(Bindable))] [Cached(typeof(IBindable))] - private OsuTestBeatmap beatmap; + private NonNullableBindable beatmap; - protected BindableBeatmap Beatmap => beatmap; + protected Bindable Beatmap => beatmap; [Cached] [Cached(typeof(IBindable))] @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures var working = new DummyWorkingBeatmap(parent.Get(), parent.Get()); - beatmap = new OsuTestBeatmap(working) { Default = working }; + beatmap = new NonNullableBindable(working) { Default = working }; beatmap.BindValueChanged(b => ScheduleAfterChildren(() => { // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) @@ -320,13 +320,5 @@ namespace osu.Game.Tests.Visual public void RunTestBlocking(TestScene test) => runner.RunTestBlocking(test); } - - private class OsuTestBeatmap : BindableBeatmap - { - public OsuTestBeatmap(WorkingBeatmap defaultValue) - : base(defaultValue) - { - } - } } } From 208b9a4eba5c5df080683456afae698feafe99c9 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Tue, 1 Oct 2019 20:47:53 +0700 Subject: [PATCH 36/85] Add new virtual float for username to timestamp padding --- .../Visual/Online/TestSceneStandAloneChatDisplay.cs | 12 ++++++++++++ osu.Game/Online/Chat/StandAloneChatDisplay.cs | 1 + osu.Game/Overlays/Chat/ChatLine.cs | 8 +++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index 91006bc0d9..3c5641fcd6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -32,6 +32,12 @@ namespace osu.Game.Tests.Visual.Online Id = 4, }; + private readonly User longUsernameUser = new User + { + Username = "Very Long Long Username", + Id = 5, + }; + [Cached] private ChannelManager channelManager = new ChannelManager(); @@ -99,6 +105,12 @@ namespace osu.Game.Tests.Visual.Online Sender = admin, Content = "Okay okay, calm down guys. Let's do this!" })); + + AddStep("message from long username", () => testChannel.AddNewMessages(new Message(sequence++) + { + Sender = longUsernameUser, + Content = "Hi guys, my new username is lit!" + })); } } } diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 9dab2f2aba..00defd5f34 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -146,6 +146,7 @@ namespace osu.Game.Online.Chat protected override float HorizontalPadding => 10; protected override float MessagePadding => 120; + protected override float TimestampPadding => 130; public StandAloneMessage(Message message) : base(message) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 7596231a3d..1f5f5d3ff6 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -31,7 +31,9 @@ namespace osu.Game.Overlays.Chat protected virtual float MessagePadding => default_message_padding; - private const float timestamp_padding = 65; + private const float default_timestamp_padding = 65; + + protected virtual float TimestampPadding => default_timestamp_padding; private const float default_horizontal_padding = 15; @@ -94,7 +96,7 @@ namespace osu.Game.Overlays.Chat Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - MaxWidth = default_message_padding - timestamp_padding + MaxWidth = default_message_padding - TimestampPadding }; if (hasBackground) @@ -149,7 +151,7 @@ namespace osu.Game.Overlays.Chat new MessageSender(message.Sender) { AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = timestamp_padding }, + Padding = new MarginPadding { Left = TimestampPadding }, Origin = Anchor.TopRight, Anchor = Anchor.TopRight, Child = effectedUsername, From 5f700f2ae9e57139cfa4aa0cb7e249363cc6d7e6 Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 1 Oct 2019 08:26:34 -0700 Subject: [PATCH 37/85] Simplify exit logic of screens with textboxes using back button receptor --- .../Graphics/UserInterface/FocusedTextBox.cs | 16 +++++----------- osu.Game/Graphics/UserInterface/OsuTextBox.cs | 17 +---------------- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 4 ---- .../Chat/Selection/ChannelSelectionOverlay.cs | 1 - osu.Game/Overlays/ChatOverlay.cs | 1 - osu.Game/Overlays/Music/FilterControl.cs | 3 --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 1 - .../SearchableList/SearchableListOverlay.cs | 2 -- osu.Game/Overlays/SettingsPanel.cs | 1 - .../Screens/Multi/Lounge/LoungeSubScreen.cs | 2 -- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 9 +-------- osu.Game/Screens/Select/FilterControl.cs | 8 +------- osu.Game/Screens/Select/SongSelect.cs | 5 ----- 13 files changed, 8 insertions(+), 62 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index f873db0dcb..62fbb3592c 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -2,22 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using osuTK.Graphics; -using System; using osu.Framework.Allocation; using osu.Framework.Input.Events; using osu.Framework.Platform; using osu.Game.Input.Bindings; using osuTK.Input; +using osu.Framework.Input.Bindings; namespace osu.Game.Graphics.UserInterface { /// /// A textbox which holds focus eagerly. /// - public class FocusedTextBox : OsuTextBox + public class FocusedTextBox : OsuTextBox, IKeyBindingHandler { - public Action Exit; - private bool focus; private bool allowImmediateFocus => host?.OnScreenKeyboardOverlapsGameWindow != true; @@ -68,7 +66,7 @@ namespace osu.Game.Graphics.UserInterface return base.OnKeyDown(e); } - public override bool OnPressed(GlobalAction action) + public bool OnPressed(GlobalAction action) { if (action == GlobalAction.Back) { @@ -79,14 +77,10 @@ namespace osu.Game.Graphics.UserInterface } } - return base.OnPressed(action); + return false; } - protected override void KillFocus() - { - base.KillFocus(); - Exit?.Invoke(); - } + public bool OnReleased(GlobalAction action) => false; public override bool RequestsFocus => HoldFocus; } diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs index 89de91bc9b..1cac4d76ab 100644 --- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs @@ -8,13 +8,11 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Sprites; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Game.Input.Bindings; namespace osu.Game.Graphics.UserInterface { - public class OsuTextBox : TextBox, IKeyBindingHandler + public class OsuTextBox : TextBox { protected override float LeftRightPadding => 10; @@ -57,18 +55,5 @@ namespace osu.Game.Graphics.UserInterface } protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: CalculatedTextSize) }; - - public virtual bool OnPressed(GlobalAction action) - { - if (action == GlobalAction.Back) - { - KillFocus(); - return true; - } - - return false; - } - - public bool OnReleased(GlobalAction action) => false; } } diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 9dab2f2aba..b9edc36dfb 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -21,8 +21,6 @@ namespace osu.Game.Online.Chat { public readonly Bindable Channel = new Bindable(); - public Action Exit; - private readonly FocusedTextBox textbox; protected ChannelManager ChannelManager; @@ -66,8 +64,6 @@ namespace osu.Game.Online.Chat Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, }); - - textbox.Exit += () => Exit?.Invoke(); } Channel.BindValueChanged(channelChanged); diff --git a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs index e0ded11ec9..621728830a 100644 --- a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs @@ -119,7 +119,6 @@ namespace osu.Game.Overlays.Chat.Selection { RelativeSizeAxes = Axes.X, PlaceholderText = @"Search", - Exit = Hide, }, }, }, diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 6f848c7627..0cadbdfd31 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -138,7 +138,6 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Height = 1, PlaceholderText = "type your message", - Exit = Hide, OnCommit = postMessage, ReleaseFocusOnCommit = false, HoldFocus = true, diff --git a/osu.Game/Overlays/Music/FilterControl.cs b/osu.Game/Overlays/Music/FilterControl.cs index 99017579a2..278bb55170 100644 --- a/osu.Game/Overlays/Music/FilterControl.cs +++ b/osu.Game/Overlays/Music/FilterControl.cs @@ -31,7 +31,6 @@ namespace osu.Game.Overlays.Music { RelativeSizeAxes = Axes.X, Height = 40, - Exit = () => ExitRequested?.Invoke(), }, new CollectionsDropdown { @@ -47,8 +46,6 @@ namespace osu.Game.Overlays.Music private void current_ValueChanged(ValueChangedEvent e) => FilterChanged?.Invoke(e.NewValue); - public Action ExitRequested; - public Action FilterChanged; public class FilterTextBox : SearchTextBox diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index ae81a6c117..bb88960280 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -63,7 +63,6 @@ namespace osu.Game.Overlays.Music { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - ExitRequested = Hide, FilterChanged = search => list.Filter(search), Padding = new MarginPadding(10), }, diff --git a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs index 293ee4bcda..177f731f12 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs @@ -88,8 +88,6 @@ namespace osu.Game.Overlays.SearchableList }, }, }; - - Filter.Search.Exit = Hide; } protected override void Update() diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 9dd0def453..37e7b62483 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -91,7 +91,6 @@ namespace osu.Game.Overlays Top = 20, Bottom = 20 }, - Exit = Hide, }, Footer = CreateFooter() }, diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 7f8e690516..0a48f761cf 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -69,8 +69,6 @@ namespace osu.Game.Screens.Multi.Lounge }, }, }; - - Filter.Search.Exit += this.Exit; } protected override void UpdateAfterChildren() diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index f3e10db444..c2bb7da6b5 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -62,7 +62,6 @@ namespace osu.Game.Screens.Multi.Match [BackgroundDependencyLoader] private void load() { - MatchChatDisplay chat; Components.Header header; Info info; GridContainer bottomRow; @@ -122,7 +121,7 @@ namespace osu.Game.Screens.Multi.Match Vertical = 10, }, RelativeSizeAxes = Axes.Both, - Child = chat = new MatchChatDisplay + Child = new MatchChatDisplay { RelativeSizeAxes = Axes.Both } @@ -159,12 +158,6 @@ namespace osu.Game.Screens.Multi.Match bottomRow.FadeTo(settingsDisplayed ? 0 : 1, fade_duration, Easing.OutQuint); }, true); - chat.Exit += () => - { - if (this.IsCurrentScreen()) - this.Exit(); - }; - beatmapManager.ItemAdded += beatmapAdded; } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 91f1ca0307..8755c3fda6 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -49,8 +49,6 @@ namespace osu.Game.Screens.Select return criteria; } - public Action Exit; - private readonly SearchTextBox searchTextBox; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => @@ -75,11 +73,7 @@ namespace osu.Game.Screens.Select Origin = Anchor.TopRight, Children = new Drawable[] { - searchTextBox = new SearchTextBox - { - RelativeSizeAxes = Axes.X, - Exit = () => Exit?.Invoke(), - }, + searchTextBox = new SearchTextBox { RelativeSizeAxes = Axes.X }, new Box { RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index d40dd9414a..5ab49fa2b9 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -171,11 +171,6 @@ namespace osu.Game.Screens.Select Height = FilterControl.HEIGHT, FilterChanged = c => Carousel.Filter(c), Background = { Width = 2 }, - Exit = () => - { - if (this.IsCurrentScreen()) - this.Exit(); - }, }, } }, From ff6367fa4b113052fd835a05ff776049e3ca818f Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 1 Oct 2019 08:26:45 -0700 Subject: [PATCH 38/85] Make back button glow when pressing escape --- osu.Game/Graphics/UserInterface/BackButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs index 62c33b9a39..23565e8742 100644 --- a/osu.Game/Graphics/UserInterface/BackButton.cs +++ b/osu.Game/Graphics/UserInterface/BackButton.cs @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface public BackButton(Receptor receptor) { - receptor.OnBackPressed = () => Action?.Invoke(); + receptor.OnBackPressed = () => button.Click(); Size = TwoLayerButton.SIZE_EXTENDED; From e3502f52008e01e6de850ab7820332a9e6f2a694 Mon Sep 17 00:00:00 2001 From: Joehu Date: Tue, 1 Oct 2019 08:37:08 -0700 Subject: [PATCH 39/85] Fix typo on Key.Escape comment --- osu.Game/Graphics/UserInterface/FocusedTextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 62fbb3592c..0b183c0ec9 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -61,7 +61,7 @@ namespace osu.Game.Graphics.UserInterface if (!HasFocus) return false; if (e.Key == Key.Escape) - return false; // disable the framework-level handling of escape key for confority (we use GlobalAction.Back). + return false; // disable the framework-level handling of escape key for conformity (we use GlobalAction.Back). return base.OnKeyDown(e); } From 2ac5e0bfa0b6961eb07e09f8995b5d041b449561 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Tue, 1 Oct 2019 17:39:01 +0200 Subject: [PATCH 40/85] Make use of SessionStatics --- osu.Game/Configuration/SessionStatics.cs | 2 ++ osu.Game/Screens/Play/PlayerLoader.cs | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 818a95c0be..40b2adb867 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -11,11 +11,13 @@ namespace osu.Game.Configuration protected override void InitialiseDefaults() { Set(Static.LoginOverlayDisplayed, false); + Set(Static.MutedAudioNotificationShownOnce, false); } } public enum Static { LoginOverlayDisplayed, + MutedAudioNotificationShownOnce } } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 157ff8fcd4..635201879b 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -15,6 +16,7 @@ using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -71,8 +73,10 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load() + private void load(SessionStatics sessionStatics) { + muteWarningShownOnce = sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce); + InternalChild = (content = new LogoTrackingContainer { Anchor = Anchor.Centre, @@ -157,18 +161,18 @@ namespace osu.Game.Screens.Play [Resolved] private AudioManager audioManager { get; set; } - private static bool muteWarningShownOnce; + private Bindable muteWarningShownOnce; private void checkVolume() { - if (muteWarningShownOnce) + if (muteWarningShownOnce.Value) return; //Checks if the notification has not been shown yet and also if master volume is muted, track/music volume is muted or if the whole game is muted. if (volumeOverlay?.IsMuted.Value == true || audioManager.Volume.Value <= audioManager.Volume.MinValue || audioManager.VolumeTrack.Value <= audioManager.VolumeTrack.MinValue) { notificationOverlay?.Post(new MutedNotification()); - muteWarningShownOnce = true; + muteWarningShownOnce.Value = true; } } From 5f399add82ece40b2f927982f0762d1d86369c04 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Tue, 1 Oct 2019 18:15:40 +0200 Subject: [PATCH 41/85] Resolve @iiSaLMaN 's suggested changes --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 6 +++++- osu.Game/Screens/Play/PlayerLoader.cs | 8 -------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 6866fd4c62..74ae641bfe 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; using osu.Framework.Screens; +using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; @@ -36,6 +37,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private AudioManager audioManager { get; set; } + [Resolved] + private SessionStatics sessionStatics { get; set; } + /// /// Sets the input manager child to a new test player loader container instance. /// @@ -137,7 +141,7 @@ namespace osu.Game.Tests.Visual.Gameplay /// The function to be invoked and checked private void addVolumeSteps(string volumeName, Action beforeLoad, Action afterLoad, Func assert) { - AddStep("reset notification lock", PlayerLoader.ResetNotificationLock); + AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce).Value = false); AddStep("load player", () => ResetPlayer(false, beforeLoad, afterLoad)); AddUntilStep("wait for player", () => player.IsLoaded); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 635201879b..7f8ef60a5d 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -536,13 +536,5 @@ namespace osu.Game.Screens.Play }; } } - - /// - /// Sets to false, reserved for testing. - /// - public static void ResetNotificationLock() - { - muteWarningShownOnce = false; - } } } From b6dd610af847b9d15c7a8fcb761110f7fd40c18f Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Tue, 1 Oct 2019 23:18:03 +0700 Subject: [PATCH 42/85] Apply reviews --- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 2 +- osu.Game/Overlays/Chat/ChatLine.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 00defd5f34..6a94cec4fd 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -146,7 +146,7 @@ namespace osu.Game.Online.Chat protected override float HorizontalPadding => 10; protected override float MessagePadding => 120; - protected override float TimestampPadding => 130; + protected override float TimestampPadding => 50; public StandAloneMessage(Message message) : base(message) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 1f5f5d3ff6..db378bde73 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Chat Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - MaxWidth = default_message_padding - TimestampPadding + MaxWidth = MessagePadding - TimestampPadding }; if (hasBackground) @@ -151,7 +151,6 @@ namespace osu.Game.Overlays.Chat new MessageSender(message.Sender) { AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = TimestampPadding }, Origin = Anchor.TopRight, Anchor = Anchor.TopRight, Child = effectedUsername, From faf8fe132ecc0fc9971406d25a09109088fa4b32 Mon Sep 17 00:00:00 2001 From: HDragonHR Date: Wed, 2 Oct 2019 12:26:46 +0800 Subject: [PATCH 43/85] Change bindable int to float --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/Graphics/Containers/HoldToConfirmContainer.cs | 4 ++-- .../Settings/Sections/Graphics/UserInterfaceSettings.cs | 6 +++--- osu.Game/Screens/Menu/MainMenu.cs | 4 ++-- osu.Game/Screens/Play/HUD/HoldForMenuButton.cs | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 64b1f2d7bc..c0ce08ba08 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -114,7 +114,7 @@ namespace osu.Game.Configuration Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f); - Set(OsuSetting.UIHoldActivationDelay, 200, 0, 500); + Set(OsuSetting.UIHoldActivationDelay, 200f, 0f, 500f, 50f); Set(OsuSetting.IntroSequence, IntroSequence.Triangles); } diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index 5d549ba217..fcf445a878 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -30,12 +30,12 @@ namespace osu.Game.Graphics.Containers public Bindable Progress = new BindableDouble(); - private Bindable holdActivationDelay; + private Bindable holdActivationDelay; [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - holdActivationDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); + holdActivationDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); } protected void BeginConfirm() diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs index a6956b7d9a..a8953ac3a2 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs @@ -27,16 +27,16 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics LabelText = "Parallax", Bindable = config.GetBindable(OsuSetting.MenuParallax) }, - new SettingsSlider + new SettingsSlider { LabelText = "Hold-to-confirm activation time", - Bindable = config.GetBindable(OsuSetting.UIHoldActivationDelay), + Bindable = config.GetBindable(OsuSetting.UIHoldActivationDelay), KeyboardStep = 50 }, }; } - private class TimeSlider : OsuSliderBar + private class TimeSlider : OsuSliderBar { public override string TooltipText => Current.Value.ToString("N0") + "ms"; } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 16e9d67cc3..c195ed6cb6 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens.Menu protected override BackgroundScreen CreateBackground() => background; - private Bindable holdDelay; + private Bindable holdDelay; private Bindable loginDisplayed; private ExitConfirmOverlay exitConfirmOverlay; @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader(true)] private void load(DirectOverlay direct, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics) { - holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); + holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); if (host.CanExit) diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index 2dc50326a8..a05937801c 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -63,11 +63,11 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private OsuConfigManager config { get; set; } - private Bindable activationDelay; + private Bindable activationDelay; protected override void LoadComplete() { - activationDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); + activationDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); activationDelay.BindValueChanged(v => { text.Text = v.NewValue > 0 From dfaa9531f8f4fc6470a5a4bb60bea87fc44eed76 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 2 Oct 2019 18:48:50 +0900 Subject: [PATCH 44/85] Only lock the database for the duration of a deletion --- osu.Game/Database/ArchiveModelManager.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 17d1bd822e..2cc1e016d1 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -400,20 +400,17 @@ namespace osu.Game.Database int i = 0; - using (ContextFactory.GetForWrite()) + foreach (var b in items) { - foreach (var b in items) - { - if (notification.State == ProgressNotificationState.Cancelled) - // user requested abort - return; + if (notification.State == ProgressNotificationState.Cancelled) + // user requested abort + return; - notification.Text = $"Deleting {HumanisedModelName}s ({++i} of {items.Count})"; + notification.Text = $"Deleting {HumanisedModelName}s ({++i} of {items.Count})"; - Delete(b); + Delete(b); - notification.Progress = (float)i / items.Count; - } + notification.Progress = (float)i / items.Count; } notification.State = ProgressNotificationState.Completed; From 6929847b0870664c8f2568170cd08935cc876dd8 Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Wed, 2 Oct 2019 17:22:34 +0200 Subject: [PATCH 45/85] Remove redundant override --- osu.Game/Screens/Play/PlayerLoader.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 7f8ef60a5d..cd4b331fce 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -516,8 +516,6 @@ namespace osu.Game.Screens.Play public override bool IsImportant => true; - public override bool RequestsFocus => true; - [BackgroundDependencyLoader] private void load(OsuColour colours, AudioManager audioManager, NotificationOverlay notificationOverlay, VolumeOverlay volumeOverlay) { From a69b9f1148bbee373e6243452291cbab0bab0e16 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 2 Oct 2019 11:16:31 -0700 Subject: [PATCH 46/85] Fix alt-f4 being blocked during gameplay --- osu.Game/Screens/Play/Player.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 44be73b089..91d34c8056 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -299,7 +299,10 @@ namespace osu.Game.Screens.Play { if (!this.IsCurrentScreen()) return; - this.Exit(); + if (canPause) + Pause(); + else + this.Exit(); } public void Restart() @@ -508,12 +511,6 @@ namespace osu.Game.Screens.Play return true; } - if (canPause) - { - Pause(); - return true; - } - // ValidForResume is false when restarting if (ValidForResume) { From 752dd26a4f5a73823a9acbcbc5b518f9a9b6f4bf Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 2 Oct 2019 11:17:43 -0700 Subject: [PATCH 47/85] Fix alt-f4 being blocked in interface --- osu.Game/Screens/Menu/MainMenu.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index c195ed6cb6..df4803b2b6 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -81,8 +81,8 @@ namespace osu.Game.Screens.Menu { if (holdDelay.Value > 0) confirmAndExit(); - else - this.Exit(); + else if (!exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) + dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort())); } }); } @@ -253,12 +253,6 @@ namespace osu.Game.Screens.Menu public override bool OnExiting(IScreen next) { - if (!exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) - { - dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort())); - return true; - } - buttons.State = ButtonSystemState.Exit; this.FadeOut(3000); return base.OnExiting(next); From ff56453f1a259ed80d8ab6338ba4d8c9b93a27d3 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 2 Oct 2019 12:07:07 -0700 Subject: [PATCH 48/85] Fix test regressions --- osu.Game.Tests/Visual/Gameplay/TestScenePause.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 50583e43c4..c67001c3d8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -132,9 +132,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("exit", () => Player.Exit()); - confirmPaused(); - - exitAndConfirm(); + confirmExited(); } [Test] From 38fe519c91f9836f46b6a9347e4ceb97a96f0d67 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 2 Oct 2019 12:28:48 -0700 Subject: [PATCH 49/85] Remove unnecessary exitConfirmed condition check --- osu.Game/Screens/Menu/MainMenu.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index df4803b2b6..6deb29c2f2 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -80,9 +80,9 @@ namespace osu.Game.Screens.Menu Action = () => { if (holdDelay.Value > 0) - confirmAndExit(); - else if (!exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) - dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort())); + this.Exit(); + else if (dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) + dialogOverlay.Push(new ConfirmExitDialog(this.Exit, () => exitConfirmOverlay.Abort())); } }); } @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Menu OnEdit = delegate { this.Push(new Editor()); }, OnSolo = onSolo, OnMulti = delegate { this.Push(new Multiplayer()); }, - OnExit = confirmAndExit, + OnExit = this.Exit, } } }, @@ -129,12 +129,6 @@ namespace osu.Game.Screens.Menu preloadSongSelect(); } - private void confirmAndExit() - { - exitConfirmed = true; - this.Exit(); - } - private void preloadSongSelect() { if (songSelect == null) @@ -172,8 +166,6 @@ namespace osu.Game.Screens.Menu Beatmap.ValueChanged += beatmap_ValueChanged; } - private bool exitConfirmed; - protected override void LogoArriving(OsuLogo logo, bool resuming) { base.LogoArriving(logo, resuming); From 148089f160240fc408f5a68b72caa2b84dc8731e Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 2 Oct 2019 16:08:19 -0700 Subject: [PATCH 50/85] Revert "Remove unnecessary exitConfirmed condition check" This reverts commit 38fe519c91f9836f46b6a9347e4ceb97a96f0d67. --- osu.Game/Screens/Menu/MainMenu.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 6deb29c2f2..df4803b2b6 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -80,9 +80,9 @@ namespace osu.Game.Screens.Menu Action = () => { if (holdDelay.Value > 0) - this.Exit(); - else if (dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) - dialogOverlay.Push(new ConfirmExitDialog(this.Exit, () => exitConfirmOverlay.Abort())); + confirmAndExit(); + else if (!exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) + dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort())); } }); } @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Menu OnEdit = delegate { this.Push(new Editor()); }, OnSolo = onSolo, OnMulti = delegate { this.Push(new Multiplayer()); }, - OnExit = this.Exit, + OnExit = confirmAndExit, } } }, @@ -129,6 +129,12 @@ namespace osu.Game.Screens.Menu preloadSongSelect(); } + private void confirmAndExit() + { + exitConfirmed = true; + this.Exit(); + } + private void preloadSongSelect() { if (songSelect == null) @@ -166,6 +172,8 @@ namespace osu.Game.Screens.Menu Beatmap.ValueChanged += beatmap_ValueChanged; } + private bool exitConfirmed; + protected override void LogoArriving(OsuLogo logo, bool resuming) { base.LogoArriving(logo, resuming); From 80177885213d56d52f2386dc885784c0549a0db6 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 2 Oct 2019 16:08:24 -0700 Subject: [PATCH 51/85] Revert "Fix alt-f4 being blocked in interface" This reverts commit 752dd26a4f5a73823a9acbcbc5b518f9a9b6f4bf. --- osu.Game/Screens/Menu/MainMenu.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index df4803b2b6..c195ed6cb6 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -81,8 +81,8 @@ namespace osu.Game.Screens.Menu { if (holdDelay.Value > 0) confirmAndExit(); - else if (!exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) - dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort())); + else + this.Exit(); } }); } @@ -253,6 +253,12 @@ namespace osu.Game.Screens.Menu public override bool OnExiting(IScreen next) { + if (!exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) + { + dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort())); + return true; + } + buttons.State = ButtonSystemState.Exit; this.FadeOut(3000); return base.OnExiting(next); From f8eb07b21116e9ae3f01eba30cd626e50f097a32 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 11:23:21 +0900 Subject: [PATCH 52/85] Only lock database for the duration of a model restoration --- osu.Game/Database/ArchiveModelManager.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 2cc1e016d1..b567f0c0e3 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -436,20 +436,17 @@ namespace osu.Game.Database int i = 0; - using (ContextFactory.GetForWrite()) + foreach (var item in items) { - foreach (var item in items) - { - if (notification.State == ProgressNotificationState.Cancelled) - // user requested abort - return; + if (notification.State == ProgressNotificationState.Cancelled) + // user requested abort + return; - notification.Text = $"Restoring ({++i} of {items.Count})"; + notification.Text = $"Restoring ({++i} of {items.Count})"; - Undelete(item); + Undelete(item); - notification.Progress = (float)i / items.Count; - } + notification.Progress = (float)i / items.Count; } notification.State = ProgressNotificationState.Completed; From 897b3233afdd196157db7891bc7ad05b8a173e0e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 14:23:48 +0900 Subject: [PATCH 53/85] Add start time tracking to EditorBeatmap --- osu.Game/Rulesets/Objects/HitObject.cs | 20 ++++++++++++++++- osu.Game/Screens/Edit/EditorBeatmap.cs | 31 +++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 6c5627c5d2..a99fac09cc 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using Newtonsoft.Json; +using osu.Framework.Bindables; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -27,10 +28,16 @@ namespace osu.Game.Rulesets.Objects /// private const double control_point_leniency = 1; + public readonly Bindable StartTimeBindable = new Bindable(); + /// /// The time at which the HitObject starts. /// - public virtual double StartTime { get; set; } + public virtual double StartTime + { + get => StartTimeBindable.Value; + set => StartTimeBindable.Value = value; + } private List samples; @@ -67,6 +74,17 @@ namespace osu.Game.Rulesets.Objects [JsonIgnore] public IReadOnlyList NestedHitObjects => nestedHitObjects; + public HitObject() + { + StartTimeBindable.ValueChanged += time => + { + double offset = time.NewValue - time.OldValue; + + foreach (var nested in NestedHitObjects) + nested.StartTime += offset; + }; + } + /// /// Applies default values to this HitObject. /// diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index f0b6c62154..22aa133de7 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; @@ -15,12 +16,17 @@ namespace osu.Game.Screens.Edit { public event Action HitObjectAdded; public event Action HitObjectRemoved; + public event Action HitObjectChanged; + private readonly Dictionary> startTimeBindables = new Dictionary>(); private readonly Beatmap beatmap; public EditorBeatmap(Beatmap beatmap) { this.beatmap = beatmap; + + foreach (var obj in HitObjects) + trackStartTime(obj); } public BeatmapInfo BeatmapInfo @@ -37,7 +43,7 @@ namespace osu.Game.Screens.Edit public double TotalBreakTime => beatmap.TotalBreakTime; - IReadOnlyList IBeatmap.HitObjects => beatmap.HitObjects; + public IReadOnlyList HitObjects => beatmap.HitObjects; IReadOnlyList IBeatmap.HitObjects => beatmap.HitObjects; @@ -51,6 +57,8 @@ namespace osu.Game.Screens.Edit /// The to add. public void Add(T hitObject) { + trackStartTime(hitObject); + // Preserve existing sorting order in the beatmap var insertionIndex = beatmap.HitObjects.FindLastIndex(h => h.StartTime <= hitObject.StartTime); beatmap.HitObjects.Insert(insertionIndex + 1, hitObject); @@ -65,7 +73,28 @@ namespace osu.Game.Screens.Edit public void Remove(T hitObject) { if (beatmap.HitObjects.Remove(hitObject)) + { + var bindable = startTimeBindables[hitObject]; + bindable.UnbindAll(); + + startTimeBindables.Remove(hitObject); HitObjectRemoved?.Invoke(hitObject); + } + } + + private void trackStartTime(T hitObject) + { + startTimeBindables[hitObject] = hitObject.StartTimeBindable.GetBoundCopy(); + startTimeBindables[hitObject].ValueChanged += _ => + { + // For now we'll remove and re-add the hitobject. This is not optimal and can be improved if required. + beatmap.HitObjects.Remove(hitObject); + + var insertionIndex = beatmap.HitObjects.FindLastIndex(h => h.StartTime <= hitObject.StartTime); + beatmap.HitObjects.Insert(insertionIndex + 1, hitObject); + + HitObjectChanged?.Invoke(hitObject); + }; } /// From f2719afd0e7f7e445659e413e03edd32125db3f0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 14:27:40 +0900 Subject: [PATCH 54/85] Add tests for Editorbeatmap --- osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs | 154 +++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs diff --git a/osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs b/osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs new file mode 100644 index 0000000000..88c9f9cb6c --- /dev/null +++ b/osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs @@ -0,0 +1,154 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using Microsoft.EntityFrameworkCore.Internal; +using NUnit.Framework; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit; + +namespace osu.Game.Tests.Beatmaps +{ + [TestFixture] + public class EditorBeatmapTest + { + /// + /// Tests that the addition event is correctly invoked after a hitobject is added. + /// + [Test] + public void TestHitObjectAddEvent() + { + var editorBeatmap = new EditorBeatmap(new OsuBeatmap()); + + HitObject addedObject = null; + editorBeatmap.HitObjectAdded += h => addedObject = h; + + var hitCircle = new HitCircle(); + + editorBeatmap.Add(hitCircle); + Assert.That(addedObject, Is.EqualTo(hitCircle)); + } + + /// + /// Tests that the removal event is correctly invoked after a hitobject is removed. + /// + [Test] + public void HitObjectRemoveEvent() + { + var hitCircle = new HitCircle(); + var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } }); + + HitObject removedObject = null; + editorBeatmap.HitObjectRemoved += h => removedObject = h; + + editorBeatmap.Remove(hitCircle); + Assert.That(removedObject, Is.EqualTo(hitCircle)); + } + + /// + /// Tests that the changed event is correctly invoked after the start time of a hitobject is changed. + /// This tests for hitobjects which were already present before the editor beatmap was constructed. + /// + [Test] + public void TestInitialHitObjectStartTimeChangeEvent() + { + var hitCircle = new HitCircle(); + var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } }); + + HitObject changedObject = null; + editorBeatmap.HitObjectChanged += h => changedObject = h; + + hitCircle.StartTime = 1000; + Assert.That(changedObject, Is.EqualTo(hitCircle)); + } + + /// + /// Tests that the changed event is correctly invoked after the start time of a hitobject is changed. + /// This tests for hitobjects which were added to an existing editor beatmap. + /// + [Test] + public void TestAddedHitObjectStartTimeChangeEvent() + { + var editorBeatmap = new EditorBeatmap(new OsuBeatmap()); + + HitObject changedObject = null; + editorBeatmap.HitObjectChanged += h => changedObject = h; + + var hitCircle = new HitCircle(); + + editorBeatmap.Add(hitCircle); + Assert.That(changedObject, Is.Null); + + hitCircle.StartTime = 1000; + Assert.That(changedObject, Is.EqualTo(hitCircle)); + } + + /// + /// Tests that the channged event is not invoked after a hitobject is removed from the beatmap/ + /// + [Test] + public void TestRemovedHitObjectStartTimeChangeEvent() + { + var hitCircle = new HitCircle(); + var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } }); + + HitObject changedObject = null; + editorBeatmap.HitObjectChanged += h => changedObject = h; + + editorBeatmap.Remove(hitCircle); + Assert.That(changedObject, Is.Null); + + hitCircle.StartTime = 1000; + Assert.That(changedObject, Is.Null); + } + + /// + /// Tests that an added hitobject is correctly inserted to preserve the sorting order of the beatmap. + /// + [Test] + public void TestAddHitObjectInMiddle() + { + var editorBeatmap = new EditorBeatmap(new OsuBeatmap + { + HitObjects = + { + new HitCircle(), + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 2000 }, + } + }); + + var hitCircle = new HitCircle { StartTime = 1000 }; + editorBeatmap.Add(hitCircle); + Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1)); + Assert.That(editorBeatmap.HitObjects.IndexOf(hitCircle), Is.EqualTo(3)); + } + + /// + /// Tests that the beatmap remains correctly sorted after the start time of a hitobject is changed. + /// + [Test] + public void TestResortWhenStartTimeChanged() + { + var hitCircle = new HitCircle { StartTime = 1000 }; + var editorBeatmap = new EditorBeatmap(new OsuBeatmap + { + HitObjects = + { + new HitCircle(), + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 1000 }, + hitCircle, + new HitCircle { StartTime = 2000 }, + } + }); + + hitCircle.StartTime = 0; + Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1)); + Assert.That(editorBeatmap.HitObjects.IndexOf(hitCircle), Is.EqualTo(1)); + } + } +} From 3fb0b0b66833d107ef2e5e17f9930959deefeff2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 14:37:16 +0900 Subject: [PATCH 55/85] Rename to StartTimeChanged and add xmldocs --- osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs | 6 +++--- osu.Game/Screens/Edit/EditorBeatmap.cs | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs b/osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs index 88c9f9cb6c..98e630abd2 100644 --- a/osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Beatmaps var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } }); HitObject changedObject = null; - editorBeatmap.HitObjectChanged += h => changedObject = h; + editorBeatmap.StartTimeChanged += h => changedObject = h; hitCircle.StartTime = 1000; Assert.That(changedObject, Is.EqualTo(hitCircle)); @@ -74,7 +74,7 @@ namespace osu.Game.Tests.Beatmaps var editorBeatmap = new EditorBeatmap(new OsuBeatmap()); HitObject changedObject = null; - editorBeatmap.HitObjectChanged += h => changedObject = h; + editorBeatmap.StartTimeChanged += h => changedObject = h; var hitCircle = new HitCircle(); @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Beatmaps var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } }); HitObject changedObject = null; - editorBeatmap.HitObjectChanged += h => changedObject = h; + editorBeatmap.StartTimeChanged += h => changedObject = h; editorBeatmap.Remove(hitCircle); Assert.That(changedObject, Is.Null); diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 22aa133de7..c3a322ea36 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -14,9 +14,20 @@ namespace osu.Game.Screens.Edit public class EditorBeatmap : IEditorBeatmap where T : HitObject { + /// + /// Invoked when a is added to this . + /// public event Action HitObjectAdded; + + /// + /// Invoked when a is removed from this . + /// public event Action HitObjectRemoved; - public event Action HitObjectChanged; + + /// + /// Invoked when the start time of a in this was changed. + /// + public event Action StartTimeChanged; private readonly Dictionary> startTimeBindables = new Dictionary>(); private readonly Beatmap beatmap; @@ -93,7 +104,7 @@ namespace osu.Game.Screens.Edit var insertionIndex = beatmap.HitObjects.FindLastIndex(h => h.StartTime <= hitObject.StartTime); beatmap.HitObjects.Insert(insertionIndex + 1, hitObject); - HitObjectChanged?.Invoke(hitObject); + StartTimeChanged?.Invoke(hitObject); }; } From 2c13043c42b79fb166483862284fce652f3fb17f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 14:40:00 +0900 Subject: [PATCH 56/85] Hook up the event to HitObjectComposer --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index fc324d7021..a267d7c44d 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -132,6 +132,7 @@ namespace osu.Game.Rulesets.Edit editorBeatmap = new EditorBeatmap(playableBeatmap); editorBeatmap.HitObjectAdded += addHitObject; editorBeatmap.HitObjectRemoved += removeHitObject; + editorBeatmap.StartTimeChanged += updateHitObject; var dependencies = new DependencyContainer(parent); dependencies.CacheAs(editorBeatmap); @@ -162,12 +163,7 @@ namespace osu.Game.Rulesets.Edit }); } - private void addHitObject(HitObject hitObject) - { - beatmapProcessor?.PreProcess(); - hitObject.ApplyDefaults(playableBeatmap.ControlPointInfo, playableBeatmap.BeatmapInfo.BaseDifficulty); - beatmapProcessor?.PostProcess(); - } + private void addHitObject(HitObject hitObject) => updateHitObject(hitObject); private void removeHitObject(HitObject hitObject) { @@ -175,6 +171,13 @@ namespace osu.Game.Rulesets.Edit beatmapProcessor?.PostProcess(); } + private void updateHitObject(HitObject hitObject) + { + beatmapProcessor?.PreProcess(); + hitObject.ApplyDefaults(playableBeatmap.ControlPointInfo, playableBeatmap.BeatmapInfo.BaseDifficulty); + beatmapProcessor?.PostProcess(); + } + public override IEnumerable HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects; public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); From b28689c774c8a0e4bc029eda00cb5a4083ad040e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Oct 2019 15:00:47 +0800 Subject: [PATCH 57/85] Fix key counters appearing negative on intense beatmaps When `FrameStabilityContainer` decides it needs multiple updates on the same frame, it ends up with an elapsed time of zero. This was interacting badly with the condition used in `RulesetInputManager` to govern playback direction. I have changed this to use `Rate` as exposed by the frame stable clock. - Closes #6198. --- .../Rulesets/UI/FrameStabilityContainer.cs | 28 ++++++++++--------- osu.Game/Rulesets/UI/RulesetInputManager.cs | 4 +-- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 05d3c02381..74d3439c59 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -47,6 +47,11 @@ namespace osu.Game.Rulesets.UI private IFrameBasedClock parentGameplayClock; + /// + /// The current direction of playback to be exposed to frame stable children. + /// + private int direction; + [BackgroundDependencyLoader(true)] private void load(GameplayClock clock) { @@ -111,16 +116,12 @@ namespace osu.Game.Rulesets.UI validState = true; - manualClock.Rate = parentGameplayClock.Rate; - manualClock.IsRunning = parentGameplayClock.IsRunning; - var newProposedTime = parentGameplayClock.CurrentTime; try { if (!FrameStablePlayback) { - manualClock.CurrentTime = newProposedTime; requireMoreUpdateLoops = false; return; } @@ -128,9 +129,9 @@ namespace osu.Game.Rulesets.UI { // On the first update, frame-stability seeking would result in unexpected/unwanted behaviour. // Instead we perform an initial seek to the proposed time. - manualClock.CurrentTime = newProposedTime; - // do a second process to clear out ElapsedTime + // process frame (in addition to finally clause) to clear out ElapsedTime + manualClock.CurrentTime = newProposedTime; framedClock.ProcessFrame(); firstConsumption = false; @@ -144,11 +145,7 @@ namespace osu.Game.Rulesets.UI : Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time); } - if (!isAttached) - { - manualClock.CurrentTime = newProposedTime; - } - else + if (isAttached) { double? newTime = ReplayInputHandler.SetFrameFromTime(newProposedTime); @@ -156,9 +153,7 @@ namespace osu.Game.Rulesets.UI { // we shouldn't execute for this time value. probably waiting on more replay data. validState = false; - requireMoreUpdateLoops = true; - manualClock.CurrentTime = newProposedTime; return; } @@ -169,6 +164,13 @@ namespace osu.Game.Rulesets.UI } finally { + if (newProposedTime != manualClock.CurrentTime) + direction = newProposedTime > manualClock.CurrentTime ? 1 : -1; + + manualClock.CurrentTime = newProposedTime; + manualClock.Rate = Math.Abs(parentGameplayClock.Rate) * direction; + manualClock.IsRunning = parentGameplayClock.IsRunning; + // The manual clock time has changed in the above code. The framed clock now needs to be updated // to ensure that the its time is valid for our children before input is processed framedClock.ProcessFrame(); diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 98e27240d3..5cc213be41 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -137,9 +137,9 @@ namespace osu.Game.Rulesets.UI { } - public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action, Clock.ElapsedFrameTime > 0)); + public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action, Clock.Rate >= 0)); - public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action, Clock.ElapsedFrameTime > 0)); + public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action, Clock.Rate >= 0)); } #endregion From 652acac87fa69d2debfd9ee18872e08379c4ad0e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 16:14:42 +0900 Subject: [PATCH 58/85] Move placement movement event to BlueprintContainer --- .../Blueprints/HoldNotePlacementBlueprint.cs | 11 ++++------ .../Blueprints/ManiaPlacementBlueprint.cs | 9 ++++---- .../HitCircles/HitCirclePlacementBlueprint.cs | 13 ++--------- .../Sliders/SliderPlacementBlueprint.cs | 20 +++++------------ .../Spinners/SpinnerPlacementBlueprint.cs | 5 +++++ osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 8 ++++++- .../Compose/Components/BlueprintContainer.cs | 22 ++++++++++++++++++- 7 files changed, 48 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index 26115311f7..bcbc1ee527 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Input.Events; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; using osuTK; @@ -49,13 +48,13 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints private double originalStartTime; - protected override bool OnMouseMove(MouseMoveEvent e) + public override void UpdatePosition(Vector2 screenSpacePosition) { - base.OnMouseMove(e); + base.UpdatePosition(screenSpacePosition); if (PlacementBegun) { - var endTime = TimeAt(e.ScreenSpaceMousePosition); + var endTime = TimeAt(screenSpacePosition); HitObject.StartTime = endTime < originalStartTime ? endTime : originalStartTime; HitObject.Duration = Math.Abs(endTime - originalStartTime); @@ -65,10 +64,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints headPiece.Width = tailPiece.Width = SnappedWidth; headPiece.X = tailPiece.X = SnappedMousePosition.X; - originalStartTime = HitObject.StartTime = TimeAt(e.ScreenSpaceMousePosition); + originalStartTime = HitObject.StartTime = TimeAt(screenSpacePosition); } - - return true; } } } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index d3779e2e18..7ad38860dd 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -62,19 +62,18 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return base.OnMouseUp(e); } - protected override bool OnMouseMove(MouseMoveEvent e) + public override void UpdatePosition(Vector2 screenSpacePosition) { if (!PlacementBegun) - Column = ColumnAt(e.ScreenSpaceMousePosition); + Column = ColumnAt(screenSpacePosition); - if (Column == null) return false; + if (Column == null) return; SnappedWidth = Column.DrawWidth; // Snap to the column var parentPos = Parent.ToLocalSpace(Column.ToScreenSpace(new Vector2(Column.DrawWidth / 2, 0))); - SnappedMousePosition = new Vector2(parentPos.X, e.MousePosition.Y); - return true; + SnappedMousePosition = new Vector2(parentPos.X, Parent.ToLocalSpace(screenSpacePosition).Y); } protected double TimeAt(Vector2 screenSpacePosition) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs index a4050f0c31..0f6bee19bb 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs @@ -19,14 +19,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles InternalChild = new HitCirclePiece(HitObject); } - protected override void LoadComplete() - { - base.LoadComplete(); - - // Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame - HitObject.Position = Parent?.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position) ?? Vector2.Zero; - } - protected override bool OnClick(ClickEvent e) { HitObject.StartTime = EditorClock.CurrentTime; @@ -34,10 +26,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles return true; } - protected override bool OnMouseMove(MouseMoveEvent e) + public override void UpdatePosition(Vector2 screenSpacePosition) { - HitObject.Position = e.MousePosition; - return true; + HitObject.Position = ToLocalSpace(screenSpacePosition); } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 55de626d7d..62c879b05e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -47,28 +47,18 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders setState(PlacementState.Initial); } - protected override void LoadComplete() - { - base.LoadComplete(); - - // Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame - HitObject.Position = Parent?.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position) ?? Vector2.Zero; - } - - protected override bool OnMouseMove(MouseMoveEvent e) + public override void UpdatePosition(Vector2 screenSpacePosition) { switch (state) { case PlacementState.Initial: - HitObject.Position = e.MousePosition; - return true; + HitObject.Position = ToLocalSpace(screenSpacePosition); + break; case PlacementState.Body: - cursor = e.MousePosition - HitObject.Position; - return true; + cursor = ToLocalSpace(screenSpacePosition) - HitObject.Position; + break; } - - return false; } protected override bool OnClick(ClickEvent e) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs index 03d761c67f..730b8448de 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; +using osuTK; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners { @@ -43,5 +44,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners return true; } + + public override void UpdatePosition(Vector2 screenSpacePosition) + { + } } } diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 757c269358..290fd8d27d 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -108,6 +108,12 @@ namespace osu.Game.Rulesets.Edit placementHandler.EndPlacement(HitObject); } + /// + /// Updates the position of this to a new screen-space position. + /// + /// The screen-space position. + public abstract void UpdatePosition(Vector2 screenSpacePosition); + /// /// Invokes , /// refreshing and parameters for the . @@ -125,7 +131,7 @@ namespace osu.Game.Rulesets.Edit case ScrollEvent _: return false; - case MouseEvent _: + case MouseButtonEvent _: return true; default: diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 7d25fd5283..d96d88c2b9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; +using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Game.Rulesets.Edit; @@ -22,8 +23,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private Container placementBlueprintContainer; private PlacementBlueprint currentPlacement; - private SelectionHandler selectionHandler; + private InputManager inputManager; private IEnumerable selections => selectionBlueprints.Children.Where(c => c.IsAlive); @@ -66,6 +67,8 @@ namespace osu.Game.Screens.Edit.Compose.Components beatmap.HitObjectAdded += addBlueprintFor; beatmap.HitObjectRemoved += removeBlueprintFor; + + inputManager = GetContainingInputManager(); } private HitObjectCompositionTool currentTool; @@ -136,6 +139,17 @@ namespace osu.Game.Screens.Edit.Compose.Components return true; } + protected override bool OnMouseMove(MouseMoveEvent e) + { + if (currentPlacement != null) + { + currentPlacement.UpdatePosition(e.ScreenSpaceMousePosition); + return true; + } + + return base.OnMouseMove(e); + } + protected override void Update() { base.Update(); @@ -158,8 +172,14 @@ namespace osu.Game.Screens.Edit.Compose.Components currentPlacement = null; var blueprint = CurrentTool?.CreatePlacementBlueprint(); + if (blueprint != null) + { placementBlueprintContainer.Child = currentPlacement = blueprint; + + // Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame + blueprint.UpdatePosition(inputManager.CurrentState.Mouse.Position); + } } /// From f2ba87a1d2a2f66fa423a682a0eb2ef0f7edef9c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 16:28:56 +0900 Subject: [PATCH 59/85] Fix placement blueprint test scenes not working --- .../Visual/PlacementBlueprintTestScene.cs | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs index 2b177e264f..0688620b8e 100644 --- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs @@ -4,6 +4,8 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; @@ -18,16 +20,17 @@ namespace osu.Game.Tests.Visual protected Container HitObjectContainer; private PlacementBlueprint currentBlueprint; + private InputManager inputManager; + protected PlacementBlueprintTestScene() { - Add(HitObjectContainer = CreateHitObjectContainer()); + Add(HitObjectContainer = CreateHitObjectContainer().With(c => c.Clock = new FramedClock(new StopwatchClock()))); } [BackgroundDependencyLoader] private void load() { Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = 2; - Add(currentBlueprint = CreateBlueprint()); } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -38,6 +41,14 @@ namespace osu.Game.Tests.Visual return dependencies; } + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + Add(currentBlueprint = CreateBlueprint()); + } + public void BeginPlacement(HitObject hitObject) { } @@ -54,10 +65,27 @@ namespace osu.Game.Tests.Visual { } - protected virtual Container CreateHitObjectContainer() => new Container { RelativeSizeAxes = Axes.Both }; + protected override bool OnMouseMove(MouseMoveEvent e) + { + currentBlueprint.UpdatePosition(e.ScreenSpaceMousePosition); + return true; + } + + public override void Add(Drawable drawable) + { + base.Add(drawable); + + if (drawable is PlacementBlueprint blueprint) + { + blueprint.Show(); + blueprint.UpdatePosition(inputManager.CurrentState.Mouse.Position); + } + } protected virtual void AddHitObject(DrawableHitObject hitObject) => HitObjectContainer.Add(hitObject); + protected virtual Container CreateHitObjectContainer() => new Container { RelativeSizeAxes = Axes.Both }; + protected abstract DrawableHitObject CreateHitObject(HitObject hitObject); protected abstract PlacementBlueprint CreateBlueprint(); } From e9c73ce30fae825f2ed79150f3b4a098d346eb6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Oct 2019 16:21:14 +0800 Subject: [PATCH 60/85] Fix random failures on BeatmapCarousel filter test The "un-filter" step causes a `SelectNextRandom` invocation. If this happens to select a difficulty in set 3 other than the previously buffered difficulty #2, the subsequent test would fail. I've split this test out to remove the random element, but added a new assert to ensure buffered (previously visited?) difficulty is re-selected on return to the same set. --- .../SongSelect/TestSceneBeatmapCarousel.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 90c6c9065c..6bdd94db21 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -239,6 +239,18 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("Selection is non-null", () => currentSelection != null); setSelected(1, 3); + } + + [Test] + public void TestFilterRange() + { + loadBeatmaps(); + + // buffer the selection + setSelected(3, 2); + + setSelected(1, 3); + AddStep("Apply a range filter", () => carousel.Filter(new FilterCriteria { SearchText = "#3", @@ -249,9 +261,9 @@ namespace osu.Game.Tests.Visual.SongSelect IsLowerInclusive = true } }, false)); - waitForSelection(3, 2); - AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); + // should reselect the buffered selection. + waitForSelection(3, 2); } /// From ee34c5ccb457e582868ced659a6d0f952cb98665 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 18:21:00 +0900 Subject: [PATCH 61/85] Add a flip step to mania placement test scenes --- .../ManiaPlacementBlueprintTestScene.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs index 4b3786c30a..afde1c9521 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaPlacementBlueprintTestScene.cs @@ -27,8 +27,13 @@ namespace osu.Game.Rulesets.Mania.Tests [Cached(typeof(IReadOnlyList))] private IReadOnlyList mods { get; set; } = Array.Empty(); + [Cached(typeof(IScrollingInfo))] + private IScrollingInfo scrollingInfo; + protected ManiaPlacementBlueprintTestScene() { + scrollingInfo = ((ScrollingTestContainer)HitObjectContainer).ScrollingInfo; + Add(column = new Column(0) { Anchor = Anchor.Centre, @@ -36,15 +41,8 @@ namespace osu.Game.Rulesets.Mania.Tests AccentColour = Color4.OrangeRed, Clock = new FramedClock(new StopwatchClock()), // No scroll }); - } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - - dependencies.CacheAs(((ScrollingTestContainer)HitObjectContainer).ScrollingInfo); - - return dependencies; + AddStep("change direction", () => ((ScrollingTestContainer)HitObjectContainer).Flip()); } protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both }; From 754fbc59e1a96f2dc7da030dce850a0cb5231e78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 18:21:36 +0900 Subject: [PATCH 62/85] Fix note placement being offset --- .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index d3779e2e18..f7d21ddf55 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints // If we're scrolling downwards, a position of 0 is actually further away from the hit target // so we need to flip the vertical coordinate in the hitobject container's space - var hitObjectPos = Column.HitObjectContainer.ToLocalSpace(applyPositionOffset(screenSpacePosition, false)).Y; + var hitObjectPos = applyPositionOffset(Column.HitObjectContainer.ToLocalSpace(screenSpacePosition), false).Y; if (scrollingInfo.Direction.Value == ScrollingDirection.Down) hitObjectPos = hitObjectContainer.DrawHeight - hitObjectPos; From 0a409075be251fbc1acbebfdfa80f81204d8f0a6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 18:21:50 +0900 Subject: [PATCH 63/85] Fix note placement offset not working for down-scroll --- .../Blueprints/ManiaPlacementBlueprint.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index f7d21ddf55..83282f9990 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -111,8 +111,23 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints private Vector2 applyPositionOffset(Vector2 position, bool reverse) { - position.Y += (scrollingInfo.Direction.Value == ScrollingDirection.Up && !reverse ? -1 : 1) * NotePiece.NOTE_HEIGHT / 2; - return position; + float offset = 0; + + switch (scrollingInfo.Direction.Value) + { + case ScrollingDirection.Up: + offset = -NotePiece.NOTE_HEIGHT / 2; + break; + + case ScrollingDirection.Down: + offset = NotePiece.NOTE_HEIGHT / 2; + break; + } + + if (reverse) + offset = -offset; + + return new Vector2(position.X, position.Y + offset); } } } From 39369620fa530000cf22dc6b1643b932b738dda1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 18:22:06 +0900 Subject: [PATCH 64/85] Remove position offset from ColumnAt --- .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 83282f9990..a0feecee3b 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints } protected Column ColumnAt(Vector2 screenSpacePosition) - => composer.ColumnAt(applyPositionOffset(screenSpacePosition, false)); + => composer.ColumnAt(screenSpacePosition); private Vector2 applyPositionOffset(Vector2 position, bool reverse) { From f1ff22cf8bc1612aef947df2bdafd4e0e0d2cafd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 18:23:13 +0900 Subject: [PATCH 65/85] Fix hold note blueprint placing in the wrong direction --- .../Edit/Blueprints/ManiaPlacementBlueprint.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index a0feecee3b..4a187b1942 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -103,6 +103,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints scrollingInfo.TimeRange.Value, Column.HitObjectContainer.DrawHeight); + if (scrollingInfo.Direction.Value == ScrollingDirection.Down) + pos = Column.HitObjectContainer.DrawHeight - pos; + return applyPositionOffset(Column.HitObjectContainer.ToSpaceOfOtherDrawable(new Vector2(0, pos), Parent), true).Y; } From 80585d446c108268e3261933caf9ef2f3d56f1c6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 18:27:39 +0900 Subject: [PATCH 66/85] Split applyPositionOffset into two methods and add xmldocs --- .../Blueprints/ManiaPlacementBlueprint.cs | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 4a187b1942..e83c85b40e 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints // If we're scrolling downwards, a position of 0 is actually further away from the hit target // so we need to flip the vertical coordinate in the hitobject container's space - var hitObjectPos = applyPositionOffset(Column.HitObjectContainer.ToLocalSpace(screenSpacePosition), false).Y; + var hitObjectPos = mouseToHitObjectPosition(Column.HitObjectContainer.ToLocalSpace(screenSpacePosition)).Y; if (scrollingInfo.Direction.Value == ScrollingDirection.Down) hitObjectPos = hitObjectContainer.DrawHeight - hitObjectPos; @@ -106,31 +106,55 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints if (scrollingInfo.Direction.Value == ScrollingDirection.Down) pos = Column.HitObjectContainer.DrawHeight - pos; - return applyPositionOffset(Column.HitObjectContainer.ToSpaceOfOtherDrawable(new Vector2(0, pos), Parent), true).Y; + return hitObjectToMousePosition(Column.HitObjectContainer.ToSpaceOfOtherDrawable(new Vector2(0, pos), Parent)).Y; } protected Column ColumnAt(Vector2 screenSpacePosition) => composer.ColumnAt(screenSpacePosition); - private Vector2 applyPositionOffset(Vector2 position, bool reverse) + /// + /// Converts a mouse position to a hitobject position. + /// + /// + /// Blueprints are centred on the mouse position, such that the hitobject position is anchored at the top or bottom of the blueprint depending on the scroll direction. + /// + /// The mouse position. + /// The resulting hitobject position, acnhored at the top or bottom of the blueprint depending on the scroll direction. + private Vector2 mouseToHitObjectPosition(Vector2 mousePosition) { - float offset = 0; - switch (scrollingInfo.Direction.Value) { case ScrollingDirection.Up: - offset = -NotePiece.NOTE_HEIGHT / 2; + mousePosition.Y -= NotePiece.NOTE_HEIGHT / 2; break; case ScrollingDirection.Down: - offset = NotePiece.NOTE_HEIGHT / 2; + mousePosition.Y += NotePiece.NOTE_HEIGHT / 2; break; } - if (reverse) - offset = -offset; + return mousePosition; + } - return new Vector2(position.X, position.Y + offset); + /// + /// Converts a hitobject position to a mouse position. + /// + /// The hitobject position. + /// The resulting mouse position, anchored at the centre of the hitobject. + private Vector2 hitObjectToMousePosition(Vector2 hitObjectPosition) + { + switch (scrollingInfo.Direction.Value) + { + case ScrollingDirection.Up: + hitObjectPosition.Y += NotePiece.NOTE_HEIGHT / 2; + break; + + case ScrollingDirection.Down: + hitObjectPosition.Y -= NotePiece.NOTE_HEIGHT / 2; + break; + } + + return hitObjectPosition; } } } From bcf0b2752e75f40b046e6447cbfce2b705dfb0ce Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 18:48:44 +0900 Subject: [PATCH 67/85] Fix possible MusicController nullref --- osu.Game/Overlays/MusicController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 49d16a4f3e..51afac4b8c 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays /// /// Returns whether the current beatmap track is playing. /// - public bool IsPlaying => beatmap.Value?.Track.IsRunning ?? false; + public bool IsPlaying => beatmap?.Value?.Track.IsRunning ?? false; private void handleBeatmapAdded(BeatmapSetInfo set) => Schedule(() => beatmapSets.Add(set)); From 662a1a9c2cb8af2b6e151a9b1bed43d32491ca3b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 18:55:53 +0900 Subject: [PATCH 68/85] Use current --- osu.Game/Overlays/MusicController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 51afac4b8c..f5c36a9cac 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays /// /// Returns whether the current beatmap track is playing. /// - public bool IsPlaying => beatmap?.Value?.Track.IsRunning ?? false; + public bool IsPlaying => current?.Track.IsRunning ?? false; private void handleBeatmapAdded(BeatmapSetInfo set) => Schedule(() => beatmapSets.Add(set)); From 6c878cb167544addc3a6af83efad12558a7b230d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 19:11:50 +0900 Subject: [PATCH 69/85] Prevent nullrefs --- osu.Game/Overlays/VolumeOverlay.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index 0c08e0eb6e..27e2eef200 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -32,7 +32,8 @@ namespace osu.Game.Overlays private readonly BindableDouble muteAdjustment = new BindableDouble(); - public Bindable IsMuted => muteButton.Current; + private readonly Bindable isMuted = new Bindable(); + public Bindable IsMuted => isMuted; [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colours) @@ -66,7 +67,8 @@ namespace osu.Game.Overlays volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker), muteButton = new MuteButton { - Margin = new MarginPadding { Top = 100 } + Margin = new MarginPadding { Top = 100 }, + Current = { BindTarget = isMuted } } } }, @@ -76,13 +78,13 @@ namespace osu.Game.Overlays volumeMeterEffect.Bindable.BindTo(audio.VolumeSample); volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack); - muteButton.Current.ValueChanged += muted => + isMuted.BindValueChanged(muted => { if (muted.NewValue) audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment); else audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment); - }; + }); } protected override void LoadComplete() From 636913a4a6a62319f23f3b17f52552bfa3905409 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Oct 2019 19:16:31 +0900 Subject: [PATCH 70/85] Refactor PlayerLoader changes --- osu.Game/Screens/Play/PlayerLoader.cs | 63 ++++++++++++--------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index cd4b331fce..87d902b547 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -58,9 +58,19 @@ namespace osu.Game.Screens.Play private Task loadTask; private InputManager inputManager; - private IdleTracker idleTracker; + [Resolved(CanBeNull = true)] + private NotificationOverlay notificationOverlay { get; set; } + + [Resolved(CanBeNull = true)] + private VolumeOverlay volumeOverlay { get; set; } + + [Resolved] + private AudioManager audioManager { get; set; } + + private Bindable muteWarningShownOnce; + public PlayerLoader(Func createPlayer) { this.createPlayer = createPlayer; @@ -110,7 +120,22 @@ namespace osu.Game.Screens.Play loadNewPlayer(); } - private void playerLoaded(Player player) => info.Loading = false; + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + + if (!muteWarningShownOnce.Value) + { + //Checks if the notification has not been shown yet and also if master volume is muted, track/music volume is muted or if the whole game is muted. + if (volumeOverlay?.IsMuted.Value == true || audioManager.Volume.Value <= audioManager.Volume.MinValue || audioManager.VolumeTrack.Value <= audioManager.VolumeTrack.MinValue) + { + notificationOverlay?.Post(new MutedNotification()); + muteWarningShownOnce.Value = true; + } + } + } public override void OnResuming(IScreen last) { @@ -134,7 +159,7 @@ namespace osu.Game.Screens.Play player.RestartCount = restartCount; player.RestartRequested = restartRequested; - loadTask = LoadComponentAsync(player, playerLoaded); + loadTask = LoadComponentAsync(player, _ => info.Loading = false); } private void contentIn() @@ -152,30 +177,6 @@ namespace osu.Game.Screens.Play content.FadeOut(250); } - [Resolved(CanBeNull = true)] - private NotificationOverlay notificationOverlay { get; set; } - - [Resolved(CanBeNull = true)] - private VolumeOverlay volumeOverlay { get; set; } - - [Resolved] - private AudioManager audioManager { get; set; } - - private Bindable muteWarningShownOnce; - - private void checkVolume() - { - if (muteWarningShownOnce.Value) - return; - - //Checks if the notification has not been shown yet and also if master volume is muted, track/music volume is muted or if the whole game is muted. - if (volumeOverlay?.IsMuted.Value == true || audioManager.Volume.Value <= audioManager.Volume.MinValue || audioManager.VolumeTrack.Value <= audioManager.VolumeTrack.MinValue) - { - notificationOverlay?.Post(new MutedNotification()); - muteWarningShownOnce.Value = true; - } - } - public override void OnEntering(IScreen last) { base.OnEntering(last); @@ -216,14 +217,6 @@ namespace osu.Game.Screens.Play content.StopTracking(); } - protected override void LoadComplete() - { - inputManager = GetContainingInputManager(); - base.LoadComplete(); - - checkVolume(); - } - private ScheduledDelegate pushDebounce; protected VisualSettings VisualSettings; From 925615320e4d8ae455738b5dc3fa1de8a170f71f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Oct 2019 10:46:48 +0800 Subject: [PATCH 71/85] Update lazer default combo colours to match stable --- osu.Game/Skinning/DefaultSkinConfiguration.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/DefaultSkinConfiguration.cs b/osu.Game/Skinning/DefaultSkinConfiguration.cs index f52fac6077..cd5975edac 100644 --- a/osu.Game/Skinning/DefaultSkinConfiguration.cs +++ b/osu.Game/Skinning/DefaultSkinConfiguration.cs @@ -14,10 +14,10 @@ namespace osu.Game.Skinning { ComboColours.AddRange(new[] { - new Color4(17, 136, 170, 255), - new Color4(102, 136, 0, 255), - new Color4(204, 102, 0, 255), - new Color4(121, 9, 13, 255) + new Color4(255, 192, 0, 255), + new Color4(0, 202, 0, 255), + new Color4(18, 124, 255, 255), + new Color4(242, 24, 57, 255), }); } } From 71985c7ef13913af2d3c6acf07c17ac4d11986c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Oct 2019 11:23:42 +0800 Subject: [PATCH 72/85] Update fail logic to match --- osu.Game/Screens/Play/Player.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 91d34c8056..5560baa7d1 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -299,6 +299,12 @@ namespace osu.Game.Screens.Play { if (!this.IsCurrentScreen()) return; + if (HasFailed && !FailOverlay.IsPresent) + { + failAnimation.FinishTransforms(true); + return; + } + if (canPause) Pause(); else @@ -517,12 +523,6 @@ namespace osu.Game.Screens.Play if (pauseCooldownActive && !GameplayClockContainer.IsPaused.Value) // still want to block if we are within the cooldown period and not already paused. return true; - - if (HasFailed && !FailOverlay.IsPresent) - { - failAnimation.FinishTransforms(true); - return true; - } } GameplayClockContainer.ResetLocalAdjustments(); From e646b2677ca0b07267690783cf8894676e437dc4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Oct 2019 11:25:23 +0800 Subject: [PATCH 73/85] Add test coverage --- osu.Game.Tests/Visual/Gameplay/TestScenePause.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index c67001c3d8..48159aa5e0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -127,6 +127,15 @@ namespace osu.Game.Tests.Visual.Gameplay exitAndConfirm(); } + [Test] + public void TestExitFromFailedGameplay() + { + AddUntilStep("wait for fail", () => Player.HasFailed); + AddStep("exit", () => Player.Exit()); + + confirmExited(); + } + [Test] public void TestExitFromGameplay() { From 47c1f36f9d91f54eb68b824badad248b69c169be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Oct 2019 11:41:53 +0800 Subject: [PATCH 74/85] Add ValidForResume check --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 5560baa7d1..0b363eac4d 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -299,7 +299,7 @@ namespace osu.Game.Screens.Play { if (!this.IsCurrentScreen()) return; - if (HasFailed && !FailOverlay.IsPresent) + if (ValidForResume && HasFailed && !FailOverlay.IsPresent) { failAnimation.FinishTransforms(true); return; From 626f7388c8fdc8dd34935d2a8cf86cfaf702ac4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Oct 2019 12:23:01 +0800 Subject: [PATCH 75/85] Add tests for quick retry and quick exit scenarios --- .../Visual/Gameplay/TestScenePause.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 48159aa5e0..2df22df659 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -136,6 +136,24 @@ namespace osu.Game.Tests.Visual.Gameplay confirmExited(); } + [Test] + public void TestQuickRetryFromFailedGameplay() + { + AddUntilStep("wait for fail", () => Player.HasFailed); + AddStep("quick retry", () => Player.GameplayClockContainer.OfType().First().Action?.Invoke()); + + confirmExited(); + } + + [Test] + public void TestQuickExitFromFailedGameplay() + { + AddUntilStep("wait for fail", () => Player.HasFailed); + AddStep("quick exit", () => Player.GameplayClockContainer.OfType().First().Action?.Invoke()); + + confirmExited(); + } + [Test] public void TestExitFromGameplay() { @@ -144,6 +162,14 @@ namespace osu.Game.Tests.Visual.Gameplay confirmExited(); } + [Test] + public void TestQuickExitFromGameplay() + { + AddStep("quick exit", () => Player.GameplayClockContainer.OfType().First().Action?.Invoke()); + + confirmExited(); + } + [Test] public void TestExitViaHoldToExit() { From ffde389641a15f0b0faceb8e1e900fa186509bda Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 4 Oct 2019 13:28:32 +0900 Subject: [PATCH 76/85] Add difficulty calculator beatmap decoder fallback --- .../Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs index 2c493254e0..238187bf8f 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs @@ -24,6 +24,7 @@ namespace osu.Game.Beatmaps.Formats public new static void Register() { AddDecoder(@"osu file format v", m => new LegacyDifficultyCalculatorBeatmapDecoder(int.Parse(m.Split('v').Last()))); + SetFallbackDecoder(() => new LegacyDifficultyCalculatorBeatmapDecoder()); } protected override TimingControlPoint CreateTimingControlPoint() From ddef7fa3ba67d47ff90d62446d7ed5bbf294c46d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Oct 2019 13:32:47 +0800 Subject: [PATCH 77/85] Repair behavioural change --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 74d3439c59..c6d812aee3 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.UI return; } - manualClock.CurrentTime = newTime.Value; + newProposedTime = newTime.Value; } requireMoreUpdateLoops = manualClock.CurrentTime != parentGameplayClock.CurrentTime; From aeb62825cdb0c0727fca76b6ae21541133aeb1e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Oct 2019 13:42:06 +0800 Subject: [PATCH 78/85] Move out requireMoreUpdateLoops for better consistency --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index c6d812aee3..e569bb8459 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -115,17 +115,16 @@ namespace osu.Game.Rulesets.UI setClock(); // LoadComplete may not be run yet, but we still want the clock. validState = true; + requireMoreUpdateLoops = false; var newProposedTime = parentGameplayClock.CurrentTime; try { if (!FrameStablePlayback) - { - requireMoreUpdateLoops = false; return; - } - else if (firstConsumption) + + if (firstConsumption) { // On the first update, frame-stability seeking would result in unexpected/unwanted behaviour. // Instead we perform an initial seek to the proposed time. @@ -159,8 +158,6 @@ namespace osu.Game.Rulesets.UI newProposedTime = newTime.Value; } - - requireMoreUpdateLoops = manualClock.CurrentTime != parentGameplayClock.CurrentTime; } finally { @@ -171,6 +168,8 @@ namespace osu.Game.Rulesets.UI manualClock.Rate = Math.Abs(parentGameplayClock.Rate) * direction; manualClock.IsRunning = parentGameplayClock.IsRunning; + requireMoreUpdateLoops |= manualClock.CurrentTime != parentGameplayClock.CurrentTime; + // The manual clock time has changed in the above code. The framed clock now needs to be updated // to ensure that the its time is valid for our children before input is processed framedClock.ProcessFrame(); From 76c74719a44f2aa461cebf93cd2b71e476663632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 4 Oct 2019 17:00:51 +0200 Subject: [PATCH 79/85] Add test for fallback decoder overwrite LegacyDifficultyCalculatorBeatmapDecoder was registered as a fallback decoder in commit ffde389 for future use in the server-side difficulty calculation components. Due to the pre-existing fallback registrations this causes a runtime crash when the diffcalc components are started. Add a test reproducing this scenario to prevent the issue from resurfacing in the future. --- .../Formats/LegacyBeatmapDecoderTest.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index f6c0dbbecf..de516d3142 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -591,5 +591,27 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.Throws(() => Decoder.GetDecoder(stream)); } } + + [Test] + public void TestAllowFallbackDecoderOverwrite() + { + Decoder decoder = null; + + using (var resStream = TestResources.OpenResource("corrupted-header.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + Assert.DoesNotThrow(() => decoder = Decoder.GetDecoder(stream)); + Assert.IsInstanceOf(decoder); + } + + Assert.DoesNotThrow(LegacyDifficultyCalculatorBeatmapDecoder.Register); + + using (var resStream = TestResources.OpenResource("corrupted-header.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + Assert.DoesNotThrow(() => decoder = Decoder.GetDecoder(stream)); + Assert.IsInstanceOf(decoder); + } + } } } From 7c2c537bc9cca82740ea9df51f65c3f8047de372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 4 Oct 2019 17:23:33 +0200 Subject: [PATCH 80/85] Allow fallback decoder overwrite To fix the runtime crashes in difficulty calculation components, remove the check for pre-existing fallback registration along with the exception. The xmldoc for the registration function has been extended to make users aware of possible consequences of calling it. --- osu.Game/Beatmaps/Formats/Decoder.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index 045eb2d14d..40c329eb7e 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -93,14 +93,12 @@ namespace osu.Game.Beatmaps.Formats /// /// Registers a fallback decoder instantiation function. /// The fallback will be returned if the first non-empty line of the decoded stream does not match any known magic. + /// Calling this method will overwrite any existing global fallback registration for type - use with caution. /// /// Type of object being decoded. /// A function that constructs the fallback. protected static void SetFallbackDecoder(Func constructor) { - if (fallback_decoders.ContainsKey(typeof(T))) - throw new InvalidOperationException($"A fallback decoder was already added for type {typeof(T)}."); - fallback_decoders[typeof(T)] = constructor; } } From e7ba6ef5c43fcf1c55d93a86cb0773ea4d8070f2 Mon Sep 17 00:00:00 2001 From: Joehu Date: Fri, 4 Oct 2019 14:32:43 -0700 Subject: [PATCH 81/85] Fix keybinding order of beatmap options --- osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 4df6e6a3f3..9368bac69f 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select { ValidForResume = false; Edit(); - }, Key.Number3); + }, Key.Number4); } public override void OnResuming(IScreen last) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 452c63a18c..ee2e40dcd9 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -230,9 +230,9 @@ namespace osu.Game.Screens.Select Footer.AddButton(new FooterButtonRandom { Action = triggerRandom }); Footer.AddButton(new FooterButtonOptions(), BeatmapOptions); - BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue); - BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null, Key.Number1); - BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapInfo), Key.Number2); + BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number1, float.MaxValue); + BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null, Key.Number2); + BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapInfo), Key.Number3); } if (this.beatmaps == null) From 5d460eaf6b8a9c05d54c61f2cd01cfddb2636386 Mon Sep 17 00:00:00 2001 From: Joehu Date: Fri, 4 Oct 2019 17:14:19 -0700 Subject: [PATCH 82/85] Remove depth specification and button order regression --- osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs | 5 ++--- osu.Game/Screens/Select/SongSelect.cs | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index ede526f9da..b831ae274c 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -93,7 +93,7 @@ namespace osu.Game.Screens.Select.Options /// Lower depth to be put on the left, and higher to be put on the right. /// Notice this is different to ! /// - public void AddButton(string firstLine, string secondLine, IconUsage icon, Color4 colour, Action action, Key? hotkey = null, float depth = 0) + public void AddButton(string firstLine, string secondLine, IconUsage icon, Color4 colour, Action action, Key? hotkey = null) { var button = new BeatmapOptionsButton { @@ -101,7 +101,6 @@ namespace osu.Game.Screens.Select.Options SecondLineText = secondLine, Icon = icon, ButtonColour = colour, - Depth = depth, Action = () => { Hide(); @@ -110,7 +109,7 @@ namespace osu.Game.Screens.Select.Options HotKey = hotkey }; - buttonsContainer.Insert((int)depth, button); + buttonsContainer.Add(button); } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index ee2e40dcd9..59a143728c 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -230,9 +230,9 @@ namespace osu.Game.Screens.Select Footer.AddButton(new FooterButtonRandom { Action = triggerRandom }); Footer.AddButton(new FooterButtonOptions(), BeatmapOptions); - BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number1, float.MaxValue); - BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null, Key.Number2); - BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapInfo), Key.Number3); + BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null, Key.Number1); + BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapInfo), Key.Number2); + BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number3); } if (this.beatmaps == null) From de658c932e6ef23ce6511d15cabf28b836f94416 Mon Sep 17 00:00:00 2001 From: Joehu Date: Fri, 4 Oct 2019 17:22:42 -0700 Subject: [PATCH 83/85] Fix test regression --- .../Visual/SongSelect/TestSceneBeatmapOptionsOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapOptionsOverlay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapOptionsOverlay.cs index ecdc484887..f55c099d83 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapOptionsOverlay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapOptionsOverlay.cs @@ -18,8 +18,8 @@ namespace osu.Game.Tests.Visual.SongSelect overlay.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, Color4.Purple, null, Key.Number1); overlay.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, Color4.Purple, null, Key.Number2); - overlay.AddButton(@"Edit", @"Beatmap", FontAwesome.Solid.PencilAlt, Color4.Yellow, null, Key.Number3); - overlay.AddButton(@"Delete", @"Beatmap", FontAwesome.Solid.Trash, Color4.Pink, null, Key.Number4, float.MaxValue); + overlay.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, Color4.Pink, null, Key.Number3); + overlay.AddButton(@"Edit", @"beatmap", FontAwesome.Solid.PencilAlt, Color4.Yellow, null, Key.Number4); Add(overlay); From a71db11ea544cacf62fdd22da93a9f89a6523f5c Mon Sep 17 00:00:00 2001 From: Joehu Date: Fri, 4 Oct 2019 18:38:44 -0700 Subject: [PATCH 84/85] Remove depth parameter description --- osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index b831ae274c..c01970f536 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -89,10 +89,6 @@ namespace osu.Game.Screens.Select.Options /// Icon of the button. /// Hotkey of the button. /// Binding the button does. - /// - /// Lower depth to be put on the left, and higher to be put on the right. - /// Notice this is different to ! - /// public void AddButton(string firstLine, string secondLine, IconUsage icon, Color4 colour, Action action, Key? hotkey = null) { var button = new BeatmapOptionsButton From e257f4ca04481e6b43ef8ec52337c7c364218b56 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sat, 5 Oct 2019 10:31:44 -0700 Subject: [PATCH 85/85] Resume music to same position when exiting gameplay --- osu.Game/Screens/Select/SongSelect.cs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 59a143728c..d9ddfa2a94 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -413,7 +413,7 @@ namespace osu.Game.Screens.Select Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, previous); if (this.IsCurrentScreen() && Beatmap.Value?.Track != previous?.Track) - ensurePlayingSelected(); + ensurePlayingSelected(true); if (beatmap != null) { @@ -585,18 +585,14 @@ namespace osu.Game.Screens.Select { Track track = Beatmap.Value.Track; - if (!track.IsRunning || restart) + if (!track.IsRunning) { track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; - if (music != null) - { - // use the global music controller (when available) to cancel a potential local user paused state. - music.SeekTo(track.RestartPoint); - music.Play(); - } - else + if (restart) track.Restart(); + else + track.Start(); } }