Godot Lua PluginScript

GDNative + PluginScript library that adds support for Lua as a scripting language in Godot.

Being a GDNative library, recompiling the engine is not required, so anyone with a built release copied to their project can use it. Being a PluginScript language, Lua can seamlessly communicate with scripts written in GDScript / C# / Visual Script and vice-versa. This way, one can use the language that best suits the implementation for each script and all of them can understand each other.

For some usage examples, check out lpscoroutine.lua and plugin/luarepl.lua.

Currently, only LuaJIT is supported, since the implementation is based on its FFI library.

Installing

Put a built release of the library into the project folder and restart Godot. Make sure the lua_pluginscript.gdnlib file is located at the res://addons/godot-lua-pluginscript folder.

Plugin

An editor plugin is included, currently with a barebones REPL for Lua expressions, located in the bottom panel of the editor. Enable the Lua PluginScript plugin in Project -> Project Settings... menu, in the Plugins tab.

Goals

  • Provide support for Lua as a scripting language in Godot in a way that does not require compiling the engine from scratch
  • Be able to seamlessly communicate with any other language supported by Godot, like GDScript, Visual Script and C#, in an idiomatic way
  • Simple script description interface that doesn't need requireing anything
  • Support for LuaJIT and Lua 5.2+
  • Support paths relative to res://* and exported game/app executable path for requireing Lua modules
  • Have a simple build process, where anyone with the cloned source code and installed build system + toolchain can build the project in a single step

Non-goals

  • Provide calls to all core Godot classes' methods via native method bindings
  • Support multithreading on the Lua side

Documentation

The API is documented using LDoc and is available online at github pages.

Documentation may be generated with the following command:

# make docs

Articles

  1. Designing Godot Lua PluginScript
  2. Implementing the library's skeleton
  3. Integrating LuaJIT and FFI
  4. Initializing and finalizing scripts (TODO)

Script example

This is an example of how a Lua script looks like. There are comments regarding some design decisions, which may change during development.

-- Class definitions are regular Lua tables, to be returned from the script
local MyClass = {}

-- Optional: set class as tool, defaults to false
MyClass.is_tool = true

-- Optional: set base class by name, defaults to 'Reference'
MyClass.extends = 'Node'

-- Optional: give your class a name
MyClass.class_name = 'MyClass'

-- Declare signals
MyClass.something_happened = signal()
MyClass.something_happened_with_args = signal("arg1", "arg2")

-- Values defined in table are registered as properties of the class
MyClass.some_prop = 42

-- The property function adds metadata to defined properties,
-- like setter and getter functions
MyClass.some_prop_with_details = property {
  -- ["default_value"] or ["default"] or [1] = property default value
  5,
  -- ["type"] or [2] = variant type, optional, inferred from default value
  -- All Godot variant type names are defined globally as written in
  -- GDScript, like bool, int, float, String, Array, Vector2, etc...
  -- Notice that Lua <= 5.2 does not differentiate integers from float
  -- numbers, so we should always specify int where appropriate
  -- or use int(5) in the default value instead
  type = int,
  -- ["get"] or ["getter"] = getter function or method name, optional
  get = function(self)
    return self.some_prop_with_details
  end,
  -- ["set"] or ["setter"] = setter function or method name, optional
  set = 'set_some_prop_with_details',
  -- ["usage"] = property usage, from enum godot_property_usage_flags
  -- optional, default to PropertyUsage.DEFAULT
  usage = PropertyUsage.DEFAULT,
  -- ["hint"] = property hint, from enum godot_property_hint
  -- optional, default to PropertyHint.NONE
  hint = PropertyHint.RANGE,
  -- ["hint_string"] = property hint text, only required for some hints
  hint_string = '1,10',
  -- ["rset_mode"] = property remote set mode, from enum godot_method_rpc_mode
  -- optional, default to RPCMode.DISABLED
  rset_mode = RPCMode.MASTER,
}

-- Functions defined in table are public methods
function MyClass:_ready()  -- function t:f(...) is an alias for function t.f(self, ...)
  -- Singletons are available globally
  local os_name = OS:get_name()
  print("MyClass instance is ready! Running on a " .. os_name .. " system")
end

function MyClass:set_some_prop_with_details(value)
    self.some_prop_with_details = value
    -- Indexing self with keys undefined in script will search base
    -- class for methods and properties
    self:emit_signal("something_happened_with_args", "some_prop_with_details", value)
end

function MyClass:get_some_prop_doubled()
  return self.some_prop * 2
end

-- In the end, table with class declaration must be returned from script
return MyClass

Status

  • [X] LuaJIT support
  • [ ] Lua 5.2+ support
  • [X] Useful definitions for all GDNative objects, with methods and metamethods
  • [X] A yield function similar to GDScript's, to resume after a signal is emitted (GD.yield)
  • [X] Working PluginScript language definition
  • [X] PluginScript script validation and template source code
  • [ ] PluginScript code editor callbacks
  • [ ] PluginScript debug callbacks
  • [ ] PluginScript profiling callbacks
  • [X] Package searcher for Lua and C modules that work with paths relative to the res:// folder and/or exported games' executable path
  • [X] Lua REPL
  • [X] API documentation
  • [ ] Example projects
  • [ ] Export plugin to minify Lua scripts
  • [ ] Drop-in binary release in GitHub
  • [ ] Submit to Asset Library

Building

This project uses git submodules for its dependencies, so be sure to activate submodules before building.

# clone this repository and activate submodules in a single command
git clone --recurse-submodules https://github.com/gilzoide/godot-lua-pluginscript.git

# or clone it normally and then activate submodules
git clone https://github.com/gilzoide/godot-lua-pluginscript.git
cd godot-lua-pluginscript
git submodule init
git submodule update

Build using make from project root, specifying the system as target:

# Choose one of the supported platforms, based on your operating system
make windows64  # x86_64
make windows32  # x86
make linux64    # x86_64
make linux32    # x86
make osx64      # "universal" multiarch x86_64 + amd64 dylib

The GDNativeLibrary file lua_pluginscript.gdnlib is already configured to use the built files stored in the build folder, so that one can use this repository directly inside a Godot project under the folder addons/godot-lua-pluginscript.

Third-party software

This project uses the following software:

  • godot-headers: headers for GDNative, released under the MIT license
  • LuaJIT: Just-In-Time Compiler (JIT) for the Lua programming language, released under the MIT license
  • High Level GDNative (HGDN): higher level GDNative API header, released to the Public Domain

Other projects for using Lua in Godot

  • https://github.com/perbone/luascript
  • https://github.com/Trey2k/lua
  • https://github.com/zozer/godot-lua-module
generated by LDoc 1.4.6 Last updated 2021-09-07 20:50:30