MoonSharp is meant to support multiple platforms. It can’t choose which platforms it will run on, as that’s the choice of the end user of the library, so it has somehow to be able to run, say, as a daemon in Linux, as a WPF application, as an app on a mobile phone or a game on a console. For example, APIs as simple as a humble FileStream are not available on Windows Store Apps.
Also, MoonSharp doesn’t know how it’s supposed to run and work on the app using it. For example, what if you want
loadfile to load from embedded resources instead ?
For these reasons MoonSharp has two objects hierarchies:
Note that usually script loaders are independent from platform accessors, although they can use their method if they want. This just to say that if, for example, your platform accessor does not support loading from file, it doesn’t necessarily mean that your script loader can’t and viceversa.
They are two separate objects because they handle two different responsibilities. A script loader is responsible for how scripts are loaded from files. A platform accessor is responsible for how library functions which should call OS APIs (mostly in the ‘os’ and ‘io’ modules) are handled.
Depending on the platforms you have these choices of script loaders :
FileSystemScriptLoader : raw access to files on filesystem, customizable, not supported on portable class libraries
ReplInterpreterScriptLoader : same as
FileSystemScriptLoader, plus uses the same logic as Lua to get the paths from environment variables (
"?;?.lua", whatever exists first)
EmbeddedResourcesScriptLoader : offers access to embedded resources of a given assembly instead of the file system
InvalidScriptLoader : throws exceptions
UnityAssetsScriptLoader : works on Unity3D, to load scripts from text assets
The default script loader used by MoonSharp if no redefinition happens is selected as follows:
UnityAssetsScriptLoader over the
Assets/Resources/MoonSharp/Scripts path. Files must have .txt extension (Unity is weird).
InvalidScriptLoader is selected (and you can’t load from files unless you do something)
FileSystemScriptLoader is used
Let’s say we want to have script loaded from embedded resources of the current assembly.
There are basically two ways, one local one global.
First, given a script you can change its script loader with:
Otherwise, the following can be used to specify that all newly created scripts should use the new script loader:
A common need is to change which directories are used by the
require function to load modules.
Most script loaders extend the
ScriptLoaderBase class which exposes a
ModulePaths property which contains all the paths that will be checked to load modules.
You can easily change the paths:
Note that the
ScriptLoaderBase also checks the
LUA_PATH global variable in the current
_ENV to decide which paths to use to load modules.
If you want the
LUA_PATH global to be ignored, use:
A word of caution. Changing the
IgnoreLuaPathGlobalis not enough to “sandbox” which files can be loaded. If you need this, implement a custom script loader.
Embedding scripts as resources is easily done.
In Visual Studio:
Then, right-click on the file in Solution Explorer, the following window will appear:
Make sure “Build Action” is “Embedded Resource”.
After this, let’s just use the right script loader:
Similar steps can be followed in other IDEs, for example Xamarin.
Time to create your own script loader.
You have two options basically : extend
ScriptLoaderBase (reccommended) or implement
Both are pretty self-explanatory, but let’s go with the first for the sake of it.
Our script loader, will actually generate a little script on the fly with the name of the requested file instead of really loading:
Getting this to work is pretty easy:
Running all of this, as predictable, will print:
A request to load 'somemodule_module.lua' has been made
A request to load 'someothermodule.lua' has been made