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

Merge branch 'master' into new-multiplayer-playlist

This commit is contained in:
Dan Balasescu 2021-11-25 19:07:37 +09:00
commit 942e48ffbd
134 changed files with 1069 additions and 847 deletions

View File

@ -38,7 +38,7 @@ jobs:
run: dotnet build -c Debug -warnaserror osu.Desktop.slnf
- name: Test
run: dotnet test $pwd/*.Tests/bin/Debug/*/*.Tests.dll --blame-crash --blame-hang --blame-hang-timeout 5m --logger "trx;LogFileName=TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx"
run: dotnet test $pwd/*.Tests/bin/Debug/*/*.Tests.dll --logger "trx;LogFileName=TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx"
shell: pwsh
# Attempt to upload results even if test fails.
@ -48,7 +48,7 @@ jobs:
if: ${{ always() }}
with:
name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
path: ${{github.workspace}}/TestResults/**/*
path: ${{github.workspace}}/TestResults/TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx
build-only-android:
name: Build only (Android)

View File

@ -8,4 +8,5 @@ M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900)
T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods.
T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods.
M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast<T>() instead.
T:NuGet.Packaging.CollectionExtensions;Don't use internal extension methods.
M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast<T>() instead.

View File

@ -52,7 +52,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1112.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1119.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1124.0" />
</ItemGroup>
<ItemGroup Label="Transitive Dependencies">
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->

View File

@ -108,7 +108,10 @@ namespace osu.Desktop
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.GlobalRank > 0 ? $" (rank #{user.Value.Statistics.GlobalRank:N0})" : string.Empty);
// update ruleset
presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom";
int onlineID = ruleset.Value.OnlineID;
bool isLegacyRuleset = onlineID >= 0 && onlineID <= ILegacyRuleset.MAX_LEGACY_RULESET_ID;
presence.Assets.SmallImageKey = isLegacyRuleset ? $"mode_{onlineID}" : "mode_custom";
presence.Assets.SmallImageText = ruleset.Value.Name;
client.SetPresence(presence);

View File

@ -103,7 +103,10 @@ namespace osu.Desktop.Updater
}
else
{
// In the case of an error, a separate notification will be displayed.
notification.State = ProgressNotificationState.Cancelled;
notification.Close();
Logger.Error(e, @"update failed!");
}
}

View File

@ -30,7 +30,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
var score = decoder.Parse(resourceStream);
Assert.AreEqual(3, score.ScoreInfo.Ruleset.ID);
Assert.AreEqual(3, score.ScoreInfo.Ruleset.OnlineID);
Assert.AreEqual(2, score.ScoreInfo.Statistics[HitResult.Great]);
Assert.AreEqual(1, score.ScoreInfo.Statistics[HitResult.Good]);

View File

@ -584,7 +584,7 @@ namespace osu.Game.Tests.Beatmaps.IO
{
OnlineID = 1,
Metadata = metadata,
Beatmaps = new List<BeatmapInfo>
Beatmaps =
{
new BeatmapInfo
{
@ -596,7 +596,7 @@ namespace osu.Game.Tests.Beatmaps.IO
{
OnlineID = 2,
Metadata = metadata,
Status = BeatmapSetOnlineStatus.Loved,
Status = BeatmapOnlineStatus.Loved,
BaseDifficulty = difficulty
}
}

View File

@ -100,8 +100,8 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestKeyEqualsWithDifferentModInstances()
{
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
Assert.That(key1, Is.EqualTo(key2));
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
@ -110,8 +110,8 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestKeyEqualsWithDifferentModOrder()
{
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
Assert.That(key1, Is.EqualTo(key2));
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
@ -120,8 +120,8 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestKeyDoesntEqualWithDifferentModSettings()
{
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } });
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } });
Assert.That(key1, Is.Not.EqualTo(key2));
Assert.That(key1.GetHashCode(), Is.Not.EqualTo(key2.GetHashCode()));
@ -130,8 +130,8 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestKeyEqualWithMatchingModSettings()
{
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
Assert.That(key1, Is.EqualTo(key2));
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));

View File

@ -74,6 +74,24 @@ namespace osu.Game.Tests.Database
});
}
[Test]
public void TestAccessFileAfterImport()
{
RunTestWithRealmAsync(async (realmFactory, storage) =>
{
using var importer = new BeatmapImporter(realmFactory, storage);
using var store = new RealmRulesetStore(realmFactory, storage);
var imported = await LoadOszIntoStore(importer, realmFactory.Context);
var beatmap = imported.Beatmaps.First();
var file = beatmap.File;
Assert.NotNull(file);
Assert.AreEqual(beatmap.Hash, file!.File.Hash);
});
}
[Test]
public void TestImportThenDelete()
{
@ -532,7 +550,7 @@ namespace osu.Game.Tests.Database
new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), metadata)
{
OnlineID = 2,
Status = BeatmapSetOnlineStatus.Loved,
Status = BeatmapOnlineStatus.Loved,
}
}
};

View File

@ -29,9 +29,9 @@ namespace osu.Game.Tests.Models
{
var mock = new Mock<IBeatmapSetInfo>();
mock.Setup(m => m.Metadata!.Artist).Returns("artist");
mock.Setup(m => m.Metadata!.Title).Returns("title");
mock.Setup(m => m.Metadata!.Author.Username).Returns("author");
mock.Setup(m => m.Metadata.Artist).Returns("artist");
mock.Setup(m => m.Metadata.Title).Returns("title");
mock.Setup(m => m.Metadata.Author.Username).Returns("author");
Assert.That(mock.Object.GetDisplayString(), Is.EqualTo("artist - title (author)"));
}
@ -41,9 +41,9 @@ namespace osu.Game.Tests.Models
{
var mock = new Mock<IBeatmapSetInfo>();
mock.Setup(m => m.Metadata!.Artist).Returns("artist");
mock.Setup(m => m.Metadata!.Title).Returns("title");
mock.Setup(m => m.Metadata!.Author.Username).Returns(string.Empty);
mock.Setup(m => m.Metadata.Artist).Returns("artist");
mock.Setup(m => m.Metadata.Title).Returns("title");
mock.Setup(m => m.Metadata.Author.Username).Returns(string.Empty);
Assert.That(mock.Object.GetDisplayString(), Is.EqualTo("artist - title"));
}

View File

@ -16,7 +16,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
{
private BeatmapInfo getExampleBeatmap() => new BeatmapInfo
{
Ruleset = new RulesetInfo { ID = 5 },
Ruleset = new RulesetInfo { OnlineID = 5 },
StarRating = 4.0d,
BaseDifficulty = new BeatmapDifficulty
{
@ -38,7 +38,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
Length = 2500,
BPM = 160,
BeatDivisor = 12,
Status = BeatmapSetOnlineStatus.Loved
Status = BeatmapOnlineStatus.Loved
};
[Test]
@ -57,7 +57,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
var exampleBeatmapInfo = getExampleBeatmap();
var criteria = new FilterCriteria
{
Ruleset = new RulesetInfo { ID = 6 }
Ruleset = new RulesetInfo { OnlineID = 6 }
};
var carouselItem = new CarouselBeatmap(exampleBeatmapInfo);
carouselItem.Filter(criteria);
@ -70,7 +70,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
var exampleBeatmapInfo = getExampleBeatmap();
var criteria = new FilterCriteria
{
Ruleset = new RulesetInfo { ID = 6 },
Ruleset = new RulesetInfo { OnlineID = 6 },
AllowConvertedBeatmaps = true
};
var carouselItem = new CarouselBeatmap(exampleBeatmapInfo);
@ -86,7 +86,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
var exampleBeatmapInfo = getExampleBeatmap();
var criteria = new FilterCriteria
{
Ruleset = new RulesetInfo { ID = 6 },
Ruleset = new RulesetInfo { OnlineID = 6 },
AllowConvertedBeatmaps = true,
ApproachRate = new FilterCriteria.OptionalRange<float>
{
@ -107,7 +107,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
var exampleBeatmapInfo = getExampleBeatmap();
var criteria = new FilterCriteria
{
Ruleset = new RulesetInfo { ID = 6 },
Ruleset = new RulesetInfo { OnlineID = 6 },
AllowConvertedBeatmaps = true,
BPM = new FilterCriteria.OptionalRange<double>
{
@ -132,7 +132,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
var exampleBeatmapInfo = getExampleBeatmap();
var criteria = new FilterCriteria
{
Ruleset = new RulesetInfo { ID = 6 },
Ruleset = new RulesetInfo { OnlineID = 6 },
AllowConvertedBeatmaps = true,
SearchText = terms
};

View File

@ -162,9 +162,9 @@ namespace osu.Game.Tests.NonVisual.Filtering
FilterQueryParser.ApplyQueries(filterCriteria, query);
Assert.AreEqual("I want the pp", filterCriteria.SearchText.Trim());
Assert.AreEqual(4, filterCriteria.SearchTerms.Length);
Assert.AreEqual(BeatmapSetOnlineStatus.Ranked, filterCriteria.OnlineStatus.Min);
Assert.AreEqual(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Min);
Assert.IsTrue(filterCriteria.OnlineStatus.IsLowerInclusive);
Assert.AreEqual(BeatmapSetOnlineStatus.Ranked, filterCriteria.OnlineStatus.Max);
Assert.AreEqual(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Max);
Assert.IsTrue(filterCriteria.OnlineStatus.IsUpperInclusive);
}

View File

@ -72,7 +72,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
RoomManager.CreateRoom(newRoom);
});
AddUntilStep("wait for room join", () => Client.Room != null);
AddUntilStep("wait for room join", () => RoomJoined);
checkPlayingUserCount(1);
}

View File

@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -143,7 +143,10 @@ namespace osu.Game.Tests.Online
var beatmap = decoder.Decode(reader);
info = beatmap.BeatmapInfo;
info.BeatmapSet.Beatmaps = new List<BeatmapInfo> { info };
Debug.Assert(info.BeatmapSet != null);
info.BeatmapSet.Beatmaps.Add(info);
info.BeatmapSet.Metadata = info.Metadata;
info.MD5Hash = stream.ComputeMD5Hash();
info.Hash = stream.ComputeSHA2Hash();

View File

@ -2,10 +2,18 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using NUnit.Framework;
using osu.Framework.Extensions;
using osu.Framework.IO.Stores;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
namespace osu.Game.Tests.Resources
{
@ -56,5 +64,78 @@ namespace osu.Game.Tests.Resources
}
private static string getTempFilename() => temp_storage.GetFullPath(Guid.NewGuid() + ".osz");
private static int importId;
/// <summary>
/// Create a test beatmap set model.
/// </summary>
/// <param name="difficultyCount">Number of difficulties. If null, a random number between 1 and 20 will be used.</param>
/// <param name="rulesets">Rulesets to cycle through when creating difficulties. If <c>null</c>, osu! ruleset will be used.</param>
public static BeatmapSetInfo CreateTestBeatmapSetInfo(int? difficultyCount = null, RulesetInfo[] rulesets = null)
{
int j = 0;
RulesetInfo getRuleset() => rulesets?[j++ % rulesets.Length] ?? new OsuRuleset().RulesetInfo;
int setId = Interlocked.Increment(ref importId);
var metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
Artist = "Some Artist " + RNG.Next(0, 9),
Title = $"Some Song (set id {setId}) {Guid.NewGuid()}",
AuthorString = "Some Guy " + RNG.Next(0, 9),
};
var beatmapSet = new BeatmapSetInfo
{
OnlineID = setId,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
DateAdded = DateTimeOffset.UtcNow,
Metadata = metadata
};
foreach (var b in getBeatmaps(difficultyCount ?? RNG.Next(1, 20)))
beatmapSet.Beatmaps.Add(b);
return beatmapSet;
IEnumerable<BeatmapInfo> getBeatmaps(int count)
{
for (int i = 0; i < count; i++)
{
int beatmapId = setId * 1000 + i;
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
float diff = (float)i / count * 10;
string version = "Normal";
if (diff > 6.6)
version = "Insane";
else if (diff > 3.3)
version = "Hard";
var rulesetInfo = getRuleset();
yield return new BeatmapInfo
{
OnlineID = beatmapId,
DifficultyName = $"{version} {beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
StarRating = diff,
Length = length,
BPM = bpm,
Ruleset = rulesetInfo,
RulesetID = rulesetInfo.ID ?? -1,
Metadata = metadata,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = diff,
}
};
}
}
}
}
}

View File

@ -142,7 +142,7 @@ namespace osu.Game.Tests.Scores.IO
var scoreManager = osu.Dependencies.Get<ScoreManager>();
beatmapManager.Delete(beatmapManager.QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == imported.BeatmapInfo.ID)));
Assert.That(scoreManager.Query(s => s.ID == imported.ID).DeletePending, Is.EqualTo(true));
Assert.That(scoreManager.Query(s => s.Equals(imported)).DeletePending, Is.EqualTo(true));
var secondImport = await LoadScoreIntoOsu(osu, imported);
Assert.That(secondImport, Is.Null);

View File

@ -70,7 +70,7 @@ namespace osu.Game.Tests.Testing
{
// temporary ID to let RulesetConfigCache pass our
// config manager to the ruleset dependencies.
RulesetInfo.ID = -1;
RulesetInfo.OnlineID = -1;
}
public override IResourceStore<byte[]> CreateResourceStore() => new NamespacedResourceStore<byte[]>(TestResources.GetStore(), @"Resources");

View File

@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
var withStatistics = CreateAPIBeatmapSet(Ruleset.Value);
withStatistics.Title = withStatistics.TitleUnicode = "play favourite stats";
withStatistics.Status = BeatmapSetOnlineStatus.Approved;
withStatistics.Status = BeatmapOnlineStatus.Approved;
withStatistics.FavouriteCount = 284_239;
withStatistics.PlayCount = 999_001;
withStatistics.Ranked = DateTimeOffset.Now.AddDays(-45);
@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
var someDifficulties = getManyDifficultiesBeatmapSet(11);
someDifficulties.Title = someDifficulties.TitleUnicode = "favourited";
someDifficulties.Title = someDifficulties.TitleUnicode = "some difficulties";
someDifficulties.Status = BeatmapSetOnlineStatus.Qualified;
someDifficulties.Status = BeatmapOnlineStatus.Qualified;
someDifficulties.HasFavourited = true;
someDifficulties.FavouriteCount = 1;
someDifficulties.NominationStatus = new BeatmapSetNominationStatus
@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
};
var manyDifficulties = getManyDifficultiesBeatmapSet(100);
manyDifficulties.Status = BeatmapSetOnlineStatus.Pending;
manyDifficulties.Status = BeatmapOnlineStatus.Pending;
var explicitMap = CreateAPIBeatmapSet(Ruleset.Value);
explicitMap.Title = someDifficulties.TitleUnicode = "explicit beatmap";

View File

@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
Origin = Anchor.Centre,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
ChildrenEnumerable = Enum.GetValues(typeof(BeatmapSetOnlineStatus)).Cast<BeatmapSetOnlineStatus>().Select(status => new BeatmapSetOnlineStatusPill
ChildrenEnumerable = Enum.GetValues(typeof(BeatmapOnlineStatus)).Cast<BeatmapOnlineStatus>().Select(status => new BeatmapSetOnlineStatusPill
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,

View File

@ -233,7 +233,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
prepareTokenResponse(true);
createPlayerTest(false, createRuleset: () => new OsuRuleset { RulesetInfo = { ID = rulesetId } });
createPlayerTest(false, createRuleset: () => new OsuRuleset { RulesetInfo = { OnlineID = rulesetId ?? -1 } });
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);

View File

@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
new Drawable[]
{
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
Recorder = recorder = new TestReplayRecorder(new Score
{
@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.Gameplay
},
new Drawable[]
{
playbackManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
ReplayInputHandler = new TestFramedReplayInputHandler(replay)
{

View File

@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
new Drawable[]
{
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
Recorder = new TestReplayRecorder(new Score
{
@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.Gameplay
},
new Drawable[]
{
playbackManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
ReplayInputHandler = new TestFramedReplayInputHandler(replay)
{

View File

@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestClientSendsCorrectRuleset()
{
AddUntilStep("spectator client sending frames", () => spectatorClient.PlayingUserStates.ContainsKey(dummy_user_id));
AddAssert("spectator client sent correct ruleset", () => spectatorClient.PlayingUserStates[dummy_user_id].RulesetID == Ruleset.Value.ID);
AddAssert("spectator client sent correct ruleset", () => spectatorClient.PlayingUserStates[dummy_user_id].RulesetID == Ruleset.Value.OnlineID);
}
public override void TearDownSteps()

View File

@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
new Drawable[]
{
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
Recorder = recorder = new TestReplayRecorder
{
@ -139,7 +139,7 @@ namespace osu.Game.Tests.Visual.Gameplay
},
new Drawable[]
{
playbackManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
Clock = new FramedClock(manualClock),
ReplayInputHandler = replayHandler = new TestFramedReplayInputHandler(replay)

View File

@ -30,25 +30,10 @@ namespace osu.Game.Tests.Visual.Menus
[Test]
public void TestMusicNavigationActions()
{
int importId = 0;
Queue<(IWorkingBeatmap working, TrackChangeDirection changeDirection)> trackChangeQueue = null;
// ensure we have at least two beatmaps available to identify the direction the music controller navigated to.
AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(new BeatmapSetInfo
{
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty(),
}
},
Metadata = new BeatmapMetadata
{
Artist = $"a test map {importId++}",
Title = "title",
}
}).Wait(), 5);
AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(TestResources.CreateTestBeatmapSetInfo()).Wait(), 5);
AddStep("import beatmap with track", () =>
{

View File

@ -5,6 +5,7 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
@ -92,7 +93,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for join", () => Client.Room != null);
AddUntilStep("wait for join", () => RoomManager.RoomJoined);
}
[Test]
@ -104,23 +105,24 @@ namespace osu.Game.Tests.Visual.Multiplayer
protected void RunGameplay()
{
AddUntilStep("wait for idle", () => Client.LocalUser?.State == MultiplayerUserState.Idle);
AddStep("click ready button", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerReadyButton>().Single());
InputManager.Click(MouseButton.Left);
});
clickReadyButton();
AddUntilStep("wait for ready", () => Client.LocalUser?.State == MultiplayerUserState.Ready);
AddStep("click ready button", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerReadyButton>().Single());
InputManager.Click(MouseButton.Left);
});
clickReadyButton();
AddUntilStep("wait for player", () => multiplayerScreenStack.CurrentScreen is Player player && player.IsLoaded);
AddStep("exit player", () => multiplayerScreenStack.MultiplayerScreen.MakeCurrent());
}
private void clickReadyButton()
{
AddUntilStep("wait for ready button to be enabled", () => this.ChildrenOfType<MultiplayerReadyButton>().Single().ChildrenOfType<Button>().Single().Enabled.Value);
AddStep("click ready button", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerReadyButton>().Single());
InputManager.Click(MouseButton.Left);
});
}
}
}

View File

@ -63,6 +63,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("second playlist item changed", () => Client.APIRoom?.Playlist[1].Beatmap.Value != firstBeatmap);
}
[Test]
public void TestSettingsUpdatedWhenChangingQueueMode()
{
AddStep("change queue mode", () => Client.ChangeSettings(new MultiplayerRoomSettings
{
QueueMode = QueueMode.AllPlayers
}));
AddUntilStep("api room updated", () => Client.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
}
private void selectNewItem(Func<BeatmapInfo> beatmap)
{
AddStep("click edit button", () =>

View File

@ -214,7 +214,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("Press select", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
AddUntilStep("wait for join", () => client.Room != null);
AddUntilStep("wait for join", () => roomManager.RoomJoined);
}
[Test]
@ -293,7 +293,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("join room", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
AddUntilStep("wait for join", () => client.Room != null);
AddUntilStep("wait for join", () => roomManager.RoomJoined);
AddAssert("Check participant count correct", () => client.APIRoom?.ParticipantCount.Value == 1);
AddAssert("Check participant list contains user", () => client.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
@ -351,7 +351,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
AddUntilStep("wait for join", () => client.Room != null);
AddUntilStep("wait for join", () => roomManager.RoomJoined);
}
[Test]
@ -618,7 +618,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("join room", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
AddUntilStep("wait for join", () => client.Room != null);
AddUntilStep("wait for join", () => roomManager.RoomJoined);
AddAssert("local room has correct settings", () =>
{
@ -644,7 +644,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for join", () => client.Room != null);
AddUntilStep("wait for join", () => roomManager.RoomJoined);
}
}
}

View File

@ -47,6 +47,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
beatmaps = new List<BeatmapInfo>();
var metadata = new BeatmapMetadata
{
Artist = "Some Artist",
Title = "Some Beatmap",
AuthorString = "Some Author"
};
var beatmapSetInfo = new BeatmapSetInfo
{
OnlineID = 10,
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
Metadata = metadata,
DateAdded = DateTimeOffset.UtcNow
};
for (int i = 0; i < 8; ++i)
{
int beatmapId = 10 * 10 + i;
@ -54,29 +69,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
beatmaps.Add(new BeatmapInfo
var beatmap = new BeatmapInfo
{
Ruleset = rulesets.GetRuleset(i % 4),
OnlineID = beatmapId,
Length = length,
BPM = bpm,
Metadata = metadata,
BaseDifficulty = new BeatmapDifficulty()
});
};
beatmaps.Add(beatmap);
beatmapSetInfo.Beatmaps.Add(beatmap);
}
manager.Import(new BeatmapSetInfo
{
OnlineID = 10,
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
Artist = "Some Artist",
Title = "Some Beatmap",
AuthorString = "Some Author"
},
Beatmaps = beatmaps,
DateAdded = DateTimeOffset.UtcNow
}).Wait();
manager.Import(beatmapSetInfo).Wait();
}
public override void SetUpSteps()

View File

@ -78,7 +78,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
});
AddUntilStep("wait for join", () => Client.Room != null);
AddUntilStep("wait for join", () => RoomJoined);
}
[Test]
@ -116,7 +116,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for room join", () => Client.Room != null);
AddUntilStep("wait for room join", () => RoomJoined);
AddStep("join other user (ready)", () =>
{

View File

@ -9,6 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Framework.Utils;
@ -198,11 +199,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
}, users);
}
private void addClickButtonStep() => AddStep("click button", () =>
private void addClickButtonStep()
{
InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for button to be ready", () => button.ChildrenOfType<Button>().Single().Enabled.Value);
AddStep("click button", () =>
{
InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});
}
private void verifyGameplayStartFlow()
{

View File

@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
PlaylistItem playlistItem = new PlaylistItem
{
BeatmapID = beatmapInfo.ID,
BeatmapID = beatmapInfo.OnlineID ?? -1,
};
Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem));

View File

@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
PlaylistItem playlistItem = new PlaylistItem
{
BeatmapID = beatmapInfo.ID,
BeatmapID = beatmapInfo.OnlineID ?? -1,
};
SortedDictionary<int, BindableInt> teamScores = new SortedDictionary<int, BindableInt>

View File

@ -2,14 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Extensions;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Utils;
@ -21,6 +17,7 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Tests.Resources;
using osu.Game.Tests.Visual.OnlinePlay;
namespace osu.Game.Tests.Visual.Multiplayer
@ -42,43 +39,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
var beatmaps = new List<BeatmapInfo>();
var beatmapSet = TestResources.CreateTestBeatmapSetInfo();
for (int i = 0; i < 6; i++)
{
int beatmapId = 10 * 10 + i;
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
beatmaps.Add(new BeatmapInfo
{
Ruleset = new OsuRuleset().RulesetInfo,
OnlineID = beatmapId,
DifficultyName = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
Length = length,
BPM = bpm,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 3.5f,
},
});
}
manager.Import(new BeatmapSetInfo
{
OnlineID = 10,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
Artist = "Some Artist " + RNG.Next(0, 9),
Title = $"Some Song (set id 10), max bpm {beatmaps.Max(b => b.BPM):0.#})",
AuthorString = "Some Guy " + RNG.Next(0, 9),
},
Beatmaps = beatmaps,
DateAdded = DateTimeOffset.UtcNow,
}).Wait();
manager.Import(beatmapSet).Wait();
}
public override void SetUpSteps()

View File

@ -158,7 +158,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for join", () => client.Room != null);
AddUntilStep("wait for join", () => multiplayerScreenStack.RoomManager.RoomJoined);
}
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Screens;
@ -110,7 +109,7 @@ namespace osu.Game.Tests.Visual.Navigation
Hash = Guid.NewGuid().ToString(),
OnlineID = i,
Metadata = metadata,
Beatmaps = new List<BeatmapInfo>
Beatmaps =
{
new BeatmapInfo
{
@ -141,7 +140,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect);
AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapSetInfo.MatchesOnlineID(getImport()));
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID);
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.Equals(getImport().Beatmaps.First().Ruleset));
}
private void presentSecondDifficultyAndConfirm(Func<BeatmapSetInfo> getImport, int importedID)
@ -151,7 +150,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect);
AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineID == importedID * 2048);
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID);
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.Equals(getImport().Beatmaps.First().Ruleset));
}
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Screens;
@ -41,7 +40,7 @@ namespace osu.Game.Tests.Visual.Navigation
Hash = Guid.NewGuid().ToString(),
OnlineID = 1,
Metadata = metadata,
Beatmaps = new List<BeatmapInfo>
Beatmaps =
{
new BeatmapInfo
{
@ -155,15 +154,15 @@ namespace osu.Game.Tests.Visual.Navigation
case ScorePresentType.Results:
AddUntilStep("wait for results", () => lastWaitedScreen != Game.ScreenStack.CurrentScreen && Game.ScreenStack.CurrentScreen is ResultsScreen);
AddStep("store last waited screen", () => lastWaitedScreen = Game.ScreenStack.CurrentScreen);
AddUntilStep("correct score displayed", () => ((ResultsScreen)Game.ScreenStack.CurrentScreen).Score.ID == getImport().ID);
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Ruleset.ID);
AddUntilStep("correct score displayed", () => ((ResultsScreen)Game.ScreenStack.CurrentScreen).Score.Equals(getImport()));
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.Equals(getImport().Ruleset));
break;
case ScorePresentType.Gameplay:
AddUntilStep("wait for player loader", () => lastWaitedScreen != Game.ScreenStack.CurrentScreen && Game.ScreenStack.CurrentScreen is ReplayPlayerLoader);
AddStep("store last waited screen", () => lastWaitedScreen = Game.ScreenStack.CurrentScreen);
AddUntilStep("correct score displayed", () => ((ReplayPlayerLoader)Game.ScreenStack.CurrentScreen).Score.ID == getImport().ID);
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Ruleset.ID);
AddUntilStep("correct score displayed", () => ((ReplayPlayerLoader)Game.ScreenStack.CurrentScreen).Score.Equals(getImport()));
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.Equals(getImport().Ruleset));
break;
}
}

View File

@ -257,7 +257,7 @@ namespace osu.Game.Tests.Visual.Navigation
InputManager.ReleaseKey(Key.ControlLeft);
});
AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single().Current.Value.ID == 1);
AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single().Current.Value.OnlineID == 1);
AddAssert("Mods overlay still visible", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
}
@ -278,7 +278,7 @@ namespace osu.Game.Tests.Visual.Navigation
InputManager.ReleaseKey(Key.ControlLeft);
});
AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single().Current.Value.ID == 1);
AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single().Current.Value.OnlineID == 1);
AddAssert("Options overlay still visible", () => songSelect.BeatmapOptionsOverlay.State.Value == Visibility.Visible);
}

View File

@ -200,7 +200,7 @@ namespace osu.Game.Tests.Visual.Online
{
OnlineID = i * 10,
DifficultyName = $"Test #{i}",
RulesetID = Ruleset.Value.ID ?? -1,
RulesetID = Ruleset.Value.OnlineID,
StarRating = 2 + i * 0.1,
OverallDifficulty = 3.5f,
FailTimes = new APIFailTimes

View File

@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Online
}
},
Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray(),
Status = BeatmapSetOnlineStatus.Ranked
Status = BeatmapOnlineStatus.Ranked
};
}

View File

@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.Online
{
new APIBeatmap
{
RulesetID = Ruleset.Value.ID ?? 0,
RulesetID = Ruleset.Value.OnlineID,
DifficultyName = "Test",
StarRating = 6.42,
}

View File

@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Online
AddStep("Set beatmap", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null)
{
BeatmapInfo = { OnlineID = hasOnlineId ? 1234 : (int?)null }
BeatmapInfo = { OnlineID = hasOnlineId ? 1234 : -1 }
});
AddStep("Run command", () => Add(new NowPlayingCommand()));

View File

@ -338,7 +338,7 @@ namespace osu.Game.Tests.Visual.Ranking
: base(score, true)
{
Score.BeatmapInfo.OnlineID = 0;
Score.BeatmapInfo.Status = BeatmapSetOnlineStatus.Pending;
Score.BeatmapInfo.Status = BeatmapOnlineStatus.Pending;
}
protected override void LoadComplete()

View File

@ -0,0 +1,83 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
namespace osu.Game.Tests.Visual.Settings
{
public class TestSceneSettingsNumberBox : OsuTestScene
{
private SettingsNumberBox numberBox;
private OsuTextBox textBox;
[SetUpSteps]
public void SetUpSteps()
{
AddStep("create number box", () => Child = numberBox = new SettingsNumberBox());
AddStep("get inner text box", () => textBox = numberBox.ChildrenOfType<OsuTextBox>().Single());
}
[Test]
public void TestLargeInteger()
{
AddStep("set current to 1,000,000,000", () => numberBox.Current.Value = 1_000_000_000);
AddAssert("text box text is correct", () => textBox.Text == "1000000000");
}
[Test]
public void TestUserInput()
{
inputText("42");
currentValueIs(42);
currentTextIs("42");
inputText(string.Empty);
currentValueIs(null);
currentTextIs(string.Empty);
inputText("555");
currentValueIs(555);
currentTextIs("555");
inputText("-4444");
// attempting to input the minus will raise an input error, the rest will pass through fine.
currentValueIs(4444);
currentTextIs("4444");
// checking the upper bound.
inputText(int.MaxValue.ToString());
currentValueIs(int.MaxValue);
currentTextIs(int.MaxValue.ToString());
inputText(smallestOverflowValue.ToString());
currentValueIs(int.MaxValue);
currentTextIs(int.MaxValue.ToString());
inputText("0");
currentValueIs(0);
currentTextIs("0");
// checking that leading zeroes are stripped.
inputText("00");
currentValueIs(0);
currentTextIs("0");
inputText("01");
currentValueIs(1);
currentTextIs("1");
}
private void inputText(string text) => AddStep($"set textbox text to {text}", () => textBox.Text = text);
private void currentValueIs(int? value) => AddAssert($"current value is {value?.ToString() ?? "null"}", () => numberBox.Current.Value == value);
private void currentTextIs(string value) => AddAssert($"current text is {value}", () => textBox.Text == value);
/// <summary>
/// The smallest number that overflows <see langword="int"/>.
/// </summary>
private static long smallestOverflowValue => 1L + int.MaxValue;
}
}

View File

@ -3,15 +3,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets;
@ -19,6 +15,7 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
using osu.Game.Tests.Resources;
using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect
@ -78,9 +75,9 @@ namespace osu.Game.Tests.Visual.SongSelect
{
AddStep("store selection", () => selection = carousel.SelectedBeatmapInfo);
if (isIterating)
AddUntilStep("selection changed", () => carousel.SelectedBeatmapInfo != selection);
AddUntilStep("selection changed", () => !carousel.SelectedBeatmapInfo.Equals(selection));
else
AddUntilStep("selection not changed", () => carousel.SelectedBeatmapInfo == selection);
AddUntilStep("selection not changed", () => carousel.SelectedBeatmapInfo.Equals(selection));
}
}
}
@ -152,7 +149,7 @@ namespace osu.Game.Tests.Visual.SongSelect
const int total_set_count = 200;
for (int i = 0; i < total_set_count; i++)
sets.Add(createTestBeatmapSet(i + 1));
sets.Add(TestResources.CreateTestBeatmapSetInfo());
loadBeatmaps(sets);
@ -183,7 +180,7 @@ namespace osu.Game.Tests.Visual.SongSelect
const int total_set_count = 20;
for (int i = 0; i < total_set_count; i++)
sets.Add(createTestBeatmapSet(i + 1));
sets.Add(TestResources.CreateTestBeatmapSetInfo(3));
loadBeatmaps(sets);
@ -228,10 +225,9 @@ namespace osu.Game.Tests.Visual.SongSelect
loadBeatmaps();
// basic filtering
setSelected(1, 1);
AddStep("Filter", () => carousel.Filter(new FilterCriteria { SearchText = "set #3!" }, false));
AddStep("Filter", () => carousel.Filter(new FilterCriteria { SearchText = carousel.BeatmapSets.ElementAt(2).Metadata.Title }, false));
checkVisibleItemCount(diff: false, count: 1);
checkVisibleItemCount(diff: true, count: 3);
waitForSelection(3, 1);
@ -275,16 +271,20 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestFilterRange()
{
string searchText = null;
loadBeatmaps();
// buffer the selection
setSelected(3, 2);
AddStep("get search text", () => searchText = carousel.SelectedBeatmapSet.Metadata.Title);
setSelected(1, 3);
AddStep("Apply a range filter", () => carousel.Filter(new FilterCriteria
{
SearchText = "#3",
SearchText = searchText,
StarDifficulty = new FilterCriteria.OptionalRange<double>
{
Min = 2,
@ -327,7 +327,7 @@ namespace osu.Game.Tests.Visual.SongSelect
nextRandom();
AddAssert("ensure repeat", () => selectedSets.Contains(carousel.SelectedBeatmapSet));
AddStep("Add set with 100 difficulties", () => carousel.UpdateBeatmapSet(createTestBeatmapSetWithManyDifficulties(set_count + 1)));
AddStep("Add set with 100 difficulties", () => carousel.UpdateBeatmapSet(TestResources.CreateTestBeatmapSetInfo(100, rulesets.AvailableRulesets.ToArray())));
AddStep("Filter Extra", () => carousel.Filter(new FilterCriteria { SearchText = "Extra 10" }, false));
checkInvisibleDifficultiesUnselectable();
checkInvisibleDifficultiesUnselectable();
@ -345,18 +345,21 @@ namespace osu.Game.Tests.Visual.SongSelect
{
loadBeatmaps();
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 1)));
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 2)));
var firstAdded = TestResources.CreateTestBeatmapSetInfo();
var secondAdded = TestResources.CreateTestBeatmapSetInfo();
AddStep("Add new set", () => carousel.UpdateBeatmapSet(firstAdded));
AddStep("Add new set", () => carousel.UpdateBeatmapSet(secondAdded));
checkVisibleItemCount(false, set_count + 2);
AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 2)));
AddStep("Remove set", () => carousel.RemoveBeatmapSet(firstAdded));
checkVisibleItemCount(false, set_count + 1);
setSelected(set_count + 1, 1);
AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 1)));
AddStep("Remove set", () => carousel.RemoveBeatmapSet(secondAdded));
checkVisibleItemCount(false, set_count);
@ -372,7 +375,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
sets.Clear();
var rulesetBeatmapSet = createTestBeatmapSet(1);
var rulesetBeatmapSet = TestResources.CreateTestBeatmapSetInfo(1);
var taikoRuleset = rulesets.AvailableRulesets.ElementAt(1);
rulesetBeatmapSet.Beatmaps.ForEach(b =>
{
@ -397,12 +400,29 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestSorting()
{
loadBeatmaps();
var sets = new List<BeatmapSetInfo>();
const string zzz_string = "zzzzz";
for (int i = 0; i < 20; i++)
{
var set = TestResources.CreateTestBeatmapSetInfo();
if (i == 4)
set.Metadata.Artist = zzz_string;
if (i == 16)
set.Metadata.AuthorString = zzz_string;
sets.Add(set);
}
loadBeatmaps(sets);
AddStep("Sort by author", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Author }, false));
AddAssert("Check zzzzz is at bottom", () => carousel.BeatmapSets.Last().Metadata.Author.Username == "zzzzz");
AddAssert($"Check {zzz_string} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Author.Username == zzz_string);
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
AddAssert($"Check #{set_count} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Title.EndsWith($"#{set_count}!", StringComparison.Ordinal));
AddAssert($"Check {zzz_string} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Artist == zzz_string);
}
[Test]
@ -412,19 +432,21 @@ namespace osu.Game.Tests.Visual.SongSelect
for (int i = 0; i < 20; i++)
{
var set = createTestBeatmapSet(i);
var set = TestResources.CreateTestBeatmapSetInfo();
set.Metadata.Artist = "same artist";
set.Metadata.Title = "same title";
sets.Add(set);
}
int idOffset = sets.First().OnlineID ?? 0;
loadBeatmaps(sets);
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.ID == index).All(b => b));
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == index + idOffset).All(b => b));
AddStep("Sort by title", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false));
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.ID == index).All(b => b));
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == index + idOffset).All(b => b));
}
[Test]
@ -434,7 +456,7 @@ namespace osu.Game.Tests.Visual.SongSelect
for (int i = 0; i < 3; i++)
{
var set = createTestBeatmapSet(i);
var set = TestResources.CreateTestBeatmapSetInfo(3);
set.Beatmaps[0].StarRating = 3 - i;
set.Beatmaps[2].StarRating = 6 + i;
sets.Add(set);
@ -443,12 +465,12 @@ namespace osu.Game.Tests.Visual.SongSelect
loadBeatmaps(sets);
AddStep("Filter to normal", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Difficulty, SearchText = "Normal" }, false));
AddAssert("Check first set at end", () => carousel.BeatmapSets.First() == sets.Last());
AddAssert("Check last set at start", () => carousel.BeatmapSets.Last() == sets.First());
AddAssert("Check first set at end", () => carousel.BeatmapSets.First().Equals(sets.Last()));
AddAssert("Check last set at start", () => carousel.BeatmapSets.Last().Equals(sets.First()));
AddStep("Filter to insane", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Difficulty, SearchText = "Insane" }, false));
AddAssert("Check first set at start", () => carousel.BeatmapSets.First() == sets.First());
AddAssert("Check last set at end", () => carousel.BeatmapSets.Last() == sets.Last());
AddAssert("Check first set at start", () => carousel.BeatmapSets.First().Equals(sets.First()));
AddAssert("Check last set at end", () => carousel.BeatmapSets.Last().Equals(sets.Last()));
}
[Test]
@ -503,7 +525,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("create hidden set", () =>
{
hidingSet = createTestBeatmapSet(1);
hidingSet = TestResources.CreateTestBeatmapSetInfo(3);
hidingSet.Beatmaps[1].Hidden = true;
hiddenList.Clear();
@ -550,7 +572,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("add mixed ruleset beatmapset", () =>
{
testMixed = createTestBeatmapSet(set_count + 1);
testMixed = TestResources.CreateTestBeatmapSetInfo();
for (int i = 0; i <= 2; i++)
{
@ -573,7 +595,7 @@ namespace osu.Game.Tests.Visual.SongSelect
BeatmapSetInfo testSingle = null;
AddStep("add single ruleset beatmapset", () =>
{
testSingle = createTestBeatmapSet(set_count + 2);
testSingle = TestResources.CreateTestBeatmapSetInfo();
testSingle.Beatmaps.ForEach(b =>
{
b.Ruleset = rulesets.AvailableRulesets.ElementAt(1);
@ -593,7 +615,7 @@ namespace osu.Game.Tests.Visual.SongSelect
List<BeatmapSetInfo> manySets = new List<BeatmapSetInfo>();
for (int i = 1; i <= 50; i++)
manySets.Add(createTestBeatmapSet(i));
manySets.Add(TestResources.CreateTestBeatmapSetInfo(i));
loadBeatmaps(manySets);
@ -626,18 +648,11 @@ namespace osu.Game.Tests.Visual.SongSelect
{
for (int i = 0; i < 10; i++)
{
var set = createTestBeatmapSet(i);
foreach (var b in set.Beatmaps)
manySets.Add(TestResources.CreateTestBeatmapSetInfo(3, new[]
{
// all taiko except for first
int ruleset = i > 0 ? 1 : 0;
b.Ruleset = rulesets.GetRuleset(ruleset);
b.RulesetID = ruleset;
}
manySets.Add(set);
rulesets.GetRuleset(i > 0 ? 1 : 0)
}));
}
});
@ -659,10 +674,10 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("Restore different ruleset filter", () =>
{
carousel.Filter(new FilterCriteria { Ruleset = rulesets.GetRuleset(1) }, false);
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.OnlineID ?? -1);
});
AddAssert("selection changed", () => carousel.SelectedBeatmapInfo != manySets.First().Beatmaps.First());
AddAssert("selection changed", () => !carousel.SelectedBeatmapInfo.Equals(manySets.First().Beatmaps.First()));
}
AddAssert("Selection was random", () => eagerSelectedIDs.Count > 2);
@ -677,7 +692,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("add mixed difficulty set", () =>
{
set = createTestBeatmapSet(1);
set = TestResources.CreateTestBeatmapSetInfo(1);
set.Beatmaps.Clear();
for (int i = 1; i <= 15; i++)
@ -729,7 +744,11 @@ namespace osu.Game.Tests.Visual.SongSelect
beatmapSets = new List<BeatmapSetInfo>();
for (int i = 1; i <= (count ?? set_count); i++)
beatmapSets.Add(createTestBeatmapSet(i, randomDifficulties));
{
beatmapSets.Add(randomDifficulties
? TestResources.CreateTestBeatmapSetInfo()
: TestResources.CreateTestBeatmapSetInfo(3));
}
}
carousel.Filter(initialCriteria?.Invoke() ?? new FilterCriteria());
@ -759,13 +778,13 @@ namespace osu.Game.Tests.Visual.SongSelect
}
private void ensureRandomFetchSuccess() =>
AddAssert("ensure prev random fetch worked", () => selectedSets.Peek() == carousel.SelectedBeatmapSet);
AddAssert("ensure prev random fetch worked", () => selectedSets.Peek().Equals(carousel.SelectedBeatmapSet));
private void waitForSelection(int set, int? diff = null) =>
AddUntilStep($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () =>
{
if (diff != null)
return carousel.SelectedBeatmapInfo == carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First();
return carousel.SelectedBeatmapInfo?.Equals(carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First()) == true;
return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmapInfo);
});
@ -833,88 +852,6 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("Selection is visible", selectedBeatmapVisible);
}
private BeatmapSetInfo createTestBeatmapSet(int id, bool randomDifficultyCount = false)
{
return new BeatmapSetInfo
{
ID = id,
OnlineID = id,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
Artist = $"peppy{id.ToString().PadLeft(6, '0')}",
Title = $"test set #{id}!",
AuthorString = string.Concat(Enumerable.Repeat((char)('z' - Math.Min(25, id - 1)), 5))
},
Beatmaps = getBeatmaps(randomDifficultyCount ? RNG.Next(1, 20) : 3).ToList()
};
}
private IEnumerable<BeatmapInfo> getBeatmaps(int count)
{
int id = 0;
for (int i = 0; i < count; i++)
{
float diff = (float)i / count * 10;
string version = "Normal";
if (diff > 6.6)
version = "Insane";
else if (diff > 3.3)
version = "Hard";
yield return new BeatmapInfo
{
OnlineID = id++ * 10,
DifficultyName = version,
StarRating = diff,
Ruleset = new OsuRuleset().RulesetInfo,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = diff,
}
};
}
}
private BeatmapSetInfo createTestBeatmapSetWithManyDifficulties(int id)
{
var toReturn = new BeatmapSetInfo
{
ID = id,
OnlineID = id,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
Artist = $"peppy{id.ToString().PadLeft(6, '0')}",
Title = $"test set #{id}!",
AuthorString = string.Concat(Enumerable.Repeat((char)('z' - Math.Min(25, id - 1)), 5))
},
Beatmaps = new List<BeatmapInfo>(),
};
for (int b = 1; b < 101; b++)
{
toReturn.Beatmaps.Add(new BeatmapInfo
{
OnlineID = b * 10,
Path = $"extra{b}.osu",
DifficultyName = $"Extra {b}",
Ruleset = rulesets.GetRuleset((b - 1) % 4),
StarRating = 2,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 3.5f,
}
});
}
return toReturn;
}
private class TestBeatmapCarousel : BeatmapCarousel
{
public bool PendingFilterTask => PendingFilter != null;

View File

@ -220,7 +220,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Title = "Verrrrry long Title"
},
DifficultyName = "Verrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Version",
Status = BeatmapSetOnlineStatus.Graveyard,
Status = BeatmapOnlineStatus.Graveyard,
},
};
}

View File

@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestBeatmapStates()
{
foreach (BeatmapSetOnlineStatus status in Enum.GetValues(typeof(BeatmapSetOnlineStatus)))
foreach (BeatmapOnlineStatus status in Enum.GetValues(typeof(BeatmapOnlineStatus)))
AddStep($"{status} beatmap", () => showBeatmapWithStatus(status));
}
@ -384,7 +384,7 @@ namespace osu.Game.Tests.Visual.SongSelect
};
}
private void showBeatmapWithStatus(BeatmapSetOnlineStatus status)
private void showBeatmapWithStatus(BeatmapOnlineStatus status)
{
leaderboard.BeatmapInfo = new BeatmapInfo
{

View File

@ -16,6 +16,7 @@ using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Tests.Resources;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.SongSelect
@ -32,7 +33,7 @@ namespace osu.Game.Tests.Visual.SongSelect
switch (req)
{
case GetUserRequest userRequest:
userRequest.TriggerSuccess(getUser(userRequest.Ruleset.ID));
userRequest.TriggerSuccess(getUser(userRequest.Ruleset.OnlineID));
return true;
}
@ -89,7 +90,7 @@ namespace osu.Game.Tests.Visual.SongSelect
for (int i = 0; i < import_count; ++i)
{
beatmapSets.Add(importBeatmapSet(i, Enumerable.Repeat(new OsuRuleset().RulesetInfo, 5)));
beatmapSets.Add(importBeatmapSet(Enumerable.Repeat(new OsuRuleset().RulesetInfo, 5)));
}
});
@ -103,9 +104,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{
BeatmapSetInfo catchSet = null, mixedSet = null;
AddStep("create catch beatmapset", () => catchSet = importBeatmapSet(1, new[] { new CatchRuleset().RulesetInfo }));
AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(2,
new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo }));
AddStep("create catch beatmapset", () => catchSet = importBeatmapSet(new[] { new CatchRuleset().RulesetInfo }));
AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo }));
AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { catchSet, mixedSet }));
@ -121,9 +121,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{
BeatmapSetInfo osuSet = null, mixedSet = null;
AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(1, new[] { new OsuRuleset().RulesetInfo }));
AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(2,
new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo }));
AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(new[] { new OsuRuleset().RulesetInfo }));
AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo }));
AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, mixedSet }));
@ -139,9 +138,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{
BeatmapSetInfo osuSet = null, mixedSet = null;
AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(1, new[] { new OsuRuleset().RulesetInfo }));
AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(2,
new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new TaikoRuleset().RulesetInfo }));
AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(new[] { new OsuRuleset().RulesetInfo }));
AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new TaikoRuleset().RulesetInfo }));
AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, mixedSet }));
@ -157,8 +155,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{
BeatmapSetInfo osuSet = null, maniaSet = null;
AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(1, new[] { new OsuRuleset().RulesetInfo }));
AddStep("create mania beatmapset", () => maniaSet = importBeatmapSet(2, Enumerable.Repeat(new ManiaRuleset().RulesetInfo, 10)));
AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(new[] { new OsuRuleset().RulesetInfo }));
AddStep("create mania beatmapset", () => maniaSet = importBeatmapSet(Enumerable.Repeat(new ManiaRuleset().RulesetInfo, 10)));
AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, maniaSet }));
@ -169,30 +167,19 @@ namespace osu.Game.Tests.Visual.SongSelect
presentAndConfirm(() => maniaSet, 5);
}
private BeatmapSetInfo importBeatmapSet(int importID, IEnumerable<RulesetInfo> difficultyRulesets)
private BeatmapSetInfo importBeatmapSet(IEnumerable<RulesetInfo> difficultyRulesets)
{
var metadata = new BeatmapMetadata
{
Artist = "SomeArtist",
AuthorString = "SomeAuthor",
Title = $"import {importID}"
};
var rulesets = difficultyRulesets.ToArray();
var beatmapSet = new BeatmapSetInfo
var beatmapSet = TestResources.CreateTestBeatmapSetInfo(rulesets.Length, rulesets);
for (int i = 0; i < rulesets.Length; i++)
{
Hash = Guid.NewGuid().ToString(),
OnlineID = importID,
Metadata = metadata,
Beatmaps = difficultyRulesets.Select((ruleset, difficultyIndex) => new BeatmapInfo
{
OnlineID = importID * 1024 + difficultyIndex,
Metadata = metadata,
BaseDifficulty = new BeatmapDifficulty(),
Ruleset = ruleset,
StarRating = difficultyIndex + 1,
DifficultyName = $"SR{difficultyIndex + 1}"
}).ToList()
};
var beatmap = beatmapSet.Beatmaps[i];
beatmap.StarRating = i + 1;
beatmap.DifficultyName = $"SR{i + 1}";
}
return Game.BeatmapManager.Import(beatmapSet).Result.Value;
}

View File

@ -3,15 +3,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Utils;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
@ -31,6 +27,7 @@ using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
using osu.Game.Tests.Resources;
using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect
@ -148,7 +145,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select next and enter", () =>
{
InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType<DrawableCarouselBeatmap>()
.First(b => ((CarouselBeatmap)b.Item).BeatmapInfo != songSelect.Carousel.SelectedBeatmapInfo));
.First(b => !((CarouselBeatmap)b.Item).BeatmapInfo.Equals(songSelect.Carousel.SelectedBeatmapInfo)));
InputManager.Click(MouseButton.Left);
@ -175,7 +172,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select next and enter", () =>
{
InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType<DrawableCarouselBeatmap>()
.First(b => ((CarouselBeatmap)b.Item).BeatmapInfo != songSelect.Carousel.SelectedBeatmapInfo));
.First(b => !((CarouselBeatmap)b.Item).BeatmapInfo.Equals(songSelect.Carousel.SelectedBeatmapInfo)));
InputManager.PressButton(MouseButton.Left);
@ -258,8 +255,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{
AddStep("import multi-ruleset map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
manager.Import(TestResources.CreateTestBeatmapSetInfo(rulesets: usableRulesets)).Wait();
});
}
else
@ -354,7 +351,7 @@ namespace osu.Game.Tests.Visual.SongSelect
target = manager.GetAllUsableBeatmapSets()
.Last(b => b.Beatmaps.Any(bi => bi.RulesetID == 0)).Beatmaps.Last();
Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == 0);
Ruleset.Value = rulesets.AvailableRulesets.First(r => r.OnlineID == 0);
Beatmap.Value = manager.GetWorkingBeatmap(target);
});
@ -385,12 +382,12 @@ namespace osu.Game.Tests.Visual.SongSelect
.Last(b => b.Beatmaps.Any(bi => bi.RulesetID == 0)).Beatmaps.Last();
Beatmap.Value = manager.GetWorkingBeatmap(target);
Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == 0);
Ruleset.Value = rulesets.AvailableRulesets.First(r => r.OnlineID == 0);
});
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.Equals(target));
AddUntilStep("has correct ruleset", () => Ruleset.Value.ID == 0);
AddUntilStep("has correct ruleset", () => Ruleset.Value.OnlineID == 0);
// this is an important check, to make sure updateComponentFromBeatmap() was actually run
AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.MatchesOnlineID(target));
@ -505,7 +502,7 @@ namespace osu.Game.Tests.Visual.SongSelect
// special case for converts checked here.
return selectedPanel.ChildrenOfType<FilterableDifficultyIcon>().All(i =>
i.IsFiltered || i.Item.BeatmapInfo.Ruleset.ID == targetRuleset || i.Item.BeatmapInfo.Ruleset.ID == 0);
i.IsFiltered || i.Item.BeatmapInfo.Ruleset.OnlineID == targetRuleset || i.Item.BeatmapInfo.Ruleset.OnlineID == 0);
});
AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.MatchesOnlineID(target) == true);
@ -644,7 +641,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.Click(MouseButton.Left);
});
AddAssert("Selected beatmap correct", () => songSelect.Carousel.SelectedBeatmapInfo == filteredBeatmap);
AddAssert("Selected beatmap correct", () => songSelect.Carousel.SelectedBeatmapInfo.Equals(filteredBeatmap));
}
[Test]
@ -665,8 +662,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import multi-ruleset map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
manager.Import(TestResources.CreateTestBeatmapSetInfo(3, usableRulesets)).Wait();
});
int previousSetID = 0;
@ -676,11 +673,11 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("record set ID", () => previousSetID = ((IBeatmapSetInfo)Beatmap.Value.BeatmapSetInfo).OnlineID);
AddAssert("selection changed once", () => changeCount == 1);
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
AddAssert("Check ruleset is osu!", () => Ruleset.Value.OnlineID == 0);
changeRuleset(3);
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.OnlineID == 3);
AddUntilStep("selection changed", () => changeCount > 1);
@ -705,8 +702,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import multi-ruleset map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
manager.Import(TestResources.CreateTestBeatmapSetInfo(3, usableRulesets)).Wait();
});
DrawableCarouselBeatmapSet set = null;
@ -720,11 +717,11 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("Find an icon for different ruleset", () =>
{
difficultyIcon = set.ChildrenOfType<FilterableDifficultyIcon>()
.FirstOrDefault(icon => icon.Item.BeatmapInfo.Ruleset.ID == 3);
.FirstOrDefault(icon => icon.Item.BeatmapInfo.Ruleset.OnlineID == 3);
return difficultyIcon != null;
});
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
AddAssert("Check ruleset is osu!", () => Ruleset.Value.OnlineID == 0);
int previousSetID = 0;
@ -737,7 +734,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.Click(MouseButton.Left);
});
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.OnlineID == 3);
AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmapInfo.BeatmapSet.OnlineID == previousSetID);
AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.OnlineID == 3);
@ -754,8 +751,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import huge difficulty count map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
imported = manager.Import(createTestBeatmapSet(usableRulesets, 50)).Result.Value;
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
imported = manager.Import(TestResources.CreateTestBeatmapSetInfo(50, usableRulesets)).Result.Value;
});
AddStep("select the first beatmap of import", () => Beatmap.Value = manager.GetWorkingBeatmap(imported.Beatmaps.First()));
@ -771,10 +768,10 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("Find group icon for different ruleset", () =>
{
return (groupIcon = set.ChildrenOfType<FilterableGroupedDifficultyIcon>()
.FirstOrDefault(icon => icon.Items.First().BeatmapInfo.Ruleset.ID == 3)) != null;
.FirstOrDefault(icon => icon.Items.First().BeatmapInfo.Ruleset.OnlineID == 3)) != null;
});
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
AddAssert("Check ruleset is osu!", () => Ruleset.Value.OnlineID == 0);
AddStep("Click on group", () =>
{
@ -783,7 +780,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.Click(MouseButton.Left);
});
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.OnlineID == 3);
AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(groupIcon.Items.First().BeatmapInfo));
}
@ -817,7 +814,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen());
AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(getPresentBeatmap()));
AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0);
AddAssert("check ruleset is correct for score", () => Ruleset.Value.OnlineID == 0);
}
[Test]
@ -849,7 +846,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen());
AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(getPresentBeatmap()));
AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0);
AddAssert("check ruleset is correct for score", () => Ruleset.Value.OnlineID == 0);
}
private void waitForInitialSelection()
@ -858,7 +855,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("wait for difficulty panels visible", () => songSelect.Carousel.ChildrenOfType<DrawableCarouselBeatmap>().Any());
}
private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.FindIndex(b => b == info);
private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.IndexOf(info);
private int getCurrentBeatmapIndex() => getBeatmapIndex(songSelect.Carousel.SelectedBeatmapSet, songSelect.Carousel.SelectedBeatmapInfo);
@ -869,18 +866,14 @@ namespace osu.Game.Tests.Visual.SongSelect
private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id));
private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait();
private static int importId;
private int getImportId() => ++importId;
private void importForRuleset(int id) => manager.Import(TestResources.CreateTestBeatmapSetInfo(3, rulesets.AvailableRulesets.Where(r => r.OnlineID == id).ToArray())).Wait();
private void checkMusicPlaying(bool playing) =>
AddUntilStep($"music {(playing ? "" : "not ")}playing", () => music.IsPlaying == playing);
private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => SelectedMods.Value = mods);
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.OnlineID == id));
private void createSongSelect()
{
@ -893,59 +886,13 @@ namespace osu.Game.Tests.Visual.SongSelect
{
AddStep("import test maps", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray();
for (int i = 0; i < 100; i += 10)
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
manager.Import(TestResources.CreateTestBeatmapSetInfo(rulesets: usableRulesets)).Wait();
});
}
private BeatmapSetInfo createTestBeatmapSet(RulesetInfo[] rulesets, int countPerRuleset = 6)
{
int j = 0;
RulesetInfo getRuleset() => rulesets[j++ % rulesets.Length];
int setId = getImportId();
var beatmaps = new List<BeatmapInfo>();
for (int i = 0; i < countPerRuleset; i++)
{
int beatmapId = setId * 1000 + i;
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
beatmaps.Add(new BeatmapInfo
{
Ruleset = getRuleset(),
OnlineID = beatmapId,
DifficultyName = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
Length = length,
BPM = bpm,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 3.5f,
},
});
}
return new BeatmapSetInfo
{
OnlineID = setId,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
Artist = "Some Artist " + RNG.Next(0, 9),
Title = $"Some Song (set id {setId}, max bpm {beatmaps.Max(b => b.BPM):0.#})",
AuthorString = "Some Guy " + RNG.Next(0, 9),
},
Beatmaps = beatmaps,
DateAdded = DateTimeOffset.UtcNow,
};
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);

View File

@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface
[Test]
public void TestLabelledEnumDropdown()
=> AddStep(@"create dropdown", () => Child = new LabelledEnumDropdown<BeatmapSetOnlineStatus>
=> AddStep(@"create dropdown", () => Child = new LabelledEnumDropdown<BeatmapOnlineStatus>
{
Label = @"Beatmap status",
Description = @"This is a description"

View File

@ -378,9 +378,9 @@ namespace osu.Game.Tests.Visual.UserInterface
});
}
private void changeRuleset(int? id)
private void changeRuleset(int? onlineId)
{
AddStep($"change ruleset to {(id?.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == id); });
AddStep($"change ruleset to {(onlineId?.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.OnlineID == onlineId); });
waitForLoad();
}

View File

@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.UserInterface
public void SetUp() => Schedule(() =>
{
SelectedMods.Value = Array.Empty<Mod>();
Ruleset.Value = new TestRulesetInfo();
Ruleset.Value = CreateTestRulesetInfo();
});
[Test]
@ -170,40 +170,32 @@ namespace osu.Game.Tests.Visual.UserInterface
ModSettingsContainer.Parent.Width = newWidth;
}
public class TestRulesetInfo : RulesetInfo
public static RulesetInfo CreateTestRulesetInfo() => new TestCustomisableModRuleset().RulesetInfo;
public class TestCustomisableModRuleset : Ruleset
{
public override Ruleset CreateInstance() => new TestCustomisableModRuleset();
public TestRulesetInfo()
public override IEnumerable<Mod> GetModsFor(ModType type)
{
Available = true;
}
public class TestCustomisableModRuleset : Ruleset
{
public override IEnumerable<Mod> GetModsFor(ModType type)
if (type == ModType.Conversion)
{
if (type == ModType.Conversion)
return new Mod[]
{
return new Mod[]
{
new TestModCustomisable1(),
new TestModCustomisable2()
};
}
return Array.Empty<Mod>();
new TestModCustomisable1(),
new TestModCustomisable2()
};
}
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new NotImplementedException();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException();
public override string Description { get; } = "test";
public override string ShortName { get; } = "tst";
return Array.Empty<Mod>();
}
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new NotImplementedException();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException();
public override string Description { get; } = "test";
public override string ShortName { get; } = "tst";
}
private class TestModCustomisable1 : TestModCustomisable

View File

@ -10,7 +10,7 @@ namespace osu.Game.Tests.Visual.UserInterface
public class TestSceneOsuDropdown : ThemeComparisonTestScene
{
protected override Drawable CreateContent() =>
new OsuEnumDropdown<BeatmapSetOnlineStatus>
new OsuEnumDropdown<BeatmapOnlineStatus>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -1,16 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Music;
using osu.Game.Tests.Resources;
using osuTK;
using osuTK.Input;
@ -22,6 +22,8 @@ namespace osu.Game.Tests.Visual.UserInterface
private PlaylistOverlay playlistOverlay;
private BeatmapSetInfo first;
[SetUp]
public void Setup() => Schedule(() =>
{
@ -43,19 +45,11 @@ namespace osu.Game.Tests.Visual.UserInterface
for (int i = 0; i < 100; i++)
{
beatmapSets.Add(new BeatmapSetInfo
{
Metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
Artist = "Some Artist " + RNG.Next(0, 9),
Title = $"Some Song {i + 1}",
AuthorString = "Some Guy " + RNG.Next(0, 9),
},
DateAdded = DateTimeOffset.UtcNow,
});
beatmapSets.Add(TestResources.CreateTestBeatmapSetInfo());
}
first = beatmapSets.First();
playlistOverlay.BeatmapSets.BindTo(beatmapSets);
});
@ -66,7 +60,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("hold 1st item handle", () =>
{
var handle = this.ChildrenOfType<PlaylistItem.PlaylistItemHandle>().First();
var handle = this.ChildrenOfType<OsuRearrangeableListItem<BeatmapSetInfo>.PlaylistItemHandle>().First();
InputManager.MoveMouseTo(handle.ScreenSpaceDrawQuad.Centre);
InputManager.PressButton(MouseButton.Left);
});
@ -77,7 +71,7 @@ namespace osu.Game.Tests.Visual.UserInterface
InputManager.MoveMouseTo(item.ScreenSpaceDrawQuad.Centre);
});
AddAssert("song 1 is 5th", () => beatmapSets[4].Metadata.Title == "Some Song 1");
AddAssert("song 1 is 5th", () => beatmapSets[4] == first);
AddStep("release handle", () => InputManager.ReleaseButton(MouseButton.Left));
}

View File

@ -42,7 +42,12 @@ namespace osu.Game.Tournament.Tests.Components
private void success(APIBeatmap beatmap)
{
var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().AllMods;
var ruleset = rulesets.GetRuleset(Ladder.Ruleset.Value.OnlineID);
if (ruleset == null)
return;
var mods = ruleset.CreateInstance().AllMods;
foreach (var mod in mods)
{

View File

@ -38,7 +38,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
{
using (HeadlessGameHost host = new HeadlessGameHost(nameof(TestCustomDirectory))) // don't use clean run as we are writing a config file.
{
string osuDesktopStorage = basePath(nameof(TestCustomDirectory));
string osuDesktopStorage = PrepareBasePath(nameof(TestCustomDirectory));
const string custom_tournament = "custom";
// need access before the game has constructed its own storage yet.
@ -60,6 +60,15 @@ namespace osu.Game.Tournament.Tests.NonVisual
finally
{
host.Exit();
try
{
if (Directory.Exists(osuDesktopStorage))
Directory.Delete(osuDesktopStorage, true);
}
catch
{
}
}
}
}
@ -69,7 +78,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
{
using (HeadlessGameHost host = new HeadlessGameHost(nameof(TestMigration))) // don't use clean run as we are writing test files for migration.
{
string osuRoot = basePath(nameof(TestMigration));
string osuRoot = PrepareBasePath(nameof(TestMigration));
string configFile = Path.Combine(osuRoot, "tournament.ini");
if (File.Exists(configFile))
@ -136,18 +145,29 @@ namespace osu.Game.Tournament.Tests.NonVisual
}
finally
{
host.Exit();
try
{
host.Storage.Delete("tournament.ini");
host.Storage.DeleteDirectory("tournaments");
if (Directory.Exists(osuRoot))
Directory.Delete(osuRoot, true);
}
catch
{
}
catch { }
host.Exit();
}
}
}
private string basePath(string testInstance) => Path.Combine(RuntimeInfo.StartupDirectory, "headless", testInstance);
public static string PrepareBasePath(string testInstance)
{
string basePath = Path.Combine(RuntimeInfo.StartupDirectory, "headless", testInstance);
// manually clean before starting in case there are left-over files at the test site.
if (Directory.Exists(basePath))
Directory.Delete(basePath, true);
return basePath;
}
}
}

View File

@ -3,7 +3,6 @@
using System.IO;
using NUnit.Framework;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Platform;
using osu.Game.Tournament.IO;
@ -20,7 +19,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
// don't use clean run because files are being written before osu! launches.
using (HeadlessGameHost host = new HeadlessGameHost(nameof(CheckIPCLocation)))
{
string basePath = Path.Combine(RuntimeInfo.StartupDirectory, "headless", nameof(CheckIPCLocation));
string basePath = CustomTourneyDirectoryTest.PrepareBasePath(nameof(CheckIPCLocation));
// Set up a fake IPC client for the IPC Storage to switch to.
string testStableInstallDirectory = Path.Combine(basePath, "stable-ce");
@ -42,9 +41,16 @@ namespace osu.Game.Tournament.Tests.NonVisual
}
finally
{
host.Storage.DeleteDirectory(testStableInstallDirectory);
host.Storage.DeleteDirectory("tournaments");
host.Exit();
try
{
if (Directory.Exists(basePath))
Directory.Delete(basePath, true);
}
catch
{
}
}
}
}

View File

@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
return tournament;
}
public static void WaitForOrAssert(Func<bool> result, string failureMessage, int timeout = 90000)
public static void WaitForOrAssert(Func<bool> result, string failureMessage, int timeout = 30000)
{
Task task = Task.Run(() =>
{

View File

@ -127,7 +127,7 @@ namespace osu.Game.Tournament.Components
(string heading, string content)[] stats;
switch (ruleset.Value.ID)
switch (ruleset.Value.OnlineID)
{
default:
stats = new (string heading, string content)[]

View File

@ -340,8 +340,8 @@ namespace osu.Game.Beatmaps
}
public bool Equals(DifficultyCacheLookup other)
=> BeatmapInfo.ID == other.BeatmapInfo.ID
&& Ruleset.ID == other.Ruleset.ID
=> BeatmapInfo.Equals(other.BeatmapInfo)
&& Ruleset.Equals(other.Ruleset)
&& OrderedMods.SequenceEqual(other.OrderedMods);
public override int GetHashCode()
@ -349,7 +349,7 @@ namespace osu.Game.Beatmaps
var hashCode = new HashCode();
hashCode.Add(BeatmapInfo.ID);
hashCode.Add(Ruleset.ID);
hashCode.Add(Ruleset.ShortName);
foreach (var mod in OrderedMods)
hashCode.Add(mod);

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Testing;
using osu.Game.Database;
@ -36,7 +35,7 @@ namespace osu.Game.Beatmaps
[JsonIgnore]
public int BeatmapSetInfoID { get; set; }
public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
public BeatmapOnlineStatus Status { get; set; } = BeatmapOnlineStatus.None;
[Required]
public BeatmapSetInfo BeatmapSet { get; set; }
@ -104,28 +103,6 @@ namespace osu.Game.Beatmaps
/// </summary>
public int CountdownOffset { get; set; }
// Editor
// This bookmarks stuff is necessary because DB doesn't know how to store int[]
[JsonIgnore]
public string StoredBookmarks
{
get => string.Join(',', Bookmarks);
set
{
if (string.IsNullOrEmpty(value))
{
Bookmarks = Array.Empty<int>();
return;
}
Bookmarks = value.Split(',').Select(v =>
{
bool result = int.TryParse(v, out int val);
return new { result, val };
}).Where(p => p.result).Select(p => p.val).ToArray();
}
}
[NotMapped]
public int[] Bookmarks { get; set; } = Array.Empty<int>();

View File

@ -10,7 +10,6 @@ using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Audio;
using osu.Framework.Audio.Mixing;
using osu.Framework.Audio.Track;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
@ -40,7 +39,7 @@ namespace osu.Game.Beatmaps
private readonly WorkingBeatmapCache workingBeatmapCache;
private readonly BeatmapOnlineLookupQueue onlineBeatmapLookupQueue;
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore<byte[]> gameResources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false, AudioMixer mainTrackMixer = null)
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore<byte[]> gameResources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false)
{
var userResources = new FileStore(contextFactory, storage).Store;
@ -86,7 +85,7 @@ namespace osu.Game.Beatmaps
var set = new BeatmapSetInfo
{
Metadata = metadata,
Beatmaps = new List<BeatmapInfo>
Beatmaps =
{
new BeatmapInfo
{

View File

@ -82,7 +82,7 @@ namespace osu.Game.Beatmaps
protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
{
if (archive != null)
beatmapSet.Beatmaps = createBeatmapDifficulties(beatmapSet.Files);
beatmapSet.Beatmaps.AddRange(createBeatmapDifficulties(beatmapSet.Files));
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
{
@ -208,7 +208,7 @@ namespace osu.Game.Beatmaps
using (ContextFactory.GetForWrite())
{
beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == beatmapInfo.ID);
beatmapInfo = setInfo.Beatmaps.Single(b => b.Equals(beatmapInfo));
var metadata = beatmapInfo.Metadata ?? setInfo.Metadata;
@ -370,7 +370,6 @@ namespace osu.Game.Beatmaps
return new BeatmapSetInfo
{
OnlineID = beatmap.BeatmapInfo.BeatmapSet?.OnlineID,
Beatmaps = new List<BeatmapInfo>(),
Metadata = beatmap.Metadata,
DateAdded = DateTimeOffset.UtcNow
};

View File

@ -83,7 +83,7 @@ namespace osu.Game.Beatmaps
if (res != null)
{
beatmapInfo.Status = res.Status;
beatmapInfo.BeatmapSet.Status = res.BeatmapSet?.Status ?? BeatmapSetOnlineStatus.None;
beatmapInfo.BeatmapSet.Status = res.BeatmapSet?.Status ?? BeatmapOnlineStatus.None;
beatmapInfo.BeatmapSet.OnlineID = res.OnlineBeatmapSetID;
beatmapInfo.OnlineID = res.OnlineID;
@ -182,7 +182,7 @@ namespace osu.Game.Beatmaps
{
if (reader.Read())
{
var status = (BeatmapSetOnlineStatus)reader.GetByte(2);
var status = (BeatmapOnlineStatus)reader.GetByte(2);
beatmapInfo.Status = status;
beatmapInfo.BeatmapSet.Status = status;

View File

@ -6,7 +6,7 @@ using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Beatmaps
{
public enum BeatmapSetOnlineStatus
public enum BeatmapOnlineStatus
{
None = -3,
@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps
public static class BeatmapSetOnlineStatusExtensions
{
public static bool GrantsPerformancePoints(this BeatmapSetOnlineStatus status)
=> status == BeatmapSetOnlineStatus.Ranked || status == BeatmapSetOnlineStatus.Approved;
public static bool GrantsPerformancePoints(this BeatmapOnlineStatus status)
=> status == BeatmapOnlineStatus.Ranked || status == BeatmapOnlineStatus.Approved;
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Extensions;
@ -30,9 +31,10 @@ namespace osu.Game.Beatmaps
public BeatmapMetadata Metadata { get; set; }
public List<BeatmapInfo> Beatmaps { get; set; }
[NotNull]
public List<BeatmapInfo> Beatmaps { get; } = new List<BeatmapInfo>();
public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
public BeatmapOnlineStatus Status { get; set; } = BeatmapOnlineStatus.None;
[NotNull]
public List<BeatmapSetFileInfo> Files { get; set; } = new List<BeatmapSetFileInfo>();
@ -40,17 +42,20 @@ namespace osu.Game.Beatmaps
/// <summary>
/// The maximum star difficulty of all beatmaps in this set.
/// </summary>
public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarRating) ?? 0;
[JsonIgnore]
public double MaxStarDifficulty => Beatmaps.Count == 0 ? 0 : Beatmaps.Max(b => b.StarRating);
/// <summary>
/// The maximum playable length in milliseconds of all beatmaps in this set.
/// </summary>
public double MaxLength => Beatmaps?.Max(b => b.Length) ?? 0;
[JsonIgnore]
public double MaxLength => Beatmaps.Count == 0 ? 0 : Beatmaps.Max(b => b.Length);
/// <summary>
/// The maximum BPM of all beatmaps in this set.
/// </summary>
public double MaxBPM => Beatmaps?.Max(b => b.BPM) ?? 0;
[JsonIgnore]
public double MaxBPM => Beatmaps.Count == 0 ? 0 : Beatmaps.Max(b => b.BPM);
[NotMapped]
public bool DeletePending { get; set; }
@ -89,7 +94,7 @@ namespace osu.Game.Beatmaps
#region Implementation of IBeatmapSetInfo
IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => Metadata;
IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => Metadata ?? Beatmaps.FirstOrDefault()?.Metadata ?? new BeatmapMetadata();
IEnumerable<IBeatmapInfo> IBeatmapSetInfo.Beatmaps => Beatmaps;
IEnumerable<INamedFileUsage> IBeatmapSetInfo.Files => Files;

View File

@ -83,7 +83,7 @@ namespace osu.Game.Beatmaps
requestedUserId = api.LocalUser.Value.Id;
// only query API for built-in rulesets
rulesets.AvailableRulesets.Where(ruleset => ruleset.ID <= ILegacyRuleset.MAX_LEGACY_RULESET_ID).ForEach(rulesetInfo =>
rulesets.AvailableRulesets.Where(ruleset => ruleset.OnlineID >= 0 && ruleset.OnlineID <= ILegacyRuleset.MAX_LEGACY_RULESET_ID).ForEach(rulesetInfo =>
{
var req = new GetUserRequest(api.LocalUser.Value.Id, rulesetInfo);

View File

@ -18,9 +18,9 @@ namespace osu.Game.Beatmaps.Drawables
{
public class BeatmapSetOnlineStatusPill : CircularContainer
{
private BeatmapSetOnlineStatus status;
private BeatmapOnlineStatus status;
public BeatmapSetOnlineStatus Status
public BeatmapOnlineStatus Status
{
get => status;
set
@ -75,7 +75,7 @@ namespace osu.Game.Beatmaps.Drawables
},
};
Status = BeatmapSetOnlineStatus.None;
Status = BeatmapOnlineStatus.None;
TextPadding = new MarginPadding { Horizontal = 5, Bottom = 1 };
}
@ -87,14 +87,14 @@ namespace osu.Game.Beatmaps.Drawables
private void updateState()
{
Alpha = Status == BeatmapSetOnlineStatus.None ? 0 : 1;
Alpha = Status == BeatmapOnlineStatus.None ? 0 : 1;
statusText.Text = Status.GetLocalisableDescription().ToUpper();
if (colourProvider != null)
statusText.Colour = status == BeatmapSetOnlineStatus.Graveyard ? colourProvider.Background1 : colourProvider.Background3;
statusText.Colour = status == BeatmapOnlineStatus.Graveyard ? colourProvider.Background1 : colourProvider.Background3;
else
statusText.Colour = status == BeatmapSetOnlineStatus.Graveyard ? colours.GreySeafoamLight : Color4.Black;
statusText.Colour = status == BeatmapOnlineStatus.Graveyard ? colours.GreySeafoamLight : Color4.Black;
background.Colour = OsuColour.ForBeatmapSetOnlineStatus(Status) ?? colourProvider?.Light1 ?? colours.GreySeafoamLighter;
}

View File

@ -41,10 +41,10 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Statistics
// reference: https://github.com/ppy/osu-web/blob/ef432c11719fd1207bec5f9194b04f0033bdf02c/resources/assets/lib/beatmapset-panel.tsx#L36-L44
switch (beatmapSetInfo.Status)
{
case BeatmapSetOnlineStatus.Ranked:
case BeatmapSetOnlineStatus.Approved:
case BeatmapSetOnlineStatus.Loved:
case BeatmapSetOnlineStatus.Qualified:
case BeatmapOnlineStatus.Ranked:
case BeatmapOnlineStatus.Approved:
case BeatmapOnlineStatus.Loved:
case BeatmapOnlineStatus.Qualified:
return beatmapSetInfo.Ranked;
default:

View File

@ -117,7 +117,7 @@ namespace osu.Game.Beatmaps.Drawables
Spacing = new Vector2(1, 0);
Direction = FillDirection.Horizontal;
var icon = rulesets.GetRuleset(rulesetId)?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle };
var icon = rulesets.GetRuleset(rulesetId)?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle };
Add(icon.With(i =>
{
i.Size = new Vector2(14);

View File

@ -38,7 +38,7 @@ namespace osu.Game.Beatmaps
CircleSize = 0,
OverallDifficulty = 0,
},
Ruleset = new DummyRulesetInfo()
Ruleset = new DummyRuleset().RulesetInfo
}, audio)
{
this.textures = textures;
@ -54,42 +54,37 @@ namespace osu.Game.Beatmaps
public override Stream GetStream(string storagePath) => null;
private class DummyRulesetInfo : RulesetInfo
private class DummyRuleset : Ruleset
{
public override Ruleset CreateInstance() => new DummyRuleset();
public override IEnumerable<Mod> GetModsFor(ModType type) => Array.Empty<Mod>();
private class DummyRuleset : Ruleset
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
{
public override IEnumerable<Mod> GetModsFor(ModType type) => Array.Empty<Mod>();
throw new NotImplementedException();
}
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new DummyBeatmapConverter { Beatmap = beatmap };
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null;
public override string Description => "dummy";
public override string ShortName => "dummy";
private class DummyBeatmapConverter : IBeatmapConverter
{
public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
public IBeatmap Beatmap { get; set; }
public bool CanConvert() => true;
public IBeatmap Convert(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
foreach (var obj in Beatmap.HitObjects)
ObjectConverted?.Invoke(obj, obj.Yield());
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new DummyBeatmapConverter { Beatmap = beatmap };
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null;
public override string Description => "dummy";
public override string ShortName => "dummy";
private class DummyBeatmapConverter : IBeatmapConverter
{
public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
public IBeatmap Beatmap { get; set; }
public bool CanConvert() => true;
public IBeatmap Convert(CancellationToken cancellationToken = default)
{
foreach (var obj in Beatmap.HitObjects)
ObjectConverted?.Invoke(obj, obj.Yield());
return Beatmap;
}
return Beatmap;
}
}
}

View File

@ -201,7 +201,11 @@ namespace osu.Game.Beatmaps.Formats
switch (pair.Key)
{
case @"Bookmarks":
beatmap.BeatmapInfo.StoredBookmarks = pair.Value;
beatmap.BeatmapInfo.Bookmarks = pair.Value.Split(',').Select(v =>
{
bool result = int.TryParse(v, out int val);
return new { result, val };
}).Where(p => p.result).Select(p => p.val).ToArray();
break;
case @"DistanceSpacing":

View File

@ -22,7 +22,7 @@ namespace osu.Game.Beatmaps
/// <summary>
/// The best-effort metadata representing this set. In the case metadata differs between contained beatmaps, one is arbitrarily chosen.
/// </summary>
IBeatmapMetadataInfo? Metadata { get; }
IBeatmapMetadataInfo Metadata { get; }
/// <summary>
/// All beatmaps contained in this set.

View File

@ -30,7 +30,7 @@ namespace osu.Game.Beatmaps
/// <summary>
/// The status of this beatmap set.
/// </summary>
BeatmapSetOnlineStatus Status { get; }
BeatmapOnlineStatus Status { get; }
/// <summary>
/// Whether or not this beatmap set has explicit content.
@ -105,7 +105,7 @@ namespace osu.Game.Beatmaps
/// <summary>
/// Contains the current hype status of the beatmap set.
/// Non-null only for <see cref="BeatmapSetOnlineStatus.WIP"/>, <see cref="BeatmapSetOnlineStatus.Pending"/>, and <see cref="BeatmapSetOnlineStatus.Qualified"/> sets.
/// Non-null only for <see cref="BeatmapOnlineStatus.WIP"/>, <see cref="BeatmapOnlineStatus.Pending"/>, and <see cref="BeatmapOnlineStatus.Qualified"/> sets.
/// </summary>
/// <remarks>
/// See: https://github.com/ppy/osu-web/blob/93930cd02cfbd49724929912597c727c9fbadcd1/app/Models/Beatmapset.php#L155

View File

@ -55,8 +55,6 @@ namespace osu.Game.Beatmaps
public void Invalidate(BeatmapSetInfo info)
{
if (info.Beatmaps == null) return;
foreach (var b in info.Beatmaps)
Invalidate(b);
}
@ -65,7 +63,7 @@ namespace osu.Game.Beatmaps
{
lock (workingCache)
{
var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID);
var working = workingCache.FirstOrDefault(w => info.Equals(w.BeatmapInfo));
if (working != null)
{
@ -89,7 +87,7 @@ namespace osu.Game.Beatmaps
lock (workingCache)
{
var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID);
var working = workingCache.FirstOrDefault(w => beatmapInfo.Equals(w.BeatmapInfo));
if (working != null)
return working;

View File

@ -26,7 +26,7 @@ namespace osu.Game.Configuration
protected override void InitialiseDefaults()
{
// UI/selection defaults
SetDefault(OsuSetting.Ruleset, 0, 0, int.MaxValue);
SetDefault(OsuSetting.Ruleset, string.Empty);
SetDefault(OsuSetting.Skin, 0, -1, int.MaxValue);
SetDefault(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Details);

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Game.Database;
using Realms;
#nullable enable
@ -10,13 +8,10 @@ using Realms;
namespace osu.Game.Configuration
{
[MapTo(@"RulesetSetting")]
public class RealmRulesetSetting : RealmObject, IHasGuidPrimaryKey
public class RealmRulesetSetting : RealmObject
{
[PrimaryKey]
public Guid ID { get; set; } = Guid.NewGuid();
[Indexed]
public int RulesetID { get; set; }
public string RulesetName { get; set; } = string.Empty;
[Indexed]
public int Variant { get; set; }

View File

@ -11,6 +11,7 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Statistics;
using osu.Game.Configuration;
using osu.Game.Input.Bindings;
using osu.Game.Models;
using Realms;
@ -31,22 +32,27 @@ namespace osu.Game.Database
/// </summary>
public readonly string Filename;
private readonly IDatabaseContextFactory? efContextFactory;
/// <summary>
/// Version history:
/// 6 First tracked version (~20211018)
/// 7 Changed OnlineID fields to non-nullable to add indexing support (20211018)
/// 8 Rebind scroll adjust keys to not have control modifier (20211029)
/// 9 Converted BeatmapMetadata.Author from string to RealmUser (20211104)
/// 6 ~2021-10-18 First tracked version.
/// 7 2021-10-18 Changed OnlineID fields to non-nullable to add indexing support.
/// 8 2021-10-29 Rebind scroll adjust keys to not have control modifier.
/// 9 2021-11-04 Converted BeatmapMetadata.Author from string to RealmUser.
/// 10 2021-11-22 Use ShortName instead of RulesetID for ruleset settings.
/// 11 2021-11-22 Use ShortName instead of RulesetID for ruleset key bindings.
/// 12 2021-11-24 Add Status to RealmBeatmapSet.
/// </summary>
private const int schema_version = 9;
private const int schema_version = 12;
/// <summary>
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking context creation during blocking periods.
/// </summary>
private readonly SemaphoreSlim contextCreationLock = new SemaphoreSlim(1);
private static readonly GlobalStatistic<int> refreshes = GlobalStatistics.Get<int>("Realm", "Dirty Refreshes");
private static readonly GlobalStatistic<int> contexts_created = GlobalStatistics.Get<int>("Realm", "Contexts (Created)");
private static readonly GlobalStatistic<int> refreshes = GlobalStatistics.Get<int>(@"Realm", @"Dirty Refreshes");
private static readonly GlobalStatistic<int> contexts_created = GlobalStatistics.Get<int>(@"Realm", @"Contexts (Created)");
private readonly object contextLock = new object();
private Realm? context;
@ -56,14 +62,14 @@ namespace osu.Game.Database
get
{
if (!ThreadSafety.IsUpdateThread)
throw new InvalidOperationException($"Use {nameof(CreateContext)} when performing realm operations from a non-update thread");
throw new InvalidOperationException(@$"Use {nameof(CreateContext)} when performing realm operations from a non-update thread");
lock (contextLock)
{
if (context == null)
{
context = CreateContext();
Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}");
Logger.Log(@$"Opened realm ""{context.Config.DatabasePath}"" at version {context.Config.SchemaVersion}");
}
// creating a context will ensure our schema is up-to-date and migrated.
@ -72,18 +78,30 @@ namespace osu.Game.Database
}
}
public RealmContextFactory(Storage storage, string filename)
/// <summary>
/// Construct a new instance of a realm context factory.
/// </summary>
/// <param name="storage">The game storage which will be used to create the realm backing file.</param>
/// <param name="filename">The filename to use for the realm backing file. A ".realm" extension will be added automatically if not specified.</param>
/// <param name="efContextFactory">An EF factory used only for migration purposes.</param>
public RealmContextFactory(Storage storage, string filename, IDatabaseContextFactory? efContextFactory = null)
{
this.storage = storage;
this.efContextFactory = efContextFactory;
Filename = filename;
const string realm_extension = ".realm";
const string realm_extension = @".realm";
if (!Filename.EndsWith(realm_extension, StringComparison.Ordinal))
Filename += realm_extension;
// This method triggers the first `CreateContext` call, which will implicitly run realm migrations and bring the schema up-to-date.
cleanupPendingDeletions();
// Data migration is handled separately from schema migrations.
// This is required as the user may be initialising realm for the first time ever, which would result in no schema migrations running.
migrateDataFromEF();
}
private void cleanupPendingDeletions()
@ -151,15 +169,62 @@ namespace osu.Game.Database
};
}
private void migrateDataFromEF()
{
if (efContextFactory == null)
return;
using (var db = efContextFactory.GetForWrite())
{
// migrate ruleset settings. can be removed 20220315.
var existingSettings = db.Context.DatabasedSetting;
// previous entries in EF are removed post migration.
if (!existingSettings.Any())
return;
using (var realm = CreateContext())
using (var transaction = realm.BeginWrite())
{
// only migrate data if the realm database is empty.
if (!realm.All<RealmRulesetSetting>().Any())
{
foreach (var dkb in existingSettings)
{
if (dkb.RulesetID == null)
continue;
string? shortName = getRulesetShortNameFromLegacyID(dkb.RulesetID.Value);
if (string.IsNullOrEmpty(shortName))
continue;
realm.Add(new RealmRulesetSetting
{
Key = dkb.Key,
Value = dkb.StringValue,
RulesetName = shortName,
Variant = dkb.Variant ?? 0,
});
}
}
db.Context.RemoveRange(existingSettings);
transaction.Commit();
}
}
}
private void onMigration(Migration migration, ulong lastSchemaVersion)
{
for (ulong i = lastSchemaVersion; i <= schema_version; i++)
for (ulong i = lastSchemaVersion + 1; i <= schema_version; i++)
applyMigrationsForVersion(migration, i);
}
private void applyMigrationsForVersion(Migration migration, ulong version)
private void applyMigrationsForVersion(Migration migration, ulong targetVersion)
{
switch (version)
switch (targetVersion)
{
case 7:
convertOnlineIDs<RealmBeatmap>();
@ -232,10 +297,60 @@ namespace osu.Game.Database
};
}
break;
case 10:
string rulesetSettingClassName = getMappedOrOriginalName(typeof(RealmRulesetSetting));
var oldSettings = migration.OldRealm.DynamicApi.All(rulesetSettingClassName);
var newSettings = migration.NewRealm.All<RealmRulesetSetting>().ToList();
for (int i = 0; i < newSettings.Count; i++)
{
dynamic? oldItem = oldSettings.ElementAt(i);
var newItem = newSettings.ElementAt(i);
long rulesetId = oldItem.RulesetID;
string? rulesetName = getRulesetShortNameFromLegacyID(rulesetId);
if (string.IsNullOrEmpty(rulesetName))
migration.NewRealm.Remove(newItem);
else
newItem.RulesetName = rulesetName;
}
break;
case 11:
string keyBindingClassName = getMappedOrOriginalName(typeof(RealmKeyBinding));
var oldKeyBindings = migration.OldRealm.DynamicApi.All(keyBindingClassName);
var newKeyBindings = migration.NewRealm.All<RealmKeyBinding>().ToList();
for (int i = 0; i < newKeyBindings.Count; i++)
{
dynamic? oldItem = oldKeyBindings.ElementAt(i);
var newItem = newKeyBindings.ElementAt(i);
if (oldItem.RulesetID == null)
continue;
long rulesetId = oldItem.RulesetID;
string? rulesetName = getRulesetShortNameFromLegacyID(rulesetId);
if (string.IsNullOrEmpty(rulesetName))
migration.NewRealm.Remove(newItem);
else
newItem.RulesetName = rulesetName;
}
break;
}
}
private string? getRulesetShortNameFromLegacyID(long rulesetId) =>
efContextFactory?.Get().RulesetInfo.FirstOrDefault(r => r.ID == rulesetId)?.ShortName;
/// <summary>
/// Flush any active contexts and block any further writes.
/// </summary>
@ -250,7 +365,7 @@ namespace osu.Game.Database
throw new ObjectDisposedException(nameof(RealmContextFactory));
if (!ThreadSafety.IsUpdateThread)
throw new InvalidOperationException($"{nameof(BlockAllOperations)} must be called from the update thread.");
throw new InvalidOperationException(@$"{nameof(BlockAllOperations)} must be called from the update thread.");
Logger.Log(@"Blocking realm operations.", LoggingTarget.Database);
@ -274,7 +389,7 @@ namespace osu.Game.Database
timeout -= sleep_length;
if (timeout < 0)
throw new TimeoutException("Took too long to acquire lock");
throw new TimeoutException(@"Took too long to acquire lock");
}
}
catch

View File

@ -0,0 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
namespace osu.Game.Extensions
{
public static class CollectionExtensions
{
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
{
// List<T> has a potentially more optimal path to adding a range.
if (collection is List<T> list)
list.AddRange(items);
else
{
foreach (T obj in items)
collection.Add(obj);
}
}
}
}

View File

@ -42,7 +42,7 @@ namespace osu.Game.Extensions
switch (model)
{
case IBeatmapSetInfo beatmapSetInfo:
result = beatmapSetInfo.Metadata?.GetDisplayTitle();
result = beatmapSetInfo.Metadata.GetDisplayTitle();
break;
case IBeatmapInfo beatmapInfo:

View File

@ -122,34 +122,34 @@ namespace osu.Game.Graphics
}
/// <summary>
/// Retrieves a colour for the given <see cref="BeatmapSetOnlineStatus"/>.
/// Retrieves a colour for the given <see cref="BeatmapOnlineStatus"/>.
/// A <see langword="null"/> value indicates that a "background" shade from the local <see cref="OverlayColourProvider"/>
/// (or another fallback colour) should be used.
/// </summary>
/// <remarks>
/// Sourced from web: https://github.com/ppy/osu-web/blob/007eebb1916ed5cb6a7866d82d8011b1060a945e/resources/assets/less/layout.less#L36-L50
/// </remarks>
public static Color4? ForBeatmapSetOnlineStatus(BeatmapSetOnlineStatus status)
public static Color4? ForBeatmapSetOnlineStatus(BeatmapOnlineStatus status)
{
switch (status)
{
case BeatmapSetOnlineStatus.Ranked:
case BeatmapSetOnlineStatus.Approved:
case BeatmapOnlineStatus.Ranked:
case BeatmapOnlineStatus.Approved:
return Color4Extensions.FromHex(@"b3ff66");
case BeatmapSetOnlineStatus.Loved:
case BeatmapOnlineStatus.Loved:
return Color4Extensions.FromHex(@"ff66ab");
case BeatmapSetOnlineStatus.Qualified:
case BeatmapOnlineStatus.Qualified:
return Color4Extensions.FromHex(@"66ccff");
case BeatmapSetOnlineStatus.Pending:
case BeatmapOnlineStatus.Pending:
return Color4Extensions.FromHex(@"ffd966");
case BeatmapSetOnlineStatus.WIP:
case BeatmapOnlineStatus.WIP:
return Color4Extensions.FromHex(@"ff9966");
case BeatmapSetOnlineStatus.Graveyard:
case BeatmapOnlineStatus.Graveyard:
return Color4.Black;
default:

View File

@ -50,23 +50,20 @@ namespace osu.Game.Input.Bindings
protected override void LoadComplete()
{
if (ruleset == null || ruleset.ID.HasValue)
{
int? rulesetId = ruleset?.ID;
string rulesetName = ruleset?.ShortName;
realmKeyBindings = realmFactory.Context.All<RealmKeyBinding>()
.Where(b => b.RulesetID == rulesetId && b.Variant == variant);
realmKeyBindings = realmFactory.Context.All<RealmKeyBinding>()
.Where(b => b.RulesetName == rulesetName && b.Variant == variant);
realmSubscription = realmKeyBindings
.SubscribeForNotifications((sender, changes, error) =>
{
// first subscription ignored as we are handling this in LoadComplete.
if (changes == null)
return;
realmSubscription = realmKeyBindings
.SubscribeForNotifications((sender, changes, error) =>
{
// first subscription ignored as we are handling this in LoadComplete.
if (changes == null)
return;
ReloadMappings();
});
}
ReloadMappings();
});
base.LoadComplete();
}

View File

@ -6,6 +6,8 @@ using osu.Framework.Input.Bindings;
using osu.Game.Database;
using Realms;
#nullable enable
namespace osu.Game.Input.Bindings
{
[MapTo(nameof(KeyBinding))]
@ -14,7 +16,7 @@ namespace osu.Game.Input.Bindings
[PrimaryKey]
public Guid ID { get; set; } = Guid.NewGuid();
public int? RulesetID { get; set; }
public string? RulesetName { get; set; }
public int? Variant { get; set; }
@ -34,6 +36,6 @@ namespace osu.Game.Input.Bindings
public int ActionInt { get; set; }
[MapTo(nameof(KeyCombination))]
public string KeyCombinationString { get; set; }
public string KeyCombinationString { get; set; } = string.Empty;
}
}

View File

@ -36,7 +36,7 @@ namespace osu.Game.Input
using (var context = realmFactory.CreateContext())
{
foreach (var action in context.All<RealmKeyBinding>().Where(b => b.RulesetID == null && (GlobalAction)b.ActionInt == globalAction))
foreach (var action in context.All<RealmKeyBinding>().Where(b => string.IsNullOrEmpty(b.RulesetName) && (GlobalAction)b.ActionInt == globalAction))
{
string str = keyCombinationProvider.GetReadableString(action.KeyCombination);
@ -69,20 +69,20 @@ namespace osu.Game.Input
{
var instance = ruleset.CreateInstance();
foreach (int variant in instance.AvailableVariants)
insertDefaults(realm, existingBindings, instance.GetDefaultKeyBindings(variant), ruleset.ID, variant);
insertDefaults(realm, existingBindings, instance.GetDefaultKeyBindings(variant), ruleset.ShortName, variant);
}
transaction.Commit();
}
}
private void insertDefaults(Realm realm, List<RealmKeyBinding> existingBindings, IEnumerable<IKeyBinding> defaults, int? rulesetId = null, int? variant = null)
private void insertDefaults(Realm realm, List<RealmKeyBinding> existingBindings, IEnumerable<IKeyBinding> defaults, string? rulesetName = null, int? variant = null)
{
// compare counts in database vs defaults for each action type.
foreach (var defaultsForAction in defaults.GroupBy(k => k.Action))
{
// avoid performing redundant queries when the database is empty and needs to be re-filled.
int existingCount = existingBindings.Count(k => k.RulesetID == rulesetId && k.Variant == variant && k.ActionInt == (int)defaultsForAction.Key);
int existingCount = existingBindings.Count(k => k.RulesetName == rulesetName && k.Variant == variant && k.ActionInt == (int)defaultsForAction.Key);
if (defaultsForAction.Count() <= existingCount)
continue;
@ -92,7 +92,7 @@ namespace osu.Game.Input
{
KeyCombinationString = k.KeyCombination.ToString(),
ActionInt = (int)k.Action,
RulesetID = rulesetId,
RulesetName = rulesetName,
Variant = variant
}));
}

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json;
using osu.Framework.Testing;
@ -35,14 +36,17 @@ namespace osu.Game.Models
public RealmBeatmapSet? BeatmapSet { get; set; }
public BeatmapSetOnlineStatus Status
[Ignored]
public RealmNamedFileUsage? File => BeatmapSet?.Files.First(f => f.File.Hash == Hash);
public BeatmapOnlineStatus Status
{
get => (BeatmapSetOnlineStatus)StatusInt;
get => (BeatmapOnlineStatus)StatusInt;
set => StatusInt = (int)value;
}
[MapTo(nameof(Status))]
public int StatusInt { get; set; }
public int StatusInt { get; set; } = (int)BeatmapOnlineStatus.None;
[Indexed]
public int OnlineID { get; set; } = -1;

View File

@ -26,12 +26,21 @@ namespace osu.Game.Models
public DateTimeOffset DateAdded { get; set; }
public IBeatmapMetadataInfo? Metadata => Beatmaps.FirstOrDefault()?.Metadata;
public IBeatmapMetadataInfo Metadata => Beatmaps.FirstOrDefault()?.Metadata ?? new RealmBeatmapMetadata();
public IList<RealmBeatmap> Beatmaps { get; } = null!;
public IList<RealmNamedFileUsage> Files { get; } = null!;
public BeatmapOnlineStatus Status
{
get => (BeatmapOnlineStatus)StatusInt;
set => StatusInt = (int)value;
}
[MapTo(nameof(Status))]
public int StatusInt { get; set; } = (int)BeatmapOnlineStatus.None;
public bool DeletePending { get; set; }
public string Hash { get; set; } = string.Empty;
@ -62,7 +71,7 @@ namespace osu.Game.Models
return ID == other.ID;
}
public override string ToString() => Metadata?.GetDisplayString() ?? base.ToString();
public override string ToString() => Metadata.GetDisplayString();
public bool Equals(IBeatmapSetInfo? other) => other is RealmBeatmapSet b && Equals(b);

View File

@ -25,12 +25,12 @@ namespace osu.Game.Models
public string InstantiationInfo { get; set; } = string.Empty;
public RealmRuleset(string shortName, string name, string instantiationInfo, int? onlineID = null)
public RealmRuleset(string shortName, string name, string instantiationInfo, int onlineID)
{
ShortName = shortName;
Name = name;
InstantiationInfo = instantiationInfo;
OnlineID = onlineID ?? -1;
OnlineID = onlineID;
}
[UsedImplicitly]
@ -60,5 +60,27 @@ namespace osu.Game.Models
InstantiationInfo = InstantiationInfo,
Available = Available
};
public Ruleset CreateInstance()
{
if (!Available)
throw new RulesetLoadException(@"Ruleset not available");
var type = Type.GetType(InstantiationInfo);
if (type == null)
throw new RulesetLoadException(@"Type lookup failure");
var ruleset = Activator.CreateInstance(type) as Ruleset;
if (ruleset == null)
throw new RulesetLoadException(@"Instantiation failure");
// overwrite the pre-populated RulesetInfo with a potentially database attached copy.
// TODO: figure if we still want/need this after switching to realm.
// ruleset.RulesetInfo = this;
return ruleset;
}
}
}

View File

@ -20,7 +20,7 @@ namespace osu.Game.Online.API.Requests.Responses
public int OnlineBeatmapSetID { get; set; }
[JsonProperty(@"status")]
public BeatmapSetOnlineStatus Status { get; set; }
public BeatmapOnlineStatus Status { get; set; }
[JsonProperty("checksum")]
public string Checksum { get; set; } = string.Empty;
@ -98,7 +98,7 @@ namespace osu.Game.Online.API.Requests.Responses
public string MD5Hash => Checksum;
public IRulesetInfo Ruleset => new RulesetInfo { ID = RulesetID };
public IRulesetInfo Ruleset => new RulesetInfo { OnlineID = RulesetID };
[JsonIgnore]
public string Hash => throw new NotImplementedException();

View File

@ -21,7 +21,7 @@ namespace osu.Game.Online.API.Requests.Responses
public int OnlineID { get; set; }
[JsonProperty(@"status")]
public BeatmapSetOnlineStatus Status { get; set; }
public BeatmapOnlineStatus Status { get; set; }
[JsonProperty(@"preview_url")]
public string Preview { get; set; } = string.Empty;

View File

@ -146,7 +146,7 @@ namespace osu.Game.Online.API.Requests.Responses
return scoreInfo;
}
public IRulesetInfo Ruleset => new RulesetInfo { ID = RulesetID };
public IRulesetInfo Ruleset => new RulesetInfo { OnlineID = RulesetID };
IBeatmapInfo IScoreInfo.Beatmap => Beatmap;
}

View File

@ -86,8 +86,8 @@ namespace osu.Game.Online.API.Requests
if (General != null && General.Any())
req.AddParameter("c", string.Join('.', General.Select(e => e.ToString().Underscore())));
if (ruleset.ID.HasValue)
req.AddParameter("m", ruleset.ID.Value.ToString());
if (ruleset.OnlineID >= 0)
req.AddParameter("m", ruleset.OnlineID.ToString());
req.AddParameter("s", SearchCategory.ToString().ToLowerInvariant());

View File

@ -162,11 +162,10 @@ namespace osu.Game.Online.Multiplayer
foreach (var user in joinedRoom.Users)
updateUserPlayingState(user.UserID, user.State);
updateLocalRoomSettings(joinedRoom.Settings);
OnRoomJoined();
}, cancellationSource.Token).ConfigureAwait(false);
// Update room settings.
await updateLocalRoomSettings(joinedRoom.Settings, cancellationSource.Token).ConfigureAwait(false);
}, cancellationSource.Token).ConfigureAwait(false);
}
@ -449,8 +448,7 @@ namespace osu.Game.Online.Multiplayer
Task IMultiplayerClient.SettingsChanged(MultiplayerRoomSettings newSettings)
{
// Do not return this task, as it will cause tests to deadlock.
updateLocalRoomSettings(newSettings);
Scheduler.Add(() => updateLocalRoomSettings(newSettings));
return Task.CompletedTask;
}
@ -690,8 +688,7 @@ namespace osu.Game.Online.Multiplayer
/// This updates both the joined <see cref="MultiplayerRoom"/> and the respective API <see cref="Room"/>.
/// </remarks>
/// <param name="settings">The new <see cref="MultiplayerRoomSettings"/> to update from.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to cancel the update.</param>
private Task updateLocalRoomSettings(MultiplayerRoomSettings settings, CancellationToken cancellationToken = default) => scheduleAsync(() =>
private void updateLocalRoomSettings(MultiplayerRoomSettings settings)
{
if (Room == null)
return;
@ -702,10 +699,11 @@ namespace osu.Game.Online.Multiplayer
Room.Settings = settings;
APIRoom.Name.Value = Room.Settings.Name;
APIRoom.Password.Value = Room.Settings.Password;
APIRoom.QueueMode.Value = Room.Settings.QueueMode;
RoomUpdated?.Invoke();
CurrentMatchPlayingItem.Value = APIRoom.Playlist.SingleOrDefault(p => p.ID == settings.PlaylistItemId);
}, cancellationToken);
}
private async Task<PlaylistItem> createPlaylistItem(MultiplayerPlaylistItem item)
{

View File

@ -4,59 +4,58 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using osu.Framework.Configuration;
using osu.Framework.Screens;
using osu.Game.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Framework.Logging;
using osu.Framework.Allocation;
using osu.Game.Overlays.Toolbar;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Humanizer;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Collections;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input;
using osu.Game.Overlays.Notifications;
using osu.Game.Input.Bindings;
using osu.Game.Online.Chat;
using osu.Game.Overlays.Music;
using osu.Game.Skinning;
using osuTK.Graphics;
using osu.Game.Overlays.Volume;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Updater;
using osu.Game.Utils;
using LogLevel = osu.Framework.Logging.LogLevel;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.IO;
using osu.Game.Localisation;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Chat;
using osu.Game.Overlays;
using osu.Game.Overlays.Music;
using osu.Game.Overlays.Notifications;
using osu.Game.Overlays.Toolbar;
using osu.Game.Overlays.Volume;
using osu.Game.Performance;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Skinning;
using osu.Game.Skinning.Editor;
using osu.Game.Updater;
using osu.Game.Users;
using osu.Game.Utils;
using osuTK.Graphics;
namespace osu.Game
{
@ -158,7 +157,7 @@ namespace osu.Game
[CanBeNull]
private IntroScreen introScreen;
private Bindable<int> configRuleset;
private Bindable<string> configRuleset;
private Bindable<float> uiScale;
@ -222,10 +221,13 @@ namespace osu.Game
dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 });
// bind config int to database RulesetInfo
configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset);
configRuleset = LocalConfig.GetBindable<string>(OsuSetting.Ruleset);
uiScale = LocalConfig.GetBindable<float>(OsuSetting.UIScale);
var preferredRuleset = RulesetStore.GetRuleset(configRuleset.Value);
var preferredRuleset = int.TryParse(configRuleset.Value, out int rulesetId)
// int parsing can be removed 20220522
? RulesetStore.GetRuleset(rulesetId)
: RulesetStore.GetRuleset(configRuleset.Value);
try
{
@ -238,7 +240,7 @@ namespace osu.Game
Ruleset.Value = RulesetStore.AvailableRulesets.First();
}
Ruleset.ValueChanged += r => configRuleset.Value = r.NewValue.ID ?? 0;
Ruleset.ValueChanged += r => configRuleset.Value = r.NewValue.ShortName;
// bind config int to database SkinInfo
configSkin = LocalConfig.GetBindable<int>(OsuSetting.Skin);

View File

@ -188,7 +188,11 @@ namespace osu.Game
dependencies.Cache(contextFactory = new DatabaseContextFactory(Storage));
dependencies.Cache(realmFactory = new RealmContextFactory(Storage, "client"));
runMigrations();
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory, Storage));
dependencies.Cache(realmFactory = new RealmContextFactory(Storage, "client", contextFactory));
dependencies.CacheAs(Storage);
@ -203,8 +207,6 @@ namespace osu.Game
Audio.Samples.PlaybackConcurrency = SAMPLE_CONCURRENCY;
runMigrations();
dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Resources, Audio));
dependencies.CacheAs<ISkinSource>(SkinManager);
@ -212,7 +214,7 @@ namespace osu.Game
SkinManager.ItemRemoved += item => Schedule(() =>
{
// check the removed skin is not the current user choice. if it is, switch back to default.
if (item.ID == SkinManager.CurrentSkinInfo.Value.ID)
if (item.Equals(SkinManager.CurrentSkinInfo.Value))
SkinManager.CurrentSkinInfo.Value = SkinInfo.Default;
});
@ -227,7 +229,6 @@ namespace osu.Game
var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures);
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory, Storage));
dependencies.Cache(fileStore = new FileStore(contextFactory, Storage));
// ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup()
@ -261,8 +262,6 @@ namespace osu.Game
dependencies.Cache(scorePerformanceManager);
AddInternal(scorePerformanceManager);
migrateDataToRealm();
dependencies.Cache(rulesetConfigCache = new RulesetConfigCache(realmFactory, RulesetStore));
var powerStatus = CreateBatteryInfo();
@ -438,34 +437,6 @@ namespace osu.Game
private void migrateDataToRealm()
{
using (var db = contextFactory.GetForWrite())
using (var realm = realmFactory.CreateContext())
using (var transaction = realm.BeginWrite())
{
// migrate ruleset settings. can be removed 20220315.
var existingSettings = db.Context.DatabasedSetting;
// only migrate data if the realm database is empty.
if (!realm.All<RealmRulesetSetting>().Any())
{
foreach (var dkb in existingSettings)
{
if (dkb.RulesetID == null) continue;
realm.Add(new RealmRulesetSetting
{
Key = dkb.Key,
Value = dkb.StringValue,
RulesetID = dkb.RulesetID.Value,
Variant = dkb.Variant ?? 0,
});
}
}
db.Context.RemoveRange(existingSettings);
transaction.Commit();
}
}
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> r)

View File

@ -244,7 +244,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
noScoresPlaceholder.Hide();
if (Beatmap.Value == null || Beatmap.Value.OnlineID <= 0 || (Beatmap.Value?.BeatmapSet as IBeatmapSetOnlineInfo)?.Status <= BeatmapSetOnlineStatus.Pending)
if (Beatmap.Value == null || Beatmap.Value.OnlineID <= 0 || (Beatmap.Value?.BeatmapSet as IBeatmapSetOnlineInfo)?.Status <= BeatmapOnlineStatus.Pending)
{
Scores = null;
Hide();

View File

@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Music
filter.Search.OnCommit += (sender, newText) =>
{
BeatmapInfo toSelect = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault();
BeatmapInfo toSelect = list.FirstVisibleSet?.Beatmaps.FirstOrDefault();
if (toSelect != null)
{
@ -114,7 +114,7 @@ namespace osu.Game.Overlays.Music
private void itemSelected(BeatmapSetInfo set)
{
if (set.ID == (beatmap.Value?.BeatmapSetInfo?.ID ?? -1))
if (set.Equals((beatmap.Value?.BeatmapSetInfo)))
{
beatmap.Value?.Track.Seek(0);
return;

View File

@ -111,7 +111,7 @@ namespace osu.Game.Overlays
beatmapSets.Add(set);
});
private void beatmapRemoved(BeatmapSetInfo set) => Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID));
private void beatmapRemoved(BeatmapSetInfo set) => Schedule(() => beatmapSets.RemoveAll(s => s.Equals(set)));
private ScheduledDelegate seekDelegate;
@ -228,7 +228,7 @@ namespace osu.Game.Overlays
queuedDirection = TrackChangeDirection.Prev;
var playable = BeatmapSets.TakeWhile(i => i.ID != current.BeatmapSetInfo.ID).LastOrDefault() ?? BeatmapSets.LastOrDefault();
var playable = BeatmapSets.TakeWhile(i => !i.Equals(current.BeatmapSetInfo)).LastOrDefault() ?? BeatmapSets.LastOrDefault();
if (playable != null)
{
@ -259,7 +259,7 @@ namespace osu.Game.Overlays
queuedDirection = TrackChangeDirection.Next;
var playable = BeatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).ElementAtOrDefault(1) ?? BeatmapSets.FirstOrDefault();
var playable = BeatmapSets.SkipWhile(i => !i.Equals(current.BeatmapSetInfo)).ElementAtOrDefault(1) ?? BeatmapSets.FirstOrDefault();
if (playable != null)
{
@ -309,8 +309,8 @@ namespace osu.Game.Overlays
else
{
// figure out the best direction based on order in playlist.
int last = BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
int next = newWorking == null ? -1 : BeatmapSets.TakeWhile(b => b.ID != newWorking.BeatmapSetInfo?.ID).Count();
int last = BeatmapSets.TakeWhile(b => !b.Equals(current.BeatmapSetInfo)).Count();
int next = newWorking == null ? -1 : BeatmapSets.TakeWhile(b => !b.Equals(newWorking.BeatmapSetInfo)).Count();
direction = last > next ? TrackChangeDirection.Prev : TrackChangeDirection.Next;
}

View File

@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
public void SetDefaultRuleset(RulesetInfo ruleset)
{
foreach (var tabItem in TabContainer)
((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID;
((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.Equals(ruleset);
}
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value);

View File

@ -5,7 +5,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Localisation;
using osu.Game.Overlays.Settings.Sections.Debug;
using osu.Game.Overlays.Settings.Sections.DebugSettings;
namespace osu.Game.Overlays.Settings.Sections
{

View File

@ -9,7 +9,7 @@ using osu.Framework.Screens;
using osu.Game.Localisation;
using osu.Game.Screens.Import;
namespace osu.Game.Overlays.Settings.Sections.Debug
namespace osu.Game.Overlays.Settings.Sections.DebugSettings
{
public class GeneralSettings : SettingsSubsection
{

Some files were not shown because too many files have changed in this diff Show More