mirror of
https://github.com/ppy/osu.git
synced 2025-03-14 05:47:20 +08:00
Merge branch 'master' into mod-overlay/panel
This commit is contained in:
commit
6e8daa06fa
@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.217.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.223.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -8,12 +8,15 @@ using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
@ -23,13 +26,37 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
protected override bool AllowFail => true;
|
||||
|
||||
[Test]
|
||||
public void TestSpinnerAutoCompleted() => CreateModTest(new ModTestData
|
||||
public void TestSpinnerAutoCompleted()
|
||||
{
|
||||
Mod = new OsuModSpunOut(),
|
||||
Autoplay = false,
|
||||
Beatmap = singleSpinnerBeatmap,
|
||||
PassCondition = () => Player.ChildrenOfType<DrawableSpinner>().SingleOrDefault()?.Progress >= 1
|
||||
});
|
||||
DrawableSpinner spinner = null;
|
||||
JudgementResult lastResult = null;
|
||||
|
||||
CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModSpunOut(),
|
||||
Autoplay = false,
|
||||
Beatmap = singleSpinnerBeatmap,
|
||||
PassCondition = () =>
|
||||
{
|
||||
// Bind to the first spinner's results for further tracking.
|
||||
if (spinner == null)
|
||||
{
|
||||
// We only care about the first spinner we encounter for this test.
|
||||
var nextSpinner = Player.ChildrenOfType<DrawableSpinner>().SingleOrDefault();
|
||||
|
||||
if (nextSpinner == null)
|
||||
return false;
|
||||
|
||||
lastResult = null;
|
||||
|
||||
spinner = nextSpinner;
|
||||
spinner.OnNewResult += (o, result) => lastResult = result;
|
||||
}
|
||||
|
||||
return lastResult?.Type == HitResult.Great;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(null)]
|
||||
[TestCase(typeof(OsuModDoubleTime))]
|
||||
@ -48,7 +75,57 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
PassCondition = () =>
|
||||
{
|
||||
var counter = Player.ChildrenOfType<SpinnerSpmCalculator>().SingleOrDefault();
|
||||
return counter != null && Precision.AlmostEquals(counter.Result.Value, 286, 1);
|
||||
var spinner = Player.ChildrenOfType<DrawableSpinner>().FirstOrDefault();
|
||||
|
||||
if (counter == null || spinner == null)
|
||||
return false;
|
||||
|
||||
// ignore cases where the spinner hasn't started as these lead to false-positives
|
||||
if (Precision.AlmostEquals(counter.Result.Value, 0, 1))
|
||||
return false;
|
||||
|
||||
float rotationSpeed = (float)(1.01 * spinner.HitObject.SpinsRequired / spinner.HitObject.Duration);
|
||||
|
||||
return Precision.AlmostEquals(counter.Result.Value, rotationSpeed * 1000 * 60, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSpinnerGetsNoBonusScore()
|
||||
{
|
||||
DrawableSpinner spinner = null;
|
||||
List<JudgementResult> results = new List<JudgementResult>();
|
||||
|
||||
CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModSpunOut(),
|
||||
Autoplay = false,
|
||||
Beatmap = singleSpinnerBeatmap,
|
||||
PassCondition = () =>
|
||||
{
|
||||
// Bind to the first spinner's results for further tracking.
|
||||
if (spinner == null)
|
||||
{
|
||||
// We only care about the first spinner we encounter for this test.
|
||||
var nextSpinner = Player.ChildrenOfType<DrawableSpinner>().SingleOrDefault();
|
||||
|
||||
if (nextSpinner == null)
|
||||
return false;
|
||||
|
||||
spinner = nextSpinner;
|
||||
spinner.OnNewResult += (o, result) => results.Add(result);
|
||||
|
||||
results.Clear();
|
||||
}
|
||||
|
||||
// we should only be checking the bonus/progress after the spinner has fully completed.
|
||||
if (results.OfType<OsuSpinnerJudgementResult>().All(r => r.TimeCompleted == null))
|
||||
return false;
|
||||
|
||||
return
|
||||
results.Any(r => r.Type == HitResult.SmallBonus)
|
||||
&& results.All(r => r.Type != HitResult.LargeBonus);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -45,7 +45,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
// for that reason using ElapsedFrameTime directly leads to fewer SPM with Half Time and more SPM with Double Time.
|
||||
// for spinners we want the real (wall clock) elapsed time; to achieve that, unapply the clock rate locally here.
|
||||
double rateIndependentElapsedTime = spinner.Clock.ElapsedFrameTime / spinner.Clock.Rate;
|
||||
spinner.RotationTracker.AddRotation(MathUtils.RadiansToDegrees((float)rateIndependentElapsedTime * 0.03f));
|
||||
|
||||
// multiply the SPM by 1.01 to ensure that the spinner is completed. if the calculation is left exact,
|
||||
// some spinners may not complete due to very minor decimal loss during calculation
|
||||
float rotationSpeed = (float)(1.01 * spinner.HitObject.SpinsRequired / spinner.HitObject.Duration);
|
||||
spinner.RotationTracker.AddRotation(MathUtils.RadiansToDegrees((float)rateIndependentElapsedTime * rotationSpeed * MathF.PI * 2.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -409,26 +409,26 @@ namespace osu.Game.Tests.Chat
|
||||
|
||||
Assert.AreEqual(result.Content, result.DisplayContent);
|
||||
Assert.AreEqual(2, result.Links.Count);
|
||||
Assert.AreEqual("osu://chan/#english", result.Links[0].Url);
|
||||
Assert.AreEqual("osu://chan/#japanese", result.Links[1].Url);
|
||||
Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#english", result.Links[0].Url);
|
||||
Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#japanese", result.Links[1].Url);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOsuProtocol()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a custom protocol osu://chan/#english." });
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = $"This is a custom protocol {OsuGameBase.OSU_PROTOCOL}chan/#english." });
|
||||
|
||||
Assert.AreEqual(result.Content, result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("osu://chan/#english", result.Links[0].Url);
|
||||
Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#english", result.Links[0].Url);
|
||||
Assert.AreEqual(26, result.Links[0].Index);
|
||||
Assert.AreEqual(19, result.Links[0].Length);
|
||||
|
||||
result = MessageFormatter.FormatMessage(new Message { Content = "This is a [custom protocol](osu://chan/#english)." });
|
||||
result = MessageFormatter.FormatMessage(new Message { Content = $"This is a [custom protocol]({OsuGameBase.OSU_PROTOCOL}chan/#english)." });
|
||||
|
||||
Assert.AreEqual("This is a custom protocol.", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("osu://chan/#english", result.Links[0].Url);
|
||||
Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#english", result.Links[0].Url);
|
||||
Assert.AreEqual("#english", result.Links[0].Argument);
|
||||
Assert.AreEqual(10, result.Links[0].Index);
|
||||
Assert.AreEqual(15, result.Links[0].Length);
|
||||
|
@ -0,0 +1,54 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
public class TestSceneStartupBeatmapDisplay : OsuGameTestScene
|
||||
{
|
||||
private const int requested_beatmap_id = 75;
|
||||
private const int requested_beatmap_set_id = 1;
|
||||
|
||||
protected override TestOsuGame CreateTestGame() => new TestOsuGame(LocalStorage, API, new[] { $"osu://b/{requested_beatmap_id}" });
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
((DummyAPIAccess)API).HandleRequest = request =>
|
||||
{
|
||||
switch (request)
|
||||
{
|
||||
case GetBeatmapSetRequest gbr:
|
||||
var apiBeatmapSet = CreateAPIBeatmapSet();
|
||||
apiBeatmapSet.OnlineID = requested_beatmap_set_id;
|
||||
apiBeatmapSet.Beatmaps = apiBeatmapSet.Beatmaps.Append(new APIBeatmap
|
||||
{
|
||||
DifficultyName = "Target difficulty",
|
||||
OnlineID = requested_beatmap_id,
|
||||
}).ToArray();
|
||||
|
||||
gbr.TriggerSuccess(apiBeatmapSet);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapLink()
|
||||
{
|
||||
AddUntilStep("Beatmap overlay displayed", () => Game.ChildrenOfType<BeatmapSetOverlay>().FirstOrDefault()?.State.Value == Visibility.Visible);
|
||||
AddUntilStep("Beatmap overlay showing content", () => Game.ChildrenOfType<BeatmapPicker>().FirstOrDefault()?.Beatmap.Value.OnlineID == requested_beatmap_id);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
public class TestSceneStartupBeatmapSetDisplay : OsuGameTestScene
|
||||
{
|
||||
private const int requested_beatmap_set_id = 1;
|
||||
|
||||
protected override TestOsuGame CreateTestGame() => new TestOsuGame(LocalStorage, API, new[] { $"osu://s/{requested_beatmap_set_id}" });
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
((DummyAPIAccess)API).HandleRequest = request =>
|
||||
{
|
||||
switch (request)
|
||||
{
|
||||
case GetBeatmapSetRequest gbr:
|
||||
|
||||
var apiBeatmapSet = CreateAPIBeatmapSet();
|
||||
apiBeatmapSet.OnlineID = requested_beatmap_set_id;
|
||||
apiBeatmapSet.Beatmaps = apiBeatmapSet.Beatmaps.Append(new APIBeatmap
|
||||
{
|
||||
DifficultyName = "Target difficulty",
|
||||
OnlineID = 75,
|
||||
}).ToArray();
|
||||
gbr.TriggerSuccess(apiBeatmapSet);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapSetLink()
|
||||
{
|
||||
AddUntilStep("Beatmap overlay displayed", () => Game.ChildrenOfType<BeatmapSetOverlay>().FirstOrDefault()?.State.Value == Visibility.Visible);
|
||||
AddUntilStep("Beatmap overlay showing content", () => Game.ChildrenOfType<BeatmapSetOverlay>().FirstOrDefault()?.Header.BeatmapSet.Value.OnlineID == requested_beatmap_set_id);
|
||||
}
|
||||
}
|
||||
}
|
@ -87,8 +87,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
addMessageWithChecks("likes to post this [https://dev.ppy.sh/home link].", 1, true, true, expectedActions: LinkAction.External);
|
||||
addMessageWithChecks("Join my multiplayer game osump://12346.", 1, expectedActions: LinkAction.JoinMultiplayerMatch);
|
||||
addMessageWithChecks("Join my [multiplayer game](osump://12346).", 1, expectedActions: LinkAction.JoinMultiplayerMatch);
|
||||
addMessageWithChecks("Join my [#english](osu://chan/#english).", 1, expectedActions: LinkAction.OpenChannel);
|
||||
addMessageWithChecks("Join my osu://chan/#english.", 1, expectedActions: LinkAction.OpenChannel);
|
||||
addMessageWithChecks($"Join my [#english]({OsuGameBase.OSU_PROTOCOL}chan/#english).", 1, expectedActions: LinkAction.OpenChannel);
|
||||
addMessageWithChecks($"Join my {OsuGameBase.OSU_PROTOCOL}chan/#english.", 1, expectedActions: LinkAction.OpenChannel);
|
||||
addMessageWithChecks("Join my #english or #japanese channels.", 2, expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel });
|
||||
addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", 1, expectedActions: LinkAction.OpenChannel);
|
||||
|
||||
|
@ -268,10 +268,10 @@ namespace osu.Game.Online.Chat
|
||||
handleAdvanced(advanced_link_regex, result, startIndex);
|
||||
|
||||
// handle editor times
|
||||
handleMatches(time_regex, "{0}", "osu://edit/{0}", result, startIndex, LinkAction.OpenEditorTimestamp);
|
||||
handleMatches(time_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}edit/{{0}}", result, startIndex, LinkAction.OpenEditorTimestamp);
|
||||
|
||||
// handle channels
|
||||
handleMatches(channel_regex, "{0}", "osu://chan/{0}", result, startIndex, LinkAction.OpenChannel);
|
||||
handleMatches(channel_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}chan/{{0}}", result, startIndex, LinkAction.OpenChannel);
|
||||
|
||||
string empty = "";
|
||||
while (space-- > 0)
|
||||
|
@ -150,6 +150,7 @@ namespace osu.Game
|
||||
protected SettingsOverlay Settings;
|
||||
|
||||
private VolumeOverlay volume;
|
||||
|
||||
private OsuLogo osuLogo;
|
||||
|
||||
private MainMenu menuScreen;
|
||||
@ -898,8 +899,20 @@ namespace osu.Game
|
||||
if (args?.Length > 0)
|
||||
{
|
||||
string[] paths = args.Where(a => !a.StartsWith('-')).ToArray();
|
||||
|
||||
if (paths.Length > 0)
|
||||
Task.Run(() => Import(paths));
|
||||
{
|
||||
string firstPath = paths.First();
|
||||
|
||||
if (firstPath.StartsWith(OSU_PROTOCOL, StringComparison.Ordinal))
|
||||
{
|
||||
HandleLink(firstPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Task.Run(() => Import(paths));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,8 @@ namespace osu.Game
|
||||
/// </summary>
|
||||
public partial class OsuGameBase : Framework.Game, ICanAcceptFiles
|
||||
{
|
||||
public const string OSU_PROTOCOL = "osu://";
|
||||
|
||||
public const string CLIENT_STREAM_NAME = @"lazer";
|
||||
|
||||
public const int SAMPLE_CONCURRENCY = 6;
|
||||
|
@ -183,7 +183,14 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
}
|
||||
|
||||
starRatingContainer.FadeOut(100);
|
||||
Beatmap.Value = Difficulties.FirstOrDefault()?.Beatmap;
|
||||
|
||||
// If a selection is already made, try and maintain it.
|
||||
if (Beatmap.Value != null)
|
||||
Beatmap.Value = Difficulties.FirstOrDefault(b => b.Beatmap.OnlineID == Beatmap.Value.OnlineID)?.Beatmap;
|
||||
|
||||
// Else just choose the first available difficulty for now.
|
||||
Beatmap.Value ??= Difficulties.FirstOrDefault()?.Beatmap;
|
||||
|
||||
plays.Value = BeatmapSet?.PlayCount ?? 0;
|
||||
favourites.Value = BeatmapSet?.FavouriteCount ?? 0;
|
||||
|
||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var dllStore = new DllResourceStore(DynamicCompilationOriginal.GetType().Assembly);
|
||||
var dllStore = new DllResourceStore(GetType().Assembly);
|
||||
|
||||
metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), this, true);
|
||||
defaultSkin = new DefaultLegacySkin(this);
|
||||
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.9.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.217.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.223.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
||||
<PackageReference Include="Sentry" Version="3.14.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||
|
@ -60,7 +60,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.217.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.223.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
||||
@ -83,7 +83,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.217.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.223.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user