Move handling of loading EmuHawk lua librarys from LuaLibrariesBase to LuaLibraries, as part of moving LuaLibrariesBase to BizHawk.Client.Common (to support testing).

Remove params that can instead be obtained from mainForm (instead of creating private variables to hold them for reference in code that was moved outside of the constructor). Also remove them from Restart method for consistency in how ApiManager.RestartLua is called.
This commit is contained in:
SuuperW 2023-09-19 02:59:28 -05:00
parent 53b2d1d7bc
commit 1623a0f44d
6 changed files with 134 additions and 93 deletions

View File

@ -32,6 +32,10 @@ namespace BizHawk.Client.Common
/// <remarks>only referenced from <c>EmuClientApi</c></remarks>
bool PauseAvi { get; set; }
IToolManager Tools { get; }
IMovieSession MovieSession { get; }
/// <remarks>only referenced from <c>EmuClientApi</c></remarks>
void ClearHolds();

View File

@ -44,7 +44,7 @@ namespace BizHawk.Client.Common
NLuaTableHelper GetTableHelper();
void Restart(IEmulatorServiceProvider newServiceProvider, Config config, IEmulator emulator, IGameInfo game);
void Restart(Config config, IGameInfo game);
bool RemoveNamedFunctionMatching(Func<INamedLuaFunction, bool> predicate);

View File

@ -1034,6 +1034,7 @@ namespace BizHawk.Client.EmuHawk
internal readonly ExternalToolManager ExtToolManager;
public readonly ToolManager Tools;
IToolManager IMainFormForApi.Tools => Tools;
private DisplayManager DisplayManager;

View File

@ -195,7 +195,7 @@ namespace BizHawk.Client.EmuHawk
if (LuaImp.IsRebootingCore)
{
// Even if the lua console is self-rebooting from client.reboot_core() we still want to re-inject dependencies
LuaImp.Restart(Emulator.ServiceProvider, Config, Emulator, Game);
LuaImp.Restart(Config, Game);
return;
}
@ -214,12 +214,10 @@ namespace BizHawk.Client.EmuHawk
LuaImp = new LuaLibraries(
newScripts,
registeredFuncList,
Emulator.ServiceProvider,
(MainForm) MainForm, //HACK
DisplayManager,
InputManager,
Config,
Emulator,
Game);
InputBox.AutoCompleteCustomSource.AddRange(LuaImp.Docs.Select(a => $"{a.Library}.{a.Name}").ToArray());

View File

@ -0,0 +1,58 @@
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{
internal class LuaLibraries : LuaLibrariesBase
{
private MainForm _mainForm;
public LuaLibraries(
LuaFileList scriptList,
LuaFunctionList registeredFuncList,
MainForm mainForm,
DisplayManagerBase displayManager,
InputManager inputManager,
Config config,
IGameInfo game)
: base(scriptList, registeredFuncList, mainForm, displayManager, inputManager, config, game)
{
_mainForm = mainForm;
RegisterLuaLibraries(ReflectionCache.Types);
}
protected override void HandleSpecialLuaLibraryProperties(LuaLibraryBase library)
{
base.HandleSpecialLuaLibraryProperties(library);
// TODO: make EmuHawk libraries have a base class with common properties such as this
// and inject them here
if (library is ConsoleLuaLibrary consoleLib)
{
consoleLib.Tools = _mainForm.Tools;
_logToLuaConsoleCallback = consoleLib.Log;
}
else if (library is FormsLuaLibrary formsLib)
{
formsLib.MainForm = _mainForm;
}
else if (library is TAStudioLuaLibrary tastudioLib)
{
tastudioLib.Tools = _mainForm.Tools;
}
else if (library is GuiLuaLibrary guiLib) // GuiLuaLibrary isn't in EmuHawk, but LuaCanvas is.
{
// emu lib may be null now, depending on order of ReflectionCache.Types, but definitely won't be null when this is called
guiLib.CreateLuaCanvasCallback = (width, height, x, y) =>
{
var canvas = new LuaCanvas(EmulationLuaLibrary, width, height, x, y, GetTableHelper(), LogToLuaConsole);
canvas.Show();
return GetTableHelper().ObjectToTable(canvas);
};
EnumerateLuaFunctions(nameof(LuaCanvas), typeof(LuaCanvas), null); // add LuaCanvas to Lua function reference table
}
}
}
}

View File

@ -15,94 +15,27 @@ using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
public class LuaLibraries : ILuaLibraries
public class LuaLibrariesBase : ILuaLibraries
{
public LuaLibraries(
public LuaLibrariesBase(
LuaFileList scriptList,
LuaFunctionList registeredFuncList,
IEmulatorServiceProvider serviceProvider,
MainForm mainForm,
IMainFormForApi mainFormApi,
DisplayManagerBase displayManager,
InputManager inputManager,
Config config,
IEmulator emulator,
IGameInfo game)
{
void EnumerateLuaFunctions(string name, Type type, LuaLibraryBase instance)
{
if (instance != null) _lua.NewTable(name);
foreach (var method in type.GetMethods())
{
var foundAttrs = method.GetCustomAttributes(typeof(LuaMethodAttribute), false);
if (foundAttrs.Length == 0) continue;
if (instance != null) _lua.RegisterFunction($"{name}.{((LuaMethodAttribute)foundAttrs[0]).Name}", instance, method);
LibraryFunction libFunc = new(
name,
type.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast<DescriptionAttribute>()
.Select(descAttr => descAttr.Description).FirstOrDefault() ?? string.Empty,
method
);
Docs.Add(libFunc);
}
}
_th = new NLuaTableHelper(_lua, LogToLuaConsole);
_displayManager = displayManager;
_inputManager = inputManager;
_mainForm = mainForm;
_mainFormApi = mainFormApi;
LuaWait = new AutoResetEvent(false);
PathEntries = config.PathEntries;
RegisteredFunctions = registeredFuncList;
ScriptList = scriptList;
Docs.Clear();
_apiContainer = ApiManager.RestartLua(serviceProvider, LogToLuaConsole, _mainForm, _displayManager, _inputManager, _mainForm.MovieSession, _mainForm.Tools, config, emulator, game);
// Register lua libraries
foreach (var lib in Client.Common.ReflectionCache.Types.Concat(EmuHawk.ReflectionCache.Types)
.Where(t => typeof(LuaLibraryBase).IsAssignableFrom(t) && t.IsSealed && ServiceInjector.IsAvailable(serviceProvider, t)))
{
if (VersionInfo.DeveloperBuild
|| lib.GetCustomAttribute<LuaLibraryAttribute>(inherit: false)?.Released is not false)
{
var instance = (LuaLibraryBase)Activator.CreateInstance(lib, this, _apiContainer, (Action<string>)LogToLuaConsole);
if (!ServiceInjector.UpdateServices(serviceProvider, instance, mayCache: true)) throw new Exception("Lua lib has required service(s) that can't be fulfilled");
// TODO: make EmuHawk libraries have a base class with common properties such as this
// and inject them here
if (instance is ClientLuaLibrary clientLib)
{
clientLib.MainForm = _mainForm;
}
else if (instance is ConsoleLuaLibrary consoleLib)
{
consoleLib.Tools = _mainForm.Tools;
_logToLuaConsoleCallback = consoleLib.Log;
}
else if (instance is FormsLuaLibrary formsLib)
{
formsLib.MainForm = _mainForm;
}
else if (instance is GuiLuaLibrary guiLib)
{
// emu lib may be null now, depending on order of ReflectionCache.Types, but definitely won't be null when this is called
guiLib.CreateLuaCanvasCallback = (width, height, x, y) =>
{
var canvas = new LuaCanvas(EmulationLuaLibrary, width, height, x, y, _th, LogToLuaConsole);
canvas.Show();
return _th.ObjectToTable(canvas);
};
}
else if (instance is TAStudioLuaLibrary tastudioLib)
{
tastudioLib.Tools = _mainForm.Tools;
}
EnumerateLuaFunctions(instance.Name, lib, instance);
Libraries.Add(lib, instance);
}
}
_lua.RegisterFunction("print", this, typeof(LuaLibraries).GetMethod(nameof(Print)));
_apiContainer = ApiManager.RestartLua(_mainFormApi.Emulator.ServiceProvider, LogToLuaConsole, _mainFormApi, _displayManager, _inputManager, _mainFormApi.MovieSession, _mainFormApi.Tools, config, _mainFormApi.Emulator, game);
var packageTable = (LuaTable) _lua["package"];
var luaPath = PathEntries.LuaAbsolutePath();
@ -123,10 +56,58 @@ namespace BizHawk.Client.EmuHawk
packageTable["cpath"] = ((string)packageTable["cpath"]).Replace(";.\\?.dll", "");
}
EmulationLuaLibrary.FrameAdvanceCallback = FrameAdvance;
EmulationLuaLibrary.YieldCallback = EmuYield;
_lua.RegisterFunction("print", this, typeof(LuaLibrariesBase).GetMethod(nameof(Print)));
EnumerateLuaFunctions(nameof(LuaCanvas), typeof(LuaCanvas), null); // add LuaCanvas to Lua function reference table
RegisterLuaLibraries(Common.ReflectionCache.Types);
}
protected void EnumerateLuaFunctions(string name, Type type, LuaLibraryBase instance)
{
if (instance != null) _lua.NewTable(name);
foreach (var method in type.GetMethods())
{
var foundAttrs = method.GetCustomAttributes(typeof(LuaMethodAttribute), false);
if (foundAttrs.Length == 0) continue;
if (instance != null) _lua.RegisterFunction($"{name}.{((LuaMethodAttribute)foundAttrs[0]).Name}", instance, method);
LibraryFunction libFunc = new(
name,
type.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast<DescriptionAttribute>()
.Select(descAttr => descAttr.Description).FirstOrDefault() ?? string.Empty,
method
);
Docs.Add(libFunc);
}
}
protected void RegisterLuaLibraries(IEnumerable<Type> typesToSearch)
{
foreach (var lib in typesToSearch
.Where(t => typeof(LuaLibraryBase).IsAssignableFrom(t) && t.IsSealed && ServiceInjector.IsAvailable(_mainFormApi.Emulator.ServiceProvider, t)))
{
if (VersionInfo.DeveloperBuild
|| lib.GetCustomAttribute<LuaLibraryAttribute>(inherit: false)?.Released is not false)
{
var instance = (LuaLibraryBase)Activator.CreateInstance(lib, this, _apiContainer, (Action<string>)LogToLuaConsole);
if (!ServiceInjector.UpdateServices(_mainFormApi.Emulator.ServiceProvider, instance, mayCache: true)) throw new Exception("Lua lib has required service(s) that can't be fulfilled");
HandleSpecialLuaLibraryProperties(instance);
EnumerateLuaFunctions(instance.Name, lib, instance);
Libraries.Add(lib, instance);
}
}
}
protected virtual void HandleSpecialLuaLibraryProperties(LuaLibraryBase library)
{
if (library is ClientLuaLibrary clientLib)
{
clientLib.MainForm = _mainFormApi;
}
else if (library is EmulationLuaLibrary emulationLib)
{
emulationLib.FrameAdvanceCallback = FrameAdvance;
emulationLib.YieldCallback = EmuYield;
}
}
private ApiContainer _apiContainer;
@ -137,20 +118,20 @@ namespace BizHawk.Client.EmuHawk
private readonly InputManager _inputManager;
private readonly MainForm _mainForm;
private readonly IMainFormForApi _mainFormApi;
private Lua _lua = new();
private LuaThread _currThread;
private readonly NLuaTableHelper _th;
private static 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");
private FormsLuaLibrary FormsLibrary => (FormsLuaLibrary)Libraries[typeof(FormsLuaLibrary)];
public LuaDocumentation Docs { get; } = new LuaDocumentation();
private EmulationLuaLibrary EmulationLuaLibrary => (EmulationLuaLibrary)Libraries[typeof(EmulationLuaLibrary)];
protected EmulationLuaLibrary EmulationLuaLibrary => (EmulationLuaLibrary)Libraries[typeof(EmulationLuaLibrary)];
public string EngineName => "NLua+Lua";
@ -166,22 +147,21 @@ namespace BizHawk.Client.EmuHawk
public LuaFileList ScriptList { get; }
private static void LogToLuaConsole(object outputs) => _logToLuaConsoleCallback(new[] { outputs });
protected void LogToLuaConsole(object outputs) => _logToLuaConsoleCallback(new[] { outputs });
public NLuaTableHelper GetTableHelper() => _th;
public void Restart(
IEmulatorServiceProvider newServiceProvider,
Config config,
IEmulator emulator,
IGameInfo game)
/// <summary>
/// This is called when a Lua script causes the core to reboot, or a new core to load.
/// </summary>
public void Restart(Config config, IGameInfo game)
{
_apiContainer = ApiManager.RestartLua(newServiceProvider, LogToLuaConsole, _mainForm, _displayManager, _inputManager, _mainForm.MovieSession, _mainForm.Tools, config, emulator, game);
_apiContainer = ApiManager.RestartLua(_mainFormApi.Emulator.ServiceProvider, LogToLuaConsole, _mainFormApi, _displayManager, _inputManager, _mainFormApi.MovieSession, _mainFormApi.Tools, config, _mainFormApi.Emulator, game);
PathEntries = config.PathEntries;
foreach (var lib in Libraries.Values)
{
lib.APIs = _apiContainer;
Debug.Assert(ServiceInjector.UpdateServices(newServiceProvider, lib, mayCache: true));
Debug.Assert(ServiceInjector.UpdateServices(_mainFormApi.Emulator.ServiceProvider, lib, mayCache: true));
lib.Restarted();
}
}
@ -284,7 +264,7 @@ namespace BizHawk.Client.EmuHawk
closeCallback.Call();
}
RegisteredFunctions.Clear(_mainForm.Emulator);
RegisteredFunctions.Clear(_mainFormApi.Emulator);
ScriptList.Clear();
FormsLibrary.DestroyAll();
_lua.Dispose();
@ -308,7 +288,7 @@ namespace BizHawk.Client.EmuHawk
{
var nlf = (NamedLuaFunction)RegisteredFunctions.FirstOrDefault(predicate);
if (nlf == null) return false;
RegisteredFunctions.Remove(nlf, _mainForm.Emulator);
RegisteredFunctions.Remove(nlf, _mainFormApi.Emulator);
return true;
}
@ -354,7 +334,7 @@ namespace BizHawk.Client.EmuHawk
}
}
public static void Print(params object[] outputs)
public void Print(params object[] outputs)
{
_logToLuaConsoleCallback(outputs);
}