Skip to content

Commit 822cc53

Browse files
committed
Add an option to RewindConfig to specify if the rewinder should allow out-of-order states. Currently, the default rewinder should but TAStudio rewinders should not.
Previously, TAStudio's ZwinderStateManager had to handle this, which made the code somewhat confusing. (Especially when I was looking at ZwinderBuffer and forgetting about the default rewinder.)
1 parent 014cd51 commit 822cc53

File tree

4 files changed

+24
-15
lines changed

4 files changed

+24
-15
lines changed

src/BizHawk.Client.Common/config/RewindConfig.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ public interface IRewindSettings
3434
/// </summary>
3535
int TargetRewindInterval { get; }
3636

37+
/// <summary>
38+
/// Specifies if the rewinder should accept states that are given out of order.
39+
/// </summary>
40+
bool AllowOutOfOrderStates { get; }
41+
3742
public enum BackingStoreType
3843
{
3944
Memory,
@@ -52,6 +57,8 @@ public class RewindConfig : IRewindSettings
5257
public bool UseFixedRewindInterval { get; set; } = false;
5358
public int TargetFrameLength { get; set; } = 600;
5459
public int TargetRewindInterval { get; set; } = 5;
60+
public bool AllowOutOfOrderStates { get; set; } = true;
61+
5562
public IRewindSettings.BackingStoreType BackingStore { get; set; } = IRewindSettings.BackingStoreType.Memory;
5663
}
5764
}

src/BizHawk.Client.Common/movie/tasproj/ZwinderStateManager.cs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -320,23 +320,12 @@ public void Capture(int frame, IStatable source, bool force = false)
320320
}
321321

322322
// We use the gap buffer for forced capture to avoid crowding the "current" buffer and thus reducing it's actual span of covered frames.
323-
if (force)
323+
if (NeedsGap(frame) || force)
324324
{
325325
CaptureGap(frame, source);
326326
return;
327327
}
328328

329-
// We do not want to consider reserved states for a notion of Last
330-
// reserved states can include future states in the case of branch states
331-
if (frame <= LastRing)
332-
{
333-
if (NeedsGap(frame))
334-
{
335-
CaptureGap(frame, source);
336-
}
337-
return;
338-
}
339-
340329
_current.Capture(frame,
341330
s =>
342331
{
@@ -404,6 +393,12 @@ private bool HasNearByReserved(int frame)
404393

405394
private bool NeedsGap(int frame)
406395
{
396+
// We don't want to "fill gaps" if we are past the latest state in the current/recent buffers.
397+
if (frame >= LastRing)
398+
{
399+
return false;
400+
}
401+
407402
// When starting to fill gaps we won't actually know the true frequency, so fall back to current
408403
// Current may very well not be the same as gap, but it's a reasonable behavior to have a current sized gap before seeing filler sized gaps
409404
var frequency = _gapFiller.Count == 0 ? _current.RewindFrequency : _gapFiller.RewindFrequency;

src/BizHawk.Client.Common/movie/tasproj/ZwinderStateManagerSettings.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ public RewindConfig Current()
109109
BufferSize = CurrentBufferSize,
110110
UseFixedRewindInterval = false,
111111
TargetFrameLength = CurrentTargetFrameLength,
112+
AllowOutOfOrderStates = false,
112113
BackingStore = CurrentStoreType
113114
};
114115
}
@@ -120,6 +121,7 @@ public RewindConfig Recent()
120121
BufferSize = RecentBufferSize,
121122
UseFixedRewindInterval = false,
122123
TargetFrameLength = RecentTargetFrameLength,
124+
AllowOutOfOrderStates = false,
123125
BackingStore = RecentStoreType
124126
};
125127
}
@@ -131,6 +133,7 @@ public RewindConfig GapFiller()
131133
BufferSize = GapsBufferSize,
132134
UseFixedRewindInterval = false,
133135
TargetFrameLength = GapsTargetFrameLength,
136+
AllowOutOfOrderStates = false,
134137
BackingStore = GapsStoreType
135138
};
136139
}

src/BizHawk.Client.Common/rewind/ZwinderBuffer.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public ZwinderBuffer(IRewindSettings settings)
6565
_fixedRewindInterval = false;
6666
_targetFrameLength = settings.TargetFrameLength;
6767
}
68+
_allowOutOfOrderStates = settings.AllowOutOfOrderStates;
6869
_states = new StateInfo[STATEMASK + 1];
6970
_useCompression = settings.UseCompression;
7071
}
@@ -111,6 +112,8 @@ public void Dispose()
111112
private readonly int _targetFrameLength;
112113
private readonly int _targetRewindInterval;
113114

115+
private readonly bool _allowOutOfOrderStates;
116+
114117
private struct StateInfo
115118
{
116119
public long Start;
@@ -162,6 +165,7 @@ public bool MatchesSettings(RewindConfig settings)
162165
_useCompression == settings.UseCompression &&
163166
_fixedRewindInterval == settings.UseFixedRewindInterval &&
164167
(_fixedRewindInterval ? _targetRewindInterval == settings.TargetRewindInterval : _targetFrameLength == settings.TargetFrameLength) &&
168+
_allowOutOfOrderStates == settings.AllowOutOfOrderStates &&
165169
_backingStoreType == settings.BackingStore;
166170
}
167171

@@ -173,9 +177,8 @@ private bool ShouldCaptureForFrameDiff(int frameDiff)
173177
}
174178
if (frameDiff < 1)
175179
{
176-
// non-linear time is from a combination of other state changing mechanisms and the rewinder
177-
// not much we can say here, so just take a state
178-
return true;
180+
// Manually loading a savestate can cause this. The default rewinder should capture in this situation.
181+
return _allowOutOfOrderStates;
179182
}
180183
return frameDiff >= ComputeIdealRewindInterval();
181184
}
@@ -369,6 +372,7 @@ public static ZwinderBuffer Create(BinaryReader reader, RewindConfig rewindConfi
369372
UseFixedRewindInterval = false,
370373
TargetFrameLength = targetFrameLength,
371374
TargetRewindInterval = 5,
375+
AllowOutOfOrderStates = false,
372376
UseCompression = useCompression
373377
});
374378
if (ret.Size != size || ret._sizeMask != sizeMask)

0 commit comments

Comments
 (0)