Plugins

Edit

LÖVR has a small core. Extra features can be provided by Libraries written in Lua, or by plugins. Plugins are similar to libraries -- they can be required from Lua to access their features. However, instead of Lua files in a project folder, plugins are native libraries (.dll or .so files) that are placed next to the lovr executable.

Using Plugins

To use a plugin, place its library file next to the lovr executable and require it from Lua:

-- myplugin.dll is next to lovr.exe
local myplugin = require 'myplugin'

function lovr.load()
  myplugin.dothething()
end

Plugins are not officially supported in WebAssembly yet, but this is theoretically possible.

List of Known Plugins

lovr-luasocket HTTP and socket support via luasocket
lua-cjson Fast native JSON encoder/decoder
lua-cmsgpack Lua MessagePack C implementation.
lua-deepspeech Speech recognition using Mozilla's DeepSpeech library
lua-enet enet for UDP multiplayer servers/clients
lua-https Lua HTTPS module using native platform backends.
luv libuv bindings for Lua

Building Plugins with CMake

LÖVR's CMake build system has support for automatically building plugins from source code. In the main lovr folder, a plugins folder can be created, containing a subfolder for each plugin to build. CMake will check all the subfolders of plugins, building anything with a CMakeLists.txt file. Their libraries will automatically be moved next to the final lovr executable, or packaged into the apk on Android.

Inside the plugins' CMakeLists.txt scripts, the LOVR variable will be set to 1, so libraries can detect when they're being built as lovr plugins. Plugins also automatically have access to the version of Lua used by LÖVR, no calls to find_package are needed.

This makes it easier to manage plugins -- they can be copied, symlinked, cloned with git, or added as git submodules. A fork of lovr can be created that has this custom plugins folder, making it easy to quickly get a set of plugins on multiple machines. Version control also means that the plugins are versioned and tied to a known version of lovr.

Creating Plugins

Internally, a plugin is no different from a regular native Lua library. A plugin library only needs to have a Lua C function with a symbol named after the plugin:

int luaopen_supermegaplugin(lua_State* L) {
  // This code gets run when the plugin is required,
  // and the value it leaves at the top of the stack
  // is used as require's return value.
}

All of Lua's rules for native plugin loading, including processing of dots and hyphens and all-in-one loading, apply to LÖVR plugins. However, note that LÖVR plugins do not use package.cpath or Lua's default loader. The lovr.filesystem module has its own loader for loading plugins (it always looks for plugins next to the executable, and checks the lib/arm64-v8a folder of the APK).

Android

Android currently offers no way for a native library to get access to the JNIEnv* pointer if that native library was loaded by another native library. This means that LÖVR plugins have no way to use the JNI. To work around this, before LÖVR calls luaopen_supermegaplugin, it will call the JNI_OnLoad function from the plugin (if present) and pass it the Java VM. Example:

#include <jni.h>

static JNIEnv* jni;

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  (*vm)->GetEnv(vm, (void**) &jni, JNI_VERSION_1_6);
  return 0;
}

int luaopen_supermegaplugin(lua_State* L) {
  // can use jni!
}