mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 09:27:29 +08:00
Merge branch 'master' into beatmap-colour-setting
This commit is contained in:
commit
1364de1166
@ -1,21 +1,68 @@
|
||||
clone_depth: 1
|
||||
version: '{build}'
|
||||
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
|
||||
skip_non_tags: true
|
||||
configuration: Release
|
||||
build:
|
||||
project: build\Desktop.proj # Skipping Xamarin Release that's slow and covered by fastlane
|
||||
parallel: true
|
||||
verbosity: minimal
|
||||
publish_nuget: true
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- job_name: osu-game
|
||||
- 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:
|
||||
- provider: Environment
|
||||
name: nuget
|
||||
name: nuget
|
@ -48,10 +48,6 @@ desc 'Deploy to play store'
|
||||
|
||||
desc 'Compile the project'
|
||||
lane :build do |options|
|
||||
nuget_restore(
|
||||
project_path: 'osu.sln'
|
||||
)
|
||||
|
||||
souyuz(
|
||||
build_configuration: 'Release',
|
||||
solution_path: 'osu.sln',
|
||||
@ -107,10 +103,6 @@ platform :ios do
|
||||
|
||||
desc 'Compile the project'
|
||||
lane :build do
|
||||
nuget_restore(
|
||||
project_path: 'osu.sln'
|
||||
)
|
||||
|
||||
souyuz(
|
||||
platform: "ios",
|
||||
plist_path: "osu.iOS/Info.plist"
|
||||
|
10
global.json
10
global.json
@ -1,10 +0,0 @@
|
||||
{
|
||||
"sdk": {
|
||||
"allowPrerelease": false,
|
||||
"rollForward": "minor",
|
||||
"version": "3.1.100"
|
||||
},
|
||||
"msbuild-sdks": {
|
||||
"Microsoft.Build.Traversal": "3.0.2"
|
||||
}
|
||||
}
|
@ -52,6 +52,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<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>
|
||||
</Project>
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -12,13 +13,14 @@ using Android.OS;
|
||||
using Android.Provider;
|
||||
using Android.Views;
|
||||
using osu.Framework.Android;
|
||||
using osu.Game.Database;
|
||||
|
||||
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)]
|
||||
[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.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" })]
|
||||
public class OsuGameActivity : AndroidGameActivity
|
||||
{
|
||||
@ -54,43 +56,59 @@ namespace osu.Android
|
||||
{
|
||||
case Intent.ActionDefault:
|
||||
if (intent.Scheme == ContentResolver.SchemeContent)
|
||||
handleImportFromUri(intent.Data);
|
||||
handleImportFromUris(intent.Data);
|
||||
else if (osu_url_schemes.Contains(intent.Scheme))
|
||||
game.HandleLink(intent.DataString);
|
||||
break;
|
||||
|
||||
case Intent.ActionSend:
|
||||
case Intent.ActionSendMultiple:
|
||||
{
|
||||
var content = intent.ClipData?.GetItemAt(0);
|
||||
if (content != null)
|
||||
handleImportFromUri(content.Uri);
|
||||
var uris = new List<Uri>();
|
||||
for (int i = 0; i < intent.ClipData?.ItemCount; i++)
|
||||
{
|
||||
var content = intent.ClipData?.GetItemAt(i);
|
||||
if (content != null)
|
||||
uris.Add(content.Uri);
|
||||
}
|
||||
handleImportFromUris(uris.ToArray());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleImportFromUri(Uri uri) => Task.Factory.StartNew(async () =>
|
||||
private void handleImportFromUris(params Uri[] uris) => Task.Factory.StartNew(async () =>
|
||||
{
|
||||
// there are more performant overloads of this method, but this one is the most backwards-compatible
|
||||
// (dates back to API 1).
|
||||
var cursor = ContentResolver?.Query(uri, null, null, null, null);
|
||||
var tasks = new List<ImportTask>();
|
||||
|
||||
if (cursor == null)
|
||||
return;
|
||||
await Task.WhenAll(uris.Select(async uri =>
|
||||
{
|
||||
// there are more performant overloads of this method, but this one is the most backwards-compatible
|
||||
// (dates back to API 1).
|
||||
var cursor = ContentResolver?.Query(uri, null, null, null, null);
|
||||
|
||||
cursor.MoveToFirst();
|
||||
if (cursor == null)
|
||||
return;
|
||||
|
||||
var filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName);
|
||||
string filename = cursor.GetString(filenameColumn);
|
||||
cursor.MoveToFirst();
|
||||
|
||||
// SharpCompress requires archive streams to be seekable, which the stream opened by
|
||||
// OpenInputStream() seems to not necessarily be.
|
||||
// copy to an arbitrary-access memory stream to be able to proceed with the import.
|
||||
var copy = new MemoryStream();
|
||||
using (var stream = ContentResolver.OpenInputStream(uri))
|
||||
await stream.CopyToAsync(copy);
|
||||
var filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName);
|
||||
string filename = cursor.GetString(filenameColumn);
|
||||
|
||||
await game.Import(copy, filename);
|
||||
// SharpCompress requires archive streams to be seekable, which the stream opened by
|
||||
// OpenInputStream() seems to not necessarily be.
|
||||
// copy to an arbitrary-access memory stream to be able to proceed with the import.
|
||||
var copy = new MemoryStream();
|
||||
using (var stream = ContentResolver.OpenInputStream(uri))
|
||||
await stream.CopyToAsync(copy);
|
||||
|
||||
lock (tasks)
|
||||
{
|
||||
tasks.Add(new ImportTask(copy, filename));
|
||||
}
|
||||
}));
|
||||
|
||||
await game.Import(tasks.ToArray());
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Win32;
|
||||
using osu.Desktop.Overlays;
|
||||
@ -56,16 +57,16 @@ namespace osu.Desktop
|
||||
|
||||
string stableInstallPath;
|
||||
|
||||
try
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
||||
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", "");
|
||||
try
|
||||
{
|
||||
stableInstallPath = getStableInstallPathFromRegistry();
|
||||
|
||||
if (checkExists(stableInstallPath))
|
||||
return stableInstallPath;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stableInstallPath) && checkExists(stableInstallPath))
|
||||
return stableInstallPath;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
|
||||
@ -79,6 +80,13 @@ namespace osu.Desktop
|
||||
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()
|
||||
{
|
||||
switch (RuntimeInfo.OS)
|
||||
|
@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup Label="Project">
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
@ -9,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
||||
|
@ -5,6 +5,13 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Description>catch the fruit. to the beat.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Nuget">
|
||||
<Title>osu!catch (ruleset)</Title>
|
||||
<PackageId>ppy.osu.Game.Rulesets.Catch</PackageId>
|
||||
<IsPackable>true</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -9,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
||||
|
@ -5,6 +5,13 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Description>smash the keys. to the beat.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Nuget">
|
||||
<Title>osu!mania (ruleset)</Title>
|
||||
<PackageId>ppy.osu.Game.Rulesets.Mania</PackageId>
|
||||
<IsPackable>true</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -1,9 +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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -34,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase(true)]
|
||||
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("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0));
|
||||
}
|
||||
@ -55,7 +57,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
var spinner = new Spinner
|
||||
{
|
||||
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 });
|
||||
|
@ -9,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
|
||||
|
@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
if (Attributes.ApproachRate > 10.33)
|
||||
approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33);
|
||||
else if (Attributes.ApproachRate < 8.0)
|
||||
approachRateFactor += 0.1 * (8.0 - Attributes.ApproachRate);
|
||||
approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate);
|
||||
|
||||
aimValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0));
|
||||
|
||||
|
@ -5,6 +5,13 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Description>click the circles. to the beat.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Nuget">
|
||||
<Title>osu! (ruleset)</Title>
|
||||
<PackageId>ppy.osu.Game.Rulesets.Osu</PackageId>
|
||||
<IsPackable>true</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -9,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||
|
@ -5,6 +5,13 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Description>bash the drum. to the beat.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Nuget">
|
||||
<Title>osu!taiko (ruleset)</Title>
|
||||
<PackageId>ppy.osu.Game.Rulesets.Taiko</PackageId>
|
||||
<IsPackable>true</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -244,7 +244,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
|
||||
{
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
|
||||
public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.MouseLeft, TestAction.Down),
|
||||
};
|
||||
|
@ -179,7 +179,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
|
||||
{
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
|
||||
public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.MouseLeft, TestAction.Down),
|
||||
};
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
@ -74,6 +75,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
switch (args.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
Debug.Assert(args.NewItems != null);
|
||||
|
||||
foreach (int user in args.NewItems)
|
||||
{
|
||||
if (user == api.LocalUser.Value.Id)
|
||||
@ -83,6 +86,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
Debug.Assert(args.OldItems != null);
|
||||
|
||||
foreach (int user in args.OldItems)
|
||||
{
|
||||
if (user == api.LocalUser.Value.Id)
|
||||
@ -298,7 +303,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
|
||||
{
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
|
||||
public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.MouseLeft, TestAction.Down),
|
||||
};
|
||||
|
@ -222,6 +222,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
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)
|
||||
=> AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType<OsuRearrangeableListItem<PlaylistItem>>().ElementAt(index), offset));
|
||||
|
||||
|
@ -143,6 +143,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
RoomManager =
|
||||
{
|
||||
TimeBetweenListingPolls = { Value = 1 },
|
||||
TimeBetweenSelectionPolls = { Value = 1 }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -235,6 +235,17 @@ namespace osu.Game.Tests.Visual.Online
|
||||
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]
|
||||
public void TestHide()
|
||||
{
|
||||
|
@ -99,13 +99,16 @@ namespace osu.Game.Tests.Visual.Online
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
var normal = CreateWorkingBeatmap(Ruleset.Value).BeatmapSetInfo;
|
||||
var normal = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
|
||||
normal.OnlineInfo.HasVideo = true;
|
||||
normal.OnlineInfo.HasStoryboard = true;
|
||||
|
||||
var undownloadable = getUndownloadableBeatmapSet();
|
||||
var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets);
|
||||
|
||||
var explicitMap = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
|
||||
explicitMap.OnlineInfo.HasExplicitContent = true;
|
||||
|
||||
Child = new BasicScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -121,9 +124,11 @@ namespace osu.Game.Tests.Visual.Online
|
||||
new GridBeatmapPanel(normal),
|
||||
new GridBeatmapPanel(undownloadable),
|
||||
new GridBeatmapPanel(manyDifficulties),
|
||||
new GridBeatmapPanel(explicitMap),
|
||||
new ListBeatmapPanel(normal),
|
||||
new ListBeatmapPanel(undownloadable),
|
||||
new ListBeatmapPanel(manyDifficulties),
|
||||
new ListBeatmapPanel(explicitMap)
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
@ -19,9 +20,18 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
[Cached]
|
||||
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 ruleset;
|
||||
@ -31,30 +41,34 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
OsuSpriteText extra;
|
||||
OsuSpriteText ranks;
|
||||
OsuSpriteText played;
|
||||
OsuSpriteText explicitMap;
|
||||
|
||||
Add(control = new BeatmapListingSearchControl
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
|
||||
Add(new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 5),
|
||||
Children = new Drawable[]
|
||||
control = new BeatmapListingSearchControl
|
||||
{
|
||||
query = new OsuSpriteText(),
|
||||
ruleset = new OsuSpriteText(),
|
||||
category = new OsuSpriteText(),
|
||||
genre = new OsuSpriteText(),
|
||||
language = new OsuSpriteText(),
|
||||
extra = new OsuSpriteText(),
|
||||
ranks = new OsuSpriteText(),
|
||||
played = new OsuSpriteText()
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
query = new OsuSpriteText(),
|
||||
ruleset = new OsuSpriteText(),
|
||||
category = new OsuSpriteText(),
|
||||
genre = new OsuSpriteText(),
|
||||
language = new OsuSpriteText(),
|
||||
extra = new OsuSpriteText(),
|
||||
ranks = new OsuSpriteText(),
|
||||
played = new OsuSpriteText(),
|
||||
explicitMap = new OsuSpriteText(),
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true);
|
||||
control.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.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.ExplicitContent.BindValueChanged(e => explicitMap.Text = $"Explicit Maps: {e.NewValue}", true);
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestCovers()
|
||||
@ -74,6 +89,22 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
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
|
||||
{
|
||||
OnlineInfo = new BeatmapSetOnlineInfo
|
||||
|
@ -10,7 +10,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
|
||||
|
@ -11,7 +11,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
||||
|
@ -260,17 +260,10 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
catch (BeatmapInvalidForRulesetException e)
|
||||
{
|
||||
// Conversion has failed for the given ruleset, so return the difficulty in the beatmap's default ruleset.
|
||||
|
||||
// Ensure the beatmap's default ruleset isn't the one already being converted to.
|
||||
// This shouldn't happen as it means something went seriously wrong, but if it does an endless loop should be avoided.
|
||||
if (rulesetInfo.Equals(beatmapInfo.Ruleset))
|
||||
{
|
||||
Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineBeatmapID} to the beatmap's default ruleset ({beatmapInfo.Ruleset}).");
|
||||
return new StarDifficulty();
|
||||
}
|
||||
|
||||
return GetAsync(new DifficultyCacheLookup(key.Beatmap, key.Beatmap.Ruleset, key.OrderedMods)).Result;
|
||||
return new StarDifficulty();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -31,6 +31,11 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
public BeatmapSetOnlineStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this beatmap set has explicit content.
|
||||
/// </summary>
|
||||
public bool HasExplicitContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this beatmap set has a background video.
|
||||
/// </summary>
|
||||
|
@ -60,6 +60,8 @@ namespace osu.Game.Configuration
|
||||
Set(OsuSetting.ExternalLinkWarning, true);
|
||||
Set(OsuSetting.PreferNoVideo, false);
|
||||
|
||||
Set(OsuSetting.ShowOnlineExplicitContent, false);
|
||||
|
||||
// Audio
|
||||
Set(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01);
|
||||
|
||||
@ -272,5 +274,6 @@ namespace osu.Game.Configuration
|
||||
EditorWaveformOpacity,
|
||||
DiscordRichPresence,
|
||||
AutomaticallyDownloadWhenSpectating,
|
||||
ShowOnlineExplicitContent,
|
||||
}
|
||||
}
|
||||
|
@ -115,13 +115,13 @@ namespace osu.Game.Database
|
||||
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 };
|
||||
|
||||
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)
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Game.Database
|
||||
@ -19,11 +18,10 @@ namespace osu.Game.Database
|
||||
Task Import(params string[] paths);
|
||||
|
||||
/// <summary>
|
||||
/// Import the provided stream as a simple item.
|
||||
/// Import the specified files from the given import tasks.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to import files from. Should be in a supported archive format.</param>
|
||||
/// <param name="filename">The filename of the archive being imported.</param>
|
||||
Task Import(Stream stream, string filename);
|
||||
/// <param name="tasks">The import tasks from which the files should be imported.</param>
|
||||
Task Import(params ImportTask[] tasks);
|
||||
|
||||
/// <summary>
|
||||
/// An array of accepted file extensions (in the standard format of ".abc").
|
||||
|
@ -135,6 +135,8 @@ namespace osu.Game.Database
|
||||
|
||||
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => new { b.RulesetID, b.Variant });
|
||||
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 });
|
||||
|
||||
|
@ -73,7 +73,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
TooltipText = "Downloading...";
|
||||
break;
|
||||
|
||||
case DownloadState.Downloaded:
|
||||
case DownloadState.Importing:
|
||||
background.FadeColour(colours.Yellow, 500, Easing.InOutExpo);
|
||||
TooltipText = "Importing";
|
||||
break;
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Input.Bindings
|
||||
|
||||
private KeyBindingStore store;
|
||||
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0);
|
||||
public override IEnumerable<IKeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance.
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Input.Bindings
|
||||
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[]
|
||||
{
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -42,6 +42,9 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
[JsonProperty(@"bpm")]
|
||||
private double bpm { get; set; }
|
||||
|
||||
[JsonProperty(@"nsfw")]
|
||||
private bool hasExplicitContent { get; set; }
|
||||
|
||||
[JsonProperty(@"video")]
|
||||
private bool hasVideo { get; set; }
|
||||
|
||||
@ -94,6 +97,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
FavouriteCount = favouriteCount,
|
||||
BPM = bpm,
|
||||
Status = Status,
|
||||
HasExplicitContent = hasExplicitContent,
|
||||
HasVideo = hasVideo,
|
||||
HasStoryboard = hasStoryboard,
|
||||
Submitted = submitted,
|
||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Online.API.Requests
|
||||
|
||||
public SearchPlayed Played { get; }
|
||||
|
||||
public SearchExplicit ExplicitContent { get; }
|
||||
|
||||
[CanBeNull]
|
||||
public IReadOnlyCollection<ScoreRank> Ranks { get; }
|
||||
|
||||
@ -50,7 +52,8 @@ namespace osu.Game.Online.API.Requests
|
||||
SearchLanguage language = SearchLanguage.Any,
|
||||
IReadOnlyCollection<SearchExtra> extra = 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.ruleset = ruleset;
|
||||
@ -64,6 +67,7 @@ namespace osu.Game.Online.API.Requests
|
||||
Extra = extra;
|
||||
Ranks = ranks;
|
||||
Played = played;
|
||||
ExplicitContent = explicitContent;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
@ -93,6 +97,8 @@ namespace osu.Game.Online.API.Requests
|
||||
if (Played != SearchPlayed.Any)
|
||||
req.AddParameter("played", Played.ToString().ToLowerInvariant());
|
||||
|
||||
req.AddParameter("nsfw", ExplicitContent == SearchExplicit.Show ? "true" : "false");
|
||||
|
||||
req.AddCursor(cursor);
|
||||
|
||||
return req;
|
||||
|
@ -7,7 +7,7 @@ namespace osu.Game.Online
|
||||
{
|
||||
NotDownloaded,
|
||||
Downloading,
|
||||
Downloaded,
|
||||
Importing,
|
||||
LocallyAvailable
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ namespace osu.Game.Online
|
||||
{
|
||||
if (attachedRequest.Progress == 1)
|
||||
{
|
||||
State.Value = DownloadState.Downloaded;
|
||||
State.Value = DownloadState.Importing;
|
||||
Progress.Value = 1;
|
||||
}
|
||||
else
|
||||
@ -125,7 +125,7 @@ namespace osu.Game.Online
|
||||
}
|
||||
}
|
||||
|
||||
private void onRequestSuccess(string _) => Schedule(() => State.Value = DownloadState.Downloaded);
|
||||
private void onRequestSuccess(string _) => Schedule(() => State.Value = DownloadState.Importing);
|
||||
|
||||
private void onRequestProgress(float progress) => Schedule(() => Progress.Value = progress);
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer
|
||||
{
|
||||
@ -47,6 +48,13 @@ namespace osu.Game.Online.Multiplayer
|
||||
/// <param name="state">The new state of the user.</param>
|
||||
Task UserStateChanged(int userId, MultiplayerUserState state);
|
||||
|
||||
/// <summary>
|
||||
/// Signals that a user in this room changed their beatmap availability state.
|
||||
/// </summary>
|
||||
/// <param name="userId">The ID of the user whose beatmap availability state has changed.</param>
|
||||
/// <param name="beatmapAvailability">The new beatmap availability state of the user.</param>
|
||||
Task UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability);
|
||||
|
||||
/// <summary>
|
||||
/// Signals that a match is to be started. This will *only* be sent to clients which are to begin loading at this point.
|
||||
/// </summary>
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer
|
||||
{
|
||||
@ -40,6 +41,12 @@ namespace osu.Game.Online.Multiplayer
|
||||
/// <exception cref="NotJoinedRoomException">If the user is not in a room.</exception>
|
||||
Task ChangeState(MultiplayerUserState newState);
|
||||
|
||||
/// <summary>
|
||||
/// Change the local user's availability state of the current beatmap set in joined room.
|
||||
/// </summary>
|
||||
/// <param name="newBeatmapAvailability">The proposed new beatmap availability state.</param>
|
||||
Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability);
|
||||
|
||||
/// <summary>
|
||||
/// As the host of a room, start the match.
|
||||
/// </summary>
|
||||
|
@ -14,6 +14,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer
|
||||
{
|
||||
@ -173,6 +174,14 @@ namespace osu.Game.Online.Multiplayer
|
||||
return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeState), newState);
|
||||
}
|
||||
|
||||
public override Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability)
|
||||
{
|
||||
if (!isConnected.Value)
|
||||
return Task.CompletedTask;
|
||||
|
||||
return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeBeatmapAvailability), newBeatmapAvailability);
|
||||
}
|
||||
|
||||
public override Task StartMatch()
|
||||
{
|
||||
if (!isConnected.Value)
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer
|
||||
@ -16,6 +17,11 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle;
|
||||
|
||||
/// <summary>
|
||||
/// The availability state of the current beatmap.
|
||||
/// </summary>
|
||||
public BeatmapAvailability BeatmapAvailability { get; set; } = BeatmapAvailability.LocallyAvailable();
|
||||
|
||||
public User? User { get; set; }
|
||||
|
||||
[JsonConstructor]
|
||||
|
@ -7,7 +7,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -52,6 +51,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
/// <summary>
|
||||
/// Whether the <see cref="StatefulMultiplayerClient"/> is currently connected.
|
||||
/// This is NOT thread safe and usage should be scheduled.
|
||||
/// </summary>
|
||||
public abstract IBindable<bool> IsConnected { get; }
|
||||
|
||||
@ -127,7 +127,8 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
Debug.Assert(Room != null);
|
||||
|
||||
var users = getRoomUsers();
|
||||
var users = await getRoomUsers();
|
||||
Debug.Assert(users != null);
|
||||
|
||||
await Task.WhenAll(users.Select(PopulateUser));
|
||||
|
||||
@ -227,6 +228,8 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
public abstract Task ChangeState(MultiplayerUserState newState);
|
||||
|
||||
public abstract Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability);
|
||||
|
||||
public abstract Task StartMatch();
|
||||
|
||||
Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state)
|
||||
@ -354,6 +357,27 @@ namespace osu.Game.Online.Multiplayer
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Task IMultiplayerClient.UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability)
|
||||
{
|
||||
if (Room == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
Scheduler.Add(() =>
|
||||
{
|
||||
var user = Room?.Users.SingleOrDefault(u => u.UserID == userId);
|
||||
|
||||
// errors here are not critical - beatmap availability state is mostly for display.
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
user.BeatmapAvailability = beatmapAvailability;
|
||||
|
||||
RoomUpdated?.Invoke();
|
||||
}, false);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Task IMultiplayerClient.LoadRequested()
|
||||
{
|
||||
if (Room == null)
|
||||
@ -413,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"/>).
|
||||
/// </summary>
|
||||
/// <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;
|
||||
|
||||
ManualResetEventSlim resetEvent = new ManualResetEventSlim();
|
||||
var tcs = new TaskCompletionSource<List<MultiplayerRoomUser>?>();
|
||||
|
||||
// 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,
|
||||
// let's just add a manual schedule for the non-scheduled usages instead.
|
||||
Scheduler.Add(() =>
|
||||
{
|
||||
users = Room?.Users.ToList();
|
||||
resetEvent.Set();
|
||||
var users = Room?.Users.ToList();
|
||||
tcs.SetResult(users);
|
||||
}, false);
|
||||
|
||||
resetEvent.Wait(100);
|
||||
|
||||
return users;
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
40
osu.Game/Online/Rooms/BeatmapAvailability.cs
Normal file
40
osu.Game/Online/Rooms/BeatmapAvailability.cs
Normal file
@ -0,0 +1,40 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.Rooms
|
||||
{
|
||||
/// <summary>
|
||||
/// The local availability information about a certain beatmap for the client.
|
||||
/// </summary>
|
||||
public class BeatmapAvailability : IEquatable<BeatmapAvailability>
|
||||
{
|
||||
/// <summary>
|
||||
/// The beatmap's availability state.
|
||||
/// </summary>
|
||||
public readonly DownloadState State;
|
||||
|
||||
/// <summary>
|
||||
/// The beatmap's downloading progress, null when not in <see cref="DownloadState.Downloading"/> state.
|
||||
/// </summary>
|
||||
public readonly double? DownloadProgress;
|
||||
|
||||
[JsonConstructor]
|
||||
private BeatmapAvailability(DownloadState state, double? downloadProgress = null)
|
||||
{
|
||||
State = state;
|
||||
DownloadProgress = downloadProgress;
|
||||
}
|
||||
|
||||
public static BeatmapAvailability NotDownloaded() => new BeatmapAvailability(DownloadState.NotDownloaded);
|
||||
public static BeatmapAvailability Downloading(double progress) => new BeatmapAvailability(DownloadState.Downloading, progress);
|
||||
public static BeatmapAvailability Importing() => new BeatmapAvailability(DownloadState.Importing);
|
||||
public static BeatmapAvailability LocallyAvailable() => new BeatmapAvailability(DownloadState.LocallyAvailable);
|
||||
|
||||
public bool Equals(BeatmapAvailability other) => other != null && State == other.State && DownloadProgress == other.DownloadProgress;
|
||||
|
||||
public override string ToString() => $"{string.Join(", ", State, $"{DownloadProgress:0.00%}")}";
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ using osu.Game.Screens.Select;
|
||||
using osu.Game.Updater;
|
||||
using osu.Game.Utils;
|
||||
using LogLevel = osu.Framework.Logging.LogLevel;
|
||||
using System.IO;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game
|
||||
{
|
||||
@ -438,10 +438,10 @@ namespace osu.Game
|
||||
}, 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.
|
||||
var importTask = new Task(async () => await base.Import(stream, filename));
|
||||
var importTask = new Task(async () => await base.Import(imports));
|
||||
|
||||
waitForReady(() => this, _ => importTask.Start());
|
||||
|
||||
|
@ -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();
|
||||
|
||||
foreach (var importer in fileImporters)
|
||||
var tasksPerExtension = tasks.GroupBy(t => Path.GetExtension(t.Path).ToLowerInvariant());
|
||||
await Task.WhenAll(tasksPerExtension.Select(taskGroup =>
|
||||
{
|
||||
if (importer.HandledExtensions.Contains(extension))
|
||||
await importer.Import(stream, Path.GetFileNameWithoutExtension(filename));
|
||||
}
|
||||
var importer = fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(taskGroup.Key));
|
||||
return importer?.Import(taskGroup.ToArray()) ?? Task.CompletedTask;
|
||||
}));
|
||||
}
|
||||
|
||||
public IEnumerable<string> HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions);
|
||||
|
@ -141,6 +141,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
searchControl.Extra.CollectionChanged += (_, __) => queueUpdateSearch();
|
||||
searchControl.Ranks.CollectionChanged += (_, __) => queueUpdateSearch();
|
||||
searchControl.Played.BindValueChanged(_ => queueUpdateSearch());
|
||||
searchControl.ExplicitContent.BindValueChanged(_ => queueUpdateSearch());
|
||||
|
||||
sortCriteria.BindValueChanged(_ => queueUpdateSearch());
|
||||
sortDirection.BindValueChanged(_ => queueUpdateSearch());
|
||||
@ -193,7 +194,8 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
searchControl.Language.Value,
|
||||
searchControl.Extra,
|
||||
searchControl.Ranks,
|
||||
searchControl.Played.Value);
|
||||
searchControl.Played.Value,
|
||||
searchControl.ExplicitContent.Value);
|
||||
|
||||
getSetsRequest.Success += response =>
|
||||
{
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osuTK.Graphics;
|
||||
@ -42,6 +43,8 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
|
||||
public Bindable<SearchPlayed> Played => playedFilter.Current;
|
||||
|
||||
public Bindable<SearchExplicit> ExplicitContent => explicitContentFilter.Current;
|
||||
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
{
|
||||
set
|
||||
@ -65,6 +68,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
private readonly BeatmapSearchMultipleSelectionFilterRow<SearchExtra> extraFilter;
|
||||
private readonly BeatmapSearchScoreFilterRow ranksFilter;
|
||||
private readonly BeatmapSearchFilterRow<SearchPlayed> playedFilter;
|
||||
private readonly BeatmapSearchFilterRow<SearchExplicit> explicitContentFilter;
|
||||
|
||||
private readonly Box background;
|
||||
private readonly UpdateableBeatmapSetCover beatmapCover;
|
||||
@ -125,7 +129,8 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
languageFilter = new BeatmapSearchFilterRow<SearchLanguage>(@"Language"),
|
||||
extraFilter = new BeatmapSearchMultipleSelectionFilterRow<SearchExtra>(@"Extra"),
|
||||
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;
|
||||
}
|
||||
|
||||
private IBindable<bool> allowExplicitContent;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
private void load(OverlayColourProvider colourProvider, OsuConfigManager config)
|
||||
{
|
||||
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();
|
||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
switch (State.Value)
|
||||
{
|
||||
case DownloadState.Downloading:
|
||||
case DownloadState.Downloaded:
|
||||
case DownloadState.Importing:
|
||||
shakeContainer.Shake();
|
||||
break;
|
||||
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
|
||||
break;
|
||||
|
||||
case DownloadState.Downloaded:
|
||||
case DownloadState.Importing:
|
||||
progressBar.FadeIn(400, Easing.OutQuint);
|
||||
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
|
||||
|
||||
|
@ -14,6 +14,7 @@ using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -24,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
private const float horizontal_padding = 10;
|
||||
private const float vertical_padding = 5;
|
||||
|
||||
private FillFlowContainer bottomPanel, statusContainer;
|
||||
private FillFlowContainer bottomPanel, statusContainer, titleContainer;
|
||||
private PlayButton playButton;
|
||||
private Box progressBar;
|
||||
|
||||
@ -73,12 +74,20 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = horizontal_padding, Right = horizontal_padding },
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new[]
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
titleContainer = new FillFlowContainer
|
||||
{
|
||||
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
}
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
@ -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)
|
||||
{
|
||||
statusContainer.Add(new IconPill(FontAwesome.Solid.Film));
|
||||
|
@ -14,6 +14,7 @@ using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -26,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
private const float vertical_padding = 5;
|
||||
private const float height = 70;
|
||||
|
||||
private FillFlowContainer statusContainer;
|
||||
private FillFlowContainer statusContainer, titleContainer;
|
||||
protected BeatmapPanelDownloadButton DownloadButton;
|
||||
private PlayButton playButton;
|
||||
private Box progressBar;
|
||||
@ -98,10 +99,18 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
titleContainer = new FillFlowContainer
|
||||
{
|
||||
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
}
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
@ -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)
|
||||
{
|
||||
statusContainer.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) });
|
||||
|
11
osu.Game/Overlays/BeatmapListing/SearchExplicit.cs
Normal file
11
osu.Game/Overlays/BeatmapListing/SearchExplicit.cs
Normal 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
|
||||
}
|
||||
}
|
@ -126,7 +126,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||
};
|
||||
break;
|
||||
|
||||
case DownloadState.Downloaded:
|
||||
case DownloadState.Importing:
|
||||
textSprites.Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
|
45
osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs
Normal file
45
osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs
Normal 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,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -34,6 +34,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
private readonly Box coverGradient;
|
||||
private readonly OsuSpriteText title, artist;
|
||||
private readonly AuthorInfo author;
|
||||
private readonly ExplicitContentBeatmapPill explicitContentPill;
|
||||
private readonly FillFlowContainer downloadButtonsContainer;
|
||||
private readonly BeatmapAvailability beatmapAvailability;
|
||||
private readonly BeatmapSetOnlineStatusPill onlineStatusPill;
|
||||
@ -144,8 +145,15 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
Anchor = 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
|
||||
@ -253,6 +261,8 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty;
|
||||
artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty;
|
||||
|
||||
explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0;
|
||||
|
||||
onlineStatusPill.FadeIn(500, Easing.OutQuint);
|
||||
onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status;
|
||||
|
||||
@ -287,7 +297,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
break;
|
||||
|
||||
case DownloadState.Downloading:
|
||||
case DownloadState.Downloaded:
|
||||
case DownloadState.Importing:
|
||||
// temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design.
|
||||
downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value);
|
||||
break;
|
||||
|
@ -11,11 +11,23 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 Content1 => getColour(0.4f, 1);
|
||||
public Color4 Content2 => getColour(0.4f, 0.9f);
|
||||
|
@ -33,6 +33,12 @@ namespace osu.Game.Overlays.Settings.Sections.Online
|
||||
Keywords = new[] { "spectator" },
|
||||
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),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Judgements
|
||||
{
|
||||
JudgementText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = Result.GetDescription().ToUpperInvariant(),
|
||||
Colour = colours.ForHitResult(Result),
|
||||
Font = OsuFont.Numeric.With(size: 20),
|
||||
|
@ -138,6 +138,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
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()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
@ -103,7 +103,7 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
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()
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -47,9 +48,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
|
||||
pollReq.Success += result =>
|
||||
{
|
||||
var rooms = new List<Room>(roomManager.Rooms);
|
||||
// existing rooms need to be ordered by their position because the received of NotifyRoomsReceives expects to be able to sort them based on this order.
|
||||
var rooms = new List<Room>(roomManager.Rooms.OrderBy(r => r.Position.Value));
|
||||
|
||||
int index = rooms.FindIndex(r => r.RoomID.Value == result.RoomID.Value);
|
||||
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
|
@ -23,6 +23,7 @@ using osu.Game.Online;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
@ -41,6 +42,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
private Container difficultyIconContainer;
|
||||
private LinkFlowContainer beatmapText;
|
||||
private LinkFlowContainer authorText;
|
||||
private ExplicitContentBeatmapPill explicitContentPill;
|
||||
private ModDisplay modDisplay;
|
||||
|
||||
private readonly Bindable<BeatmapInfo> beatmap = new Bindable<BeatmapInfo>();
|
||||
@ -116,6 +118,9 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
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();
|
||||
}
|
||||
|
||||
@ -165,18 +170,37 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(15, 0),
|
||||
Spacing = new Vector2(10f, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
authorText = new LinkFlowContainer { AutoSizeAxes = Axes.Both },
|
||||
modDisplay = new ModDisplay
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(10f, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
authorText = new LinkFlowContainer { AutoSizeAxes = Axes.Both },
|
||||
explicitContentPill = new ExplicitContentBeatmapPill
|
||||
{
|
||||
Alpha = 0f,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Margin = new MarginPadding { Top = 3f },
|
||||
}
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(0.4f),
|
||||
DisplayUnrankedText = false,
|
||||
ExpansionMode = ExpansionMode.AlwaysExpanded
|
||||
Child = modDisplay = new ModDisplay
|
||||
{
|
||||
Scale = new Vector2(0.4f),
|
||||
DisplayUnrankedText = false,
|
||||
ExpansionMode = ExpansionMode.AlwaysExpanded
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
isConnected.BindValueChanged(_ => updateState());
|
||||
operationInProgress.BindValueChanged(_ => updateState(), true);
|
||||
isConnected.BindValueChanged(_ => Scheduler.AddOnce(updateState));
|
||||
operationInProgress.BindValueChanged(_ => Scheduler.AddOnce(updateState), true);
|
||||
}
|
||||
|
||||
private void updateState() => Enabled.Value = isConnected.Value && !operationInProgress.Value;
|
||||
|
@ -33,6 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
if (!this.IsCurrentScreen())
|
||||
{
|
||||
multiplayerRoomManager.TimeBetweenListingPolls.Value = 0;
|
||||
multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -40,16 +41,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
case LoungeSubScreen _:
|
||||
multiplayerRoomManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000;
|
||||
multiplayerRoomManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000;
|
||||
break;
|
||||
|
||||
// Don't poll inside the match or anywhere else.
|
||||
default:
|
||||
multiplayerRoomManager.TimeBetweenListingPolls.Value = 0;
|
||||
multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value})");
|
||||
Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})");
|
||||
}
|
||||
|
||||
protected override Room CreateNewRoom()
|
||||
|
@ -77,14 +77,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
});
|
||||
|
||||
isConnected = client.IsConnected.GetBoundCopy();
|
||||
isConnected.BindValueChanged(connected =>
|
||||
isConnected.BindValueChanged(connected => Schedule(() =>
|
||||
{
|
||||
if (!connected.NewValue)
|
||||
{
|
||||
// messaging to the user about this disconnect will be provided by the MultiplayerMatchSubScreen.
|
||||
failAndBail();
|
||||
}
|
||||
}, true);
|
||||
}), true);
|
||||
|
||||
Debug.Assert(client.Room != null);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
private StatefulMultiplayerClient multiplayerClient { get; set; }
|
||||
|
||||
public readonly Bindable<double> TimeBetweenListingPolls = new Bindable<double>();
|
||||
|
||||
public readonly Bindable<double> TimeBetweenSelectionPolls = new Bindable<double>();
|
||||
private readonly IBindable<bool> isConnected = new Bindable<bool>();
|
||||
private readonly Bindable<bool> allowPolling = new Bindable<bool>();
|
||||
|
||||
@ -34,10 +34,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
base.LoadComplete();
|
||||
|
||||
isConnected.BindTo(multiplayerClient.IsConnected);
|
||||
isConnected.BindValueChanged(_ => Schedule(updatePolling));
|
||||
JoinedRoom.BindValueChanged(_ => updatePolling());
|
||||
|
||||
updatePolling();
|
||||
isConnected.BindValueChanged(_ => Scheduler.AddOnce(updatePolling));
|
||||
JoinedRoom.BindValueChanged(_ => Scheduler.AddOnce(updatePolling), true);
|
||||
}
|
||||
|
||||
public override void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||
@ -119,6 +117,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls },
|
||||
AllowPolling = { BindTarget = allowPolling }
|
||||
},
|
||||
new MultiplayerSelectionPollingComponent
|
||||
{
|
||||
TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls },
|
||||
AllowPolling = { BindTarget = allowPolling }
|
||||
}
|
||||
};
|
||||
|
||||
private class MultiplayerListingPollingComponent : ListingPollingComponent
|
||||
@ -141,5 +144,26 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll();
|
||||
}
|
||||
|
||||
private class MultiplayerSelectionPollingComponent : SelectionPollingComponent
|
||||
{
|
||||
public readonly IBindable<bool> AllowPolling = new Bindable<bool>();
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
AllowPolling.BindValueChanged(allowPolling =>
|
||||
{
|
||||
if (!allowPolling.NewValue)
|
||||
return;
|
||||
|
||||
if (IsLoaded)
|
||||
PollImmediately();
|
||||
});
|
||||
}
|
||||
|
||||
protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Screens.Ranking
|
||||
scores.Download(Model.Value);
|
||||
break;
|
||||
|
||||
case DownloadState.Downloaded:
|
||||
case DownloadState.Importing:
|
||||
case DownloadState.Downloading:
|
||||
shakeContainer.Shake();
|
||||
break;
|
||||
|
@ -500,7 +500,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (beatmap != null)
|
||||
{
|
||||
if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID)
|
||||
if (beatmap.BeatmapSetInfoID == previous?.BeatmapInfo.BeatmapSetInfoID)
|
||||
sampleChangeDifficulty.Play();
|
||||
else
|
||||
sampleChangeBeatmap.Play();
|
||||
|
@ -378,8 +378,10 @@ namespace osu.Game.Skinning
|
||||
// kind of wasteful that we throw this away, but should do for now.
|
||||
if (createDrawable() != null)
|
||||
{
|
||||
if (Configuration.LegacyVersion > 1)
|
||||
return new LegacyJudgementPieceNew(resultComponent.Component, createDrawable, getParticleTexture(resultComponent.Component));
|
||||
var particle = getParticleTexture(resultComponent.Component);
|
||||
|
||||
if (particle != null)
|
||||
return new LegacyJudgementPieceNew(resultComponent.Component, createDrawable, particle);
|
||||
else
|
||||
return new LegacyJudgementPieceOld(resultComponent.Component, createDrawable);
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
BeatmapInfo.BeatmapSet.Files = new List<BeatmapSetFileInfo>();
|
||||
BeatmapInfo.BeatmapSet.Beatmaps = new List<BeatmapInfo> { BeatmapInfo };
|
||||
BeatmapInfo.Length = 75000;
|
||||
BeatmapInfo.OnlineInfo = new BeatmapOnlineInfo();
|
||||
BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo
|
||||
{
|
||||
Status = BeatmapSetOnlineStatus.Ranked,
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
@ -77,6 +78,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
});
|
||||
}
|
||||
|
||||
public void ChangeUserBeatmapAvailability(int userId, BeatmapAvailability newBeatmapAvailability)
|
||||
{
|
||||
Debug.Assert(Room != null);
|
||||
|
||||
((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(userId, newBeatmapAvailability);
|
||||
}
|
||||
|
||||
protected override Task<MultiplayerRoom> JoinRoom(long roomId)
|
||||
{
|
||||
var user = new MultiplayerRoomUser(api.LocalUser.Value.Id) { User = api.LocalUser.Value };
|
||||
@ -108,6 +116,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability)
|
||||
{
|
||||
ChangeUserBeatmapAvailability(api.LocalUser.Value.Id, newBeatmapAvailability);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override Task StartMatch()
|
||||
{
|
||||
Debug.Assert(Room != null);
|
||||
|
@ -26,7 +26,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<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="Sentry" Version="2.1.8" />
|
||||
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
||||
|
@ -70,7 +70,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
<!-- 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.Core" Version="2.2.6" />
|
||||
<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="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
1
osu.sln
1
osu.sln
@ -57,7 +57,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
Directory.Build.props = Directory.Build.props
|
||||
global.json = global.json
|
||||
osu.Android.props = osu.Android.props
|
||||
osu.iOS.props = osu.iOS.props
|
||||
CodeAnalysis\osu.ruleset = CodeAnalysis\osu.ruleset
|
||||
|
Loading…
Reference in New Issue
Block a user