1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-26 18:03:11 +08:00

Merge branch 'master' into fix-spinner-spinning-weirdness

This commit is contained in:
Dean Herbert 2021-01-18 18:26:44 +09:00
commit 707d5346c9
66 changed files with 932 additions and 270 deletions

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,10 +48,6 @@ desc 'Deploy to play store'
desc 'Compile the project' desc 'Compile the project'
lane :build do |options| lane :build do |options|
nuget_restore(
project_path: 'osu.sln'
)
souyuz( souyuz(
build_configuration: 'Release', build_configuration: 'Release',
solution_path: 'osu.sln', solution_path: 'osu.sln',
@ -107,10 +103,6 @@ platform :ios do
desc 'Compile the project' desc 'Compile the project'
lane :build do lane :build do
nuget_restore(
project_path: 'osu.sln'
)
souyuz( souyuz(
platform: "ios", platform: "ios",
plist_path: "osu.iOS/Info.plist" plist_path: "osu.iOS/Info.plist"

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="2021.106.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2021.115.1" />
</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,22 +56,32 @@ 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>();
for (int i = 0; i < intent.ClipData?.ItemCount; i++)
{
var content = intent.ClipData?.GetItemAt(i);
if (content != null) if (content != null)
handleImportFromUri(content.Uri); 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 () =>
{
var tasks = new List<ImportTask>();
await Task.WhenAll(uris.Select(async uri =>
{ {
// there are more performant overloads of this method, but this one is the most backwards-compatible // there are more performant overloads of this method, but this one is the most backwards-compatible
// (dates back to API 1). // (dates back to API 1).
@ -90,7 +102,13 @@ namespace osu.Android
using (var stream = ContentResolver.OpenInputStream(uri)) using (var stream = ContentResolver.OpenInputStream(uri))
await stream.CopyToAsync(copy); await stream.CopyToAsync(copy);
await game.Import(copy, filename); lock (tasks)
{
tasks.Add(new ImportTask(copy, filename));
}
}));
await game.Import(tasks.ToArray());
}, TaskCreationOptions.LongRunning); }, TaskCreationOptions.LongRunning);
} }
} }

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;
if (OperatingSystem.IsWindows())
{
try try
{ {
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) stableInstallPath = getStableInstallPathFromRegistry();
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", "");
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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -222,6 +222,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

@ -235,6 +235,17 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("left-most beatmap selected", () => overlay.Header.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected); AddAssert("left-most beatmap selected", () => overlay.Header.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]
public void TestHide() public void TestHide()
{ {

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

@ -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,14 +41,16 @@ 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[]
{
control = new BeatmapListingSearchControl
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
}); },
new FillFlowContainer
Add(new FillFlowContainer
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
@ -52,9 +64,11 @@ namespace osu.Game.Tests.Visual.UserInterface
language = new OsuSpriteText(), language = new OsuSpriteText(),
extra = new OsuSpriteText(), extra = new OsuSpriteText(),
ranks = new OsuSpriteText(), ranks = new OsuSpriteText(),
played = 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

@ -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

@ -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

@ -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

@ -60,6 +60,8 @@ namespace osu.Game.Configuration
Set(OsuSetting.ExternalLinkWarning, true); Set(OsuSetting.ExternalLinkWarning, true);
Set(OsuSetting.PreferNoVideo, false); Set(OsuSetting.PreferNoVideo, false);
Set(OsuSetting.ShowOnlineExplicitContent, false);
// Audio // Audio
Set(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01); Set(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01);
@ -82,6 +84,7 @@ namespace osu.Game.Configuration
Set(OsuSetting.ShowStoryboard, true); Set(OsuSetting.ShowStoryboard, true);
Set(OsuSetting.BeatmapSkins, true); Set(OsuSetting.BeatmapSkins, true);
Set(OsuSetting.BeatmapColours, true);
Set(OsuSetting.BeatmapHitsounds, true); Set(OsuSetting.BeatmapHitsounds, true);
Set(OsuSetting.CursorRotation, true); Set(OsuSetting.CursorRotation, true);
@ -250,6 +253,7 @@ namespace osu.Game.Configuration
ScreenshotCaptureMenuCursor, ScreenshotCaptureMenuCursor,
SongSelectRightMouseScroll, SongSelectRightMouseScroll,
BeatmapSkins, BeatmapSkins,
BeatmapColours,
BeatmapHitsounds, BeatmapHitsounds,
IncreaseFirstObjectVisibility, IncreaseFirstObjectVisibility,
ScoreDisplayMode, ScoreDisplayMode,
@ -270,5 +274,6 @@ namespace osu.Game.Configuration
EditorWaveformOpacity, EditorWaveformOpacity,
DiscordRichPresence, DiscordRichPresence,
AutomaticallyDownloadWhenSpectating, AutomaticallyDownloadWhenSpectating,
ShowOnlineExplicitContent,
} }
} }

View File

@ -115,13 +115,13 @@ namespace osu.Game.Database
return Import(notification, paths.Select(p => new ImportTask(p)).ToArray()); return Import(notification, paths.Select(p => new ImportTask(p)).ToArray());
} }
public Task Import(Stream stream, string filename) public Task Import(params ImportTask[] tasks)
{ {
var notification = new ProgressNotification { State = ProgressNotificationState.Active }; var notification = new ProgressNotification { State = ProgressNotificationState.Active };
PostNotification?.Invoke(notification); PostNotification?.Invoke(notification);
return Import(notification, new ImportTask(stream, filename)); return Import(notification, tasks);
} }
protected async Task<IEnumerable<TModel>> Import(ProgressNotification notification, params ImportTask[] tasks) protected async Task<IEnumerable<TModel>> Import(ProgressNotification notification, params ImportTask[] tasks)

View File

@ -2,7 +2,6 @@
// 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.Collections.Generic;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace osu.Game.Database namespace osu.Game.Database
@ -19,11 +18,10 @@ namespace osu.Game.Database
Task Import(params string[] paths); Task Import(params string[] paths);
/// <summary> /// <summary>
/// Import the provided stream as a simple item. /// Import the specified files from the given import tasks.
/// </summary> /// </summary>
/// <param name="stream">The stream to import files from. Should be in a supported archive format.</param> /// <param name="tasks">The import tasks from which the files should be imported.</param>
/// <param name="filename">The filename of the archive being imported.</param> Task Import(params ImportTask[] tasks);
Task Import(Stream stream, string filename);
/// <summary> /// <summary>
/// An array of accepted file extensions (in the standard format of ".abc"). /// An array of accepted file extensions (in the standard format of ".abc").

View File

@ -135,6 +135,8 @@ namespace osu.Game.Database
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => new { b.RulesetID, b.Variant }); modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => new { b.RulesetID, b.Variant });
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction); modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction);
modelBuilder.Entity<DatabasedKeyBinding>().Ignore(b => b.KeyCombination);
modelBuilder.Entity<DatabasedKeyBinding>().Ignore(b => b.Action);
modelBuilder.Entity<DatabasedSetting>().HasIndex(b => new { b.RulesetID, b.Variant }); modelBuilder.Entity<DatabasedSetting>().HasIndex(b => new { b.RulesetID, b.Variant });

View File

@ -23,7 +23,7 @@ namespace osu.Game.Input.Bindings
private KeyBindingStore store; private KeyBindingStore store;
public override IEnumerable<KeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0); public override IEnumerable<IKeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0);
/// <summary> /// <summary>
/// Create a new instance. /// Create a new instance.

View File

@ -21,7 +21,7 @@ namespace osu.Game.Input.Bindings
handler = game; handler = game;
} }
public override IEnumerable<KeyBinding> DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings).Concat(AudioControlKeyBindings).Concat(EditorKeyBindings); public override IEnumerable<IKeyBinding> DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings).Concat(AudioControlKeyBindings).Concat(EditorKeyBindings);
public IEnumerable<KeyBinding> GlobalKeyBindings => new[] public IEnumerable<KeyBinding> GlobalKeyBindings => new[]
{ {

View File

@ -49,7 +49,7 @@ namespace osu.Game.Input
} }
} }
private void insertDefaults(IEnumerable<KeyBinding> defaults, int? rulesetId = null, int? variant = null) private void insertDefaults(IEnumerable<IKeyBinding> defaults, int? rulesetId = null, int? variant = null)
{ {
using (var usage = ContextFactory.GetForWrite()) using (var usage = ContextFactory.GetForWrite())
{ {

View File

@ -42,6 +42,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"bpm")] [JsonProperty(@"bpm")]
private double bpm { get; set; } private double bpm { get; set; }
[JsonProperty(@"nsfw")]
private bool hasExplicitContent { get; set; }
[JsonProperty(@"video")] [JsonProperty(@"video")]
private bool hasVideo { get; set; } private bool hasVideo { get; set; }
@ -94,6 +97,7 @@ namespace osu.Game.Online.API.Requests.Responses
FavouriteCount = favouriteCount, FavouriteCount = favouriteCount,
BPM = bpm, BPM = bpm,
Status = Status, Status = Status,
HasExplicitContent = hasExplicitContent,
HasVideo = hasVideo, HasVideo = hasVideo,
HasStoryboard = hasStoryboard, HasStoryboard = hasStoryboard,
Submitted = submitted, Submitted = submitted,

View File

@ -30,6 +30,8 @@ namespace osu.Game.Online.API.Requests
public SearchPlayed Played { get; } public SearchPlayed Played { get; }
public SearchExplicit ExplicitContent { get; }
[CanBeNull] [CanBeNull]
public IReadOnlyCollection<ScoreRank> Ranks { get; } public IReadOnlyCollection<ScoreRank> Ranks { get; }
@ -50,7 +52,8 @@ namespace osu.Game.Online.API.Requests
SearchLanguage language = SearchLanguage.Any, SearchLanguage language = SearchLanguage.Any,
IReadOnlyCollection<SearchExtra> extra = null, IReadOnlyCollection<SearchExtra> extra = null,
IReadOnlyCollection<ScoreRank> ranks = null, IReadOnlyCollection<ScoreRank> ranks = null,
SearchPlayed played = SearchPlayed.Any) SearchPlayed played = SearchPlayed.Any,
SearchExplicit explicitContent = SearchExplicit.Hide)
{ {
this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query);
this.ruleset = ruleset; this.ruleset = ruleset;
@ -64,6 +67,7 @@ namespace osu.Game.Online.API.Requests
Extra = extra; Extra = extra;
Ranks = ranks; Ranks = ranks;
Played = played; Played = played;
ExplicitContent = explicitContent;
} }
protected override WebRequest CreateWebRequest() protected override WebRequest CreateWebRequest()
@ -93,6 +97,8 @@ namespace osu.Game.Online.API.Requests
if (Played != SearchPlayed.Any) if (Played != SearchPlayed.Any)
req.AddParameter("played", Played.ToString().ToLowerInvariant()); req.AddParameter("played", Played.ToString().ToLowerInvariant());
req.AddParameter("nsfw", ExplicitContent == SearchExplicit.Show ? "true" : "false");
req.AddCursor(cursor); req.AddCursor(cursor);
return req; return req;

View File

@ -7,7 +7,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -128,7 +127,8 @@ namespace osu.Game.Online.Multiplayer
Debug.Assert(Room != null); Debug.Assert(Room != null);
var users = getRoomUsers(); var users = await getRoomUsers();
Debug.Assert(users != null);
await Task.WhenAll(users.Select(PopulateUser)); await Task.WhenAll(users.Select(PopulateUser));
@ -437,24 +437,20 @@ namespace osu.Game.Online.Multiplayer
/// This should be used whenever accessing users from outside of an Update thread context (ie. when not calling <see cref="Drawable.Schedule"/>). /// This should be used whenever accessing users from outside of an Update thread context (ie. when not calling <see cref="Drawable.Schedule"/>).
/// </summary> /// </summary>
/// <returns>A copy of users in the current room, or null if unavailable.</returns> /// <returns>A copy of users in the current room, or null if unavailable.</returns>
private List<MultiplayerRoomUser>? getRoomUsers() private Task<List<MultiplayerRoomUser>?> getRoomUsers()
{ {
List<MultiplayerRoomUser>? users = null; var tcs = new TaskCompletionSource<List<MultiplayerRoomUser>?>();
ManualResetEventSlim resetEvent = new ManualResetEventSlim();
// at some point we probably want to replace all these schedule calls with Room.LockForUpdate. // at some point we probably want to replace all these schedule calls with Room.LockForUpdate.
// for now, as this would require quite some consideration due to the number of accesses to the room instance, // for now, as this would require quite some consideration due to the number of accesses to the room instance,
// let's just add a manual schedule for the non-scheduled usages instead. // let's just add a manual schedule for the non-scheduled usages instead.
Scheduler.Add(() => Scheduler.Add(() =>
{ {
users = Room?.Users.ToList(); var users = Room?.Users.ToList();
resetEvent.Set(); tcs.SetResult(users);
}, false); }, false);
resetEvent.Wait(100); return tcs.Task;
return users;
} }
/// <summary> /// <summary>

View File

@ -51,7 +51,7 @@ using osu.Game.Screens.Select;
using osu.Game.Updater; using osu.Game.Updater;
using osu.Game.Utils; using osu.Game.Utils;
using LogLevel = osu.Framework.Logging.LogLevel; using LogLevel = osu.Framework.Logging.LogLevel;
using System.IO; using osu.Game.Database;
namespace osu.Game namespace osu.Game
{ {
@ -438,10 +438,10 @@ namespace osu.Game
}, validScreens: new[] { typeof(PlaySongSelect) }); }, validScreens: new[] { typeof(PlaySongSelect) });
} }
public override Task Import(Stream stream, string filename) public override Task Import(params ImportTask[] imports)
{ {
// encapsulate task as we don't want to begin the import process until in a ready state. // encapsulate task as we don't want to begin the import process until in a ready state.
var importTask = new Task(async () => await base.Import(stream, filename)); var importTask = new Task(async () => await base.Import(imports));
waitForReady(() => this, _ => importTask.Start()); waitForReady(() => this, _ => importTask.Start());

View File

@ -419,15 +419,14 @@ namespace osu.Game
} }
} }
public virtual async Task Import(Stream stream, string filename) public virtual async Task Import(params ImportTask[] tasks)
{ {
var extension = Path.GetExtension(filename)?.ToLowerInvariant(); var tasksPerExtension = tasks.GroupBy(t => Path.GetExtension(t.Path).ToLowerInvariant());
await Task.WhenAll(tasksPerExtension.Select(taskGroup =>
foreach (var importer in fileImporters)
{ {
if (importer.HandledExtensions.Contains(extension)) var importer = fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(taskGroup.Key));
await importer.Import(stream, Path.GetFileNameWithoutExtension(filename)); return importer?.Import(taskGroup.ToArray()) ?? Task.CompletedTask;
} }));
} }
public IEnumerable<string> HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions); public IEnumerable<string> HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions);

View File

@ -141,6 +141,7 @@ namespace osu.Game.Overlays.BeatmapListing
searchControl.Extra.CollectionChanged += (_, __) => queueUpdateSearch(); searchControl.Extra.CollectionChanged += (_, __) => queueUpdateSearch();
searchControl.Ranks.CollectionChanged += (_, __) => queueUpdateSearch(); searchControl.Ranks.CollectionChanged += (_, __) => queueUpdateSearch();
searchControl.Played.BindValueChanged(_ => queueUpdateSearch()); searchControl.Played.BindValueChanged(_ => queueUpdateSearch());
searchControl.ExplicitContent.BindValueChanged(_ => queueUpdateSearch());
sortCriteria.BindValueChanged(_ => queueUpdateSearch()); sortCriteria.BindValueChanged(_ => queueUpdateSearch());
sortDirection.BindValueChanged(_ => queueUpdateSearch()); sortDirection.BindValueChanged(_ => queueUpdateSearch());
@ -193,7 +194,8 @@ namespace osu.Game.Overlays.BeatmapListing
searchControl.Language.Value, searchControl.Language.Value,
searchControl.Extra, searchControl.Extra,
searchControl.Ranks, searchControl.Ranks,
searchControl.Played.Value); searchControl.Played.Value,
searchControl.ExplicitContent.Value);
getSetsRequest.Success += response => getSetsRequest.Success += response =>
{ {

View File

@ -11,6 +11,7 @@ using osu.Framework.Bindables;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osuTK.Graphics; using osuTK.Graphics;
@ -42,6 +43,8 @@ namespace osu.Game.Overlays.BeatmapListing
public Bindable<SearchPlayed> Played => playedFilter.Current; public Bindable<SearchPlayed> Played => playedFilter.Current;
public Bindable<SearchExplicit> ExplicitContent => explicitContentFilter.Current;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
set set
@ -65,6 +68,7 @@ namespace osu.Game.Overlays.BeatmapListing
private readonly BeatmapSearchMultipleSelectionFilterRow<SearchExtra> extraFilter; private readonly BeatmapSearchMultipleSelectionFilterRow<SearchExtra> extraFilter;
private readonly BeatmapSearchScoreFilterRow ranksFilter; private readonly BeatmapSearchScoreFilterRow ranksFilter;
private readonly BeatmapSearchFilterRow<SearchPlayed> playedFilter; private readonly BeatmapSearchFilterRow<SearchPlayed> playedFilter;
private readonly BeatmapSearchFilterRow<SearchExplicit> explicitContentFilter;
private readonly Box background; private readonly Box background;
private readonly UpdateableBeatmapSetCover beatmapCover; private readonly UpdateableBeatmapSetCover beatmapCover;
@ -125,7 +129,8 @@ namespace osu.Game.Overlays.BeatmapListing
languageFilter = new BeatmapSearchFilterRow<SearchLanguage>(@"Language"), languageFilter = new BeatmapSearchFilterRow<SearchLanguage>(@"Language"),
extraFilter = new BeatmapSearchMultipleSelectionFilterRow<SearchExtra>(@"Extra"), extraFilter = new BeatmapSearchMultipleSelectionFilterRow<SearchExtra>(@"Extra"),
ranksFilter = new BeatmapSearchScoreFilterRow(), ranksFilter = new BeatmapSearchScoreFilterRow(),
playedFilter = new BeatmapSearchFilterRow<SearchPlayed>(@"Played") playedFilter = new BeatmapSearchFilterRow<SearchPlayed>(@"Played"),
explicitContentFilter = new BeatmapSearchFilterRow<SearchExplicit>(@"Explicit Content"),
} }
} }
} }
@ -136,10 +141,18 @@ namespace osu.Game.Overlays.BeatmapListing
categoryFilter.Current.Value = SearchCategory.Leaderboard; categoryFilter.Current.Value = SearchCategory.Leaderboard;
} }
private IBindable<bool> allowExplicitContent;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider) private void load(OverlayColourProvider colourProvider, OsuConfigManager config)
{ {
background.Colour = colourProvider.Dark6; background.Colour = colourProvider.Dark6;
allowExplicitContent = config.GetBindable<bool>(OsuSetting.ShowOnlineExplicitContent);
allowExplicitContent.BindValueChanged(allow =>
{
ExplicitContent.Value = allow.NewValue ? SearchExplicit.Show : SearchExplicit.Hide;
}, true);
} }
public void TakeFocus() => textBox.TakeFocus(); public void TakeFocus() => textBox.TakeFocus();

View File

@ -14,6 +14,7 @@ using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.BeatmapSet;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -24,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
private const float horizontal_padding = 10; private const float horizontal_padding = 10;
private const float vertical_padding = 5; private const float vertical_padding = 5;
private FillFlowContainer bottomPanel, statusContainer; private FillFlowContainer bottomPanel, statusContainer, titleContainer;
private PlayButton playButton; private PlayButton playButton;
private Box progressBar; private Box progressBar;
@ -73,13 +74,21 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = horizontal_padding, Right = horizontal_padding }, Padding = new MarginPadding { Left = horizontal_padding, Right = horizontal_padding },
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Children = new[] Children = new Drawable[]
{
titleContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{ {
new OsuSpriteText new OsuSpriteText
{ {
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)), Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true) Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
}, },
}
},
new OsuSpriteText new OsuSpriteText
{ {
Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)), Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)),
@ -194,6 +203,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
}, },
}); });
if (SetInfo.OnlineInfo?.HasExplicitContent ?? false)
{
titleContainer.Add(new ExplicitContentBeatmapPill
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Margin = new MarginPadding { Left = 10f, Top = 2f },
});
}
if (SetInfo.OnlineInfo?.HasVideo ?? false) if (SetInfo.OnlineInfo?.HasVideo ?? false)
{ {
statusContainer.Add(new IconPill(FontAwesome.Solid.Film)); statusContainer.Add(new IconPill(FontAwesome.Solid.Film));

View File

@ -14,6 +14,7 @@ using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.BeatmapSet;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -26,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
private const float vertical_padding = 5; private const float vertical_padding = 5;
private const float height = 70; private const float height = 70;
private FillFlowContainer statusContainer; private FillFlowContainer statusContainer, titleContainer;
protected BeatmapPanelDownloadButton DownloadButton; protected BeatmapPanelDownloadButton DownloadButton;
private PlayButton playButton; private PlayButton playButton;
private Box progressBar; private Box progressBar;
@ -97,12 +98,20 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Children = new Drawable[] Children = new Drawable[]
{
titleContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{ {
new OsuSpriteText new OsuSpriteText
{ {
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)), Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true) Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
}, },
}
},
new OsuSpriteText new OsuSpriteText
{ {
Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)), Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)),
@ -208,6 +217,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
}, },
}); });
if (SetInfo.OnlineInfo?.HasExplicitContent ?? false)
{
titleContainer.Add(new ExplicitContentBeatmapPill
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Margin = new MarginPadding { Left = 10f, Top = 2f },
});
}
if (SetInfo.OnlineInfo?.HasVideo ?? false) if (SetInfo.OnlineInfo?.HasVideo ?? false)
{ {
statusContainer.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) }); statusContainer.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) });

View File

@ -0,0 +1,11 @@
// 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.
namespace osu.Game.Overlays.BeatmapListing
{
public enum SearchExplicit
{
Hide,
Show
}
}

View File

@ -0,0 +1,45 @@
// 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.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays.BeatmapSet
{
public class ExplicitContentBeatmapPill : CompositeDrawable
{
public ExplicitContentBeatmapPill()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, OverlayColourProvider colourProvider)
{
InternalChild = new CircularContainer
{
Masking = true,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider?.Background5 ?? colours.Gray2,
},
new OsuSpriteText
{
Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f },
Text = "EXPLICIT",
Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold),
Colour = OverlayColourProvider.Orange.Colour2,
}
}
};
}
}
}

View File

@ -34,6 +34,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Box coverGradient; private readonly Box coverGradient;
private readonly OsuSpriteText title, artist; private readonly OsuSpriteText title, artist;
private readonly AuthorInfo author; private readonly AuthorInfo author;
private readonly ExplicitContentBeatmapPill explicitContentPill;
private readonly FillFlowContainer downloadButtonsContainer; private readonly FillFlowContainer downloadButtonsContainer;
private readonly BeatmapAvailability beatmapAvailability; private readonly BeatmapAvailability beatmapAvailability;
private readonly BeatmapSetOnlineStatusPill onlineStatusPill; private readonly BeatmapSetOnlineStatusPill onlineStatusPill;
@ -144,8 +145,15 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Left = 3, Bottom = 4 }, // To better lineup with the font Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font
}, },
explicitContentPill = new ExplicitContentBeatmapPill
{
Alpha = 0f,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Left = 10, Bottom = 4 },
}
} }
}, },
artist = new OsuSpriteText artist = new OsuSpriteText
@ -253,6 +261,8 @@ namespace osu.Game.Overlays.BeatmapSet
title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty; title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty;
artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty; artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty;
explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0;
onlineStatusPill.FadeIn(500, Easing.OutQuint); onlineStatusPill.FadeIn(500, Easing.OutQuint);
onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status;

View File

@ -11,11 +11,23 @@ namespace osu.Game.Overlays
{ {
private readonly OverlayColourScheme colourScheme; private readonly OverlayColourScheme colourScheme;
public static OverlayColourProvider Red { get; } = new OverlayColourProvider(OverlayColourScheme.Red);
public static OverlayColourProvider Pink { get; } = new OverlayColourProvider(OverlayColourScheme.Pink);
public static OverlayColourProvider Orange { get; } = new OverlayColourProvider(OverlayColourScheme.Orange);
public static OverlayColourProvider Green { get; } = new OverlayColourProvider(OverlayColourScheme.Green);
public static OverlayColourProvider Purple { get; } = new OverlayColourProvider(OverlayColourScheme.Purple);
public static OverlayColourProvider Blue { get; } = new OverlayColourProvider(OverlayColourScheme.Blue);
public OverlayColourProvider(OverlayColourScheme colourScheme) public OverlayColourProvider(OverlayColourScheme colourScheme)
{ {
this.colourScheme = colourScheme; this.colourScheme = colourScheme;
} }
public Color4 Colour1 => getColour(1, 0.7f);
public Color4 Colour2 => getColour(0.8f, 0.6f);
public Color4 Colour3 => getColour(0.6f, 0.5f);
public Color4 Colour4 => getColour(0.4f, 0.3f);
public Color4 Highlight1 => getColour(1, 0.7f); public Color4 Highlight1 => getColour(1, 0.7f);
public Color4 Content1 => getColour(0.4f, 1); public Color4 Content1 => getColour(0.4f, 1);
public Color4 Content2 => getColour(0.4f, 0.9f); public Color4 Content2 => getColour(0.4f, 0.9f);

View File

@ -33,6 +33,12 @@ namespace osu.Game.Overlays.Settings.Sections.Online
Keywords = new[] { "spectator" }, Keywords = new[] { "spectator" },
Current = config.GetBindable<bool>(OsuSetting.AutomaticallyDownloadWhenSpectating), Current = config.GetBindable<bool>(OsuSetting.AutomaticallyDownloadWhenSpectating),
}, },
new SettingsCheckbox
{
LabelText = "Show explicit content in search results",
Keywords = new[] { "nsfw", "18+", "offensive" },
Current = config.GetBindable<bool>(OsuSetting.ShowOnlineExplicitContent),
}
}; };
} }
} }

View File

@ -70,6 +70,11 @@ namespace osu.Game.Overlays.Settings.Sections
Current = config.GetBindable<bool>(OsuSetting.BeatmapSkins) Current = config.GetBindable<bool>(OsuSetting.BeatmapSkins)
}, },
new SettingsCheckbox new SettingsCheckbox
{
LabelText = "Beatmap colours",
Current = config.GetBindable<bool>(OsuSetting.BeatmapColours)
},
new SettingsCheckbox
{ {
LabelText = "Beatmap hitsounds", LabelText = "Beatmap hitsounds",
Current = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds) Current = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds)

View File

@ -138,6 +138,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
scrollToTrackTime(); scrollToTrackTime();
} }
protected override bool OnScroll(ScrollEvent e)
{
// if this is not a precision scroll event, let the editor handle the seek itself (for snapping support)
if (!e.AltPressed && !e.IsPrecise)
return false;
return base.OnScroll(e);
}
protected override void UpdateAfterChildren() protected override void UpdateAfterChildren()
{ {
base.UpdateAfterChildren(); base.UpdateAfterChildren();

View File

@ -103,7 +103,7 @@ namespace osu.Game.Screens.Edit.Setup
return Task.CompletedTask; return Task.CompletedTask;
} }
Task ICanAcceptFiles.Import(Stream stream, string filename) => throw new NotImplementedException(); Task ICanAcceptFiles.Import(params ImportTask[] tasks) => throw new NotImplementedException();
protected override void LoadComplete() protected override void LoadComplete()
{ {

View File

@ -23,6 +23,7 @@ using osu.Game.Online;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Overlays.BeatmapListing.Panels;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
@ -41,6 +42,7 @@ namespace osu.Game.Screens.OnlinePlay
private Container difficultyIconContainer; private Container difficultyIconContainer;
private LinkFlowContainer beatmapText; private LinkFlowContainer beatmapText;
private LinkFlowContainer authorText; private LinkFlowContainer authorText;
private ExplicitContentBeatmapPill explicitContentPill;
private ModDisplay modDisplay; private ModDisplay modDisplay;
private readonly Bindable<BeatmapInfo> beatmap = new Bindable<BeatmapInfo>(); private readonly Bindable<BeatmapInfo> beatmap = new Bindable<BeatmapInfo>();
@ -116,6 +118,9 @@ namespace osu.Game.Screens.OnlinePlay
authorText.AddUserLink(Item.Beatmap.Value?.Metadata.Author); authorText.AddUserLink(Item.Beatmap.Value?.Metadata.Author);
} }
bool hasExplicitContent = Item.Beatmap.Value.BeatmapSet.OnlineInfo?.HasExplicitContent == true;
explicitContentPill.Alpha = hasExplicitContent ? 1 : 0;
modDisplay.Current.Value = requiredMods.ToArray(); modDisplay.Current.Value = requiredMods.ToArray();
} }
@ -165,15 +170,33 @@ namespace osu.Game.Screens.OnlinePlay
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
Spacing = new Vector2(15, 0), Spacing = new Vector2(10f, 0),
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0),
Children = new Drawable[] Children = new Drawable[]
{ {
authorText = new LinkFlowContainer { AutoSizeAxes = Axes.Both }, authorText = new LinkFlowContainer { AutoSizeAxes = Axes.Both },
modDisplay = new ModDisplay explicitContentPill = new ExplicitContentBeatmapPill
{
Alpha = 0f,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Margin = new MarginPadding { Top = 3f },
}
},
},
new Container
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Child = modDisplay = new ModDisplay
{
Scale = new Vector2(0.4f), Scale = new Vector2(0.4f),
DisplayUnrankedText = false, DisplayUnrankedText = false,
ExpansionMode = ExpansionMode.AlwaysExpanded ExpansionMode = ExpansionMode.AlwaysExpanded
@ -183,6 +206,7 @@ namespace osu.Game.Screens.OnlinePlay
} }
} }
} }
}
}, },
new FillFlowContainer new FillFlowContainer
{ {

View File

@ -14,6 +14,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
private readonly PlayerSliderBar<double> blurSliderBar; private readonly PlayerSliderBar<double> blurSliderBar;
private readonly PlayerCheckbox showStoryboardToggle; private readonly PlayerCheckbox showStoryboardToggle;
private readonly PlayerCheckbox beatmapSkinsToggle; private readonly PlayerCheckbox beatmapSkinsToggle;
private readonly PlayerCheckbox beatmapColorsToggle;
private readonly PlayerCheckbox beatmapHitsoundsToggle; private readonly PlayerCheckbox beatmapHitsoundsToggle;
public VisualSettings() public VisualSettings()
@ -43,6 +44,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
}, },
showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboard / Video" }, showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboard / Video" },
beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" }, beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" },
beatmapColorsToggle = new PlayerCheckbox { LabelText = "Beatmap colours" },
beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" } beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" }
}; };
} }
@ -54,6 +56,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
blurSliderBar.Current = config.GetBindable<double>(OsuSetting.BlurLevel); blurSliderBar.Current = config.GetBindable<double>(OsuSetting.BlurLevel);
showStoryboardToggle.Current = config.GetBindable<bool>(OsuSetting.ShowStoryboard); showStoryboardToggle.Current = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
beatmapSkinsToggle.Current = config.GetBindable<bool>(OsuSetting.BeatmapSkins); beatmapSkinsToggle.Current = config.GetBindable<bool>(OsuSetting.BeatmapSkins);
beatmapColorsToggle.Current = config.GetBindable<bool>(OsuSetting.BeatmapColours);
beatmapHitsoundsToggle.Current = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds); beatmapHitsoundsToggle.Current = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds);
} }
} }

View File

@ -15,6 +15,7 @@ namespace osu.Game.Skinning
public class BeatmapSkinProvidingContainer : SkinProvidingContainer public class BeatmapSkinProvidingContainer : SkinProvidingContainer
{ {
private Bindable<bool> beatmapSkins; private Bindable<bool> beatmapSkins;
private Bindable<bool> beatmapColours;
private Bindable<bool> beatmapHitsounds; private Bindable<bool> beatmapHitsounds;
protected override bool AllowConfigurationLookup protected override bool AllowConfigurationLookup
@ -28,6 +29,17 @@ namespace osu.Game.Skinning
} }
} }
protected override bool AllowColourLookup
{
get
{
if (beatmapColours == null)
throw new InvalidOperationException($"{nameof(BeatmapSkinProvidingContainer)} needs to be loaded before being consumed.");
return beatmapColours.Value;
}
}
protected override bool AllowDrawableLookup(ISkinComponent component) protected override bool AllowDrawableLookup(ISkinComponent component)
{ {
if (beatmapSkins == null) if (beatmapSkins == null)
@ -62,6 +74,7 @@ namespace osu.Game.Skinning
var config = parent.Get<OsuConfigManager>(); var config = parent.Get<OsuConfigManager>();
beatmapSkins = config.GetBindable<bool>(OsuSetting.BeatmapSkins); beatmapSkins = config.GetBindable<bool>(OsuSetting.BeatmapSkins);
beatmapColours = config.GetBindable<bool>(OsuSetting.BeatmapColours);
beatmapHitsounds = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds); beatmapHitsounds = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds);
return base.CreateChildDependencies(parent); return base.CreateChildDependencies(parent);
@ -71,6 +84,7 @@ namespace osu.Game.Skinning
private void load() private void load()
{ {
beatmapSkins.BindValueChanged(_ => TriggerSourceChanged()); beatmapSkins.BindValueChanged(_ => TriggerSourceChanged());
beatmapColours.BindValueChanged(_ => TriggerSourceChanged());
beatmapHitsounds.BindValueChanged(_ => TriggerSourceChanged()); beatmapHitsounds.BindValueChanged(_ => TriggerSourceChanged());
} }
} }

View File

@ -378,8 +378,10 @@ namespace osu.Game.Skinning
// kind of wasteful that we throw this away, but should do for now. // kind of wasteful that we throw this away, but should do for now.
if (createDrawable() != null) if (createDrawable() != null)
{ {
if (Configuration.LegacyVersion > 1) var particle = getParticleTexture(resultComponent.Component);
return new LegacyJudgementPieceNew(resultComponent.Component, createDrawable, getParticleTexture(resultComponent.Component));
if (particle != null)
return new LegacyJudgementPieceNew(resultComponent.Component, createDrawable, particle);
else else
return new LegacyJudgementPieceOld(resultComponent.Component, createDrawable); return new LegacyJudgementPieceOld(resultComponent.Component, createDrawable);
} }

View File

@ -32,6 +32,8 @@ namespace osu.Game.Skinning
protected virtual bool AllowConfigurationLookup => true; protected virtual bool AllowConfigurationLookup => true;
protected virtual bool AllowColourLookup => true;
public SkinProvidingContainer(ISkin skin) public SkinProvidingContainer(ISkin skin)
{ {
this.skin = skin; this.skin = skin;
@ -68,7 +70,20 @@ namespace osu.Game.Skinning
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
{ {
if (AllowConfigurationLookup && skin != null) if (skin != null)
{
if (lookup is GlobalSkinColours || lookup is SkinCustomColourLookup)
return lookupWithFallback<TLookup, TValue>(lookup, AllowColourLookup);
return lookupWithFallback<TLookup, TValue>(lookup, AllowConfigurationLookup);
}
return fallbackSource?.GetConfig<TLookup, TValue>(lookup);
}
private IBindable<TValue> lookupWithFallback<TLookup, TValue>(TLookup lookup, bool canUseSkinLookup)
{
if (canUseSkinLookup)
{ {
var bindable = skin.GetConfig<TLookup, TValue>(lookup); var bindable = skin.GetConfig<TLookup, TValue>(lookup);
if (bindable != null) if (bindable != null)

View File

@ -0,0 +1,169 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.IO.Stores;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
using osuTK.Graphics;
namespace osu.Game.Tests.Beatmaps
{
public class LegacyBeatmapSkinColourTest : ScreenTestScene
{
protected readonly Bindable<bool> BeatmapSkins = new Bindable<bool>();
protected readonly Bindable<bool> BeatmapColours = new Bindable<bool>();
protected ExposedPlayer TestPlayer;
protected WorkingBeatmap TestBeatmap;
public virtual void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, true, userHasCustomColours);
public virtual void TestBeatmapComboColoursOverride(bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, false, true);
public virtual void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, false, false);
public virtual void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, false);
public virtual void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, true);
protected virtual void ConfigureTest(bool useBeatmapSkin, bool useBeatmapColours, bool userHasCustomColours)
{
configureSettings(useBeatmapSkin, useBeatmapColours);
AddStep($"load {(((CustomSkinWorkingBeatmap)TestBeatmap).HasColours ? "coloured " : "")} beatmap", () => TestPlayer = LoadBeatmap(userHasCustomColours));
AddUntilStep("wait for player load", () => TestPlayer.IsLoaded);
}
private void configureSettings(bool beatmapSkins, bool beatmapColours)
{
AddStep($"{(beatmapSkins ? "enable" : "disable")} beatmap skins", () =>
{
BeatmapSkins.Value = beatmapSkins;
});
AddStep($"{(beatmapColours ? "enable" : "disable")} beatmap colours", () =>
{
BeatmapColours.Value = beatmapColours;
});
}
protected virtual ExposedPlayer LoadBeatmap(bool userHasCustomColours)
{
ExposedPlayer player;
Beatmap.Value = TestBeatmap;
LoadScreen(player = CreateTestPlayer(userHasCustomColours));
return player;
}
protected virtual ExposedPlayer CreateTestPlayer(bool userHasCustomColours) => new ExposedPlayer(userHasCustomColours);
protected class ExposedPlayer : Player
{
protected readonly bool UserHasCustomColours;
public ExposedPlayer(bool userHasCustomColours)
: base(new PlayerConfiguration
{
AllowPause = false,
ShowResults = false,
})
{
UserHasCustomColours = userHasCustomColours;
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
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;
}
protected class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
{
public readonly bool HasColours;
public CustomSkinWorkingBeatmap(IBeatmap beatmap, AudioManager audio, bool hasColours)
: base(beatmap, null, null, audio)
{
HasColours = hasColours;
}
protected override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, HasColours);
}
protected class TestBeatmapSkin : LegacyBeatmapSkin
{
public static Color4[] Colours { get; } =
{
new Color4(50, 100, 150, 255),
new Color4(40, 80, 120, 255),
};
public static readonly Color4 HYPER_DASH_COLOUR = Color4.DarkBlue;
public static readonly Color4 HYPER_DASH_AFTER_IMAGE_COLOUR = Color4.DarkCyan;
public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.DarkGoldenrod;
public TestBeatmapSkin(BeatmapInfo beatmap, bool hasColours)
: base(beatmap, new ResourceStore<byte[]>(), null)
{
if (hasColours)
{
Configuration.AddComboColours(Colours);
Configuration.CustomColours.Add("HyperDash", HYPER_DASH_COLOUR);
Configuration.CustomColours.Add("HyperDashAfterImage", HYPER_DASH_AFTER_IMAGE_COLOUR);
Configuration.CustomColours.Add("HyperDashFruit", HYPER_DASH_FRUIT_COLOUR);
}
}
}
protected class TestSkin : LegacySkin, ISkinSource
{
public static Color4[] Colours { get; } =
{
new Color4(150, 100, 50, 255),
new Color4(20, 20, 20, 255),
};
public static readonly Color4 HYPER_DASH_COLOUR = Color4.LightBlue;
public static readonly Color4 HYPER_DASH_AFTER_IMAGE_COLOUR = Color4.LightCoral;
public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.LightCyan;
public TestSkin(bool hasCustomColours)
: base(new SkinInfo(), new ResourceStore<byte[]>(), null, string.Empty)
{
if (hasCustomColours)
{
Configuration.AddComboColours(Colours);
Configuration.CustomColours.Add("HyperDash", HYPER_DASH_COLOUR);
Configuration.CustomColours.Add("HyperDashAfterImage", HYPER_DASH_AFTER_IMAGE_COLOUR);
Configuration.CustomColours.Add("HyperDashFruit", HYPER_DASH_FRUIT_COLOUR);
}
}
public event Action SourceChanged
{
add { }
remove { }
}
}
}
}

View File

@ -32,6 +32,7 @@ namespace osu.Game.Tests.Beatmaps
BeatmapInfo.BeatmapSet.Files = new List<BeatmapSetFileInfo>(); BeatmapInfo.BeatmapSet.Files = new List<BeatmapSetFileInfo>();
BeatmapInfo.BeatmapSet.Beatmaps = new List<BeatmapInfo> { BeatmapInfo }; BeatmapInfo.BeatmapSet.Beatmaps = new List<BeatmapInfo> { BeatmapInfo };
BeatmapInfo.Length = 75000; BeatmapInfo.Length = 75000;
BeatmapInfo.OnlineInfo = new BeatmapOnlineInfo();
BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo
{ {
Status = BeatmapSetOnlineStatus.Ranked, Status = BeatmapSetOnlineStatus.Ranked,

View File

@ -26,7 +26,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2021.106.0" /> <PackageReference Include="ppy.osu.Framework" Version="2021.115.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
<PackageReference Include="Sentry" Version="2.1.8" /> <PackageReference Include="Sentry" Version="2.1.8" />
<PackageReference Include="SharpCompress" Version="0.26.0" /> <PackageReference Include="SharpCompress" Version="0.26.0" />

View File

@ -70,7 +70,7 @@
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.106.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2021.115.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
</ItemGroup> </ItemGroup>
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) --> <!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
@ -88,7 +88,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2021.106.0" /> <PackageReference Include="ppy.osu.Framework" Version="2021.115.1" />
<PackageReference Include="SharpCompress" Version="0.26.0" /> <PackageReference Include="SharpCompress" Version="0.26.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />

View File

@ -57,7 +57,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
Directory.Build.props = Directory.Build.props Directory.Build.props = Directory.Build.props
global.json = global.json
osu.Android.props = osu.Android.props osu.Android.props = osu.Android.props
osu.iOS.props = osu.iOS.props osu.iOS.props = osu.iOS.props
CodeAnalysis\osu.ruleset = CodeAnalysis\osu.ruleset CodeAnalysis\osu.ruleset = CodeAnalysis\osu.ruleset