Merge branch 'master' into single-bind-reset-button

This commit is contained in:
Swords
2021-05-15 12:05:42 +10:00
committed by GitHub
31 changed files with 662 additions and 54 deletions

View File

@ -5,6 +5,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Skinning.Legacy;
using osu.Game.Skinning;
@ -27,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
}));
AddToggleStep("Toggle passing", passing => this.ChildrenOfType<LegacyTaikoScroller>().ForEach(s => s.LastResult.Value =
new JudgementResult(null, new Judgement()) { Type = passing ? HitResult.Great : HitResult.Miss }));
new JudgementResult(new HitObject(), new Judgement()) { Type = passing ? HitResult.Great : HitResult.Miss }));
AddToggleStep("toggle playback direction", reversed => this.reversed = reversed);
}

View File

@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
base.Update();
// store X before checking wide enough so if we perform layout there is no positional discrepancy.
float currentX = (InternalChildren?.FirstOrDefault()?.X ?? 0) - (float)Clock.ElapsedFrameTime * 0.1f;
float currentX = (InternalChildren.FirstOrDefault()?.X ?? 0) - (float)Clock.ElapsedFrameTime * 0.1f;
// ensure we have enough sprites
if (!InternalChildren.Any()

View File

@ -12,6 +12,7 @@ using osu.Framework.Platform;
using osu.Game.IPC;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Database;
@ -264,7 +265,7 @@ namespace osu.Game.Tests.Beatmaps.IO
// change filename
var firstFile = new FileInfo(Directory.GetFiles(extractedFolder).First());
firstFile.MoveTo(Path.Combine(firstFile.DirectoryName, $"{firstFile.Name}-changed{firstFile.Extension}"));
firstFile.MoveTo(Path.Combine(firstFile.DirectoryName.AsNonNull(), $"{firstFile.Name}-changed{firstFile.Extension}"));
using (var zip = ZipArchive.Create())
{

View File

@ -146,7 +146,7 @@ namespace osu.Game.Tests.Mods
if (isValid)
Assert.IsNull(invalid);
else
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
}
public abstract class CustomMod1 : Mod

View File

@ -4,6 +4,7 @@
using System.Linq;
using Humanizer;
using NUnit.Framework;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Testing;
using osu.Game.Online.Multiplayer;
using osu.Game.Tests.Visual.Multiplayer;
@ -34,7 +35,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
changeState(6, MultiplayerUserState.WaitingForLoad);
checkPlayingUserCount(6);
AddStep("another user left", () => Client.RemoveUser(Client.Room?.Users.Last().User));
AddStep("another user left", () => Client.RemoveUser((Client.Room?.Users.Last().User).AsNonNull()));
checkPlayingUserCount(5);
AddStep("leave room", () => Client.LeaveRoom());

View File

@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
@ -45,14 +46,14 @@ namespace osu.Game.Tests.Visual.Online
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
args.NewItems.Cast<Mod>().ForEach(mod => selectedMods.Add(new OsuSpriteText
args.NewItems.AsNonNull().Cast<Mod>().ForEach(mod => selectedMods.Add(new OsuSpriteText
{
Text = mod.Acronym,
}));
break;
case NotifyCollectionChangedAction.Remove:
args.OldItems.Cast<Mod>().ForEach(mod =>
args.OldItems.AsNonNull().Cast<Mod>().ForEach(mod =>
{
foreach (var selected in selectedMods)
{

View File

@ -264,7 +264,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private void moveLogoFacade()
{
if (!(logoFacade?.Transforms).Any() && !(transferContainer?.Transforms).Any())
if (!logoFacade.Transforms.Any() && !transferContainer.Transforms.Any())
{
Random random = new Random();
trackingContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300);

View File

@ -7,6 +7,7 @@ using System.Linq;
using JetBrains.Annotations;
using Microsoft.Win32;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Threading;
@ -77,8 +78,8 @@ namespace osu.Game.Tournament.IPC
using (var stream = IPCStorage.GetStream(file_ipc_filename))
using (var sr = new StreamReader(stream))
{
var beatmapId = int.Parse(sr.ReadLine());
var mods = int.Parse(sr.ReadLine());
var beatmapId = int.Parse(sr.ReadLine().AsNonNull());
var mods = int.Parse(sr.ReadLine().AsNonNull());
if (lastBeatmapId != beatmapId)
{
@ -124,7 +125,7 @@ namespace osu.Game.Tournament.IPC
using (var stream = IPCStorage.GetStream(file_ipc_state_filename))
using (var sr = new StreamReader(stream))
{
State.Value = (TourneyState)Enum.Parse(typeof(TourneyState), sr.ReadLine());
State.Value = (TourneyState)Enum.Parse(typeof(TourneyState), sr.ReadLine().AsNonNull());
}
}
catch (Exception)

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading;
@ -83,6 +82,12 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
if (beatmap.Metadata != null)
beatmap.Metadata.AuthorID = res.AuthorID;
if (beatmap.BeatmapSet.Metadata != null)
beatmap.BeatmapSet.Metadata.AuthorID = res.AuthorID;
LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
}
}
@ -157,7 +162,7 @@ namespace osu.Game.Beatmaps
using (var cmd = db.CreateCommand())
{
cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path";
cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path";
cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash));
cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value));
@ -174,6 +179,12 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0);
beatmap.OnlineBeatmapID = reader.GetInt32(1);
if (beatmap.Metadata != null)
beatmap.Metadata.AuthorID = reader.GetInt32(3);
if (beatmap.BeatmapSet.Metadata != null)
beatmap.BeatmapSet.Metadata.AuthorID = reader.GetInt32(3);
LogForModel(set, $"Cached local retrieval for {beatmap}.");
return true;
}
@ -194,17 +205,6 @@ namespace osu.Game.Beatmaps
cacheDownloadRequest?.Dispose();
updateScheduler?.Dispose();
}
[Serializable]
[SuppressMessage("ReSharper", "InconsistentNaming")]
private class CachedOnlineBeatmapLookup
{
public int approved { get; set; }
public int? beatmapset_id { get; set; }
public int? beatmap_id { get; set; }
}
}
}
}

View File

@ -34,6 +34,21 @@ namespace osu.Game.Beatmaps
[JsonIgnore]
public List<BeatmapSetInfo> BeatmapSets { get; set; }
/// <summary>
/// Helper property to deserialize a username to <see cref="User"/>.
/// </summary>
[JsonProperty(@"user_id")]
[Column("AuthorID")]
public int AuthorID
{
get => Author?.Id ?? 1;
set
{
Author ??= new User();
Author.Id = value;
}
}
/// <summary>
/// Helper property to deserialize a username to <see cref="User"/>.
/// </summary>
@ -42,7 +57,11 @@ namespace osu.Game.Beatmaps
public string AuthorString
{
get => Author?.Username;
set => Author = new User { Username = value };
set
{
Author ??= new User();
Author.Username = value;
}
}
/// <summary>

View File

@ -6,6 +6,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Extensions.ObjectExtensions;
namespace osu.Game.Extensions
{
@ -50,7 +51,7 @@ namespace osu.Game.Extensions
}
else if (continuationTask.IsFaulted)
{
tcs.TrySetException(continuationTask.Exception);
tcs.TrySetException(continuationTask.Exception.AsNonNull());
}
else
{

View File

@ -38,6 +38,7 @@ namespace osu.Game.IO.Legacy
/// <summary> Reads a string from the buffer. Overrides the base implementation so it can cope with nulls. </summary>
public override string ReadString()
{
// ReSharper disable once AssignNullToNotNullAttribute
if (ReadByte() == 0) return null;
return base.ReadString();

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using osu.Framework.Extensions.ObjectExtensions;
namespace osu.Game.IO.Serialization.Converters
{
@ -60,7 +61,7 @@ namespace osu.Game.IO.Serialization.Converters
throw new JsonException("Expected $type token.");
var typeName = lookupTable[(int)tok["$type"]];
var instance = (T)Activator.CreateInstance(Type.GetType(typeName));
var instance = (T)Activator.CreateInstance(Type.GetType(typeName).AsNonNull());
serializer.Populate(itemReader, instance);
list.Add(instance);

View File

@ -0,0 +1,511 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20210514062639_AddAuthorIdToBeatmapMetadata")]
partial class AddAuthorIdToBeatmapMetadata
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<double>("SliderMultiplier");
b.Property<double>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("AudioLeadIn");
b.Property<double>("BPM");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<bool>("EpilepsyWarning");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<double>("Length");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<int>("AuthorID")
.HasColumnName("AuthorID");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset>("DateAdded");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Key")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<int?>("SkinInfoID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("SkinInfoID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int?>("ScoreInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("ScoreInfoID");
b.ToTable("ScoreFileInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("Accuracy")
.HasColumnType("DECIMAL(1,4)");
b.Property<int>("BeatmapInfoID");
b.Property<int>("Combo");
b.Property<DateTimeOffset>("Date");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int>("MaxCombo");
b.Property<string>("ModsJson")
.HasColumnName("Mods");
b.Property<long?>("OnlineScoreID");
b.Property<double?>("PP");
b.Property<int>("Rank");
b.Property<int>("RulesetID");
b.Property<string>("StatisticsJson")
.HasColumnName("Statistics");
b.Property<long>("TotalScore");
b.Property<int?>("UserID")
.HasColumnName("UserID");
b.Property<string>("UserString")
.HasColumnName("User");
b.HasKey("ID");
b.HasIndex("BeatmapInfoID");
b.HasIndex("OnlineScoreID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("ScoreInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Settings")
.HasForeignKey("SkinInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Scoring.ScoreInfo")
.WithMany("Files")
.HasForeignKey("ScoreInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany("Scores")
.HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class AddAuthorIdToBeatmapMetadata : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "AuthorID",
table: "BeatmapMetadata",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "AuthorID",
table: "BeatmapMetadata");
}
}
}

View File

@ -126,6 +126,9 @@ namespace osu.Game.Migrations
b.Property<string>("AudioFile");
b.Property<int>("AuthorID")
.HasColumnName("AuthorID");
b.Property<string>("AuthorString")
.HasColumnName("Author");

View File

@ -270,7 +270,7 @@ namespace osu.Game.Online.API
{
try
{
return JObject.Parse(req.GetResponseString()).SelectToken("form_error", true).AsNonNull().ToObject<RegistrationRequest.RegistrationRequestErrors>();
return JObject.Parse(req.GetResponseString().AsNonNull()).SelectToken("form_error", true).AsNonNull().ToObject<RegistrationRequest.RegistrationRequestErrors>();
}
catch
{

View File

@ -24,7 +24,13 @@ namespace osu.Game.Online.Spectator
[Key(2)]
public IEnumerable<APIMod> Mods { get; set; } = Enumerable.Empty<APIMod>();
public bool Equals(SpectatorState other) => BeatmapID == other?.BeatmapID && Mods.SequenceEqual(other?.Mods) && RulesetID == other?.RulesetID;
public bool Equals(SpectatorState other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return BeatmapID == other.BeatmapID && Mods.SequenceEqual(other.Mods) && RulesetID == other.RulesetID;
}
public override string ToString() => $"Beatmap:{BeatmapID} Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}";
}

View File

@ -4,6 +4,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Testing;
namespace osu.Game.Rulesets
@ -27,7 +28,7 @@ namespace osu.Game.Rulesets
{
if (!Available) return null;
var ruleset = (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo));
var ruleset = (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo).AsNonNull());
// overwrite the pre-populated RulesetInfo with a potentially database attached copy.
ruleset.RulesetInfo = this;

View File

@ -7,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using osu.Framework;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Database;
@ -111,7 +112,7 @@ namespace osu.Game.Rulesets
{
try
{
var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo))).RulesetInfo;
var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo).AsNonNull())).RulesetInfo;
r.Name = instanceInfo.Name;
r.ShortName = instanceInfo.ShortName;

View File

@ -635,6 +635,9 @@ namespace osu.Game.Screens.Edit
case EditorScreenMode.Verify:
currentScreen = new VerifyScreen();
break;
default:
throw new InvalidOperationException("Editor menu bar switched to an unsupported mode");
}
LoadComponentAsync(currentScreen, newScreen =>

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -193,14 +194,16 @@ namespace osu.Game.Skinning.Editor
SelectedComponents.Add(component);
}
private IEnumerable<ISkinnableTarget> availableTargets => targetScreen.ChildrenOfType<ISkinnableTarget>();
private ISkinnableTarget getTarget(SkinnableTarget target)
{
return targetScreen.ChildrenOfType<ISkinnableTarget>().FirstOrDefault(c => c.Target == target);
return availableTargets.FirstOrDefault(c => c.Target == target);
}
private void revert()
{
SkinnableTargetContainer[] targetContainers = targetScreen.ChildrenOfType<SkinnableTargetContainer>().ToArray();
ISkinnableTarget[] targetContainers = availableTargets.ToArray();
foreach (var t in targetContainers)
{
@ -216,7 +219,7 @@ namespace osu.Game.Skinning.Editor
if (!hasBegunMutating)
return;
SkinnableTargetContainer[] targetContainers = targetScreen.ChildrenOfType<SkinnableTargetContainer>().ToArray();
ISkinnableTarget[] targetContainers = availableTargets.ToArray();
foreach (var t in targetContainers)
currentSkin.Value.UpdateDrawableTarget(t);
@ -237,5 +240,11 @@ namespace osu.Game.Skinning.Editor
{
this.FadeOut(TRANSITION_DURATION, Easing.OutQuint);
}
public void DeleteItems(ISkinnableDrawable[] items)
{
foreach (var item in items)
availableTargets.FirstOrDefault(t => t.Components.Contains(item))?.Remove(item);
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
@ -17,6 +18,9 @@ namespace osu.Game.Skinning.Editor
{
public class SkinSelectionHandler : SelectionHandler<ISkinnableDrawable>
{
[Resolved]
private SkinEditor skinEditor { get; set; }
public override bool HandleRotation(float angle)
{
// TODO: this doesn't correctly account for origin/anchor specs being different in a multi-selection.
@ -72,14 +76,8 @@ namespace osu.Game.Skinning.Editor
SelectionBox.CanReverse = false;
}
protected override void DeleteItems(IEnumerable<ISkinnableDrawable> items)
{
foreach (var i in items)
{
((Drawable)i).Expire();
SelectedItems.Remove(i);
}
}
protected override void DeleteItems(IEnumerable<ISkinnableDrawable> items) =>
skinEditor.DeleteItems(items.ToArray());
protected override IEnumerable<MenuItem> GetContextMenuItemsForSelection(IEnumerable<SelectionBlueprint<ISkinnableDrawable>> selection)
{

View File

@ -37,8 +37,15 @@ namespace osu.Game.Skinning
void Reload();
/// <summary>
/// Add the provided item to this target.
/// Add a new skinnable component to this target.
/// </summary>
/// <param name="drawable">The component to add.</param>
void Add(ISkinnableDrawable drawable);
/// <summary>
/// Remove an existing skinnable component from this target.
/// </summary>
/// <param name="component">The component to remove.</param>
public void Remove(ISkinnableDrawable component);
}
}

View File

@ -56,8 +56,12 @@ namespace osu.Game.Skinning
continue;
string jsonContent = Encoding.UTF8.GetString(bytes);
var deserializedContent = JsonConvert.DeserializeObject<IEnumerable<SkinnableInfo>>(jsonContent);
DrawableComponentInfo[skinnableTarget] = JsonConvert.DeserializeObject<IEnumerable<SkinnableInfo>>(jsonContent).ToArray();
if (deserializedContent == null)
continue;
DrawableComponentInfo[skinnableTarget] = deserializedContent.ToArray();
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.IO.Stores;
using osu.Game.Configuration;
using osu.Game.Database;
@ -32,7 +33,7 @@ namespace osu.Game.Skinning
var type = string.IsNullOrEmpty(InstantiationInfo)
// handle the case of skins imported before InstantiationInfo was added.
? typeof(LegacySkin)
: Type.GetType(InstantiationInfo);
: Type.GetType(InstantiationInfo).AsNonNull();
if (type == typeof(DefaultLegacySkin))
return (Skin)Activator.CreateInstance(type, this, legacyDefaultResources, resources);

View File

@ -43,10 +43,7 @@ namespace osu.Game.Skinning
}
}
/// <summary>
/// Add a new skinnable component to this target.
/// </summary>
/// <param name="component">The component to add.</param>
/// <inheritdoc cref="ISkinnableTarget"/>
/// <exception cref="NotSupportedException">Thrown when attempting to add an element to a target which is not supported by the current skin.</exception>
/// <exception cref="ArgumentException">Thrown if the provided instance is not a <see cref="Drawable"/>.</exception>
public void Add(ISkinnableDrawable component)
@ -55,12 +52,27 @@ namespace osu.Game.Skinning
throw new NotSupportedException("Attempting to add a new component to a target container which is not supported by the current skin.");
if (!(component is Drawable drawable))
throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(drawable));
throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(component));
content.Add(drawable);
components.Add(component);
}
/// <inheritdoc cref="ISkinnableTarget"/>
/// <exception cref="NotSupportedException">Thrown when attempting to add an element to a target which is not supported by the current skin.</exception>
/// <exception cref="ArgumentException">Thrown if the provided instance is not a <see cref="Drawable"/>.</exception>
public void Remove(ISkinnableDrawable component)
{
if (content == null)
throw new NotSupportedException("Attempting to remove a new component from a target container which is not supported by the current skin.");
if (!(component is Drawable drawable))
throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(component));
content.Remove(drawable);
components.Remove(component);
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);

View File

@ -9,6 +9,7 @@ using System.Reflection;
using Newtonsoft.Json;
using NUnit.Framework;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
@ -164,7 +165,7 @@ namespace osu.Game.Tests.Beatmaps
private Stream openResource(string name)
{
var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path));
var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)).AsNonNull();
return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}");
}

View File

@ -5,6 +5,7 @@ using System;
using System.IO;
using System.Reflection;
using NUnit.Framework;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
@ -41,7 +42,7 @@ namespace osu.Game.Tests.Beatmaps
private Stream openResource(string name)
{
var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path));
var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)).AsNonNull();
return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}");
}

View File

@ -92,7 +92,7 @@ namespace osu.Game.Utils
/// <param name="mods">The mods to check.</param>
/// <param name="invalidMods">Invalid mods, if any were found. Can be null if all mods were valid.</param>
/// <returns>Whether the input mods were all valid. If false, <paramref name="invalidMods"/> will contain all invalid entries.</returns>
public static bool CheckValidForGameplay(IEnumerable<Mod> mods, out List<Mod>? invalidMods)
public static bool CheckValidForGameplay(IEnumerable<Mod> mods, [NotNullWhen(false)] out List<Mod>? invalidMods)
{
mods = mods.ToArray();

View File

@ -18,7 +18,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeTypeModifiers/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AssignedValueIsNeverUsed/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AssignmentIsFullyDiscarded/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AssignNullToNotNullAttribute/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AssignNullToNotNullAttribute/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AutoPropertyCanBeMadeGetOnly_002EGlobal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AutoPropertyCanBeMadeGetOnly_002ELocal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=BadAttributeBracketsSpaces/@EntryIndexedValue">WARNING</s:String>