1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-20 05:56:42 +08:00

Fix the MOTHERLOAD of undetected issues that are now visible thanks to net6.0

This commit is contained in:
Dean Herbert 2022-12-16 18:16:26 +09:00
parent d6cae991da
commit 27c497145f
67 changed files with 165 additions and 130 deletions

View File

@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Replays
private class ReplayFrameComparer : IComparer<ReplayFrame>
{
public int Compare(ReplayFrame f1, ReplayFrame f2)
public int Compare(ReplayFrame? f1, ReplayFrame? f2)
{
if (f1 == null) throw new ArgumentNullException(nameof(f1));
if (f2 == null) throw new ArgumentNullException(nameof(f2));

View File

@ -245,8 +245,10 @@ namespace osu.Game.Tournament.IPC
{
string stableInstallPath;
#pragma warning disable CA1416
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", "");
#pragma warning restore CA1416
if (ipcFileExistsInDirectory(stableInstallPath))
return stableInstallPath;

View File

@ -70,7 +70,7 @@ namespace osu.Game.Tournament
private async Task checkForChanges()
{
string serialisedLadder = await Task.Run(() => tournamentGame.GetSerialisedLadder());
string serialisedLadder = await Task.Run(() => tournamentGame.GetSerialisedLadder()).ConfigureAwait(false);
// If a save hasn't been triggered by the user yet, populate the initial value
lastSerialisedLadder ??= serialisedLadder;

View File

@ -35,7 +35,7 @@ namespace osu.Game.Audio
public bool Equals(SampleInfo? other)
=> other != null && sampleNames.SequenceEqual(other.sampleNames);
public override bool Equals(object obj)
public override bool Equals(object? obj)
=> obj is SampleInfo other && Equals(other);
}
}

View File

@ -44,7 +44,7 @@ namespace osu.Game.Beatmaps
public override async Task<Live<BeatmapSetInfo>?> ImportAsUpdate(ProgressNotification notification, ImportTask importTask, BeatmapSetInfo original)
{
var imported = await Import(notification, importTask);
var imported = await Import(notification, importTask).ConfigureAwait(false);
if (!imported.Any())
return null;

View File

@ -178,7 +178,7 @@ namespace osu.Game.Beatmaps
{
try
{
await cacheDownloadRequest.PerformAsync();
await cacheDownloadRequest.PerformAsync().ConfigureAwait(false);
}
catch
{

View File

@ -16,7 +16,7 @@ namespace osu.Game.Beatmaps.ControlPoints
public void AttachGroup(ControlPointGroup pointGroup) => Time = pointGroup.Time;
public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time);
public int CompareTo(ControlPoint? other) => Time.CompareTo(other?.Time);
public virtual Color4 GetRepresentingColour(OsuColour colours) => colours.Yellow;
@ -32,7 +32,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// </summary>
public ControlPoint DeepClone()
{
var copy = (ControlPoint)Activator.CreateInstance(GetType());
var copy = (ControlPoint)Activator.CreateInstance(GetType())!;
copy.CopyFrom(this);

View File

@ -26,7 +26,7 @@ namespace osu.Game.Beatmaps.ControlPoints
Time = time;
}
public int CompareTo(ControlPointGroup other) => Time.CompareTo(other.Time);
public int CompareTo(ControlPointGroup? other) => Time.CompareTo(other?.Time);
public void Add(ControlPoint point)
{

View File

@ -51,11 +51,11 @@ namespace osu.Game.Beatmaps
if (!recommendedDifficultyMapping.TryGetValue(r, out double recommendation))
continue;
BeatmapInfo beatmapInfo = beatmaps.Where(b => b.Ruleset.ShortName.Equals(r)).OrderBy(b =>
BeatmapInfo beatmapInfo = beatmaps.Where(b => b.Ruleset.ShortName.Equals(r, StringComparison.Ordinal)).MinBy(b =>
{
double difference = b.StarRating - recommendation;
return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder
}).FirstOrDefault();
});
if (beatmapInfo != null)
return beatmapInfo;
@ -90,7 +90,7 @@ namespace osu.Game.Beatmaps
return recommendedDifficultyMapping
.OrderByDescending(pair => pair.Value)
.Select(pair => pair.Key)
.Where(r => !r.Equals(ruleset.Value.ShortName))
.Where(r => !r.Equals(ruleset.Value.ShortName, StringComparison.Ordinal))
.Prepend(ruleset.Value.ShortName);
}
}

View File

@ -91,15 +91,15 @@ namespace osu.Game.Configuration
OrderPosition = orderPosition;
}
public int CompareTo(SettingSourceAttribute other)
public int CompareTo(SettingSourceAttribute? other)
{
if (OrderPosition == other.OrderPosition)
if (OrderPosition == other?.OrderPosition)
return 0;
// unordered items come last (are greater than any ordered items).
if (OrderPosition == null)
return 1;
if (other.OrderPosition == null)
if (other?.OrderPosition == null)
return -1;
// ordered items are sorted by the order value.
@ -113,7 +113,7 @@ namespace osu.Game.Configuration
{
foreach (var (attr, property) in obj.GetOrderedSettingsSourceProperties())
{
object value = property.GetValue(obj);
object value = property.GetValue(obj)!;
if (attr.SettingControlType != null)
{
@ -121,7 +121,7 @@ namespace osu.Game.Configuration
if (controlType.EnumerateBaseTypes().All(t => !t.IsGenericType || t.GetGenericTypeDefinition() != typeof(SettingsItem<>)))
throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} had an unsupported custom control type ({controlType.ReadableName()})");
var control = (Drawable)Activator.CreateInstance(controlType);
var control = (Drawable)Activator.CreateInstance(controlType)!;
controlType.GetProperty(nameof(SettingsItem<object>.SettingSourceObject))?.SetValue(control, obj);
controlType.GetProperty(nameof(SettingsItem<object>.LabelText))?.SetValue(control, attr.Label);
controlType.GetProperty(nameof(SettingsItem<object>.TooltipText))?.SetValue(control, attr.Description);
@ -188,7 +188,7 @@ namespace osu.Game.Configuration
case IBindable bindable:
var dropdownType = typeof(ModSettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]);
var dropdown = (Drawable)Activator.CreateInstance(dropdownType);
var dropdown = (Drawable)Activator.CreateInstance(dropdownType)!;
dropdownType.GetProperty(nameof(SettingsDropdown<object>.LabelText))?.SetValue(dropdown, attr.Label);
dropdownType.GetProperty(nameof(SettingsDropdown<object>.TooltipText))?.SetValue(dropdown, attr.Description);
@ -231,7 +231,7 @@ namespace osu.Game.Configuration
// An unknown (e.g. enum) generic type.
var valueMethod = u.GetType().GetProperty(nameof(IBindable<int>.Value));
Debug.Assert(valueMethod != null);
return valueMethod.GetValue(u);
return valueMethod.GetValue(u)!;
default:
// fall back for non-bindable cases.

View File

@ -66,16 +66,16 @@ namespace osu.Game.Database
switch (content)
{
case StableContent.Beatmaps:
return await new LegacyBeatmapImporter(beatmaps).GetAvailableCount(stableStorage);
return await new LegacyBeatmapImporter(beatmaps).GetAvailableCount(stableStorage).ConfigureAwait(false);
case StableContent.Skins:
return await new LegacySkinImporter(skins).GetAvailableCount(stableStorage);
return await new LegacySkinImporter(skins).GetAvailableCount(stableStorage).ConfigureAwait(false);
case StableContent.Collections:
return await new LegacyCollectionImporter(realmAccess).GetAvailableCount(stableStorage);
return await new LegacyCollectionImporter(realmAccess).GetAvailableCount(stableStorage).ConfigureAwait(false);
case StableContent.Scores:
return await new LegacyScoreImporter(scores).GetAvailableCount(stableStorage);
return await new LegacyScoreImporter(scores).GetAvailableCount(stableStorage).ConfigureAwait(false);
default:
throw new ArgumentException($"Only one {nameof(StableContent)} flag should be specified.");

View File

@ -61,6 +61,6 @@ namespace osu.Game.Database
public override int GetHashCode() => HashCode.Combine(ID);
public override string ToString() => PerformRead(i => i.ToString());
public override string? ToString() => PerformRead(i => i.ToString());
}
}

View File

@ -71,9 +71,9 @@ namespace osu.Game.Database
bool importSuccessful;
if (originalModel != null)
importSuccessful = (await importer.ImportAsUpdate(notification, new ImportTask(filename), originalModel)) != null;
importSuccessful = (await importer.ImportAsUpdate(notification, new ImportTask(filename), originalModel).ConfigureAwait(false)) != null;
else
importSuccessful = (await importer.Import(notification, new ImportTask(filename))).Any();
importSuccessful = (await importer.Import(notification, new ImportTask(filename)).ConfigureAwait(false)).Any();
// for now a failed import will be marked as a failed download for simplicity.
if (!importSuccessful)

View File

@ -481,7 +481,7 @@ namespace osu.Game.Database
// server, which we don't use. May want to report upstream or revisit in the future.
using (var realm = getRealmInstance())
// ReSharper disable once AccessToDisposedClosure (WriteAsync should be marked as [InstantHandle]).
await realm.WriteAsync(() => action(realm));
await realm.WriteAsync(() => action(realm)).ConfigureAwait(false);
pendingAsyncWrites.Signal();
});
@ -558,7 +558,7 @@ namespace osu.Game.Database
return new InvokeOnDisposal(() => model.PropertyChanged -= onPropertyChanged);
void onPropertyChanged(object sender, PropertyChangedEventArgs args)
void onPropertyChanged(object? sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == propertyName)
onChanged(propLookupCompiled(model));

View File

@ -66,10 +66,10 @@ namespace osu.Game.Extensions
foreach (var (_, property) in component.GetSettingsSourceProperties())
{
if (!info.Settings.TryGetValue(property.Name.ToSnakeCase(), out object settingValue))
if (!info.Settings.TryGetValue(property.Name.ToSnakeCase(), out object? settingValue))
continue;
skinnable.CopyAdjustedSetting((IBindable)property.GetValue(component), settingValue);
skinnable.CopyAdjustedSetting(((IBindable)property.GetValue(component)!), settingValue);
}
}

View File

@ -37,7 +37,7 @@ namespace osu.Game.Extensions
if (cancellationToken.IsCancellationRequested)
{
tcs.SetCanceled();
tcs.SetCanceled(cancellationToken);
}
else
{

View File

@ -277,7 +277,7 @@ namespace osu.Game.Graphics.UserInterface
{
var samples = sampleMap[feedbackSampleType];
if (samples == null || samples.Length == 0)
if (samples.Length == 0)
return null;
return samples[RNG.Next(0, samples.Length)]?.GetChannel();

View File

@ -27,7 +27,7 @@ namespace osu.Game.Models
public bool IsBot => false;
public bool Equals(RealmUser other)
public bool Equals(RealmUser? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;

View File

@ -39,7 +39,7 @@ namespace osu.Game.Online.API
foreach (var (_, property) in mod.GetSettingsSourceProperties())
{
var bindable = (IBindable)property.GetValue(mod);
var bindable = (IBindable)property.GetValue(mod)!;
if (!bindable.IsDefault)
Settings.Add(property.Name.ToSnakeCase(), bindable.GetUnderlyingSettingValue());
@ -60,16 +60,16 @@ namespace osu.Game.Online.API
{
foreach (var (_, property) in resultMod.GetSettingsSourceProperties())
{
if (!Settings.TryGetValue(property.Name.ToSnakeCase(), out object settingValue))
if (!Settings.TryGetValue(property.Name.ToSnakeCase(), out object? settingValue))
continue;
try
{
resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod), settingValue);
resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod)!, settingValue);
}
catch (Exception ex)
{
Logger.Log($"Failed to copy mod setting value '{settingValue ?? "null"}' to \"{property.Name}\": {ex.Message}");
Logger.Log($"Failed to copy mod setting value '{settingValue}' to \"{property.Name}\": {ex.Message}");
}
}
}
@ -79,7 +79,7 @@ namespace osu.Game.Online.API
public bool ShouldSerializeSettings() => Settings.Count > 0;
public bool Equals(APIMod other)
public bool Equals(APIMod? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;

View File

@ -143,7 +143,7 @@ namespace osu.Game.Online.API.Requests.Responses
public bool Equals(IRulesetInfo? other) => other is APIRuleset r && this.MatchesOnlineID(r);
public int CompareTo(IRulesetInfo other)
public int CompareTo(IRulesetInfo? other)
{
if (!(other is APIRuleset ruleset))
throw new ArgumentException($@"Object is not of type {nameof(APIRuleset)}.", nameof(other));

View File

@ -71,7 +71,7 @@ namespace osu.Game.Online.Chat
{
int index = m.Index - captureOffset;
string? displayText = string.Format(display,
string displayText = string.Format(display,
m.Groups[0],
m.Groups["text"].Value,
m.Groups["url"].Value).Trim();
@ -109,7 +109,7 @@ namespace osu.Game.Online.Chat
foreach (Match m in regex.Matches(result.Text, startIndex))
{
int index = m.Index;
string? linkText = m.Groups["link"].Value;
string linkText = m.Groups["link"].Value;
int indexLength = linkText.Length;
var details = GetLinkDetails(linkText);
@ -125,7 +125,7 @@ namespace osu.Game.Online.Chat
public static LinkDetails GetLinkDetails(string url)
{
string[]? args = url.Split('/', StringSplitOptions.RemoveEmptyEntries);
string[] args = url.Split('/', StringSplitOptions.RemoveEmptyEntries);
args[0] = args[0].TrimEnd(':');
switch (args[0])
@ -362,6 +362,6 @@ namespace osu.Game.Online.Chat
public bool Overlaps(Link otherLink) => Index < otherLink.Index + otherLink.Length && otherLink.Index < Index + Length;
public int CompareTo(Link otherLink) => Index > otherLink.Index ? 1 : -1;
public int CompareTo(Link? otherLink) => Index > otherLink?.Index ? 1 : -1;
}
}

View File

@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using osu.Framework.Allocation;
@ -61,12 +62,16 @@ namespace osu.Game.Online.Chat
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
Debug.Assert(e.NewItems != null);
foreach (var channel in e.NewItems.Cast<Channel>())
channel.NewMessagesArrived += checkNewMessages;
break;
case NotifyCollectionChangedAction.Remove:
Debug.Assert(e.OldItems != null);
foreach (var channel in e.OldItems.Cast<Channel>())
channel.NewMessagesArrived -= checkNewMessages;

View File

@ -68,7 +68,7 @@ namespace osu.Game.Online.Metadata
while (true)
{
Logger.Log($"Requesting catch-up from {lastQueueId.Value}");
var catchUpChanges = await GetChangesSince(lastQueueId.Value);
var catchUpChanges = await GetChangesSince(lastQueueId.Value).ConfigureAwait(true);
lastQueueId.Value = catchUpChanges.LastProcessedQueueID;
@ -78,7 +78,7 @@ namespace osu.Game.Online.Metadata
break;
}
await ProcessChanges(catchUpChanges.BeatmapSetIDs);
await ProcessChanges(catchUpChanges.BeatmapSetIDs).ConfigureAwait(true);
}
}
catch (Exception e)
@ -101,7 +101,7 @@ namespace osu.Game.Online.Metadata
if (!catchingUp)
lastQueueId.Value = updates.LastProcessedQueueID;
await ProcessChanges(updates.BeatmapSetIDs);
await ProcessChanges(updates.BeatmapSetIDs).ConfigureAwait(false);
}
public override Task<BeatmapUpdates> GetChangesSince(int queueId)

View File

@ -822,7 +822,7 @@ namespace osu.Game.Online.Multiplayer
{
if (cancellationToken.IsCancellationRequested)
{
tcs.SetCanceled();
tcs.SetCanceled(cancellationToken);
return;
}

View File

@ -54,10 +54,10 @@ namespace osu.Game.Online.Multiplayer
return UserID == other.UserID;
}
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
if (obj?.GetType() != GetType()) return false;
return Equals((MultiplayerRoomUser)obj);
}

View File

@ -80,7 +80,7 @@ namespace osu.Game.Online.Multiplayer
try
{
return await connection.InvokeAsync<MultiplayerRoom>(nameof(IMultiplayerServer.JoinRoomWithPassword), roomId, password ?? string.Empty);
return await connection.InvokeAsync<MultiplayerRoom>(nameof(IMultiplayerServer.JoinRoomWithPassword), roomId, password ?? string.Empty).ConfigureAwait(false);
}
catch (HubException exception)
{
@ -88,8 +88,8 @@ namespace osu.Game.Online.Multiplayer
{
Debug.Assert(connector != null);
await connector.Reconnect();
return await JoinRoom(roomId, password);
await connector.Reconnect().ConfigureAwait(false);
return await JoinRoom(roomId, password).ConfigureAwait(false);
}
throw;

View File

@ -27,7 +27,7 @@ namespace osu.Game.Online.Notifications
protected sealed override async Task<PersistentEndpointClient> BuildConnectionAsync(CancellationToken cancellationToken)
{
var client = await BuildNotificationClientAsync(cancellationToken);
var client = await BuildNotificationClientAsync(cancellationToken).ConfigureAwait(false);
client.ChannelJoined = c => ChannelJoined?.Invoke(c);
client.ChannelParted = c => ChannelParted?.Invoke(c);

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
@ -36,11 +37,11 @@ namespace osu.Game.Online.Notifications.WebSocket
public override async Task ConnectAsync(CancellationToken cancellationToken)
{
await socket.ConnectAsync(new Uri(endpoint), cancellationToken).ConfigureAwait(false);
await sendMessage(new StartChatRequest(), CancellationToken.None);
await sendMessage(new StartChatRequest(), CancellationToken.None).ConfigureAwait(false);
runReadLoop(cancellationToken);
await base.ConnectAsync(cancellationToken);
await base.ConnectAsync(cancellationToken).ConfigureAwait(false);
}
private void runReadLoop(CancellationToken cancellationToken) => Task.Run(async () =>
@ -52,7 +53,7 @@ namespace osu.Game.Online.Notifications.WebSocket
{
try
{
WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, cancellationToken);
WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false);
switch (result.MessageType)
{
@ -72,7 +73,7 @@ namespace osu.Game.Online.Notifications.WebSocket
break;
}
await onMessageReceivedAsync(message);
await onMessageReceivedAsync(message).ConfigureAwait(false);
}
break;
@ -81,12 +82,12 @@ namespace osu.Game.Online.Notifications.WebSocket
throw new NotImplementedException("Binary message type not supported.");
case WebSocketMessageType.Close:
throw new Exception("Connection closed by remote host.");
throw new WebException("Connection closed by remote host.");
}
}
catch (Exception ex)
{
await InvokeClosed(ex);
await InvokeClosed(ex).ConfigureAwait(false);
return;
}
}
@ -109,7 +110,7 @@ namespace osu.Game.Online.Notifications.WebSocket
if (socket.State != WebSocketState.Open)
return;
await socket.SendAsync(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message)), WebSocketMessageType.Text, true, cancellationToken);
await socket.SendAsync(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message)), WebSocketMessageType.Text, true, cancellationToken).ConfigureAwait(false);
}
private async Task onMessageReceivedAsync(SocketMessage message)
@ -141,7 +142,7 @@ namespace osu.Game.Online.Notifications.WebSocket
Debug.Assert(messageData != null);
foreach (var msg in messageData.Messages)
HandleChannelJoined(await getChannel(msg.ChannelId));
HandleChannelJoined(await getChannel(msg.ChannelId).ConfigureAwait(false));
HandleMessages(messageData.Messages);
break;
@ -150,7 +151,7 @@ namespace osu.Game.Online.Notifications.WebSocket
private async Task<Channel> getChannel(long channelId)
{
if (channelsMap.TryGetValue(channelId, out Channel channel))
if (channelsMap.TryGetValue(channelId, out Channel? channel))
return channel;
var tsc = new TaskCompletionSource<Channel>();
@ -166,13 +167,13 @@ namespace osu.Game.Online.Notifications.WebSocket
API.Queue(req);
return await tsc.Task;
return await tsc.Task.ConfigureAwait(false);
}
public override async ValueTask DisposeAsync()
{
await base.DisposeAsync();
await closeAsync();
await base.DisposeAsync().ConfigureAwait(false);
await closeAsync().ConfigureAwait(false);
socket.Dispose();
}
}

View File

@ -32,7 +32,7 @@ namespace osu.Game.Online.Notifications.WebSocket
req.Failure += ex => tcs.SetException(ex);
api.Queue(req);
string endpoint = await tcs.Task;
string endpoint = await tcs.Task.ConfigureAwait(false);
ClientWebSocket socket = new ClientWebSocket();
socket.Options.SetRequestHeader(@"Authorization", @$"Bearer {api.AccessToken}");

View File

@ -65,11 +65,11 @@ namespace osu.Game.Online
{
case APIState.Failing:
case APIState.Offline:
await disconnect(true);
await disconnect(true).ConfigureAwait(true);
break;
case APIState.Online:
await connect();
await connect().ConfigureAwait(true);
break;
}
}
@ -147,7 +147,7 @@ namespace osu.Game.Online
{
bool hasBeenCancelled = cancellationToken.IsCancellationRequested;
await disconnect(true);
await disconnect(true).ConfigureAwait(false);
if (ex != null)
await handleErrorAndDelay(ex, CancellationToken.None).ConfigureAwait(false);

View File

@ -33,7 +33,8 @@ namespace osu.Game.Online
object? instance = Activator.CreateInstance(resolvedType);
jsonSerializer.Populate(obj["$value"]!.CreateReader(), instance);
if (instance != null)
jsonSerializer.Populate(obj["$value"]!.CreateReader(), instance);
return instance;
}

View File

@ -56,7 +56,7 @@ namespace osu.Game.Online.Spectator
try
{
await connection.InvokeAsync(nameof(ISpectatorServer.BeginPlaySession), scoreToken, state);
await connection.InvokeAsync(nameof(ISpectatorServer.BeginPlaySession), scoreToken, state).ConfigureAwait(false);
}
catch (Exception exception)
{
@ -64,8 +64,8 @@ namespace osu.Game.Online.Spectator
{
Debug.Assert(connector != null);
await connector.Reconnect();
await BeginPlayingInternal(scoreToken, state);
await connector.Reconnect().ConfigureAwait(false);
await BeginPlayingInternal(scoreToken, state).ConfigureAwait(false);
}
// Exceptions can occur if, for instance, the locally played beatmap doesn't have a server-side counterpart.

View File

@ -184,7 +184,7 @@ namespace osu.Game.Online.Spectator
Header = header;
}
public int CompareTo(TimedFrame other) => Time.CompareTo(other.Time);
public int CompareTo(TimedFrame? other) => Time.CompareTo(other?.Time);
}
}
}

View File

@ -352,11 +352,13 @@ namespace osu.Game.Overlays
protected virtual DrawableChannel CreateDrawableChannel(Channel newChannel) => new DrawableChannel(newChannel);
private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args)
private void joinedChannelsChanged(object? sender, NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
IEnumerable<Channel> newChannels = args.NewItems.OfType<Channel>().Where(isChatChannel);
foreach (var channel in newChannels)
@ -365,6 +367,8 @@ namespace osu.Game.Overlays
break;
case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
IEnumerable<Channel> leftChannels = args.OldItems.OfType<Channel>().Where(isChatChannel);
foreach (var channel in leftChannels)
@ -384,7 +388,7 @@ namespace osu.Game.Overlays
}
}
private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs args)
private void availableChannelsChanged(object? sender, NotifyCollectionChangedEventArgs args)
=> channelListing.UpdateAvailableChannels(channelManager.AvailableChannels);
private void handleChatMessage(string message)

View File

@ -19,6 +19,7 @@ using System;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Extensions.IEnumerableExtensions;
using System.Collections.Specialized;
using System.Diagnostics;
using osu.Framework.Localisation;
using osu.Framework.Platform;
using osu.Game.Graphics.UserInterface;
@ -359,6 +360,8 @@ namespace osu.Game.Overlays.Comments
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
onRepliesAdded(args.NewItems.Cast<DrawableComment>());
break;

View File

@ -301,7 +301,7 @@ namespace osu.Game.Overlays
if (currentStepIndex < steps.Count)
{
var nextScreen = (Screen)Activator.CreateInstance(steps[currentStepIndex.Value]);
var nextScreen = (Screen)Activator.CreateInstance(steps[currentStepIndex.Value])!;
loadingShowDelegate = Scheduler.AddDelayed(() => loading.Show(), 200);
nextScreen.OnLoadComplete += _ =>

View File

@ -3,7 +3,6 @@
#nullable disable
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -131,8 +130,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
{
var metadata = beatmapInfo.Metadata;
Debug.Assert(metadata != null);
return new Drawable[]
{
new OsuSpriteText

View File

@ -4,7 +4,6 @@
#nullable disable
using System;
using System.Diagnostics;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
@ -269,8 +268,6 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{
var metadata = beatmapInfo.Metadata;
Debug.Assert(metadata != null);
return new Drawable[]
{
new OsuSpriteText

View File

@ -3,7 +3,6 @@
#nullable disable
using System.Diagnostics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
@ -29,8 +28,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input
var r = ruleset.CreateInstance();
Debug.Assert(r != null);
foreach (int variant in r.AvailableVariants)
Add(new VariantBindingsSubsection(ruleset, variant));
}

View File

@ -55,6 +55,6 @@ namespace osu.Game.Rulesets.Mods
/// <summary>
/// Create a fresh <see cref="Mod"/> instance based on this mod.
/// </summary>
Mod CreateInstance() => (Mod)Activator.CreateInstance(GetType());
Mod CreateInstance() => (Mod)Activator.CreateInstance(GetType())!;
}
}

View File

@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mods
foreach ((SettingSourceAttribute attr, PropertyInfo property) in this.GetOrderedSettingsSourceProperties())
{
var bindable = (IBindable)property.GetValue(this);
var bindable = (IBindable)property.GetValue(this)!;
if (!bindable.IsDefault)
tooltipTexts.Add($"{attr.Label} {bindable}");
@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Mods
/// </summary>
public virtual Mod DeepClone()
{
var result = (Mod)Activator.CreateInstance(GetType());
var result = (Mod)Activator.CreateInstance(GetType())!;
result.CopyFrom(this);
return result;
}
@ -150,8 +150,8 @@ namespace osu.Game.Rulesets.Mods
foreach (var (_, prop) in this.GetSettingsSourceProperties())
{
var targetBindable = (IBindable)prop.GetValue(this);
var sourceBindable = (IBindable)prop.GetValue(source);
var targetBindable = (IBindable)prop.GetValue(this)!;
var sourceBindable = (IBindable)prop.GetValue(source)!;
CopyAdjustedSetting(targetBindable, sourceBindable);
}
@ -183,9 +183,9 @@ namespace osu.Game.Rulesets.Mods
}
}
public bool Equals(IMod other) => other is Mod them && Equals(them);
public bool Equals(IMod? other) => other is Mod them && Equals(them);
public bool Equals(Mod other)
public bool Equals(Mod? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
@ -209,16 +209,16 @@ namespace osu.Game.Rulesets.Mods
/// <summary>
/// Reset all custom settings for this mod back to their defaults.
/// </summary>
public virtual void ResetSettingsToDefaults() => CopyFrom((Mod)Activator.CreateInstance(GetType()));
public virtual void ResetSettingsToDefaults() => CopyFrom((Mod)Activator.CreateInstance(GetType())!);
private class ModSettingsEqualityComparer : IEqualityComparer<IBindable>
{
public static ModSettingsEqualityComparer Default { get; } = new ModSettingsEqualityComparer();
public bool Equals(IBindable x, IBindable y)
public bool Equals(IBindable? x, IBindable? y)
{
object xValue = x.GetUnderlyingSettingValue();
object yValue = y.GetUnderlyingSettingValue();
object? xValue = x?.GetUnderlyingSettingValue();
object? yValue = y?.GetUnderlyingSettingValue();
return EqualityComparer<object>.Default.Equals(xValue, yValue);
}

View File

@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Bindables;
@ -57,12 +58,16 @@ namespace osu.Game.Rulesets.Objects
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
foreach (var c in args.NewItems.Cast<PathControlPoint>())
c.Changed += invalidate;
break;
case NotifyCollectionChangedAction.Reset:
case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
foreach (var c in args.OldItems.Cast<PathControlPoint>())
c.Changed -= invalidate;
break;

View File

@ -89,6 +89,8 @@ namespace osu.Game.Rulesets
// Confine all mods of each mod type into a single IEnumerable<Mod>
.SelectMany(GetModsFor)
// Filter out all null mods
// This is to handle old rulesets which were doing mods bad. Can be removed at some point we are sure nulls will not appear here.
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
.Where(mod => mod != null)
// Resolve MultiMods as their .Mods property
.SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod });

View File

@ -53,21 +53,21 @@ namespace osu.Game.Rulesets
public bool Equals(IRulesetInfo? other) => other is RulesetInfo r && Equals(r);
public int CompareTo(RulesetInfo other)
public int CompareTo(RulesetInfo? other)
{
if (OnlineID >= 0 && other.OnlineID >= 0)
if (OnlineID >= 0 && other?.OnlineID >= 0)
return OnlineID.CompareTo(other.OnlineID);
// Official rulesets are always given precedence for the time being.
if (OnlineID >= 0)
return -1;
if (other.OnlineID >= 0)
if (other?.OnlineID >= 0)
return 1;
return string.Compare(ShortName, other.ShortName, StringComparison.Ordinal);
return string.Compare(ShortName, other?.ShortName, StringComparison.Ordinal);
}
public int CompareTo(IRulesetInfo other)
public int CompareTo(IRulesetInfo? other)
{
if (!(other is RulesetInfo ruleset))
throw new ArgumentException($@"Object is not of type {nameof(RulesetInfo)}.", nameof(other));

View File

@ -127,7 +127,7 @@ namespace osu.Game.Rulesets
private void loadRulesetFromFile(string file)
{
string? filename = Path.GetFileNameWithoutExtension(file);
string filename = Path.GetFileNameWithoutExtension(file);
if (LoadedAssemblies.Values.Any(t => Path.GetFileNameWithoutExtension(t.Assembly.Location) == filename))
return;
@ -158,7 +158,7 @@ namespace osu.Game.Rulesets
}
catch (Exception e)
{
LogFailedLoad(assembly.GetName().Name.Split('.').Last(), e);
LogFailedLoad(assembly.GetName().Name!.Split('.').Last(), e);
}
}

View File

@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Scoring
{
foreach (var condition in FailConditions.GetInvocationList())
{
bool conditionResult = (bool)condition.Method.Invoke(condition.Target, new object[] { this, result });
bool conditionResult = (bool)condition.Method.Invoke(condition.Target, new object[] { this, result })!;
if (conditionResult)
return true;
}

View File

@ -317,7 +317,7 @@ namespace osu.Game.Scoring
#endregion
public bool Equals(ScoreInfo other) => other.ID == ID;
public bool Equals(ScoreInfo? other) => other?.ID == ID;
public override string ToString() => this.GetDisplayTitle();
}

View File

@ -17,7 +17,7 @@ namespace osu.Game.Scoring
protected override ArchiveDownloadRequest<IScoreInfo> CreateDownloadRequest(IScoreInfo score, bool minimiseDownload) => new DownloadReplayRequest(score);
public override ArchiveDownloadRequest<IScoreInfo> GetExistingDownload(IScoreInfo model)
public override ArchiveDownloadRequest<IScoreInfo>? GetExistingDownload(IScoreInfo model)
=> CurrentDownloads.Find(r => r.Model.MatchesOnlineID(model));
}
}

View File

@ -5,6 +5,7 @@
using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Bindables;
using osu.Game.Beatmaps.ControlPoints;
@ -33,6 +34,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
break;
case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
foreach (var group in args.NewItems.OfType<ControlPointGroup>())
{
// as an optimisation, don't add a visualisation if there are already groups with the same types in close proximity.
@ -47,6 +50,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
break;
case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
foreach (var group in args.OldItems.OfType<ControlPointGroup>())
{
var matching = Children.SingleOrDefault(gv => ReferenceEquals(gv.Group, group));

View File

@ -58,6 +58,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
foreach (object o in args.NewItems)
{
if (blueprintMap.TryGetValue((T)o, out var blueprint))
@ -67,6 +69,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
break;
case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
foreach (object o in args.OldItems)
{
if (blueprintMap.TryGetValue((T)o, out var blueprint))

View File

@ -4,6 +4,7 @@
#nullable disable
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Bindables;
using osu.Game.Beatmaps.ControlPoints;
@ -33,11 +34,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
break;
case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
foreach (var group in args.NewItems.OfType<ControlPointGroup>())
Add(new TimelineControlPointGroup(group));
break;
case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
foreach (var group in args.OldItems.OfType<ControlPointGroup>())
{
var matching = Children.SingleOrDefault(gv => ReferenceEquals(gv.Group, group));

View File

@ -304,7 +304,7 @@ namespace osu.Game.Screens.Edit
/// <param name="index">The index of the <see cref="HitObject"/> to remove.</param>
public void RemoveAt(int index)
{
var hitObject = (HitObject)mutableHitObjects[index];
HitObject hitObject = (HitObject)mutableHitObjects[index]!;
mutableHitObjects.RemoveAt(index);

View File

@ -4,6 +4,7 @@
#nullable disable
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -197,11 +198,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
Debug.Assert(e.NewItems != null);
foreach (var added in e.NewItems.OfType<APIUser>())
addUser(added);
break;
case NotifyCollectionChangedAction.Remove:
Debug.Assert(e.OldItems != null);
foreach (var removed in e.OldItems.OfType<APIUser>())
removeUser(removed);
break;

View File

@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -117,10 +118,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
addRooms(args.NewItems.Cast<Room>());
break;
case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
removeRooms(args.OldItems.Cast<Room>());
break;
}

View File

@ -34,7 +34,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
/// <summary>
/// Whether all spectating players have finished loading.
/// </summary>
public bool AllPlayersLoaded => instances.All(p => p?.PlayerLoaded == true);
public bool AllPlayersLoaded => instances.All(p => p.PlayerLoaded);
protected override UserActivity InitialActivity => new UserActivity.SpectatingMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value);

View File

@ -349,7 +349,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
public void SelectBeatmap() => editPlaylistButton.TriggerClick();
private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) =>
private void onPlaylistChanged(object? sender, NotifyCollectionChangedEventArgs e) =>
playlistLength.Text = $"Length: {Playlist.GetTotalDuration()}";
private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0;

View File

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using osu.Framework.Allocation;
@ -153,11 +154,13 @@ namespace osu.Game.Screens.Play.HUD
}
}
private void playingUsersChanged(object sender, NotifyCollectionChangedEventArgs e)
private void playingUsersChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Remove:
Debug.Assert(e.OldItems != null);
foreach (int userId in e.OldItems.OfType<int>())
{
spectatorClient.StopWatchingUser(userId);

View File

@ -67,7 +67,7 @@ namespace osu.Game.Screens.Play.HUD
foreach (var (_, property) in component.GetSettingsSourceProperties())
{
var bindable = (IBindable)property.GetValue(component);
var bindable = (IBindable)property.GetValue(component)!;
if (!bindable.IsDefault)
Settings.Add(property.Name.ToSnakeCase(), bindable.GetUnderlyingSettingValue());
@ -88,7 +88,7 @@ namespace osu.Game.Screens.Play.HUD
{
try
{
Drawable d = (Drawable)Activator.CreateInstance(Type);
Drawable d = (Drawable)Activator.CreateInstance(Type)!;
d.ApplySkinnableInfo(this);
return d;
}

View File

@ -259,10 +259,7 @@ namespace osu.Game.Screens.Utility
var displayMode = host.Window?.CurrentDisplayMode.Value;
string exclusive = "unknown";
if (host.Renderer is IWindowsRenderer windowsRenderer)
exclusive = windowsRenderer.FullscreenCapability.ToString();
string exclusive = (host.Renderer as IWindowsRenderer)?.FullscreenCapability.ToString() ?? "unknown";
statusText.Clear();

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -59,9 +58,7 @@ namespace osu.Game.Skinning.Editor
{
try
{
var instance = (Drawable)Activator.CreateInstance(type);
Debug.Assert(instance != null);
Drawable instance = (Drawable)Activator.CreateInstance(type)!;
if (!((ISkinnableDrawable)instance).IsEditable) return;

View File

@ -16,7 +16,7 @@ namespace osu.Game.Skinning
}
protected virtual string RulesetPrefix => string.Empty;
protected virtual string ComponentName => Component.ToString();
protected virtual string ComponentName => Component.ToString() ?? string.Empty;
public string LookupName =>
string.Join('/', new[] { "Gameplay", RulesetPrefix, ComponentName }.Where(s => !string.IsNullOrEmpty(s)));

View File

@ -105,7 +105,7 @@ namespace osu.Game.Skinning
return SkinUtils.As<TValue>(GetComboColour(Configuration, comboColour.ColourIndex, comboColour.Combo));
case SkinCustomColourLookup customColour:
return SkinUtils.As<TValue>(getCustomColour(Configuration, customColour.Lookup.ToString()));
return SkinUtils.As<TValue>(getCustomColour(Configuration, customColour.Lookup.ToString() ?? string.Empty));
case LegacyManiaSkinConfigurationLookup maniaLookup:
if (!AllowManiaSkin)
@ -277,7 +277,7 @@ namespace osu.Game.Skinning
=> source.CustomColours.TryGetValue(lookup, out var col) ? new Bindable<Color4>(col) : null;
private IBindable<string>? getManiaImage(LegacyManiaSkinConfiguration source, string lookup)
=> source.ImageLookups.TryGetValue(lookup, out string image) ? new Bindable<string>(image) : null;
=> source.ImageLookups.TryGetValue(lookup, out string? image) ? new Bindable<string>(image) : null;
private IBindable<TValue>? legacySettingLookup<TValue>(SkinConfiguration.LegacySetting legacySetting)
where TValue : notnull
@ -298,7 +298,7 @@ namespace osu.Game.Skinning
{
try
{
if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out string val))
if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString() ?? string.Empty, out string? val))
{
// special case for handling skins which use 1 or 0 to signify a boolean state.
// ..or in some cases 2 (https://github.com/ppy/osu/issues/18579).

View File

@ -52,7 +52,7 @@ namespace osu.Game.Skinning
private string? getPathForFile(string filename)
{
if (fileToStoragePathMapping.Value.TryGetValue(filename.ToLowerInvariant(), out string path))
if (fileToStoragePathMapping.Value.TryGetValue(filename.ToLowerInvariant(), out string? path))
return path;
return null;

View File

@ -56,7 +56,7 @@ namespace osu.Game.Skinning
return new TrianglesSkin(this, resources);
}
return (Skin)Activator.CreateInstance(type, this, resources);
return (Skin)Activator.CreateInstance(type, this, resources)!;
}
public IList<RealmNamedFileUsage> Files { get; } = null!;

View File

@ -185,7 +185,7 @@ namespace osu.Game.Tests.Beatmaps
private Stream openResource(string name)
{
string localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)).AsNonNull();
string localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().Location).Path)).AsNonNull();
return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}");
}

View File

@ -54,7 +54,7 @@ namespace osu.Game.Tests.Beatmaps
private Stream openResource(string name)
{
string localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)).AsNonNull();
string localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().Location).Path)).AsNonNull();
return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}");
}

View File

@ -24,8 +24,8 @@ namespace osu.Game.Tests
{
while (!cancellationToken.IsCancellationRequested)
{
await API.PerformAsync(CreateInitialFetchRequest());
await Task.Delay(1000, cancellationToken);
await API.PerformAsync(CreateInitialFetchRequest()).ConfigureAwait(true);
await Task.Delay(1000, cancellationToken).ConfigureAwait(true);
}
}, cancellationToken);