Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Assets/Lua/GBA/SonicAdvance_CamHack.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ local addr_offY = 0x5B98
local addr_camX = 0x59D0
local addr_camY = 0x59D2

event.onexit(function() client.invisibleemulation(false) end)

while true do
client.invisibleemulation(true)
local memorystate = memorysavestate.savecorestate()
Expand All @@ -21,12 +23,11 @@ while true do
mainmemory.write_u16_le(addr_camX, Xval)
mainmemory.write_u16_le(addr_camY, Yval)

client.seekframe(emu.framecount()+1)
emu.frameadvance()
client.invisibleemulation(false)
client.seekframe(emu.framecount()+1)
emu.frameadvance()
client.invisibleemulation(true)
memorysavestate.loadcorestate(memorystate)
memorysavestate.removestate(memorystate)
-- client.invisibleemulation(false)
emu.frameadvance()
end
26 changes: 26 additions & 0 deletions Assets/Lua/seek.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-- Seek forward to a given frame.
-- This will unpause emulation, and restore the users' desired pause state when seeking is completed.
-- Note that this may interfere with TAStudio's seeking behavior.
local function force_seek_frame(frame)
local pause = client.unpause()
while emu.framecount() < frame do
-- The user may pause mid-seek, perhaps even by accident.
-- In this case, we will unpause but remember that the user wants to pause at the end.
if client.ispaused() then
pause = true
client.unpause()
end
-- Yield, not frameadvance. With frameadvance we cannot detect pauses, since frameadvance would not return.
-- This is true even if we have just called client.unpause.
emu.yield()
end

if pause then client.pause() end
end

-- Seek but without touching the pause state. Function will not return if the given frame is never reached due to the user manaully pausing/rewinding.
local function seek_frame(frame)
while emu.framecount() < frame do
emu.frameadvance()
end
end
2 changes: 1 addition & 1 deletion src/BizHawk.Client.Common/Api/Classes/EmuClientApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ public void SpeedMode(int percent)

public Point TransformPoint(Point point) => _displayManager.TransformPoint(point);

public void Unpause() => _mainForm.UnpauseEmulator();
public bool Unpause() => _mainForm.UnpauseEmulator();

public void UnpauseAv() => _mainForm.PauseAvi = false;

Expand Down
5 changes: 4 additions & 1 deletion src/BizHawk.Client.Common/Api/Interfaces/IEmuClientApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ public interface IEmuClientApi : IDisposable, IExternalApi

Point TransformPoint(Point point);

void Unpause();
/// <returns>True if <see cref="Pause"/> should be called if you want to restore the previous pause state.
/// <br/>Note that this is not the same as checking <see cref="IsPaused"/> before unpausing.
/// If the user was holding frame advance, emulation will have already been unpaused and releasing frame advance will not pause.</returns>
bool Unpause();

void UnpauseAv();

Expand Down
2 changes: 1 addition & 1 deletion src/BizHawk.Client.Common/IMainFormForApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public interface IMainFormForApi
void ToggleSound();

/// <remarks>only referenced from <see cref="EmuClientApi"/></remarks>
void UnpauseEmulator();
bool UnpauseEmulator();

/// <remarks>only referenced from <see cref="EmuClientApi"/></remarks>
event BeforeQuickLoadEventHandler QuicksaveLoad;
Expand Down
13 changes: 10 additions & 3 deletions src/BizHawk.Client.Common/lua/CommonLibs/ClientLuaLibrary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,14 @@ public string GetLuaEngine()
=> "NLua+Lua";

[LuaMethodExample("client.invisibleemulation( true );")]
[LuaMethod("invisibleemulation", "Enters/exits turbo mode and disables/enables most emulator updates.")]
[LuaMethod(
name: "invisibleemulation",
description: "Disables/enables invisible emulation, starting on the next frame. During invisible emulation:"
+ " (1) All rendering and sound is disabled, including A/V capture."
+ " (2) Emulation runs at maximum speed with turbo enabled, regardless of pause state."
+ " (3) Frame rate is not calculated."
+ " (4) With one additional frame of delay, state capture for rewind+TAStudio is disabled."
)]
public void InvisibleEmulation(bool invisible)
=> APIs.EmuClient.InvisibleEmulation(invisible);

Expand Down Expand Up @@ -304,8 +311,8 @@ public LuaTable TransformPoint(int x, int y) {
}

[LuaMethodExample("client.unpause( );")]
[LuaMethod("unpause", "Unpauses the emulator")]
public void Unpause()
[LuaMethod("unpause", "Unpauses the emulator. Returns True if client.pause should be called if you want to restore the previous pause state. Note that this is not the same as checking client.ispaused before unpausing. If the user was holding frame advance, emulation will have already been unpaused and releasing frame advance will not pause.")]
public bool Unpause()
=> APIs.EmuClient.Unpause();

[LuaMethodExample("client.unpause_av( );")]
Expand Down
2 changes: 1 addition & 1 deletion src/BizHawk.Client.EmuHawk/IMainFormForTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public interface IMainFormForTools : IDialogController
void TogglePause();

/// <remarks>referenced by 3 or more tools</remarks>
void UnpauseEmulator();
bool UnpauseEmulator();

/// <remarks>only referenced from <see cref="BasicBot"/></remarks>
void Unthrottle();
Expand Down
101 changes: 71 additions & 30 deletions src/BizHawk.Client.EmuHawk/MainForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1002,8 +1002,12 @@ public int ProgramRunLoop()
Tools.GeneralUpdateActiveExtTools();

StepRunLoop_Core();
Render();
StepRunLoop_Throttle();

if (!_invisibleEmulation)
{
Render();
StepRunLoop_Throttle();
}

// HACK: RAIntegration might peek at memory during messages
// we need this to allow memory access here, otherwise it will deadlock
Expand Down Expand Up @@ -1076,20 +1080,44 @@ protected override void Dispose(bool disposing)
private bool _emulatorPaused;
public bool EmulatorPaused
{
get => _emulatorPaused;
get => _emulatorPaused && !_unpauseByFrameAdvance;

private set
{
_didMenuPause = false; // overwritten where relevant
if (_emulatorPaused == value) return;
if (_emulatorPaused && !value) // Unpausing
{
InitializeFpsData();
}

if (value != _emulatorPaused) Tools.OnPauseToggle(value);
_emulatorPaused = value;

OnPauseToggle(value);
}
}

private bool _unpauseByFrameAdvance;

/// <summary>
/// Avoids using EmulatorPaused to handle frame advance, thus allowing Lua to unpause during a frame advance.
/// </summary>
private bool UnpauseByFrameAdvance
{
get => _unpauseByFrameAdvance;
set
{
if (_unpauseByFrameAdvance == value) return;
_unpauseByFrameAdvance = value;

OnPauseToggle(!value);
}
}

private void OnPauseToggle(bool newPauseState)
{
if (!newPauseState) // Unpausing
{
InitializeFpsData();
}

Tools.OnPauseToggle(newPauseState);
SetPauseStatusBarIcon();
}

public bool BlockFrameAdvance { get; set; }
Expand Down Expand Up @@ -1121,7 +1149,11 @@ private set
/// <item><description><see cref="ClientLuaLibrary.InvisibleEmulation(bool)"/></description></item>
/// </list>
/// </summary>
public bool InvisibleEmulation { get; set; }
public bool InvisibleEmulation { get => _invisibleUpdate; set => _invisibleEmulateNextFrame = value; }

private bool _invisibleEmulateNextFrame;
private bool _invisibleEmulation;
private bool _invisibleUpdate;

private long MouseWheelTracker;

Expand All @@ -1146,7 +1178,7 @@ private set

public bool IsSeeking => PauseOnFrame.HasValue;
private bool IsTurboSeeking => PauseOnFrame.HasValue && Config.TurboSeek;
public bool IsTurboing => InputManager.ClientControls["Turbo"] || IsTurboSeeking || InvisibleEmulation;
public bool IsTurboing => InputManager.ClientControls["Turbo"] || IsTurboSeeking || _invisibleUpdate;
public bool IsFastForwarding => InputManager.ClientControls["Fast Forward"] || IsTurboing;
public bool IsRewinding { get; private set; }

Expand Down Expand Up @@ -1341,13 +1373,13 @@ public bool RebootCore()
public void PauseEmulator()
{
EmulatorPaused = true;
SetPauseStatusBarIcon();
}

public void UnpauseEmulator()
public bool UnpauseEmulator()
{
bool ret = _emulatorPaused;
EmulatorPaused = false;
SetPauseStatusBarIcon();
return ret;
}

public void TogglePause()
Expand Down Expand Up @@ -2930,32 +2962,33 @@ private void StepRunLoop_Core(bool force = false)
// handle the initial trigger of a frame advance
runFrame = true;
_frameAdvanceTimestamp = currentTimestamp;
// Pausing is inconsistent with the behavior of TAStudio while seeking, but it's always been this way so.
PauseEmulator();
}
else if (frameProgressTimeElapsed)
{
runFrame = true;
_runloopFrameProgress = true;
UnpauseEmulator();
UnpauseByFrameAdvance = true;
}
}
else
{
if (_runloopFrameAdvance)
{
// handle release of frame advance
PauseEmulator();
UnpauseByFrameAdvance = false;
}
_runloopFrameProgress = false;
}

_runloopFrameAdvance = frameAdvance;

bool unpaused = !EmulatorPaused || _invisibleEmulation;
#if BIZHAWKBUILD_SUPERHAWK
if (!EmulatorPaused && (!Config.SuperHawkThrottle || InputManager.ClientControls.AnyInputHeld))
#else
if (!EmulatorPaused)
unpaused = unpaused && (!Config.SuperHawkThrottle || InputManager.ClientControls.AnyInputHeld);
#endif
if (unpaused)
{
runFrame = true;
}
Expand All @@ -2969,15 +3002,20 @@ private void StepRunLoop_Core(bool force = false)
// BlockFrameAdvance (true when input it being editted in TAStudio) supercedes all other frame advance conditions
if ((runFrame || force) && !BlockFrameAdvance)
{
_invisibleEmulation = _invisibleEmulateNextFrame;

var isFastForwarding = IsFastForwarding;
var isFastForwardingOrRewinding = isFastForwarding || isRewinding || Config.Unthrottled;

if (isFastForwardingOrRewinding != _lastFastForwardingOrRewinding)
if (!_invisibleEmulation)
{
InitializeFpsData();
}
if (isFastForwardingOrRewinding != _lastFastForwardingOrRewinding)
{
InitializeFpsData();
}

_lastFastForwardingOrRewinding = isFastForwardingOrRewinding;
_lastFastForwardingOrRewinding = isFastForwardingOrRewinding;
}

// client input-related duties
OSD.ClearGuiText();
Expand All @@ -2997,13 +3035,13 @@ private void StepRunLoop_Core(bool force = false)
Tools.UpdateToolsBefore();
}

if (!InvisibleEmulation)
if (!_invisibleEmulation)
{
CaptureRewind(isRewinding);
}

// Set volume, if enabled
if (Config.SoundEnabledNormal && !InvisibleEmulation)
if (Config.SoundEnabledNormal && !_invisibleEmulation)
{
atten = Config.SoundVolume / 100.0f;

Expand All @@ -3030,7 +3068,7 @@ private void StepRunLoop_Core(bool force = false)

RA?.OnFrameAdvance();

if (Config.AutosaveSaveRAM)
if (Config.AutosaveSaveRAM && !_invisibleEmulation)
{
AutoFlushSaveRamIn--;
if (AutoFlushSaveRamIn <= 0)
Expand All @@ -3048,10 +3086,10 @@ private void StepRunLoop_Core(bool force = false)
}

bool atTurboSeekEnd = IsTurboSeeking && Emulator.Frame == PauseOnFrame.Value - 1;
bool render = !InvisibleEmulation && (!_throttle.skipNextFrame || _currAviWriter?.UsesVideo is true || atTurboSeekEnd);
bool render = !_invisibleEmulation && (!_throttle.skipNextFrame || _currAviWriter?.UsesVideo is true || atTurboSeekEnd);
bool newFrame = Emulator.FrameAdvance(InputManager.ControllerOutput, render, renderSound);

MovieSession.HandleFrameAfter(ToolBypassingMovieEndAction is not null);
if (!_invisibleUpdate) MovieSession.HandleFrameAfter(ToolBypassingMovieEndAction is not null);

if (returnToRecording)
{
Expand Down Expand Up @@ -3088,13 +3126,14 @@ private void StepRunLoop_Core(bool force = false)
UpdateToolsAfter();
}
}
_invisibleUpdate = _invisibleEmulation;

if (!PauseAvi && newFrame && !InvisibleEmulation)
if (!PauseAvi && newFrame && !_invisibleEmulation)
{
AvFrameAdvance();
}

if (newFrame)
if (newFrame && !_invisibleEmulation)
{
_framesSinceLastFpsUpdate++;

Expand Down Expand Up @@ -4375,6 +4414,8 @@ private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToR

returnToRecording = false;

if (_invisibleEmulation) return false;

if (ToolControllingRewind is { } rewindTool)
{
if (InputManager.ClientControls["Rewind"] || PressRewind)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public RAIntegration(
clientVer: $"{VersionInfo.MainVersion}{(VersionInfo.DeveloperBuild ? "-dev" : string.Empty)}");

_isActive = () => !Emu.IsNull();
_unpause = _mainForm.UnpauseEmulator;
_unpause = () => _ = _mainForm.UnpauseEmulator();
_pause = _mainForm.PauseEmulator;
_rebuildMenu = RebuildMenu;
_estimateTitle = buffer =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ protected override void GeneralUpdate()

protected override void UpdateAfter()
{
if (!IsHandleCreated || IsDisposed || CurrentTasMovie == null)
if (!IsHandleCreated || IsDisposed || CurrentTasMovie == null || MainForm.InvisibleEmulation)
{
return;
}
Expand Down Expand Up @@ -109,6 +109,8 @@ protected override void UpdateAfter()

protected override void FastUpdateAfter()
{
if (MainForm.InvisibleEmulation) return;

if (_seekingTo != -1 && Emulator.Frame >= _seekingTo)
{
bool smga = _shouldMoveGreenArrow;
Expand Down
Loading