2013-10-28 19:13:01 +00:00
using System ;
2017-05-07 21:33:35 +00:00
using System.Collections.Generic ;
using System.IO ;
2014-06-01 22:02:59 +00:00
using System.Linq ;
2017-05-07 21:33:35 +00:00
using System.Reflection ;
2013-10-28 19:13:01 +00:00
using System.Threading ;
2013-10-31 16:58:56 +00:00
2017-07-10 04:51:02 +00:00
using NLua ;
2017-05-07 21:33:35 +00:00
using BizHawk.Common.ReflectionExtensions ;
2014-12-17 23:03:58 +00:00
using BizHawk.Emulation.Common ;
2017-05-07 21:33:35 +00:00
using BizHawk.Client.Common ;
2013-10-28 19:13:01 +00:00
2013-11-03 03:54:37 +00:00
namespace BizHawk.Client.EmuHawk
2013-10-28 19:13:01 +00:00
{
2019-01-03 22:50:55 +00:00
public class EmuLuaLibrary : PlatformEmuLuaLibrary
2013-10-28 19:13:01 +00:00
{
2019-12-31 22:46:05 +00:00
private readonly MainForm _mainForm ;
2014-01-21 00:43:57 +00:00
public EmuLuaLibrary ( )
{
2019-11-04 04:30:05 +00:00
// if (NLua.Lua.WhichLua == "NLua")
2017-07-15 21:36:18 +00:00
_lua [ "keepalives" ] = _lua . NewTable ( ) ;
2014-01-21 00:43:57 +00:00
}
2019-12-31 22:46:05 +00:00
public EmuLuaLibrary ( IEmulatorServiceProvider serviceProvider , MainForm mainForm )
2014-01-25 21:10:51 +00:00
: this ( )
2014-01-21 16:31:12 +00:00
{
2019-12-31 22:46:05 +00:00
_mainForm = mainForm ;
2019-12-14 17:52:48 +00:00
static ApiContainer InitApiHawkContainerInstance ( IEmulatorServiceProvider sp , Action < string > logCallback )
2019-11-16 07:19:04 +00:00
{
var ctorParamTypes = new [ ] { typeof ( Action < string > ) } ;
var ctorParams = new object [ ] { logCallback } ;
var libDict = new Dictionary < Type , IExternalApi > ( ) ;
2019-12-14 17:52:48 +00:00
foreach ( var api in Assembly . GetAssembly ( typeof ( EmuApi ) ) . GetTypes ( )
. Concat ( Assembly . GetAssembly ( typeof ( ToolApi ) ) . GetTypes ( ) )
2019-11-16 07:19:04 +00:00
. Where ( t = > t . IsSealed & & typeof ( IExternalApi ) . IsAssignableFrom ( t ) & & ServiceInjector . IsAvailable ( sp , t ) ) )
{
var ctorWithParams = api . GetConstructor ( ctorParamTypes ) ;
var instance = ( IExternalApi ) ( ctorWithParams = = null ? Activator . CreateInstance ( api ) : ctorWithParams . Invoke ( ctorParams ) ) ;
ServiceInjector . UpdateServices ( sp , instance ) ;
libDict . Add ( api , instance ) ;
}
2019-12-14 17:52:48 +00:00
return ApiHawkContainerInstance = new ApiContainer ( libDict ) ;
2019-11-16 07:19:04 +00:00
}
2014-01-21 16:31:12 +00:00
LuaWait = new AutoResetEvent ( false ) ;
Docs . Clear ( ) ;
2014-05-21 00:17:35 +00:00
// Register lua libraries
2019-12-15 18:08:10 +00:00
foreach ( var lib in Assembly . Load ( "BizHawk.Client.Common" ) . GetTypes ( )
. Concat ( Assembly . GetAssembly ( typeof ( EmuLuaLibrary ) ) . GetTypes ( ) )
. Where ( t = > typeof ( LuaLibraryBase ) . IsAssignableFrom ( t ) & & t . IsSealed & & ServiceInjector . IsAvailable ( serviceProvider , t ) ) )
2014-05-21 01:15:52 +00:00
{
2014-09-20 01:00:50 +00:00
bool addLibrary = true ;
2017-07-10 19:02:00 +00:00
var attributes = lib . GetCustomAttributes ( typeof ( LuaLibraryAttribute ) , false ) ;
2014-09-20 01:00:50 +00:00
if ( attributes . Any ( ) )
{
2019-11-29 19:14:30 +00:00
addLibrary = VersionInfo . DeveloperBuild | | ( ( LuaLibraryAttribute ) attributes . First ( ) ) . Released ;
2014-09-20 01:00:50 +00:00
}
if ( addLibrary )
{
var instance = ( LuaLibraryBase ) Activator . CreateInstance ( lib , _lua ) ;
instance . LuaRegister ( lib , Docs ) ;
2014-11-08 14:54:00 +00:00
instance . LogOutputCallback = ConsoleLuaLibrary . LogOutput ;
2016-12-06 16:35:11 +00:00
ServiceInjector . UpdateServices ( serviceProvider , instance ) ;
2019-12-14 17:52:48 +00:00
2019-12-31 22:46:05 +00:00
// TODO: make EmuHawk libraries have a base class with common properties such as this
// and inject them here
if ( instance is EmuHawkLuaLibrary emuHawkLibrary )
{
emuHawkLibrary . MainForm = _mainForm ;
}
2020-01-18 19:22:19 +00:00
ApiHawkContainerInstance = InitApiHawkContainerInstance ( serviceProvider , ConsoleLuaLibrary . LogOutput ) ;
2019-12-14 17:52:48 +00:00
if ( instance is DelegatingLuaLibraryEmu dlgInstanceEmu ) dlgInstanceEmu . APIs = ApiHawkContainerInstance ; // this is necessary as the property has the `new` modifier
else if ( instance is DelegatingLuaLibrary dlgInstance ) dlgInstance . APIs = ApiHawkContainerInstance ;
2014-09-20 01:00:50 +00:00
Libraries . Add ( lib , instance ) ;
}
2014-06-01 22:02:59 +00:00
}
2014-05-21 00:17:35 +00:00
2014-06-01 22:02:59 +00:00
_lua . RegisterFunction ( "print" , this , GetType ( ) . GetMethod ( "Print" ) ) ;
2014-05-21 00:17:35 +00:00
2014-06-01 22:02:59 +00:00
EmulatorLuaLibrary . FrameAdvanceCallback = Frameadvance ;
EmulatorLuaLibrary . YieldCallback = EmuYield ;
2017-05-07 21:33:35 +00:00
// Add LuaCanvas to Docs
Type luaCanvas = typeof ( LuaCanvas ) ;
2019-12-15 18:08:10 +00:00
foreach ( var method in luaCanvas . GetMethods ( ) )
2017-05-07 21:33:35 +00:00
{
2019-12-15 18:08:10 +00:00
if ( method . GetCustomAttributes ( typeof ( LuaMethodAttribute ) , false ) . Length ! = 0 )
{
Docs . Add ( new LibraryFunction ( nameof ( LuaCanvas ) , luaCanvas . Description ( ) , method ) ) ;
}
2017-05-07 21:33:35 +00:00
}
2014-01-21 16:31:12 +00:00
}
2019-11-16 07:19:04 +00:00
/// <remarks>lazily instantiated</remarks>
2019-12-14 17:52:48 +00:00
private static ApiContainer ApiHawkContainerInstance ;
2019-11-16 07:19:04 +00:00
2017-05-22 21:47:27 +00:00
private Lua _lua = new Lua ( ) ;
private Lua _currThread ;
private FormsLuaLibrary FormsLibrary = > ( FormsLuaLibrary ) Libraries [ typeof ( FormsLuaLibrary ) ] ;
private EventLuaLibrary EventsLibrary = > ( EventLuaLibrary ) Libraries [ typeof ( EventLuaLibrary ) ] ;
private EmulatorLuaLibrary EmulatorLuaLibrary = > ( EmulatorLuaLibrary ) Libraries [ typeof ( EmulatorLuaLibrary ) ] ;
2019-01-03 22:50:55 +00:00
public override void Restart ( IEmulatorServiceProvider newServiceProvider )
2016-08-28 16:07:26 +00:00
{
foreach ( var lib in Libraries )
{
2016-12-06 16:35:11 +00:00
ServiceInjector . UpdateServices ( newServiceProvider , lib . Value ) ;
2016-08-28 16:07:26 +00:00
}
}
2019-01-03 22:50:55 +00:00
public override void StartLuaDrawing ( )
2017-05-23 17:40:40 +00:00
{
2019-12-15 18:08:10 +00:00
if ( ScriptList . Count ! = 0 & & GuiLibrary . SurfaceIsNull )
2017-05-23 17:40:40 +00:00
{
GuiLibrary . DrawNew ( "emu" ) ;
}
}
2019-01-03 22:50:55 +00:00
public override void EndLuaDrawing ( )
2017-05-23 17:40:40 +00:00
{
2019-12-15 18:08:10 +00:00
if ( ScriptList . Count ! = 0 )
2017-05-23 17:40:40 +00:00
{
GuiLibrary . DrawFinish ( ) ;
}
}
2014-01-21 00:43:57 +00:00
public bool FrameAdvanceRequested { get ; private set ; }
2013-10-28 19:13:01 +00:00
2019-11-04 09:50:45 +00:00
public override LuaFunctionList RegisteredFunctions = > EventsLibrary . RegisteredFunctions ;
2014-02-14 01:27:38 +00:00
2019-01-03 22:50:55 +00:00
public override void WindowClosed ( IntPtr handle )
2013-10-31 16:45:08 +00:00
{
2014-06-01 22:02:59 +00:00
FormsLibrary . WindowClosed ( handle ) ;
2013-10-31 16:45:08 +00:00
}
2019-01-03 22:50:55 +00:00
public override void CallSaveStateEvent ( string name )
2013-10-31 16:58:56 +00:00
{
2014-06-01 22:02:59 +00:00
EventsLibrary . CallSaveStateEvent ( name ) ;
2013-10-31 16:58:56 +00:00
}
2019-01-03 22:50:55 +00:00
public override void CallLoadStateEvent ( string name )
2013-10-31 16:58:56 +00:00
{
2014-06-01 22:02:59 +00:00
EventsLibrary . CallLoadStateEvent ( name ) ;
2013-10-31 16:58:56 +00:00
}
2019-01-03 22:50:55 +00:00
public override void CallFrameBeforeEvent ( )
2013-10-31 16:58:56 +00:00
{
2014-06-01 22:02:59 +00:00
EventsLibrary . CallFrameBeforeEvent ( ) ;
2013-10-31 16:58:56 +00:00
}
2019-01-03 22:50:55 +00:00
public override void CallFrameAfterEvent ( )
2013-10-31 16:58:56 +00:00
{
2014-06-01 22:02:59 +00:00
EventsLibrary . CallFrameAfterEvent ( ) ;
2013-10-31 16:58:56 +00:00
}
2019-01-03 22:50:55 +00:00
public override void CallExitEvent ( LuaFile lf )
{
2019-11-29 21:02:40 +00:00
EventsLibrary . CallExitEvent ( lf ) ;
2019-01-03 22:50:55 +00:00
}
public override void Close ( )
2013-10-28 19:13:01 +00:00
{
2015-11-06 15:08:44 +00:00
FormsLibrary . DestroyAll ( ) ;
2016-10-06 20:17:05 +00:00
_lua . Close ( ) ;
2013-10-28 19:13:01 +00:00
_lua = new Lua ( ) ;
2014-06-01 22:02:59 +00:00
GuiLibrary . Dispose ( ) ;
2013-10-31 18:43:01 +00:00
}
2013-10-28 19:13:01 +00:00
2013-11-25 00:55:56 +00:00
public Lua SpawnCoroutine ( string file )
2013-10-28 19:13:01 +00:00
{
2013-11-26 01:21:24 +00:00
var lua = _lua . NewThread ( ) ;
2015-12-20 03:14:00 +00:00
var content = File . ReadAllText ( file ) ;
var main = lua . LoadString ( content , "main" ) ;
2014-01-21 00:43:57 +00:00
lua . Push ( main ) ; // push main function on to stack for subsequent resuming
2017-07-23 04:41:12 +00:00
//if (NLua.Lua.WhichLua == "NLua")
2017-07-15 21:36:18 +00:00
{
_lua . GetTable ( "keepalives" ) [ lua ] = 1 ;
//this not being run is the origin of a memory leak if you restart scripts too many times
_lua . Pop ( ) ;
}
2013-11-25 00:44:18 +00:00
return lua ;
2013-10-28 19:13:01 +00:00
}
2019-01-03 22:50:55 +00:00
public override void SpawnAndSetFileThread ( string pathToLoad , LuaFile lf )
{
lf . Thread = SpawnCoroutine ( pathToLoad ) ;
}
public override void ExecuteString ( string command )
2015-10-15 23:26:07 +00:00
{
_currThread = _lua . NewThread ( ) ;
_currThread . DoString ( command ) ;
2017-07-23 04:41:12 +00:00
//if (NLua.Lua.WhichLua == "NLua")
2017-07-15 21:36:18 +00:00
_lua . Pop ( ) ;
2015-10-15 23:26:07 +00:00
}
2015-01-02 19:36:32 +00:00
2019-11-29 18:47:50 +00:00
public override ResumeResult ResumeScript ( LuaFile lf )
2013-10-28 19:13:01 +00:00
{
2019-11-29 18:47:50 +00:00
_currThread = lf . Thread ;
2015-03-28 05:53:03 +00:00
2016-02-01 01:54:48 +00:00
try
2013-10-28 19:13:01 +00:00
{
2019-11-29 21:02:40 +00:00
LuaLibraryBase . SetCurrentThread ( lf ) ;
2016-02-01 01:54:48 +00:00
2019-11-29 18:47:50 +00:00
var execResult = _currThread . Resume ( 0 ) ;
2016-02-01 01:54:48 +00:00
2017-07-10 17:58:52 +00:00
_lua . RunScheduledDisposes ( ) ;
2017-05-19 14:47:18 +00:00
// not sure how this is going to work out, so do this too
2019-11-29 18:47:50 +00:00
_currThread . RunScheduledDisposes ( ) ;
2016-02-01 01:54:48 +00:00
_currThread = null ;
var result = new ResumeResult ( ) ;
if ( execResult = = 0 )
{
// terminated
result . Terminated = true ;
}
else
{
// yielded
result . WaitForFrame = FrameAdvanceRequested ;
}
FrameAdvanceRequested = false ;
return result ;
2013-10-28 19:13:01 +00:00
}
2016-02-01 01:54:48 +00:00
finally
2013-10-28 19:13:01 +00:00
{
2016-02-01 01:54:48 +00:00
LuaLibraryBase . ClearCurrentThread ( ) ;
2013-10-28 19:13:01 +00:00
}
}
2014-04-29 05:07:43 +00:00
public static void Print ( params object [ ] outputs )
2013-10-28 19:13:01 +00:00
{
2014-04-29 05:07:43 +00:00
ConsoleLuaLibrary . Log ( outputs ) ;
2013-10-28 19:13:01 +00:00
}
2013-10-31 18:09:40 +00:00
private void Frameadvance ( )
{
FrameAdvanceRequested = true ;
2013-11-25 00:55:56 +00:00
_currThread . Yield ( 0 ) ;
2013-10-31 18:09:40 +00:00
}
private void EmuYield ( )
{
2013-11-25 00:55:56 +00:00
_currThread . Yield ( 0 ) ;
2013-10-31 18:09:40 +00:00
}
2014-02-14 01:27:38 +00:00
public class ResumeResult
{
public bool WaitForFrame { get ; set ; }
public bool Terminated { get ; set ; }
}
2013-10-28 19:13:01 +00:00
}
}