Skip to content

Commit

Permalink
fix: Playback not accounting for TAS recording
Browse files Browse the repository at this point in the history
  • Loading branch information
psyGamer committed Feb 21, 2025
1 parent a3131e4 commit eccfd12
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ private void ProcessRecordTAS(string fileName) {
WriteRecordingFailed(RecordingFailedReason.TASRecorderNotInstalled);
return;
}
if (!TASRecorderInterop.FFmpegInstalled) {
if (!TASRecorderInterop.IsFFmpegInstalled) {
WriteRecordingFailed(RecordingFailedReason.FFmpegNotInstalled);
return;
}
Expand Down
40 changes: 31 additions & 9 deletions CelesteTAS-EverestInterop/Source/ModInterop/TASRecorderInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,47 @@

namespace TAS.ModInterop;

public static class TASRecorderInterop {
internal static class TASRecorderInterop {
public static bool Installed => installed.Value;
private static readonly Lazy<bool> installed = new(() => ModUtils.IsInstalled("TASRecorder"));

public static void StartRecording(string fileName = null) {
if (installed.Value) startRecording(fileName);
/// Starts a recording.
/// If <see cref="IsRecording"/> is true or <see cref="IsFFmpegInstalled"/> is false, this shouldn't be called.
/// <param name="fileName">The file name of the recording. If <c>null</c>, it's generated from "dd-MM-yyyy_HH-mm-ss"</param>
public static void StartRecording(string? fileName = null) {
if (Installed) {
startRecording(fileName);
}
}

/// Stops a recording which was previously started.
/// If <see cref="IsRecording"/> or <see cref="IsFFmpegInstalled"/> is false, this shouldn't be called.
public static void StopRecording() {
if (installed.Value) stopRecording();
if (Installed) {
stopRecording();
}
}
public static void SetDurationEstimate(int frames) {
if (installed.Value) setDurationEstimate(frames);

/// Sets the estimated amount of total frames.
/// This is used for the progress bar, but doesn't actually interact with the recording.
/// If <see cref="IsRecording"/> or <see cref="IsFFmpegInstalled"/> is false, this shouldn't be called.
/// <param name="frames">The total amount of frames, excluding loading times. If set to <c>null</c>, there isn't a progress bar.</param>
public static void SetDurationEstimate(int? frames) {
if (Installed) {
setDurationEstimate(frames ?? TASRecorderAPI.NoEstimate);
}
}

public static bool Recording => installed.Value && isRecording();
public static bool FFmpegInstalled => installed.Value && isFFmpegInstalled();
/// Whether TAS Recorder is currently recording.
public static bool IsRecording => Installed && isRecording();

/// Whether TAS Recorder could properly load FFmpeg.
public static bool IsFFmpegInstalled => Installed && isFFmpegInstalled();

// These methods **must not** be called if 'Installed == false'

[MethodImpl(MethodImplOptions.NoInlining)]
private static void startRecording(string fileName = null) => TASRecorderAPI.StartRecording(fileName);
private static void startRecording(string? fileName = null) => TASRecorderAPI.StartRecording(fileName);
[MethodImpl(MethodImplOptions.NoInlining)]
private static void stopRecording() => TASRecorderAPI.StopRecording();
[MethodImpl(MethodImplOptions.NoInlining)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,25 @@

namespace TAS.Input.Commands;

public static class RecordingCommand {
/// Manages recording TASes to video files with TAS Recorder
internal static class RecordingCommand {
internal record RecordingTime {
public int StartFrame = int.MaxValue;
public int StopFrame = int.MaxValue;

public int Duration => StopFrame - StartFrame;
}

internal static readonly Dictionary<int, RecordingTime> RecordingTimes = new();

// workaround the first few frames get skipped when there is a breakpoint after StartRecording command
public static bool StopFastForward {
get {
if (TASRecorderInterop.Recording) {
return true;
}

return RecordingTimes.Values.Any(time => {
int currentFrame = Manager.Controller.CurrentFrameInTas;
return currentFrame > time.StartFrame - 60 && currentFrame <= time.StopFrame;
});
}
}

// "StartRecording"
/// "StartRecording"
[TasCommand("StartRecording", ExecuteTiming = ExecuteTiming.Parse | ExecuteTiming.Runtime)]
private static void StartRecording(CommandLine commandLine, int studioLine, string filePath, int fileLine) {
if (ParsingCommand) {
if (CommunicationWrapper.Connected && Manager.Running) {
if (!TASRecorderInterop.Installed) {
CommunicationWrapper.SendRecordingFailed(RecordingFailedReason.TASRecorderNotInstalled);
} else if (!TASRecorderInterop.FFmpegInstalled) {
} else if (!TASRecorderInterop.IsFFmpegInstalled) {
CommunicationWrapper.SendRecordingFailed(RecordingFailedReason.FFmpegNotInstalled);
}
}
Expand All @@ -47,8 +35,8 @@ private static void StartRecording(CommandLine commandLine, int studioLine, stri
AbortTas("TAS Recorder isn't installed");
return;
}
if (!TASRecorderInterop.FFmpegInstalled) {
AbortTas("FFmpeg libraries aren't properly installed");
if (!TASRecorderInterop.IsFFmpegInstalled) {
AbortTas("FFmpeg isn't properly installed");
return;
}

Expand All @@ -61,25 +49,24 @@ private static void StartRecording(CommandLine commandLine, int studioLine, stri
}
}

RecordingTime time = new() { StartFrame = Manager.Controller.Inputs.Count };
var time = new RecordingTime { StartFrame = Manager.Controller.Inputs.Count };
RecordingTimes[time.StartFrame] = time;
} else {
if (TASRecorderInterop.Recording) {
if (TASRecorderInterop.IsRecording) {
AbortTas("Tried to start recording, while already recording");
return;
}

TASRecorderInterop.StartRecording();
if (RecordingTimes.TryGetValue(Manager.Controller.CurrentFrameInTas, out RecordingTime time) &&
time.StartFrame != int.MaxValue && time.StopFrame != int.MaxValue) {
if (RecordingTimes.TryGetValue(Manager.Controller.CurrentFrameInTas, out var time) && time.StartFrame != int.MaxValue && time.StopFrame != int.MaxValue) {
TASRecorderInterop.SetDurationEstimate(time.Duration);
}

Manager.CurrState = Manager.NextState = Manager.State.Running;
}
}

// "StopRecording"
/// "StopRecording"
[TasCommand("StopRecording", ExecuteTiming = ExecuteTiming.Parse | ExecuteTiming.Runtime)]
private static void StopRecording(CommandLine commandLine, int studioLine, string filePath, int fileLine) {
if (ParsingCommand) {
Expand All @@ -89,8 +76,7 @@ private static void StopRecording(CommandLine commandLine, int studioLine, strin
return;
}

RecordingTime last = RecordingTimes.Last().Value;

var last = RecordingTimes.Last().Value;
if (last.StopFrame != int.MaxValue) {
if (last.StopFrame == int.MaxValue) {
AbortTas($"{errorText}StartRecording is required before another StopRecording");
Expand All @@ -110,7 +96,7 @@ private static void ParseFileEnd() {
return;
}

RecordingTime last = RecordingTimes.Last().Value;
var last = RecordingTimes.Last().Value;
if (last.StopFrame == int.MaxValue) {
last.StopFrame = Manager.Controller.Inputs.Count;
}
Expand All @@ -123,7 +109,7 @@ private static void Clear() {

[DisableRun]
private static void DisableRun() {
if (TASRecorderInterop.Recording) {
if (TASRecorderInterop.IsRecording) {
TASRecorderInterop.StopRecording();
}
}
Expand Down
12 changes: 12 additions & 0 deletions CelesteTAS-EverestInterop/Source/TAS/Manager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using TAS.EverestInterop;
using TAS.Input;
using TAS.Input.Commands;
using TAS.ModInterop;
using TAS.Module;
using TAS.Utils;

Expand Down Expand Up @@ -218,6 +219,13 @@ public static void UpdateMeta() {
return;
}

if (TASRecorderInterop.IsRecording) {
// Force recording at 1x playback
NextState = State.Running;
PlaybackSpeed = 1.0f;
return;
}

switch (CurrState) {
case State.Running:
if (Hotkeys.PauseResume.Pressed || Hotkeys.FrameAdvance.Pressed) {
Expand Down Expand Up @@ -305,6 +313,10 @@ overworld.Next is OuiChapterSelect && UserIO.Saving ||

/// Determine if current TAS file is a draft
private static bool IsDraft() {
if (TASRecorderInterop.IsRecording) {
return false;
}

// Require any FileTime or ChapterTime, alternatively MidwayFileTime or MidwayChapterTime at the end for the TAS to be counted as finished
return Controller.Commands.Values
.SelectMany(commands => commands)
Expand Down
2 changes: 1 addition & 1 deletion CelesteTAS-EverestInterop/Source/TAS/Savestates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public static void SaveState(bool byBreakpoint) {

public static void LoadState() {
// Don't load save-states while recording
if (TASRecorderInterop.Recording) {
if (TASRecorderInterop.IsRecording) {
return;
}

Expand Down

0 comments on commit eccfd12

Please sign in to comment.