This PR re-architects how C# functionality is exposed in Lua.
Replacing plugins is: LuaLibrary, LuaInstances and LuaReferences.
LuaLibrary
This thread-safe object will be the main entry point for interacting with c# code from lua.
LuaLibraries needs to implement ILuaLibraryAutoRegistration and by doing so they will be picked up by AutoFac, when in turns results in a ILuaLibraryRepository that contains all the available LuaLibraries.
LuaLibrary needs to be threadsafe!
When needing to use a LuaLibrary, you can in lua do
let audio = require("api/audio")
Slipstream changes require() and will check if there is a LuaLibrary matching the requested name. If so, the LuaLibrary is returned. LuaLibraries can expose themselves given any name, but by convention we'll use api/ prefix. The thought is, that you require a module. And these happens to be Slipstream functionality, but otherwise not really special.
Note: Multiple Lua scripts will get the same LuaLibrary instance. So it needs to be thread-safe!
All LuaLibraries exposes a instance(cfg) function, where cfg is a lua table containing settings to setup the instance. As minimum cfg needs to contain a id key, that identifies exactly which instance is required. If such instance doesn't exist, a new instance is created, otherwise the existing is used.
instance(cfg) will return a reference to the instance.
Lua Instances
A lua instance is started on request. And stopped again, if nothing refers to it. Lua scripts never interact directly with it, but via references. For really simple LuaLibraries, there might not even be a Instance, but for most it will create a InstanceThread that provides the features for the LuaLibrary.
For some LuaLibraries there is a singleton instance, meaning that the id of that instance is ignored, and you'll always get the one instance. For these, the instance id forced to be singleton. IRacingLuaLibrary is an example using a singleton instance.
Lua References
As mentioned earlier these are exposed directly in Lua. These are normally pretty might in code, as they would for most part, just send Events, which the Instance can process and act on. All references are discarded when the script is stopped (e.g lua file is deleted or changed). Once all references to a instance is dropped, the instance might shut down.
Plugins?
Are no more. A lot of code was removed as the new implementation is simpler (I hope) and doesn't need as much management from slipstream.
init.lua
Is changed a lot. Engnie still loads init.lua, but it differs greatly. For one, the syntax changed (instead of register_plugins, we do require("api/..."), but also the hot reloading of lua scripts is move from LuaManagerPlugin to init.lua - and simplified in the process.
This means that you can implement you own logic if you need to. And it also means that lua scripts can start other lua scripts.
Removed plugnis
Txrx was removed. It wasn't used, and should be implemented in a better way.
LuaPluginManager - replaced by init.lua code.
Migrating scripts
Look at LuaScripts to get an idea on how to migrate lua scripts.
We recommend that you create a config.lua that contains the arguments used for the instance(cfg) call. This means that order of scripts doesn't matter and only the features needed is activated.
Documentation
Documentation is still lacking. Also, now I've remove the Events from the documentation, as https://github.com/dennis/slipstream/pull/91 will offer a view of the events via UI. As this is based on code, these will not be outdated as the previous documentation were.
Example
Let's look at the following line:
local audio = require("api/audio"):instance({ id = "audio"})
audio:say("Hello world")
First require("api/audio") will return AudioLuaLibrary, which provides instance(...). For AudioLuaLIbrary the only required argument is id, which we set to audio. This means, that any other script refering to this id, will share the same resource.
instance({ id = "audio"}) will start up the AudioInstanceThread which takes care of TTS and playing audio. It also creates a AudioLuaReference to it, that is returned. We store this as audio variable.
audio:say("Hello world") invokes say on AudioLuaReference. This will publish an event, asking the instance to play audio.
After these lines is executed, and since there is no handle(event) function, nor any pending functions (via wait or debounce), the script will stop.
Lua scripts and their execution is also done by same approach. The script is evaluated by a Instance and you can get references to it - and start/stop scripts as you please. This can be seen in init.lua.
This PR re-architects how C# functionality is exposed in Lua.
Replacing plugins is: LuaLibrary, LuaInstances and LuaReferences.
LuaLibrary
This thread-safe object will be the main entry point for interacting with c# code from lua.
LuaLibraries needs to implement
ILuaLibraryAutoRegistration
and by doing so they will be picked up by AutoFac, when in turns results in aILuaLibraryRepository
that contains all the available LuaLibraries.LuaLibrary needs to be threadsafe!
When needing to use a LuaLibrary, you can in lua do
Slipstream changes
require()
and will check if there is a LuaLibrary matching the requested name. If so, the LuaLibrary is returned. LuaLibraries can expose themselves given any name, but by convention we'll useapi/
prefix. The thought is, that you require a module. And these happens to be Slipstream functionality, but otherwise not really special.Note: Multiple Lua scripts will get the same LuaLibrary instance. So it needs to be thread-safe!
All LuaLibraries exposes a
instance(cfg)
function, wherecfg
is a lua table containing settings to setup the instance. As minimumcfg
needs to contain aid
key, that identifies exactly which instance is required. If such instance doesn't exist, a new instance is created, otherwise the existing is used.instance(cfg)
will return a reference to the instance.Lua Instances
A lua instance is started on request. And stopped again, if nothing refers to it. Lua scripts never interact directly with it, but via references. For really simple LuaLibraries, there might not even be a Instance, but for most it will create a InstanceThread that provides the features for the LuaLibrary.
For some LuaLibraries there is a singleton instance, meaning that the id of that instance is ignored, and you'll always get the one instance. For these, the instance id forced to be
singleton
.IRacingLuaLibrary
is an example using a singleton instance.Lua References
As mentioned earlier these are exposed directly in Lua. These are normally pretty might in code, as they would for most part, just send Events, which the Instance can process and act on. All references are discarded when the script is stopped (e.g lua file is deleted or changed). Once all references to a instance is dropped, the instance might shut down.
Plugins?
Are no more. A lot of code was removed as the new implementation is simpler (I hope) and doesn't need as much management from slipstream.
init.lua
Is changed a lot.
Engnie
still loads init.lua, but it differs greatly. For one, the syntax changed (instead ofregister_plugins
, we dorequire("api/...")
, but also the hot reloading of lua scripts is move fromLuaManagerPlugin
toinit.lua
- and simplified in the process.This means that you can implement you own logic if you need to. And it also means that lua scripts can start other lua scripts.
Removed plugnis
Migrating scripts
Look at
LuaScripts
to get an idea on how to migrate lua scripts.We recommend that you create a
config.lua
that contains the arguments used for theinstance(cfg)
call. This means that order of scripts doesn't matter and only the features needed is activated.Documentation
Documentation is still lacking. Also, now I've remove the Events from the documentation, as https://github.com/dennis/slipstream/pull/91 will offer a view of the events via UI. As this is based on code, these will not be outdated as the previous documentation were.
Example
Let's look at the following line:
First
require("api/audio")
will returnAudioLuaLibrary
, which providesinstance(...)
. ForAudioLuaLIbrary
the only required argument is id, which we set toaudio
. This means, that any other script refering to this id, will share the same resource.instance({ id = "audio"})
will start up theAudioInstanceThread
which takes care of TTS and playing audio. It also creates aAudioLuaReference
to it, that is returned. We store this asaudio
variable.audio:say("Hello world")
invokessay
onAudioLuaReference
. This will publish an event, asking the instance to play audio.After these lines is executed, and since there is no
handle(event)
function, nor any pending functions (viawait
ordebounce
), the script will stop.Lua scripts and their execution is also done by same approach. The script is evaluated by a Instance and you can get references to it - and start/stop scripts as you please. This can be seen in init.lua.