Skip to content

Lua

The Lua language, specifically version 5.4, is the fundamental way how to add logic into a mod via scripting.

Fundamentals

To get started with Lua fundamentals, it is recommended to read through the Lua PIL book, available online. Alternatively there is a Tutorialspoint on Lua also available online.

This mod documentation will not explain the fundamentals. You must understand the fundamentals, by reading the PIL book for example, or any other source.

Game specific concepts

The Lua language does not have classes. The language also has no notion of public and private fields such as in more advanced languages as Java. Therefore, the modding API adopts some well known Lua concepts to get classes working. The next sections will explain classes, modules, and anything you need to know beyond the Lua language fundamentals.

Init file

An init.lua file is a script that gets run every time a game starts. The name init.lua is hardcoded and can not be changed. The expected path is mods/YourModName/scripts/init.lua.

The file is optional. There can be only one init.lua file in a mod folder. The game runs the script file only if it exists.

Each mod can have its own init.lua and the order in which they are run depends on the mod load order.

TODO: Show logs for mod order load

Require

You can use the require("a.b.c") function to import a lua file. The require function needs a path to the file, without the file extensions, and dots instead of slashes. The require can be used in any Lua script.

The require function looks up a file from any of the mod folders, including the game's base mod.

In the example below, let's assume that there are two mods, with the following contents (/ denotes a directory).

text
mods/
    YourModName/
        scripts/
            init.lua
            foo.lua
            subfolder/
                bar.lua
    AnotherModName/
        scripts/
            abc.lua

Then, in your init.lua to import foo.lua and bar.lua, you would do:

lua
-- File: mods/YourModName/scripts/init.lua
local foo = require("YourModName.scripts.foo")
local bar = require("YourModName.scripts.subfolder.bar")

To import another mod's file, the same rules apply:

lua
-- File: mods/YourModName/scripts/init.lua
local foo = ...
local bar = ...
local abc = require("AnotherModName.scripts.abc")

INFO

Any mod has any access to any other's mod files. This also applies to base mod, which is located in the installation folder of the game. To import anything from the base mod, you would do: local aaa = require("base.scripts.aaa"). Simply put, import is based on the mod's folder name. The game's core mod base has folder name "base".

Modules

In the previous example, you may have noticed that we assign a variable as a return value from the require function.

Consider a scenario where you want to create a helper function, or a class, or constants, in an another file and re-use it anywhere else.

For that, you would do the following:

lua
-- File: utils.lua
local module = {}

module.foo = function(a, b)
    return a + b
end

module.MY_CONSTANT = 42

return module

And then in an another file:

lua
-- The "utils" variable will reference the "module" table.
local utils = require("MyModName.scripts.utils")

-- Calls the function "foo" from file "utils.lua"
local res = utils.foo(10, 20)

DANGER

It is highly not recommended to use global functions (non-local). This could cause name collision between other mods, or some game's core functionality. Moreover, the plan is to move to Lua 5.6 in the future, which needs an explicit global keyword, and your mod would not be compatible if it relies on globals.

Please do NOT globally define anything!

Classes

TODO

Server versus sector contexts

A word "context" means the entire state of the Lua language, such as variables, imported modules, globals, etc. A name similar to this would be "state".

There are two different Lua contexts in this game:

  • Server context - A high level context which mostly is responsible for event handlers, deciding which sectors to run, handling player spawn, etc. Anything that is not specific to in-game location (sector).
  • Sector context - Runs entity logic. One context for each sector.

DANGER

It is not possible to share Lua data between sectors. For example, if you define a global variable in Lua context that belongs to the sector A, that variable will not exist in sector B's context, and vise versa.

It is also not possible to directly share Lua variables between sector context and the server context.

To share any data you must use Events, [Signals][#Signals)], and the Database.

Because a new sector context is initialized every time a sector starts, you see that require functions are called again and again. If you have a function that does something when a context starts, then you will see such function executed every time a sector loads.

::: note The server context does not run in "ticks" (a fixed period), only the sector context does. :::

Events

Events are a way how to send data from the sector context, for example from an entity, into the server context.

This can be useful in situations when some high level decision needs to be made. For example:

  • When a player dies, spawn a new ship in a safe home sector.
  • When an enemy is destroyed, update the mission log.
  • Tracking of achievements.
  • Faction hauler is destroyed, the faction's resources are lost.

However, this is not the only use case. You can also send events within the server context. For example, when a player enters the galaxy, we need to:

  • Ensure that the player has a fleet.
  • Spawn a starter ship if necessary.
  • Give some credits to the player if this is the first time they are spawning.
  • etc.

All of these items can be processed separately for the same event.

Signals

TODO