diff --git a/osu-framework b/osu-framework index a584706f13..1440ae8538 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit a584706f1303f54cd7f5472240e95b70920ce079 +Subproject commit 1440ae8538560b3c40883ec51ab39108d6a69e3b diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 3cc4e7f943..b0d9ea4e81 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -136,7 +136,7 @@ True - $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll + $(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll True diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config index 37014057a0..b5dc43267d 100644 --- a/osu.Desktop/packages.config +++ b/osu.Desktop/packages.config @@ -6,7 +6,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - + diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index cdce598ce8..31c225288b 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -41,7 +41,7 @@ True - $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll + $(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll True diff --git a/osu.Game.Rulesets.Catch/packages.config b/osu.Game.Rulesets.Catch/packages.config index e67d3e9b34..16fae25086 100644 --- a/osu.Game.Rulesets.Catch/packages.config +++ b/osu.Game.Rulesets.Catch/packages.config @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index b9e7f8e60f..38689fb19b 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -41,7 +41,7 @@ True - $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll + $(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll True diff --git a/osu.Game.Rulesets.Mania/packages.config b/osu.Game.Rulesets.Mania/packages.config index e67d3e9b34..16fae25086 100644 --- a/osu.Game.Rulesets.Mania/packages.config +++ b/osu.Game.Rulesets.Mania/packages.config @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 97a003513f..d734fd70a9 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -42,7 +42,7 @@ True - $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll + $(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll True diff --git a/osu.Game.Rulesets.Osu/packages.config b/osu.Game.Rulesets.Osu/packages.config index e67d3e9b34..16fae25086 100644 --- a/osu.Game.Rulesets.Osu/packages.config +++ b/osu.Game.Rulesets.Osu/packages.config @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 5795048322..74859f924d 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -41,7 +41,7 @@ True - $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll + $(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll True diff --git a/osu.Game.Rulesets.Taiko/packages.config b/osu.Game.Rulesets.Taiko/packages.config index e67d3e9b34..16fae25086 100644 --- a/osu.Game.Rulesets.Taiko/packages.config +++ b/osu.Game.Rulesets.Taiko/packages.config @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs b/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs index 1bb72a5ab4..ec70253118 100644 --- a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs +++ b/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs @@ -63,8 +63,8 @@ namespace osu.Game.Tests.Visual { Value = new[] { - new User { GlobalRank = 1355 }, - new User { GlobalRank = 8756 }, + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1355 } } }, + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 8756 } } }, }, }, }), @@ -99,10 +99,10 @@ namespace osu.Game.Tests.Visual }, Participants = { - Value = new[] + Value = new[] { - new User { GlobalRank = 578975 }, - new User { GlobalRank = 24554 }, + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 578975 } } }, + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24554 } } }, }, }, }), @@ -116,8 +116,8 @@ namespace osu.Game.Tests.Visual AddStep(@"change beatmap", () => first.Room.Beatmap.Value = null); AddStep(@"change participants", () => first.Room.Participants.Value = new[] { - new User { GlobalRank = 1254 }, - new User { GlobalRank = 123189 }, + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1254 } } }, + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 123189 } } }, }); } diff --git a/osu.Game.Tests/Visual/TestCaseRankGraph.cs b/osu.Game.Tests/Visual/TestCaseRankGraph.cs index 54930c51a2..88631aa982 100644 --- a/osu.Game.Tests/Visual/TestCaseRankGraph.cs +++ b/osu.Game.Tests/Visual/TestCaseRankGraph.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual { Statistics = new UserStatistics { - Rank = 123456, + Ranks = new UserStatistics.UserRanks { Global = 123456 }, PP = 12345, } }; @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual { Statistics = new UserStatistics { - Rank = 89000, + Ranks = new UserStatistics.UserRanks { Global = 89000 }, PP = 12345, }, RankHistory = new User.RankHistoryData @@ -93,7 +93,7 @@ namespace osu.Game.Tests.Visual { Statistics = new UserStatistics { - Rank = 89000, + Ranks = new UserStatistics.UserRanks { Global = 89000 }, PP = 12345, }, RankHistory = new User.RankHistoryData @@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual { Statistics = new UserStatistics { - Rank = 12000, + Ranks = new UserStatistics.UserRanks { Global = 12000 }, PP = 12345, }, RankHistory = new User.RankHistoryData diff --git a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs b/osu.Game.Tests/Visual/TestCaseRoomInspector.cs index e613d87500..8c4aa02a68 100644 --- a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs +++ b/osu.Game.Tests/Visual/TestCaseRoomInspector.cs @@ -54,12 +54,12 @@ namespace osu.Game.Tests.Visual { Value = new[] { - new User { Username = @"flyte", Id = 3103765, GlobalRank = 1425 }, - new User { Username = @"Cookiezi", Id = 124493, GlobalRank = 5466 }, - new User { Username = @"Angelsim", Id = 1777162, GlobalRank = 2873 }, - new User { Username = @"Rafis", Id = 2558286, GlobalRank = 4687 }, - new User { Username = @"hvick225", Id = 50265, GlobalRank = 3258 }, - new User { Username = @"peppy", Id = 2, GlobalRank = 6251 } + new User { Username = @"flyte", Id = 3103765, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 142 } } }, + new User { Username = @"Cookiezi", Id = 124493, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 546 } } }, + new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 287 } } }, + new User { Username = @"Rafis", Id = 2558286, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 468 } } }, + new User { Username = @"hvick225", Id = 50265, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 325 } } }, + new User { Username = @"peppy", Id = 2, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 625 } } }, } } }; @@ -80,8 +80,8 @@ namespace osu.Game.Tests.Visual AddStep(@"change max participants", () => room.MaxParticipants.Value = null); AddStep(@"change participants", () => room.Participants.Value = new[] { - new User { Username = @"filsdelama", Id = 2831793, GlobalRank = 8542 }, - new User { Username = @"_index", Id = 652457, GlobalRank = 15024 } + new User { Username = @"filsdelama", Id = 2831793, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 854 } } }, + new User { Username = @"_index", Id = 652457, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 150 } } } }); AddStep(@"change room", () => @@ -121,9 +121,9 @@ namespace osu.Game.Tests.Visual { Value = new[] { - new User { Username = @"Angelsim", Id = 1777162, GlobalRank = 4 }, - new User { Username = @"HappyStick", Id = 256802, GlobalRank = 752 }, - new User { Username = @"-Konpaku-", Id = 2258797, GlobalRank = 571 } + new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 4 } } }, + new User { Username = @"HappyStick", Id = 256802, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 752 } } }, + new User { Username = @"-Konpaku-", Id = 2258797, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 571 } } } } } }; diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index da81de6a3a..8acc8d1b5b 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -42,11 +42,10 @@ namespace osu.Game.Tests.Visual LastVisit = DateTimeOffset.Now, Age = 1, ProfileOrder = new[] { "me" }, - CountryRank = 1, Statistics = new UserStatistics { - Rank = 2148, - PP = 4567.89m + Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 }, + PP = 4567.89m, }, RankHistory = new User.RankHistoryData { diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 2f8bc5522d..14810abf84 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -42,7 +42,7 @@ True - $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll + $(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll True diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config index 9b125da215..a3b983e3ef 100644 --- a/osu.Game.Tests/packages.config +++ b/osu.Game.Tests/packages.config @@ -7,7 +7,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - + diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 3c3939586e..8f375d9885 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -20,6 +20,11 @@ namespace osu.Game.Graphics.UserInterface public class OsuSliderBar : SliderBar, IHasTooltip, IHasAccentColour where T : struct, IEquatable, IComparable, IConvertible { + /// + /// Maximum number of decimal digits to be displayed in the tooltip. + /// + private const int max_decimal_digits = 5; + private SampleChannel sample; private double lastSampleTime; private T lastSampleValue; @@ -35,6 +40,7 @@ namespace osu.Game.Graphics.UserInterface var bindableDouble = CurrentNumber as BindableNumber; var bindableFloat = CurrentNumber as BindableNumber; var floatValue = bindableDouble?.Value ?? bindableFloat?.Value; + var floatPrecision = bindableDouble?.Precision ?? bindableFloat?.Precision; if (floatValue != null) { @@ -44,7 +50,12 @@ namespace osu.Game.Graphics.UserInterface if (floatMaxValue == 1 && (floatMinValue == 0 || floatMinValue == -1)) return floatValue.Value.ToString("P0"); - return floatValue.Value.ToString("N1"); + var decimalPrecision = normalise((decimal)floatPrecision, max_decimal_digits); + + // Find the number of significant digits (we could have less than 5 after normalize()) + var significantDigits = findPrecision(decimalPrecision); + + return floatValue.Value.ToString($"N{significantDigits}"); } var bindableInt = CurrentNumber as BindableNumber; @@ -177,5 +188,31 @@ namespace osu.Game.Graphics.UserInterface { Nub.MoveToX(RangePadding + UsableWidth * value, 250, Easing.OutQuint); } + + /// + /// Removes all non-significant digits, keeping at most a requested number of decimal digits. + /// + /// The decimal to normalize. + /// The maximum number of decimal digits to keep. The final result may have fewer decimal digits than this value. + /// The normalised decimal. + private decimal normalise(decimal d, int sd) + => decimal.Parse(Math.Round(d, sd).ToString(string.Concat("0.", new string('#', sd)), CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Finds the number of digits after the decimal. + /// + /// The value to find the number of decimal digits for. + /// The number decimal digits. + private int findPrecision(decimal d) + { + int precision = 0; + while (d != Math.Round(d)) + { + d *= 10; + precision++; + } + + return precision; + } } } diff --git a/osu.Game/Overlays/Profile/RankGraph.cs b/osu.Game/Overlays/Profile/RankGraph.cs index 9d3183339e..429049c7bc 100644 --- a/osu.Game/Overlays/Profile/RankGraph.cs +++ b/osu.Game/Overlays/Profile/RankGraph.cs @@ -105,7 +105,7 @@ namespace osu.Game.Overlays.Profile return; } - int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Rank }; + int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Ranks.Global }; ranks = userRanks.Select((x, index) => new KeyValuePair(index, x)).Where(x => x.Value != 0).ToArray(); if (ranks.Length > 1) @@ -124,9 +124,11 @@ namespace osu.Game.Overlays.Profile private void updateRankTexts() { - rankText.Text = User.Value.Statistics.Rank > 0 ? $"#{User.Value.Statistics.Rank:#,0}" : "no rank"; - performanceText.Text = User.Value.Statistics.PP != null ? $"{User.Value.Statistics.PP:#,0}pp" : string.Empty; - relativeText.Text = $"{User.Value.Country?.FullName} #{User.Value.CountryRank:#,0}"; + var user = User.Value; + + performanceText.Text = user.Statistics.PP != null ? $"{user.Statistics.PP:#,0}pp" : string.Empty; + rankText.Text = user.Statistics.Ranks.Global > 0 ? $"#{user.Statistics.Ranks.Global:#,0}" : "no rank"; + relativeText.Text = user.Country != null && user.Statistics.Ranks.Country > 0 ? $"{user.Country.FullName} #{user.Statistics.Ranks.Country:#,0}" : "no rank"; } private void showHistoryRankTexts(int dayIndex) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index ab501906dc..c368b8fea7 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -33,6 +33,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = "Cursor Sensitivity", Bindable = config.GetBindable(FrameworkSetting.CursorSensitivity) }, + new SettingsCheckbox + { + LabelText = "Map absolute input to window", + Bindable = config.GetBindable(FrameworkSetting.MapAbsoluteInputToWindow) + }, new SettingsEnumDropdown { LabelText = "Confine mouse cursor to window", @@ -88,6 +93,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input // this bindable will still act as the "interactive" bindable displayed during a drag. base.Bindable = new BindableDouble(doubleValue.Value) { + Default = doubleValue.Default, MinValue = doubleValue.MinValue, MaxValue = doubleValue.MaxValue }; diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index adb7c509c0..5afc415d83 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -181,7 +181,7 @@ namespace osu.Game.Overlays.Settings { hovering = true; UpdateState(); - return true; + return false; } protected override void OnHoverLost(InputState state) diff --git a/osu.Game/Screens/Multiplayer/ParticipantInfo.cs b/osu.Game/Screens/Multiplayer/ParticipantInfo.cs index ff00f53600..0fd4f4d08d 100644 --- a/osu.Game/Screens/Multiplayer/ParticipantInfo.cs +++ b/osu.Game/Screens/Multiplayer/ParticipantInfo.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Multiplayer { set { - var ranks = value.Select(u => u.GlobalRank); + var ranks = value.Select(u => u.Statistics.Ranks.Global); levelRangeLower.Text = ranks.Min().ToString(); levelRangeHigher.Text = ranks.Max().ToString(); } diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 15d8e73a76..6878bb098e 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -42,7 +42,6 @@ namespace osu.Game.Screens.Play.PlayerSettings { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Text = "1x", Font = @"Exo2.0-Bold", } }, @@ -54,12 +53,13 @@ namespace osu.Game.Screens.Play.PlayerSettings Default = 1, MinValue = 0.5, MaxValue = 2, - Precision = 0.01, + Precision = 0.1, }, } }; - sliderbar.Bindable.ValueChanged += rateMultiplier => multiplierText.Text = $"{rateMultiplier}x"; + sliderbar.Bindable.ValueChanged += rateMultiplier => multiplierText.Text = $"{sliderbar.Bar.TooltipText}x"; + sliderbar.Bindable.TriggerChange(); } protected override void LoadComplete() diff --git a/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs b/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs index 946669e3dd..43fe14cc24 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs @@ -13,6 +13,8 @@ namespace osu.Game.Screens.Play.PlayerSettings public class PlayerSliderBar : SettingsSlider where T : struct, IEquatable, IComparable, IConvertible { + public OsuSliderBar Bar => (OsuSliderBar)Control; + protected override Drawable CreateControl() => new Sliderbar { Margin = new MarginPadding { Top = 5, Bottom = 5 }, @@ -21,8 +23,6 @@ namespace osu.Game.Screens.Play.PlayerSettings private class Sliderbar : OsuSliderBar { - public override string TooltipText => $"{CurrentNumber.Value}"; - [BackgroundDependencyLoader] private void load(OsuColour colours) { diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 8379e69869..777eb7beca 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -26,10 +26,6 @@ namespace osu.Game.Users [JsonProperty(@"age")] public int? Age; - public int GlobalRank; - - public int CountryRank; - //public Team Team; [JsonProperty(@"profile_colour")] diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index 73d20eafb9..c29bc91d17 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -22,8 +22,11 @@ namespace osu.Game.Users [JsonProperty(@"pp")] public decimal? PP; - [JsonProperty(@"pp_rank")] - public int Rank; + [JsonProperty(@"pp_rank")] // the API sometimes only returns this value in condensed user responses + private int rank { set => Ranks.Global = value; } + + [JsonProperty(@"rank")] + public UserRanks Ranks; [JsonProperty(@"ranked_score")] public long RankedScore; @@ -66,5 +69,15 @@ namespace osu.Game.Users [JsonProperty(@"a")] public int A; } + + public struct UserRanks + { + [JsonProperty(@"global")] + public int Global; + + [JsonProperty(@"country")] + public int Country; + } + } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4944613828..a5c3fc7f38 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -148,7 +148,7 @@ True - $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll + $(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll True diff --git a/osu.Game/packages.config b/osu.Game/packages.config index 0216c8ae67..6d46360b99 100644 --- a/osu.Game/packages.config +++ b/osu.Game/packages.config @@ -67,7 +67,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - +