mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 10:12:54 +08:00
Merge pull request #24885 from peppy/clock-fix-attempt-2
Adjust clock usage in line with framework changes
This commit is contained in:
commit
037632940d
@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.922.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.1006.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
gameplayClock = new GameplayClockContainer(manualClock)
|
||||
gameplayClock = new GameplayClockContainer(manualClock, false, false)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
|
@ -126,7 +126,7 @@ namespace osu.Game.Tests.Gameplay
|
||||
public void TestSamplePlaybackWithBeatmapHitsoundsOff()
|
||||
{
|
||||
GameplayClockContainer gameplayContainer = null;
|
||||
TestDrawableStoryboardSample sample = null;
|
||||
DrawableStoryboardSample sample = null;
|
||||
|
||||
AddStep("disable beatmap hitsounds", () => config.SetValue(OsuSetting.BeatmapHitsounds, false));
|
||||
|
||||
@ -141,7 +141,7 @@ namespace osu.Game.Tests.Gameplay
|
||||
Child = beatmapSkinSourceContainer
|
||||
});
|
||||
|
||||
beatmapSkinSourceContainer.Add(sample = new TestDrawableStoryboardSample(new StoryboardSampleInfo("test-sample", 1, 1))
|
||||
beatmapSkinSourceContainer.Add(sample = new DrawableStoryboardSample(new StoryboardSampleInfo("test-sample", 1, 1))
|
||||
{
|
||||
Clock = gameplayContainer
|
||||
});
|
||||
@ -199,14 +199,6 @@ namespace osu.Game.Tests.Gameplay
|
||||
protected internal override ISkin GetSkin() => new TestSkin("test-sample", resources);
|
||||
}
|
||||
|
||||
private partial class TestDrawableStoryboardSample : DrawableStoryboardSample
|
||||
{
|
||||
public TestDrawableStoryboardSample(StoryboardSampleInfo sampleInfo)
|
||||
: base(sampleInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#region IResourceStorageProvider
|
||||
|
||||
public IRenderer Renderer => host.Renderer;
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -16,16 +17,16 @@ namespace osu.Game.Tests.NonVisual
|
||||
[TestCase(1)]
|
||||
public void TestTrueGameplayRateWithGameplayAdjustment(double underlyingClockRate)
|
||||
{
|
||||
var framedClock = new FramedClock(new ManualClock { Rate = underlyingClockRate });
|
||||
var gameplayClock = new TestGameplayClockContainer(framedClock);
|
||||
var trackVirtual = new TrackVirtual(60000) { Frequency = { Value = underlyingClockRate } };
|
||||
var gameplayClock = new TestGameplayClockContainer(trackVirtual);
|
||||
|
||||
Assert.That(gameplayClock.GetTrueGameplayRate(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
private partial class TestGameplayClockContainer : GameplayClockContainer
|
||||
{
|
||||
public TestGameplayClockContainer(IFrameBasedClock underlyingClock)
|
||||
: base(underlyingClock)
|
||||
public TestGameplayClockContainer(IClock underlyingClock)
|
||||
: base(underlyingClock, false, false)
|
||||
{
|
||||
AdjustmentsFromMods.AddAdjustment(AdjustableProperty.Frequency, new BindableDouble(2.0));
|
||||
}
|
||||
|
@ -15,6 +15,9 @@ using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.OnlinePlay
|
||||
{
|
||||
// NOTE: This test scene never calls ProcessFrame on clocks.
|
||||
// The current tests are fine without this as they are testing very static scenarios, but it's worth knowing
|
||||
// if adding further tests to this class.
|
||||
[HeadlessTest]
|
||||
public partial class TestSceneCatchUpSyncManager : OsuTestScene
|
||||
{
|
||||
@ -28,7 +31,7 @@ namespace osu.Game.Tests.OnlinePlay
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
syncManager = new SpectatorSyncManager(master = new GameplayClockContainer(new TestManualClock()));
|
||||
syncManager = new SpectatorSyncManager(master = new GameplayClockContainer(new TestManualClock(), false, false));
|
||||
player1 = syncManager.CreateManagedClock();
|
||||
player2 = syncManager.CreateManagedClock();
|
||||
|
||||
@ -188,6 +191,8 @@ namespace osu.Game.Tests.OnlinePlay
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
IsRunning = false;
|
||||
CurrentTime = 0;
|
||||
}
|
||||
|
||||
public void ResetSpeedAdjustments()
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddStep("seek near end", () => EditorClock.Seek(EditorClock.TrackLength - 250));
|
||||
AddUntilStep("clock stops", () => !EditorClock.IsRunning);
|
||||
|
||||
AddUntilStep("clock stopped at end", () => EditorClock.CurrentTime - EditorClock.TotalAppliedOffset, () => Is.EqualTo(EditorClock.TrackLength));
|
||||
AddUntilStep("clock stopped at end", () => EditorClock.CurrentTime, () => Is.EqualTo(EditorClock.TrackLength));
|
||||
|
||||
AddStep("start clock again", () => EditorClock.Start());
|
||||
AddAssert("clock looped to start", () => EditorClock.IsRunning && EditorClock.CurrentTime < 500);
|
||||
|
@ -6,11 +6,11 @@ using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset());
|
||||
|
||||
[Cached(typeof(IGameplayClock))]
|
||||
private readonly IGameplayClock gameplayClock = new GameplayClockContainer(new FramedClock());
|
||||
private readonly IGameplayClock gameplayClock = new GameplayClockContainer(new TrackVirtual(60000), false, false);
|
||||
|
||||
// best way to check without exposing.
|
||||
private Drawable hideTarget => hudOverlay.ChildrenOfType<SkinComponentsContainer>().First();
|
||||
|
@ -4,10 +4,10 @@
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Overlays.SkinEditor;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset());
|
||||
|
||||
[Cached(typeof(IGameplayClock))]
|
||||
private readonly IGameplayClock gameplayClock = new GameplayClockContainer(new FramedClock());
|
||||
private readonly IGameplayClock gameplayClock = new GameplayClockContainer(new TrackVirtual(60000), false, false);
|
||||
|
||||
[Cached]
|
||||
public readonly EditorClipboard Clipboard = new EditorClipboard();
|
||||
|
@ -8,11 +8,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset());
|
||||
|
||||
[Cached(typeof(IGameplayClock))]
|
||||
private readonly IGameplayClock gameplayClock = new GameplayClockContainer(new FramedClock());
|
||||
private readonly IGameplayClock gameplayClock = new GameplayClockContainer(new TrackVirtual(60000), false, false);
|
||||
|
||||
private IEnumerable<HUDOverlay> hudOverlays => CreatedDrawables.OfType<HUDOverlay>();
|
||||
|
||||
|
@ -106,14 +106,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
if (storyboard != null)
|
||||
storyboardContainer.Remove(storyboard, true);
|
||||
|
||||
var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true };
|
||||
storyboardContainer.Clock = decoupledClock;
|
||||
storyboardContainer.Clock = new FramedClock(Beatmap.Value.Track);
|
||||
|
||||
storyboard = toLoad.CreateDrawable(SelectedMods.Value);
|
||||
storyboard.Passing = false;
|
||||
|
||||
storyboardContainer.Add(storyboard);
|
||||
decoupledClock.ChangeSource(Beatmap.Value.Track);
|
||||
}
|
||||
|
||||
private void loadStoryboard(string filename, Action<Storyboard>? setUpStoryboard = null)
|
||||
|
@ -5,7 +5,6 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
@ -28,16 +27,6 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
private readonly bool applyOffsets;
|
||||
|
||||
/// <summary>
|
||||
/// The length of the underlying beatmap track. Will default to 60 seconds if unavailable.
|
||||
/// </summary>
|
||||
public double TrackLength => Track.Length;
|
||||
|
||||
/// <summary>
|
||||
/// The underlying beatmap track, if available.
|
||||
/// </summary>
|
||||
public Track Track { get; private set; } = new TrackVirtual(60000);
|
||||
|
||||
/// <summary>
|
||||
/// The total frequency adjustment from pause transforms. Should eventually be handled in a better way.
|
||||
/// </summary>
|
||||
@ -53,7 +42,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
private IDisposable? beatmapOffsetSubscription;
|
||||
|
||||
private readonly DecoupleableInterpolatingFramedClock decoupledClock;
|
||||
private readonly DecouplingFramedClock decoupledTrack;
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; } = null!;
|
||||
@ -66,25 +55,21 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public bool IsRewinding { get; private set; }
|
||||
|
||||
public bool IsCoupled
|
||||
{
|
||||
get => decoupledClock.IsCoupled;
|
||||
set => decoupledClock.IsCoupled = value;
|
||||
}
|
||||
|
||||
public FramedBeatmapClock(bool applyOffsets = false)
|
||||
public FramedBeatmapClock(bool applyOffsets, bool requireDecoupling, IClock? source = null)
|
||||
{
|
||||
this.applyOffsets = applyOffsets;
|
||||
|
||||
// A decoupled clock is used to ensure precise time values even when the host audio subsystem is not reporting
|
||||
decoupledTrack = new DecouplingFramedClock(source) { AllowDecoupling = requireDecoupling };
|
||||
|
||||
// An interpolating clock is used to ensure precise time values even when the host audio subsystem is not reporting
|
||||
// high precision times (on windows there's generally only 5-10ms reporting intervals, as an example).
|
||||
decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true };
|
||||
var interpolatedTrack = new InterpolatingFramedClock(decoupledTrack);
|
||||
|
||||
if (applyOffsets)
|
||||
{
|
||||
// Audio timings in general with newer BASS versions don't match stable.
|
||||
// This only seems to be required on windows. We need to eventually figure out why, with a bit of luck.
|
||||
platformOffsetClock = new OffsetCorrectionClock(decoupledClock, ExternalPauseFrequencyAdjust) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 15 : 0 };
|
||||
platformOffsetClock = new OffsetCorrectionClock(interpolatedTrack, ExternalPauseFrequencyAdjust) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 15 : 0 };
|
||||
|
||||
// User global offset (set in settings) should also be applied.
|
||||
userGlobalOffsetClock = new OffsetCorrectionClock(platformOffsetClock, ExternalPauseFrequencyAdjust);
|
||||
@ -94,7 +79,7 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
else
|
||||
{
|
||||
finalClockSource = decoupledClock;
|
||||
finalClockSource = interpolatedTrack;
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,6 +95,7 @@ namespace osu.Game.Beatmaps
|
||||
userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset);
|
||||
userAudioOffset.BindValueChanged(offset => userGlobalOffsetClock.Offset = offset.NewValue, true);
|
||||
|
||||
// TODO: this doesn't update when using ChangeSource() to change beatmap.
|
||||
beatmapOffsetSubscription = realm.SubscribeToPropertyChanged(
|
||||
r => r.Find<BeatmapInfo>(beatmap.Value.BeatmapInfo.ID)?.UserSettings,
|
||||
settings => settings.Offset,
|
||||
@ -124,17 +110,7 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Source != null && Source is not IAdjustableClock && Source.CurrentTime < decoupledClock.CurrentTime - 100)
|
||||
{
|
||||
// InterpolatingFramedClock won't interpolate backwards unless its source has an ElapsedFrameTime.
|
||||
// See https://github.com/ppy/osu-framework/blob/ba1385330cc501f34937e08257e586c84e35d772/osu.Framework/Timing/InterpolatingFramedClock.cs#L91-L93
|
||||
// This is not always the case here when doing large seeks.
|
||||
// (Of note, this is not an issue if the source is adjustable, as the source is seeked to be in time by DecoupleableInterpolatingFramedClock).
|
||||
// Rather than trying to get around this by fixing the framework clock stack, let's work around it for now.
|
||||
Seek(Source.CurrentTime);
|
||||
}
|
||||
else
|
||||
finalClockSource.ProcessFrame();
|
||||
finalClockSource.ProcessFrame();
|
||||
|
||||
if (Clock.ElapsedFrameTime != 0)
|
||||
IsRewinding = Clock.ElapsedFrameTime < 0;
|
||||
@ -157,46 +133,42 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
#region Delegation of IAdjustableClock / ISourceChangeableClock to decoupled clock.
|
||||
|
||||
public void ChangeSource(IClock? source)
|
||||
{
|
||||
Track = source as Track ?? new TrackVirtual(60000);
|
||||
decoupledClock.ChangeSource(source);
|
||||
}
|
||||
public void ChangeSource(IClock? source) => decoupledTrack.ChangeSource(source);
|
||||
|
||||
public IClock? Source => decoupledClock.Source;
|
||||
public IClock Source => decoupledTrack.Source;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
decoupledClock.Reset();
|
||||
decoupledTrack.Reset();
|
||||
finalClockSource.ProcessFrame();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
decoupledClock.Start();
|
||||
decoupledTrack.Start();
|
||||
finalClockSource.ProcessFrame();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
decoupledClock.Stop();
|
||||
decoupledTrack.Stop();
|
||||
finalClockSource.ProcessFrame();
|
||||
}
|
||||
|
||||
public bool Seek(double position)
|
||||
{
|
||||
bool success = decoupledClock.Seek(position - TotalAppliedOffset);
|
||||
bool success = decoupledTrack.Seek(position - TotalAppliedOffset);
|
||||
finalClockSource.ProcessFrame();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void ResetSpeedAdjustments() => decoupledClock.ResetSpeedAdjustments();
|
||||
public void ResetSpeedAdjustments() => decoupledTrack.ResetSpeedAdjustments();
|
||||
|
||||
public double Rate
|
||||
{
|
||||
get => decoupledClock.Rate;
|
||||
set => decoupledClock.Rate = value;
|
||||
get => decoupledTrack.Rate;
|
||||
set => decoupledTrack.Rate = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -215,7 +215,7 @@ namespace osu.Game
|
||||
/// For now, this is used as a source specifically for beat synced components.
|
||||
/// Going forward, it could potentially be used as the single source-of-truth for beatmap timing.
|
||||
/// </summary>
|
||||
private readonly FramedBeatmapClock beatmapClock = new FramedBeatmapClock(true);
|
||||
private readonly FramedBeatmapClock beatmapClock = new FramedBeatmapClock(applyOffsets: true, requireDecoupling: false);
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
@ -441,16 +441,7 @@ namespace osu.Game
|
||||
}
|
||||
}
|
||||
|
||||
private void onTrackChanged(WorkingBeatmap beatmap, TrackChangeDirection direction)
|
||||
{
|
||||
// FramedBeatmapClock uses a decoupled clock internally which will mutate the source if it is an `IAdjustableClock`.
|
||||
// We don't want this for now, as the intention of beatmapClock is to be a read-only source for beat sync components.
|
||||
//
|
||||
// Encapsulating in a FramedClock will avoid any mutations.
|
||||
var framedClock = new FramedClock(beatmap.Track);
|
||||
|
||||
beatmapClock.ChangeSource(framedClock);
|
||||
}
|
||||
private void onTrackChanged(WorkingBeatmap beatmap, TrackChangeDirection direction) => beatmapClock.ChangeSource(beatmap.Track);
|
||||
|
||||
protected virtual void InitialiseFonts()
|
||||
{
|
||||
|
@ -862,7 +862,7 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private void resetTrack(bool seekToStart = false)
|
||||
{
|
||||
Beatmap.Value.Track.Stop();
|
||||
clock.Stop();
|
||||
|
||||
if (seekToStart)
|
||||
{
|
||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
this.beatDivisor = beatDivisor ?? new BindableBeatDivisor();
|
||||
|
||||
underlyingClock = new FramedBeatmapClock(applyOffsets: true) { IsCoupled = false };
|
||||
underlyingClock = new FramedBeatmapClock(applyOffsets: true, requireDecoupling: true);
|
||||
AddInternal(underlyingClock);
|
||||
}
|
||||
|
||||
@ -158,8 +158,6 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
public double CurrentTime => underlyingClock.CurrentTime;
|
||||
|
||||
public double TotalAppliedOffset => underlyingClock.TotalAppliedOffset;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ClearTransforms();
|
||||
|
@ -36,7 +36,6 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private Sample welcome;
|
||||
|
||||
private DecoupleableInterpolatingFramedClock decoupledClock;
|
||||
private TrianglesIntroSequence intro;
|
||||
|
||||
public IntroTriangles([CanBeNull] Func<MainMenu> createNextScreen = null)
|
||||
@ -59,18 +58,12 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
PrepareMenuLoad();
|
||||
|
||||
decoupledClock = new DecoupleableInterpolatingFramedClock
|
||||
{
|
||||
IsCoupled = false
|
||||
};
|
||||
|
||||
if (UsingThemedIntro)
|
||||
decoupledClock.ChangeSource(Track);
|
||||
var decouplingClock = new DecouplingFramedClock(UsingThemedIntro ? Track : null);
|
||||
|
||||
LoadComponentAsync(intro = new TrianglesIntroSequence(logo, () => FadeInBackground())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = decoupledClock,
|
||||
Clock = new InterpolatingFramedClock(decouplingClock),
|
||||
LoadMenu = LoadMenu
|
||||
}, _ =>
|
||||
{
|
||||
@ -94,7 +87,7 @@ namespace osu.Game.Screens.Menu
|
||||
StartTrack();
|
||||
|
||||
// no-op for the case of themed intro, no harm in calling for both scenarios as a safety measure.
|
||||
decoupledClock.Start();
|
||||
decouplingClock.Start();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
protected override void Update()
|
||||
{
|
||||
// The player clock's running state is controlled externally, but the local pausing state needs to be updated to start/stop gameplay.
|
||||
if (GameplayClockContainer.SourceClock.IsRunning)
|
||||
if (GameplayClockContainer.IsRunning)
|
||||
GameplayClockContainer.Start();
|
||||
else
|
||||
GameplayClockContainer.Stop();
|
||||
@ -67,7 +67,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
|
||||
protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart)
|
||||
{
|
||||
var gameplayClockContainer = new GameplayClockContainer(spectatorPlayerClock);
|
||||
// Importantly, we don't want to apply decoupling because SpectatorPlayerClock updates its IsRunning directly.
|
||||
// If we applied decoupling, this state change wouldn't actually cause the clock to stop.
|
||||
// TODO: Can we just use Start/Stop rather than this workaround, now that DecouplingClock is more sane?
|
||||
var gameplayClockContainer = new GameplayClockContainer(spectatorPlayerClock, applyOffsets: false, requireDecoupling: false);
|
||||
clockAdjustmentsFromMods.BindAdjustments(gameplayClockContainer.AdjustmentsFromMods);
|
||||
return gameplayClockContainer;
|
||||
}
|
||||
|
@ -23,11 +23,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public bool IsRewinding => GameplayClock.IsRewinding;
|
||||
|
||||
/// <summary>
|
||||
/// The source clock. Should generally not be used for any timekeeping purposes.
|
||||
/// </summary>
|
||||
public IClock SourceClock { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a seek has been performed via <see cref="Seek"/>
|
||||
/// </summary>
|
||||
@ -60,15 +55,14 @@ namespace osu.Game.Screens.Play
|
||||
/// </summary>
|
||||
/// <param name="sourceClock">The source <see cref="IClock"/> used for timing.</param>
|
||||
/// <param name="applyOffsets">Whether to apply platform, user and beatmap offsets to the mix.</param>
|
||||
public GameplayClockContainer(IClock sourceClock, bool applyOffsets = false)
|
||||
/// <param name="requireDecoupling">Whether decoupling logic should be applied on the source clock.</param>
|
||||
public GameplayClockContainer(IClock sourceClock, bool applyOffsets, bool requireDecoupling)
|
||||
{
|
||||
SourceClock = sourceClock;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
GameplayClock = new FramedBeatmapClock(applyOffsets) { IsCoupled = false },
|
||||
GameplayClock = new FramedBeatmapClock(applyOffsets, requireDecoupling, sourceClock),
|
||||
Content
|
||||
};
|
||||
}
|
||||
@ -83,8 +77,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
isPaused.Value = false;
|
||||
|
||||
ensureSourceClockSet();
|
||||
|
||||
PrepareStart();
|
||||
|
||||
// The case which caused this to be added is FrameStabilityContainer, which manages its own current and elapsed time.
|
||||
@ -153,28 +145,11 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
Stop();
|
||||
|
||||
ensureSourceClockSet();
|
||||
|
||||
if (time != null)
|
||||
StartTime = time.Value;
|
||||
|
||||
Seek(StartTime);
|
||||
|
||||
// This is a workaround for the fact that DecoupleableInterpolatingFramedClock doesn't seek the source
|
||||
// if the source is not IsRunning. (see https://github.com/ppy/osu-framework/blob/2102638056dfcf85d21b4d85266d53b5dd018767/osu.Framework/Timing/DecoupleableInterpolatingFramedClock.cs#L209-L210)
|
||||
// I hope to remove this once we knock some sense into clocks in general.
|
||||
//
|
||||
// Without this seek, the multiplayer spectator start sequence breaks:
|
||||
// - Individual clients' clocks are never updated to their expected time
|
||||
// - The sync manager thinks they are running behind
|
||||
// - Gameplay doesn't start when it should (until a timeout occurs because nothing is happening for 10+ seconds)
|
||||
//
|
||||
// In addition, we use `CurrentTime` for this seek instead of `StartTime` as the above seek may have applied inherent
|
||||
// offsets which need to be accounted for (ie. FramedBeatmapClock.TotalAppliedOffset).
|
||||
//
|
||||
// See https://github.com/ppy/osu/pull/24451/files/87fee001c786b29db34063ef3350e9a9f024d3ab#diff-28ca02979641e2d98a15fe5d5e806f56acf60ac100258a059fa72503b6cc54e8.
|
||||
(SourceClock as IAdjustableClock)?.Seek(CurrentTime);
|
||||
|
||||
if (!wasPaused || startClock)
|
||||
Start();
|
||||
}
|
||||
@ -183,20 +158,7 @@ namespace osu.Game.Screens.Play
|
||||
/// Changes the source clock.
|
||||
/// </summary>
|
||||
/// <param name="sourceClock">The new source.</param>
|
||||
protected void ChangeSource(IClock sourceClock) => GameplayClock.ChangeSource(SourceClock = sourceClock);
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the <see cref="GameplayClock"/> is set to <see cref="SourceClock"/>, if it hasn't been given a source yet.
|
||||
/// This is usually done before a seek to avoid accidentally seeking only the adjustable source in decoupled mode,
|
||||
/// but not the actual source clock.
|
||||
/// That will pretty much only happen on the very first call of this method, as the source clock is passed in the constructor,
|
||||
/// but it is not yet set on the adjustable source there.
|
||||
/// </summary>
|
||||
private void ensureSourceClockSet()
|
||||
{
|
||||
if (GameplayClock.Source == null)
|
||||
ChangeSource(SourceClock);
|
||||
}
|
||||
protected void ChangeSource(IClock sourceClock) => GameplayClock.ChangeSource(sourceClock);
|
||||
|
||||
#region IAdjustableClock
|
||||
|
||||
|
@ -65,7 +65,7 @@ namespace osu.Game.Screens.Play
|
||||
/// <param name="beatmap">The beatmap to be used for time and metadata references.</param>
|
||||
/// <param name="skipTargetTime">The latest time which should be used when introducing gameplay. Will be used when skipping forward.</param>
|
||||
public MasterGameplayClockContainer(WorkingBeatmap beatmap, double skipTargetTime)
|
||||
: base(beatmap.Track, true)
|
||||
: base(beatmap.Track, applyOffsets: true, requireDecoupling: true)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
this.skipTargetTime = skipTargetTime;
|
||||
@ -187,7 +187,13 @@ namespace osu.Game.Screens.Play
|
||||
public void StopUsingBeatmapClock()
|
||||
{
|
||||
removeSourceClockAdjustments();
|
||||
ChangeSource(new TrackVirtual(beatmap.Track.Length));
|
||||
|
||||
var virtualTrack = new TrackVirtual(beatmap.Track.Length);
|
||||
virtualTrack.Seek(CurrentTime);
|
||||
if (IsRunning)
|
||||
virtualTrack.Start();
|
||||
ChangeSource(virtualTrack);
|
||||
|
||||
addSourceClockAdjustments();
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="11.5.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.922.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.1006.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.1003.0" />
|
||||
<PackageReference Include="Sentry" Version="3.39.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.33.0" />
|
||||
|
@ -23,6 +23,6 @@
|
||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.922.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.1006.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
Loading…
Reference in New Issue
Block a user