mirror of
https://github.com/ppy/osu
synced 2025-01-04 13:22:08 +00:00
Merge branch 'master' into osu-mod-repel
This commit is contained in:
commit
36f129a9b6
@ -52,7 +52,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.704.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.707.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||||
|
@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
complete.BindValueChanged(complete => updateComplete(complete.NewValue, 200));
|
complete.BindValueChanged(complete => updateDiscColour(complete.NewValue, 200));
|
||||||
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
|
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
|
||||||
|
|
||||||
updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
|
updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
|
||||||
@ -137,6 +137,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
this.ScaleTo(initial_scale);
|
this.ScaleTo(initial_scale);
|
||||||
this.RotateTo(0);
|
this.RotateTo(0);
|
||||||
|
|
||||||
|
updateDiscColour(false);
|
||||||
|
|
||||||
using (BeginDelayedSequence(spinner.TimePreempt / 2))
|
using (BeginDelayedSequence(spinner.TimePreempt / 2))
|
||||||
{
|
{
|
||||||
// constant ambient rotation to give the spinner "spinning" character.
|
// constant ambient rotation to give the spinner "spinning" character.
|
||||||
@ -177,12 +179,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// transforms we have from completing the spinner will be rolled back, so reapply immediately.
|
if (drawableSpinner.Result?.TimeCompleted is double completionTime)
|
||||||
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
|
{
|
||||||
updateComplete(state == ArmedState.Hit, 0);
|
using (BeginAbsoluteSequence(completionTime))
|
||||||
|
updateDiscColour(true, 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateComplete(bool complete, double duration)
|
private void updateDiscColour(bool complete, double duration = 0)
|
||||||
{
|
{
|
||||||
var colour = complete ? completeColour : normalColour;
|
var colour = complete ? completeColour : normalColour;
|
||||||
|
|
||||||
|
@ -196,6 +196,27 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
var slider = (Slider)workingObject.HitObject;
|
var slider = (Slider)workingObject.HitObject;
|
||||||
var possibleMovementBounds = CalculatePossibleMovementBounds(slider);
|
var possibleMovementBounds = CalculatePossibleMovementBounds(slider);
|
||||||
|
|
||||||
|
// The slider rotation applied in computeModifiedPosition might make it impossible to fit the slider into the playfield
|
||||||
|
// For example, a long horizontal slider will be off-screen when rotated by 90 degrees
|
||||||
|
// In this case, limit the rotation to either 0 or 180 degrees
|
||||||
|
if (possibleMovementBounds.Width < 0 || possibleMovementBounds.Height < 0)
|
||||||
|
{
|
||||||
|
float currentRotation = getSliderRotation(slider);
|
||||||
|
float diff1 = getAngleDifference(workingObject.RotationOriginal, currentRotation);
|
||||||
|
float diff2 = getAngleDifference(workingObject.RotationOriginal + MathF.PI, currentRotation);
|
||||||
|
|
||||||
|
if (diff1 < diff2)
|
||||||
|
{
|
||||||
|
RotateSlider(slider, workingObject.RotationOriginal - getSliderRotation(slider));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RotateSlider(slider, workingObject.RotationOriginal + MathF.PI - getSliderRotation(slider));
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleMovementBounds = calculatePossibleMovementBounds(slider);
|
||||||
|
}
|
||||||
|
|
||||||
var previousPosition = workingObject.PositionModified;
|
var previousPosition = workingObject.PositionModified;
|
||||||
|
|
||||||
// Clamp slider position to the placement area
|
// Clamp slider position to the placement area
|
||||||
@ -355,6 +376,18 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
return MathF.Atan2(endPositionVector.Y, endPositionVector.X);
|
return MathF.Atan2(endPositionVector.Y, endPositionVector.X);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the absolute difference between 2 angles measured in Radians.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="angle1">The first angle</param>
|
||||||
|
/// <param name="angle2">The second angle</param>
|
||||||
|
/// <returns>The absolute difference with interval <c>[0, MathF.PI)</c></returns>
|
||||||
|
private static float getAngleDifference(float angle1, float angle2)
|
||||||
|
{
|
||||||
|
float diff = MathF.Abs(angle1 - angle2) % (MathF.PI * 2);
|
||||||
|
return MathF.Min(diff, MathF.PI * 2 - diff);
|
||||||
|
}
|
||||||
|
|
||||||
public class ObjectPositionInfo
|
public class ObjectPositionInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -397,6 +430,7 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
|
|
||||||
private class WorkingObject
|
private class WorkingObject
|
||||||
{
|
{
|
||||||
|
public float RotationOriginal { get; }
|
||||||
public Vector2 PositionOriginal { get; }
|
public Vector2 PositionOriginal { get; }
|
||||||
public Vector2 PositionModified { get; set; }
|
public Vector2 PositionModified { get; set; }
|
||||||
public Vector2 EndPositionModified { get; set; }
|
public Vector2 EndPositionModified { get; set; }
|
||||||
@ -407,6 +441,7 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
public WorkingObject(ObjectPositionInfo positionInfo)
|
public WorkingObject(ObjectPositionInfo positionInfo)
|
||||||
{
|
{
|
||||||
PositionInfo = positionInfo;
|
PositionInfo = positionInfo;
|
||||||
|
RotationOriginal = HitObject is Slider slider ? getSliderRotation(slider) : 0;
|
||||||
PositionModified = PositionOriginal = HitObject.Position;
|
PositionModified = PositionOriginal = HitObject.Position;
|
||||||
EndPositionModified = HitObject.EndPosition;
|
EndPositionModified = HitObject.EndPosition;
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,64 @@ namespace osu.Game.Tests.Database
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFailedWritePerformsRollback()
|
||||||
|
{
|
||||||
|
RunTestWithRealm((realm, _) =>
|
||||||
|
{
|
||||||
|
Assert.Throws<InvalidOperationException>(() =>
|
||||||
|
{
|
||||||
|
realm.Write(r =>
|
||||||
|
{
|
||||||
|
r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()));
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(realm.Run(r => r.All<BeatmapInfo>()), Is.Empty);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFailedNestedWritePerformsRollback()
|
||||||
|
{
|
||||||
|
RunTestWithRealm((realm, _) =>
|
||||||
|
{
|
||||||
|
Assert.Throws<InvalidOperationException>(() =>
|
||||||
|
{
|
||||||
|
realm.Write(r =>
|
||||||
|
{
|
||||||
|
realm.Write(_ =>
|
||||||
|
{
|
||||||
|
r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()));
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(realm.Run(r => r.All<BeatmapInfo>()), Is.Empty);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNestedWriteCalls()
|
||||||
|
{
|
||||||
|
RunTestWithRealm((realm, _) =>
|
||||||
|
{
|
||||||
|
var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata());
|
||||||
|
|
||||||
|
var liveBeatmap = beatmap.ToLive(realm);
|
||||||
|
|
||||||
|
realm.Run(r =>
|
||||||
|
r.Write(_ =>
|
||||||
|
r.Write(_ =>
|
||||||
|
r.Add(beatmap)))
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAccessAfterAttach()
|
public void TestAccessAfterAttach()
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -116,10 +115,10 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
AddStep($"set audio offset to {userOffset}", () => localConfig.SetValue(OsuSetting.AudioOffset, userOffset));
|
AddStep($"set audio offset to {userOffset}", () => localConfig.SetValue(OsuSetting.AudioOffset, userOffset));
|
||||||
|
|
||||||
AddStep("seek to 2500", () => gameplayClockContainer.Seek(2500));
|
AddStep("seek to 2500", () => gameplayClockContainer.Seek(2500));
|
||||||
AddAssert("gameplay clock time = 2500", () => Precision.AlmostEquals(gameplayClockContainer.CurrentTime, 2500, 10f));
|
AddStep("gameplay clock time = 2500", () => Assert.AreEqual(gameplayClockContainer.CurrentTime, 2500, 10f));
|
||||||
|
|
||||||
AddStep("seek to 10000", () => gameplayClockContainer.Seek(10000));
|
AddStep("seek to 10000", () => gameplayClockContainer.Seek(10000));
|
||||||
AddAssert("gameplay clock time = 10000", () => Precision.AlmostEquals(gameplayClockContainer.CurrentTime, 10000, 10f));
|
AddStep("gameplay clock time = 10000", () => Assert.AreEqual(gameplayClockContainer.CurrentTime, 10000, 10f));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
@ -212,17 +212,17 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override BeatmapImporter CreateBeatmapImporter(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapUpdater beatmapUpdater)
|
protected override BeatmapImporter CreateBeatmapImporter(Storage storage, RealmAccess realm)
|
||||||
{
|
{
|
||||||
return new TestBeatmapImporter(this, storage, realm, beatmapUpdater);
|
return new TestBeatmapImporter(this, storage, realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class TestBeatmapImporter : BeatmapImporter
|
internal class TestBeatmapImporter : BeatmapImporter
|
||||||
{
|
{
|
||||||
private readonly TestBeatmapManager testBeatmapManager;
|
private readonly TestBeatmapManager testBeatmapManager;
|
||||||
|
|
||||||
public TestBeatmapImporter(TestBeatmapManager testBeatmapManager, Storage storage, RealmAccess databaseAccess, BeatmapUpdater beatmapUpdater)
|
public TestBeatmapImporter(TestBeatmapManager testBeatmapManager, Storage storage, RealmAccess databaseAccess)
|
||||||
: base(storage, databaseAccess, beatmapUpdater)
|
: base(storage, databaseAccess)
|
||||||
{
|
{
|
||||||
this.testBeatmapManager = testBeatmapManager;
|
this.testBeatmapManager = testBeatmapManager;
|
||||||
}
|
}
|
||||||
|
@ -83,20 +83,20 @@ namespace osu.Game.Tests.Skins.IO
|
|||||||
#region Cases where imports should match existing
|
#region Cases where imports should match existing
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public Task TestImportTwiceWithSameMetadataAndFilename() => runSkinTest(async osu =>
|
public Task TestImportTwiceWithSameMetadataAndFilename([Values] bool batchImport) => runSkinTest(async osu =>
|
||||||
{
|
{
|
||||||
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "skin.osk"));
|
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "skin.osk"), batchImport);
|
||||||
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "skin.osk"));
|
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "skin.osk"), batchImport);
|
||||||
|
|
||||||
assertImportedOnce(import1, import2);
|
assertImportedOnce(import1, import2);
|
||||||
});
|
});
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public Task TestImportTwiceWithNoMetadataSameDownloadFilename() => runSkinTest(async osu =>
|
public Task TestImportTwiceWithNoMetadataSameDownloadFilename([Values] bool batchImport) => runSkinTest(async osu =>
|
||||||
{
|
{
|
||||||
// if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety.
|
// if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety.
|
||||||
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni(string.Empty, string.Empty), "download.osk"));
|
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni(string.Empty, string.Empty), "download.osk"), batchImport);
|
||||||
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni(string.Empty, string.Empty), "download.osk"));
|
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni(string.Empty, string.Empty), "download.osk"), batchImport);
|
||||||
|
|
||||||
assertImportedOnce(import1, import2);
|
assertImportedOnce(import1, import2);
|
||||||
});
|
});
|
||||||
@ -134,10 +134,10 @@ namespace osu.Game.Tests.Skins.IO
|
|||||||
});
|
});
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public Task TestSameMetadataNameSameFolderName() => runSkinTest(async osu =>
|
public Task TestSameMetadataNameSameFolderName([Values] bool batchImport) => runSkinTest(async osu =>
|
||||||
{
|
{
|
||||||
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "my custom skin 1"));
|
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "my custom skin 1"), batchImport);
|
||||||
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "my custom skin 1"));
|
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "my custom skin 1"), batchImport);
|
||||||
|
|
||||||
assertImportedOnce(import1, import2);
|
assertImportedOnce(import1, import2);
|
||||||
assertCorrectMetadata(import1, "name 1 [my custom skin 1]", "author 1", osu);
|
assertCorrectMetadata(import1, "name 1 [my custom skin 1]", "author 1", osu);
|
||||||
@ -357,10 +357,10 @@ namespace osu.Game.Tests.Skins.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Live<SkinInfo>> loadSkinIntoOsu(OsuGameBase osu, ImportTask import)
|
private async Task<Live<SkinInfo>> loadSkinIntoOsu(OsuGameBase osu, ImportTask import, bool batchImport = false)
|
||||||
{
|
{
|
||||||
var skinManager = osu.Dependencies.Get<SkinManager>();
|
var skinManager = osu.Dependencies.Get<SkinManager>();
|
||||||
return await skinManager.Import(import);
|
return await skinManager.Import(import, batchImport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -24,7 +22,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneComposeScreen : EditorClockTestScene
|
public class TestSceneComposeScreen : EditorClockTestScene
|
||||||
{
|
{
|
||||||
private EditorBeatmap editorBeatmap;
|
private EditorBeatmap editorBeatmap = null!;
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private EditorClipboard clipboard = new EditorClipboard();
|
private EditorClipboard clipboard = new EditorClipboard();
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -39,7 +37,9 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
protected override bool IsolateSavingFromDatabase => false;
|
protected override bool IsolateSavingFromDatabase => false;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private BeatmapManager beatmapManager { get; set; }
|
private BeatmapManager beatmapManager { get; set; } = null!;
|
||||||
|
|
||||||
|
private Guid currentBeatmapSetID => EditorBeatmap.BeatmapInfo.BeatmapSet?.ID ?? Guid.Empty;
|
||||||
|
|
||||||
public override void SetUpSteps()
|
public override void SetUpSteps()
|
||||||
{
|
{
|
||||||
@ -50,19 +50,19 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("make new beatmap unique", () => EditorBeatmap.Metadata.Title = Guid.NewGuid().ToString());
|
AddStep("make new beatmap unique", () => EditorBeatmap.Metadata.Title = Guid.NewGuid().ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => new DummyWorkingBeatmap(Audio, null);
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null) => new DummyWorkingBeatmap(Audio, null);
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCreateNewBeatmap()
|
public void TestCreateNewBeatmap()
|
||||||
{
|
{
|
||||||
AddStep("save beatmap", () => Editor.Save());
|
AddStep("save beatmap", () => Editor.Save());
|
||||||
AddAssert("new beatmap in database", () => beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID)?.Value.DeletePending == false);
|
AddAssert("new beatmap in database", () => beatmapManager.QueryBeatmapSet(s => s.ID == currentBeatmapSetID)?.Value.DeletePending == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestExitWithoutSave()
|
public void TestExitWithoutSave()
|
||||||
{
|
{
|
||||||
EditorBeatmap editorBeatmap = null;
|
EditorBeatmap editorBeatmap = null!;
|
||||||
|
|
||||||
AddStep("store editor beatmap", () => editorBeatmap = EditorBeatmap);
|
AddStep("store editor beatmap", () => editorBeatmap = EditorBeatmap);
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddUntilStep("wait for exit", () => !Editor.IsCurrentScreen());
|
AddUntilStep("wait for exit", () => !Editor.IsCurrentScreen());
|
||||||
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
|
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
|
||||||
AddAssert("new beatmap not persisted", () => beatmapManager.QueryBeatmapSet(s => s.ID == editorBeatmap.BeatmapInfo.BeatmapSet.ID)?.Value.DeletePending == true);
|
AddAssert("new beatmap not persisted", () => beatmapManager.QueryBeatmapSet(s => s.ID == editorBeatmap.BeatmapInfo.BeatmapSet.AsNonNull().ID)?.Value.DeletePending == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -160,7 +160,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddAssert("new beatmap persisted", () =>
|
AddAssert("new beatmap persisted", () =>
|
||||||
{
|
{
|
||||||
var beatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == firstDifficultyName);
|
var beatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == firstDifficultyName);
|
||||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID);
|
var set = beatmapManager.QueryBeatmapSet(s => s.ID == currentBeatmapSetID);
|
||||||
|
|
||||||
return beatmap != null
|
return beatmap != null
|
||||||
&& beatmap.DifficultyName == firstDifficultyName
|
&& beatmap.DifficultyName == firstDifficultyName
|
||||||
@ -179,7 +179,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddUntilStep("wait for created", () =>
|
AddUntilStep("wait for created", () =>
|
||||||
{
|
{
|
||||||
string difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||||
return difficultyName != null && difficultyName != firstDifficultyName;
|
return difficultyName != null && difficultyName != firstDifficultyName;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddAssert("new beatmap persisted", () =>
|
AddAssert("new beatmap persisted", () =>
|
||||||
{
|
{
|
||||||
var beatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == secondDifficultyName);
|
var beatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == secondDifficultyName);
|
||||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID);
|
var set = beatmapManager.QueryBeatmapSet(s => s.ID == currentBeatmapSetID);
|
||||||
|
|
||||||
return beatmap != null
|
return beatmap != null
|
||||||
&& beatmap.DifficultyName == secondDifficultyName
|
&& beatmap.DifficultyName == secondDifficultyName
|
||||||
@ -246,7 +246,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddAssert("new beatmap persisted", () =>
|
AddAssert("new beatmap persisted", () =>
|
||||||
{
|
{
|
||||||
var beatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == originalDifficultyName);
|
var beatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == originalDifficultyName);
|
||||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID);
|
var set = beatmapManager.QueryBeatmapSet(s => s.ID == currentBeatmapSetID);
|
||||||
|
|
||||||
return beatmap != null
|
return beatmap != null
|
||||||
&& beatmap.DifficultyName == originalDifficultyName
|
&& beatmap.DifficultyName == originalDifficultyName
|
||||||
@ -262,7 +262,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddUntilStep("wait for created", () =>
|
AddUntilStep("wait for created", () =>
|
||||||
{
|
{
|
||||||
string difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||||
return difficultyName != null && difficultyName != originalDifficultyName;
|
return difficultyName != null && difficultyName != originalDifficultyName;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -281,13 +281,13 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddStep("save beatmap", () => Editor.Save());
|
AddStep("save beatmap", () => Editor.Save());
|
||||||
|
|
||||||
BeatmapInfo refetchedBeatmap = null;
|
BeatmapInfo? refetchedBeatmap = null;
|
||||||
Live<BeatmapSetInfo> refetchedBeatmapSet = null;
|
Live<BeatmapSetInfo>? refetchedBeatmapSet = null;
|
||||||
|
|
||||||
AddStep("refetch from database", () =>
|
AddStep("refetch from database", () =>
|
||||||
{
|
{
|
||||||
refetchedBeatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == copyDifficultyName);
|
refetchedBeatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == copyDifficultyName);
|
||||||
refetchedBeatmapSet = beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID);
|
refetchedBeatmapSet = beatmapManager.QueryBeatmapSet(s => s.ID == currentBeatmapSetID);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("new beatmap persisted", () =>
|
AddAssert("new beatmap persisted", () =>
|
||||||
@ -323,7 +323,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddUntilStep("wait for created", () =>
|
AddUntilStep("wait for created", () =>
|
||||||
{
|
{
|
||||||
string difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||||
return difficultyName != null && difficultyName != "New Difficulty";
|
return difficultyName != null && difficultyName != "New Difficulty";
|
||||||
});
|
});
|
||||||
AddAssert("new difficulty has correct name", () => EditorBeatmap.BeatmapInfo.DifficultyName == "New Difficulty (1)");
|
AddAssert("new difficulty has correct name", () => EditorBeatmap.BeatmapInfo.DifficultyName == "New Difficulty (1)");
|
||||||
@ -359,7 +359,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddUntilStep("wait for created", () =>
|
AddUntilStep("wait for created", () =>
|
||||||
{
|
{
|
||||||
string difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||||
return difficultyName != null && difficultyName != duplicate_difficulty_name;
|
return difficultyName != null && difficultyName != duplicate_difficulty_name;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -40,6 +40,8 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
SaveEditor();
|
SaveEditor();
|
||||||
|
|
||||||
|
AddAssert("Hash updated", () => !string.IsNullOrEmpty(EditorBeatmap.BeatmapInfo.BeatmapSet?.Hash));
|
||||||
|
|
||||||
AddAssert("Beatmap has correct metadata", () => EditorBeatmap.BeatmapInfo.Metadata.Artist == "artist" && EditorBeatmap.BeatmapInfo.Metadata.Title == "title");
|
AddAssert("Beatmap has correct metadata", () => EditorBeatmap.BeatmapInfo.Metadata.Artist == "artist" && EditorBeatmap.BeatmapInfo.Metadata.Title == "title");
|
||||||
AddAssert("Beatmap has correct author", () => EditorBeatmap.BeatmapInfo.Metadata.Author.Username == "author");
|
AddAssert("Beatmap has correct author", () => EditorBeatmap.BeatmapInfo.Metadata.Author.Username == "author");
|
||||||
AddAssert("Beatmap has correct difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName == "difficulty");
|
AddAssert("Beatmap has correct difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName == "difficulty");
|
||||||
|
@ -24,10 +24,11 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
public void TestMusicPlayAction()
|
public void TestMusicPlayAction()
|
||||||
{
|
{
|
||||||
AddStep("ensure playing something", () => Game.MusicController.EnsurePlayingSomething());
|
AddStep("ensure playing something", () => Game.MusicController.EnsurePlayingSomething());
|
||||||
|
AddUntilStep("music playing", () => Game.MusicController.IsPlaying);
|
||||||
AddStep("toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay));
|
AddStep("toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay));
|
||||||
AddAssert("music paused", () => !Game.MusicController.IsPlaying && Game.MusicController.UserPauseRequested);
|
AddUntilStep("music paused", () => !Game.MusicController.IsPlaying && Game.MusicController.UserPauseRequested);
|
||||||
AddStep("toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay));
|
AddStep("toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay));
|
||||||
AddAssert("music resumed", () => Game.MusicController.IsPlaying && !Game.MusicController.UserPauseRequested);
|
AddUntilStep("music resumed", () => Game.MusicController.IsPlaying && !Game.MusicController.UserPauseRequested);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -33,20 +31,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
public class TestSceneMultiSpectatorScreen : MultiplayerTestScene
|
public class TestSceneMultiSpectatorScreen : MultiplayerTestScene
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuGameBase game { get; set; }
|
private OsuGameBase game { get; set; } = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuConfigManager config { get; set; }
|
private OsuConfigManager config { get; set; } = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private BeatmapManager beatmapManager { get; set; }
|
private BeatmapManager beatmapManager { get; set; } = null!;
|
||||||
|
|
||||||
private MultiSpectatorScreen spectatorScreen;
|
private MultiSpectatorScreen spectatorScreen = null!;
|
||||||
|
|
||||||
private readonly List<MultiplayerRoomUser> playingUsers = new List<MultiplayerRoomUser>();
|
private readonly List<MultiplayerRoomUser> playingUsers = new List<MultiplayerRoomUser>();
|
||||||
|
|
||||||
private BeatmapSetInfo importedSet;
|
private BeatmapSetInfo importedSet = null!;
|
||||||
private BeatmapInfo importedBeatmap;
|
private BeatmapInfo importedBeatmap = null!;
|
||||||
|
|
||||||
private int importedBeatmapId;
|
private int importedBeatmapId;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -340,7 +339,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
sendFrames(getPlayerIds(count), 300);
|
sendFrames(getPlayerIds(count), 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
Player player = null;
|
Player? player = null;
|
||||||
|
|
||||||
AddStep($"get {PLAYER_1_ID} player instance", () => player = getInstance(PLAYER_1_ID).ChildrenOfType<Player>().Single());
|
AddStep($"get {PLAYER_1_ID} player instance", () => player = getInstance(PLAYER_1_ID).ChildrenOfType<Player>().Single());
|
||||||
|
|
||||||
@ -369,7 +368,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
b.Storyboard.GetLayer("Background").Add(sprite);
|
b.Storyboard.GetLayer("Background").Add(sprite);
|
||||||
});
|
});
|
||||||
|
|
||||||
private void testLeadIn(Action<WorkingBeatmap> applyToBeatmap = null)
|
private void testLeadIn(Action<WorkingBeatmap>? applyToBeatmap = null)
|
||||||
{
|
{
|
||||||
start(PLAYER_1_ID);
|
start(PLAYER_1_ID);
|
||||||
|
|
||||||
@ -387,7 +386,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
assertRunning(PLAYER_1_ID);
|
assertRunning(PLAYER_1_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadSpectateScreen(bool waitForPlayerLoad = true, Action<WorkingBeatmap> applyToBeatmap = null)
|
private void loadSpectateScreen(bool waitForPlayerLoad = true, Action<WorkingBeatmap>? applyToBeatmap = null)
|
||||||
{
|
{
|
||||||
AddStep("load screen", () =>
|
AddStep("load screen", () =>
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -51,17 +49,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
public class TestSceneMultiplayer : ScreenTestScene
|
public class TestSceneMultiplayer : ScreenTestScene
|
||||||
{
|
{
|
||||||
private BeatmapManager beatmaps;
|
private BeatmapManager beatmaps = null!;
|
||||||
private RulesetStore rulesets;
|
private RulesetStore rulesets = null!;
|
||||||
private BeatmapSetInfo importedSet;
|
private BeatmapSetInfo importedSet = null!;
|
||||||
|
|
||||||
private TestMultiplayerComponents multiplayerComponents;
|
private TestMultiplayerComponents multiplayerComponents = null!;
|
||||||
|
|
||||||
private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient;
|
private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient;
|
||||||
private TestMultiplayerRoomManager roomManager => multiplayerComponents.RoomManager;
|
private TestMultiplayerRoomManager roomManager => multiplayerComponents.RoomManager;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuConfigManager config { get; set; }
|
private OsuConfigManager config { get; set; } = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
@ -146,7 +144,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
private void removeLastUser()
|
private void removeLastUser()
|
||||||
{
|
{
|
||||||
APIUser lastUser = multiplayerClient.ServerRoom?.Users.Last().User;
|
APIUser? lastUser = multiplayerClient.ServerRoom?.Users.Last().User;
|
||||||
|
|
||||||
if (lastUser == null || lastUser == multiplayerClient.LocalUser?.User)
|
if (lastUser == null || lastUser == multiplayerClient.LocalUser?.User)
|
||||||
return;
|
return;
|
||||||
@ -156,7 +154,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
private void kickLastUser()
|
private void kickLastUser()
|
||||||
{
|
{
|
||||||
APIUser lastUser = multiplayerClient.ServerRoom?.Users.Last().User;
|
APIUser? lastUser = multiplayerClient.ServerRoom?.Users.Last().User;
|
||||||
|
|
||||||
if (lastUser == null || lastUser == multiplayerClient.LocalUser?.User)
|
if (lastUser == null || lastUser == multiplayerClient.LocalUser?.User)
|
||||||
return;
|
return;
|
||||||
@ -351,7 +349,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||||
AddStep("join room", () => InputManager.Key(Key.Enter));
|
AddStep("join room", () => InputManager.Key(Key.Enter));
|
||||||
|
|
||||||
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
|
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
|
||||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||||
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password");
|
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password");
|
||||||
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
|
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
|
||||||
@ -678,7 +676,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestGameplayExitFlow()
|
public void TestGameplayExitFlow()
|
||||||
{
|
{
|
||||||
Bindable<double> holdDelay = null;
|
Bindable<double>? holdDelay = null;
|
||||||
|
|
||||||
AddStep("Set hold delay to zero", () =>
|
AddStep("Set hold delay to zero", () =>
|
||||||
{
|
{
|
||||||
@ -709,7 +707,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddUntilStep("wait for lounge", () => multiplayerComponents.CurrentScreen is Screens.OnlinePlay.Multiplayer.Multiplayer);
|
AddUntilStep("wait for lounge", () => multiplayerComponents.CurrentScreen is Screens.OnlinePlay.Multiplayer.Multiplayer);
|
||||||
|
|
||||||
AddStep("stop holding", () => InputManager.ReleaseKey(Key.Escape));
|
AddStep("stop holding", () => InputManager.ReleaseKey(Key.Escape));
|
||||||
AddStep("set hold delay to default", () => holdDelay.SetDefault());
|
AddStep("set hold delay to default", () => holdDelay?.SetDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -992,7 +990,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddUntilStep("wait for ready button to be enabled", () => readyButton.Enabled.Value);
|
AddUntilStep("wait for ready button to be enabled", () => readyButton.Enabled.Value);
|
||||||
|
|
||||||
MultiplayerUserState lastState = MultiplayerUserState.Idle;
|
MultiplayerUserState lastState = MultiplayerUserState.Idle;
|
||||||
MultiplayerRoomUser user = null;
|
MultiplayerRoomUser? user = null;
|
||||||
|
|
||||||
AddStep("click ready button", () =>
|
AddStep("click ready button", () =>
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -66,7 +64,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestRemoveUser()
|
public void TestRemoveUser()
|
||||||
{
|
{
|
||||||
APIUser secondUser = null;
|
APIUser? secondUser = null;
|
||||||
|
|
||||||
AddStep("add a user", () =>
|
AddStep("add a user", () =>
|
||||||
{
|
{
|
||||||
@ -80,7 +78,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
AddStep("remove host", () => MultiplayerClient.RemoveUser(API.LocalUser.Value));
|
AddStep("remove host", () => MultiplayerClient.RemoveUser(API.LocalUser.Value));
|
||||||
|
|
||||||
AddAssert("single panel is for second user", () => this.ChildrenOfType<ParticipantPanel>().Single().User.UserID == secondUser.Id);
|
AddAssert("single panel is for second user", () => this.ChildrenOfType<ParticipantPanel>().Single().User.UserID == secondUser?.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -368,7 +366,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
private void createNewParticipantsList()
|
private void createNewParticipantsList()
|
||||||
{
|
{
|
||||||
ParticipantsList participantsList = null;
|
ParticipantsList? participantsList = null;
|
||||||
|
|
||||||
AddStep("create new list", () => Child = participantsList = new ParticipantsList
|
AddStep("create new list", () => Child = participantsList = new ParticipantsList
|
||||||
{
|
{
|
||||||
@ -378,7 +376,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
Size = new Vector2(380, 0.7f)
|
Size = new Vector2(380, 0.7f)
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for list to load", () => participantsList.IsLoaded);
|
AddUntilStep("wait for list to load", () => participantsList?.IsLoaded == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkProgressBarVisibility(bool visible) =>
|
private void checkProgressBarVisibility(bool visible) =>
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Game.Overlays.BeatmapSet;
|
using osu.Game.Overlays.BeatmapSet;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -29,7 +27,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
LeaderboardModSelector modSelector;
|
LeaderboardModSelector modSelector;
|
||||||
FillFlowContainer<SpriteText> selectedMods;
|
FillFlowContainer<SpriteText> selectedMods;
|
||||||
|
|
||||||
var ruleset = new Bindable<IRulesetInfo>();
|
var ruleset = new Bindable<IRulesetInfo?>();
|
||||||
|
|
||||||
Add(selectedMods = new FillFlowContainer<SpriteText>
|
Add(selectedMods = new FillFlowContainer<SpriteText>
|
||||||
{
|
{
|
||||||
|
@ -31,12 +31,11 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
protected override string[] HashableFileTypes => new[] { ".osu" };
|
protected override string[] HashableFileTypes => new[] { ".osu" };
|
||||||
|
|
||||||
private readonly BeatmapUpdater? beatmapUpdater;
|
public Action<BeatmapSetInfo>? ProcessBeatmap { private get; set; }
|
||||||
|
|
||||||
public BeatmapImporter(Storage storage, RealmAccess realm, BeatmapUpdater? beatmapUpdater = null)
|
public BeatmapImporter(Storage storage, RealmAccess realm)
|
||||||
: base(storage, realm)
|
: base(storage, realm)
|
||||||
{
|
{
|
||||||
this.beatmapUpdater = beatmapUpdater;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path).ToLowerInvariant() == ".osz";
|
protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path).ToLowerInvariant() == ".osz";
|
||||||
@ -100,7 +99,7 @@ namespace osu.Game.Beatmaps
|
|||||||
{
|
{
|
||||||
base.PostImport(model, realm);
|
base.PostImport(model, realm);
|
||||||
|
|
||||||
beatmapUpdater?.Process(model);
|
ProcessBeatmap?.Invoke(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateOnlineIds(BeatmapSetInfo beatmapSet, Realm realm)
|
private void validateOnlineIds(BeatmapSetInfo beatmapSet, Realm realm)
|
||||||
|
@ -34,14 +34,15 @@ namespace osu.Game.Beatmaps
|
|||||||
/// Handles general operations related to global beatmap management.
|
/// Handles general operations related to global beatmap management.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ExcludeFromDynamicCompile]
|
[ExcludeFromDynamicCompile]
|
||||||
public class BeatmapManager : ModelManager<BeatmapSetInfo>, IModelImporter<BeatmapSetInfo>, IWorkingBeatmapCache, IDisposable
|
public class BeatmapManager : ModelManager<BeatmapSetInfo>, IModelImporter<BeatmapSetInfo>, IWorkingBeatmapCache
|
||||||
{
|
{
|
||||||
public ITrackStore BeatmapTrackStore { get; }
|
public ITrackStore BeatmapTrackStore { get; }
|
||||||
|
|
||||||
private readonly BeatmapImporter beatmapImporter;
|
private readonly BeatmapImporter beatmapImporter;
|
||||||
|
|
||||||
private readonly WorkingBeatmapCache workingBeatmapCache;
|
private readonly WorkingBeatmapCache workingBeatmapCache;
|
||||||
private readonly BeatmapUpdater? beatmapUpdater;
|
|
||||||
|
public Action<BeatmapSetInfo>? ProcessBeatmap { private get; set; }
|
||||||
|
|
||||||
public BeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, IAPIProvider? api, AudioManager audioManager, IResourceStore<byte[]> gameResources, GameHost? host = null,
|
public BeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, IAPIProvider? api, AudioManager audioManager, IResourceStore<byte[]> gameResources, GameHost? host = null,
|
||||||
WorkingBeatmap? defaultBeatmap = null, BeatmapDifficultyCache? difficultyCache = null, bool performOnlineLookups = false)
|
WorkingBeatmap? defaultBeatmap = null, BeatmapDifficultyCache? difficultyCache = null, bool performOnlineLookups = false)
|
||||||
@ -54,15 +55,14 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
if (difficultyCache == null)
|
if (difficultyCache == null)
|
||||||
throw new ArgumentNullException(nameof(difficultyCache), "Difficulty cache must be provided if online lookups are required.");
|
throw new ArgumentNullException(nameof(difficultyCache), "Difficulty cache must be provided if online lookups are required.");
|
||||||
|
|
||||||
beatmapUpdater = new BeatmapUpdater(this, difficultyCache, api, storage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var userResources = new RealmFileStore(realm, storage).Store;
|
var userResources = new RealmFileStore(realm, storage).Store;
|
||||||
|
|
||||||
BeatmapTrackStore = audioManager.GetTrackStore(userResources);
|
BeatmapTrackStore = audioManager.GetTrackStore(userResources);
|
||||||
|
|
||||||
beatmapImporter = CreateBeatmapImporter(storage, realm, rulesets, beatmapUpdater);
|
beatmapImporter = CreateBeatmapImporter(storage, realm);
|
||||||
|
beatmapImporter.ProcessBeatmap = obj => ProcessBeatmap?.Invoke(obj);
|
||||||
beatmapImporter.PostNotification = obj => PostNotification?.Invoke(obj);
|
beatmapImporter.PostNotification = obj => PostNotification?.Invoke(obj);
|
||||||
|
|
||||||
workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host);
|
workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host);
|
||||||
@ -74,8 +74,7 @@ namespace osu.Game.Beatmaps
|
|||||||
return new WorkingBeatmapCache(BeatmapTrackStore, audioManager, resources, storage, defaultBeatmap, host);
|
return new WorkingBeatmapCache(BeatmapTrackStore, audioManager, resources, storage, defaultBeatmap, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual BeatmapImporter CreateBeatmapImporter(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapUpdater? beatmapUpdater) =>
|
protected virtual BeatmapImporter CreateBeatmapImporter(Storage storage, RealmAccess realm) => new BeatmapImporter(storage, realm);
|
||||||
new BeatmapImporter(storage, realm, beatmapUpdater);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new beatmap set, backed by a <see cref="BeatmapSetInfo"/> model,
|
/// Create a new beatmap set, backed by a <see cref="BeatmapSetInfo"/> model,
|
||||||
@ -317,13 +316,15 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
AddFile(setInfo, stream, createBeatmapFilenameFromMetadata(beatmapInfo));
|
AddFile(setInfo, stream, createBeatmapFilenameFromMetadata(beatmapInfo));
|
||||||
|
|
||||||
|
setInfo.Hash = beatmapImporter.ComputeHash(setInfo);
|
||||||
|
|
||||||
Realm.Write(r =>
|
Realm.Write(r =>
|
||||||
{
|
{
|
||||||
var liveBeatmapSet = r.Find<BeatmapSetInfo>(setInfo.ID);
|
var liveBeatmapSet = r.Find<BeatmapSetInfo>(setInfo.ID);
|
||||||
|
|
||||||
setInfo.CopyChangesToRealm(liveBeatmapSet);
|
setInfo.CopyChangesToRealm(liveBeatmapSet);
|
||||||
|
|
||||||
beatmapUpdater?.Process(liveBeatmapSet, r);
|
ProcessBeatmap?.Invoke(liveBeatmapSet);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,15 +469,6 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Implementation of IDisposable
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
beatmapUpdater?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Implementation of IPostImports<out BeatmapSetInfo>
|
#region Implementation of IPostImports<out BeatmapSetInfo>
|
||||||
|
|
||||||
public Action<IEnumerable<Live<BeatmapSetInfo>>>? PresentImport
|
public Action<IEnumerable<Live<BeatmapSetInfo>>>? PresentImport
|
||||||
|
@ -10,7 +10,6 @@ using osu.Framework.Platform;
|
|||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using Realms;
|
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
@ -31,6 +30,14 @@ namespace osu.Game.Beatmaps
|
|||||||
onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
|
onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queue a beatmap for background processing.
|
||||||
|
/// </summary>
|
||||||
|
public void Queue(int beatmapSetId)
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queue a beatmap for background processing.
|
/// Queue a beatmap for background processing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -44,9 +51,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Run all processing on a beatmap immediately.
|
/// Run all processing on a beatmap immediately.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Process(BeatmapSetInfo beatmapSet) => beatmapSet.Realm.Write(r => Process(beatmapSet, r));
|
public void Process(BeatmapSetInfo beatmapSet) => beatmapSet.Realm.Write(r =>
|
||||||
|
|
||||||
public void Process(BeatmapSetInfo beatmapSet, Realm realm)
|
|
||||||
{
|
{
|
||||||
// Before we use below, we want to invalidate.
|
// Before we use below, we want to invalidate.
|
||||||
workingBeatmapCache.Invalidate(beatmapSet);
|
workingBeatmapCache.Invalidate(beatmapSet);
|
||||||
@ -71,7 +76,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
// And invalidate again afterwards as re-fetching the most up-to-date database metadata will be required.
|
// And invalidate again afterwards as re-fetching the most up-to-date database metadata will be required.
|
||||||
workingBeatmapCache.Invalidate(beatmapSet);
|
workingBeatmapCache.Invalidate(beatmapSet);
|
||||||
}
|
});
|
||||||
|
|
||||||
private double calculateLength(IBeatmap b)
|
private double calculateLength(IBeatmap b)
|
||||||
{
|
{
|
||||||
|
@ -137,8 +137,17 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path))))
|
string fileStorePath = BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path);
|
||||||
return Decoder.GetDecoder<Beatmap>(stream).Decode(stream);
|
var stream = GetStream(fileStorePath);
|
||||||
|
|
||||||
|
if (stream == null)
|
||||||
|
{
|
||||||
|
Logger.Log($"Beatmap failed to load (file {BeatmapInfo.Path} not found on disk at expected location {fileStorePath}).", level: LogLevel.Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var reader = new LineBufferedReader(stream))
|
||||||
|
return Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -154,7 +163,16 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return resources.LargeTextureStore.Get(BeatmapSetInfo.GetPathForFile(Metadata.BackgroundFile));
|
string fileStorePath = BeatmapSetInfo.GetPathForFile(Metadata.BackgroundFile);
|
||||||
|
var texture = resources.LargeTextureStore.Get(fileStorePath);
|
||||||
|
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
Logger.Log($"Beatmap background failed to load (file {Metadata.BackgroundFile} not found on disk at expected location {fileStorePath}).", level: LogLevel.Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return texture;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -173,7 +191,16 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return resources.Tracks.Get(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile));
|
string fileStorePath = BeatmapSetInfo.GetPathForFile(Metadata.AudioFile);
|
||||||
|
var track = resources.Tracks.Get(fileStorePath);
|
||||||
|
|
||||||
|
if (track == null)
|
||||||
|
{
|
||||||
|
Logger.Log($"Beatmap failed to load (file {Metadata.AudioFile} not found on disk at expected location {fileStorePath}).", level: LogLevel.Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return track;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -192,8 +219,17 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var trackData = GetStream(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile));
|
string fileStorePath = BeatmapSetInfo.GetPathForFile(Metadata.AudioFile);
|
||||||
return trackData == null ? null : new Waveform(trackData);
|
|
||||||
|
var trackData = GetStream(fileStorePath);
|
||||||
|
|
||||||
|
if (trackData == null)
|
||||||
|
{
|
||||||
|
Logger.Log($"Beatmap waveform failed to load (file {Metadata.AudioFile} not found on disk at expected location {fileStorePath}).", level: LogLevel.Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Waveform(trackData);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -211,20 +247,38 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path))))
|
string fileStorePath = BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path);
|
||||||
|
var beatmapFileStream = GetStream(fileStorePath);
|
||||||
|
|
||||||
|
if (beatmapFileStream == null)
|
||||||
{
|
{
|
||||||
var decoder = Decoder.GetDecoder<Storyboard>(stream);
|
Logger.Log($"Beatmap failed to load (file {BeatmapInfo.Path} not found on disk at expected location {fileStorePath})", level: LogLevel.Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
string storyboardFilename = BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename;
|
using (var reader = new LineBufferedReader(beatmapFileStream))
|
||||||
|
{
|
||||||
|
var decoder = Decoder.GetDecoder<Storyboard>(reader);
|
||||||
|
|
||||||
// todo: support loading from both set-wide storyboard *and* beatmap specific.
|
Stream storyboardFileStream = null;
|
||||||
if (string.IsNullOrEmpty(storyboardFilename))
|
|
||||||
storyboard = decoder.Decode(stream);
|
if (BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename is string storyboardFilename)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(storyboardFilename))))
|
string storyboardFileStorePath = BeatmapSetInfo?.GetPathForFile(storyboardFilename);
|
||||||
storyboard = decoder.Decode(stream, secondaryStream);
|
storyboardFileStream = GetStream(storyboardFileStorePath);
|
||||||
|
|
||||||
|
if (storyboardFileStream == null)
|
||||||
|
Logger.Log($"Storyboard failed to load (file {storyboardFilename} not found on disk at expected location {storyboardFileStorePath})", level: LogLevel.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (storyboardFileStream != null)
|
||||||
|
{
|
||||||
|
// Stand-alone storyboard was found, so parse in addition to the beatmap's local storyboard.
|
||||||
|
using (var secondaryReader = new LineBufferedReader(storyboardFileStream))
|
||||||
|
storyboard = decoder.Decode(reader, secondaryReader);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
storyboard = decoder.Decode(reader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -167,6 +167,8 @@ namespace osu.Game.Configuration
|
|||||||
SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full);
|
SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full);
|
||||||
|
|
||||||
SetDefault(OsuSetting.EditorWaveformOpacity, 0.25f);
|
SetDefault(OsuSetting.EditorWaveformOpacity, 0.25f);
|
||||||
|
|
||||||
|
SetDefault(OsuSetting.LastProcessedMetadataId, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDictionary<OsuSetting, string> GetLoggableState() =>
|
public IDictionary<OsuSetting, string> GetLoggableState() =>
|
||||||
@ -363,5 +365,6 @@ namespace osu.Game.Configuration
|
|||||||
DiscordRichPresence,
|
DiscordRichPresence,
|
||||||
AutomaticallyDownloadWhenSpectating,
|
AutomaticallyDownloadWhenSpectating,
|
||||||
ShowOnlineExplicitContent,
|
ShowOnlineExplicitContent,
|
||||||
|
LastProcessedMetadataId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,15 +258,13 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
bool checkedExisting = false;
|
TModel? existing;
|
||||||
TModel? existing = null;
|
|
||||||
|
|
||||||
if (batchImport && archive != null)
|
if (batchImport && archive != null)
|
||||||
{
|
{
|
||||||
// this is a fast bail condition to improve large import performance.
|
// this is a fast bail condition to improve large import performance.
|
||||||
item.Hash = computeHashFast(archive);
|
item.Hash = computeHashFast(archive);
|
||||||
|
|
||||||
checkedExisting = true;
|
|
||||||
existing = CheckForExisting(item, realm);
|
existing = CheckForExisting(item, realm);
|
||||||
|
|
||||||
if (existing != null)
|
if (existing != null)
|
||||||
@ -311,8 +309,12 @@ namespace osu.Game.Database
|
|||||||
// TODO: we may want to run this outside of the transaction.
|
// TODO: we may want to run this outside of the transaction.
|
||||||
Populate(item, archive, realm, cancellationToken);
|
Populate(item, archive, realm, cancellationToken);
|
||||||
|
|
||||||
if (!checkedExisting)
|
// Populate() may have adjusted file content (see SkinImporter.updateSkinIniMetadata), so regardless of whether a fast check was done earlier, let's
|
||||||
existing = CheckForExisting(item, realm);
|
// check for existing items a second time.
|
||||||
|
//
|
||||||
|
// If this is ever a performance issue, the fast-check hash can be compared and trigger a skip of this second check if it still matches.
|
||||||
|
// I don't think it is a huge deal doing a second indexed check, though.
|
||||||
|
existing = CheckForExisting(item, realm);
|
||||||
|
|
||||||
if (existing != null)
|
if (existing != null)
|
||||||
{
|
{
|
||||||
@ -386,7 +388,7 @@ namespace osu.Game.Database
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// In the case of no matching files, a hash will be generated from the passed archive's <see cref="ArchiveReader.Name"/>.
|
/// In the case of no matching files, a hash will be generated from the passed archive's <see cref="ArchiveReader.Name"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected string ComputeHash(TModel item)
|
public string ComputeHash(TModel item)
|
||||||
{
|
{
|
||||||
// for now, concatenate all hashable files in the set to create a unique hash.
|
// for now, concatenate all hashable files in the set to create a unique hash.
|
||||||
MemoryStream hashable = new MemoryStream();
|
MemoryStream hashable = new MemoryStream();
|
||||||
|
@ -8,19 +8,60 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
public static class RealmExtensions
|
public static class RealmExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a write operation against the provided realm instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will automatically start a transaction if not already in one.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="realm">The realm to operate on.</param>
|
||||||
|
/// <param name="function">The write operation to run.</param>
|
||||||
public static void Write(this Realm realm, Action<Realm> function)
|
public static void Write(this Realm realm, Action<Realm> function)
|
||||||
{
|
{
|
||||||
using var transaction = realm.BeginWrite();
|
Transaction? transaction = null;
|
||||||
function(realm);
|
|
||||||
transaction.Commit();
|
try
|
||||||
|
{
|
||||||
|
if (!realm.IsInTransaction)
|
||||||
|
transaction = realm.BeginWrite();
|
||||||
|
|
||||||
|
function(realm);
|
||||||
|
|
||||||
|
transaction?.Commit();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
transaction?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a write operation against the provided realm instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will automatically start a transaction if not already in one.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="realm">The realm to operate on.</param>
|
||||||
|
/// <param name="function">The write operation to run.</param>
|
||||||
public static T Write<T>(this Realm realm, Func<Realm, T> function)
|
public static T Write<T>(this Realm realm, Func<Realm, T> function)
|
||||||
{
|
{
|
||||||
using var transaction = realm.BeginWrite();
|
Transaction? transaction = null;
|
||||||
var result = function(realm);
|
|
||||||
transaction.Commit();
|
try
|
||||||
return result;
|
{
|
||||||
|
if (!realm.IsInTransaction)
|
||||||
|
transaction = realm.BeginWrite();
|
||||||
|
|
||||||
|
var result = function(realm);
|
||||||
|
|
||||||
|
transaction?.Commit();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
transaction?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -38,15 +38,21 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
private void load(OsuConfigManager config, SessionStatics sessionStatics)
|
private void load(OsuConfigManager config, SessionStatics sessionStatics)
|
||||||
{
|
{
|
||||||
seasonalBackgroundMode = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode);
|
seasonalBackgroundMode = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode);
|
||||||
seasonalBackgroundMode.BindValueChanged(_ => SeasonalBackgroundChanged?.Invoke());
|
seasonalBackgroundMode.BindValueChanged(_ => triggerSeasonalBackgroundChanged());
|
||||||
|
|
||||||
seasonalBackgrounds = sessionStatics.GetBindable<APISeasonalBackgrounds>(Static.SeasonalBackgrounds);
|
seasonalBackgrounds = sessionStatics.GetBindable<APISeasonalBackgrounds>(Static.SeasonalBackgrounds);
|
||||||
seasonalBackgrounds.BindValueChanged(_ => SeasonalBackgroundChanged?.Invoke());
|
seasonalBackgrounds.BindValueChanged(_ => triggerSeasonalBackgroundChanged());
|
||||||
|
|
||||||
apiState.BindTo(api.State);
|
apiState.BindTo(api.State);
|
||||||
apiState.BindValueChanged(fetchSeasonalBackgrounds, true);
|
apiState.BindValueChanged(fetchSeasonalBackgrounds, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void triggerSeasonalBackgroundChanged()
|
||||||
|
{
|
||||||
|
if (shouldShowSeasonal)
|
||||||
|
SeasonalBackgroundChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
private void fetchSeasonalBackgrounds(ValueChangedEvent<APIState> stateChanged)
|
private void fetchSeasonalBackgrounds(ValueChangedEvent<APIState> stateChanged)
|
||||||
{
|
{
|
||||||
if (seasonalBackgrounds.Value != null || stateChanged.NewValue != APIState.Online)
|
if (seasonalBackgrounds.Value != null || stateChanged.NewValue != APIState.Online)
|
||||||
@ -64,15 +70,10 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
|
|
||||||
public SeasonalBackground LoadNextBackground()
|
public SeasonalBackground LoadNextBackground()
|
||||||
{
|
{
|
||||||
if (seasonalBackgroundMode.Value == SeasonalBackgroundMode.Never
|
if (!shouldShowSeasonal)
|
||||||
|| (seasonalBackgroundMode.Value == SeasonalBackgroundMode.Sometimes && !isInSeason))
|
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
var backgrounds = seasonalBackgrounds.Value?.Backgrounds;
|
var backgrounds = seasonalBackgrounds.Value.Backgrounds;
|
||||||
if (backgrounds == null || !backgrounds.Any())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
current = (current + 1) % backgrounds.Count;
|
current = (current + 1) % backgrounds.Count;
|
||||||
string url = backgrounds[current].Url;
|
string url = backgrounds[current].Url;
|
||||||
@ -80,6 +81,20 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
return new SeasonalBackground(url);
|
return new SeasonalBackground(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool shouldShowSeasonal
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (seasonalBackgroundMode.Value == SeasonalBackgroundMode.Never)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (seasonalBackgroundMode.Value == SeasonalBackgroundMode.Sometimes && !isInSeason)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return seasonalBackgrounds.Value?.Backgrounds?.Any() == true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool isInSeason => seasonalBackgrounds.Value != null && DateTimeOffset.Now < seasonalBackgrounds.Value.EndDate;
|
private bool isInSeason => seasonalBackgrounds.Value != null && DateTimeOffset.Now < seasonalBackgrounds.Value.EndDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ namespace osu.Game.Online
|
|||||||
APIClientID = "5";
|
APIClientID = "5";
|
||||||
SpectatorEndpointUrl = $"{APIEndpointUrl}/spectator";
|
SpectatorEndpointUrl = $"{APIEndpointUrl}/spectator";
|
||||||
MultiplayerEndpointUrl = $"{APIEndpointUrl}/multiplayer";
|
MultiplayerEndpointUrl = $"{APIEndpointUrl}/multiplayer";
|
||||||
|
MetadataEndpointUrl = $"{APIEndpointUrl}/metadata";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,5 +39,10 @@ namespace osu.Game.Online
|
|||||||
/// The endpoint for the SignalR multiplayer server.
|
/// The endpoint for the SignalR multiplayer server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string MultiplayerEndpointUrl { get; set; }
|
public string MultiplayerEndpointUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The endpoint for the SignalR metadata server.
|
||||||
|
/// </summary>
|
||||||
|
public string MetadataEndpointUrl { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ namespace osu.Game.Online
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task handleErrorAndDelay(Exception exception, CancellationToken cancellationToken)
|
private async Task handleErrorAndDelay(Exception exception, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Logger.Log($"{clientName} connection error: {exception}", LoggingTarget.Network);
|
Logger.Log($"{clientName} connect attempt failed: {exception.Message}", LoggingTarget.Network);
|
||||||
await Task.Delay(5000, cancellationToken).ConfigureAwait(false);
|
await Task.Delay(5000, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
osu.Game/Online/Metadata/BeatmapUpdates.cs
Normal file
28
osu.Game/Online/Metadata/BeatmapUpdates.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// 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 MessagePack;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.Metadata
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Describes a set of beatmaps which have been updated in some way.
|
||||||
|
/// </summary>
|
||||||
|
[MessagePackObject]
|
||||||
|
[Serializable]
|
||||||
|
public class BeatmapUpdates
|
||||||
|
{
|
||||||
|
[Key(0)]
|
||||||
|
public int[] BeatmapSetIDs { get; set; }
|
||||||
|
|
||||||
|
[Key(1)]
|
||||||
|
public int LastProcessedQueueID { get; set; }
|
||||||
|
|
||||||
|
public BeatmapUpdates(int[] beatmapSetIDs, int lastProcessedQueueID)
|
||||||
|
{
|
||||||
|
BeatmapSetIDs = beatmapSetIDs;
|
||||||
|
LastProcessedQueueID = lastProcessedQueueID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
osu.Game/Online/Metadata/IMetadataClient.cs
Normal file
12
osu.Game/Online/Metadata/IMetadataClient.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// 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.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.Metadata
|
||||||
|
{
|
||||||
|
public interface IMetadataClient
|
||||||
|
{
|
||||||
|
Task BeatmapSetsUpdated(BeatmapUpdates updates);
|
||||||
|
}
|
||||||
|
}
|
21
osu.Game/Online/Metadata/IMetadataServer.cs
Normal file
21
osu.Game/Online/Metadata/IMetadataServer.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// 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.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.Metadata
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Metadata server is responsible for keeping the osu! client up-to-date with any changes.
|
||||||
|
/// </summary>
|
||||||
|
public interface IMetadataServer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get any changes since a specific point in the queue.
|
||||||
|
/// Should be used to allow the client to catch up with any changes after being closed or disconnected.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queueId">The last processed queue ID.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<BeatmapUpdates> GetChangesSince(int queueId);
|
||||||
|
}
|
||||||
|
}
|
15
osu.Game/Online/Metadata/MetadataClient.cs
Normal file
15
osu.Game/Online/Metadata/MetadataClient.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// 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.Threading.Tasks;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.Metadata
|
||||||
|
{
|
||||||
|
public abstract class MetadataClient : Component, IMetadataClient, IMetadataServer
|
||||||
|
{
|
||||||
|
public abstract Task BeatmapSetsUpdated(BeatmapUpdates updates);
|
||||||
|
|
||||||
|
public abstract Task<BeatmapUpdates> GetChangesSince(int queueId);
|
||||||
|
}
|
||||||
|
}
|
134
osu.Game/Online/Metadata/OnlineMetadataClient.cs
Normal file
134
osu.Game/Online/Metadata/OnlineMetadataClient.cs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// 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.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.Metadata
|
||||||
|
{
|
||||||
|
public class OnlineMetadataClient : MetadataClient
|
||||||
|
{
|
||||||
|
private readonly BeatmapUpdater beatmapUpdater;
|
||||||
|
private readonly string endpoint;
|
||||||
|
|
||||||
|
private IHubClientConnector? connector;
|
||||||
|
|
||||||
|
private Bindable<int> lastQueueId = null!;
|
||||||
|
|
||||||
|
private HubConnection? connection => connector?.CurrentConnection;
|
||||||
|
|
||||||
|
public OnlineMetadataClient(EndpointConfiguration endpoints, BeatmapUpdater beatmapUpdater)
|
||||||
|
{
|
||||||
|
this.beatmapUpdater = beatmapUpdater;
|
||||||
|
endpoint = endpoints.MetadataEndpointUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IAPIProvider api, OsuConfigManager config)
|
||||||
|
{
|
||||||
|
// Importantly, we are intentionally not using MessagePack here to correctly support derived class serialization.
|
||||||
|
// More information on the limitations / reasoning can be found in osu-server-spectator's initialisation code.
|
||||||
|
connector = api.GetHubConnector(nameof(OnlineMetadataClient), endpoint);
|
||||||
|
|
||||||
|
if (connector != null)
|
||||||
|
{
|
||||||
|
connector.ConfigureConnection = connection =>
|
||||||
|
{
|
||||||
|
// this is kind of SILLY
|
||||||
|
// https://github.com/dotnet/aspnetcore/issues/15198
|
||||||
|
connection.On<BeatmapUpdates>(nameof(IMetadataClient.BeatmapSetsUpdated), ((IMetadataClient)this).BeatmapSetsUpdated);
|
||||||
|
};
|
||||||
|
|
||||||
|
connector.IsConnected.BindValueChanged(isConnectedChanged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastQueueId = config.GetBindable<int>(OsuSetting.LastProcessedMetadataId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool catchingUp;
|
||||||
|
|
||||||
|
private void isConnectedChanged(ValueChangedEvent<bool> connected)
|
||||||
|
{
|
||||||
|
if (!connected.NewValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (lastQueueId.Value >= 0)
|
||||||
|
{
|
||||||
|
catchingUp = true;
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Logger.Log($"Requesting catch-up from {lastQueueId.Value}");
|
||||||
|
var catchUpChanges = await GetChangesSince(lastQueueId.Value);
|
||||||
|
|
||||||
|
lastQueueId.Value = catchUpChanges.LastProcessedQueueID;
|
||||||
|
|
||||||
|
if (catchUpChanges.BeatmapSetIDs.Length == 0)
|
||||||
|
{
|
||||||
|
Logger.Log($"Catch-up complete at {lastQueueId.Value}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ProcessChanges(catchUpChanges.BeatmapSetIDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
catchingUp = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task BeatmapSetsUpdated(BeatmapUpdates updates)
|
||||||
|
{
|
||||||
|
Logger.Log($"Received beatmap updates {updates.BeatmapSetIDs.Length} updates with last id {updates.LastProcessedQueueID}");
|
||||||
|
|
||||||
|
// If we're still catching up, avoid updating the last ID as it will interfere with catch-up efforts.
|
||||||
|
if (!catchingUp)
|
||||||
|
lastQueueId.Value = updates.LastProcessedQueueID;
|
||||||
|
|
||||||
|
await ProcessChanges(updates.BeatmapSetIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Task ProcessChanges(int[] beatmapSetIDs)
|
||||||
|
{
|
||||||
|
foreach (int id in beatmapSetIDs)
|
||||||
|
{
|
||||||
|
Logger.Log($"Processing {id}...");
|
||||||
|
beatmapUpdater.Queue(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<BeatmapUpdates> GetChangesSince(int queueId)
|
||||||
|
{
|
||||||
|
if (connector?.IsConnected.Value != true)
|
||||||
|
return Task.FromCanceled<BeatmapUpdates>(default);
|
||||||
|
|
||||||
|
Logger.Log($"Requesting any changes since last known queue id {queueId}");
|
||||||
|
|
||||||
|
Debug.Assert(connection != null);
|
||||||
|
|
||||||
|
return connection.InvokeAsync<BeatmapUpdates>(nameof(IMetadataServer.GetChangesSince), queueId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
connector?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ namespace osu.Game.Online
|
|||||||
APIClientID = "5";
|
APIClientID = "5";
|
||||||
SpectatorEndpointUrl = "https://spectator.ppy.sh/spectator";
|
SpectatorEndpointUrl = "https://spectator.ppy.sh/spectator";
|
||||||
MultiplayerEndpointUrl = "https://spectator.ppy.sh/multiplayer";
|
MultiplayerEndpointUrl = "https://spectator.ppy.sh/multiplayer";
|
||||||
|
MetadataEndpointUrl = "https://spectator.ppy.sh/metadata";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -41,6 +40,7 @@ using osu.Game.IO;
|
|||||||
using osu.Game.Online;
|
using osu.Game.Online;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.Chat;
|
using osu.Game.Online.Chat;
|
||||||
|
using osu.Game.Online.Metadata;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.Spectator;
|
using osu.Game.Online.Spectator;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
@ -52,6 +52,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
|
using File = System.IO.File;
|
||||||
using RuntimeInfo = osu.Framework.RuntimeInfo;
|
using RuntimeInfo = osu.Framework.RuntimeInfo;
|
||||||
|
|
||||||
namespace osu.Game
|
namespace osu.Game
|
||||||
@ -170,6 +171,7 @@ namespace osu.Game
|
|||||||
public readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> AvailableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>(new Dictionary<ModType, IReadOnlyList<Mod>>());
|
public readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> AvailableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>(new Dictionary<ModType, IReadOnlyList<Mod>>());
|
||||||
|
|
||||||
private BeatmapDifficultyCache difficultyCache;
|
private BeatmapDifficultyCache difficultyCache;
|
||||||
|
private BeatmapUpdater beatmapUpdater;
|
||||||
|
|
||||||
private UserLookupCache userCache;
|
private UserLookupCache userCache;
|
||||||
private BeatmapLookupCache beatmapCache;
|
private BeatmapLookupCache beatmapCache;
|
||||||
@ -180,6 +182,8 @@ namespace osu.Game
|
|||||||
|
|
||||||
private MultiplayerClient multiplayerClient;
|
private MultiplayerClient multiplayerClient;
|
||||||
|
|
||||||
|
private MetadataClient metadataClient;
|
||||||
|
|
||||||
private RealmAccess realm;
|
private RealmAccess realm;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
@ -263,15 +267,13 @@ namespace osu.Game
|
|||||||
|
|
||||||
dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash));
|
dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash));
|
||||||
|
|
||||||
dependencies.CacheAs(spectatorClient = new OnlineSpectatorClient(endpoints));
|
|
||||||
dependencies.CacheAs(multiplayerClient = new OnlineMultiplayerClient(endpoints));
|
|
||||||
|
|
||||||
var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures);
|
var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures);
|
||||||
|
|
||||||
dependencies.Cache(difficultyCache = new BeatmapDifficultyCache());
|
dependencies.Cache(difficultyCache = new BeatmapDifficultyCache());
|
||||||
|
|
||||||
// ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup()
|
// ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup()
|
||||||
dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, realm, Scheduler, difficultyCache, LocalConfig));
|
dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, realm, Scheduler, difficultyCache, LocalConfig));
|
||||||
|
|
||||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, realm, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, difficultyCache, performOnlineLookups: true));
|
dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, realm, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, difficultyCache, performOnlineLookups: true));
|
||||||
|
|
||||||
dependencies.Cache(BeatmapDownloader = new BeatmapModelDownloader(BeatmapManager, API));
|
dependencies.Cache(BeatmapDownloader = new BeatmapModelDownloader(BeatmapManager, API));
|
||||||
@ -280,6 +282,15 @@ namespace osu.Game
|
|||||||
// Add after all the above cache operations as it depends on them.
|
// Add after all the above cache operations as it depends on them.
|
||||||
AddInternal(difficultyCache);
|
AddInternal(difficultyCache);
|
||||||
|
|
||||||
|
// TODO: OsuGame or OsuGameBase?
|
||||||
|
beatmapUpdater = new BeatmapUpdater(BeatmapManager, difficultyCache, API, Storage);
|
||||||
|
|
||||||
|
dependencies.CacheAs(spectatorClient = new OnlineSpectatorClient(endpoints));
|
||||||
|
dependencies.CacheAs(multiplayerClient = new OnlineMultiplayerClient(endpoints));
|
||||||
|
dependencies.CacheAs(metadataClient = new OnlineMetadataClient(endpoints, beatmapUpdater));
|
||||||
|
|
||||||
|
BeatmapManager.ProcessBeatmap = set => beatmapUpdater.Process(set);
|
||||||
|
|
||||||
dependencies.Cache(userCache = new UserLookupCache());
|
dependencies.Cache(userCache = new UserLookupCache());
|
||||||
AddInternal(userCache);
|
AddInternal(userCache);
|
||||||
|
|
||||||
@ -316,8 +327,10 @@ namespace osu.Game
|
|||||||
// add api components to hierarchy.
|
// add api components to hierarchy.
|
||||||
if (API is APIAccess apiAccess)
|
if (API is APIAccess apiAccess)
|
||||||
AddInternal(apiAccess);
|
AddInternal(apiAccess);
|
||||||
|
|
||||||
AddInternal(spectatorClient);
|
AddInternal(spectatorClient);
|
||||||
AddInternal(multiplayerClient);
|
AddInternal(multiplayerClient);
|
||||||
|
AddInternal(metadataClient);
|
||||||
|
|
||||||
AddInternal(rulesetConfigCache);
|
AddInternal(rulesetConfigCache);
|
||||||
|
|
||||||
@ -574,9 +587,10 @@ namespace osu.Game
|
|||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
RulesetStore?.Dispose();
|
RulesetStore?.Dispose();
|
||||||
BeatmapManager?.Dispose();
|
|
||||||
LocalConfig?.Dispose();
|
LocalConfig?.Dispose();
|
||||||
|
|
||||||
|
beatmapUpdater?.Dispose();
|
||||||
|
|
||||||
realm?.Dispose();
|
realm?.Dispose();
|
||||||
|
|
||||||
if (Host != null)
|
if (Host != null)
|
||||||
|
@ -133,9 +133,9 @@ namespace osu.Game.Overlays
|
|||||||
UserPauseRequested = false;
|
UserPauseRequested = false;
|
||||||
|
|
||||||
if (restart)
|
if (restart)
|
||||||
CurrentTrack.Restart();
|
CurrentTrack.RestartAsync();
|
||||||
else if (!IsPlaying)
|
else if (!IsPlaying)
|
||||||
CurrentTrack.Start();
|
CurrentTrack.StartAsync();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -152,7 +152,7 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
UserPauseRequested |= requestedByUser;
|
UserPauseRequested |= requestedByUser;
|
||||||
if (CurrentTrack.IsRunning)
|
if (CurrentTrack.IsRunning)
|
||||||
CurrentTrack.Stop();
|
CurrentTrack.StopAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -250,7 +250,7 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
// if not scheduled, the previously track will be stopped one frame later (see ScheduleAfterChildren logic in GameBase).
|
// if not scheduled, the previously track will be stopped one frame later (see ScheduleAfterChildren logic in GameBase).
|
||||||
// we probably want to move this to a central method for switching to a new working beatmap in the future.
|
// we probably want to move this to a central method for switching to a new working beatmap in the future.
|
||||||
Schedule(() => CurrentTrack.Restart());
|
Schedule(() => CurrentTrack.RestartAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorkingBeatmap current;
|
private WorkingBeatmap current;
|
||||||
|
@ -62,7 +62,8 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
{
|
{
|
||||||
skinDropdown = new SkinSettingsDropdown
|
skinDropdown = new SkinSettingsDropdown
|
||||||
{
|
{
|
||||||
LabelText = SkinSettingsStrings.CurrentSkin
|
LabelText = SkinSettingsStrings.CurrentSkin,
|
||||||
|
Keywords = new[] { @"skins" }
|
||||||
},
|
},
|
||||||
new SettingsButton
|
new SettingsButton
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,7 @@ using System.Threading;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -26,7 +27,7 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
private const int background_count = 7;
|
private const int background_count = 7;
|
||||||
private IBindable<APIUser> user;
|
private IBindable<APIUser> user;
|
||||||
private Bindable<Skin> skin;
|
private Bindable<Skin> skin;
|
||||||
private Bindable<BackgroundSource> mode;
|
private Bindable<BackgroundSource> source;
|
||||||
private Bindable<IntroSequence> introSequence;
|
private Bindable<IntroSequence> introSequence;
|
||||||
private readonly SeasonalBackgroundLoader seasonalBackgroundLoader = new SeasonalBackgroundLoader();
|
private readonly SeasonalBackgroundLoader seasonalBackgroundLoader = new SeasonalBackgroundLoader();
|
||||||
|
|
||||||
@ -45,24 +46,29 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
{
|
{
|
||||||
user = api.LocalUser.GetBoundCopy();
|
user = api.LocalUser.GetBoundCopy();
|
||||||
skin = skinManager.CurrentSkin.GetBoundCopy();
|
skin = skinManager.CurrentSkin.GetBoundCopy();
|
||||||
mode = config.GetBindable<BackgroundSource>(OsuSetting.MenuBackgroundSource);
|
source = config.GetBindable<BackgroundSource>(OsuSetting.MenuBackgroundSource);
|
||||||
introSequence = config.GetBindable<IntroSequence>(OsuSetting.IntroSequence);
|
introSequence = config.GetBindable<IntroSequence>(OsuSetting.IntroSequence);
|
||||||
|
|
||||||
AddInternal(seasonalBackgroundLoader);
|
AddInternal(seasonalBackgroundLoader);
|
||||||
|
|
||||||
user.ValueChanged += _ => Scheduler.AddOnce(loadNextIfRequired);
|
// Load first background asynchronously as part of BDL load.
|
||||||
skin.ValueChanged += _ => Scheduler.AddOnce(loadNextIfRequired);
|
|
||||||
mode.ValueChanged += _ => Scheduler.AddOnce(loadNextIfRequired);
|
|
||||||
beatmap.ValueChanged += _ => Scheduler.AddOnce(loadNextIfRequired);
|
|
||||||
introSequence.ValueChanged += _ => Scheduler.AddOnce(loadNextIfRequired);
|
|
||||||
seasonalBackgroundLoader.SeasonalBackgroundChanged += () => Scheduler.AddOnce(loadNextIfRequired);
|
|
||||||
|
|
||||||
currentDisplay = RNG.Next(0, background_count);
|
currentDisplay = RNG.Next(0, background_count);
|
||||||
|
|
||||||
Next();
|
Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
user.ValueChanged += _ => Scheduler.AddOnce(next);
|
||||||
|
skin.ValueChanged += _ => Scheduler.AddOnce(next);
|
||||||
|
source.ValueChanged += _ => Scheduler.AddOnce(next);
|
||||||
|
beatmap.ValueChanged += _ => Scheduler.AddOnce(next);
|
||||||
|
introSequence.ValueChanged += _ => Scheduler.AddOnce(next);
|
||||||
|
seasonalBackgroundLoader.SeasonalBackgroundChanged += () => Scheduler.AddOnce(next);
|
||||||
|
|
||||||
// helper function required for AddOnce usage.
|
// helper function required for AddOnce usage.
|
||||||
void loadNextIfRequired() => Next();
|
void next() => Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate nextTask;
|
private ScheduledDelegate nextTask;
|
||||||
@ -80,6 +86,8 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
if (nextBackground == background)
|
if (nextBackground == background)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
Logger.Log("🌅 Background change queued");
|
||||||
|
|
||||||
cancellationTokenSource?.Cancel();
|
cancellationTokenSource?.Cancel();
|
||||||
cancellationTokenSource = new CancellationTokenSource();
|
cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
@ -108,12 +116,12 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
|
|
||||||
if (newBackground == null && user.Value?.IsSupporter == true)
|
if (newBackground == null && user.Value?.IsSupporter == true)
|
||||||
{
|
{
|
||||||
switch (mode.Value)
|
switch (source.Value)
|
||||||
{
|
{
|
||||||
case BackgroundSource.Beatmap:
|
case BackgroundSource.Beatmap:
|
||||||
case BackgroundSource.BeatmapWithStoryboard:
|
case BackgroundSource.BeatmapWithStoryboard:
|
||||||
{
|
{
|
||||||
if (mode.Value == BackgroundSource.BeatmapWithStoryboard && AllowStoryboardBackground)
|
if (source.Value == BackgroundSource.BeatmapWithStoryboard && AllowStoryboardBackground)
|
||||||
newBackground = new BeatmapBackgroundWithStoryboard(beatmap.Value, getBackgroundTextureName());
|
newBackground = new BeatmapBackgroundWithStoryboard(beatmap.Value, getBackgroundTextureName());
|
||||||
newBackground ??= new BeatmapBackground(beatmap.Value, getBackgroundTextureName());
|
newBackground ??= new BeatmapBackground(beatmap.Value, getBackgroundTextureName());
|
||||||
|
|
||||||
|
@ -88,6 +88,11 @@ namespace osu.Game.Screens.Menu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool UsingThemedIntro { get; private set; }
|
protected bool UsingThemedIntro { get; private set; }
|
||||||
|
|
||||||
|
protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(false)
|
||||||
|
{
|
||||||
|
Colour = Color4.Black
|
||||||
|
};
|
||||||
|
|
||||||
protected IntroScreen([CanBeNull] Func<MainMenu> createNextScreen = null)
|
protected IntroScreen([CanBeNull] Func<MainMenu> createNextScreen = null)
|
||||||
{
|
{
|
||||||
this.createNextScreen = createNextScreen;
|
this.createNextScreen = createNextScreen;
|
||||||
@ -201,6 +206,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
this.FadeIn(300);
|
this.FadeIn(300);
|
||||||
|
|
||||||
|
ApplyToBackground(b => b.FadeColour(Color4.Black, 100));
|
||||||
|
|
||||||
double fadeOutTime = exit_delay;
|
double fadeOutTime = exit_delay;
|
||||||
|
|
||||||
var track = musicController.CurrentTrack;
|
var track = musicController.CurrentTrack;
|
||||||
@ -243,13 +250,22 @@ namespace osu.Game.Screens.Menu
|
|||||||
base.OnResuming(e);
|
base.OnResuming(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool backgroundFaded;
|
||||||
|
|
||||||
|
protected void FadeInBackground(float duration = 0)
|
||||||
|
{
|
||||||
|
ApplyToBackground(b => b.FadeColour(Color4.White, duration));
|
||||||
|
backgroundFaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnSuspending(ScreenTransitionEvent e)
|
public override void OnSuspending(ScreenTransitionEvent e)
|
||||||
{
|
{
|
||||||
base.OnSuspending(e);
|
base.OnSuspending(e);
|
||||||
initialBeatmap = null;
|
initialBeatmap = null;
|
||||||
}
|
|
||||||
|
|
||||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
|
if (!backgroundFaded)
|
||||||
|
FadeInBackground(200);
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void StartTrack()
|
protected virtual void StartTrack()
|
||||||
{
|
{
|
||||||
|
@ -8,19 +8,18 @@ using JetBrains.Annotations;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Screens;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Screens.Backgrounds;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -32,16 +31,9 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
protected override string BeatmapFile => "triangles.osz";
|
protected override string BeatmapFile => "triangles.osz";
|
||||||
|
|
||||||
protected override BackgroundScreen CreateBackground() => background = new BackgroundScreenDefault(false)
|
|
||||||
{
|
|
||||||
Alpha = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private AudioManager audio { get; set; }
|
private AudioManager audio { get; set; }
|
||||||
|
|
||||||
private BackgroundScreenDefault background;
|
|
||||||
|
|
||||||
private Sample welcome;
|
private Sample welcome;
|
||||||
|
|
||||||
private DecoupleableInterpolatingFramedClock decoupledClock;
|
private DecoupleableInterpolatingFramedClock decoupledClock;
|
||||||
@ -75,7 +67,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
if (UsingThemedIntro)
|
if (UsingThemedIntro)
|
||||||
decoupledClock.ChangeSource(Track);
|
decoupledClock.ChangeSource(Track);
|
||||||
|
|
||||||
LoadComponentAsync(intro = new TrianglesIntroSequence(logo, background)
|
LoadComponentAsync(intro = new TrianglesIntroSequence(logo, () => FadeInBackground())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Clock = decoupledClock,
|
Clock = decoupledClock,
|
||||||
@ -95,19 +87,10 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
base.OnSuspending(e);
|
base.OnSuspending(e);
|
||||||
|
|
||||||
// ensure the background is shown, even if the TriangleIntroSequence failed to do so.
|
|
||||||
background.ApplyToBackground(b => b.Show());
|
|
||||||
|
|
||||||
// important as there is a clock attached to a track which will likely be disposed before returning to this screen.
|
// important as there is a clock attached to a track which will likely be disposed before returning to this screen.
|
||||||
intro.Expire();
|
intro.Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnResuming(ScreenTransitionEvent e)
|
|
||||||
{
|
|
||||||
base.OnResuming(e);
|
|
||||||
background.FadeOut(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void StartTrack()
|
protected override void StartTrack()
|
||||||
{
|
{
|
||||||
decoupledClock.Start();
|
decoupledClock.Start();
|
||||||
@ -116,7 +99,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
private class TrianglesIntroSequence : CompositeDrawable
|
private class TrianglesIntroSequence : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly OsuLogo logo;
|
private readonly OsuLogo logo;
|
||||||
private readonly BackgroundScreenDefault background;
|
private readonly Action showBackgroundAction;
|
||||||
private OsuSpriteText welcomeText;
|
private OsuSpriteText welcomeText;
|
||||||
|
|
||||||
private RulesetFlow rulesets;
|
private RulesetFlow rulesets;
|
||||||
@ -128,10 +111,10 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
public Action LoadMenu;
|
public Action LoadMenu;
|
||||||
|
|
||||||
public TrianglesIntroSequence(OsuLogo logo, BackgroundScreenDefault background)
|
public TrianglesIntroSequence(OsuLogo logo, Action showBackgroundAction)
|
||||||
{
|
{
|
||||||
this.logo = logo;
|
this.logo = logo;
|
||||||
this.background = background;
|
this.showBackgroundAction = showBackgroundAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
@ -205,7 +188,6 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
rulesets.Hide();
|
rulesets.Hide();
|
||||||
lazerLogo.Hide();
|
lazerLogo.Hide();
|
||||||
background.ApplyToBackground(b => b.Hide());
|
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(0))
|
using (BeginAbsoluteSequence(0))
|
||||||
{
|
{
|
||||||
@ -267,7 +249,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
logo.FadeIn();
|
logo.FadeIn();
|
||||||
|
|
||||||
background.ApplyToBackground(b => b.Show());
|
showBackgroundAction();
|
||||||
|
|
||||||
game.Add(new GameWideFlash());
|
game.Add(new GameWideFlash());
|
||||||
|
|
||||||
|
@ -5,11 +5,9 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osuTK;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Screens;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@ -17,8 +15,8 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Screens.Backgrounds;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Menu
|
namespace osu.Game.Screens.Menu
|
||||||
@ -35,13 +33,6 @@ namespace osu.Game.Screens.Menu
|
|||||||
private ISample pianoReverb;
|
private ISample pianoReverb;
|
||||||
protected override string SeeyaSampleName => "Intro/Welcome/seeya";
|
protected override string SeeyaSampleName => "Intro/Welcome/seeya";
|
||||||
|
|
||||||
protected override BackgroundScreen CreateBackground() => background = new BackgroundScreenDefault(false)
|
|
||||||
{
|
|
||||||
Alpha = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
private BackgroundScreenDefault background;
|
|
||||||
|
|
||||||
public IntroWelcome([CanBeNull] Func<MainMenu> createNextScreen = null)
|
public IntroWelcome([CanBeNull] Func<MainMenu> createNextScreen = null)
|
||||||
: base(createNextScreen)
|
: base(createNextScreen)
|
||||||
{
|
{
|
||||||
@ -100,7 +91,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
logo.ScaleTo(1);
|
logo.ScaleTo(1);
|
||||||
logo.FadeIn(fade_in_time);
|
logo.FadeIn(fade_in_time);
|
||||||
|
|
||||||
background.FadeIn(fade_in_time);
|
FadeInBackground(fade_in_time);
|
||||||
|
|
||||||
LoadMenu();
|
LoadMenu();
|
||||||
}, delay_step_two);
|
}, delay_step_two);
|
||||||
@ -108,12 +99,6 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnResuming(ScreenTransitionEvent e)
|
|
||||||
{
|
|
||||||
base.OnResuming(e);
|
|
||||||
background.FadeOut(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class WelcomeIntroSequence : Container
|
private class WelcomeIntroSequence : Container
|
||||||
{
|
{
|
||||||
private Drawable welcomeText;
|
private Drawable welcomeText;
|
||||||
|
@ -65,9 +65,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
[Resolved(canBeNull: true)]
|
[Resolved(canBeNull: true)]
|
||||||
private IDialogOverlay dialogOverlay { get; set; }
|
private IDialogOverlay dialogOverlay { get; set; }
|
||||||
|
|
||||||
private BackgroundScreenDefault background;
|
protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault();
|
||||||
|
|
||||||
protected override BackgroundScreen CreateBackground() => background;
|
|
||||||
|
|
||||||
protected override bool PlayExitSound => false;
|
protected override bool PlayExitSound => false;
|
||||||
|
|
||||||
@ -148,7 +146,6 @@ namespace osu.Game.Screens.Menu
|
|||||||
Buttons.OnSettings = () => settings?.ToggleVisibility();
|
Buttons.OnSettings = () => settings?.ToggleVisibility();
|
||||||
Buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility();
|
Buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility();
|
||||||
|
|
||||||
LoadComponentAsync(background = new BackgroundScreenDefault());
|
|
||||||
preloadSongSelect();
|
preloadSongSelect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
// TODO: Add realm queries to hint at which ruleset results are available in (and allow clicking to switch).
|
// TODO: Add realm queries to hint at which ruleset results are available in (and allow clicking to switch).
|
||||||
// TODO: Make this message more certain by ensuring the osu! beatmaps exist before suggesting.
|
// TODO: Make this message more certain by ensuring the osu! beatmaps exist before suggesting.
|
||||||
if (filter?.Ruleset?.OnlineID > 0 && !filter.AllowConvertedBeatmaps)
|
if (filter?.Ruleset?.OnlineID != 0 && filter?.AllowConvertedBeatmaps == false)
|
||||||
{
|
{
|
||||||
textFlow.AddParagraph("- Try");
|
textFlow.AddParagraph("- Try");
|
||||||
textFlow.AddLink(" enabling ", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true));
|
textFlow.AddLink(" enabling ", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true));
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
public FlakyTestAttribute(int tryCount)
|
public FlakyTestAttribute(int tryCount)
|
||||||
: base(Environment.GetEnvironmentVariable("OSU_TESTS_FAIL_FLAKY") == "1" ? 0 : tryCount)
|
: base(Environment.GetEnvironmentVariable("OSU_TESTS_FAIL_FLAKY") == "1" ? 1 : tryCount)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,11 +430,19 @@ namespace osu.Game.Tests.Visual
|
|||||||
return accumulated == seek;
|
return accumulated == seek;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Task<bool> SeekAsync(double seek) => Task.FromResult(Seek(seek));
|
||||||
|
|
||||||
public override void Start()
|
public override void Start()
|
||||||
{
|
{
|
||||||
running = true;
|
running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Task StartAsync()
|
||||||
|
{
|
||||||
|
Start();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
public override void Reset()
|
public override void Reset()
|
||||||
{
|
{
|
||||||
Seek(0);
|
Seek(0);
|
||||||
@ -450,6 +458,12 @@ namespace osu.Game.Tests.Visual
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Task StopAsync()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool IsRunning => running;
|
public override bool IsRunning => running;
|
||||||
|
|
||||||
private double? lastReferenceTime;
|
private double? lastReferenceTime;
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.14.0" />
|
<PackageReference Include="Realm" Version="10.14.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.704.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2022.707.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.17.1" />
|
<PackageReference Include="Sentry" Version="3.17.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.31.0" />
|
<PackageReference Include="SharpCompress" Version="0.31.0" />
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.704.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.707.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
||||||
@ -84,7 +84,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.704.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2022.707.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.31.0" />
|
<PackageReference Include="SharpCompress" Version="0.31.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user