diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml new file mode 100644 index 0000000000..be69e92748 --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml new file mode 100644 index 0000000000..da968b54b7 --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml new file mode 100644 index 0000000000..f2b8155e37 --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml new file mode 100644 index 0000000000..941455f8b4 --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net471_.xml similarity index 82% rename from .idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml rename to .idea/.idea.osu/.idea/runConfigurations/VisualTests__net471_.xml index cf4bccfe60..20a15f985f 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net471_.xml @@ -1,6 +1,6 @@ - - \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml b/.idea/.idea.osu/.idea/runConfigurations/osu___net471_.xml similarity index 83% rename from .idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml rename to .idea/.idea.osu/.idea/runConfigurations/osu___net471_.xml index 971868a81b..7196e486d2 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/osu___net471_.xml @@ -1,6 +1,6 @@ - - \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 624e584f10..df5b11f63a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,13 +2,13 @@ "version": "0.2.0", "configurations": [ { - "name": "VisualTests (Debug, net461)", + "name": "VisualTests (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net461/osu.Game.Tests.exe", + "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net471/osu.Game.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -16,13 +16,13 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, net461)", + "name": "VisualTests (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net461/osu.Game.Tests.exe", + "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net471/osu.Game.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, @@ -30,13 +30,13 @@ "console": "internalConsole" }, { - "name": "osu! (Debug, net461)", + "name": "osu! (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop/bin/Debug/net461/osu!.exe", + "program": "${workspaceRoot}/osu.Desktop/bin/Debug/net471/osu!.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -44,13 +44,13 @@ "console": "internalConsole" }, { - "name": "osu! (Release, net461)", + "name": "osu! (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop/bin/Release/net461/osu!.exe", + "program": "${workspaceRoot}/osu.Desktop/bin/Release/net471/osu!.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b1d2c6b57d..7144a584f3 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -8,7 +8,7 @@ "type": "shell", "command": "msbuild", "args": [ - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -22,7 +22,7 @@ "command": "msbuild", "args": [ "/p:Configuration=Release", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -64,7 +64,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Restore (net461)", + "label": "Restore (net471)", "type": "shell", "command": "nuget", "args": [ diff --git a/README.md b/README.md index 47df86f57e..9d19f16ebd 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This is still heavily under development and is not intended for end-user use. Th # Requirements -- A desktop platform that can compile .NET 4.6.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here. +- A desktop platform that can compile .NET 4.7.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here. # Getting Started - Clone the repository including submodules (`git clone --recurse-submodules https://github.com/ppy/osu`) diff --git a/appveyor.yml b/appveyor.yml index 4c4b70827f..15484e4c68 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,27 +1,26 @@ -# 2017-09-14 -clone_depth: 1 -version: '{branch}-{build}' -image: Visual Studio 2017 -configuration: Debug -cache: - - C:\ProgramData\chocolatey\bin -> appveyor.yml - - C:\ProgramData\chocolatey\lib -> appveyor.yml - - inspectcode -> appveyor.yml - - packages -> **\packages.config -install: - - cmd: git submodule update --init --recursive --depth=5 - - cmd: choco install resharper-clt -y - - cmd: choco install nvika -y - - cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.4/CodeFileSanity.exe -before_build: - - cmd: CodeFileSanity.exe - - cmd: nuget restore -verbosity quiet -environment: - TargetFramework: net461 -build: - project: osu.sln - parallel: true - verbosity: minimal -after_build: - - cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL +clone_depth: 1 +version: '{branch}-{build}' +image: Visual Studio 2017 +configuration: Debug +cache: + - C:\ProgramData\chocolatey\bin -> appveyor.yml + - C:\ProgramData\chocolatey\lib -> appveyor.yml + - inspectcode -> appveyor.yml + - packages -> **\packages.config +install: + - cmd: git submodule update --init --recursive --depth=5 + - cmd: choco install resharper-clt -y + - cmd: choco install nvika -y + - cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.4/CodeFileSanity.exe +before_build: + - cmd: CodeFileSanity.exe + - cmd: nuget restore -verbosity quiet +environment: + TargetFramework: net471 +build: + project: osu.sln + parallel: true + verbosity: minimal +after_build: + - cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors \ No newline at end of file diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml index cd241eb88f..cadebd9d11 100644 --- a/appveyor_deploy.yml +++ b/appveyor_deploy.yml @@ -1,4 +1,8 @@ -# 2017-09-14 +branches: + only: + - release +skip_tags: true +skip_branch_with_pr: true clone_depth: 1 version: '{branch}-{build}' image: Visual Studio 2017 @@ -17,11 +21,11 @@ after_build: - appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate - cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx - appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration - - cmd: appveyor-tools\secure-file -decrypt fdc6f19b04.enc -secret %decode_secret% -out osu.Desktop.Deploy\bin\Debug\net461\osu.Desktop.Deploy.exe.config - - cd osu.Desktop.Deploy\bin\Debug\net461\ + - cmd: appveyor-tools\secure-file -decrypt fdc6f19b04.enc -secret %decode_secret% -out osu.Desktop.Deploy\bin\Debug\net471\osu.Desktop.Deploy.exe.config + - cd osu.Desktop.Deploy\bin\Debug\net471\ - osu.Desktop.Deploy.exe %code_signing_password% environment: - TargetFramework: net461 + TargetFramework: net471 decode_secret: secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY= code_signing_password: diff --git a/osu-framework b/osu-framework index 16e6a453db..0773d895d9 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 16e6a453db9a8f4454238a2911eb5f1444b7ec2a +Subproject commit 0773d895d9aa0729995cd4a23efc28238e35ceed diff --git a/osu.Desktop.Deploy/.vscode/launch.json b/osu.Desktop.Deploy/.vscode/launch.json index 82cd6b4c13..8c35d211bd 100644 --- a/osu.Desktop.Deploy/.vscode/launch.json +++ b/osu.Desktop.Deploy/.vscode/launch.json @@ -7,7 +7,7 @@ "name": "Deploy (Debug)", "request": "launch", "type": "mono", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Desktop.Deploy.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Desktop.Deploy.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug)", "runtimeExecutable": null, @@ -18,7 +18,7 @@ "name": "Deploy (Release)", "request": "launch", "type": "clr", - "program": "${workspaceRoot}/bin/Release/net461/osu.Desktop.Deploy.exe", + "program": "${workspaceRoot}/bin/Release/net471/osu.Desktop.Deploy.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release)", "runtimeExecutable": null, diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index 8c460f03cf..16bbf90cd4 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -7,6 +7,7 @@ using System.Configuration; using System.Diagnostics; using System.IO; using System.Linq; +using System.Management.Automation; using Newtonsoft.Json; using osu.Framework.IO.Network; using FileWebRequest = osu.Framework.IO.Network.FileWebRequest; @@ -99,6 +100,7 @@ namespace osu.Desktop.Deploy write("Updating AssemblyInfo..."); updateCsprojVersion(version); + updateAppveyorVersion(version); write("Running build process..."); foreach (string targetName in TargetNames.Split(',')) @@ -404,6 +406,25 @@ namespace osu.Desktop.Deploy Console.WriteLine(); } + private static bool updateAppveyorVersion(string version) + { + try + { + using (PowerShell ps = PowerShell.Create()) + { + ps.AddScript($"Update-AppveyorBuild -Version \"{version}\""); + ps.Invoke(); + } + return true; + } + catch + { + // we don't have appveyor and don't care + } + + return false; + } + private static void write(string message, ConsoleColor col = ConsoleColor.Gray) { if (sw.ElapsedMilliseconds > 0) diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj index 6b7509a381..a97b8197b4 100644 --- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj +++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj @@ -1,7 +1,7 @@ - + - net461 + net471 Exe AnyCPU true @@ -12,7 +12,8 @@ - + + \ No newline at end of file diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 2ad7b67842..27bc3f7597 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -1,7 +1,7 @@  - net461;netcoreapp2.0 + net471;netcoreapp2.0 WinExe AnyCPU true @@ -14,7 +14,7 @@ 0.0.0.0 - $(DefineConstants);NET_FRAMEWORK + $(DefineConstants);NET_FRAMEWORK osu.Desktop.Program @@ -31,7 +31,7 @@ - + diff --git a/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json b/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json index 5098b78a42..eb80f4474c 100644 --- a/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json @@ -2,13 +2,13 @@ "version": "0.2.0", "configurations": [ { - "name": "VisualTests (Debug, net461)", + "name": "VisualTests (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Catch.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -16,13 +16,13 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, net461)", + "name": "VisualTests (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Catch.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, diff --git a/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json index d21bb8a69a..41ae88f425 100644 --- a/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json @@ -9,7 +9,7 @@ "command": "msbuild", "args": [ "osu.Game.Rulesets.Catch.Tests.csproj", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -24,7 +24,7 @@ "args": [ "osu.Game.Rulesets.Catch.Tests.csproj", "/p:Configuration=Release", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -66,7 +66,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Restore (net461)", + "label": "Restore (net471)", "type": "shell", "command": "nuget", "args": [ diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 7a4c7b3f1c..3797edde61 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net461 + netcoreapp2.0;net471 diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json index c71178059b..fceb403f30 100644 --- a/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json @@ -2,13 +2,13 @@ "version": "0.2.0", "configurations": [ { - "name": "VisualTests (Debug, net461)", + "name": "VisualTests (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Mania.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -16,13 +16,13 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, net461)", + "name": "VisualTests (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Mania.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json index 781e89598f..b04b068b0d 100644 --- a/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json @@ -9,7 +9,7 @@ "command": "msbuild", "args": [ "osu.Game.Rulesets.Mania.Tests.csproj", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -24,7 +24,7 @@ "args": [ "osu.Game.Rulesets.Mania.Tests.csproj", "/p:Configuration=Release", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -66,7 +66,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Restore (net461)", + "label": "Restore (net471)", "type": "shell", "command": "nuget", "args": [ diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index 02040fd23f..e90155568e 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net461 + netcoreapp2.0;net471 diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json index 24431eb8de..714fb6db6f 100644 --- a/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json @@ -2,13 +2,13 @@ "version": "0.2.0", "configurations": [ { - "name": "VisualTests (Debug, net461)", + "name": "VisualTests (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Osu.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -16,13 +16,13 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, net461)", + "name": "VisualTests (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Osu.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json index 734e15353b..657fe07e1a 100644 --- a/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json @@ -9,7 +9,7 @@ "command": "msbuild", "args": [ "osu.Game.Rulesets.Osu.Tests.csproj", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -24,7 +24,7 @@ "args": [ "osu.Game.Rulesets.Osu.Tests.csproj", "/p:Configuration=Release", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -66,7 +66,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Restore (net461)", + "label": "Restore (net471)", "type": "shell", "command": "nuget", "args": [ diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 49c5f15713..1695ceacee 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net461 + netcoreapp2.0;net471 diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs index 3518e030b6..6213bb1329 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs @@ -7,7 +7,6 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuEditPlayfield : OsuPlayfield { - protected override bool ProxyApproachCircles => false; protected override bool DisplayJudgements => false; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index f9d21ea5bb..10539f85a2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Y, - Children = new Drawable[] + Children = new[] { Background = new SpinnerBackground { diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 8330aa8e9d..b63f85623e 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -21,9 +21,6 @@ namespace osu.Game.Rulesets.Osu.UI private readonly JudgementContainer judgementLayer; private readonly ConnectionRenderer connectionLayer; - // Todo: This should not be a thing, but is currently required for the editor - // https://github.com/ppy/osu-framework/issues/1283 - protected virtual bool ProxyApproachCircles => true; protected virtual bool DisplayJudgements => true; public static readonly Vector2 BASE_SIZE = new Vector2(512, 384); @@ -59,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.UI h.OnJudgement += onJudgement; var c = h as IDrawableHitObjectWithProxiedApproach; - if (c != null && ProxyApproachCircles) + if (c != null) approachCircles.Add(c.ProxiedLayer.CreateProxy()); base.Add(h); diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json index caa90c32ce..e1df54e99b 100644 --- a/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json @@ -2,13 +2,13 @@ "version": "0.2.0", "configurations": [ { - "name": "VisualTests (Debug, net461)", + "name": "VisualTests (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Taiko.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -16,13 +16,13 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, net461)", + "name": "VisualTests (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Taiko.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json index 13044e1ccb..8bdbcd8e8e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json @@ -9,7 +9,7 @@ "command": "msbuild", "args": [ "osu.Game.Rulesets.Taiko.Tests.csproj", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -24,7 +24,7 @@ "args": [ "osu.Game.Rulesets.Taiko.Tests.csproj", "/p:Configuration=Release", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -66,7 +66,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Restore (net461)", + "label": "Restore (net471)", "type": "shell", "command": "nuget", "args": [ diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index ccd69c574d..1221584a2b 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net461 + netcoreapp2.0;net471 diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 9c9589f398..6e0cf6be2e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -116,8 +116,8 @@ namespace osu.Game.Tests.Beatmaps.Formats // [TestCase(with_sb)] public void TestParity(string beatmap) { - var beatmaps = decode(beatmap); - beatmaps.jsonDecoded.ShouldDeepEqual(beatmaps.legacyDecoded); + var legacy = decode(beatmap, out Beatmap json); + json.ShouldDeepEqual(legacy); } /// @@ -126,15 +126,20 @@ namespace osu.Game.Tests.Beatmaps.Formats /// /// The .osu file to decode. /// The after being decoded by an . - private Beatmap decodeAsJson(string filename) => decode(filename).jsonDecoded; + private Beatmap decodeAsJson(string filename) + { + decode(filename, out Beatmap jsonDecoded); + return jsonDecoded; + } /// /// Reads a .osu file first with a , serializes the resulting to JSON /// and then deserializes the result back into a through an . /// /// The .osu file to decode. + /// The after being decoded by an . /// The after being decoded by an . - private (Beatmap legacyDecoded, Beatmap jsonDecoded) decode(string filename) + private Beatmap decode(string filename, out Beatmap jsonDecoded) { using (var stream = Resource.OpenResource(filename)) using (var sr = new StreamReader(stream)) @@ -149,7 +154,9 @@ namespace osu.Game.Tests.Beatmaps.Formats sw.Flush(); ms.Position = 0; - return (legacyDecoded, new JsonBeatmapDecoder().Decode(sr2)); + + jsonDecoded = new JsonBeatmapDecoder().Decode(sr2); + return legacyDecoded; } } } diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs index 790c4cedc3..886c1120d4 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs @@ -52,6 +52,9 @@ namespace osu.Game.Tests.Visual infoWedge.UpdateBeatmap(beatmap); }); + // select part is redundant, but wait for load isn't + selectBeatmap(beatmap.Value.Beatmap); + AddWaitStep(3); AddStep("hide", () => { infoWedge.State = Visibility.Hidden; }); @@ -63,10 +66,11 @@ namespace osu.Game.Tests.Visual foreach (var rulesetInfo in rulesets.AvailableRulesets) { var ruleset = rulesetInfo.CreateInstance(); - beatmaps.Add(createTestBeatmap(rulesetInfo)); + var testBeatmap = createTestBeatmap(rulesetInfo); - var name = rulesetInfo.ShortName; - selectBeatmap(name); + beatmaps.Add(testBeatmap); + + selectBeatmap(testBeatmap); // TODO: adjust cases once more info is shown for other gamemodes switch (ruleset) @@ -108,14 +112,14 @@ namespace osu.Game.Tests.Visual AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any()); } - private void selectBeatmap(string name) + private void selectBeatmap(Beatmap b) { - var infoBefore = infoWedge.Info; + BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null; - AddStep($"select {name} beatmap", () => + AddStep($"select {b.Metadata.Title} beatmap", () => { - beatmap.Value = new TestWorkingBeatmap(beatmaps.First(b => b.BeatmapInfo.Ruleset.ShortName == name)); - infoWedge.UpdateBeatmap(beatmap); + infoBefore = infoWedge.Info; + infoWedge.UpdateBeatmap(beatmap.Value = new TestWorkingBeatmap(b)); }); AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load"); diff --git a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs new file mode 100644 index 0000000000..1e7618232d --- /dev/null +++ b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + public class TestCasePlayerLoader : OsuTestCase + { + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + AddStep("load dummy beatmap", () => Add(new PlayerLoader(new Player + { + InitialBeatmap = new DummyWorkingBeatmap(game), + AllowPause = false, + AllowLeadIn = false, + AllowResults = false, + }))); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index 0fdc01a974..aca832110a 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -5,7 +5,9 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile.Header; @@ -17,6 +19,7 @@ namespace osu.Game.Tests.Visual public class TestCaseUserProfile : OsuTestCase { private readonly TestUserProfileOverlay profile; + private APIAccess api; public override IReadOnlyList RequiredTypes => new[] { @@ -32,6 +35,12 @@ namespace osu.Game.Tests.Visual Add(profile = new TestUserProfileOverlay()); } + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + this.api = api; + } + protected override void LoadComplete() { base.LoadComplete(); @@ -79,9 +88,10 @@ namespace osu.Game.Tests.Visual { Username = @"peppy", Id = 2, + IsSupporter = true, Country = new Country { FullName = @"Australia", FlagName = @"AU" }, CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg" - })); + }, api.IsLoggedIn)); checkSupporterTag(true); @@ -91,7 +101,7 @@ namespace osu.Game.Tests.Visual Id = 3103765, Country = new Country { FullName = @"Japan", FlagName = @"JP" }, CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" - })); + }, api.IsLoggedIn)); AddStep("Hide", profile.Hide); AddStep("Show without reload", profile.Show); diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 2646e953cf..057c2c2de1 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net461 + netcoreapp2.0;net471 diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index bcd11f1fc8..2927654f62 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -21,7 +21,7 @@ namespace osu.Game.Beatmaps.Formats return output; } - protected abstract void ParseStreamInto(StreamReader stream, TOutput beatmap); + protected abstract void ParseStreamInto(StreamReader stream, TOutput output); } public abstract class Decoder diff --git a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs index e5574cd82e..fba89b8ac1 100644 --- a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs @@ -13,15 +13,15 @@ namespace osu.Game.Beatmaps.Formats AddDecoder("{", m => new JsonBeatmapDecoder()); } - protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap) + protected override void ParseStreamInto(StreamReader stream, Beatmap output) { stream.BaseStream.Position = 0; stream.DiscardBufferedData(); - stream.ReadToEnd().DeserializeInto(beatmap); + stream.ReadToEnd().DeserializeInto(output); - foreach (var hitObject in beatmap.HitObjects) - hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); + foreach (var hitObject in output.HitObjects) + hitObject.ApplyDefaults(output.ControlPointInfo, output.BeatmapInfo.BaseDifficulty); } } } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 06160a87e0..e77efd8508 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -19,7 +19,7 @@ namespace osu.Game.Beatmaps.Formats FormatVersion = version; } - protected override void ParseStreamInto(StreamReader stream, T beatmap) + protected override void ParseStreamInto(StreamReader stream, T output) { Section section = Section.None; @@ -33,14 +33,21 @@ namespace osu.Game.Beatmaps.Formats { if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) { - Logger.Log($"Unknown section \"{line}\" in {beatmap}"); + Logger.Log($"Unknown section \"{line}\" in {output}"); section = Section.None; } continue; } - ParseLine(beatmap, section, line); + try + { + ParseLine(output, section, line); + } + catch (Exception e) + { + Logger.Error(e, $"Failed to process line \"{line}\" into {output}"); + } } } diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 3efaa02a31..509622c2fe 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -60,6 +60,9 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowFpsDisplay, false); Set(OsuSetting.ShowStoryboard, true); + Set(OsuSetting.BeatmapSkins, true); + Set(OsuSetting.BeatmapHitsounds, true); + Set(OsuSetting.CursorRotation, true); Set(OsuSetting.MenuParallax, true); @@ -133,6 +136,8 @@ namespace osu.Game.Configuration Skin, ScreenshotFormat, ScreenshotCaptureMenuCursor, - SongSelectRightMouseScroll + SongSelectRightMouseScroll, + BeatmapSkins, + BeatmapHitsounds } } diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index c39e9abf8b..157c814f55 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -71,9 +71,9 @@ namespace osu.Game.Graphics.Containers switch (linkType) { case LinkAction.OpenBeatmap: - // todo: replace this with overlay.ShowBeatmap(id) once an appropriate API call is implemented. - if (int.TryParse(linkArgument, out int beatmapId)) - Process.Start($"https://osu.ppy.sh/b/{beatmapId}"); + // TODO: proper query params handling + if (linkArgument != null && int.TryParse(linkArgument.Contains('?') ? linkArgument.Split('?')[0] : linkArgument, out int beatmapId)) + game?.ShowBeatmap(beatmapId); break; case LinkAction.OpenBeatmapSet: if (int.TryParse(linkArgument, out int setId)) diff --git a/osu.Game/IO/Archives/ArchiveReader.cs b/osu.Game/IO/Archives/ArchiveReader.cs index d14080de5b..808ce159bb 100644 --- a/osu.Game/IO/Archives/ArchiveReader.cs +++ b/osu.Game/IO/Archives/ArchiveReader.cs @@ -1,14 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using System.IO; using osu.Framework.IO.Stores; namespace osu.Game.IO.Archives { - public abstract class ArchiveReader : IDisposable, IResourceStore + public abstract class ArchiveReader : IResourceStore { /// /// Opens a stream for reading a specific file from this archive. diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs index b031791900..37dd77af46 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs @@ -5,13 +5,21 @@ namespace osu.Game.Online.API.Requests { public class GetBeatmapSetRequest : APIRequest { - private readonly int beatmapSetId; + private readonly int id; + private readonly BeatmapSetLookupType type; - public GetBeatmapSetRequest(int beatmapSetId) + public GetBeatmapSetRequest(int id, BeatmapSetLookupType type = BeatmapSetLookupType.SetId) { - this.beatmapSetId = beatmapSetId; + this.id = id; + this.type = type; } - protected override string Target => $@"beatmapsets/{beatmapSetId}"; + protected override string Target => type == BeatmapSetLookupType.SetId ? $@"beatmapsets/{id}" : $@"beatmapsets/lookup?beatmap_id={id}"; + } + + public enum BeatmapSetLookupType + { + SetId, + BeatmapId, } } diff --git a/osu.Game/Online/API/Requests/GetMessagesRequest.cs b/osu.Game/Online/API/Requests/GetMessagesRequest.cs index d600f40716..68de194bae 100644 --- a/osu.Game/Online/API/Requests/GetMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetMessagesRequest.cs @@ -11,7 +11,7 @@ namespace osu.Game.Online.API.Requests public class GetMessagesRequest : APIRequest> { private readonly List channels; - private long? since; + private readonly long? since; public GetMessagesRequest(List channels, long? sinceId) { diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index 9026d10334..607e8e5127 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -7,7 +7,7 @@ namespace osu.Game.Online.API.Requests { public class GetUserRequest : APIRequest { - private long? userId; + private readonly long? userId; public GetUserRequest(long? userId = null) { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 578d88bd89..3852580c49 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -166,6 +166,12 @@ namespace osu.Game /// The user to display. public void ShowUser(long userId) => userProfile.ShowUser(userId); + /// + /// Show a beatmap's set as an overlay, displaying the given beatmap. + /// + /// The beatmap to show. + public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId); + protected void LoadScore(Score s) { scoreLoad?.Cancel(); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index b76510ea1e..487cb50c9a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -188,6 +188,20 @@ namespace osu.Game FileStore.Cleanup(); AddInternal(api); + + GlobalActionContainer globalBinding; + + CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; + CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this) + { + RelativeSizeAxes = Axes.Both, + Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both } + }; + + base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer }); + + KeyBindingStore.Register(globalBinding); + dependencies.Cache(globalBinding); } private void runMigrations() @@ -217,20 +231,6 @@ namespace osu.Game { base.LoadComplete(); - GlobalActionContainer globalBinding; - - CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; - CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this) - { - RelativeSizeAxes = Axes.Both, - Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both } - }; - - base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer }); - - KeyBindingStore.Register(globalBinding); - dependencies.Cache(globalBinding); - // TODO: This is temporary until we reimplement the local FPS display. // It's just to allow end-users to access the framework FPS display without knowing the shortcut key. fpsDisplayVisible = LocalConfig.GetBindable(OsuSetting.ShowFpsDisplay); diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 366c34eae3..096f7bb63c 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -17,21 +17,39 @@ using osu.Game.Online.API.Requests; using osu.Game.Overlays.BeatmapSet; using osu.Game.Rulesets; using osu.Game.Overlays.BeatmapSet.Scores; +using System.Linq; namespace osu.Game.Overlays { public class BeatmapSetOverlay : WaveOverlayContainer { + private const int fade_duration = 300; + public const float X_PADDING = 40; public const float RIGHT_WIDTH = 275; private readonly Header header; private readonly Info info; + private APIAccess api; private RulesetStore rulesets; private readonly ScrollContainer scroll; + private BeatmapSetInfo beatmapSet; + + public BeatmapSetInfo BeatmapSet + { + get => beatmapSet; + set + { + if (value == beatmapSet) + return; + + header.BeatmapSet = info.BeatmapSet = beatmapSet = value; + } + } + // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; @@ -107,7 +125,8 @@ namespace osu.Game.Overlays { base.PopOut(); header.Details.StopPreview(); - FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); + + FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null); } protected override bool OnClick(InputState state) @@ -116,17 +135,31 @@ namespace osu.Game.Overlays return true; } + public void FetchAndShowBeatmap(int beatmapId) + { + BeatmapSet = null; + var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); + req.Success += res => + { + ShowBeatmapSet(res.ToBeatmapSet(rulesets)); + header.Picker.Beatmap.Value = header.BeatmapSet.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); + }; + api.Queue(req); + Show(); + } + public void FetchAndShowBeatmapSet(int beatmapSetId) { - // todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work. + BeatmapSet = null; var req = new GetBeatmapSetRequest(beatmapSetId); req.Success += res => ShowBeatmapSet(res.ToBeatmapSet(rulesets)); api.Queue(req); + Show(); } public void ShowBeatmapSet(BeatmapSetInfo set) { - header.BeatmapSet = info.BeatmapSet = set; + BeatmapSet = set; Show(); scroll.ScrollTo(0); } diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index 77a30e6389..91b790ece4 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -307,6 +307,8 @@ namespace osu.Game.Overlays.Chat { public override bool IsRemovable => false; + public override bool IsSwitchable => false; + public ChannelSelectorTabItem(Channel value) : base(value) { Depth = float.MaxValue; diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs index 291db45e97..36a9a9b01a 100644 --- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs @@ -92,22 +92,18 @@ namespace osu.Game.Overlays.Profile.Header public void ShowBadges(Badge[] badges) { - switch (badges.Length) + if (badges == null || badges.Length == 0) { - case 0: - Hide(); - return; - case 1: - badgeCountText.Hide(); - break; - default: - badgeCountText.Show(); - badgeCountText.Text = $"{badges.Length} badges"; - break; + Hide(); + return; } - Show(); badgeCount = badges.Length; + + badgeCountText.FadeTo(badgeCount > 1 ? 1 : 0); + badgeCountText.Text = $"{badges.Length} badges"; + + Show(); visibleBadge = 0; badgeFlowContainer.Clear(); diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index edfea57e94..36c6b07f54 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.UI.Scrolling base.UpdateAfterChildrenLife(); // We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions - speedChangeVisualiser.ComputePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize); + speedChangeVisualiser.UpdatePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize); } } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs index 02791e0517..097e28b2dc 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs @@ -10,24 +10,23 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers public interface ISpeedChangeVisualiser { /// - /// Computes the states of s that are constant, such as lifetime and spatial length. + /// Computes the states of s that remain constant while scrolling, such as lifetime and spatial length. /// This is invoked once whenever or changes. /// /// The s whose states should be computed. /// The scrolling direction. - /// The duration required to scroll through one length of the screen before any control point adjustments. + /// The duration required to scroll through one length of the screen before any speed adjustments. /// The length of the screen that is scrolled through. void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length); /// - /// Computes the states of s that change depending on , such as position. - /// This is invoked once per frame. + /// Updates the positions of s, depending on the current time. This is invoked once per frame. /// - /// The s whose states should be computed. + /// The s whose positions should be computed. /// The scrolling direction. /// The current time. - /// The duration required to scroll through one length of the screen before any control point adjustments. + /// The duration required to scroll through one length of the screen before any speed adjustments. /// The length of the screen that is scrolled through. - void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length); + void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index 2365582645..35a91275a7 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using osu.Framework.Lists; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Timing; using OpenTK; @@ -22,24 +23,45 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { foreach (var obj in hitObjects) { - var controlPoint = controlPointAt(obj.HitObject.StartTime); - obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPoint.Multiplier; + // The total amount of time that the hitobject will remain visible within the timeRange, which decreases as the speed multiplier increases + double visibleDuration = timeRange / controlPointAt(obj.HitObject.StartTime).Multiplier; + + obj.LifetimeStart = obj.HitObject.StartTime - visibleDuration; + + if (obj.HitObject is IHasEndTime endTime) + { + // At the hitobject's end time, the hitobject will be positioned such that its end rests at the origin. + // This results in a negative-position value, and the absolute of it indicates the length of the hitobject. + var hitObjectLength = -hitObjectPositionAt(obj, endTime.EndTime, timeRange); + + switch (direction) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + obj.Height = (float)(hitObjectLength * length.Y); + break; + case ScrollingDirection.Left: + case ScrollingDirection.Right: + obj.Width = (float)(hitObjectLength * length.X); + break; + } + } if (obj.HasNestedHitObjects) { ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); - ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); + + // Nested hitobjects don't need to scroll, but they do need accurate positions + UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); } } } - public void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) + public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) { foreach (var obj in hitObjects) { - var controlPoint = controlPointAt(obj.HitObject.StartTime); - - var position = (obj.HitObject.StartTime - currentTime) * controlPoint.Multiplier / timeRange; + var position = hitObjectPositionAt(obj, currentTime, timeRange); switch (direction) { @@ -59,7 +81,28 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers } } + /// + /// Computes the position of a at a point in time. + /// + /// At t < startTime, position > 0.
+ /// At t = startTime, position = 0.
+ /// At t > startTime, position < 0. + ///
+ ///
+ /// The . + /// The time to find the position of at. + /// The amount of time visualised by the scrolling area. + /// The position of in the scrolling area at time = . + private double hitObjectPositionAt(DrawableHitObject obj, double time, double timeRange) + => (obj.HitObject.StartTime - time) / timeRange * controlPointAt(obj.HitObject.StartTime).Multiplier; + private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint(); + + /// + /// Finds the which affects the speed of hitobjects at a specific time. + /// + /// The time which the should affect. + /// The . private MultiplierControlPoint controlPointAt(double time) { if (controlPoints.Count == 0) diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs index 708a2f173b..e353c07e9f 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs @@ -25,23 +25,25 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { foreach (var obj in hitObjects) { + // To reduce iterations when updating hitobject positions later on, their initial positions are cached var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange); + // Todo: This is approximate and will be incorrect in the case of extreme speed changes obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000; if (obj.HitObject is IHasEndTime endTime) { - var diff = positionAt(endTime.EndTime, timeRange) - startPosition; + var hitObjectLength = positionAt(endTime.EndTime, timeRange) - startPosition; switch (direction) { case ScrollingDirection.Up: case ScrollingDirection.Down: - obj.Height = (float)(diff * length.Y); + obj.Height = (float)(hitObjectLength * length.Y); break; case ScrollingDirection.Left: case ScrollingDirection.Right: - obj.Width = (float)(diff * length.X); + obj.Width = (float)(hitObjectLength * length.X); break; } } @@ -49,12 +51,14 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers if (obj.HasNestedHitObjects) { ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); - ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); + + // Nested hitobjects don't need to scroll, but they do need accurate positions + UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); } } } - public void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) + public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) { var timelinePosition = positionAt(currentTime, timeRange); @@ -80,21 +84,38 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers } } + /// + /// Finds the position which corresponds to a point in time. + /// This is a non-linear operation that depends on all the control points up to and including the one active at the time value. + /// + /// The time to find the position at. + /// The amount of time visualised by the scrolling area. + /// A positive value indicating the position at . private double positionAt(double time, double timeRange) { double length = 0; + + // We need to consider all timing points until the specified time and not just the currently-active one, + // since each timing point individually affects the positions of _all_ hitobjects after its start time for (int i = 0; i < controlPoints.Count; i++) { var current = controlPoints[i]; var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null; + // We don't need to consider any control points beyond the current time, since it will not yet + // affect any hitobjects if (i > 0 && current.StartTime > time) continue; // Duration of the current control point var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime; - length += Math.Min(currentDuration, time - current.StartTime) * current.Multiplier / timeRange; + // We want to consider the minimal amount of time that this control point has affected, + // which may be either its duration, or the amount of time that has passed within it + var durationInCurrent = Math.Min(currentDuration, time - current.StartTime); + + // Figure out how much of the time range the duration represents, and adjust it by the speed multiplier + length += durationInCurrent / timeRange * current.Multiplier; } return length; diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index e7d3690f59..5e9863f642 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -26,14 +26,14 @@ namespace osu.Game.Screens return false; } - public override bool Push(Screen screen) + public override void Push(Screen screen) { // When trying to push a non-loaded screen, load it asynchronously and re-invoke Push // once it's done. if (screen.LoadState == LoadState.NotLoaded) { LoadComponentAsync(screen, d => Push((BackgroundScreen)d)); - return true; + return; } // Make sure the in-progress loading is complete before pushing the screen. @@ -41,8 +41,6 @@ namespace osu.Game.Screens Thread.Sleep(1); base.Push(screen); - - return true; } protected override void Update() diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index ea1d85bb5b..adb749b492 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -178,7 +178,7 @@ namespace osu.Game.Screens.Edit } currentScreen.Beatmap.BindTo(Beatmap); - screenContainer.Add(currentScreen); + LoadComponentAsync(currentScreen, screenContainer.Add); } protected override bool OnWheel(InputState state) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index ede98d986a..b9a8e9914a 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers var dragLayer = new DragLayer(maskContainer.Select); dragLayer.DragEnd += () => maskSelection.UpdateVisibility(); - InternalChildren = new Drawable[] + InternalChildren = new[] { dragLayer, maskSelection, diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 6d75b8dc15..df2691c28e 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -43,6 +43,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers public override void Add(HitObjectMask drawable) { + if (drawable == null) throw new ArgumentNullException(nameof(drawable)); + base.Add(drawable); drawable.Selected += onMaskSelected; @@ -53,6 +55,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers public override bool Remove(HitObjectMask drawable) { + if (drawable == null) throw new ArgumentNullException(nameof(drawable)); + var result = base.Remove(drawable); if (result) diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index c62a622c3c..4de76e530a 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -39,6 +39,7 @@ namespace osu.Game.Screens.Menu private Bindable menuVoice; private Bindable menuMusic; private Track track; + private WorkingBeatmap beatmap; [BackgroundDependencyLoader] private void load(AudioManager audio, OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) @@ -69,9 +70,8 @@ namespace osu.Game.Screens.Menu } } - Beatmap.Value = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); - - track = Beatmap.Value.Track; + beatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); + track = beatmap.Track; welcome = audio.Sample.Get(@"welcome"); seeya = audio.Sample.Get(@"seeya"); @@ -81,6 +81,8 @@ namespace osu.Game.Screens.Menu { base.OnEntering(last); + Game.Beatmap.Value = beatmap; + if (menuVoice) welcome.Play(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 6c5e10c836..836730e3f3 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Play private DrawableStoryboard storyboard; private Container storyboardContainer; - private bool loadedSuccessfully => RulesetContainer?.Objects.Any() == true; + public bool LoadedBeatmapSuccessfully => RulesetContainer?.Objects.Any() == true; [BackgroundDependencyLoader] private void load(AudioManager audio, APIAccess api, OsuConfigManager config) @@ -86,10 +86,7 @@ namespace osu.Game.Screens.Play WorkingBeatmap working = Beatmap.Value; if (working is DummyWorkingBeatmap) - { - Exit(); return; - } sampleRestart = audio.Sample.Get(@"Gameplay/restart"); @@ -122,14 +119,15 @@ namespace osu.Game.Screens.Play } if (!RulesetContainer.Objects.Any()) - throw new InvalidOperationException("Beatmap contains no hit objects!"); + { + Logger.Error(new InvalidOperationException("Beatmap contains no hit objects!"), "Beatmap contains no hit objects!"); + return; + } } catch (Exception e) { Logger.Error(e, "Could not load beatmap sucessfully!"); - //couldn't load, hard abort! - Exit(); return; } @@ -295,7 +293,7 @@ namespace osu.Game.Screens.Play { base.OnEntering(last); - if (!loadedSuccessfully) + if (!LoadedBeatmapSuccessfully) return; Content.Alpha = 0; @@ -345,7 +343,7 @@ namespace osu.Game.Screens.Play return base.OnExiting(next); } - if (loadedSuccessfully) + if (LoadedBeatmapSuccessfully) pauseContainer?.Pause(); return true; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index ca22b274a4..56fbd7b6e7 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -159,14 +159,14 @@ namespace osu.Game.Screens.Play loadTask = null; - if (!Push(player)) - Exit(); + //By default, we want to load the player and never be returned to. + //Note that this may change if the player we load requested a re-run. + ValidForResume = false; + + if (player.LoadedBeatmapSuccessfully) + Push(player); else - { - //By default, we want to load the player and never be returned to. - //Note that this may change if the player we load requested a re-run. - ValidForResume = false; - } + Exit(); }); }, 500); } diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs index 92d069a224..439e344020 100644 --- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs @@ -15,6 +15,8 @@ namespace osu.Game.Screens.Play.PlayerSettings private readonly PlayerSliderBar dimSliderBar; private readonly PlayerSliderBar blurSliderBar; private readonly PlayerCheckbox showStoryboardToggle; + private readonly PlayerCheckbox beatmapSkinsToggle; + private readonly PlayerCheckbox beatmapHitsoundsToggle; public VisualSettings() { @@ -34,7 +36,9 @@ namespace osu.Game.Screens.Play.PlayerSettings { Text = "Toggles:" }, - showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" } + showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" }, + beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" }, + beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" } }; } @@ -44,6 +48,8 @@ namespace osu.Game.Screens.Play.PlayerSettings dimSliderBar.Bindable = config.GetBindable(OsuSetting.DimLevel); blurSliderBar.Bindable = config.GetBindable(OsuSetting.BlurLevel); showStoryboardToggle.Bindable = config.GetBindable(OsuSetting.ShowStoryboard); + beatmapSkinsToggle.Bindable = config.GetBindable(OsuSetting.BeatmapSkins); + beatmapHitsoundsToggle.Bindable = config.GetBindable(OsuSetting.BeatmapHitsounds); } } } diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs index 9d92439a4b..42d8af07b9 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -223,11 +223,11 @@ namespace osu.Game.Screens.Ranking private class DateTimeDisplay : Container { - private DateTime datetime; + private readonly DateTime date; - public DateTimeDisplay(DateTime datetime) + public DateTimeDisplay(DateTime date) { - this.datetime = datetime; + this.date = date; AutoSizeAxes = Axes.Y; @@ -251,7 +251,7 @@ namespace osu.Game.Screens.Ranking { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Text = datetime.ToShortDateString(), + Text = date.ToShortDateString(), Padding = new MarginPadding { Horizontal = 10, Vertical = 5 }, Colour = Color4.White, }, @@ -259,7 +259,7 @@ namespace osu.Game.Screens.Ranking { Origin = Anchor.CentreRight, Anchor = Anchor.CentreRight, - Text = datetime.ToShortTimeString(), + Text = date.ToShortTimeString(), Padding = new MarginPadding { Horizontal = 10, Vertical = 5 }, Colour = Color4.White, } diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs index 7ea6e7c82b..2118cfdc78 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs @@ -16,11 +16,22 @@ namespace osu.Game.Screens.Select.Carousel protected List InternalChildren = new List(); + /// + /// Used to assign a monotonically increasing ID to children as they are added. This member is + /// incremented whenever a child is added. + /// + private ulong currentChildID; + public override List Drawables { get { var drawables = base.Drawables; + + // if we are explicitly not present, don't ever present children. + // without this check, children drawables can potentially be presented without their group header. + if (DrawableRepresentation.Value?.IsPresent == false) return drawables; + foreach (var c in InternalChildren) drawables.AddRange(c.Drawables); return drawables; @@ -39,6 +50,7 @@ namespace osu.Game.Screens.Select.Carousel public virtual void AddChild(CarouselItem i) { i.State.ValueChanged += v => ChildItemStateChanged(i, v); + i.ChildID = ++currentChildID; InternalChildren.Add(i); } diff --git a/osu.Game/Screens/Select/Carousel/CarouselItem.cs b/osu.Game/Screens/Select/Carousel/CarouselItem.cs index 2efc928984..c0781f09c0 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselItem.cs @@ -24,16 +24,18 @@ namespace osu.Game.Screens.Select.Carousel { var items = new List(); - var self = drawableRepresentation.Value; + var self = DrawableRepresentation.Value; if (self?.IsPresent == true) items.Add(self); return items; } } + private int creationOrder; + protected CarouselItem() { - drawableRepresentation = new Lazy(CreateDrawableRepresentation); + DrawableRepresentation = new Lazy(CreateDrawableRepresentation); Filtered.ValueChanged += v => { @@ -42,15 +44,23 @@ namespace osu.Game.Screens.Select.Carousel }; } - private readonly Lazy drawableRepresentation; + protected readonly Lazy DrawableRepresentation; + /// + /// Used as a default sort method for s of differing types. + /// + internal ulong ChildID; + + /// + /// Create a fresh drawable version of this item. If you wish to consume the current representation, use instead. + /// protected abstract DrawableCarouselItem CreateDrawableRepresentation(); public virtual void Filter(FilterCriteria criteria) { } - public virtual int CompareTo(FilterCriteria criteria, CarouselItem other) => GetHashCode().CompareTo(other.GetHashCode()); + public virtual int CompareTo(FilterCriteria criteria, CarouselItem other) => ChildID.CompareTo(other.ChildID); } public enum CarouselItemState diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index cda8a549da..f39952dc31 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using OpenTK; using OpenTK.Graphics; @@ -35,6 +36,8 @@ namespace osu.Game.Screens.Select.Carousel private Triangles triangles; private StarCounter starCounter; + private BeatmapSetOverlay beatmapOverlay; + public DrawableCarouselBeatmap(CarouselBeatmap panel) : base(panel) { beatmap = panel.Beatmap; @@ -42,8 +45,10 @@ namespace osu.Game.Screens.Select.Carousel } [BackgroundDependencyLoader(true)] - private void load(SongSelect songSelect, BeatmapManager manager) + private void load(SongSelect songSelect, BeatmapManager manager, BeatmapSetOverlay beatmapOverlay) { + this.beatmapOverlay = beatmapOverlay; + if (songSelect != null) { startRequested = songSelect.FinaliseSelection; @@ -171,6 +176,10 @@ namespace osu.Game.Screens.Select.Carousel new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested?.Invoke(beatmap)), new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested?.Invoke(beatmap)), new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested?.Invoke(beatmap)), + new OsuMenuItem("Details", MenuItemType.Standard, () => + { + if (beatmap.OnlineBeatmapID.HasValue) beatmapOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value); + }), }; } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 0a05137a87..45c6ec80aa 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -113,6 +113,31 @@ namespace osu.Game.Skinning string path = getPathForFile(name); return path == null ? null : underlyingStore.Get(path); } + + #region IDisposable Support + + private bool isDisposed; + + protected virtual void Dispose(bool disposing) + { + if (!isDisposed) + { + isDisposed = true; + } + } + + ~LegacySkinResourceStore() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion } } } diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 995518af0a..0ef54c7310 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -12,7 +12,7 @@ namespace osu.Game.Skinning { } - protected override void ParseLine(SkinConfiguration output, Section section, string line) + protected override void ParseLine(SkinConfiguration skin, Section section, string line) { switch (section) { @@ -22,17 +22,17 @@ namespace osu.Game.Skinning switch (pair.Key) { case @"Name": - output.SkinInfo.Name = pair.Value; + skin.SkinInfo.Name = pair.Value; break; case @"Author": - output.SkinInfo.Creator = pair.Value; + skin.SkinInfo.Creator = pair.Value; break; } break; } - base.ParseLine(output, section, line); + base.ParseLine(skin, section, line); } } } diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index bfbb1719df..ad6a033936 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -4,9 +4,11 @@ using System; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; +using osu.Game.Configuration; namespace osu.Game.Skinning { @@ -14,17 +16,37 @@ namespace osu.Game.Skinning { public event Action SourceChanged; - public Drawable GetDrawableComponent(string componentName) => source.GetDrawableComponent(componentName) ?? fallbackSource?.GetDrawableComponent(componentName); + private Bindable beatmapSkins = new Bindable(); + private Bindable beatmapHitsounds = new Bindable(); - public Texture GetTexture(string componentName) => source.GetTexture(componentName) ?? fallbackSource.GetTexture(componentName); + public Drawable GetDrawableComponent(string componentName) + { + Drawable sourceDrawable; + if (beatmapSkins && (sourceDrawable = source.GetDrawableComponent(componentName)) != null) + return sourceDrawable; + return fallbackSource?.GetDrawableComponent(componentName); + } - public SampleChannel GetSample(string sampleName) => source.GetSample(sampleName) ?? fallbackSource?.GetSample(sampleName); + public Texture GetTexture(string componentName) + { + Texture sourceTexture; + if (beatmapSkins && (sourceTexture = source.GetTexture(componentName)) != null) + return sourceTexture; + return fallbackSource.GetTexture(componentName); + } + + public SampleChannel GetSample(string sampleName) + { + SampleChannel sourceChannel; + if (beatmapHitsounds && (sourceChannel = source.GetSample(sampleName)) != null) + return sourceChannel; + return fallbackSource?.GetSample(sampleName); + } public TValue? GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : struct { TValue? val = null; - var conf = (source as Skin)?.Configuration as TConfiguration; - if (conf != null) + if ((source as Skin)?.Configuration is TConfiguration conf) val = query?.Invoke(conf); return val ?? fallbackSource?.GetValue(query); @@ -33,8 +55,7 @@ namespace osu.Game.Skinning public TValue GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : class { TValue val = null; - var conf = (source as Skin)?.Configuration as TConfiguration; - if (conf != null) + if ((source as Skin)?.Configuration is TConfiguration conf) val = query?.Invoke(conf); return val ?? fallbackSource?.GetValue(query); @@ -60,6 +81,18 @@ namespace osu.Game.Skinning return dependencies; } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + beatmapSkins = config.GetBindable(OsuSetting.BeatmapSkins); + beatmapSkins.ValueChanged += val => onSourceChanged(); + beatmapSkins.TriggerChange(); + + beatmapHitsounds = config.GetBindable(OsuSetting.BeatmapHitsounds); + beatmapHitsounds.ValueChanged += val => onSourceChanged(); + beatmapHitsounds.TriggerChange(); + } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 02f425c296..2b677f1f42 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -15,18 +15,14 @@ namespace osu.Game.Tests.Visual { protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location; - private readonly TestCaseTestRunner.TestRunner runner; + private TestCaseTestRunner.TestRunner runner; - public OsuTestCaseTestRunner() + protected override void LoadAsyncComplete() { - runner = new TestCaseTestRunner.TestRunner(); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Add(runner); + // this has to be run here rather than LoadComplete because + // TestCase.cs is checking the IsLoaded state (on another thread) and expects + // the runner to be loaded at that point. + Add(runner = new TestCaseTestRunner.TestRunner()); } public void RunTestBlocking(TestCase test) => runner.RunTestBlocking(test);