Skip to content

Commit eb00019

Browse files
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
1 parent 9206826 commit eb00019

File tree

5 files changed

+113
-82
lines changed

5 files changed

+113
-82
lines changed

Assets/dll/NLua.dll

0 Bytes
Binary file not shown.

References/NLua.dll

0 Bytes
Binary file not shown.

src/BizHawk.Client.Common/Api/Classes/GuiApi.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,23 @@ public void WithSurface(DisplaySurfaceID surfaceID, Action drawingCallsFunc)
151151
}
152152
}
153153

154-
public void ThisIsTheLuaAutounlockHack()
155-
{
156-
UnlockSurface(DisplaySurfaceID.EmuCore);
157-
UnlockSurface(DisplaySurfaceID.Client);
154+
public readonly ref struct LuaAutoUnlockHack
155+
{
156+
private readonly GuiApi _guiApi;
157+
158+
internal LuaAutoUnlockHack(GuiApi guiApi)
159+
=> _guiApi = guiApi;
160+
161+
public void Dispose()
162+
{
163+
_guiApi.UnlockSurface(DisplaySurfaceID.EmuCore);
164+
_guiApi.UnlockSurface(DisplaySurfaceID.Client);
165+
}
158166
}
159167

168+
public LuaAutoUnlockHack ThisIsTheLuaAutoUnlockHack()
169+
=> new(this);
170+
160171
public void DrawNew(string name, bool clear)
161172
{
162173
switch (name)

src/BizHawk.Client.EmuHawk/tools/Lua/Win32LuaLibraries.cs

Lines changed: 71 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -185,84 +185,92 @@ public void Restart(
185185

186186
public void CallSaveStateEvent(string name)
187187
{
188-
try
188+
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
189189
{
190-
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_SAVESTATE).ToList())
190+
try
191191
{
192-
lf.Call(name);
192+
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_SAVESTATE).ToList())
193+
{
194+
lf.Call(name);
195+
}
196+
}
197+
catch (Exception e)
198+
{
199+
LogToLuaConsole($"error running function attached by lua function event.onsavestate\nError message: {e.Message}");
193200
}
194-
GuiAPI.ThisIsTheLuaAutounlockHack();
195-
}
196-
catch (Exception e)
197-
{
198-
GuiAPI.ThisIsTheLuaAutounlockHack();
199-
LogToLuaConsole($"error running function attached by lua function event.onsavestate\nError message: {e.Message}");
200201
}
201202
}
202203

203204
public void CallLoadStateEvent(string name)
204205
{
205-
try
206+
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
206207
{
207-
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_LOADSTATE).ToList())
208+
try
208209
{
209-
lf.Call(name);
210+
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_LOADSTATE).ToList())
211+
{
212+
lf.Call(name);
213+
}
214+
}
215+
catch (Exception e)
216+
{
217+
LogToLuaConsole($"error running function attached by lua function event.onloadstate\nError message: {e.Message}");
210218
}
211-
GuiAPI.ThisIsTheLuaAutounlockHack();
212-
}
213-
catch (Exception e)
214-
{
215-
GuiAPI.ThisIsTheLuaAutounlockHack();
216-
LogToLuaConsole($"error running function attached by lua function event.onloadstate\nError message: {e.Message}");
217219
}
218220
}
219221

220222
public void CallFrameBeforeEvent()
221223
{
222224
if (IsUpdateSupressed) return;
223-
try
225+
226+
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
224227
{
225-
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_PREFRAME).ToList())
228+
try
229+
{
230+
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_PREFRAME).ToList())
231+
{
232+
lf.Call();
233+
}
234+
}
235+
catch (Exception e)
226236
{
227-
lf.Call();
237+
LogToLuaConsole($"error running function attached by lua function event.onframestart\nError message: {e.Message}");
228238
}
229-
GuiAPI.ThisIsTheLuaAutounlockHack();
230-
}
231-
catch (Exception e)
232-
{
233-
GuiAPI.ThisIsTheLuaAutounlockHack();
234-
LogToLuaConsole($"error running function attached by lua function event.onframestart\nError message: {e.Message}");
235239
}
236240
}
237241

238242
public void CallFrameAfterEvent()
239243
{
240244
if (IsUpdateSupressed) return;
241-
try
245+
246+
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
242247
{
243-
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_POSTFRAME).ToList())
248+
try
244249
{
245-
lf.Call();
250+
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_POSTFRAME).ToList())
251+
{
252+
lf.Call();
253+
}
254+
}
255+
catch (Exception e)
256+
{
257+
LogToLuaConsole($"error running function attached by lua function event.onframeend\nError message: {e.Message}");
246258
}
247-
GuiAPI.ThisIsTheLuaAutounlockHack();
248-
}
249-
catch (Exception e)
250-
{
251-
GuiAPI.ThisIsTheLuaAutounlockHack();
252-
LogToLuaConsole($"error running function attached by lua function event.onframeend\nError message: {e.Message}");
253259
}
254260
}
255261

256262
public void CallExitEvent(LuaFile lf)
257263
{
258-
foreach (var exitCallback in RegisteredFunctions
259-
.Where(l => l.Event == NamedLuaFunction.EVENT_TYPE_ENGINESTOP
260-
&& (l.LuaFile.Path == lf.Path || ReferenceEquals(l.LuaFile.Thread, lf.Thread)))
261-
.ToList())
264+
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
262265
{
263-
exitCallback.Call();
266+
foreach (var exitCallback in RegisteredFunctions
267+
.Where(l => l.Event == NamedLuaFunction.EVENT_TYPE_ENGINESTOP
268+
&& (l.LuaFile.Path == lf.Path || ReferenceEquals(l.LuaFile.Thread, lf.Thread)))
269+
.ToList())
270+
{
271+
exitCallback.Call();
272+
}
264273
}
265-
GuiAPI.ThisIsTheLuaAutounlockHack();
266274
}
267275

268276
public void Close()
@@ -319,31 +327,31 @@ public void ExecuteString(string command)
319327

320328
public (bool WaitForFrame, bool Terminated) ResumeScript(LuaFile lf)
321329
{
322-
_currThread = lf.Thread;
323-
324-
try
330+
using (GuiAPI.ThisIsTheLuaAutoUnlockHack())
325331
{
326-
LuaLibraryBase.SetCurrentThread(lf);
332+
_currThread = lf.Thread;
327333

328-
var execResult = _currThread.Resume();
329-
GuiAPI.ThisIsTheLuaAutounlockHack();
334+
try
335+
{
336+
LuaLibraryBase.SetCurrentThread(lf);
330337

331-
_currThread = null;
332-
var result = execResult == KeraLua.LuaStatus.OK
333-
? (WaitForFrame: false, Terminated: true) // terminated
334-
: (WaitForFrame: FrameAdvanceRequested, Terminated: false); // yielded
338+
var execResult = _currThread.Resume();
335339

336-
FrameAdvanceRequested = false;
337-
return result;
338-
}
339-
catch (Exception)
340-
{
341-
GuiAPI.ThisIsTheLuaAutounlockHack();
342-
throw;
343-
}
344-
finally
345-
{
346-
LuaLibraryBase.ClearCurrentThread();
340+
_currThread = null;
341+
var result = execResult switch
342+
{
343+
KeraLua.LuaStatus.OK => (WaitForFrame: false, Terminated: true),
344+
KeraLua.LuaStatus.Yield => (WaitForFrame: FrameAdvanceRequested, Terminated: false),
345+
_ => throw new InvalidOperationException($"{nameof(_currThread.Resume)}() returned {execResult}?")
346+
};
347+
348+
FrameAdvanceRequested = false;
349+
return result;
350+
}
351+
finally
352+
{
353+
LuaLibraryBase.ClearCurrentThread();
354+
}
347355
}
348356
}
349357

src/BizHawk.Tests/Client.Common/lua/LuaTests.cs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,9 @@ public void Net_Return_UIntPtr()
591591
public void Net_Return_Char()
592592
{
593593
ReturnValue = 'a';
594-
Assert.IsTrue((bool)LuaInstance.DoString($"return return_char() == {(byte)'a'}")[0]);
594+
Assert.IsTrue((bool)LuaInstance.DoString($"return return_char() == {(ushort)'a'}")[0]);
595+
ReturnValue = 'こ';
596+
Assert.IsTrue((bool)LuaInstance.DoString($"return return_char() == {(ushort)'こ'}")[0]);
595597
}
596598

597599
[TestMethod]
@@ -612,7 +614,7 @@ public void Net_Return_String_Utf8()
612614
public void Net_Return_Color()
613615
{
614616
ReturnValue = ExpectedValue = Color.Aqua;
615-
LuaInstance.DoString("return pass_color(return_color())");
617+
LuaInstance.DoString("pass_color(return_color())");
616618
}
617619

618620
[TestMethod]
@@ -679,16 +681,14 @@ public void Net_Argument_Bool()
679681
LuaInstance.DoString("pass_bool(true)");
680682
}
681683

682-
// this doesn't work for some reason
683-
// just results in an exception due to "Invalid arguments to method call"
684-
/*[TestMethod]
684+
[TestMethod]
685685
public void Net_Argument_S8()
686686
{
687687
ExpectedValue = (sbyte)123;
688688
LuaInstance.DoString("pass_s8(123)");
689689
ExpectedValue = (sbyte)-123;
690690
LuaInstance.DoString("pass_s8(-123)");
691-
}*/
691+
}
692692

693693
[TestMethod]
694694
public void Net_Argument_U8()
@@ -772,13 +772,11 @@ public void Net_Argument_Decimal()
772772
LuaInstance.DoString("pass_decimal(-123.0)");
773773
}
774774

775-
// these don't work either, although these make a bit more sense
775+
// these don't work, although there is reasoning behind this
776776
// IntPtr/UIntPtr are meant as handles to "userdata"
777777
// so raw integers result in "Invalid arguments to method call"
778-
// not sure why char doesn't work (same exception here)
779778

780-
/*
781-
[TestMethod]
779+
/*[TestMethod]
782780
public void Net_Argument_IntPtr()
783781
{
784782
ExpectedValue = (IntPtr)123;
@@ -787,20 +785,21 @@ public void Net_Argument_IntPtr()
787785
LuaInstance.DoString("pass_intptr(-123)");
788786
}*/
789787

790-
/*
791-
[TestMethod]
788+
/*[TestMethod]
792789
public void Net_Argument_UIntPtr()
793790
{
794791
ExpectedValue = (UIntPtr)123;
795792
LuaInstance.DoString("pass_uintptr(123)");
796793
}*/
797794

798-
/*[TestMethod]
795+
[TestMethod]
799796
public void Net_Argument_Char()
800797
{
801798
ExpectedValue = 'a';
802-
LuaInstance.DoString($"pass_char({(byte)'a'})");
803-
}*/
799+
LuaInstance.DoString($"pass_char({(ushort)'a'})");
800+
ExpectedValue = 'こ';
801+
LuaInstance.DoString($"pass_char({(ushort)'こ'})");
802+
}
804803

805804
[TestMethod]
806805
public void Net_Argument_String()
@@ -816,6 +815,19 @@ public void Net_Argument_String_Utf8()
816815
LuaInstance.DoString($"pass_string(\"こんにちは\")");
817816
}
818817

818+
[TestMethod]
819+
public void Net_Argument_String_Implicit_Number_Conversion()
820+
{
821+
ExpectedValue = "123";
822+
LuaInstance.DoString("pass_string(123)");
823+
ExpectedValue = "-123";
824+
LuaInstance.DoString("pass_string(-123)");
825+
ExpectedValue = "0.321";
826+
LuaInstance.DoString("pass_string(0.321)");
827+
ExpectedValue = "-0.321";
828+
LuaInstance.DoString("pass_string(-0.321)");
829+
}
830+
819831
[TestMethod]
820832
public void Net_Argument_Color()
821833
{

0 commit comments

Comments
 (0)