diff --git a/src/BizHawk.Client.Common/lua/CommonLibs/ClientLuaLibrary.cs b/src/BizHawk.Client.Common/lua/CommonLibs/ClientLuaLibrary.cs
index 6619ea5907..05188fe281 100644
--- a/src/BizHawk.Client.Common/lua/CommonLibs/ClientLuaLibrary.cs
+++ b/src/BizHawk.Client.Common/lua/CommonLibs/ClientLuaLibrary.cs
@@ -74,6 +74,11 @@ namespace BizHawk.Client.Common
[LuaMethod("closerom", "Closes the loaded Rom")]
public void CloseRom()
{
+ if (_luaLibsImpl.IsInInputOrMemoryCallback)
+ {
+ throw new InvalidOperationException("client.closerom() is not allowed during input/memory callbacks");
+ }
+
_luaLibsImpl.IsRebootingCore = true;
APIs.EmuClient.CloseRom();
_luaLibsImpl.IsRebootingCore = false;
@@ -102,6 +107,11 @@ namespace BizHawk.Client.Common
[LuaMethod("seekframe", "Makes the emulator seek to the frame specified")]
public void SeekFrame(int frame)
{
+ if (_luaLibsImpl.IsInInputOrMemoryCallback)
+ {
+ throw new InvalidOperationException("client.seekframe() is not allowed during input/memory callbacks");
+ }
+
if (frame < Emulator.Frame)
{
Log("client.seekframe: cannot seek backwards");
@@ -200,6 +210,11 @@ namespace BizHawk.Client.Common
[LuaMethod("openrom", "Loads a ROM from the given path. Returns true if the ROM was successfully loaded, otherwise false.")]
public bool OpenRom(string path)
{
+ if (_luaLibsImpl.IsInInputOrMemoryCallback)
+ {
+ throw new InvalidOperationException("client.openrom() is not allowed during input/memory callbacks");
+ }
+
_luaLibsImpl.IsRebootingCore = true;
var success = APIs.EmuClient.OpenRom(path);
_luaLibsImpl.IsRebootingCore = false;
@@ -235,6 +250,11 @@ namespace BizHawk.Client.Common
[LuaMethod("reboot_core", "Reboots the currently loaded core")]
public void RebootCore()
{
+ if (_luaLibsImpl.IsInInputOrMemoryCallback)
+ {
+ throw new InvalidOperationException("client.reboot_core() is not allowed during input/memory callbacks");
+ }
+
_luaLibsImpl.IsRebootingCore = true;
APIs.EmuClient.RebootCore();
_luaLibsImpl.IsRebootingCore = false;
diff --git a/src/BizHawk.Client.Common/lua/CommonLibs/MemorySavestateLuaLibrary.cs b/src/BizHawk.Client.Common/lua/CommonLibs/MemorySavestateLuaLibrary.cs
index 5544a88017..58f9cec115 100644
--- a/src/BizHawk.Client.Common/lua/CommonLibs/MemorySavestateLuaLibrary.cs
+++ b/src/BizHawk.Client.Common/lua/CommonLibs/MemorySavestateLuaLibrary.cs
@@ -14,21 +14,35 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local mmsvstsvcst = memorysavestate.savecorestate( );")]
[LuaMethod("savecorestate", "creates a core savestate and stores it in memory. Note: a core savestate is only the raw data from the core, and not extras such as movie input logs, or framebuffers. Returns a unique identifer for the savestate")]
public string SaveCoreStateToMemory()
- => APIs.MemorySaveState.SaveCoreStateToMemory();
+ {
+ if (_luaLibsImpl.IsInInputOrMemoryCallback)
+ {
+ throw new InvalidOperationException("memorysavestate.savecorestate() is not allowed during input/memory callbacks");
+ }
+
+ return APIs.MemorySaveState!.SaveCoreStateToMemory();
+ }
[LuaMethodExample("memorysavestate.loadcorestate( \"3fcf120f-0778-43fd-b2c5-460fb7d34184\" );")]
[LuaMethod("loadcorestate", "loads an in memory state with the given identifier")]
public void LoadCoreStateFromMemory(string identifier)
- => APIs.MemorySaveState.LoadCoreStateFromMemory(identifier);
+ {
+ if (_luaLibsImpl.IsInInputOrMemoryCallback)
+ {
+ throw new InvalidOperationException("memorysavestate.loadcorestate() is not allowed during input/memory callbacks");
+ }
+
+ APIs.MemorySaveState!.LoadCoreStateFromMemory(identifier);
+ }
[LuaMethodExample("memorysavestate.removestate( \"3fcf120f-0778-43fd-b2c5-460fb7d34184\" );")]
[LuaMethod("removestate", "removes the savestate with the given identifier from memory")]
public void DeleteState(string identifier)
- => APIs.MemorySaveState.DeleteState(identifier);
+ => APIs.MemorySaveState!.DeleteState(identifier);
[LuaMethodExample("memorysavestate.clearstatesfrommemory( );")]
[LuaMethod("clearstatesfrommemory", "clears all savestates stored in memory")]
public void ClearInMemoryStates()
- => APIs.MemorySaveState.ClearInMemoryStates();
+ => APIs.MemorySaveState!.ClearInMemoryStates();
}
}
diff --git a/src/BizHawk.Client.Common/lua/CommonLibs/MovieLuaLibrary.cs b/src/BizHawk.Client.Common/lua/CommonLibs/MovieLuaLibrary.cs
index c77d116ed9..577f74999a 100644
--- a/src/BizHawk.Client.Common/lua/CommonLibs/MovieLuaLibrary.cs
+++ b/src/BizHawk.Client.Common/lua/CommonLibs/MovieLuaLibrary.cs
@@ -71,6 +71,11 @@ namespace BizHawk.Client.Common
[LuaMethod("play_from_start", "Resets the core to frame 0 with the currently loaded movie in playback mode. If a path to a movie is specified, attempts to load it, then continues with playback if it was successful. Returns true iff successful.")]
public bool PlayFromStart(string path = "")
{
+ if (_luaLibsImpl.IsInInputOrMemoryCallback)
+ {
+ throw new InvalidOperationException("movie.play_from_start() is not allowed during input/memory callbacks");
+ }
+
_luaLibsImpl.IsRebootingCore = true;
var success = APIs.Movie.PlayFromStart(path);
_luaLibsImpl.IsRebootingCore = false;
diff --git a/src/BizHawk.Client.Common/lua/CommonLibs/SaveStateLuaLibrary.cs b/src/BizHawk.Client.Common/lua/CommonLibs/SaveStateLuaLibrary.cs
index 5d9c00e7c6..edca18f644 100644
--- a/src/BizHawk.Client.Common/lua/CommonLibs/SaveStateLuaLibrary.cs
+++ b/src/BizHawk.Client.Common/lua/CommonLibs/SaveStateLuaLibrary.cs
@@ -13,6 +13,11 @@ namespace BizHawk.Client.Common
[LuaMethod("load", "Loads a savestate with the given path. Returns true iff succeeded. If EmuHawk is deferring quicksaves, to TAStudio for example, that form will do what it likes (and the path is ignored).")]
public bool Load(string path, bool suppressOSD = false)
{
+ if (_luaLibsImpl.IsInInputOrMemoryCallback)
+ {
+ throw new InvalidOperationException("savestate.load() is not allowed during input/memory callbacks");
+ }
+
_luaLibsImpl.IsUpdateSupressed = true;
var success = APIs.SaveState.Load(path, suppressOSD);
_luaLibsImpl.IsUpdateSupressed = false;
@@ -23,6 +28,11 @@ namespace BizHawk.Client.Common
[LuaMethod("loadslot", "Loads the savestate at the given slot number (must be an integer between 1 and 10). Returns true iff succeeded. If EmuHawk is deferring quicksaves, to TAStudio for example, that form will do what it likes with the slot number.")]
public bool LoadSlot(int slotNum, bool suppressOSD = false)
{
+ if (_luaLibsImpl.IsInInputOrMemoryCallback)
+ {
+ throw new InvalidOperationException("savestate.loadslot() is not allowed during input/memory callbacks");
+ }
+
_luaLibsImpl.IsUpdateSupressed = true;
var success = APIs.SaveState.LoadSlot(slotNum, suppressOSD: suppressOSD);
_luaLibsImpl.IsUpdateSupressed = false;
@@ -32,11 +42,25 @@ namespace BizHawk.Client.Common
[LuaMethodExample("savestate.save( \"C:\\state.bin\" );")]
[LuaMethod("save", "Saves a state at the given path. If EmuHawk is deferring quicksaves, to TAStudio for example, that form will do what it likes (and the path is ignored).")]
public void Save(string path, bool suppressOSD = false)
- => APIs.SaveState.Save(path, suppressOSD);
+ {
+ if (_luaLibsImpl.IsInInputOrMemoryCallback)
+ {
+ throw new InvalidOperationException("savestate.save() is not allowed during input/memory callbacks");
+ }
+
+ APIs.SaveState.Save(path, suppressOSD);
+ }
[LuaMethodExample("savestate.saveslot( 7 );")]
[LuaMethod("saveslot", "Saves a state at the given save slot (must be an integer between 1 and 10). If EmuHawk is deferring quicksaves, to TAStudio for example, that form will do what it likes with the slot number.")]
public void SaveSlot(int slotNum, bool suppressOSD = false)
- => APIs.SaveState.SaveSlot(slotNum, suppressOSD);
+ {
+ if (_luaLibsImpl.IsInInputOrMemoryCallback)
+ {
+ throw new InvalidOperationException("savestate.saveslot() is not allowed during input/memory callbacks");
+ }
+
+ APIs.SaveState.SaveSlot(slotNum, suppressOSD);
+ }
}
}
diff --git a/src/BizHawk.Client.Common/lua/ILuaLibraries.cs b/src/BizHawk.Client.Common/lua/ILuaLibraries.cs
index d03ec1dfc6..2a36e2df45 100644
--- a/src/BizHawk.Client.Common/lua/ILuaLibraries.cs
+++ b/src/BizHawk.Client.Common/lua/ILuaLibraries.cs
@@ -17,6 +17,9 @@ namespace BizHawk.Client.Common
bool IsUpdateSupressed { get; set; }
+ /// not really sure if this is the right place to put it, multiple different places need this...
+ bool IsInInputOrMemoryCallback { get; set; }
+
LuaFunctionList RegisteredFunctions { get; }
public PathEntryCollection PathEntries { get; }
diff --git a/src/BizHawk.Client.Common/lua/NamedLuaFunction.cs b/src/BizHawk.Client.Common/lua/NamedLuaFunction.cs
index 324bed0036..d24657083c 100644
--- a/src/BizHawk.Client.Common/lua/NamedLuaFunction.cs
+++ b/src/BizHawk.Client.Common/lua/NamedLuaFunction.cs
@@ -31,12 +31,14 @@ namespace BizHawk.Client.Common
private readonly LuaFunction _function;
- public NamedLuaFunction(LuaFunction function, string theEvent, Action logCallback, LuaFile luaFile, Func createThreadCallback, string name = null)
+ public NamedLuaFunction(LuaFunction function, string theEvent, Action logCallback, LuaFile luaFile,
+ Func createThreadCallback, ILuaLibraries luaLibraries, string name = null)
{
_function = function;
Name = name ?? "Anonymous";
Event = theEvent;
CreateThreadCallback = createThreadCallback;
+ LuaLibraries = luaLibraries;
// When would a file be null?
// When a script is loaded with a callback, but no infinite loop so it closes
@@ -66,8 +68,30 @@ namespace BizHawk.Client.Common
logCallback($"error running function attached by the event {Event}\nError message: {ex.Message}");
}
};
- InputCallback = () => Callback(Array.Empty