mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 23:53:51 +09:00
Fix spectating when starting from a point that isn't at the beginning of the beatmap
This commit is contained in:
@ -93,9 +93,15 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public void TestSpectatingDuringGameplay()
|
public void TestSpectatingDuringGameplay()
|
||||||
{
|
{
|
||||||
start();
|
start();
|
||||||
sendFrames();
|
|
||||||
// should seek immediately to available frames
|
|
||||||
loadSpectatingScreen();
|
loadSpectatingScreen();
|
||||||
|
|
||||||
|
AddStep("advance frame count", () => nextFrame = 300);
|
||||||
|
sendFrames();
|
||||||
|
|
||||||
|
waitForPlayer();
|
||||||
|
|
||||||
|
AddUntilStep("playing from correct point in time", () => player.ChildrenOfType<DrawableRuleset>().First().FrameStableClock.CurrentTime > 30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -37,6 +37,7 @@ namespace osu.Game.Screens.Play
|
|||||||
private readonly DecoupleableInterpolatingFramedClock adjustableClock;
|
private readonly DecoupleableInterpolatingFramedClock adjustableClock;
|
||||||
|
|
||||||
private readonly double gameplayStartTime;
|
private readonly double gameplayStartTime;
|
||||||
|
private readonly bool startAtGameplayStart;
|
||||||
|
|
||||||
private readonly double firstHitObjectTime;
|
private readonly double firstHitObjectTime;
|
||||||
|
|
||||||
@ -62,10 +63,11 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private readonly FramedOffsetClock platformOffsetClock;
|
private readonly FramedOffsetClock platformOffsetClock;
|
||||||
|
|
||||||
public GameplayClockContainer(WorkingBeatmap beatmap, double gameplayStartTime)
|
public GameplayClockContainer(WorkingBeatmap beatmap, double gameplayStartTime, bool startAtGameplayStart = false)
|
||||||
{
|
{
|
||||||
this.beatmap = beatmap;
|
this.beatmap = beatmap;
|
||||||
this.gameplayStartTime = gameplayStartTime;
|
this.gameplayStartTime = gameplayStartTime;
|
||||||
|
this.startAtGameplayStart = startAtGameplayStart;
|
||||||
track = beatmap.Track;
|
track = beatmap.Track;
|
||||||
|
|
||||||
firstHitObjectTime = beatmap.Beatmap.HitObjects.First().StartTime;
|
firstHitObjectTime = beatmap.Beatmap.HitObjects.First().StartTime;
|
||||||
@ -103,16 +105,21 @@ namespace osu.Game.Screens.Play
|
|||||||
userAudioOffset.BindValueChanged(offset => userOffsetClock.Offset = offset.NewValue, true);
|
userAudioOffset.BindValueChanged(offset => userOffsetClock.Offset = offset.NewValue, true);
|
||||||
|
|
||||||
// sane default provided by ruleset.
|
// sane default provided by ruleset.
|
||||||
double startTime = Math.Min(0, gameplayStartTime);
|
double startTime = gameplayStartTime;
|
||||||
|
|
||||||
// if a storyboard is present, it may dictate the appropriate start time by having events in negative time space.
|
if (!startAtGameplayStart)
|
||||||
// this is commonly used to display an intro before the audio track start.
|
{
|
||||||
startTime = Math.Min(startTime, beatmap.Storyboard.FirstEventTime);
|
startTime = Math.Min(0, startTime);
|
||||||
|
|
||||||
// some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available.
|
// if a storyboard is present, it may dictate the appropriate start time by having events in negative time space.
|
||||||
// this is not available as an option in the live editor but can still be applied via .osu editing.
|
// this is commonly used to display an intro before the audio track start.
|
||||||
if (beatmap.BeatmapInfo.AudioLeadIn > 0)
|
startTime = Math.Min(startTime, beatmap.Storyboard.FirstEventTime);
|
||||||
startTime = Math.Min(startTime, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn);
|
|
||||||
|
// some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available.
|
||||||
|
// this is not available as an option in the live editor but can still be applied via .osu editing.
|
||||||
|
if (beatmap.BeatmapInfo.AudioLeadIn > 0)
|
||||||
|
startTime = Math.Min(startTime, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn);
|
||||||
|
}
|
||||||
|
|
||||||
Seek(startTime);
|
Seek(startTime);
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ namespace osu.Game.Screens.Play
|
|||||||
if (!ScoreProcessor.Mode.Disabled)
|
if (!ScoreProcessor.Mode.Disabled)
|
||||||
config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
|
config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
|
||||||
|
|
||||||
InternalChild = GameplayClockContainer = new GameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime);
|
InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime);
|
||||||
|
|
||||||
AddInternal(gameplayBeatmap = new GameplayBeatmap(playableBeatmap));
|
AddInternal(gameplayBeatmap = new GameplayBeatmap(playableBeatmap));
|
||||||
AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer));
|
AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer));
|
||||||
@ -288,6 +288,8 @@ namespace osu.Game.Screens.Play
|
|||||||
IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
|
IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) => new GameplayClockContainer(beatmap, gameplayStart);
|
||||||
|
|
||||||
private Drawable createUnderlayComponents() =>
|
private Drawable createUnderlayComponents() =>
|
||||||
DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both };
|
DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
public class ReplayPlayer : Player
|
public class ReplayPlayer : Player
|
||||||
{
|
{
|
||||||
private readonly Score score;
|
protected readonly Score Score;
|
||||||
|
|
||||||
// Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108)
|
// Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108)
|
||||||
protected override bool CheckModsAllowFailure() => false;
|
protected override bool CheckModsAllowFailure() => false;
|
||||||
@ -16,12 +16,12 @@ namespace osu.Game.Screens.Play
|
|||||||
public ReplayPlayer(Score score, bool allowPause = true, bool showResults = true)
|
public ReplayPlayer(Score score, bool allowPause = true, bool showResults = true)
|
||||||
: base(allowPause, showResults)
|
: base(allowPause, showResults)
|
||||||
{
|
{
|
||||||
this.score = score;
|
this.Score = score;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PrepareReplay()
|
protected override void PrepareReplay()
|
||||||
{
|
{
|
||||||
DrawableRuleset?.SetReplayScore(score);
|
DrawableRuleset?.SetReplayScore(Score);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false);
|
protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false);
|
||||||
@ -31,9 +31,9 @@ namespace osu.Game.Screens.Play
|
|||||||
var baseScore = base.CreateScore();
|
var baseScore = base.CreateScore();
|
||||||
|
|
||||||
// Since the replay score doesn't contain statistics, we'll pass them through here.
|
// Since the replay score doesn't contain statistics, we'll pass them through here.
|
||||||
score.ScoreInfo.HitEvents = baseScore.HitEvents;
|
Score.ScoreInfo.HitEvents = baseScore.HitEvents;
|
||||||
|
|
||||||
return score.ScoreInfo;
|
return Score.ScoreInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ namespace osu.Game.Screens.Play
|
|||||||
ruleset.Value = resolvedRuleset.RulesetInfo;
|
ruleset.Value = resolvedRuleset.RulesetInfo;
|
||||||
beatmap.Value = beatmaps.GetWorkingBeatmap(resolvedBeatmap);
|
beatmap.Value = beatmaps.GetWorkingBeatmap(resolvedBeatmap);
|
||||||
|
|
||||||
this.Push(new ReplayPlayerLoader(new Score
|
this.Push(new SpectatorPlayerLoader(new Score
|
||||||
{
|
{
|
||||||
ScoreInfo = scoreInfo,
|
ScoreInfo = scoreInfo,
|
||||||
Replay = replay,
|
Replay = replay,
|
||||||
|
28
osu.Game/Screens/Play/SpectatorPlayer.cs
Normal file
28
osu.Game/Screens/Play/SpectatorPlayer.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.Linq;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play
|
||||||
|
{
|
||||||
|
public class SpectatorPlayer : ReplayPlayer
|
||||||
|
{
|
||||||
|
public SpectatorPlayer(Score score)
|
||||||
|
: base(score)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart)
|
||||||
|
{
|
||||||
|
// if we already have frames, start gameplay at the point in time they exist, should they be too far into the beatmap.
|
||||||
|
double? firstFrameTime = Score.Replay.Frames.FirstOrDefault()?.Time;
|
||||||
|
|
||||||
|
if (firstFrameTime == null || firstFrameTime <= gameplayStart + 5000)
|
||||||
|
return base.CreateGameplayClockContainer(beatmap, gameplayStart);
|
||||||
|
|
||||||
|
return new GameplayClockContainer(beatmap, firstFrameTime.Value, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
osu.Game/Screens/Play/SpectatorPlayerLoader.cs
Normal file
32
osu.Game/Screens/Play/SpectatorPlayerLoader.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play
|
||||||
|
{
|
||||||
|
public class SpectatorPlayerLoader : PlayerLoader
|
||||||
|
{
|
||||||
|
public readonly ScoreInfo Score;
|
||||||
|
|
||||||
|
public SpectatorPlayerLoader(Score score)
|
||||||
|
: base(() => new SpectatorPlayer(score))
|
||||||
|
{
|
||||||
|
if (score.Replay == null)
|
||||||
|
throw new ArgumentException($"{nameof(score)} must have a non-null {nameof(score.Replay)}.", nameof(score));
|
||||||
|
|
||||||
|
Score = score.ScoreInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnEntering(IScreen last)
|
||||||
|
{
|
||||||
|
// these will be reverted thanks to PlayerLoader's lease.
|
||||||
|
Mods.Value = Score.Mods;
|
||||||
|
Ruleset.Value = Score.Ruleset;
|
||||||
|
|
||||||
|
base.OnEntering(last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user