fix passing numbers for string args in .net lua functions (old engine had this behavior, granted "bad user" if they relied on this), add appropriate test

fix passing sbyte/char as args, add appropriate tests
cleanup the lua auto unlock hack, using a nice ref struct + dispose to handle it
This commit is contained in:
CasualPokePlayer 2022-12-05 23:21:23 -08:00
parent 920682688b
commit eb00019c86
5 changed files with 113 additions and 82 deletions

Binary file not shown.

Binary file not shown.

View File

@ -151,11 +151,22 @@ namespace BizHawk.Client.Common
} }
} }
public void ThisIsTheLuaAutounlockHack() public readonly ref struct LuaAutoUnlockHack
{ {
UnlockSurface(DisplaySurfaceID.EmuCore); private readonly GuiApi _guiApi;
UnlockSurface(DisplaySurfaceID.Client);
internal LuaAutoUnlockHack(GuiApi guiApi)
=> _guiApi = guiApi;
public void Dispose()
{
_guiApi.UnlockSurface(DisplaySurfaceID.EmuCore);
_guiApi.UnlockSurface(DisplaySurfaceID.Client);
} }
}
public LuaAutoUnlockHack ThisIsTheLuaAutoUnlockHack()
=> new(this);
public void DrawNew(string name, bool clear) public void DrawNew(string name, bool clear)
{ {

View File

@ -184,6 +184,8 @@ namespace BizHawk.Client.EmuHawk
public LuaFunctionList RegisteredFunctions { get; } public LuaFunctionList RegisteredFunctions { get; }
public void CallSaveStateEvent(string name) public void CallSaveStateEvent(string name)
{
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
{ {
try try
{ {
@ -191,16 +193,17 @@ namespace BizHawk.Client.EmuHawk
{ {
lf.Call(name); lf.Call(name);
} }
GuiAPI.ThisIsTheLuaAutounlockHack();
} }
catch (Exception e) catch (Exception e)
{ {
GuiAPI.ThisIsTheLuaAutounlockHack();
LogToLuaConsole($"error running function attached by lua function event.onsavestate\nError message: {e.Message}"); LogToLuaConsole($"error running function attached by lua function event.onsavestate\nError message: {e.Message}");
} }
} }
}
public void CallLoadStateEvent(string name) public void CallLoadStateEvent(string name)
{
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
{ {
try try
{ {
@ -208,52 +211,57 @@ namespace BizHawk.Client.EmuHawk
{ {
lf.Call(name); lf.Call(name);
} }
GuiAPI.ThisIsTheLuaAutounlockHack();
} }
catch (Exception e) catch (Exception e)
{ {
GuiAPI.ThisIsTheLuaAutounlockHack();
LogToLuaConsole($"error running function attached by lua function event.onloadstate\nError message: {e.Message}"); LogToLuaConsole($"error running function attached by lua function event.onloadstate\nError message: {e.Message}");
} }
} }
}
public void CallFrameBeforeEvent() public void CallFrameBeforeEvent()
{ {
if (IsUpdateSupressed) return; if (IsUpdateSupressed) return;
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
{
try try
{ {
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_PREFRAME).ToList()) foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_PREFRAME).ToList())
{ {
lf.Call(); lf.Call();
} }
GuiAPI.ThisIsTheLuaAutounlockHack();
} }
catch (Exception e) catch (Exception e)
{ {
GuiAPI.ThisIsTheLuaAutounlockHack();
LogToLuaConsole($"error running function attached by lua function event.onframestart\nError message: {e.Message}"); LogToLuaConsole($"error running function attached by lua function event.onframestart\nError message: {e.Message}");
} }
} }
}
public void CallFrameAfterEvent() public void CallFrameAfterEvent()
{ {
if (IsUpdateSupressed) return; if (IsUpdateSupressed) return;
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
{
try try
{ {
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_POSTFRAME).ToList()) foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_POSTFRAME).ToList())
{ {
lf.Call(); lf.Call();
} }
GuiAPI.ThisIsTheLuaAutounlockHack();
} }
catch (Exception e) catch (Exception e)
{ {
GuiAPI.ThisIsTheLuaAutounlockHack();
LogToLuaConsole($"error running function attached by lua function event.onframeend\nError message: {e.Message}"); LogToLuaConsole($"error running function attached by lua function event.onframeend\nError message: {e.Message}");
} }
} }
}
public void CallExitEvent(LuaFile lf) public void CallExitEvent(LuaFile lf)
{
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
{ {
foreach (var exitCallback in RegisteredFunctions foreach (var exitCallback in RegisteredFunctions
.Where(l => l.Event == NamedLuaFunction.EVENT_TYPE_ENGINESTOP .Where(l => l.Event == NamedLuaFunction.EVENT_TYPE_ENGINESTOP
@ -262,7 +270,7 @@ namespace BizHawk.Client.EmuHawk
{ {
exitCallback.Call(); exitCallback.Call();
} }
GuiAPI.ThisIsTheLuaAutounlockHack(); }
} }
public void Close() public void Close()
@ -318,6 +326,8 @@ namespace BizHawk.Client.EmuHawk
=> _lua.DoString(command); => _lua.DoString(command);
public (bool WaitForFrame, bool Terminated) ResumeScript(LuaFile lf) public (bool WaitForFrame, bool Terminated) ResumeScript(LuaFile lf)
{
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
{ {
_currThread = lf.Thread; _currThread = lf.Thread;
@ -326,26 +336,24 @@ namespace BizHawk.Client.EmuHawk
LuaLibraryBase.SetCurrentThread(lf); LuaLibraryBase.SetCurrentThread(lf);
var execResult = _currThread.Resume(); var execResult = _currThread.Resume();
GuiAPI.ThisIsTheLuaAutounlockHack();
_currThread = null; _currThread = null;
var result = execResult == KeraLua.LuaStatus.OK var result = execResult switch
? (WaitForFrame: false, Terminated: true) // terminated {
: (WaitForFrame: FrameAdvanceRequested, Terminated: false); // yielded KeraLua.LuaStatus.OK => (WaitForFrame: false, Terminated: true),
KeraLua.LuaStatus.Yield => (WaitForFrame: FrameAdvanceRequested, Terminated: false),
_ => throw new InvalidOperationException($"{nameof(_currThread.Resume)}() returned {execResult}?")
};
FrameAdvanceRequested = false; FrameAdvanceRequested = false;
return result; return result;
} }
catch (Exception)
{
GuiAPI.ThisIsTheLuaAutounlockHack();
throw;
}
finally finally
{ {
LuaLibraryBase.ClearCurrentThread(); LuaLibraryBase.ClearCurrentThread();
} }
} }
}
public static void Print(params object[] outputs) public static void Print(params object[] outputs)
{ {

View File

@ -591,7 +591,9 @@ namespace BizHawk.Tests.Client.Common.Lua
public void Net_Return_Char() public void Net_Return_Char()
{ {
ReturnValue = 'a'; ReturnValue = 'a';
Assert.IsTrue((bool)LuaInstance.DoString($"return return_char() == {(byte)'a'}")[0]); Assert.IsTrue((bool)LuaInstance.DoString($"return return_char() == {(ushort)'a'}")[0]);
ReturnValue = 'こ';
Assert.IsTrue((bool)LuaInstance.DoString($"return return_char() == {(ushort)'こ'}")[0]);
} }
[TestMethod] [TestMethod]
@ -612,7 +614,7 @@ namespace BizHawk.Tests.Client.Common.Lua
public void Net_Return_Color() public void Net_Return_Color()
{ {
ReturnValue = ExpectedValue = Color.Aqua; ReturnValue = ExpectedValue = Color.Aqua;
LuaInstance.DoString("return pass_color(return_color())"); LuaInstance.DoString("pass_color(return_color())");
} }
[TestMethod] [TestMethod]
@ -679,16 +681,14 @@ namespace BizHawk.Tests.Client.Common.Lua
LuaInstance.DoString("pass_bool(true)"); LuaInstance.DoString("pass_bool(true)");
} }
// this doesn't work for some reason [TestMethod]
// just results in an exception due to "Invalid arguments to method call"
/*[TestMethod]
public void Net_Argument_S8() public void Net_Argument_S8()
{ {
ExpectedValue = (sbyte)123; ExpectedValue = (sbyte)123;
LuaInstance.DoString("pass_s8(123)"); LuaInstance.DoString("pass_s8(123)");
ExpectedValue = (sbyte)-123; ExpectedValue = (sbyte)-123;
LuaInstance.DoString("pass_s8(-123)"); LuaInstance.DoString("pass_s8(-123)");
}*/ }
[TestMethod] [TestMethod]
public void Net_Argument_U8() public void Net_Argument_U8()
@ -772,13 +772,11 @@ namespace BizHawk.Tests.Client.Common.Lua
LuaInstance.DoString("pass_decimal(-123.0)"); LuaInstance.DoString("pass_decimal(-123.0)");
} }
// these don't work either, although these make a bit more sense // these don't work, although there is reasoning behind this
// IntPtr/UIntPtr are meant as handles to "userdata" // IntPtr/UIntPtr are meant as handles to "userdata"
// so raw integers result in "Invalid arguments to method call" // so raw integers result in "Invalid arguments to method call"
// not sure why char doesn't work (same exception here)
/* /*[TestMethod]
[TestMethod]
public void Net_Argument_IntPtr() public void Net_Argument_IntPtr()
{ {
ExpectedValue = (IntPtr)123; ExpectedValue = (IntPtr)123;
@ -787,20 +785,21 @@ namespace BizHawk.Tests.Client.Common.Lua
LuaInstance.DoString("pass_intptr(-123)"); LuaInstance.DoString("pass_intptr(-123)");
}*/ }*/
/* /*[TestMethod]
[TestMethod]
public void Net_Argument_UIntPtr() public void Net_Argument_UIntPtr()
{ {
ExpectedValue = (UIntPtr)123; ExpectedValue = (UIntPtr)123;
LuaInstance.DoString("pass_uintptr(123)"); LuaInstance.DoString("pass_uintptr(123)");
}*/ }*/
/*[TestMethod] [TestMethod]
public void Net_Argument_Char() public void Net_Argument_Char()
{ {
ExpectedValue = 'a'; ExpectedValue = 'a';
LuaInstance.DoString($"pass_char({(byte)'a'})"); LuaInstance.DoString($"pass_char({(ushort)'a'})");
}*/ ExpectedValue = 'こ';
LuaInstance.DoString($"pass_char({(ushort)'こ'})");
}
[TestMethod] [TestMethod]
public void Net_Argument_String() public void Net_Argument_String()
@ -816,6 +815,19 @@ namespace BizHawk.Tests.Client.Common.Lua
LuaInstance.DoString($"pass_string(\"\")"); LuaInstance.DoString($"pass_string(\"\")");
} }
[TestMethod]
public void Net_Argument_String_Implicit_Number_Conversion()
{
ExpectedValue = "123";
LuaInstance.DoString("pass_string(123)");
ExpectedValue = "-123";
LuaInstance.DoString("pass_string(-123)");
ExpectedValue = "0.321";
LuaInstance.DoString("pass_string(0.321)");
ExpectedValue = "-0.321";
LuaInstance.DoString("pass_string(-0.321)");
}
[TestMethod] [TestMethod]
public void Net_Argument_Color() public void Net_Argument_Color()
{ {