1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 18:42:56 +08:00

Merge branch 'master' into disable-repeat-multi-actions

This commit is contained in:
Dean Herbert 2021-01-04 23:38:29 +09:00 committed by GitHub
commit deb1ad7bca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 134 additions and 137 deletions

View File

@ -15,7 +15,7 @@
] ]
}, },
"jetbrains.resharper.globaltools": { "jetbrains.resharper.globaltools": {
"version": "2020.2.4", "version": "2020.3.2",
"commands": [ "commands": [
"jb" "jb"
] ]

View File

@ -16,7 +16,7 @@
<EmbeddedResource Include="Resources\**\*.*" /> <EmbeddedResource Include="Resources\**\*.*" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Code Analysis"> <ItemGroup Label="Code Analysis">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.1" PrivateAssets="All" /> <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.2" PrivateAssets="All" />
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" /> <AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.1" PrivateAssets="All" /> <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.1" PrivateAssets="All" />
</ItemGroup> </ItemGroup>

View File

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

View File

@ -16,7 +16,9 @@ using osu.Framework.Android;
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.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] [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.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
{ {

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -8,7 +8,6 @@ using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using static osu.Game.Tests.Visual.Components.TestScenePreviewTrackManager.TestPreviewTrackManager;
namespace osu.Game.Tests.Visual.Components namespace osu.Game.Tests.Visual.Components
{ {
@ -100,7 +99,7 @@ namespace osu.Game.Tests.Visual.Components
[Test] [Test]
public void TestNonPresentTrack() public void TestNonPresentTrack()
{ {
TestPreviewTrack track = null; TestPreviewTrackManager.TestPreviewTrack track = null;
AddStep("get non-present track", () => AddStep("get non-present track", () =>
{ {
@ -182,9 +181,9 @@ namespace osu.Game.Tests.Visual.Components
AddAssert("track stopped", () => !track.IsRunning); AddAssert("track stopped", () => !track.IsRunning);
} }
private TestPreviewTrack getTrack() => (TestPreviewTrack)trackManager.Get(null); private TestPreviewTrackManager.TestPreviewTrack getTrack() => (TestPreviewTrackManager.TestPreviewTrack)trackManager.Get(null);
private TestPreviewTrack getOwnedTrack() private TestPreviewTrackManager.TestPreviewTrack getOwnedTrack()
{ {
var track = getTrack(); var track = getTrack();

View File

@ -9,7 +9,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {

View File

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

View File

@ -3,7 +3,7 @@
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="DeepEqual" Version="2.0.0" /> <PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -5,7 +5,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
</ItemGroup> </ItemGroup>

View File

@ -1,7 +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.
#nullable enable
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Extensions.ExceptionExtensions;
using osu.Framework.Logging; using osu.Framework.Logging;
namespace osu.Game.Extensions namespace osu.Game.Extensions
@ -13,13 +17,19 @@ namespace osu.Game.Extensions
/// Avoids unobserved exceptions from being fired. /// Avoids unobserved exceptions from being fired.
/// </summary> /// </summary>
/// <param name="task">The task.</param> /// <param name="task">The task.</param>
/// <param name="logOnError">Whether errors should be logged as important, or silently ignored.</param> /// <param name="logAsError">
public static void CatchUnobservedExceptions(this Task task, bool logOnError = false) /// Whether errors should be logged as errors visible to users, or as debug messages.
/// Logging as debug will essentially silence the errors on non-release builds.
/// </param>
public static void CatchUnobservedExceptions(this Task task, bool logAsError = false)
{ {
task.ContinueWith(t => task.ContinueWith(t =>
{ {
if (logOnError) Exception? exception = t.Exception?.AsSingular();
Logger.Log($"Error running task: {t.Exception?.Message ?? "unknown"}", LoggingTarget.Runtime, LogLevel.Important); if (logAsError)
Logger.Error(exception, $"Error running task: {exception?.Message ?? "(unknown)"}", LoggingTarget.Runtime, true);
else
Logger.Log($"Error running task: {exception}", LoggingTarget.Runtime, LogLevel.Debug);
}, TaskContinuationOptions.NotOnRanToCompletion); }, TaskContinuationOptions.NotOnRanToCompletion);
} }
} }

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@ -293,8 +294,21 @@ namespace osu.Game.Online.API
failureCount = 0; failureCount = 0;
return true; return true;
} }
catch (HttpRequestException re)
{
log.Add($"{nameof(HttpRequestException)} while performing request {req}: {re.Message}");
handleFailure();
return false;
}
catch (SocketException se)
{
log.Add($"{nameof(SocketException)} while performing request {req}: {se.Message}");
handleFailure();
return false;
}
catch (WebException we) catch (WebException we)
{ {
log.Add($"{nameof(WebException)} while performing request {req}: {we.Message}");
handleWebException(we); handleWebException(we);
return false; return false;
} }
@ -312,7 +326,7 @@ namespace osu.Game.Online.API
/// </summary> /// </summary>
public IBindable<APIState> State => state; public IBindable<APIState> State => state;
private bool handleWebException(WebException we) private void handleWebException(WebException we)
{ {
HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode
?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout); ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout);
@ -330,26 +344,24 @@ namespace osu.Game.Online.API
{ {
case HttpStatusCode.Unauthorized: case HttpStatusCode.Unauthorized:
Logout(); Logout();
return true; break;
case HttpStatusCode.RequestTimeout: case HttpStatusCode.RequestTimeout:
failureCount++; handleFailure();
log.Add($@"API failure count is now {failureCount}"); break;
if (failureCount < 3)
// we might try again at an api level.
return false;
if (State.Value == APIState.Online)
{
state.Value = APIState.Failing;
flushQueue();
}
return true;
} }
}
return true; private void handleFailure()
{
failureCount++;
log.Add($@"API failure count is now {failureCount}");
if (failureCount >= 3 && State.Value == APIState.Online)
{
state.Value = APIState.Failing;
flushQueue();
}
} }
public bool IsLoggedIn => localUser.Value.Id > 1; public bool IsLoggedIn => localUser.Value.Id > 1;

View File

@ -31,7 +31,12 @@ namespace osu.Game.Online.API
Acronym = mod.Acronym; Acronym = mod.Acronym;
foreach (var (_, property) in mod.GetSettingsSourceProperties()) foreach (var (_, property) in mod.GetSettingsSourceProperties())
Settings.Add(property.Name.Underscore(), property.GetValue(mod)); {
var bindable = (IBindable)property.GetValue(mod);
if (!bindable.IsDefault)
Settings.Add(property.Name.Underscore(), bindable);
}
} }
public Mod ToMod(Ruleset ruleset) public Mod ToMod(Ruleset ruleset)
@ -46,7 +51,7 @@ namespace osu.Game.Online.API
if (!Settings.TryGetValue(property.Name.Underscore(), out object settingValue)) if (!Settings.TryGetValue(property.Name.Underscore(), out object settingValue))
continue; continue;
((IBindable)property.GetValue(resultMod)).Parse(settingValue); resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod), settingValue);
} }
return resultMod; return resultMod;

View File

@ -88,11 +88,12 @@ namespace osu.Game.Online.Multiplayer
{ {
isConnected.Value = false; isConnected.Value = false;
if (ex != null) Logger.Log(ex != null
{ ? $"Multiplayer client lost connection: {ex}"
Logger.Log($"Multiplayer client lost connection: {ex}", LoggingTarget.Network); : "Multiplayer client disconnected", LoggingTarget.Network);
if (connection != null)
await tryUntilConnected(); await tryUntilConnected();
}
}; };
await tryUntilConnected(); await tryUntilConnected();

View File

@ -5,9 +5,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Allocation;
namespace osu.Game.Online.Multiplayer namespace osu.Game.Online.Multiplayer
{ {
@ -42,35 +40,12 @@ namespace osu.Game.Online.Multiplayer
/// </summary> /// </summary>
public MultiplayerRoomUser? Host { get; set; } public MultiplayerRoomUser? Host { get; set; }
private object writeLock = new object();
[JsonConstructor] [JsonConstructor]
public MultiplayerRoom(in long roomId) public MultiplayerRoom(in long roomId)
{ {
RoomID = roomId; RoomID = roomId;
} }
private object updateLock = new object();
private ManualResetEventSlim freeForWrite = new ManualResetEventSlim(true);
/// <summary>
/// Request a lock on this room to perform a thread-safe update.
/// </summary>
public IDisposable LockForUpdate()
{
// ReSharper disable once InconsistentlySynchronizedField
freeForWrite.Wait();
lock (updateLock)
{
freeForWrite.Wait();
freeForWrite.Reset();
return new ValueInvokeOnDisposal<MultiplayerRoom>(this, r => freeForWrite.Set());
}
}
public override string ToString() => $"RoomID:{RoomID} Host:{Host?.UserID} Users:{Users.Count} State:{State} Settings: [{Settings}]"; public override string ToString() => $"RoomID:{RoomID} Host:{Host?.UserID} Users:{Users.Count} State:{State} Settings: [{Settings}]";
} }
} }

View File

@ -101,7 +101,7 @@ namespace osu.Game.Online.Multiplayer
IsConnected.BindValueChanged(connected => IsConnected.BindValueChanged(connected =>
{ {
// clean up local room state on server disconnect. // clean up local room state on server disconnect.
if (!connected.NewValue) if (!connected.NewValue && Room != null)
{ {
Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important);
LeaveRoom().CatchUnobservedExceptions(); LeaveRoom().CatchUnobservedExceptions();

View File

@ -7,12 +7,11 @@ namespace osu.Game.Overlays.Changelog
{ {
public class ChangelogUpdateStreamControl : OverlayStreamControl<APIUpdateStream> public class ChangelogUpdateStreamControl : OverlayStreamControl<APIUpdateStream>
{ {
protected override OverlayStreamItem<APIUpdateStream> CreateStreamItem(APIUpdateStream value) => new ChangelogUpdateStreamItem(value); public ChangelogUpdateStreamControl()
protected override void LoadComplete()
{ {
// suppress base logic of immediately selecting first item if one exists SelectFirstTabByDefault = false;
// (we always want to start with no stream selected).
} }
protected override OverlayStreamItem<APIUpdateStream> CreateStreamItem(APIUpdateStream value) => new ChangelogUpdateStreamItem(value);
} }
} }

View File

@ -73,15 +73,19 @@ namespace osu.Game
// find closest valid target // find closest valid target
IScreen current = getCurrentScreen(); IScreen current = getCurrentScreen();
if (current == null)
return;
// a dialog may be blocking the execution for now. // a dialog may be blocking the execution for now.
if (checkForDialog(current)) return; if (checkForDialog(current)) return;
game?.CloseAllOverlays(false); game?.CloseAllOverlays(false);
// we may already be at the target screen type. // we may already be at the target screen type.
if (validScreens.Contains(getCurrentScreen().GetType()) && !beatmap.Disabled) if (validScreens.Contains(current.GetType()) && !beatmap.Disabled)
{ {
complete(); finalAction(current);
Cancel();
return; return;
} }
@ -135,11 +139,5 @@ namespace osu.Game
lastEncounteredDialogScreen = current; lastEncounteredDialogScreen = current;
return true; return true;
} }
private void complete()
{
finalAction(getCurrentScreen());
Cancel();
}
} }
} }

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -84,12 +83,10 @@ namespace osu.Game.Rulesets.Mods
foreach ((SettingSourceAttribute attr, PropertyInfo property) in this.GetOrderedSettingsSourceProperties()) foreach ((SettingSourceAttribute attr, PropertyInfo property) in this.GetOrderedSettingsSourceProperties())
{ {
object bindableObj = property.GetValue(this); var bindable = (IBindable)property.GetValue(this);
if ((bindableObj as IHasDefaultValue)?.IsDefault == true) if (!bindable.IsDefault)
continue; tooltipTexts.Add($"{attr.Label} {bindable}");
tooltipTexts.Add($"{attr.Label} {bindableObj}");
} }
return string.Join(", ", tooltipTexts.Where(s => !string.IsNullOrEmpty(s))); return string.Join(", ", tooltipTexts.Where(s => !string.IsNullOrEmpty(s)));
@ -136,19 +133,38 @@ namespace osu.Game.Rulesets.Mods
// Copy bindable values across // Copy bindable values across
foreach (var (_, prop) in this.GetSettingsSourceProperties()) foreach (var (_, prop) in this.GetSettingsSourceProperties())
{ {
var origBindable = prop.GetValue(this); var origBindable = (IBindable)prop.GetValue(this);
var copyBindable = prop.GetValue(copy); var copyBindable = (IBindable)prop.GetValue(copy);
// The bindables themselves are readonly, so the value must be transferred through the Bindable<T>.Value property. // we only care about changes that have been made away from defaults.
var valueProperty = origBindable.GetType().GetProperty(nameof(Bindable<object>.Value), BindingFlags.Public | BindingFlags.Instance); if (!origBindable.IsDefault)
Debug.Assert(valueProperty != null); copy.CopyAdjustedSetting(copyBindable, origBindable);
valueProperty.SetValue(copyBindable, valueProperty.GetValue(origBindable));
} }
return copy; return copy;
} }
/// <summary>
/// When creating copies or clones of a Mod, this method will be called
/// to copy explicitly adjusted user settings from <paramref name="target"/>.
/// The base implementation will transfer the value via <see cref="Bindable{T}.Parse"/>
/// or by binding and unbinding (if <paramref name="source"/> is an <see cref="IBindable"/>)
/// and should be called unless replaced with custom logic.
/// </summary>
/// <param name="target">The target bindable to apply the adjustment to.</param>
/// <param name="source">The adjustment to apply.</param>
internal virtual void CopyAdjustedSetting(IBindable target, object source)
{
if (source is IBindable sourceBindable)
{
// copy including transfer of default values.
target.BindTo(sourceBindable);
target.UnbindFrom(sourceBindable);
}
else
target.Parse(source);
}
public bool Equals(IMod other) => GetType() == other?.GetType(); public bool Equals(IMod other) => GetType() == other?.GetType();
} }
} }

View File

@ -114,6 +114,12 @@ namespace osu.Game.Rulesets.Mods
bindable.ValueChanged += _ => userChangedSettings[bindable] = !bindable.IsDefault; bindable.ValueChanged += _ => userChangedSettings[bindable] = !bindable.IsDefault;
} }
internal override void CopyAdjustedSetting(IBindable target, object source)
{
userChangedSettings[target] = true;
base.CopyAdjustedSetting(target, source);
}
/// <summary> /// <summary>
/// Apply all custom settings to the provided beatmap. /// Apply all custom settings to the provided beatmap.
/// </summary> /// </summary>

View File

@ -156,11 +156,11 @@ namespace osu.Game.Screens.Menu
private void onMultiplayer() private void onMultiplayer()
{ {
if (!api.IsLoggedIn) if (api.State.Value != APIState.Online)
{ {
notifications?.Post(new SimpleNotification notifications?.Post(new SimpleNotification
{ {
Text = "You gotta be logged in to multi 'yo!", Text = "You gotta be online to multi 'yo!",
Icon = FontAwesome.Solid.Globe, Icon = FontAwesome.Solid.Globe,
Activated = () => Activated = () =>
{ {
@ -177,11 +177,11 @@ namespace osu.Game.Screens.Menu
private void onPlaylists() private void onPlaylists()
{ {
if (!api.IsLoggedIn) if (api.State.Value != APIState.Online)
{ {
notifications?.Post(new SimpleNotification notifications?.Post(new SimpleNotification
{ {
Text = "You gotta be logged in to multi 'yo!", Text = "You gotta be online to view playlists 'yo!",
Icon = FontAwesome.Solid.Globe, Icon = FontAwesome.Solid.Globe,
Activated = () => Activated = () =>
{ {

View File

@ -33,7 +33,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
if (!this.IsCurrentScreen()) if (!this.IsCurrentScreen())
{ {
multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; multiplayerRoomManager.TimeBetweenListingPolls.Value = 0;
multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0;
} }
else else
{ {
@ -41,18 +40,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{ {
case LoungeSubScreen _: case LoungeSubScreen _:
multiplayerRoomManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; multiplayerRoomManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000;
multiplayerRoomManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000;
break; break;
// Don't poll inside the match or anywhere else. // Don't poll inside the match or anywhere else.
default: default:
multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; multiplayerRoomManager.TimeBetweenListingPolls.Value = 0;
multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0;
break; break;
} }
} }
Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})"); Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value})");
} }
protected override Room CreateNewRoom() protected override Room CreateNewRoom()

View File

@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private StatefulMultiplayerClient multiplayerClient { get; set; } private StatefulMultiplayerClient multiplayerClient { get; set; }
public readonly Bindable<double> TimeBetweenListingPolls = new Bindable<double>(); 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 IBindable<bool> isConnected = new Bindable<bool>();
private readonly Bindable<bool> allowPolling = new Bindable<bool>(); private readonly Bindable<bool> allowPolling = new Bindable<bool>();
@ -119,11 +119,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls }, TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls },
AllowPolling = { BindTarget = allowPolling } AllowPolling = { BindTarget = allowPolling }
}, },
new MultiplayerSelectionPollingComponent
{
TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls },
AllowPolling = { BindTarget = allowPolling }
}
}; };
private class MultiplayerListingPollingComponent : ListingPollingComponent private class MultiplayerListingPollingComponent : ListingPollingComponent
@ -146,26 +141,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); 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();
}
} }
} }

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Logging;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
@ -168,7 +169,10 @@ namespace osu.Game.Screens.OnlinePlay
private void onlineStateChanged(ValueChangedEvent<APIState> state) => Schedule(() => private void onlineStateChanged(ValueChangedEvent<APIState> state) => Schedule(() =>
{ {
if (state.NewValue != APIState.Online) if (state.NewValue != APIState.Online)
{
Logger.Log("API connection was lost, can't continue with online play", LoggingTarget.Network, LogLevel.Important);
Schedule(forcefullyExit); Schedule(forcefullyExit);
}
}); });
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -5,10 +5,9 @@ using System;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play.HUD
{ {
public class SkinnableHealthDisplay : SkinnableDrawable, IHealthDisplay public class SkinnableHealthDisplay : SkinnableDrawable, IHealthDisplay
{ {

View File

@ -21,12 +21,12 @@
<PackageReference Include="Dapper" Version="2.0.78" /> <PackageReference Include="Dapper" Version="2.0.78" />
<PackageReference Include="DiffPlex" Version="1.6.3" /> <PackageReference Include="DiffPlex" Version="1.6.3" />
<PackageReference Include="Humanizer" Version="2.8.26" /> <PackageReference Include="Humanizer" Version="2.8.26" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.9" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.10" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="3.1.9" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="3.1.10" />
<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="2020.1228.0" /> <PackageReference Include="ppy.osu.Framework" Version="2020.1229.0" />
<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="2020.1228.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2020.1229.0" />
<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="2020.1228.0" /> <PackageReference Include="ppy.osu.Framework" Version="2020.1229.0" />
<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

@ -106,6 +106,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MergeCastWithTypeCheck/@EntryIndexedValue">HINT</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MergeCastWithTypeCheck/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MergeConditionalExpression/@EntryIndexedValue">WARNING</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MergeConditionalExpression/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MergeSequentialChecks/@EntryIndexedValue">WARNING</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MergeSequentialChecks/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MergeSequentialPatterns/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MethodHasAsyncOverload/@EntryIndexedValue">WARNING</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MethodHasAsyncOverload/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MethodHasAsyncOverloadWithCancellation/@EntryIndexedValue">WARNING</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MethodHasAsyncOverloadWithCancellation/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MethodSupportsCancellation/@EntryIndexedValue">WARNING</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MethodSupportsCancellation/@EntryIndexedValue">WARNING</s:String>