Pass TestScriptsDoNotShareGlobals. Fixes issue #3036.
This commit is contained in:
parent
a959aa887b
commit
8c7b0b5eba
src
BizHawk.Client.Common/lua
BizHawk.Client.EmuHawk/tools/Lua
|
@ -48,7 +48,7 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
bool RemoveNamedFunctionMatching(Func<INamedLuaFunction, bool> predicate);
|
bool RemoveNamedFunctionMatching(Func<INamedLuaFunction, bool> predicate);
|
||||||
|
|
||||||
void SpawnAndSetFileThread(string pathToLoad, LuaFile lf);
|
void SpawnAndSetFileThread(LuaFile lf);
|
||||||
|
|
||||||
void ExecuteString(string command);
|
void ExecuteString(string command);
|
||||||
|
|
||||||
|
@ -57,5 +57,7 @@ namespace BizHawk.Client.Common
|
||||||
void EnableLuaFile(LuaFile item);
|
void EnableLuaFile(LuaFile item);
|
||||||
|
|
||||||
void DisableLuaScript(LuaFile file);
|
void DisableLuaScript(LuaFile file);
|
||||||
|
|
||||||
|
Lua GetCurrentLua();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,7 +25,7 @@ namespace BizHawk.Client.Common
|
||||||
Config config,
|
Config config,
|
||||||
IGameInfo game)
|
IGameInfo game)
|
||||||
{
|
{
|
||||||
_th = new NLuaTableHelper(_lua, LogToLuaConsole);
|
_th = new NLuaTableHelper(this, LogToLuaConsole);
|
||||||
_displayManager = displayManager;
|
_displayManager = displayManager;
|
||||||
_inputManager = inputManager;
|
_inputManager = inputManager;
|
||||||
_mainFormApi = mainFormApi;
|
_mainFormApi = mainFormApi;
|
||||||
|
@ -36,38 +36,31 @@ namespace BizHawk.Client.Common
|
||||||
Docs.Clear();
|
Docs.Clear();
|
||||||
_apiContainer = ApiManager.RestartLua(_mainFormApi.Emulator.ServiceProvider, LogToLuaConsole, _mainFormApi, _displayManager, _inputManager, _mainFormApi.MovieSession, _mainFormApi.Tools, config, _mainFormApi.Emulator, game);
|
_apiContainer = ApiManager.RestartLua(_mainFormApi.Emulator.ServiceProvider, LogToLuaConsole, _mainFormApi, _displayManager, _inputManager, _mainFormApi.MovieSession, _mainFormApi.Tools, config, _mainFormApi.Emulator, game);
|
||||||
|
|
||||||
var packageTable = (LuaTable) _lua["package"];
|
UpdatePackageTable(_luaWithoutFile);
|
||||||
var luaPath = PathEntries.LuaAbsolutePath();
|
|
||||||
if (OSTailoredCode.IsUnixHost)
|
|
||||||
{
|
|
||||||
// add %exe%/Lua to library resolution pathset (LUA_PATH)
|
|
||||||
// this is done already on windows, but not on linux it seems?
|
|
||||||
packageTable["path"] = $"{luaPath}/?.lua;{luaPath}?/init.lua;{packageTable["path"]}";
|
|
||||||
// we need to modifiy the cpath so it looks at our lua dir too, and remove the relative pathing
|
|
||||||
// we do this on Windows too, but keep in mind Linux uses .so and Windows use .dll
|
|
||||||
// TODO: Does the relative pathing issue Windows has also affect Linux? I'd assume so...
|
|
||||||
packageTable["cpath"] = $"{luaPath}/?.so;{luaPath}/loadall.so;{packageTable["cpath"]}";
|
|
||||||
packageTable["cpath"] = ((string)packageTable["cpath"]).Replace(";./?.so", "");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
packageTable["cpath"] = $"{luaPath}\\?.dll;{luaPath}\\loadall.dll;{packageTable["cpath"]}";
|
|
||||||
packageTable["cpath"] = ((string)packageTable["cpath"]).Replace(";.\\?.dll", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
_lua.RegisterFunction("print", this, typeof(LuaLibrariesBase).GetMethod(nameof(Print)));
|
_luaWithoutFile.RegisterFunction("print", this, typeof(LuaLibrariesBase).GetMethod(nameof(Print)));
|
||||||
|
|
||||||
RegisterLuaLibraries(Common.ReflectionCache.Types);
|
RegisterLuaLibraries(ReflectionCache.Types);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void EnumerateLuaFunctions(string name, Type type, LuaLibraryBase instance)
|
protected void EnumerateLuaFunctions(string name, Type type, LuaLibraryBase instance)
|
||||||
{
|
{
|
||||||
if (instance != null) _lua.NewTable(name);
|
if (instance != null)
|
||||||
|
{
|
||||||
|
_luaWithoutFile.NewTable(name);
|
||||||
|
_tablesForFunctions.Add(name);
|
||||||
|
}
|
||||||
foreach (var method in type.GetMethods())
|
foreach (var method in type.GetMethods())
|
||||||
{
|
{
|
||||||
var foundAttrs = method.GetCustomAttributes(typeof(LuaMethodAttribute), false);
|
var foundAttrs = method.GetCustomAttributes(typeof(LuaMethodAttribute), false);
|
||||||
if (foundAttrs.Length == 0) continue;
|
if (foundAttrs.Length == 0) continue;
|
||||||
if (instance != null) _lua.RegisterFunction($"{name}.{((LuaMethodAttribute)foundAttrs[0]).Name}", instance, method);
|
if (instance != null)
|
||||||
|
{
|
||||||
|
string path = $"{name}.{((LuaMethodAttribute)foundAttrs[0]).Name}";
|
||||||
|
_luaWithoutFile.RegisterFunction(path, instance, method);
|
||||||
|
_functionsToRegister[path] = (instance, method);
|
||||||
|
|
||||||
|
}
|
||||||
LibraryFunction libFunc = new(
|
LibraryFunction libFunc = new(
|
||||||
name,
|
name,
|
||||||
type.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast<DescriptionAttribute>()
|
type.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast<DescriptionAttribute>()
|
||||||
|
@ -109,6 +102,35 @@ namespace BizHawk.Client.Common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdatePackageTable(Lua lua)
|
||||||
|
{
|
||||||
|
var packageTable = (LuaTable)lua["package"];
|
||||||
|
var luaPath = PathEntries.LuaAbsolutePath();
|
||||||
|
if (OSTailoredCode.IsUnixHost)
|
||||||
|
{
|
||||||
|
// add %exe%/Lua to library resolution pathset (LUA_PATH)
|
||||||
|
// this is done already on windows, but not on linux it seems?
|
||||||
|
packageTable["path"] = $"{luaPath}/?.lua;{luaPath}?/init.lua;{packageTable["path"]}";
|
||||||
|
// we need to modifiy the cpath so it looks at our lua dir too, and remove the relative pathing
|
||||||
|
// we do this on Windows too, but keep in mind Linux uses .so and Windows use .dll
|
||||||
|
// TODO: Does the relative pathing issue Windows has also affect Linux? I'd assume so...
|
||||||
|
packageTable["cpath"] = $"{luaPath}/?.so;{luaPath}/loadall.so;{packageTable["cpath"]}";
|
||||||
|
packageTable["cpath"] = ((string)packageTable["cpath"]).Replace(";./?.so", "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
packageTable["cpath"] = $"{luaPath}\\?.dll;{luaPath}\\loadall.dll;{packageTable["cpath"]}";
|
||||||
|
packageTable["cpath"] = ((string)packageTable["cpath"]).Replace(";.\\?.dll", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<LuaFile, Lua> _activeLuas = new();
|
||||||
|
|
||||||
|
private Lua _luaWithoutFile = new();
|
||||||
|
|
||||||
|
private List<string> _tablesForFunctions = new();
|
||||||
|
private Dictionary<string, (LuaLibraryBase Lib, MethodInfo Func)> _functionsToRegister = new();
|
||||||
|
|
||||||
private ApiContainer _apiContainer;
|
private ApiContainer _apiContainer;
|
||||||
|
|
||||||
private readonly DisplayManagerBase _displayManager;
|
private readonly DisplayManagerBase _displayManager;
|
||||||
|
@ -119,9 +141,10 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
private readonly IMainFormForApi _mainFormApi;
|
private readonly IMainFormForApi _mainFormApi;
|
||||||
|
|
||||||
private Lua _lua = new();
|
|
||||||
private LuaThread _currThread;
|
private LuaThread _currThread;
|
||||||
|
|
||||||
|
private Lua _currLua;
|
||||||
|
|
||||||
private readonly NLuaTableHelper _th;
|
private readonly NLuaTableHelper _th;
|
||||||
|
|
||||||
protected Action<object[]> _logToLuaConsoleCallback = a => Console.WriteLine("a Lua lib is logging during init and the console lib hasn't been initialised yet");
|
protected Action<object[]> _logToLuaConsoleCallback = a => Console.WriteLine("a Lua lib is logging during init and the console lib hasn't been initialised yet");
|
||||||
|
@ -167,13 +190,18 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
public LuaFunctionList RegisteredFunctions { get; }
|
public LuaFunctionList RegisteredFunctions { get; }
|
||||||
|
|
||||||
|
public Lua GetCurrentLua()
|
||||||
|
{
|
||||||
|
return _currLua ?? _luaWithoutFile;
|
||||||
|
}
|
||||||
|
|
||||||
public void CallSaveStateEvent(string name)
|
public void CallSaveStateEvent(string name)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_SAVESTATE).ToList())
|
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_SAVESTATE).ToList())
|
||||||
{
|
{
|
||||||
lf.Call(name);
|
CallFunction(lf, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -190,7 +218,7 @@ namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_LOADSTATE).ToList())
|
foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_LOADSTATE).ToList())
|
||||||
{
|
{
|
||||||
lf.Call(name);
|
CallFunction(lf, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -211,7 +239,7 @@ namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
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();
|
CallFunction(lf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -228,7 +256,7 @@ namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
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();
|
CallFunction(lf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -246,7 +274,7 @@ namespace BizHawk.Client.Common
|
||||||
&& (l.LuaFile.Path == lf.Path || ReferenceEquals(l.LuaFile.Thread, lf.Thread)))
|
&& (l.LuaFile.Path == lf.Path || ReferenceEquals(l.LuaFile.Thread, lf.Thread)))
|
||||||
.ToList())
|
.ToList())
|
||||||
{
|
{
|
||||||
exitCallback.Call();
|
CallFunction(exitCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +284,7 @@ namespace BizHawk.Client.Common
|
||||||
.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_CONSOLECLOSE)
|
.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_CONSOLECLOSE)
|
||||||
.ToList())
|
.ToList())
|
||||||
{
|
{
|
||||||
closeCallback.Call();
|
CallFunction(closeCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisteredFunctions.Clear(_mainFormApi.Emulator);
|
RegisteredFunctions.Clear(_mainFormApi.Emulator);
|
||||||
|
@ -268,8 +296,10 @@ namespace BizHawk.Client.Common
|
||||||
disposable.Dispose();
|
disposable.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_lua.Dispose();
|
_luaWithoutFile.Dispose();
|
||||||
_lua = null;
|
_luaWithoutFile = null;
|
||||||
|
foreach (Lua lua in _activeLuas.Values)
|
||||||
|
lua.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public INamedLuaFunction CreateAndRegisterNamedFunction(
|
public INamedLuaFunction CreateAndRegisterNamedFunction(
|
||||||
|
@ -280,7 +310,7 @@ namespace BizHawk.Client.Common
|
||||||
string name = null)
|
string name = null)
|
||||||
{
|
{
|
||||||
var nlf = new NamedLuaFunction(function, theEvent, logCallback, luaFile,
|
var nlf = new NamedLuaFunction(function, theEvent, logCallback, luaFile,
|
||||||
() => { _lua.NewThread(out var thread); return thread; }, name);
|
() => { _activeLuas[luaFile].NewThread(out var thread); return thread; }, name);
|
||||||
RegisteredFunctions.Add(nlf);
|
RegisteredFunctions.Add(nlf);
|
||||||
return nlf;
|
return nlf;
|
||||||
}
|
}
|
||||||
|
@ -293,23 +323,48 @@ namespace BizHawk.Client.Common
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LuaThread SpawnCoroutine(string file)
|
public LuaThread SpawnCoroutine(LuaFile file)
|
||||||
{
|
{
|
||||||
var content = File.ReadAllText(file);
|
var content = File.ReadAllText(file.Path);
|
||||||
var main = _lua.LoadString(content, "main");
|
var main = _activeLuas[file].LoadString(content, "main");
|
||||||
_lua.NewThread(main, out var ret);
|
_activeLuas[file].NewThread(main, out var ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SpawnAndSetFileThread(string pathToLoad, LuaFile lf)
|
public void SpawnAndSetFileThread(LuaFile lf)
|
||||||
=> lf.Thread = SpawnCoroutine(pathToLoad);
|
{
|
||||||
|
if (_activeLuas.ContainsKey(lf))
|
||||||
|
_activeLuas[lf].Dispose();
|
||||||
|
|
||||||
|
Lua lua = new();
|
||||||
|
UpdatePackageTable(lua);
|
||||||
|
lua.RegisterFunction("print", this, typeof(LuaLibrariesBase).GetMethod(nameof(Print)));
|
||||||
|
// We cannot copy tables from one Lua to another, unfortunately. Directly assigning them doesn't work at all.
|
||||||
|
// Transferring each individual value to new tables mostly works, but not always.
|
||||||
|
// For example event.onframeend would receive bad LuaFunction references.
|
||||||
|
foreach (string name in _tablesForFunctions)
|
||||||
|
lua.NewTable(name);
|
||||||
|
foreach (var item in _functionsToRegister)
|
||||||
|
lua.RegisterFunction(item.Key, item.Value.Lib, item.Value.Func);
|
||||||
|
_activeLuas[lf] = lua;
|
||||||
|
|
||||||
|
lf.Thread = SpawnCoroutine(lf);
|
||||||
|
}
|
||||||
|
|
||||||
public void ExecuteString(string command)
|
public void ExecuteString(string command)
|
||||||
=> _lua.DoString(command);
|
=> _luaWithoutFile.DoString(command);
|
||||||
|
|
||||||
|
private void CallFunction(NamedLuaFunction func, string name = null)
|
||||||
|
{
|
||||||
|
_currLua = _activeLuas[func.LuaFile];
|
||||||
|
func.Call(name);
|
||||||
|
_currLua = null;
|
||||||
|
}
|
||||||
|
|
||||||
public (bool WaitForFrame, bool Terminated) ResumeScript(LuaFile lf)
|
public (bool WaitForFrame, bool Terminated) ResumeScript(LuaFile lf)
|
||||||
{
|
{
|
||||||
_currThread = lf.Thread;
|
_currThread = lf.Thread;
|
||||||
|
_currLua = _activeLuas[lf];
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -317,7 +372,6 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
var execResult = _currThread.Resume();
|
var execResult = _currThread.Resume();
|
||||||
|
|
||||||
_currThread = null;
|
|
||||||
var result = execResult switch
|
var result = execResult switch
|
||||||
{
|
{
|
||||||
LuaStatus.OK => (WaitForFrame: false, Terminated: true),
|
LuaStatus.OK => (WaitForFrame: false, Terminated: true),
|
||||||
|
@ -331,6 +385,8 @@ namespace BizHawk.Client.Common
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
LuaLibraryBase.ClearCurrentThread();
|
LuaLibraryBase.ClearCurrentThread();
|
||||||
|
_currThread = null;
|
||||||
|
_currLua = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,7 +424,7 @@ namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
LuaSandbox.Sandbox(null, () =>
|
LuaSandbox.Sandbox(null, () =>
|
||||||
{
|
{
|
||||||
SpawnAndSetFileThread(item.Path, item);
|
SpawnAndSetFileThread(item);
|
||||||
LuaSandbox.CreateSandbox(item.Thread, Path.GetDirectoryName(item.Path));
|
LuaSandbox.CreateSandbox(item.Thread, Path.GetDirectoryName(item.Path));
|
||||||
}, () =>
|
}, () =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,6 +14,8 @@ namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
private readonly Action<string> _logCallback;
|
private readonly Action<string> _logCallback;
|
||||||
|
|
||||||
|
private readonly ILuaLibraries _luaLibraries;
|
||||||
|
|
||||||
private readonly Lua _lua;
|
private readonly Lua _lua;
|
||||||
|
|
||||||
public NLuaTableHelper(Lua lua, Action<string> logCallback)
|
public NLuaTableHelper(Lua lua, Action<string> logCallback)
|
||||||
|
@ -22,7 +24,15 @@ namespace BizHawk.Client.Common
|
||||||
_lua = lua;
|
_lua = lua;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LuaTable CreateTable() => _lua.NewTable();
|
public NLuaTableHelper(ILuaLibraries luaLibraries, Action<string> logCallback)
|
||||||
|
{
|
||||||
|
_logCallback = logCallback;
|
||||||
|
_luaLibraries = luaLibraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Lua GetLua() => _luaLibraries?.GetCurrentLua() ?? _lua;
|
||||||
|
|
||||||
|
public LuaTable CreateTable() => GetLua().NewTable();
|
||||||
|
|
||||||
public LuaTable DictToTable<T>(IReadOnlyDictionary<string, T> dictionary)
|
public LuaTable DictToTable<T>(IReadOnlyDictionary<string, T> dictionary)
|
||||||
{
|
{
|
||||||
|
@ -60,7 +70,7 @@ namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
if (!method.IsPublic) continue;
|
if (!method.IsPublic) continue;
|
||||||
var foundAttrs = method.GetCustomAttributes(typeof(LuaMethodAttribute), false);
|
var foundAttrs = method.GetCustomAttributes(typeof(LuaMethodAttribute), false);
|
||||||
table[method.Name] = _lua.RegisterFunction(
|
table[method.Name] = GetLua().RegisterFunction(
|
||||||
foundAttrs.Length == 0 ? string.Empty : ((LuaMethodAttribute) foundAttrs[0]).Name, // empty string will default to the actual method name
|
foundAttrs.Length == 0 ? string.Empty : ((LuaMethodAttribute) foundAttrs[0]).Name, // empty string will default to the actual method name
|
||||||
obj,
|
obj,
|
||||||
method
|
method
|
||||||
|
|
|
@ -73,12 +73,8 @@ namespace BizHawk.Client.Common
|
||||||
public void DetachFromScript()
|
public void DetachFromScript()
|
||||||
{
|
{
|
||||||
var thread = CreateThreadCallback();
|
var thread = CreateThreadCallback();
|
||||||
|
LuaSandbox.CreateSandbox(thread, LuaFile.CurrentDirectory);
|
||||||
// Current dir will have to do for now, but this will inevitably not be desired
|
LuaFile.Thread = thread;
|
||||||
// Users will expect it to be the same directly as the thread that spawned this callback
|
|
||||||
// But how do we know what that directory was?
|
|
||||||
LuaSandbox.CreateSandbox(thread, ".");
|
|
||||||
LuaFile = new LuaFile(".") { Thread = thread };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid Guid { get; }
|
public Guid Guid { get; }
|
||||||
|
|
|
@ -228,7 +228,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
LuaSandbox.Sandbox(file.Thread, () =>
|
LuaSandbox.Sandbox(file.Thread, () =>
|
||||||
{
|
{
|
||||||
LuaImp.SpawnAndSetFileThread(file.Path, file);
|
LuaImp.SpawnAndSetFileThread(file);
|
||||||
LuaSandbox.CreateSandbox(file.Thread, Path.GetDirectoryName(file.Path));
|
LuaSandbox.CreateSandbox(file.Thread, Path.GetDirectoryName(file.Path));
|
||||||
file.State = LuaFile.RunState.Running;
|
file.State = LuaFile.RunState.Running;
|
||||||
}, () =>
|
}, () =>
|
||||||
|
|
Loading…
Reference in New Issue