1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-12 06:07:28 +08:00
osu-lazer/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs

152 lines
5.1 KiB
C#
Raw Normal View History

2019-03-29 11:38:47 +08:00
// 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.
2018-04-13 17:19:50 +08:00
using System;
using System.Collections.Generic;
2019-03-31 00:33:56 +08:00
using JetBrains.Annotations;
2018-07-21 10:38:28 +08:00
using osu.Framework.Input.StateChanges;
2018-04-13 17:19:50 +08:00
using osu.Game.Input.Handlers;
2018-11-28 16:20:37 +08:00
using osu.Game.Replays;
2018-11-20 15:51:59 +08:00
using osuTK;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Rulesets.Replays
{
/// <summary>
/// The ReplayHandler will take a replay and handle the propagation of updates to the input stack.
/// It handles logic of any frames which *must* be executed.
/// </summary>
public abstract class FramedReplayInputHandler<TFrame> : ReplayInputHandler
where TFrame : ReplayFrame
{
private readonly Replay replay;
protected List<ReplayFrame> Frames => replay.Frames;
public TFrame CurrentFrame
{
get
{
if (!HasFrames || !currentFrameIndex.HasValue)
return null;
return (TFrame)Frames[currentFrameIndex.Value];
}
}
public TFrame NextFrame
{
get
{
if (!HasFrames)
return null;
if (!currentFrameIndex.HasValue)
return (TFrame)Frames[0];
if (currentDirection > 0)
return currentFrameIndex == Frames.Count - 1 ? null : (TFrame)Frames[currentFrameIndex.Value + 1];
else
return currentFrameIndex == 0 ? null : (TFrame)Frames[nextFrameIndex];
}
}
2018-04-13 17:19:50 +08:00
private int? currentFrameIndex;
2018-04-13 17:19:50 +08:00
private int nextFrameIndex => currentFrameIndex.HasValue ? MathHelper.Clamp(currentFrameIndex.Value + (currentDirection > 0 ? 1 : -1), 0, Frames.Count - 1) : 0;
2018-04-13 17:19:50 +08:00
protected FramedReplayInputHandler(Replay replay)
{
this.replay = replay;
}
private bool advanceFrame()
{
int newFrame = nextFrameIndex;
//ensure we aren't at an extent.
if (newFrame == currentFrameIndex) return false;
currentFrameIndex = newFrame;
return true;
}
2018-06-11 22:00:26 +08:00
public override List<IInput> GetPendingInputs() => new List<IInput>();
2018-04-13 17:19:50 +08:00
private const double sixty_frame_time = 1000.0 / 60;
protected virtual double AllowedImportantTimeSpan => sixty_frame_time * 1.2;
protected double? CurrentTime { get; private set; }
2018-04-13 17:19:50 +08:00
private int currentDirection;
/// <summary>
/// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data.
/// Disabling this can make replay playback smoother (useful for autoplay, currently).
/// </summary>
public bool FrameAccuratePlayback = false;
2018-04-13 17:19:50 +08:00
protected bool HasFrames => Frames.Count > 0;
2019-03-31 00:33:56 +08:00
private bool inImportantSection
{
get
{
if (!HasFrames || !FrameAccuratePlayback)
return false;
2019-04-01 09:37:02 +08:00
var frame = currentDirection > 0 ? CurrentFrame : NextFrame;
2019-03-31 00:33:56 +08:00
2019-04-01 09:37:02 +08:00
if (frame == null)
2019-03-31 00:33:56 +08:00
return false;
2019-04-01 09:37:02 +08:00
return IsImportant(frame) && //a button is in a pressed state
2019-03-31 00:33:56 +08:00
Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= AllowedImportantTimeSpan; //the next frame is within an allowable time span
}
}
2018-04-13 17:19:50 +08:00
2019-03-31 00:33:56 +08:00
protected virtual bool IsImportant([NotNull] TFrame frame) => false;
2018-04-13 17:19:50 +08:00
/// <summary>
/// Update the current frame based on an incoming time value.
/// There are cases where we return a "must-use" time value that is different from the input.
/// This is to ensure accurate playback of replay data.
/// </summary>
/// <param name="time">The time which we should use for finding the current frame.</param>
/// <returns>The usable time value. If null, we should not advance time as we do not have enough data.</returns>
public override double? SetFrameFromTime(double time)
{
if (!CurrentTime.HasValue)
{
currentDirection = 1;
}
else
{
currentDirection = time.CompareTo(CurrentTime);
if (currentDirection == 0) currentDirection = 1;
}
2018-04-13 17:19:50 +08:00
if (HasFrames)
{
// check if the next frame is valid for the current playback direction.
// validity is if the next frame is equal or "earlier"
var compare = time.CompareTo(NextFrame?.Time);
if (compare == 0 || compare == currentDirection)
{
if (advanceFrame())
return CurrentTime = CurrentFrame.Time;
}
else
2018-04-13 17:19:50 +08:00
{
// if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null.
if (inImportantSection)
return null;
}
}
return CurrentTime = time;
}
}
}