This commit is contained in:
Susko3 2021-01-21 15:23:22 +01:00
commit 45b10e48e0
254 changed files with 3480 additions and 1917 deletions

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Benchmarks" type="DotNetProject" factoryName=".NET Project"> <configuration default="false" name="Benchmarks" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/netcoreapp3.1/osu.Game.Benchmarks.dll" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/net5.0/osu.Game.Benchmarks.dll" />
<option name="PROGRAM_PARAMETERS" value="--filter *" /> <option name="PROGRAM_PARAMETERS" value="--filter *" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/netcoreapp3.1" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/net5.0" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" /> <option name="PROJECT_TFM" value="net5.0" />
<method v="2"> <method v="2">
<option name="Build" enabled="true" /> <option name="Build" enabled="true" />
</method> </method>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="CatchRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false"> <configuration default="false" name="CatchRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Catch.Tests.dll" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/net5.0/osu.Game.Rulesets.Catch.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp3.1" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/net5.0" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" /> <option name="PROJECT_TFM" value="net5.0" />
<browser url="http://localhost:5000" /> <browser url="http://localhost:5000" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" />

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="ManiaRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false"> <configuration default="false" name="ManiaRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Mania.Tests.dll" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/net5.0/osu.Game.Rulesets.Mania.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/netcoreapp3.1" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/net5.0" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" /> <option name="PROJECT_TFM" value="net5.0" />
<browser url="http://localhost:5000" /> <browser url="http://localhost:5000" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" />

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="OsuRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false"> <configuration default="false" name="OsuRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Osu.Tests.dll" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/net5.0/osu.Game.Rulesets.Osu.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/netcoreapp3.1" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/net5.0" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" /> <option name="PROJECT_TFM" value="net5.0" />
<browser url="http://localhost:5000" /> <browser url="http://localhost:5000" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" />

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="TaikoRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false"> <configuration default="false" name="TaikoRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Taiko.Tests.dll" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/net5.0/osu.Game.Rulesets.Taiko.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/netcoreapp3.1" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/net5.0" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" /> <option name="PROJECT_TFM" value="net5.0" />
<browser url="http://localhost:5000" /> <browser url="http://localhost:5000" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" />

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tournament" type="DotNetProject" factoryName=".NET Project" folderName="Tournament" activateToolWindowBeforeRun="false"> <configuration default="false" name="Tournament" type="DotNetProject" factoryName=".NET Project" folderName="Tournament" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net5.0/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="--tournament" /> <option name="PROGRAM_PARAMETERS" value="--tournament" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net5.0" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" /> <option name="PROJECT_TFM" value="net5.0" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" />
</method> </method>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tournament (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Tournament" activateToolWindowBeforeRun="false"> <configuration default="false" name="Tournament (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Tournament" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tournament.Tests.dll" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/net5.0/osu.Game.Tournament.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/net5.0" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" /> <option name="PROJECT_TFM" value="net5.0" />
<browser url="http://localhost:5000" /> <browser url="http://localhost:5000" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" />

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu!" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false"> <configuration default="false" name="osu!" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net5.0/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net5.0" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" /> <option name="PROJECT_TFM" value="net5.0" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" />
</method> </method>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false"> <configuration default="false" name="osu! (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tests.dll" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/net5.0/osu.Game.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp3.1" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/net5.0" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" /> <option name="PROJECT_TFM" value="net5.0" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" />
</method> </method>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! (legacy osuTK)" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false"> <configuration default="false" name="osu! (legacy osuTK)" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net5.0/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="--tk" /> <option name="PROGRAM_PARAMETERS" value="--tk" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net5.0" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" /> <option name="PROJECT_TFM" value="net5.0" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" />
</method> </method>

18
.vscode/launch.json vendored
View File

@ -7,7 +7,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" "${workspaceRoot}/osu.Desktop/bin/Debug/net5.0/osu!.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)", "preLaunchTask": "Build osu! (Debug)",
@ -19,7 +19,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1/osu!.dll" "${workspaceRoot}/osu.Desktop/bin/Release/net5.0/osu!.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)", "preLaunchTask": "Build osu! (Release)",
@ -31,7 +31,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tests.dll" "${workspaceRoot}/osu.Game.Tests/bin/Debug/net5.0/osu.Game.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Debug)", "preLaunchTask": "Build tests (Debug)",
@ -43,7 +43,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp3.1/osu.Game.Tests.dll" "${workspaceRoot}/osu.Game.Tests/bin/Release/net5.0/osu.Game.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Release)", "preLaunchTask": "Build tests (Release)",
@ -55,7 +55,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll", "${workspaceRoot}/osu.Desktop/bin/Debug/net5.0/osu!.dll",
"--tournament" "--tournament"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
@ -68,7 +68,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1/osu!.dll", "${workspaceRoot}/osu.Desktop/bin/Release/net5.0/osu!.dll",
"--tournament" "--tournament"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
@ -81,7 +81,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tournament.Tests.dll", "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/net5.0/osu.Game.Tournament.Tests.dll",
"--tournament" "--tournament"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
@ -94,7 +94,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tournament.Tests.dll", "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/net5.0/osu.Game.Tournament.Tests.dll",
"--tournament" "--tournament"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
@ -105,7 +105,7 @@
"name": "Benchmark", "name": "Benchmark",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/osu.Game.Benchmarks/bin/Release/netcoreapp3.1/osu.Game.Benchmarks.dll", "program": "${workspaceRoot}/osu.Game.Benchmarks/bin/Release/net5.0/osu.Game.Benchmarks.dll",
"args": [ "args": [
"--filter", "--filter",
"*" "*"

View File

@ -18,7 +18,7 @@
<ItemGroup Label="Code Analysis"> <ItemGroup Label="Code Analysis">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.2" PrivateAssets="All" /> <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.2" PrivateAssets="All" />
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" /> <AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.1" PrivateAssets="All" /> <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.2" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Code Analysis"> <PropertyGroup Label="Code Analysis">
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
@ -28,9 +28,17 @@
<NoWarn>$(NoWarn);CS1591</NoWarn> <NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<!-- DeepEqual is not netstandard-compatible. This is fine since we run tests with .NET Framework anyway. <!--
This is required due to https://github.com/NuGet/Home/issues/5740 --> NU1701:
<NoWarn>$(NoWarn);NU1701</NoWarn> DeepEqual is not netstandard-compatible. This is fine since we run tests with .NET Framework anyway.
This is required due to https://github.com/NuGet/Home/issues/5740
CA9998:
Microsoft.CodeAnalysis.FxCopAnalyzers has been deprecated.
The entire package will be able to be removed after migrating to .NET 5,
as analysers are shipped as part of the .NET 5 SDK anyway.
-->
<NoWarn>$(NoWarn);NU1701;CA9998</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Nuget"> <PropertyGroup Label="Nuget">
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
@ -40,7 +48,7 @@
<RepositoryUrl>https://github.com/ppy/osu</RepositoryUrl> <RepositoryUrl>https://github.com/ppy/osu</RepositoryUrl>
<PackageReleaseNotes>Automated release.</PackageReleaseNotes> <PackageReleaseNotes>Automated release.</PackageReleaseNotes>
<Company>ppy Pty Ltd</Company> <Company>ppy Pty Ltd</Company>
<Copyright>Copyright (c) 2020 ppy Pty Ltd</Copyright> <Copyright>Copyright (c) 2021 ppy Pty Ltd</Copyright>
<PackageTags>osu game</PackageTags> <PackageTags>osu game</PackageTags>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -1,4 +1,4 @@
Copyright (c) 2020 ppy Pty Ltd <contact@ppy.sh>. Copyright (c) 2021 ppy Pty Ltd <contact@ppy.sh>.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -36,8 +36,7 @@ If you are looking to install or test osu! without setting up a development envi
- The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets. - The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets.
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore31&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs. - When running on Windows 7 or 8.1, *[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=net50&pivots=os-windows#dependencies)** may be required to correctly run .NET 5 applications if your operating system is not up-to-date with the latest service packs.
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below. If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
## Developing a custom ruleset ## Developing a custom ruleset
@ -50,7 +49,7 @@ You can see some examples of custom rulesets by visiting the [custom ruleset dir
Please make sure you have the following prerequisites: Please make sure you have the following prerequisites:
- A desktop platform with the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) or higher installed. - A desktop platform with the [.NET 5.0 SDK](https://dotnet.microsoft.com/download) or higher installed.
- When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). - When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/). - When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding. - When running on Linux, please have a system-wide FFmpeg installation available to support video decoding.

View File

@ -1,21 +1,68 @@
clone_depth: 1 clone_depth: 1
version: '{build}' version: '{build}'
image: Visual Studio 2019 image: Visual Studio 2019
dotnet_csproj:
patch: true
file: 'osu.Game\osu.Game.csproj' # Use wildcard when it's able to exclude Xamarin projects
version: $(APPVEYOR_REPO_TAG_NAME)
before_build:
- ps: dotnet --info # Useful when version mismatch between CI and local
- ps: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects
test: off test: off
skip_non_tags: true skip_non_tags: true
configuration: Release configuration: Release
build:
project: build\Desktop.proj # Skipping Xamarin Release that's slow and covered by fastlane environment:
parallel: true matrix:
verbosity: minimal - job_name: osu-game
publish_nuget: true - job_name: osu-ruleset
job_depends_on: osu-game
- job_name: taiko-ruleset
job_depends_on: osu-game
- job_name: catch-ruleset
job_depends_on: osu-game
- job_name: mania-ruleset
job_depends_on: osu-game
nuget:
project_feed: true
for:
-
matrix:
only:
- job_name: osu-game
build_script:
- cmd: dotnet pack osu.Game\osu.Game.csproj /p:Version=%APPVEYOR_REPO_TAG_NAME%
-
matrix:
only:
- job_name: osu-ruleset
build_script:
- cmd: dotnet remove osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj reference osu.Game\osu.Game.csproj
- cmd: dotnet add osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj package ppy.osu.Game -v %APPVEYOR_REPO_TAG_NAME%
- cmd: dotnet pack osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj /p:Version=%APPVEYOR_REPO_TAG_NAME%
-
matrix:
only:
- job_name: taiko-ruleset
build_script:
- cmd: dotnet remove osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj reference osu.Game\osu.Game.csproj
- cmd: dotnet add osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj package ppy.osu.Game -v %APPVEYOR_REPO_TAG_NAME%
- cmd: dotnet pack osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj /p:Version=%APPVEYOR_REPO_TAG_NAME%
-
matrix:
only:
- job_name: catch-ruleset
build_script:
- cmd: dotnet remove osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj reference osu.Game\osu.Game.csproj
- cmd: dotnet add osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj package ppy.osu.Game -v %APPVEYOR_REPO_TAG_NAME%
- cmd: dotnet pack osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj /p:Version=%APPVEYOR_REPO_TAG_NAME%
-
matrix:
only:
- job_name: mania-ruleset
build_script:
- cmd: dotnet remove osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj reference osu.Game\osu.Game.csproj
- cmd: dotnet add osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj package ppy.osu.Game -v %APPVEYOR_REPO_TAG_NAME%
- cmd: dotnet pack osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj /p:Version=%APPVEYOR_REPO_TAG_NAME%
artifacts:
- path: '**\*.nupkg'
deploy: deploy:
- provider: Environment - provider: Environment
name: nuget name: nuget

View File

@ -48,9 +48,12 @@ desc 'Deploy to play store'
desc 'Compile the project' desc 'Compile the project'
lane :build do |options| lane :build do |options|
nuget_restore( nuget_restore(project_path: 'osu.Android/osu.Android.csproj')
project_path: 'osu.sln' nuget_restore(project_path: 'osu.Game/osu.Game.csproj')
) nuget_restore(project_path: 'osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj')
nuget_restore(project_path: 'osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj')
nuget_restore(project_path: 'osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj')
nuget_restore(project_path: 'osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj')
souyuz( souyuz(
build_configuration: 'Release', build_configuration: 'Release',
@ -107,9 +110,12 @@ platform :ios do
desc 'Compile the project' desc 'Compile the project'
lane :build do lane :build do
nuget_restore( nuget_restore(project_path: 'osu.iOS/osu.iOS.csproj')
project_path: 'osu.sln' nuget_restore(project_path: 'osu.Game/osu.Game.csproj')
) nuget_restore(project_path: 'osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj')
nuget_restore(project_path: 'osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj')
nuget_restore(project_path: 'osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj')
nuget_restore(project_path: 'osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj')
souyuz( souyuz(
platform: "ios", platform: "ios",

View File

@ -1,10 +0,0 @@
{
"sdk": {
"allowPrerelease": false,
"rollForward": "minor",
"version": "3.1.100"
},
"msbuild-sdks": {
"Microsoft.Build.Traversal": "3.0.2"
}
}

View File

@ -52,6 +52,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.1229.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2021.118.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -12,13 +13,14 @@ using Android.OS;
using Android.Provider; using Android.Provider;
using Android.Views; using Android.Views;
using osu.Framework.Android; using osu.Framework.Android;
using osu.Game.Database;
namespace osu.Android namespace osu.Android
{ {
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance)] [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance)]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream" })] [IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream" })]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })]
public class OsuGameActivity : AndroidGameActivity public class OsuGameActivity : AndroidGameActivity
{ {
@ -54,43 +56,59 @@ namespace osu.Android
{ {
case Intent.ActionDefault: case Intent.ActionDefault:
if (intent.Scheme == ContentResolver.SchemeContent) if (intent.Scheme == ContentResolver.SchemeContent)
handleImportFromUri(intent.Data); handleImportFromUris(intent.Data);
else if (osu_url_schemes.Contains(intent.Scheme)) else if (osu_url_schemes.Contains(intent.Scheme))
game.HandleLink(intent.DataString); game.HandleLink(intent.DataString);
break; break;
case Intent.ActionSend: case Intent.ActionSend:
case Intent.ActionSendMultiple:
{ {
var content = intent.ClipData?.GetItemAt(0); var uris = new List<Uri>();
if (content != null) for (int i = 0; i < intent.ClipData?.ItemCount; i++)
handleImportFromUri(content.Uri); {
var content = intent.ClipData?.GetItemAt(i);
if (content != null)
uris.Add(content.Uri);
}
handleImportFromUris(uris.ToArray());
break; break;
} }
} }
} }
private void handleImportFromUri(Uri uri) => Task.Factory.StartNew(async () => private void handleImportFromUris(params Uri[] uris) => Task.Factory.StartNew(async () =>
{ {
// there are more performant overloads of this method, but this one is the most backwards-compatible var tasks = new List<ImportTask>();
// (dates back to API 1).
var cursor = ContentResolver?.Query(uri, null, null, null, null);
if (cursor == null) await Task.WhenAll(uris.Select(async uri =>
return; {
// there are more performant overloads of this method, but this one is the most backwards-compatible
// (dates back to API 1).
var cursor = ContentResolver?.Query(uri, null, null, null, null);
cursor.MoveToFirst(); if (cursor == null)
return;
var filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName); cursor.MoveToFirst();
string filename = cursor.GetString(filenameColumn);
// SharpCompress requires archive streams to be seekable, which the stream opened by var filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName);
// OpenInputStream() seems to not necessarily be. string filename = cursor.GetString(filenameColumn);
// copy to an arbitrary-access memory stream to be able to proceed with the import.
var copy = new MemoryStream();
using (var stream = ContentResolver.OpenInputStream(uri))
await stream.CopyToAsync(copy);
await game.Import(copy, filename); // SharpCompress requires archive streams to be seekable, which the stream opened by
// OpenInputStream() seems to not necessarily be.
// copy to an arbitrary-access memory stream to be able to proceed with the import.
var copy = new MemoryStream();
using (var stream = ContentResolver.OpenInputStream(uri))
await stream.CopyToAsync(copy);
lock (tasks)
{
tasks.Add(new ImportTask(copy, filename));
}
}));
await game.Import(tasks.ToArray());
}, TaskCreationOptions.LongRunning); }, TaskCreationOptions.LongRunning);
} }
} }

View File

@ -9,6 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Configuration;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Users; using osu.Game.Users;
@ -31,13 +32,15 @@ namespace osu.Desktop
private readonly IBindable<UserStatus> status = new Bindable<UserStatus>(); private readonly IBindable<UserStatus> status = new Bindable<UserStatus>();
private readonly IBindable<UserActivity> activity = new Bindable<UserActivity>(); private readonly IBindable<UserActivity> activity = new Bindable<UserActivity>();
private readonly Bindable<DiscordRichPresenceMode> privacyMode = new Bindable<DiscordRichPresenceMode>();
private readonly RichPresence presence = new RichPresence private readonly RichPresence presence = new RichPresence
{ {
Assets = new Assets { LargeImageKey = "osu_logo_lazer", } Assets = new Assets { LargeImageKey = "osu_logo_lazer", }
}; };
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(IAPIProvider provider) private void load(IAPIProvider provider, OsuConfigManager config)
{ {
client = new DiscordRpcClient(client_id) client = new DiscordRpcClient(client_id)
{ {
@ -51,6 +54,8 @@ namespace osu.Desktop
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network); client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network);
config.BindWith(OsuSetting.DiscordRichPresence, privacyMode);
(user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u => (user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u =>
{ {
status.UnbindBindings(); status.UnbindBindings();
@ -63,6 +68,7 @@ namespace osu.Desktop
ruleset.BindValueChanged(_ => updateStatus()); ruleset.BindValueChanged(_ => updateStatus());
status.BindValueChanged(_ => updateStatus()); status.BindValueChanged(_ => updateStatus());
activity.BindValueChanged(_ => updateStatus()); activity.BindValueChanged(_ => updateStatus());
privacyMode.BindValueChanged(_ => updateStatus());
client.Initialize(); client.Initialize();
} }
@ -78,7 +84,7 @@ namespace osu.Desktop
if (!client.IsInitialized) if (!client.IsInitialized)
return; return;
if (status.Value is UserStatusOffline) if (status.Value is UserStatusOffline || privacyMode.Value == DiscordRichPresenceMode.Off)
{ {
client.ClearPresence(); client.ClearPresence();
return; return;
@ -96,7 +102,10 @@ namespace osu.Desktop
} }
// update user information // update user information
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); if (privacyMode.Value == DiscordRichPresenceMode.Limited)
presence.Assets.LargeImageText = string.Empty;
else
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty);
// update ruleset // update ruleset
presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom"; presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom";
@ -137,7 +146,7 @@ namespace osu.Desktop
return edit.Beatmap.ToString(); return edit.Beatmap.ToString();
case UserActivity.InLobby lobby: case UserActivity.InLobby lobby:
return lobby.Room.Name.Value; return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;
} }
return string.Empty; return string.Empty;

View File

@ -5,6 +5,7 @@ using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Versioning;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Win32; using Microsoft.Win32;
using osu.Desktop.Overlays; using osu.Desktop.Overlays;
@ -56,16 +57,16 @@ namespace osu.Desktop
string stableInstallPath; string stableInstallPath;
try if (OperatingSystem.IsWindows())
{ {
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) try
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", ""); {
stableInstallPath = getStableInstallPathFromRegistry();
if (checkExists(stableInstallPath)) if (!string.IsNullOrEmpty(stableInstallPath) && checkExists(stableInstallPath))
return stableInstallPath; return stableInstallPath;
} }
catch catch { }
{
} }
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
@ -79,6 +80,13 @@ namespace osu.Desktop
return null; return null;
} }
[SupportedOSPlatform("windows")]
private string getStableInstallPathFromRegistry()
{
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
return key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", "");
}
protected override UpdateManager CreateUpdateManager() protected override UpdateManager CreateUpdateManager()
{ {
switch (RuntimeInfo.OS) switch (RuntimeInfo.OS)

View File

@ -26,9 +26,11 @@ namespace osu.Desktop.Overlays
Alpha = 0; Alpha = 0;
FillFlowContainer mainFill;
Children = new Drawable[] Children = new Drawable[]
{ {
new FillFlowContainer mainFill = new FillFlowContainer
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
@ -55,23 +57,30 @@ namespace osu.Desktop.Overlays
}, },
} }
}, },
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = OsuFont.Numeric.With(size: 12),
Colour = colours.Yellow,
Text = @"Development Build"
},
new Sprite
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Texture = textures.Get(@"Menu/dev-build-footer"),
},
} }
} }
}; };
if (!game.IsDeployedBuild)
{
mainFill.AddRange(new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = OsuFont.Numeric.With(size: 12),
Colour = colours.Yellow,
Text = @"Development Build"
},
new Sprite
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Texture = textures.Get(@"Menu/dev-build-footer"),
},
});
}
} }
protected override void PopIn() protected override void PopIn()

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description> <Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>

View File

@ -11,7 +11,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>A free-to-win rhythm game. Rhythm is just a *click* away!</description> <description>A free-to-win rhythm game. Rhythm is just a *click* away!</description>
<releaseNotes>testing</releaseNotes> <releaseNotes>testing</releaseNotes>
<copyright>Copyright (c) 2020 ppy Pty Ltd</copyright> <copyright>Copyright (c) 2021 ppy Pty Ltd</copyright>
<language>en-AU</language> <language>en-AU</language>
</metadata> </metadata>
<files> <files>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View File

@ -7,7 +7,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Catch.Tests.dll" "${workspaceRoot}/bin/Debug/net5.0/osu.Game.Rulesets.Catch.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)", "preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/bin/Release/netcoreapp3.1/osu.Game.Rulesets.Catch.Tests.dll" "${workspaceRoot}/bin/Release/net5.0/osu.Game.Rulesets.Catch.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)", "preLaunchTask": "Build (Release)",

View File

@ -0,0 +1,151 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Skinning;
using osu.Game.Skinning;
using osu.Game.Tests.Beatmaps;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Tests
{
public class TestSceneLegacyBeatmapSkin : LegacyBeatmapSkinColourTest
{
[Resolved]
private AudioManager audio { get; set; }
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
config.BindWith(OsuSetting.BeatmapSkins, BeatmapSkins);
config.BindWith(OsuSetting.BeatmapColours, BeatmapColours);
}
[TestCase(true, true)]
[TestCase(true, false)]
[TestCase(false, true)]
[TestCase(false, false)]
public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin);
AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
}
[TestCase(true)]
[TestCase(false)]
public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColoursOverride(useBeatmapSkin);
AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
}
[TestCase(true)]
[TestCase(false)]
public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin);
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
}
[TestCase(true, true)]
[TestCase(false, true)]
[TestCase(true, false)]
[TestCase(false, false)]
public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false);
base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour);
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
}
[TestCase(true, true)]
[TestCase(false, true)]
[TestCase(true, false)]
[TestCase(false, false)]
public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false);
base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour);
AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
}
[TestCase(true)]
[TestCase(false)]
public void TestBeatmapHyperDashColours(bool useBeatmapSkin)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
ConfigureTest(useBeatmapSkin, true, true);
AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestBeatmapSkin.HYPER_DASH_COLOUR);
AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestBeatmapSkin.HYPER_DASH_AFTER_IMAGE_COLOUR);
AddAssert("is custom hyper dash fruit colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashFruitColour == TestBeatmapSkin.HYPER_DASH_FRUIT_COLOUR);
}
[TestCase(true)]
[TestCase(false)]
public void TestBeatmapHyperDashColoursOverride(bool useBeatmapSkin)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
ConfigureTest(useBeatmapSkin, false, true);
AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestSkin.HYPER_DASH_COLOUR);
AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestSkin.HYPER_DASH_AFTER_IMAGE_COLOUR);
AddAssert("is custom hyper dash fruit colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashFruitColour == TestSkin.HYPER_DASH_FRUIT_COLOUR);
}
protected override ExposedPlayer CreateTestPlayer(bool userHasCustomColours) => new CatchExposedPlayer(userHasCustomColours);
private class CatchExposedPlayer : ExposedPlayer
{
public CatchExposedPlayer(bool userHasCustomColours)
: base(userHasCustomColours)
{
}
public Color4 UsableHyperDashColour =>
GameplayClockContainer.ChildrenOfType<BeatmapSkinProvidingContainer>()
.First()
.GetConfig<SkinCustomColourLookup, Color4>(new SkinCustomColourLookup(CatchSkinColour.HyperDash))?
.Value ?? Color4.Red;
public Color4 UsableHyperDashAfterImageColour =>
GameplayClockContainer.ChildrenOfType<BeatmapSkinProvidingContainer>()
.First()
.GetConfig<SkinCustomColourLookup, Color4>(new SkinCustomColourLookup(CatchSkinColour.HyperDashAfterImage))?
.Value ?? Color4.Red;
public Color4 UsableHyperDashFruitColour =>
GameplayClockContainer.ChildrenOfType<BeatmapSkinProvidingContainer>()
.First()
.GetConfig<SkinCustomColourLookup, Color4>(new SkinCustomColourLookup(CatchSkinColour.HyperDashFruit))?
.Value ?? Color4.Red;
}
private class CatchCustomSkinWorkingBeatmap : CustomSkinWorkingBeatmap
{
public CatchCustomSkinWorkingBeatmap(AudioManager audio, bool hasColours)
: base(createBeatmap(), audio, hasColours)
{
}
private static IBeatmap createBeatmap() =>
new Beatmap
{
BeatmapInfo =
{
BeatmapSet = new BeatmapSetInfo(),
Ruleset = new CatchRuleset().RulesetInfo
},
HitObjects = { new Fruit { StartTime = 1816, X = 56, NewCombo = true } }
};
}
}
}

View File

@ -9,7 +9,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />

View File

@ -59,8 +59,8 @@ namespace osu.Game.Rulesets.Catch.Mods
{ {
base.ApplySettings(difficulty); base.ApplySettings(difficulty);
difficulty.CircleSize = CircleSize.Value; ApplySetting(CircleSize, cs => difficulty.CircleSize = cs);
difficulty.ApproachRate = ApproachRate.Value; ApplySetting(ApproachRate, ar => difficulty.ApproachRate = ar);
} }
} }
} }

View File

@ -5,6 +5,13 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>catch the fruit. to the beat.</Description> <Description>catch the fruit. to the beat.</Description>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Nuget">
<Title>osu!catch (ruleset)</Title>
<PackageId>ppy.osu.Game.Rulesets.Catch</PackageId>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -7,7 +7,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Mania.Tests.dll" "${workspaceRoot}/bin/Debug/net5.0/osu.Game.Rulesets.Mania.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)", "preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/bin/Release/netcoreapp3.1/osu.Game.Rulesets.Mania.Tests.dll" "${workspaceRoot}/bin/Release/net5.0/osu.Game.Rulesets.Mania.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)", "preLaunchTask": "Build (Release)",

View File

@ -9,7 +9,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />

View File

@ -5,6 +5,13 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>smash the keys. to the beat.</Description> <Description>smash the keys. to the beat.</Description>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Nuget">
<Title>osu!mania (ruleset)</Title>
<PackageId>ppy.osu.Game.Rulesets.Mania</PackageId>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -7,7 +7,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Osu.Tests.dll" "${workspaceRoot}/bin/Debug/net5.0/osu.Game.Rulesets.Osu.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)", "preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/bin/Release/netcoreapp3.1/osu.Game.Rulesets.Osu.Tests.dll" "${workspaceRoot}/bin/Release/net5.0/osu.Game.Rulesets.Osu.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)", "preLaunchTask": "Build (Release)",

View File

@ -1,13 +1,17 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Tests.Mods namespace osu.Game.Rulesets.Osu.Tests.Mods
@ -18,8 +22,23 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
public void TestNoAdjustment() => CreateModTest(new ModTestData public void TestNoAdjustment() => CreateModTest(new ModTestData
{ {
Mod = new OsuModDifficultyAdjust(), Mod = new OsuModDifficultyAdjust(),
Beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 8
}
},
HitObjects = new List<HitObject>
{
new HitCircle { StartTime = 1000 },
new HitCircle { StartTime = 2000 }
}
},
Autoplay = true, Autoplay = true,
PassCondition = checkSomeHit PassCondition = () => checkSomeHit() && checkObjectsScale(0.29f)
}); });
[Test] [Test]

View File

@ -1,107 +1,91 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.IO.Stores;
using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Play;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Tests.Visual; using osu.Game.Tests.Beatmaps;
using osuTK; using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class TestSceneLegacyBeatmapSkin : ScreenTestScene public class TestSceneLegacyBeatmapSkin : LegacyBeatmapSkinColourTest
{ {
[Resolved] [Resolved]
private AudioManager audio { get; set; } private AudioManager audio { get; set; }
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
config.BindWith(OsuSetting.BeatmapSkins, BeatmapSkins);
config.BindWith(OsuSetting.BeatmapColours, BeatmapColours);
}
[TestCase(true, true)]
[TestCase(true, false)]
[TestCase(false, true)]
[TestCase(false, false)]
public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin)
{
TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin);
AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
}
[TestCase(true)] [TestCase(true)]
[TestCase(false)] [TestCase(false)]
public void TestBeatmapComboColours(bool customSkinColoursPresent) public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin)
{ {
ExposedPlayer player = null; TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColoursOverride(useBeatmapSkin);
AddStep("load coloured beatmap", () => player = loadBeatmap(customSkinColoursPresent, true)); AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
AddUntilStep("wait for player", () => player.IsLoaded);
AddAssert("is beatmap skin colours", () => player.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
} }
[Test] [TestCase(true)]
public void TestBeatmapNoComboColours() [TestCase(false)]
public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin)
{ {
ExposedPlayer player = null; TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin);
AddStep("load no-colour beatmap", () => player = loadBeatmap(false, false)); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
AddUntilStep("wait for player", () => player.IsLoaded);
AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
} }
[Test] [TestCase(true, true)]
public void TestBeatmapNoComboColoursSkinOverride() [TestCase(false, true)]
[TestCase(true, false)]
[TestCase(false, false)]
public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour)
{ {
ExposedPlayer player = null; TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false);
base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour);
AddStep("load custom-skin colour", () => player = loadBeatmap(true, false)); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
AddUntilStep("wait for player", () => player.IsLoaded);
AddAssert("is custom user skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours));
} }
private ExposedPlayer loadBeatmap(bool userHasCustomColours, bool beatmapHasColours) [TestCase(true, true)]
[TestCase(false, true)]
[TestCase(true, false)]
[TestCase(false, false)]
public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour)
{ {
ExposedPlayer player; TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false);
base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour);
Beatmap.Value = new CustomSkinWorkingBeatmap(audio, beatmapHasColours); AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
LoadScreen(player = new ExposedPlayer(userHasCustomColours));
return player;
} }
private class ExposedPlayer : Player private class OsuCustomSkinWorkingBeatmap : CustomSkinWorkingBeatmap
{ {
private readonly bool userHasCustomColours; public OsuCustomSkinWorkingBeatmap(AudioManager audio, bool hasColours)
: base(createBeatmap(), audio, hasColours)
public ExposedPlayer(bool userHasCustomColours)
: base(new PlayerConfiguration
{
AllowPause = false,
ShowResults = false,
})
{ {
this.userHasCustomColours = userHasCustomColours;
} }
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) private static IBeatmap createBeatmap() =>
{ new Beatmap
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<ISkinSource>(new TestSkin(userHasCustomColours));
return dependencies;
}
public IReadOnlyList<Color4> UsableComboColours =>
GameplayClockContainer.ChildrenOfType<BeatmapSkinProvidingContainer>()
.First()
.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value;
}
private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
{
private readonly bool hasColours;
public CustomSkinWorkingBeatmap(AudioManager audio, bool hasColours)
: base(new Beatmap
{ {
BeatmapInfo = BeatmapInfo =
{ {
@ -109,50 +93,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Ruleset = new OsuRuleset().RulesetInfo, Ruleset = new OsuRuleset().RulesetInfo,
}, },
HitObjects = { new HitCircle { Position = new Vector2(256, 192) } } HitObjects = { new HitCircle { Position = new Vector2(256, 192) } }
}, null, null, audio) };
{
this.hasColours = hasColours;
}
protected override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, hasColours);
}
private class TestBeatmapSkin : LegacyBeatmapSkin
{
public static Color4[] Colours { get; } =
{
new Color4(50, 100, 150, 255),
new Color4(40, 80, 120, 255),
};
public TestBeatmapSkin(BeatmapInfo beatmap, bool hasColours)
: base(beatmap, new ResourceStore<byte[]>(), null)
{
if (hasColours)
Configuration.AddComboColours(Colours);
}
}
private class TestSkin : LegacySkin, ISkinSource
{
public static Color4[] Colours { get; } =
{
new Color4(150, 100, 50, 255),
new Color4(20, 20, 20, 255),
};
public TestSkin(bool hasCustomColours)
: base(new SkinInfo(), null, null, string.Empty)
{
if (hasCustomColours)
Configuration.AddComboColours(Colours);
}
public event Action SourceChanged
{
add { }
remove { }
}
} }
} }
} }

View File

@ -52,6 +52,31 @@ namespace osu.Game.Rulesets.Osu.Tests
checkNextHitObject(null); checkNextHitObject(null);
} }
[Test]
public void TestBeatmapColourDefault()
{
AddStep("enable user provider", () => testUserSkin.Enabled = true);
AddStep("enable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, true));
AddStep("enable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, true));
checkNextHitObject("beatmap");
AddStep("enable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, true));
AddStep("disable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, false));
checkNextHitObject("beatmap");
AddStep("disable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, false));
AddStep("enable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, true));
checkNextHitObject("user");
AddStep("disable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, false));
AddStep("disable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, false));
checkNextHitObject("user");
AddStep("disable user provider", () => testUserSkin.Enabled = false);
checkNextHitObject(null);
}
private void checkNextHitObject(string skin) => private void checkNextHitObject(string skin) =>
AddUntilStep($"check skin from {skin}", () => AddUntilStep($"check skin from {skin}", () =>
{ {

View File

@ -1,9 +1,11 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
@ -34,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Tests
[TestCase(true)] [TestCase(true)]
public void TestLongSpinner(bool autoplay) public void TestLongSpinner(bool autoplay)
{ {
AddStep("Very short spinner", () => SetContents(() => testSingle(5, autoplay, 2000))); AddStep("Very long spinner", () => SetContents(() => testSingle(5, autoplay, 4000)));
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult); AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0)); AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0));
} }
@ -55,7 +57,11 @@ namespace osu.Game.Rulesets.Osu.Tests
var spinner = new Spinner var spinner = new Spinner
{ {
StartTime = Time.Current + delay, StartTime = Time.Current + delay,
EndTime = Time.Current + delay + length EndTime = Time.Current + delay + length,
Samples = new List<HitSampleInfo>
{
new HitSampleInfo("hitnormal")
}
}; };
spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize });

View File

@ -9,7 +9,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />

View File

@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (Attributes.ApproachRate > 10.33) if (Attributes.ApproachRate > 10.33)
approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33); approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33);
else if (Attributes.ApproachRate < 8.0) else if (Attributes.ApproachRate < 8.0)
approachRateFactor += 0.1 * (8.0 - Attributes.ApproachRate); approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate);
aimValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); aimValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0));

View File

@ -30,6 +30,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.HitArea.ReceivePositionalInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.HitArea.ReceivePositionalInputAt(screenSpacePos);
public override Quad SelectionQuad => DrawableObject.HitArea.ScreenSpaceDrawQuad; public override Quad SelectionQuad => CirclePiece.ScreenSpaceDrawQuad;
} }
} }

View File

@ -44,6 +44,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private IEditorChangeHandler changeHandler { get; set; } private IEditorChangeHandler changeHandler { get; set; }
public override Quad SelectionQuad => BodyPiece.ScreenSpaceDrawQuad;
private readonly BindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>(); private readonly BindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>();
private readonly IBindable<int> pathVersion = new Bindable<int>(); private readonly IBindable<int> pathVersion = new Bindable<int>();

View File

@ -236,7 +236,20 @@ namespace osu.Game.Rulesets.Osu.Edit
/// </summary> /// </summary>
/// <param name="hitObjects">The hit objects to calculate a quad for.</param> /// <param name="hitObjects">The hit objects to calculate a quad for.</param>
private Quad getSurroundingQuad(OsuHitObject[] hitObjects) => private Quad getSurroundingQuad(OsuHitObject[] hitObjects) =>
getSurroundingQuad(hitObjects.SelectMany(h => new[] { h.Position, h.EndPosition })); getSurroundingQuad(hitObjects.SelectMany(h =>
{
if (h is IHasPath path)
{
return new[]
{
h.Position,
// can't use EndPosition for reverse slider cases.
h.Position + path.Path.PositionAt(1)
};
}
return new[] { h.Position };
}));
/// <summary> /// <summary>
/// Returns a gamefield-space quad surrounding the provided points. /// Returns a gamefield-space quad surrounding the provided points.

View File

@ -59,8 +59,8 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
base.ApplySettings(difficulty); base.ApplySettings(difficulty);
difficulty.CircleSize = CircleSize.Value; ApplySetting(CircleSize, cs => difficulty.CircleSize = cs);
difficulty.ApproachRate = ApproachRate.Value; ApplySetting(ApproachRate, ar => difficulty.ApproachRate = ar);
} }
} }
} }

View File

@ -130,12 +130,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
if (tracking.NewValue) if (tracking.NewValue)
{ {
spinningSample?.Play(); spinningSample?.Play(!spinningSample.IsPlaying);
spinningSample?.VolumeTo(1, 200); spinningSample?.VolumeTo(1, 300);
} }
else else
{ {
spinningSample?.VolumeTo(0, 200).Finally(_ => spinningSample.Stop()); spinningSample?.VolumeTo(0, 300).OnComplete(_ => spinningSample.Stop());
} }
} }

View File

@ -5,6 +5,13 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>click the circles. to the beat.</Description> <Description>click the circles. to the beat.</Description>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Nuget">
<Title>osu! (ruleset)</Title>
<PackageId>ppy.osu.Game.Rulesets.Osu</PackageId>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -7,7 +7,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Taiko.Tests.dll" "${workspaceRoot}/bin/Debug/net5.0/osu.Game.Rulesets.Taiko.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)", "preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/bin/Release/netcoreapp3.1/osu.Game.Rulesets.Taiko.Tests.dll" "${workspaceRoot}/bin/Release/net5.0/osu.Game.Rulesets.Taiko.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)", "preLaunchTask": "Build (Release)",

View File

@ -23,6 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
[TestCase("sample-to-type-conversions")] [TestCase("sample-to-type-conversions")]
[TestCase("slider-conversion-v6")] [TestCase("slider-conversion-v6")]
[TestCase("slider-conversion-v14")] [TestCase("slider-conversion-v14")]
[TestCase("slider-generating-drumroll-2")]
public void Test(string name) => base.Test(name); public void Test(string name) => base.Test(name);
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject) protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)

View File

@ -9,7 +9,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />

View File

@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
} }
} }
private bool shouldConvertSliderToHits(HitObject obj, IBeatmap beatmap, IHasDistance distanceData, out double taikoDuration, out double tickSpacing) private bool shouldConvertSliderToHits(HitObject obj, IBeatmap beatmap, IHasDistance distanceData, out int taikoDuration, out double tickSpacing)
{ {
// DO NOT CHANGE OR REFACTOR ANYTHING IN HERE WITHOUT TESTING AGAINST _ALL_ BEATMAPS. // DO NOT CHANGE OR REFACTOR ANYTHING IN HERE WITHOUT TESTING AGAINST _ALL_ BEATMAPS.
// Some of these calculations look redundant, but they are not - extremely small floating point errors are introduced to maintain 1:1 compatibility with stable. // Some of these calculations look redundant, but they are not - extremely small floating point errors are introduced to maintain 1:1 compatibility with stable.
@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
// The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll. // The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll.
double taikoVelocity = sliderScoringPointDistance * beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate; double taikoVelocity = sliderScoringPointDistance * beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate;
taikoDuration = distance / taikoVelocity * beatLength; taikoDuration = (int)(distance / taikoVelocity * beatLength);
if (isForCurrentRuleset) if (isForCurrentRuleset)
{ {
@ -200,7 +200,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
beatLength = timingPoint.BeatLength; beatLength = timingPoint.BeatLength;
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
tickSpacing = Math.Min(beatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans); tickSpacing = Math.Min(beatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, (double)taikoDuration / spans);
return tickSpacing > 0 return tickSpacing > 0
&& distance / osuVelocity * 1000 < 2 * beatLength; && distance / osuVelocity * 1000 < 2 * beatLength;

View File

@ -1,7 +1,9 @@
{ {
"Mappings": [{ "Mappings": [
{
"StartTime": 2000, "StartTime": 2000,
"Objects": [{ "Objects": [
{
"StartTime": 2000, "StartTime": 2000,
"EndTime": 2000, "EndTime": 2000,
"IsRim": false, "IsRim": false,
@ -23,7 +25,8 @@
}, },
{ {
"StartTime": 4000, "StartTime": 4000,
"Objects": [{ "Objects": [
{
"StartTime": 4000, "StartTime": 4000,
"EndTime": 4000, "EndTime": 4000,
"IsRim": false, "IsRim": false,
@ -45,7 +48,8 @@
}, },
{ {
"StartTime": 6000, "StartTime": 6000,
"Objects": [{ "Objects": [
{
"StartTime": 6000, "StartTime": 6000,
"EndTime": 6000, "EndTime": 6000,
"IsRim": true, "IsRim": true,
@ -76,300 +80,13 @@
}, },
{ {
"StartTime": 8000, "StartTime": 8000,
"Objects": [{ "Objects": [
{
"StartTime": 8000, "StartTime": 8000,
"EndTime": 8000,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8026,
"EndTime": 8026,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8053,
"EndTime": 8053,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8080,
"EndTime": 8080,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8107,
"EndTime": 8107,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8133,
"EndTime": 8133,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8160,
"EndTime": 8160,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8187,
"EndTime": 8187,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8214,
"EndTime": 8214,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8241,
"EndTime": 8241,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8267,
"EndTime": 8267,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8294,
"EndTime": 8294,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8321,
"EndTime": 8321,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8348,
"EndTime": 8348,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8374,
"EndTime": 8374,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8401,
"EndTime": 8401,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8428,
"EndTime": 8428,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8455,
"EndTime": 8455,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8482,
"EndTime": 8482,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8508,
"EndTime": 8508,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8535,
"EndTime": 8535,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8562,
"EndTime": 8562,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8589,
"EndTime": 8589,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8615,
"EndTime": 8615,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8642,
"EndTime": 8642,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8669,
"EndTime": 8669,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8696,
"EndTime": 8696,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8723,
"EndTime": 8723,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8749,
"EndTime": 8749,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8776,
"EndTime": 8776,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8803,
"EndTime": 8803,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8830,
"EndTime": 8830,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8857,
"EndTime": 8857, "EndTime": 8857,
"IsRim": false, "IsRim": false,
"IsCentre": true, "IsCentre": false,
"IsDrumRoll": false, "IsDrumRoll": true,
"IsSwell": false, "IsSwell": false,
"IsStrong": false "IsStrong": false
} }

View File

@ -0,0 +1,18 @@
{
"Mappings": [
{
"StartTime": 51532,
"Objects": [
{
"StartTime": 51532,
"EndTime": 52301,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}
]
}
]
}

View File

@ -0,0 +1,19 @@
osu file format v14
[General]
Mode: 0
[Difficulty]
HPDrainRate:2
CircleSize:3.2
OverallDifficulty:2
ApproachRate:3
SliderMultiplier:0.999999999999999
SliderTickRate:1
[TimingPoints]
763,384.615384615385,4,2,0,70,1,0
49993,-90.9090909090909,4,2,0,75,0,1
[HitObjects]
51,245,51532,2,0,P|18:150|17:122,2,110.000003356934,0|8|0,0:0|0:0|0:0,0:0:0:0:

View File

@ -5,6 +5,13 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>bash the drum. to the beat.</Description> <Description>bash the drum. to the beat.</Description>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Nuget">
<Title>osu!taiko (ruleset)</Title>
<PackageId>ppy.osu.Game.Rulesets.Taiko</PackageId>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -95,6 +95,26 @@ namespace osu.Game.Tests.Beatmaps.Formats
} }
} }
[Test]
public void TestOutOfOrderStartTimes()
{
var decoder = new LegacyStoryboardDecoder();
using (var resStream = TestResources.OpenResource("out-of-order-starttimes.osb"))
using (var stream = new LineBufferedReader(resStream))
{
var storyboard = decoder.Decode(stream);
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
Assert.AreEqual(2, background.Elements.Count);
Assert.AreEqual(1500, background.Elements[0].StartTime);
Assert.AreEqual(1000, background.Elements[1].StartTime);
Assert.AreEqual(1000, storyboard.EarliestEventTime);
}
}
[Test] [Test]
public void TestDecodeVariableWithSuffix() public void TestDecodeVariableWithSuffix()
{ {

View File

@ -246,5 +246,32 @@ namespace osu.Game.Tests.NonVisual
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0)); Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0));
Assert.That(cpi.AllControlPoints.Count, Is.EqualTo(0)); Assert.That(cpi.AllControlPoints.Count, Is.EqualTo(0));
} }
[Test]
public void TestCreateCopyIsDeepClone()
{
var cpi = new ControlPointInfo();
cpi.Add(1000, new TimingControlPoint { BeatLength = 500 });
var cpiCopy = cpi.CreateCopy();
cpiCopy.Add(2000, new TimingControlPoint { BeatLength = 500 });
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
Assert.That(cpiCopy.Groups.Count, Is.EqualTo(2));
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(1));
Assert.That(cpiCopy.TimingPoints.Count, Is.EqualTo(2));
Assert.That(cpi.TimingPoints[0], Is.Not.SameAs(cpiCopy.TimingPoints[0]));
Assert.That(cpi.TimingPoints[0].BeatLengthBindable, Is.Not.SameAs(cpiCopy.TimingPoints[0].BeatLengthBindable));
Assert.That(cpi.TimingPoints[0].BeatLength, Is.EqualTo(cpiCopy.TimingPoints[0].BeatLength));
cpi.TimingPoints[0].BeatLength = 800;
Assert.That(cpi.TimingPoints[0].BeatLength, Is.Not.EqualTo(cpiCopy.TimingPoints[0].BeatLength));
}
} }
} }

View File

@ -0,0 +1,6 @@
[Events]
//Storyboard Layer 0 (Background)
Sprite,Background,TopCentre,"img.jpg",320,240
F,0,1500,1600,0,1
Sprite,Background,TopCentre,"img.jpg",320,240
F,0,1000,1100,0,1

View File

@ -9,7 +9,6 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Sample;
using osu.Framework.Configuration.Tracking; using osu.Framework.Configuration.Tracking;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;

View File

@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Background
}); });
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
AddStep("Stop background preview", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); AddStep("Stop background preview", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect()); AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.CheckBackgroundBlur(playerLoader.ExpectedBackgroundBlur));
} }
/// <summary> /// <summary>
@ -106,6 +106,7 @@ namespace osu.Game.Tests.Visual.Background
public void TestStoryboardBackgroundVisibility() public void TestStoryboardBackgroundVisibility()
{ {
performFullSetup(); performFullSetup();
AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
createFakeStoryboard(); createFakeStoryboard();
AddStep("Enable Storyboard", () => AddStep("Enable Storyboard", () =>
{ {
@ -198,8 +199,9 @@ namespace osu.Game.Tests.Visual.Background
}))); })));
AddUntilStep("Wait for results is current", () => results.IsCurrentScreen()); AddUntilStep("Wait for results is current", () => results.IsCurrentScreen());
AddUntilStep("Screen is undimmed, original background retained", () => AddUntilStep("Screen is undimmed, original background retained", () =>
songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect()); songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && songSelect.CheckBackgroundBlur(results.ExpectedBackgroundBlur));
} }
/// <summary> /// <summary>
@ -224,7 +226,7 @@ namespace osu.Game.Tests.Visual.Background
AddStep("Resume PlayerLoader", () => player.Restart()); AddStep("Resume PlayerLoader", () => player.Restart());
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect()); AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.CheckBackgroundBlur(playerLoader.ExpectedBackgroundBlur));
} }
private void createFakeStoryboard() => AddStep("Create storyboard", () => private void createFakeStoryboard() => AddStep("Create storyboard", () =>
@ -274,9 +276,11 @@ namespace osu.Game.Tests.Visual.Background
private class DummySongSelect : PlaySongSelect private class DummySongSelect : PlaySongSelect
{ {
private FadeAccessibleBackground background;
protected override BackgroundScreen CreateBackground() protected override BackgroundScreen CreateBackground()
{ {
FadeAccessibleBackground background = new FadeAccessibleBackground(Beatmap.Value); background = new FadeAccessibleBackground(Beatmap.Value);
DimEnabled.BindTo(background.EnableUserDim); DimEnabled.BindTo(background.EnableUserDim);
return background; return background;
} }
@ -294,25 +298,27 @@ namespace osu.Game.Tests.Visual.Background
config.BindWith(OsuSetting.BlurLevel, BlurLevel); config.BindWith(OsuSetting.BlurLevel, BlurLevel);
} }
public bool IsBackgroundDimmed() => ((FadeAccessibleBackground)Background).CurrentColour == OsuColour.Gray(1f - ((FadeAccessibleBackground)Background).CurrentDim); public bool IsBackgroundDimmed() => background.CurrentColour == OsuColour.Gray(1f - background.CurrentDim);
public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White; public bool IsBackgroundUndimmed() => background.CurrentColour == Color4.White;
public bool IsUserBlurApplied() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR); public bool IsUserBlurApplied() => background.CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR);
public bool IsUserBlurDisabled() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(0); public bool IsUserBlurDisabled() => background.CurrentBlur == new Vector2(0);
public bool IsBackgroundInvisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 0; public bool IsBackgroundInvisible() => background.CurrentAlpha == 0;
public bool IsBackgroundVisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 1; public bool IsBackgroundVisible() => background.CurrentAlpha == 1;
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); public bool IsBlurCorrect() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR);
public bool CheckBackgroundBlur(Vector2 expected) => background.CurrentBlur == expected;
/// <summary> /// <summary>
/// Make sure every time a screen gets pushed, the background doesn't get replaced /// Make sure every time a screen gets pushed, the background doesn't get replaced
/// </summary> /// </summary>
/// <returns>Whether or not the original background (The one created in DummySongSelect) is still the current background</returns> /// <returns>Whether or not the original background (The one created in DummySongSelect) is still the current background</returns>
public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen(); public bool IsBackgroundCurrent() => background?.IsCurrentScreen() == true;
} }
private class FadeAccessibleResults : ResultsScreen private class FadeAccessibleResults : ResultsScreen
@ -324,12 +330,20 @@ namespace osu.Game.Tests.Visual.Background
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); public Vector2 ExpectedBackgroundBlur => new Vector2(BACKGROUND_BLUR);
} }
private class LoadBlockingTestPlayer : TestPlayer private class LoadBlockingTestPlayer : TestPlayer
{ {
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); protected override BackgroundScreen CreateBackground() =>
new FadeAccessibleBackground(Beatmap.Value);
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
ApplyToBackground(b => ReplacesBackground.BindTo(b.StoryboardReplacesBackground));
}
public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard; public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard;
@ -354,15 +368,16 @@ namespace osu.Game.Tests.Visual.Background
Thread.Sleep(1); Thread.Sleep(1);
StoryboardEnabled = config.GetBindable<bool>(OsuSetting.ShowStoryboard); StoryboardEnabled = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
ReplacesBackground.BindTo(Background.StoryboardReplacesBackground);
DrawableRuleset.IsPaused.BindTo(IsPaused); DrawableRuleset.IsPaused.BindTo(IsPaused);
} }
} }
private class TestPlayerLoader : PlayerLoader private class TestPlayerLoader : PlayerLoader
{ {
private FadeAccessibleBackground background;
public VisualSettings VisualSettingsPos => VisualSettings; public VisualSettings VisualSettingsPos => VisualSettings;
public BackgroundScreen ScreenPos => Background; public BackgroundScreen ScreenPos => background;
public TestPlayerLoader(Player player) public TestPlayerLoader(Player player)
: base(() => player) : base(() => player)
@ -371,9 +386,9 @@ namespace osu.Game.Tests.Visual.Background
public void TriggerOnHover() => OnHover(new HoverEvent(new InputState())); public void TriggerOnHover() => OnHover(new HoverEvent(new InputState()));
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); public Vector2 ExpectedBackgroundBlur => new Vector2(BACKGROUND_BLUR);
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); protected override BackgroundScreen CreateBackground() => background = new FadeAccessibleBackground(Beatmap.Value);
} }
private class FadeAccessibleBackground : BackgroundScreenBeatmap private class FadeAccessibleBackground : BackgroundScreenBeatmap

View File

@ -17,10 +17,12 @@ using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -129,6 +131,31 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("no DHOs shown", () => !this.ChildrenOfType<DrawableTestHitObject>().Any()); AddUntilStep("no DHOs shown", () => !this.ChildrenOfType<DrawableTestHitObject>().Any());
} }
[Test]
public void TestApplyHitResultOnKilled()
{
ManualClock clock = null;
bool anyJudged = false;
void onNewResult(JudgementResult _) => anyJudged = true;
var beatmap = new Beatmap();
beatmap.HitObjects.Add(new TestKilledHitObject { Duration = 20 });
createTest(beatmap, 10, () => new FramedClock(clock = new ManualClock()));
AddStep("subscribe to new result", () =>
{
anyJudged = false;
drawableRuleset.NewResult += onNewResult;
});
AddStep("skip past object", () => clock.CurrentTime = beatmap.HitObjects[0].GetEndTime() + 1000);
AddAssert("object judged", () => anyJudged);
AddStep("clean up", () => drawableRuleset.NewResult -= onNewResult);
}
private void createTest(IBeatmap beatmap, int poolSize, Func<IFrameBasedClock> createClock = null) => AddStep("create test", () => private void createTest(IBeatmap beatmap, int poolSize, Func<IFrameBasedClock> createClock = null) => AddStep("create test", () =>
{ {
var ruleset = new TestPoolingRuleset(); var ruleset = new TestPoolingRuleset();
@ -192,6 +219,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private void load() private void load()
{ {
RegisterPool<TestHitObject, DrawableTestHitObject>(poolSize); RegisterPool<TestHitObject, DrawableTestHitObject>(poolSize);
RegisterPool<TestKilledHitObject, DrawableTestKilledHitObject>(poolSize);
} }
protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new TestHitObjectLifetimeEntry(hitObject); protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new TestHitObjectLifetimeEntry(hitObject);
@ -220,19 +248,30 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override IEnumerable<TestHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken) protected override IEnumerable<TestHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken)
{ {
yield return new TestHitObject switch (original)
{ {
StartTime = original.StartTime, case TestKilledHitObject h:
Duration = 250 yield return h;
};
break;
default:
yield return new TestHitObject
{
StartTime = original.StartTime,
Duration = 250
};
break;
}
} }
} }
#endregion #endregion
#region HitObject #region HitObjects
private class TestHitObject : ConvertHitObject private class TestHitObject : ConvertHitObject, IHasDuration
{ {
public double EndTime => StartTime + Duration; public double EndTime => StartTime + Duration;
@ -287,6 +326,30 @@ namespace osu.Game.Tests.Visual.Gameplay
} }
} }
private class TestKilledHitObject : TestHitObject
{
}
private class DrawableTestKilledHitObject : DrawableHitObject<TestKilledHitObject>
{
public DrawableTestKilledHitObject()
: base(null)
{
}
protected override void UpdateHitStateTransforms(ArmedState state)
{
base.UpdateHitStateTransforms(state);
Expire();
}
public override void OnKilled()
{
base.OnKilled();
ApplyResult(r => r.Type = r.Judgement.MinResult);
}
}
#endregion #endregion
} }
} }

View File

@ -244,7 +244,7 @@ namespace osu.Game.Tests.Visual.Gameplay
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction> internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
{ {
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[] public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[]
{ {
new KeyBinding(InputKey.MouseLeft, TestAction.Down), new KeyBinding(InputKey.MouseLeft, TestAction.Down),
}; };

View File

@ -179,7 +179,7 @@ namespace osu.Game.Tests.Visual.Gameplay
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction> internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
{ {
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[] public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[]
{ {
new KeyBinding(InputKey.MouseLeft, TestAction.Down), new KeyBinding(InputKey.MouseLeft, TestAction.Down),
}; };

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -74,6 +75,8 @@ namespace osu.Game.Tests.Visual.Gameplay
switch (args.Action) switch (args.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
foreach (int user in args.NewItems) foreach (int user in args.NewItems)
{ {
if (user == api.LocalUser.Value.Id) if (user == api.LocalUser.Value.Id)
@ -83,6 +86,8 @@ namespace osu.Game.Tests.Visual.Gameplay
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
foreach (int user in args.OldItems) foreach (int user in args.OldItems)
{ {
if (user == api.LocalUser.Value.Id) if (user == api.LocalUser.Value.Id)
@ -298,7 +303,7 @@ namespace osu.Game.Tests.Visual.Gameplay
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction> internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
{ {
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[] public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[]
{ {
new KeyBinding(InputKey.MouseLeft, TestAction.Down), new KeyBinding(InputKey.MouseLeft, TestAction.Down),
}; };

View File

@ -0,0 +1,50 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Screens.OnlinePlay.Multiplayer;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneCreateMultiplayerMatchButton : MultiplayerTestScene
{
private CreateMultiplayerMatchButton button;
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("create button", () => Child = button = new CreateMultiplayerMatchButton
{
Width = 200,
Height = 100,
Anchor = Anchor.Centre,
Origin = Anchor.Centre
});
}
[Test]
public void TestButtonEnableStateChanges()
{
IDisposable joiningRoomOperation = null;
assertButtonEnableState(true);
AddStep("begin joining room", () => joiningRoomOperation = OngoingOperationTracker.BeginOperation());
assertButtonEnableState(false);
AddStep("end joining room", () => joiningRoomOperation.Dispose());
assertButtonEnableState(true);
AddStep("disconnect client", () => Client.Disconnect());
assertButtonEnableState(false);
AddStep("re-connect client", () => Client.Connect());
assertButtonEnableState(true);
}
private void assertButtonEnableState(bool enabled)
=> AddAssert($"button {(enabled ? "enabled" : "disabled")}", () => button.Enabled.Value == enabled);
}
}

View File

@ -201,11 +201,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
} }
[Test] [Test]
public void TestDownloadButtonHiddenInitiallyWhenBeatmapExists() public void TestDownloadButtonHiddenWhenBeatmapExists()
{ {
createPlaylist(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo); createPlaylist(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo);
AddAssert("download button hidden", () => !playlist.ChildrenOfType<BeatmapDownloadTrackingComposite>().Single().IsPresent); assertDownloadButtonVisible(false);
AddStep("delete beatmap set", () => manager.Delete(manager.QueryBeatmapSets(_ => true).Single()));
assertDownloadButtonVisible(true);
AddStep("undelete beatmap set", () => manager.Undelete(manager.QueryBeatmapSets(_ => true).Single()));
assertDownloadButtonVisible(false);
void assertDownloadButtonVisible(bool visible) => AddUntilStep($"download button {(visible ? "shown" : "hidden")}",
() => playlist.ChildrenOfType<BeatmapDownloadTrackingComposite>().Single().Alpha == (visible ? 1 : 0));
} }
[Test] [Test]
@ -222,6 +231,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("download buttons shown", () => playlist.ChildrenOfType<BeatmapDownloadTrackingComposite>().All(d => d.IsPresent)); AddAssert("download buttons shown", () => playlist.ChildrenOfType<BeatmapDownloadTrackingComposite>().All(d => d.IsPresent));
} }
[Test]
public void TestExplicitBeatmapItem()
{
var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo;
beatmap.BeatmapSet.OnlineInfo.HasExplicitContent = true;
createPlaylist(beatmap);
}
private void moveToItem(int index, Vector2? offset = null) private void moveToItem(int index, Vector2? offset = null)
=> AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType<OsuRearrangeableListItem<PlaylistItem>>().ElementAt(index), offset)); => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType<OsuRearrangeableListItem<PlaylistItem>>().ElementAt(index), offset));

View File

@ -3,10 +3,12 @@
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.OnlinePlay;
using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
@ -18,6 +20,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
private MultiplayerMatchSubScreen screen; private MultiplayerMatchSubScreen screen;
[Cached]
private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker();
public TestSceneMultiplayerMatchSubScreen() public TestSceneMultiplayerMatchSubScreen()
: base(false) : base(false)
{ {

View File

@ -1,14 +1,17 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -23,10 +26,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
public class TestSceneMultiplayerReadyButton : MultiplayerTestScene public class TestSceneMultiplayerReadyButton : MultiplayerTestScene
{ {
private MultiplayerReadyButton button; private MultiplayerReadyButton button;
private BeatmapSetInfo importedSet;
private BeatmapManager beatmaps; private BeatmapManager beatmaps;
private RulesetStore rulesets; private RulesetStore rulesets;
private IDisposable readyClickOperation;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio) private void load(GameHost host, AudioManager audio)
{ {
@ -38,9 +44,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
[SetUp] [SetUp]
public new void Setup() => Schedule(() => public new void Setup() => Schedule(() =>
{ {
var beatmap = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First().Beatmaps.First(); importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First();
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First());
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap);
Child = button = new MultiplayerReadyButton Child = button = new MultiplayerReadyButton
{ {
@ -51,13 +56,43 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
Value = new PlaylistItem Value = new PlaylistItem
{ {
Beatmap = { Value = beatmap }, Beatmap = { Value = Beatmap.Value.BeatmapInfo },
Ruleset = { Value = beatmap.Ruleset } Ruleset = { Value = Beatmap.Value.BeatmapInfo.Ruleset }
} }
},
OnReadyClick = async () =>
{
readyClickOperation = OngoingOperationTracker.BeginOperation();
if (Client.IsHost && Client.LocalUser?.State == MultiplayerUserState.Ready)
{
await Client.StartMatch();
return;
}
await Client.ToggleReady();
readyClickOperation.Dispose();
} }
}; };
}); });
[Test]
public void TestDeletedBeatmapDisableReady()
{
OsuButton readyButton = null;
AddAssert("ensure ready button enabled", () =>
{
readyButton = button.ChildrenOfType<OsuButton>().Single();
return readyButton.Enabled.Value;
});
AddStep("delete beatmap", () => beatmaps.Delete(importedSet));
AddAssert("ready button disabled", () => !readyButton.Enabled.Value);
AddStep("undelete beatmap", () => beatmaps.Undelete(importedSet));
AddAssert("ready button enabled back", () => readyButton.Enabled.Value);
}
[Test] [Test]
public void TestToggleStateWhenNotHost() public void TestToggleStateWhenNotHost()
{ {
@ -89,8 +124,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
addClickButtonStep(); addClickButtonStep();
AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready);
addClickButtonStep(); verifyGameplayStartFlow();
AddAssert("match started", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad);
} }
[Test] [Test]
@ -105,8 +139,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
addClickButtonStep(); addClickButtonStep();
AddStep("make user host", () => Client.TransferHost(Client.Room?.Users[0].UserID ?? 0)); AddStep("make user host", () => Client.TransferHost(Client.Room?.Users[0].UserID ?? 0));
addClickButtonStep(); verifyGameplayStartFlow();
AddAssert("match started", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad);
} }
[Test] [Test]
@ -160,5 +193,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
InputManager.MoveMouseTo(button); InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left); InputManager.Click(MouseButton.Left);
}); });
private void verifyGameplayStartFlow()
{
addClickButtonStep();
AddAssert("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad);
AddAssert("ready button disabled", () => !button.ChildrenOfType<OsuButton>().Single().Enabled.Value);
AddStep("transitioned to gameplay", () => readyClickOperation.Dispose());
AddAssert("ready button enabled", () => button.ChildrenOfType<OsuButton>().Single().Enabled.Value);
}
} }
} }

View File

@ -143,6 +143,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
RoomManager = RoomManager =
{ {
TimeBetweenListingPolls = { Value = 1 }, TimeBetweenListingPolls = { Value = 1 },
TimeBetweenSelectionPolls = { Value = 1 }
} }
}; };

View File

@ -231,8 +231,19 @@ namespace osu.Game.Tests.Visual.Online
}); });
}); });
AddAssert("shown beatmaps of current ruleset", () => overlay.Header.Picker.Difficulties.All(b => b.Beatmap.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value))); AddAssert("shown beatmaps of current ruleset", () => overlay.Header.HeaderContent.Picker.Difficulties.All(b => b.Beatmap.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value)));
AddAssert("left-most beatmap selected", () => overlay.Header.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected); AddAssert("left-most beatmap selected", () => overlay.Header.HeaderContent.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected);
}
[Test]
public void TestExplicitBeatmap()
{
AddStep("show explicit map", () =>
{
var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
beatmapSet.OnlineInfo.HasExplicitContent = true;
overlay.ShowBeatmapSet(beatmapSet);
});
} }
[Test] [Test]
@ -299,12 +310,12 @@ namespace osu.Game.Tests.Visual.Online
private void downloadAssert(bool shown) private void downloadAssert(bool shown)
{ {
AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.DownloadButtonsVisible == shown); AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.HeaderContent.DownloadButtonsVisible == shown);
} }
private class TestBeatmapSetOverlay : BeatmapSetOverlay private class TestBeatmapSetOverlay : BeatmapSetOverlay
{ {
public new Header Header => base.Header; public new BeatmapSetHeader Header => base.Header;
} }
} }
} }

View File

@ -99,13 +99,16 @@ namespace osu.Game.Tests.Visual.Online
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(RulesetStore rulesets) private void load(RulesetStore rulesets)
{ {
var normal = CreateWorkingBeatmap(Ruleset.Value).BeatmapSetInfo; var normal = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
normal.OnlineInfo.HasVideo = true; normal.OnlineInfo.HasVideo = true;
normal.OnlineInfo.HasStoryboard = true; normal.OnlineInfo.HasStoryboard = true;
var undownloadable = getUndownloadableBeatmapSet(); var undownloadable = getUndownloadableBeatmapSet();
var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets); var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets);
var explicitMap = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
explicitMap.OnlineInfo.HasExplicitContent = true;
Child = new BasicScrollContainer Child = new BasicScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -121,9 +124,11 @@ namespace osu.Game.Tests.Visual.Online
new GridBeatmapPanel(normal), new GridBeatmapPanel(normal),
new GridBeatmapPanel(undownloadable), new GridBeatmapPanel(undownloadable),
new GridBeatmapPanel(manyDifficulties), new GridBeatmapPanel(manyDifficulties),
new GridBeatmapPanel(explicitMap),
new ListBeatmapPanel(normal), new ListBeatmapPanel(normal),
new ListBeatmapPanel(undownloadable), new ListBeatmapPanel(undownloadable),
new ListBeatmapPanel(manyDifficulties), new ListBeatmapPanel(manyDifficulties),
new ListBeatmapPanel(explicitMap)
}, },
}, },
}; };

View File

@ -11,12 +11,10 @@ using osu.Framework.Platform;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay;
using osu.Game.Screens.OnlinePlay.Match.Components;
using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
using osu.Game.Users; using osu.Game.Users;
@ -85,8 +83,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("move mouse to create button", () => AddStep("move mouse to create button", () =>
{ {
var footer = match.ChildrenOfType<Footer>().Single(); InputManager.MoveMouseTo(this.ChildrenOfType<PlaylistsMatchSettingsOverlay.CreateRoomButton>().Single());
InputManager.MoveMouseTo(footer.ChildrenOfType<OsuButton>().Single());
}); });
AddStep("click", () => InputManager.Click(MouseButton.Left)); AddStep("click", () => InputManager.Click(MouseButton.Left));

View File

@ -11,6 +11,7 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Details;
using osuTK.Graphics; using osuTK.Graphics;
@ -141,16 +142,12 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select changed Difficulty Adjust mod", () => AddStep("select changed Difficulty Adjust mod", () =>
{ {
var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
var difficultyAdjustMod = ruleset.GetAllMods().OfType<ModDifficultyAdjust>().Single(); var difficultyAdjustMod = ruleset.GetAllMods().OfType<OsuModDifficultyAdjust>().Single();
var originalDifficulty = advancedStats.Beatmap.BaseDifficulty; var originalDifficulty = advancedStats.Beatmap.BaseDifficulty;
var adjustedDifficulty = new BeatmapDifficulty
{ difficultyAdjustMod.ReadFromDifficulty(originalDifficulty);
CircleSize = originalDifficulty.CircleSize, difficultyAdjustMod.DrainRate.Value = originalDifficulty.DrainRate - 0.5f;
DrainRate = originalDifficulty.DrainRate - 0.5f, difficultyAdjustMod.ApproachRate.Value = originalDifficulty.ApproachRate + 2.2f;
OverallDifficulty = originalDifficulty.OverallDifficulty,
ApproachRate = originalDifficulty.ApproachRate + 2.2f,
};
difficultyAdjustMod.ReadFromDifficulty(adjustedDifficulty);
SelectedMods.Value = new[] { difficultyAdjustMod }; SelectedMods.Value = new[] { difficultyAdjustMod };
}); });

View File

@ -7,6 +7,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapListing; using osu.Game.Overlays.BeatmapListing;
@ -19,9 +20,18 @@ namespace osu.Game.Tests.Visual.UserInterface
[Cached] [Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private readonly BeatmapListingSearchControl control; private BeatmapListingSearchControl control;
public TestSceneBeatmapListingSearchControl() private OsuConfigManager localConfig;
[BackgroundDependencyLoader]
private void load()
{
Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage));
}
[SetUp]
public void SetUp() => Schedule(() =>
{ {
OsuSpriteText query; OsuSpriteText query;
OsuSpriteText ruleset; OsuSpriteText ruleset;
@ -31,30 +41,34 @@ namespace osu.Game.Tests.Visual.UserInterface
OsuSpriteText extra; OsuSpriteText extra;
OsuSpriteText ranks; OsuSpriteText ranks;
OsuSpriteText played; OsuSpriteText played;
OsuSpriteText explicitMap;
Add(control = new BeatmapListingSearchControl Children = new Drawable[]
{ {
Anchor = Anchor.Centre, control = new BeatmapListingSearchControl
Origin = Anchor.Centre,
});
Add(new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{ {
query = new OsuSpriteText(), Anchor = Anchor.Centre,
ruleset = new OsuSpriteText(), Origin = Anchor.Centre,
category = new OsuSpriteText(), },
genre = new OsuSpriteText(), new FillFlowContainer
language = new OsuSpriteText(), {
extra = new OsuSpriteText(), AutoSizeAxes = Axes.Both,
ranks = new OsuSpriteText(), Direction = FillDirection.Vertical,
played = new OsuSpriteText() Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
query = new OsuSpriteText(),
ruleset = new OsuSpriteText(),
category = new OsuSpriteText(),
genre = new OsuSpriteText(),
language = new OsuSpriteText(),
extra = new OsuSpriteText(),
ranks = new OsuSpriteText(),
played = new OsuSpriteText(),
explicitMap = new OsuSpriteText(),
}
} }
}); };
control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true); control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true);
control.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true); control.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true);
@ -64,7 +78,8 @@ namespace osu.Game.Tests.Visual.UserInterface
control.Extra.BindCollectionChanged((u, v) => extra.Text = $"Extra: {(control.Extra.Any() ? string.Join('.', control.Extra.Select(i => i.ToString().ToLowerInvariant())) : "")}", true); control.Extra.BindCollectionChanged((u, v) => extra.Text = $"Extra: {(control.Extra.Any() ? string.Join('.', control.Extra.Select(i => i.ToString().ToLowerInvariant())) : "")}", true);
control.Ranks.BindCollectionChanged((u, v) => ranks.Text = $"Ranks: {(control.Ranks.Any() ? string.Join('.', control.Ranks.Select(i => i.ToString())) : "")}", true); control.Ranks.BindCollectionChanged((u, v) => ranks.Text = $"Ranks: {(control.Ranks.Any() ? string.Join('.', control.Ranks.Select(i => i.ToString())) : "")}", true);
control.Played.BindValueChanged(p => played.Text = $"Played: {p.NewValue}", true); control.Played.BindValueChanged(p => played.Text = $"Played: {p.NewValue}", true);
} control.ExplicitContent.BindValueChanged(e => explicitMap.Text = $"Explicit Maps: {e.NewValue}", true);
});
[Test] [Test]
public void TestCovers() public void TestCovers()
@ -74,6 +89,22 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("Set null beatmap", () => control.BeatmapSet = null); AddStep("Set null beatmap", () => control.BeatmapSet = null);
} }
[Test]
public void TestExplicitConfig()
{
AddStep("configure explicit content to allowed", () => localConfig.Set(OsuSetting.ShowOnlineExplicitContent, true));
AddAssert("explicit control set to show", () => control.ExplicitContent.Value == SearchExplicit.Show);
AddStep("configure explicit content to disallowed", () => localConfig.Set(OsuSetting.ShowOnlineExplicitContent, false));
AddAssert("explicit control set to hide", () => control.ExplicitContent.Value == SearchExplicit.Hide);
}
protected override void Dispose(bool isDisposing)
{
localConfig?.Dispose();
base.Dispose(isDisposing);
}
private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo
{ {
OnlineInfo = new BeatmapSetOnlineInfo OnlineInfo = new BeatmapSetOnlineInfo

View File

@ -5,6 +5,7 @@ using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osuTK; using osuTK;
@ -14,8 +15,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneLoadingLayer : OsuTestScene public class TestSceneLoadingLayer : OsuTestScene
{ {
private Drawable dimContent; private TestLoadingLayer overlay;
private LoadingLayer overlay;
private Container content; private Container content;
@ -29,14 +29,14 @@ namespace osu.Game.Tests.Visual.UserInterface
Size = new Vector2(300), Size = new Vector2(300),
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Children = new[] Children = new Drawable[]
{ {
new Box new Box
{ {
Colour = Color4.SlateGray, Colour = Color4.SlateGray,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
dimContent = new FillFlowContainer new FillFlowContainer
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.UserInterface
new TriangleButton { Text = "puush me", Width = 200, Action = () => { } }, new TriangleButton { Text = "puush me", Width = 200, Action = () => { } },
} }
}, },
overlay = new LoadingLayer(dimContent), overlay = new TestLoadingLayer(true),
} }
}, },
}; };
@ -64,25 +64,11 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("show", () => overlay.Show()); AddStep("show", () => overlay.Show());
AddUntilStep("wait for content dim", () => dimContent.Colour != Color4.White); AddUntilStep("wait for content dim", () => overlay.BackgroundDimLayer.Alpha > 0);
AddStep("hide", () => overlay.Hide()); AddStep("hide", () => overlay.Hide());
AddUntilStep("wait for content restore", () => dimContent.Colour == Color4.White); AddUntilStep("wait for content restore", () => Precision.AlmostEquals(overlay.BackgroundDimLayer.Alpha, 0));
}
[Test]
public void TestContentRestoreOnDispose()
{
AddAssert("not visible", () => !overlay.IsPresent);
AddStep("show", () => overlay.Show());
AddUntilStep("wait for content dim", () => dimContent.Colour != Color4.White);
AddStep("expire", () => overlay.Expire());
AddUntilStep("wait for content restore", () => dimContent.Colour == Color4.White);
} }
[Test] [Test]
@ -98,5 +84,15 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("hide", () => overlay.Hide()); AddStep("hide", () => overlay.Hide());
} }
private class TestLoadingLayer : LoadingLayer
{
public new Box BackgroundDimLayer => base.BackgroundDimLayer;
public TestLoadingLayer(bool dimBackground = false, bool withBox = true)
: base(dimBackground, withBox)
{
}
}
} }
} }

View File

@ -131,6 +131,18 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0); AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0);
} }
[Test]
public void TestExternallySetCustomizedMod()
{
AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } });
AddAssert("ensure button is selected and customized accordingly", () =>
{
var button = modSelect.GetModButton(SelectedMods.Value.Single());
return ((OsuModDoubleTime)button.SelectedMod).SpeedChange.Value == 1.01;
});
}
private void testSingleMod(Mod mod) private void testSingleMod(Mod mod)
{ {
selectNext(mod); selectNext(mod);

View File

@ -10,7 +10,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />

View File

@ -7,7 +7,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Tournament.Tests.dll" "${workspaceRoot}/bin/Debug/net5.0/osu.Game.Tournament.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)", "preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Tournament.Tests.dll" "${workspaceRoot}/bin/Release/net5.0/osu.Game.Tournament.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)", "preLaunchTask": "Build (Release)",

View File

@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Tournament.Screens; using osu.Game.Tournament.Screens.Setup;
namespace osu.Game.Tournament.Tests.Screens namespace osu.Game.Tournament.Tests.Screens
{ {

View File

@ -1,7 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Game.Tournament.Screens; using osu.Game.Tournament.Screens.Setup;
namespace osu.Game.Tournament.Tests.Screens namespace osu.Game.Tournament.Tests.Screens
{ {

View File

@ -11,7 +11,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />

View File

@ -1,10 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.IO; using osu.Game.IO;
using System.IO; using System.IO;
using System.Collections.Generic;
using osu.Game.Tournament.Configuration; using osu.Game.Tournament.Configuration;
namespace osu.Game.Tournament.IO namespace osu.Game.Tournament.IO
@ -13,25 +15,39 @@ namespace osu.Game.Tournament.IO
{ {
private const string default_tournament = "default"; private const string default_tournament = "default";
private readonly Storage storage; private readonly Storage storage;
private readonly Storage allTournaments;
private readonly TournamentStorageManager storageConfig; private readonly TournamentStorageManager storageConfig;
public readonly Bindable<string> CurrentTournament;
public TournamentStorage(Storage storage) public TournamentStorage(Storage storage)
: base(storage.GetStorageForDirectory("tournaments"), string.Empty) : base(storage.GetStorageForDirectory("tournaments"), string.Empty)
{ {
this.storage = storage; this.storage = storage;
allTournaments = UnderlyingStorage;
storageConfig = new TournamentStorageManager(storage); storageConfig = new TournamentStorageManager(storage);
if (storage.Exists("tournament.ini")) if (storage.Exists("tournament.ini"))
{ {
ChangeTargetStorage(UnderlyingStorage.GetStorageForDirectory(storageConfig.Get<string>(StorageConfig.CurrentTournament))); ChangeTargetStorage(allTournaments.GetStorageForDirectory(storageConfig.Get<string>(StorageConfig.CurrentTournament)));
} }
else else
Migrate(UnderlyingStorage.GetStorageForDirectory(default_tournament)); Migrate(allTournaments.GetStorageForDirectory(default_tournament));
CurrentTournament = storageConfig.GetBindable<string>(StorageConfig.CurrentTournament);
Logger.Log("Using tournament storage: " + GetFullPath(string.Empty)); Logger.Log("Using tournament storage: " + GetFullPath(string.Empty));
CurrentTournament.BindValueChanged(updateTournament);
} }
private void updateTournament(ValueChangedEvent<string> newTournament)
{
ChangeTargetStorage(allTournaments.GetStorageForDirectory(newTournament.NewValue));
Logger.Log("Changing tournament storage: " + GetFullPath(string.Empty));
}
public IEnumerable<string> ListTournaments() => allTournaments.GetDirectories(string.Empty);
public override void Migrate(Storage newStorage) public override void Migrate(Storage newStorage)
{ {
// this migration only happens once on moving to the per-tournament storage system. // this migration only happens once on moving to the per-tournament storage system.

View File

@ -0,0 +1,65 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics;
using System.Drawing;
using Newtonsoft.Json;
namespace osu.Game.Tournament
{
/// <summary>
/// We made a change from using SixLabors.ImageSharp.Point to System.Drawing.Point at some stage.
/// This handles converting to a standardised format on json serialize/deserialize operations.
/// </summary>
internal class JsonPointConverter : JsonConverter<Point>
{
public override void WriteJson(JsonWriter writer, Point value, JsonSerializer serializer)
{
// use the format of LaborSharp's Point since it is nicer.
serializer.Serialize(writer, new { value.X, value.Y });
}
public override Point ReadJson(JsonReader reader, Type objectType, Point existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
{
// if there's no object present then this is using string representation (System.Drawing.Point serializes to "x,y")
string str = (string)reader.Value;
Debug.Assert(str != null);
return new PointConverter().ConvertFromString(str) as Point? ?? new Point();
}
var point = new Point();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndObject) break;
if (reader.TokenType == JsonToken.PropertyName)
{
var name = reader.Value?.ToString();
int? val = reader.ReadAsInt32();
if (val == null)
continue;
switch (name)
{
case "X":
point.X = val.Value;
break;
case "Y":
point.Y = val.Value;
break;
}
}
}
return point;
}
}
}

View File

@ -0,0 +1,72 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tournament.Screens.Setup
{
internal class ActionableInfo : LabelledDrawable<Drawable>
{
protected OsuButton Button;
public ActionableInfo()
: base(true)
{
}
public string ButtonText
{
set => Button.Text = value;
}
public string Value
{
set => valueText.Text = value;
}
public bool Failing
{
set => valueText.Colour = value ? Color4.Red : Color4.White;
}
public Action Action;
private TournamentSpriteText valueText;
protected FillFlowContainer FlowContainer;
protected override Drawable CreateComponent() => new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Children = new Drawable[]
{
valueText = new TournamentSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
FlowContainer = new FillFlowContainer
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
Button = new TriangleButton
{
Size = new Vector2(100, 40),
Action = () => Action?.Invoke()
}
}
}
}
};
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Tournament.Screens.Setup
{
internal class ResolutionSelector : ActionableInfo
{
private const int minimum_window_height = 480;
private const int maximum_window_height = 2160;
public new Action<int> Action;
private OsuNumberBox numberBox;
protected override Drawable CreateComponent()
{
var drawable = base.CreateComponent();
FlowContainer.Insert(-1, numberBox = new OsuNumberBox
{
Text = "1080",
Width = 100
});
base.Action = () =>
{
if (string.IsNullOrEmpty(numberBox.Text))
return;
// box contains text
if (!int.TryParse(numberBox.Text, out var number))
{
// at this point, the only reason we can arrive here is if the input number was too big to parse into an int
// so clamp to max allowed value
number = maximum_window_height;
}
else
{
number = Math.Clamp(number, minimum_window_height, maximum_window_height);
}
// in case number got clamped, reset number in numberBox
numberBox.Text = number.ToString();
Action?.Invoke(number);
};
return drawable;
}
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -17,9 +16,8 @@ using osu.Game.Rulesets;
using osu.Game.Tournament.IPC; using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
using osuTK; using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tournament.Screens namespace osu.Game.Tournament.Screens.Setup
{ {
public class SetupScreen : TournamentScreen, IProvideVideo public class SetupScreen : TournamentScreen, IProvideVideo
{ {
@ -64,9 +62,6 @@ namespace osu.Game.Tournament.Screens
reload(); reload();
} }
[Resolved]
private Framework.Game game { get; set; }
private void reload() private void reload()
{ {
var fileBasedIpc = ipc as FileBasedIPC; var fileBasedIpc = ipc as FileBasedIPC;
@ -111,6 +106,11 @@ namespace osu.Game.Tournament.Screens
Items = rulesets.AvailableRulesets, Items = rulesets.AvailableRulesets,
Current = LadderInfo.Ruleset, Current = LadderInfo.Ruleset,
}, },
new TournamentSwitcher
{
Label = "Current tournament",
Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart to apply changes.",
},
resolution = new ResolutionSelector resolution = new ResolutionSelector
{ {
Label = "Stream area resolution", Label = "Stream area resolution",
@ -151,108 +151,5 @@ namespace osu.Game.Tournament.Screens
Width = 0.5f, Width = 0.5f,
}; };
} }
private class ActionableInfo : LabelledDrawable<Drawable>
{
private OsuButton button;
public ActionableInfo()
: base(true)
{
}
public string ButtonText
{
set => button.Text = value;
}
public string Value
{
set => valueText.Text = value;
}
public bool Failing
{
set => valueText.Colour = value ? Color4.Red : Color4.White;
}
public Action Action;
private TournamentSpriteText valueText;
protected FillFlowContainer FlowContainer;
protected override Drawable CreateComponent() => new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Children = new Drawable[]
{
valueText = new TournamentSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
FlowContainer = new FillFlowContainer
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
button = new TriangleButton
{
Size = new Vector2(100, 40),
Action = () => Action?.Invoke()
}
}
}
}
};
}
private class ResolutionSelector : ActionableInfo
{
private const int minimum_window_height = 480;
private const int maximum_window_height = 2160;
public new Action<int> Action;
private OsuNumberBox numberBox;
protected override Drawable CreateComponent()
{
var drawable = base.CreateComponent();
FlowContainer.Insert(-1, numberBox = new OsuNumberBox
{
Text = "1080",
Width = 100
});
base.Action = () =>
{
if (string.IsNullOrEmpty(numberBox.Text))
return;
// box contains text
if (!int.TryParse(numberBox.Text, out var number))
{
// at this point, the only reason we can arrive here is if the input number was too big to parse into an int
// so clamp to max allowed value
number = maximum_window_height;
}
else
{
number = Math.Clamp(number, minimum_window_height, maximum_window_height);
}
// in case number got clamped, reset number in numberBox
numberBox.Text = number.ToString();
Action?.Invoke(number);
};
return drawable;
}
}
} }
} }

View File

@ -13,11 +13,11 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
using osu.Game.Tournament.IPC;
using osuTK; using osuTK;
namespace osu.Game.Tournament.Screens namespace osu.Game.Tournament.Screens.Setup
{ {
public class StablePathSelectScreen : TournamentScreen public class StablePathSelectScreen : TournamentScreen
{ {

View File

@ -0,0 +1,44 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Tournament.IO;
namespace osu.Game.Tournament.Screens.Setup
{
internal class TournamentSwitcher : ActionableInfo
{
private OsuDropdown<string> dropdown;
[Resolved]
private TournamentGameBase game { get; set; }
[BackgroundDependencyLoader]
private void load(TournamentStorage storage)
{
string startupTournament = storage.CurrentTournament.Value;
dropdown.Current = storage.CurrentTournament;
dropdown.Items = storage.ListTournaments();
dropdown.Current.BindValueChanged(v => Button.Enabled.Value = v.NewValue != startupTournament, true);
Action = () => game.GracefullyExit();
ButtonText = "Close osu!";
}
protected override Drawable CreateComponent()
{
var drawable = base.CreateComponent();
FlowContainer.Insert(-1, dropdown = new OsuDropdown<string>
{
Width = 510
});
return drawable;
}
}
}

View File

@ -8,12 +8,12 @@ using Newtonsoft.Json;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Platform;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Tournament.IPC;
using osu.Game.Tournament.IO; using osu.Game.Tournament.IO;
using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
using osu.Game.Users; using osu.Game.Users;
using osuTK.Input; using osuTK.Input;
@ -40,6 +40,8 @@ namespace osu.Game.Tournament
Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly)); Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly));
dependencies.CacheAs<Storage>(storage = new TournamentStorage(baseStorage)); dependencies.CacheAs<Storage>(storage = new TournamentStorage(baseStorage));
dependencies.CacheAs(storage);
dependencies.Cache(new TournamentVideoResourceStore(storage)); dependencies.Cache(new TournamentVideoResourceStore(storage));
Textures.AddStore(new TextureLoaderStore(new StorageBackedResourceStore(storage))); Textures.AddStore(new TextureLoaderStore(new StorageBackedResourceStore(storage)));
@ -60,7 +62,7 @@ namespace osu.Game.Tournament
{ {
using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open))
using (var sr = new StreamReader(stream)) using (var sr = new StreamReader(stream))
ladder = JsonConvert.DeserializeObject<LadderInfo>(sr.ReadToEnd()); ladder = JsonConvert.DeserializeObject<LadderInfo>(sr.ReadToEnd(), new JsonPointConverter());
} }
ladder ??= new LadderInfo(); ladder ??= new LadderInfo();
@ -251,6 +253,7 @@ namespace osu.Game.Tournament
Formatting = Formatting.Indented, Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore,
Converters = new JsonConverter[] { new JsonPointConverter() }
})); }));
} }
} }

View File

@ -19,6 +19,7 @@ using osu.Game.Tournament.Screens.Gameplay;
using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.Ladder;
using osu.Game.Tournament.Screens.MapPool; using osu.Game.Tournament.Screens.MapPool;
using osu.Game.Tournament.Screens.Schedule; using osu.Game.Tournament.Screens.Schedule;
using osu.Game.Tournament.Screens.Setup;
using osu.Game.Tournament.Screens.Showcase; using osu.Game.Tournament.Screens.Showcase;
using osu.Game.Tournament.Screens.TeamIntro; using osu.Game.Tournament.Screens.TeamIntro;
using osu.Game.Tournament.Screens.TeamWin; using osu.Game.Tournament.Screens.TeamWin;

View File

@ -50,7 +50,15 @@ namespace osu.Game.Beatmaps
IBeatmap IBeatmap.Clone() => Clone(); IBeatmap IBeatmap.Clone() => Clone();
public Beatmap<T> Clone() => (Beatmap<T>)MemberwiseClone(); public Beatmap<T> Clone()
{
var clone = (Beatmap<T>)MemberwiseClone();
clone.ControlPointInfo = ControlPointInfo.CreateCopy();
// todo: deep clone other elements as required.
return clone;
}
} }
public class Beatmap : Beatmap<HitObject> public class Beatmap : Beatmap<HitObject>

View File

@ -260,17 +260,10 @@ namespace osu.Game.Beatmaps
} }
catch (BeatmapInvalidForRulesetException e) catch (BeatmapInvalidForRulesetException e)
{ {
// Conversion has failed for the given ruleset, so return the difficulty in the beatmap's default ruleset.
// Ensure the beatmap's default ruleset isn't the one already being converted to.
// This shouldn't happen as it means something went seriously wrong, but if it does an endless loop should be avoided.
if (rulesetInfo.Equals(beatmapInfo.Ruleset)) if (rulesetInfo.Equals(beatmapInfo.Ruleset))
{
Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineBeatmapID} to the beatmap's default ruleset ({beatmapInfo.Ruleset})."); Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineBeatmapID} to the beatmap's default ruleset ({beatmapInfo.Ruleset}).");
return new StarDifficulty();
}
return GetAsync(new DifficultyCacheLookup(key.Beatmap, key.Beatmap.Ruleset, key.OrderedMods)).Result; return new StarDifficulty();
} }
catch catch
{ {

View File

@ -31,6 +31,11 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
public BeatmapSetOnlineStatus Status { get; set; } public BeatmapSetOnlineStatus Status { get; set; }
/// <summary>
/// Whether or not this beatmap set has explicit content.
/// </summary>
public bool HasExplicitContent { get; set; }
/// <summary> /// <summary>
/// Whether or not this beatmap set has a background video. /// Whether or not this beatmap set has a background video.
/// </summary> /// </summary>

View File

@ -28,5 +28,21 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <param name="existing">An existing control point to compare with.</param> /// <param name="existing">An existing control point to compare with.</param>
/// <returns>Whether this <see cref="ControlPoint"/> is redundant when placed alongside <paramref name="existing"/>.</returns> /// <returns>Whether this <see cref="ControlPoint"/> is redundant when placed alongside <paramref name="existing"/>.</returns>
public abstract bool IsRedundant(ControlPoint existing); public abstract bool IsRedundant(ControlPoint existing);
/// <summary>
/// Create an unbound copy of this control point.
/// </summary>
public ControlPoint CreateCopy()
{
var copy = (ControlPoint)Activator.CreateInstance(GetType());
copy.CopyFrom(this);
return copy;
}
public virtual void CopyFrom(ControlPoint other)
{
}
} }
} }

View File

@ -297,5 +297,15 @@ namespace osu.Game.Beatmaps.ControlPoints
break; break;
} }
} }
public ControlPointInfo CreateCopy()
{
var controlPointInfo = new ControlPointInfo();
foreach (var point in AllControlPoints)
controlPointInfo.Add(point.Time, point.CreateCopy());
return controlPointInfo;
}
} }
} }

View File

@ -39,5 +39,12 @@ namespace osu.Game.Beatmaps.ControlPoints
public override bool IsRedundant(ControlPoint existing) public override bool IsRedundant(ControlPoint existing)
=> existing is DifficultyControlPoint existingDifficulty => existing is DifficultyControlPoint existingDifficulty
&& SpeedMultiplier == existingDifficulty.SpeedMultiplier; && SpeedMultiplier == existingDifficulty.SpeedMultiplier;
public override void CopyFrom(ControlPoint other)
{
SpeedMultiplier = ((DifficultyControlPoint)other).SpeedMultiplier;
base.CopyFrom(other);
}
} }
} }

View File

@ -50,5 +50,13 @@ namespace osu.Game.Beatmaps.ControlPoints
&& existing is EffectControlPoint existingEffect && existing is EffectControlPoint existingEffect
&& KiaiMode == existingEffect.KiaiMode && KiaiMode == existingEffect.KiaiMode
&& OmitFirstBarLine == existingEffect.OmitFirstBarLine; && OmitFirstBarLine == existingEffect.OmitFirstBarLine;
public override void CopyFrom(ControlPoint other)
{
KiaiMode = ((EffectControlPoint)other).KiaiMode;
OmitFirstBarLine = ((EffectControlPoint)other).OmitFirstBarLine;
base.CopyFrom(other);
}
} }
} }

View File

@ -72,5 +72,13 @@ namespace osu.Game.Beatmaps.ControlPoints
=> existing is SampleControlPoint existingSample => existing is SampleControlPoint existingSample
&& SampleBank == existingSample.SampleBank && SampleBank == existingSample.SampleBank
&& SampleVolume == existingSample.SampleVolume; && SampleVolume == existingSample.SampleVolume;
public override void CopyFrom(ControlPoint other)
{
SampleVolume = ((SampleControlPoint)other).SampleVolume;
SampleBank = ((SampleControlPoint)other).SampleBank;
base.CopyFrom(other);
}
} }
} }

View File

@ -69,5 +69,13 @@ namespace osu.Game.Beatmaps.ControlPoints
// Timing points are never redundant as they can change the time signature. // Timing points are never redundant as they can change the time signature.
public override bool IsRedundant(ControlPoint existing) => false; public override bool IsRedundant(ControlPoint existing) => false;
public override void CopyFrom(ControlPoint other)
{
TimeSignature = ((TimingControlPoint)other).TimeSignature;
BeatLength = ((TimingControlPoint)other).BeatLength;
base.CopyFrom(other);
}
} }
} }

View File

@ -164,13 +164,25 @@ namespace osu.Game.Beatmaps.Formats
/// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it. /// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it.
/// DO NOT USE THIS UNLESS 100% SURE. /// DO NOT USE THIS UNLESS 100% SURE.
/// </summary> /// </summary>
public readonly float BpmMultiplier; public double BpmMultiplier { get; private set; }
public LegacyDifficultyControlPoint(double beatLength) public LegacyDifficultyControlPoint(double beatLength)
: this()
{
// Note: In stable, the division occurs on floats, but with compiler optimisations turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?).
BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1;
}
public LegacyDifficultyControlPoint()
{ {
SpeedMultiplierBindable.Precision = double.Epsilon; SpeedMultiplierBindable.Precision = double.Epsilon;
}
BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100f : 1; public override void CopyFrom(ControlPoint other)
{
base.CopyFrom(other);
BpmMultiplier = ((LegacyDifficultyControlPoint)other).BpmMultiplier;
} }
} }
@ -192,6 +204,13 @@ namespace osu.Game.Beatmaps.Formats
=> base.IsRedundant(existing) => base.IsRedundant(existing)
&& existing is LegacySampleControlPoint existingSample && existing is LegacySampleControlPoint existingSample
&& CustomSampleBank == existingSample.CustomSampleBank; && CustomSampleBank == existingSample.CustomSampleBank;
public override void CopyFrom(ControlPoint other)
{
base.CopyFrom(other);
CustomSampleBank = ((LegacySampleControlPoint)other).CustomSampleBank;
}
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More