mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 07:42:57 +08:00
Merge branch 'master' into editor-metrics
This commit is contained in:
commit
7f336b8dac
@ -8,13 +8,9 @@
|
||||
<!-- NullabilityInfoContextSupport is disabled by default for Android -->
|
||||
<NullabilityInfoContextSupport>true</NullabilityInfoContextSupport>
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
<AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.714.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.716.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="mailto" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
@ -54,9 +54,6 @@ namespace osu.Desktop
|
||||
|
||||
client.OnReady += onReady;
|
||||
|
||||
// safety measure for now, until we performance test / improve backoff for failed connections.
|
||||
client.OnConnectionFailed += (_, _) => client.Deinitialize();
|
||||
|
||||
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network);
|
||||
|
||||
config.BindWith(OsuSetting.DiscordRichPresence, privacyMode);
|
||||
|
@ -26,7 +26,7 @@
|
||||
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
<PackageReference Include="System.IO.Packaging" Version="7.0.0" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.1.4.20" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Resources">
|
||||
<EmbeddedResource Include="lazer.ico" />
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
}
|
||||
|
||||
protected override IEnumerable<HitObject> EnumerateHitObjects(IBeatmap beatmap)
|
||||
=> base.EnumerateHitObjects(beatmap).OrderBy(ho => (ManiaHitObject)ho, JudgementOrderComparer.DEFAULT);
|
||||
=> base.EnumerateHitObjects(beatmap).OrderBy(ho => ho, JudgementOrderComparer.DEFAULT);
|
||||
|
||||
protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion)
|
||||
{
|
||||
@ -34,11 +34,11 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
protected override double GetComboScoreChange(JudgementResult result)
|
||||
=> Judgement.ToNumericResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base));
|
||||
|
||||
private class JudgementOrderComparer : IComparer<ManiaHitObject>
|
||||
private class JudgementOrderComparer : IComparer<HitObject>
|
||||
{
|
||||
public static readonly JudgementOrderComparer DEFAULT = new JudgementOrderComparer();
|
||||
|
||||
public int Compare(ManiaHitObject? x, ManiaHitObject? y)
|
||||
public int Compare(HitObject? x, HitObject? y)
|
||||
{
|
||||
if (ReferenceEquals(x, y)) return 0;
|
||||
if (ReferenceEquals(x, null)) return -1;
|
||||
@ -52,7 +52,9 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
if (x is Note && y is not Note) return -1;
|
||||
if (x is not Note && y is Note) return 1;
|
||||
|
||||
return x.Column.CompareTo(y.Column);
|
||||
return x is ManiaHitObject maniaX && y is ManiaHitObject maniaY
|
||||
? maniaX.Column.CompareTo(maniaY.Column)
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@ -10,6 +11,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@ -36,11 +38,12 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements
|
||||
() => Is.EqualTo(expectedResult));
|
||||
}
|
||||
|
||||
protected void PerformTest(List<ReplayFrame> frames, Beatmap<TaikoHitObject>? beatmap = null)
|
||||
protected void PerformTest(List<ReplayFrame> frames, Beatmap<TaikoHitObject>? beatmap = null, Mod[]? mods = null)
|
||||
{
|
||||
AddStep("load player", () =>
|
||||
{
|
||||
Beatmap.Value = CreateWorkingBeatmap(beatmap);
|
||||
SelectedMods.Value = mods ?? Array.Empty<Mod>();
|
||||
|
||||
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
|
||||
|
||||
|
@ -75,6 +75,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements
|
||||
AssertResult<DrumRoll>(0, HitResult.IgnoreHit);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHitNoneStrongDrumRoll()
|
||||
{
|
||||
PerformTest(new List<ReplayFrame>
|
||||
{
|
||||
new TaikoReplayFrame(0),
|
||||
}, CreateBeatmap(createDrumRoll(true)));
|
||||
|
||||
AssertJudgementCount(12);
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
AssertResult<DrumRollTick>(i, HitResult.IgnoreMiss);
|
||||
AssertResult<DrumRollTick.StrongNestedHit>(i, HitResult.IgnoreMiss);
|
||||
}
|
||||
|
||||
AssertResult<DrumRoll>(0, HitResult.IgnoreHit);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHitAllStrongDrumRollWithOneKey()
|
||||
{
|
||||
|
@ -4,10 +4,14 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Replays;
|
||||
using osu.Game.Rulesets.Taiko.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests.Judgements
|
||||
{
|
||||
@ -157,5 +161,58 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements
|
||||
AssertJudgementCount(1);
|
||||
AssertResult<Hit>(0, HitResult.Ok);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStrongHitOneKeyWithHidden()
|
||||
{
|
||||
const double hit_time = 1000;
|
||||
|
||||
var beatmap = CreateBeatmap(new Hit
|
||||
{
|
||||
Type = HitType.Centre,
|
||||
StartTime = hit_time,
|
||||
IsStrong = true
|
||||
});
|
||||
|
||||
var hitWindows = new TaikoHitWindows();
|
||||
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
|
||||
|
||||
PerformTest(new List<ReplayFrame>
|
||||
{
|
||||
new TaikoReplayFrame(0),
|
||||
new TaikoReplayFrame(hit_time + hitWindows.WindowFor(HitResult.Ok) - 1, TaikoAction.LeftCentre),
|
||||
}, beatmap, new Mod[] { new TaikoModHidden() });
|
||||
|
||||
AssertJudgementCount(2);
|
||||
AssertResult<Hit>(0, HitResult.Ok);
|
||||
AssertResult<Hit.StrongNestedHit>(0, HitResult.IgnoreMiss);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStrongHitTwoKeysWithHidden()
|
||||
{
|
||||
const double hit_time = 1000;
|
||||
|
||||
var beatmap = CreateBeatmap(new Hit
|
||||
{
|
||||
Type = HitType.Centre,
|
||||
StartTime = hit_time,
|
||||
IsStrong = true
|
||||
});
|
||||
|
||||
var hitWindows = new TaikoHitWindows();
|
||||
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
|
||||
|
||||
PerformTest(new List<ReplayFrame>
|
||||
{
|
||||
new TaikoReplayFrame(0),
|
||||
new TaikoReplayFrame(hit_time + hitWindows.WindowFor(HitResult.Ok) - 1, TaikoAction.LeftCentre),
|
||||
new TaikoReplayFrame(hit_time + hitWindows.WindowFor(HitResult.Ok) + DrawableHit.StrongNestedHit.SECOND_HIT_WINDOW - 2, TaikoAction.LeftCentre, TaikoAction.RightCentre),
|
||||
}, beatmap, new Mod[] { new TaikoModHidden() });
|
||||
|
||||
AssertJudgementCount(2);
|
||||
AssertResult<Hit>(0, HitResult.Ok);
|
||||
AssertResult<Hit.StrongNestedHit>(0, HitResult.LargeBonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,8 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
hitObject.LifetimeEnd = state == ArmedState.Idle || !hitObject.AllJudged
|
||||
? hitObject.HitObject.GetEndTime() + hitObject.HitObject.HitWindows.WindowFor(HitResult.Miss)
|
||||
: hitObject.HitStateUpdateTime;
|
||||
// extend the lifetime end of the object in order to allow its nested strong hit (if any) to be judged.
|
||||
hitObject.LifetimeEnd += DrawableHit.StrongNestedHit.SECOND_HIT_WINDOW;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -195,14 +195,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
}
|
||||
|
||||
public override void OnKilled()
|
||||
{
|
||||
base.OnKilled();
|
||||
|
||||
if (Time.Current > ParentHitObject.HitObject.GetEndTime() && !Judged)
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
}
|
||||
}
|
||||
|
@ -108,14 +108,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
}
|
||||
|
||||
public override void OnKilled()
|
||||
{
|
||||
base.OnKilled();
|
||||
|
||||
if (Time.Current > ParentHitObject.HitObject.GetEndTime() && !Judged)
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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 osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
@ -16,5 +17,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
: base(nestedHit)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnKilled()
|
||||
{
|
||||
base.OnKilled();
|
||||
|
||||
// usually, the strong nested hit isn't judged itself, it is judged by its parent object.
|
||||
// however, in rare cases (see: drum rolls, hits with hidden active),
|
||||
// it can happen that the hit window of the nested strong hit extends past the lifetime of the parent object.
|
||||
// this is a safety to prevent such cases from causing the nested hit to never be judged and as such prevent gameplay from completing.
|
||||
if (!Judged && Time.Current > ParentHitObject?.HitObject.GetEndTime())
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -722,6 +722,30 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestForceExitWithOperationInProgress()
|
||||
{
|
||||
AddStep("set hold delay to 0", () => Game.LocalConfig.SetValue(OsuSetting.UIHoldActivationDelay, 0.0));
|
||||
AddUntilStep("wait for dialog overlay", () => Game.ChildrenOfType<DialogOverlay>().SingleOrDefault() != null);
|
||||
|
||||
AddStep("start ongoing operation", () =>
|
||||
{
|
||||
Game.Notifications.Post(new ProgressNotification
|
||||
{
|
||||
Text = "Something is still running",
|
||||
Progress = 0.5f,
|
||||
State = ProgressNotificationState.Active,
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("attempt exit", () =>
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
Game.ScreenStack.CurrentScreen.Exit();
|
||||
});
|
||||
AddUntilStep("stopped at exit confirm", () => Game.ChildrenOfType<DialogOverlay>().Single().CurrentDialog is ConfirmExitDialog);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitGameFromSongSelect()
|
||||
{
|
||||
|
@ -26,6 +26,9 @@ namespace osu.Game.Database
|
||||
if (score.IsLegacyScore)
|
||||
return false;
|
||||
|
||||
if (score.TotalScoreVersion > 30000002)
|
||||
return false;
|
||||
|
||||
// Recalculate the old-style standardised score to see if this was an old lazer score.
|
||||
bool oldScoreMatchesExpectations = GetOldStandardised(score) == score.TotalScore;
|
||||
// Some older scores don't have correct statistics populated, so let's give them benefit of doubt.
|
||||
|
@ -229,7 +229,7 @@ namespace osu.Game.Overlays.Dialog
|
||||
{
|
||||
// Buttons are regularly added in BDL or LoadComplete, so let's schedule to ensure
|
||||
// they are ready to be pressed.
|
||||
Schedule(() => Buttons.OfType<T>().First().TriggerClick());
|
||||
Scheduler.AddOnce(() => Buttons.OfType<T>().FirstOrDefault()?.TriggerClick());
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
|
@ -49,6 +49,13 @@ namespace osu.Game.Scoring.Legacy
|
||||
|
||||
scoreInfo.IsLegacyScore = version < LegacyScoreEncoder.FIRST_LAZER_VERSION;
|
||||
|
||||
// TotalScoreVersion gets initialised to LATEST_VERSION.
|
||||
// In the case where the incoming score has either an osu!stable or old lazer version, we need
|
||||
// to mark it with the correct version increment to trigger reprocessing to new standardised scoring.
|
||||
//
|
||||
// See StandardisedScoreMigrationTools.ShouldMigrateToNewStandardised().
|
||||
scoreInfo.TotalScoreVersion = version < 30000002 ? 30000001 : LegacyScoreEncoder.LATEST_VERSION;
|
||||
|
||||
string beatmapHash = sr.ReadString();
|
||||
|
||||
workingBeatmap = GetBeatmap(beatmapHash);
|
||||
|
@ -17,6 +17,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
@ -49,6 +50,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OsuGame game { get; set; }
|
||||
|
||||
@ -251,10 +255,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
public override bool OnExiting(ScreenExitEvent e)
|
||||
{
|
||||
// the room may not be left immediately after a disconnection due to async flow,
|
||||
// so checking the IsConnected status is also required.
|
||||
if (client.Room == null || !client.IsConnected.Value)
|
||||
// so checking the MultiplayerClient / IAPIAccess statuses is also required.
|
||||
if (client.Room == null || !client.IsConnected.Value || api.State.Value != APIState.Online)
|
||||
{
|
||||
// room has not been created yet; exit immediately.
|
||||
// room has not been created yet or we're offline; exit immediately.
|
||||
return base.OnExiting(e);
|
||||
}
|
||||
|
||||
|
@ -75,8 +75,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
if (changes?.HasCollectionChanges() == false)
|
||||
return;
|
||||
|
||||
ScoreInfo? topScore = sender.Detach().OrderByTotalScore().FirstOrDefault();
|
||||
|
||||
ScoreInfo? topScore = sender.MaxBy(info => (info.TotalScore, -info.Date.UtcDateTime.Ticks));
|
||||
updateable.Rank = topScore?.Rank;
|
||||
updateable.Alpha = topScore != null ? 1 : 0;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
@ -47,7 +47,7 @@ namespace osu.Game.Updater
|
||||
{
|
||||
Text = $"A newer release of osu! has been found ({version} → {latestTagName}).\n\n"
|
||||
+ "Check with your package manager / provider to bring osu! up-to-date!",
|
||||
Icon = FontAwesome.Solid.Upload,
|
||||
Icon = FontAwesome.Solid.Download,
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
@ -54,7 +54,7 @@ namespace osu.Game.Updater
|
||||
{
|
||||
Text = $"A newer release of osu! has been found ({version} → {latestTagName}).\n\n"
|
||||
+ "Click here to download the new version, which can be installed over the top of your existing installation",
|
||||
Icon = FontAwesome.Solid.Upload,
|
||||
Icon = FontAwesome.Solid.Download,
|
||||
Activated = () =>
|
||||
{
|
||||
host.OpenUrlExternally(getBestUrl(latest));
|
||||
|
@ -134,7 +134,7 @@ namespace osu.Game.Updater
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.Solid.Upload,
|
||||
Icon = FontAwesome.Solid.Download,
|
||||
Size = new Vector2(34),
|
||||
Colour = OsuColour.Gray(0.2f),
|
||||
Depth = float.MaxValue,
|
||||
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="11.1.2" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.714.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.716.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.707.0" />
|
||||
<PackageReference Include="Sentry" Version="3.28.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
|
@ -16,6 +16,6 @@
|
||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.714.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.716.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
Loading…
Reference in New Issue
Block a user