1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 11:37:28 +08:00

Merge branch 'master' into beatmap-difficulty-more-interface-usage

This commit is contained in:
Dan Balasescu 2021-10-06 12:18:51 +09:00 committed by GitHub
commit e837a3511d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 737 additions and 139 deletions

View File

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

View File

@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
Mods = mods,
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
GreatHitWindow = (int)Math.Ceiling(getHitWindow300(mods) / clockRate),
ScoreMultiplier = getScoreMultiplier(beatmap, mods),
ScoreMultiplier = getScoreMultiplier(mods),
MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1),
Skills = skills
};
@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
{
new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns)
new Strain(mods, ((ManiaBeatmap)Beatmap).TotalColumns)
};
protected override Mod[] DifficultyAdjustmentMods
@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
}
}
private double getScoreMultiplier(IBeatmap beatmap, Mod[] mods)
private double getScoreMultiplier(Mod[] mods)
{
double scoreMultiplier = 1;
@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
}
}
var maniaBeatmap = (ManiaBeatmap)beatmap;
var maniaBeatmap = (ManiaBeatmap)Beatmap;
int diff = maniaBeatmap.TotalColumns - maniaBeatmap.OriginalTotalColumns;
if (diff > 0)

View File

@ -909,7 +909,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var manager = osu.Dependencies.Get<BeatmapManager>();
var importedSet = await manager.Import(new ImportTask(temp));
var importedSet = await manager.Import(new ImportTask(temp)).ConfigureAwait(false);
ensureLoaded(osu);
@ -924,7 +924,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var manager = osu.Dependencies.Get<BeatmapManager>();
var importedSet = await manager.Import(new ImportTask(temp));
var importedSet = await manager.Import(new ImportTask(temp)).ConfigureAwait(false);
ensureLoaded(osu);

View File

@ -42,7 +42,7 @@ namespace osu.Game.Tests.Online
AddAssert("response event fired", () => response != null);
AddAssert("request has response", () => request.Result == response);
AddAssert("request has response", () => request.Response == response);
}
[Test]

View File

@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestEmptyLegacyBeatmapSkinFallsBack()
{
CreateSkinTest(SkinInfo.Default, () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null));
AddUntilStep("wait for hud load", () => Player.ChildrenOfType<SkinnableTargetContainer>().All(c => c.ComponentsLoaded));
AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value));
}
@ -84,18 +85,18 @@ namespace osu.Game.Tests.Visual.Gameplay
Remove(expectedComponentsAdjustmentContainer);
return almostEqual(actualInfo, expectedInfo);
static bool almostEqual(SkinnableInfo info, SkinnableInfo other) =>
other != null
&& info.Type == other.Type
&& info.Anchor == other.Anchor
&& info.Origin == other.Origin
&& Precision.AlmostEquals(info.Position, other.Position)
&& Precision.AlmostEquals(info.Scale, other.Scale)
&& Precision.AlmostEquals(info.Rotation, other.Rotation)
&& info.Children.SequenceEqual(other.Children, new FuncEqualityComparer<SkinnableInfo>(almostEqual));
}
private static bool almostEqual(SkinnableInfo info, SkinnableInfo other) =>
other != null
&& info.Type == other.Type
&& info.Anchor == other.Anchor
&& info.Origin == other.Origin
&& Precision.AlmostEquals(info.Position, other.Position, 1)
&& Precision.AlmostEquals(info.Scale, other.Scale)
&& Precision.AlmostEquals(info.Rotation, other.Rotation)
&& info.Children.SequenceEqual(other.Children, new FuncEqualityComparer<SkinnableInfo>(almostEqual));
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
=> new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin);

View File

@ -0,0 +1,108 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD;
using osuTK;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestScenePerformancePointsCounter : OsuTestScene
{
[Cached]
private GameplayState gameplayState;
[Cached]
private ScoreProcessor scoreProcessor;
private int iteration;
private PerformancePointsCounter counter;
public TestScenePerformancePointsCounter()
{
var ruleset = CreateRuleset();
Debug.Assert(ruleset != null);
var beatmap = CreateWorkingBeatmap(ruleset.RulesetInfo)
.GetPlayableBeatmap(ruleset.RulesetInfo);
gameplayState = new GameplayState(beatmap, ruleset);
scoreProcessor = new ScoreProcessor();
}
protected override Ruleset CreateRuleset() => new OsuRuleset();
[SetUpSteps]
public void SetUpSteps()
{
AddStep("Create counter", () =>
{
iteration = 0;
Child = counter = new PerformancePointsCounter
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(5),
};
});
}
[Test]
public void TestBasicCounting()
{
int previousValue = 0;
AddAssert("counter displaying zero", () => counter.Current.Value == 0);
AddRepeatStep("Add judgement", applyOneJudgement, 10);
AddUntilStep("counter non-zero", () => counter.Current.Value > 0);
AddUntilStep("counter opaque", () => counter.Child.Alpha == 1);
AddStep("Revert judgement", () =>
{
previousValue = counter.Current.Value;
scoreProcessor.RevertResult(new JudgementResult(new HitObject(), new OsuJudgement()));
});
AddUntilStep("counter decreased", () => counter.Current.Value < previousValue);
AddStep("Add judgement", applyOneJudgement);
AddUntilStep("counter non-zero", () => counter.Current.Value > 0);
}
private void applyOneJudgement()
{
var scoreInfo = gameplayState.Score.ScoreInfo;
scoreInfo.MaxCombo = iteration * 1000;
scoreInfo.Accuracy = 1;
scoreInfo.Statistics[HitResult.Great] = iteration * 1000;
scoreProcessor.ApplyResult(new OsuJudgementResult(new HitObject
{
StartTime = iteration * 10000,
}, new OsuJudgement())
{
Type = HitResult.Perfect,
});
iteration++;
}
}
}

View File

@ -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.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Online;
@ -8,34 +9,29 @@ using osu.Game.Online.API.Requests.Responses;
using osu.Game.Scoring;
using osu.Game.Users;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osu.Game.Screens.Ranking;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
[TestFixture]
public class TestSceneReplayDownloadButton : OsuTestScene
public class TestSceneReplayDownloadButton : OsuManualInputManagerTestScene
{
[Resolved]
private RulesetStore rulesets { get; set; }
private TestReplayDownloadButton downloadButton;
public TestSceneReplayDownloadButton()
[Test]
public void TestDisplayStates()
{
createButton(true);
AddStep(@"downloading state", () => downloadButton.SetDownloadState(DownloadState.Downloading));
AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable));
AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded));
createButton(false);
createButtonNoScore();
}
private void createButton(bool withReplay)
{
AddStep(withReplay ? @"create button with replay" : "create button without replay", () =>
AddStep(@"create button with replay", () =>
{
Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(withReplay))
Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(true))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -43,9 +39,81 @@ namespace osu.Game.Tests.Visual.Gameplay
});
AddUntilStep("wait for load", () => downloadButton.IsLoaded);
AddStep(@"downloading state", () => downloadButton.SetDownloadState(DownloadState.Downloading));
AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable));
AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded));
}
private void createButtonNoScore()
[Test]
public void TestButtonWithReplayStartsDownload()
{
bool downloadStarted = false;
bool downloadFinished = false;
AddStep(@"create button with replay", () =>
{
downloadStarted = false;
downloadFinished = false;
Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(true))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
downloadButton.State.BindValueChanged(state =>
{
switch (state.NewValue)
{
case DownloadState.Downloading:
downloadStarted = true;
break;
}
switch (state.OldValue)
{
case DownloadState.Downloading:
downloadFinished = true;
break;
}
});
});
AddUntilStep("wait for load", () => downloadButton.IsLoaded);
AddAssert("state is available", () => downloadButton.State.Value == DownloadState.NotDownloaded);
AddStep("click button", () =>
{
InputManager.MoveMouseTo(downloadButton);
InputManager.Click(MouseButton.Left);
});
AddAssert("state entered downloading", () => downloadStarted);
AddUntilStep("state left downloading", () => downloadFinished);
}
[Test]
public void TestButtonWithoutReplay()
{
AddStep("create button without replay", () =>
{
Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(false))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
});
AddUntilStep("wait for load", () => downloadButton.IsLoaded);
AddAssert("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded);
AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
}
[Test]
public void CreateButtonWithNoScore()
{
AddStep("create button with null score", () =>
{
@ -57,6 +125,9 @@ namespace osu.Game.Tests.Visual.Gameplay
});
AddUntilStep("wait for load", () => downloadButton.IsLoaded);
AddAssert("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded);
AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
}
private ScoreInfo getScoreInfo(bool replayAvailable)
@ -78,6 +149,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public void SetDownloadState(DownloadState state) => State.Value = state;
public new Bindable<DownloadState> State => base.State;
public TestReplayDownloadButton(ScoreInfo score)
: base(score)
{

View File

@ -6,6 +6,7 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
@ -68,6 +69,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
LoadScreen(dependenciesScreen = new DependenciesScreen(client));
});
AddUntilStep("wait for dependencies screen", () => Stack.CurrentScreen is DependenciesScreen);
AddUntilStep("wait for dependencies to start load", () => dependenciesScreen.LoadState > LoadState.NotLoaded);
AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded);
AddStep("load multiplayer", () => LoadScreen(multiplayerScreen));

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Textures;
@ -123,6 +124,13 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("ruleset unchanged", () => ReferenceEquals(Ruleset.Value, ruleset));
}
[Test]
public void TestSwitchThreadExecutionMode()
{
AddStep("Change thread mode to multi threaded", () => { game.Dependencies.Get<FrameworkConfigManager>().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.MultiThreaded); });
AddStep("Change thread mode to single thread", () => { game.Dependencies.Get<FrameworkConfigManager>().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.SingleThread); });
}
[Test]
public void TestUnavailableRulesetHandled()
{

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -18,6 +19,7 @@ using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Chat;
using osu.Game.Overlays;
using osu.Game.Overlays.Chat;
using osu.Game.Overlays.Chat.Selection;
using osu.Game.Overlays.Chat.Tabs;
using osu.Game.Users;
@ -41,6 +43,9 @@ namespace osu.Game.Tests.Visual.Online
private Channel channel2 => channels[1];
private Channel channel3 => channels[2];
[CanBeNull]
private Func<Channel, List<Message>> onGetMessages;
[Resolved]
private GameHost host { get; set; }
@ -79,6 +84,8 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep("register request handling", () =>
{
onGetMessages = null;
((DummyAPIAccess)API).HandleRequest = req =>
{
switch (req)
@ -102,6 +109,12 @@ namespace osu.Game.Tests.Visual.Online
}
return true;
case GetMessagesRequest getMessages:
var messages = onGetMessages?.Invoke(getMessages.Channel);
if (messages != null)
getMessages.TriggerSuccess(messages);
return true;
}
return false;
@ -122,14 +135,37 @@ namespace osu.Game.Tests.Visual.Online
}
[Test]
public void TestSelectingChannelClosesSelector()
public void TestChannelSelection()
{
AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
AddStep("Setup get message response", () => onGetMessages = channel =>
{
if (channel == channel1)
{
return new List<Message>
{
new Message(1)
{
ChannelId = channel1.Id,
Content = "hello from channel 1!",
Sender = new User
{
Id = 2,
Username = "test_user"
}
}
};
}
return null;
});
AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
AddAssert("Current channel is channel 1", () => currentChannel == channel1);
AddUntilStep("Loading spinner hidden", () => chatOverlay.ChildrenOfType<LoadingSpinner>().All(spinner => !spinner.IsPresent));
AddAssert("Channel message shown", () => chatOverlay.ChildrenOfType<ChatLine>().Count() == 1);
AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
}

View File

@ -714,10 +714,11 @@ namespace osu.Game.Tests.Visual.SongSelect
});
FilterableDifficultyIcon difficultyIcon = null;
AddStep("Find an icon for different ruleset", () =>
AddUntilStep("Find an icon for different ruleset", () =>
{
difficultyIcon = set.ChildrenOfType<FilterableDifficultyIcon>()
.First(icon => icon.Item.BeatmapInfo.Ruleset.ID == 3);
.FirstOrDefault(icon => icon.Item.BeatmapInfo.Ruleset.ID == 3);
return difficultyIcon != null;
});
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);

View File

@ -162,6 +162,8 @@ namespace osu.Game.Tests.Visual.UserInterface
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for fetch", () => leaderboard.Scores != null);
AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scoreBeingDeleted.OnlineScoreID));
}

View File

@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.UserInterface
var req = new GetBeatmapSetRequest(1);
api.Queue(req);
AddUntilStep("wait for api response", () => req.Result != null);
AddUntilStep("wait for api response", () => req.Response != null);
TestUpdateableBeatmapBackgroundSprite background = null;
@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Child = background = new TestUpdateableBeatmapBackgroundSprite
{
RelativeSizeAxes = Axes.Both,
Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Result?.ToBeatmapSet(rulesets) } }
Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Response?.ToBeatmapSet(rulesets) } }
};
});

View File

@ -182,7 +182,7 @@ namespace osu.Game.Tournament
{
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
API.Perform(req);
b.BeatmapInfo = req.Result?.ToBeatmapInfo(RulesetStore);
b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore);
addedInfo = true;
}
@ -203,7 +203,7 @@ namespace osu.Game.Tournament
{
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
req.Perform(API);
b.BeatmapInfo = req.Result?.ToBeatmapInfo(RulesetStore);
b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore);
addedInfo = true;
}

View File

@ -17,6 +17,7 @@ using osu.Framework.Utils;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
@ -147,6 +148,14 @@ namespace osu.Game.Beatmaps
}, token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
}
public Task<List<TimedDifficultyAttributes>> GetTimedDifficultyAttributesAsync(WorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken token = default)
{
return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods),
token,
TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously,
updateScheduler);
}
/// <summary>
/// Retrieves the <see cref="DifficultyRating"/> that describes a star rating.
/// </summary>

View File

@ -78,7 +78,7 @@ namespace osu.Game.Beatmaps
// intentionally blocking to limit web request concurrency
api.Perform(req);
var res = req.Result;
var res = req.Response;
if (res != null)
{

View File

@ -31,7 +31,7 @@ namespace osu.Game.Beatmaps
/// <summary>
/// The beatmap set this beatmap is part of.
/// </summary>
IBeatmapSetInfo BeatmapSet { get; }
IBeatmapSetInfo? BeatmapSet { get; }
/// <summary>
/// The playable length in milliseconds of this beatmap.

View File

@ -81,7 +81,7 @@ namespace osu.Game.Beatmaps
/// <returns>The applicable <see cref="IBeatmapConverter"/>.</returns>
protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap);
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
{
using (var cancellationSource = createCancellationTokenSource(timeout))
{

View File

@ -197,7 +197,7 @@ namespace osu.Game.Database
else
{
notification.CompletionText = imported.Count == 1
? $"Imported {imported.First()}!"
? $"Imported {imported.First().Value}!"
: $"Imported {imported.Count} {HumanisedModelName}s!";
if (imported.Count > 0 && PostImport != null)

View File

@ -115,7 +115,7 @@ namespace osu.Game.Database
createNewTask();
}
List<User> foundUsers = request.Result?.Users;
List<User> foundUsers = request.Response?.Users;
if (foundUsers != null)
{

View File

@ -25,7 +25,9 @@ namespace osu.Game.Graphics.UserInterface
set => current.Current = value;
}
private SpriteText displayedCountSpriteText;
private IHasText displayedCountText;
public Drawable DrawableCount { get; private set; }
/// <summary>
/// If true, the roll-up duration will be proportional to change in value.
@ -72,16 +74,16 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load()
{
displayedCountSpriteText = CreateSpriteText();
displayedCountText = CreateText();
UpdateDisplay();
Child = displayedCountSpriteText;
Child = DrawableCount = (Drawable)displayedCountText;
}
protected void UpdateDisplay()
{
if (displayedCountSpriteText != null)
displayedCountSpriteText.Text = FormatCount(DisplayedCount);
if (displayedCountText != null)
displayedCountText.Text = FormatCount(DisplayedCount);
}
protected override void LoadComplete()
@ -160,6 +162,15 @@ namespace osu.Game.Graphics.UserInterface
this.TransformTo(nameof(DisplayedCount), newValue, rollingTotalDuration, RollingEasing);
}
/// <summary>
/// Creates the text. Delegates to <see cref="CreateSpriteText"/> by default.
/// </summary>
protected virtual IHasText CreateText() => CreateSpriteText();
/// <summary>
/// Creates an <see cref="OsuSpriteText"/> which may be used to display this counter's text.
/// May not be called if <see cref="CreateText"/> is overridden.
/// </summary>
protected virtual OsuSpriteText CreateSpriteText() => new OsuSpriteText
{
Font = OsuFont.Numeric.With(size: 40f),

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using JetBrains.Annotations;
using Newtonsoft.Json;
using osu.Framework.IO.Network;
using osu.Framework.Logging;
@ -17,7 +18,11 @@ namespace osu.Game.Online.API
{
protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest<T>(Uri);
public T Result { get; private set; }
/// <summary>
/// The deserialised response object. May be null if the request or deserialisation failed.
/// </summary>
[CanBeNull]
public T Response { get; private set; }
/// <summary>
/// Invoked on successful completion of an API request.
@ -27,21 +32,21 @@ namespace osu.Game.Online.API
protected APIRequest()
{
base.Success += () => Success?.Invoke(Result);
base.Success += () => Success?.Invoke(Response);
}
protected override void PostProcess()
{
base.PostProcess();
Result = ((OsuJsonWebRequest<T>)WebRequest)?.ResponseObject;
Response = ((OsuJsonWebRequest<T>)WebRequest)?.ResponseObject;
}
internal void TriggerSuccess(T result)
{
if (Result != null)
if (Response != null)
throw new InvalidOperationException("Attempted to trigger success more than once");
Result = result;
Response = result;
TriggerSuccess();
}

View File

@ -8,13 +8,13 @@ namespace osu.Game.Online.API.Requests
{
public class GetMessagesRequest : APIRequest<List<Message>>
{
private readonly Channel channel;
public readonly Channel Channel;
public GetMessagesRequest(Channel channel)
{
this.channel = channel;
Channel = channel;
}
protected override string Target => $@"chat/channels/{channel.Id}/messages";
protected override string Target => $@"chat/channels/{Channel.Id}/messages";
}
}

View File

@ -15,7 +15,7 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty("count")]
public int PlayCount { get; set; }
[JsonProperty]
[JsonProperty("beatmap")]
private BeatmapInfo beatmapInfo { get; set; }
[JsonProperty]

View File

@ -13,24 +13,23 @@ using osu.Framework.Development;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.API;
using osu.Framework.Graphics.Performance;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Framework.Threading;
using osu.Framework.Platform;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Input;
using osu.Game.Input.Bindings;
using osu.Game.IO;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.Chat;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Spectator;
@ -160,8 +159,6 @@ namespace osu.Game
private readonly BindableNumber<double> globalTrackVolumeAdjust = new BindableNumber<double>(GLOBAL_TRACK_VOLUME_ADJUST);
private IBindable<GameThreadState> updateThreadState;
public OsuGameBase()
{
UseDevelopmentServer = DebugUtils.IsDebugBuild;
@ -189,9 +186,6 @@ namespace osu.Game
dependencies.Cache(realmFactory = new RealmContextFactory(Storage, "client"));
updateThreadState = Host.UpdateThread.State.GetBoundCopy();
updateThreadState.BindValueChanged(updateThreadStateChanged);
AddInternal(realmFactory);
dependencies.CacheAs(Storage);
@ -372,23 +366,6 @@ namespace osu.Game
AddFont(Resources, @"Fonts/Venera/Venera-Black");
}
private IDisposable blocking;
private void updateThreadStateChanged(ValueChangedEvent<GameThreadState> state)
{
switch (state.NewValue)
{
case GameThreadState.Running:
blocking?.Dispose();
blocking = null;
break;
case GameThreadState.Paused:
blocking = realmFactory.BlockAllOperations();
break;
}
}
protected override void LoadComplete()
{
base.LoadComplete();

View File

@ -285,7 +285,7 @@ namespace osu.Game.Overlays
return;
// check once more to ensure the channel hasn't since been removed from the loaded channels list (may have been left by some automated means).
if (loadedChannels.Contains(loaded))
if (!loadedChannels.Contains(loaded))
return;
loading.Hide();

View File

@ -143,19 +143,27 @@ namespace osu.Game.Overlays
switch (request)
{
case GetUserRankingsRequest userRequest:
if (userRequest.Response == null)
return null;
switch (userRequest.Type)
{
case UserRankingsType.Performance:
return new PerformanceTable(1, userRequest.Result.Users);
return new PerformanceTable(1, userRequest.Response.Users);
case UserRankingsType.Score:
return new ScoresTable(1, userRequest.Result.Users);
return new ScoresTable(1, userRequest.Response.Users);
}
return null;
case GetCountryRankingsRequest countryRequest:
return new CountriesTable(1, countryRequest.Result.Countries);
{
if (countryRequest.Response == null)
return null;
return new CountriesTable(1, countryRequest.Response.Countries);
}
}
return null;

View File

@ -7,6 +7,8 @@ using System.Linq;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods;
@ -16,6 +18,14 @@ namespace osu.Game.Rulesets.Difficulty
{
public abstract class DifficultyCalculator
{
/// <summary>
/// The beatmap for which difficulty will be calculated.
/// </summary>
protected IBeatmap Beatmap { get; private set; }
private Mod[] playableMods;
private double clockRate;
private readonly Ruleset ruleset;
private readonly WorkingBeatmap beatmap;
@ -32,14 +42,45 @@ namespace osu.Game.Rulesets.Difficulty
/// <returns>A structure describing the difficulty of the beatmap.</returns>
public DifficultyAttributes Calculate(params Mod[] mods)
{
mods = mods.Select(m => m.DeepClone()).ToArray();
preProcess(mods);
IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
var skills = CreateSkills(Beatmap, playableMods, clockRate);
var track = new TrackVirtual(10000);
mods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(track));
if (!Beatmap.HitObjects.Any())
return CreateDifficultyAttributes(Beatmap, playableMods, skills, clockRate);
return calculate(playableBeatmap, mods, track.Rate);
foreach (var hitObject in getDifficultyHitObjects())
{
foreach (var skill in skills)
skill.ProcessInternal(hitObject);
}
return CreateDifficultyAttributes(Beatmap, playableMods, skills, clockRate);
}
public List<TimedDifficultyAttributes> CalculateTimed(params Mod[] mods)
{
preProcess(mods);
var attribs = new List<TimedDifficultyAttributes>();
if (!Beatmap.HitObjects.Any())
return attribs;
var skills = CreateSkills(Beatmap, playableMods, clockRate);
var progressiveBeatmap = new ProgressiveCalculationBeatmap(Beatmap);
foreach (var hitObject in getDifficultyHitObjects())
{
progressiveBeatmap.HitObjects.Add(hitObject.BaseObject);
foreach (var skill in skills)
skill.ProcessInternal(hitObject);
attribs.Add(new TimedDifficultyAttributes(hitObject.EndTime, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate)));
}
return attribs;
}
/// <summary>
@ -57,24 +98,23 @@ namespace osu.Game.Rulesets.Difficulty
}
}
private DifficultyAttributes calculate(IBeatmap beatmap, Mod[] mods, double clockRate)
/// <summary>
/// Retrieves the <see cref="DifficultyHitObject"/>s to calculate against.
/// </summary>
private IEnumerable<DifficultyHitObject> getDifficultyHitObjects() => SortObjects(CreateDifficultyHitObjects(Beatmap, clockRate));
/// <summary>
/// Performs required tasks before every calculation.
/// </summary>
/// <param name="mods">The original list of <see cref="Mod"/>s.</param>
private void preProcess(Mod[] mods)
{
var skills = CreateSkills(beatmap, mods, clockRate);
playableMods = mods.Select(m => m.DeepClone()).ToArray();
Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
if (!beatmap.HitObjects.Any())
return CreateDifficultyAttributes(beatmap, mods, skills, clockRate);
var difficultyHitObjects = SortObjects(CreateDifficultyHitObjects(beatmap, clockRate)).ToList();
foreach (var hitObject in difficultyHitObjects)
{
foreach (var skill in skills)
{
skill.ProcessInternal(hitObject);
}
}
return CreateDifficultyAttributes(beatmap, mods, skills, clockRate);
var track = new TrackVirtual(10000);
mods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(track));
clockRate = track.Rate;
}
/// <summary>
@ -86,7 +126,7 @@ namespace osu.Game.Rulesets.Difficulty
=> input.OrderBy(h => h.BaseObject.StartTime);
/// <summary>
/// Creates all <see cref="Mod"/> combinations which adjust the <see cref="Beatmap"/> difficulty.
/// Creates all <see cref="Mod"/> combinations which adjust the <see cref="Beatmaps.Beatmap"/> difficulty.
/// </summary>
public Mod[] CreateDifficultyAdjustmentModCombinations()
{
@ -154,14 +194,15 @@ namespace osu.Game.Rulesets.Difficulty
}
/// <summary>
/// Retrieves all <see cref="Mod"/>s which adjust the <see cref="Beatmap"/> difficulty.
/// Retrieves all <see cref="Mod"/>s which adjust the <see cref="Beatmaps.Beatmap"/> difficulty.
/// </summary>
protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty<Mod>();
/// <summary>
/// Creates <see cref="DifficultyAttributes"/> to describe beatmap's calculated difficulty.
/// </summary>
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty was calculated.</param>
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty was calculated.
/// This may differ from <see cref="Beatmap"/> in the case of timed calculation.</param>
/// <param name="mods">The <see cref="Mod"/>s that difficulty was calculated with.</param>
/// <param name="skills">The skills which processed the beatmap.</param>
/// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
@ -178,10 +219,51 @@ namespace osu.Game.Rulesets.Difficulty
/// <summary>
/// Creates the <see cref="Skill"/>s to calculate the difficulty of an <see cref="IBeatmap"/>.
/// </summary>
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty will be calculated.</param>
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty will be calculated.
/// This may differ from <see cref="Beatmap"/> in the case of timed calculation.</param>
/// <param name="mods">Mods to calculate difficulty with.</param>
/// <param name="clockRate">Clockrate to calculate difficulty with.</param>
/// <returns>The <see cref="Skill"/>s.</returns>
protected abstract Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate);
/// <summary>
/// Used to calculate timed difficulty attributes, where only a subset of hitobjects should be visible at any point in time.
/// </summary>
private class ProgressiveCalculationBeatmap : IBeatmap
{
private readonly IBeatmap baseBeatmap;
public ProgressiveCalculationBeatmap(IBeatmap baseBeatmap)
{
this.baseBeatmap = baseBeatmap;
}
public readonly List<HitObject> HitObjects = new List<HitObject>();
IReadOnlyList<HitObject> IBeatmap.HitObjects => HitObjects;
#region Delegated IBeatmap implementation
public BeatmapInfo BeatmapInfo
{
get => baseBeatmap.BeatmapInfo;
set => baseBeatmap.BeatmapInfo = value;
}
public ControlPointInfo ControlPointInfo
{
get => baseBeatmap.ControlPointInfo;
set => baseBeatmap.ControlPointInfo = value;
}
public BeatmapMetadata Metadata => baseBeatmap.Metadata;
public List<BreakPeriod> Breaks => baseBeatmap.Breaks;
public double TotalBreakTime => baseBeatmap.TotalBreakTime;
public IEnumerable<BeatmapStatistic> GetStatistics() => baseBeatmap.GetStatistics();
public double GetMostCommonBeatLength() => baseBeatmap.GetMostCommonBeatLength();
public IBeatmap Clone() => new ProgressiveCalculationBeatmap(baseBeatmap.Clone());
#endregion
}
}
}

View File

@ -0,0 +1,25 @@
// 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;
namespace osu.Game.Rulesets.Difficulty
{
/// <summary>
/// Wraps a <see cref="DifficultyAttributes"/> object and adds a time value for which the attribute is valid.
/// Output by <see cref="DifficultyCalculator.CalculateTimed"/>.
/// </summary>
public class TimedDifficultyAttributes : IComparable<TimedDifficultyAttributes>
{
public readonly double Time;
public readonly DifficultyAttributes Attributes;
public TimedDifficultyAttributes(double time, DifficultyAttributes attributes)
{
Time = time;
Attributes = attributes;
}
public int CompareTo(TimedDifficultyAttributes other) => Time.CompareTo(other.Time);
}
}

View File

@ -28,7 +28,7 @@ namespace osu.Game.Rulesets
/// </summary>
string InstantiationInfo { get; }
public Ruleset? CreateInstance()
Ruleset? CreateInstance()
{
var type = Type.GetType(InstantiationInfo);

View File

@ -18,6 +18,11 @@ namespace osu.Game.Rulesets.Scoring
/// </summary>
public event Action<JudgementResult> NewJudgement;
/// <summary>
/// Invoked when a judgement is reverted, usually due to rewinding gameplay.
/// </summary>
public event Action<JudgementResult> JudgementReverted;
/// <summary>
/// The maximum number of hits that can be judged.
/// </summary>
@ -71,6 +76,8 @@ namespace osu.Game.Rulesets.Scoring
JudgedHits--;
RevertResultInternal(result);
JudgementReverted?.Invoke(result);
}
/// <summary>

View File

@ -66,7 +66,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
req.Failure += exception =>
{
onError?.Invoke(req.Result?.Error ?? exception.Message);
onError?.Invoke(req.Response?.Error ?? exception.Message);
};
api.Queue(req);

View File

@ -1,12 +1,14 @@
// 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 osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
#nullable enable
@ -30,7 +32,12 @@ namespace osu.Game.Screens.Play
/// <summary>
/// The mods applied to the gameplay.
/// </summary>
public IReadOnlyList<Mod> Mods;
public readonly IReadOnlyList<Mod> Mods;
/// <summary>
/// The gameplay score.
/// </summary>
public readonly Score Score;
/// <summary>
/// A bindable tracking the last judgement result applied to any hit object.
@ -39,11 +46,12 @@ namespace osu.Game.Screens.Play
private readonly Bindable<JudgementResult> lastJudgementResult = new Bindable<JudgementResult>();
public GameplayState(IBeatmap beatmap, Ruleset ruleset, IReadOnlyList<Mod> mods)
public GameplayState(IBeatmap beatmap, Ruleset ruleset, IReadOnlyList<Mod>? mods = null, Score? score = null)
{
Beatmap = beatmap;
Ruleset = ruleset;
Mods = mods;
Score = score ?? new Score();
Mods = mods ?? ArraySegment<Mod>.Empty;
}
/// <summary>

View File

@ -0,0 +1,219 @@
// 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.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Screens.Play.HUD
{
public class PerformancePointsCounter : RollingCounter<int>, ISkinnableDrawable
{
public bool UsesFixedAnchor { get; set; }
protected override bool IsRollingProportional => true;
protected override double RollingDuration => 1000;
private const float alpha_when_invalid = 0.3f;
[CanBeNull]
[Resolved(CanBeNull = true)]
private ScoreProcessor scoreProcessor { get; set; }
[Resolved(CanBeNull = true)]
[CanBeNull]
private GameplayState gameplayState { get; set; }
[CanBeNull]
private List<TimedDifficultyAttributes> timedAttributes;
private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource();
private JudgementResult lastJudgement;
public PerformancePointsCounter()
{
Current.Value = DisplayedCount = 0;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache)
{
Colour = colours.BlueLighter;
if (gameplayState != null)
{
var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap);
difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, gameplayState.Mods.ToArray(), loadCancellationSource.Token)
.ContinueWith(r => Schedule(() =>
{
timedAttributes = r.Result;
IsValid = true;
if (lastJudgement != null)
onJudgementChanged(lastJudgement);
}), TaskContinuationOptions.OnlyOnRanToCompletion);
}
}
protected override void LoadComplete()
{
base.LoadComplete();
if (scoreProcessor != null)
{
scoreProcessor.NewJudgement += onJudgementChanged;
scoreProcessor.JudgementReverted += onJudgementChanged;
}
}
private bool isValid;
protected bool IsValid
{
set
{
if (value == isValid)
return;
isValid = value;
DrawableCount.FadeTo(isValid ? 1 : alpha_when_invalid, 1000, Easing.OutQuint);
}
}
private void onJudgementChanged(JudgementResult judgement)
{
lastJudgement = judgement;
var attrib = getAttributeAtTime(judgement);
if (gameplayState == null || attrib == null)
{
IsValid = false;
return;
}
var calculator = gameplayState.Ruleset.CreatePerformanceCalculator(attrib, gameplayState.Score.ScoreInfo);
Current.Value = (int)Math.Round(calculator?.Calculate() ?? 0, MidpointRounding.AwayFromZero);
IsValid = true;
}
[CanBeNull]
private DifficultyAttributes getAttributeAtTime(JudgementResult judgement)
{
if (timedAttributes == null || timedAttributes.Count == 0)
return null;
int attribIndex = timedAttributes.BinarySearch(new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null));
if (attribIndex < 0)
attribIndex = ~attribIndex - 1;
return timedAttributes[Math.Clamp(attribIndex, 0, timedAttributes.Count - 1)].Attributes;
}
protected override LocalisableString FormatCount(int count) => count.ToString(@"D");
protected override IHasText CreateText() => new TextComponent
{
Alpha = alpha_when_invalid
};
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (scoreProcessor != null)
scoreProcessor.NewJudgement -= onJudgementChanged;
loadCancellationSource?.Cancel();
}
private class TextComponent : CompositeDrawable, IHasText
{
public LocalisableString Text
{
get => text.Text;
set => text.Text = value;
}
private readonly OsuSpriteText text;
public TextComponent()
{
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(2),
Children = new Drawable[]
{
text = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.Numeric.With(size: 16)
},
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Text = @"pp",
Font = OsuFont.Numeric.With(size: 8),
Padding = new MarginPadding { Bottom = 1.5f }, // align baseline better
}
}
};
}
}
// TODO: This class shouldn't exist, but requires breaking changes to allow DifficultyCalculator to receive an IBeatmap.
private class GameplayWorkingBeatmap : WorkingBeatmap
{
private readonly IBeatmap gameplayBeatmap;
public GameplayWorkingBeatmap(IBeatmap gameplayBeatmap)
: base(gameplayBeatmap.BeatmapInfo, null)
{
this.gameplayBeatmap = gameplayBeatmap;
}
public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
=> gameplayBeatmap;
protected override IBeatmap GetBeatmap() => gameplayBeatmap;
protected override Texture GetBackground() => throw new NotImplementedException();
protected override Track GetBeatmapTrack() => throw new NotImplementedException();
protected internal override ISkin GetSkin() => throw new NotImplementedException();
public override Stream GetStream(string storagePath) => throw new NotImplementedException();
}
}
}

View File

@ -161,13 +161,6 @@ namespace osu.Game.Screens.Play
if (!LoadedBeatmapSuccessfully)
return;
Score = CreateScore();
// ensure the score is in a consistent state with the current player.
Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo;
Score.ScoreInfo.Ruleset = ruleset.RulesetInfo;
Score.ScoreInfo.Mods = Mods.Value.ToArray();
PrepareReplay();
ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo);
@ -225,7 +218,14 @@ namespace osu.Game.Screens.Play
InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime);
dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, Mods.Value));
Score = CreateScore(playableBeatmap);
// ensure the score is in a consistent state with the current player.
Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo;
Score.ScoreInfo.Ruleset = ruleset.RulesetInfo;
Score.ScoreInfo.Mods = Mods.Value.ToArray();
dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, Mods.Value, Score));
AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer));
@ -988,8 +988,9 @@ namespace osu.Game.Screens.Play
/// <summary>
/// Creates the player's <see cref="Scoring.Score"/>.
/// </summary>
/// <param name="beatmap"></param>
/// <returns>The <see cref="Scoring.Score"/>.</returns>
protected virtual Score CreateScore() => new Score
protected virtual Score CreateScore(IBeatmap beatmap) => new Score
{
ScoreInfo = new ScoreInfo { User = api.LocalUser.Value },
};

View File

@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play
DrawableRuleset?.SetReplayScore(Score);
}
protected override Score CreateScore() => createScore(GameplayState.Beatmap, Mods.Value);
protected override Score CreateScore(IBeatmap beatmap) => createScore(beatmap, Mods.Value);
// Don't re-import replay scores as they're already present in the database.
protected override Task ImportScore(Score score) => Task.CompletedTask;

View File

@ -4,6 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Spectator;
@ -79,7 +80,7 @@ namespace osu.Game.Screens.Play
NonFrameStableSeek(score.Replay.Frames[0].Time);
}
protected override Score CreateScore() => score;
protected override Score CreateScore(IBeatmap beatmap) => score;
protected override ResultsScreen CreateResults(ScoreInfo score)
=> new SpectatorResultsScreen(score);

View File

@ -40,7 +40,7 @@ namespace osu.Game.Screens.Ranking
}
[BackgroundDependencyLoader(true)]
private void load(OsuGame game, ScoreModelDownloader scores)
private void load(OsuGame game, ScoreManager scores)
{
InternalChild = shakeContainer = new ShakeContainer
{
@ -60,7 +60,7 @@ namespace osu.Game.Screens.Ranking
break;
case DownloadState.NotDownloaded:
scores.Download(Model.Value);
scores.Download(Model.Value, false);
break;
case DownloadState.Importing:

View File

@ -68,6 +68,7 @@ namespace osu.Game.Skinning
var score = container.OfType<DefaultScoreCounter>().FirstOrDefault();
var accuracy = container.OfType<DefaultAccuracyCounter>().FirstOrDefault();
var combo = container.OfType<DefaultComboCounter>().FirstOrDefault();
var ppCounter = container.OfType<PerformancePointsCounter>().FirstOrDefault();
if (score != null)
{
@ -81,6 +82,13 @@ namespace osu.Game.Skinning
score.Position = new Vector2(0, vertical_offset);
if (ppCounter != null)
{
ppCounter.Y = score.Position.Y + ppCounter.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).Y - 4;
ppCounter.Origin = Anchor.TopCentre;
ppCounter.Anchor = Anchor.TopCentre;
}
if (accuracy != null)
{
accuracy.Position = new Vector2(-accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 - horizontal_padding, vertical_offset + 5);
@ -123,6 +131,7 @@ namespace osu.Game.Skinning
new SongProgress(),
new BarHitErrorMeter(),
new BarHitErrorMeter(),
new PerformancePointsCounter()
}
};

View File

@ -4,6 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Framework.Testing;
using osu.Game.Overlays;
using osu.Game.Screens;
@ -32,6 +33,9 @@ namespace osu.Game.Tests.Visual
content = new Container { RelativeSizeAxes = Axes.Both },
DialogOverlay = new DialogOverlay()
});
Stack.ScreenPushed += (lastScreen, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed → {newScreen}");
Stack.ScreenExited += (lastScreen, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed ← {newScreen}");
}
protected void LoadScreen(OsuScreen screen) => Stack.Push(screen);

View File

@ -36,7 +36,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Realm" Version="10.6.0" />
<PackageReference Include="ppy.osu.Framework" Version="2021.929.0" />
<PackageReference Include="ppy.osu.Framework" Version="2021.1004.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1004.0" />
<PackageReference Include="Sentry" Version="3.9.4" />
<PackageReference Include="SharpCompress" Version="0.29.0" />

View File

@ -70,7 +70,7 @@
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.929.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.1004.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1004.0" />
</ItemGroup>
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
@ -93,7 +93,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="ppy.osu.Framework" Version="2021.929.0" />
<PackageReference Include="ppy.osu.Framework" Version="2021.1004.0" />
<PackageReference Include="SharpCompress" Version="0.28.3" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="SharpRaven" Version="2.4.0" />