Server Framework Lua Setup

The game server allows you to load and run custom logic written in Lua as well as TypeScript and Go.

It’s useful to implement game code you would not want to run on the client or trust the client to provide unchecked inputs on. You can think of this feature of Nakama as similar to what is sometimes called Lambda or Cloud Functions in other systems. A good use case is if you wanted to grant the user a reward each day that they play the game.

Unlike when writing your server logic in Go or TypeScript, there is no toolchain or other setup needed when writing your code in Lua. This is because Lua is a powerful embeddable scripting language and does not need to be compiled or transpiled. This makes it a good choice if you want to get up and running quickly and easily.

You can learn more about how to write your Lua code in the official documentation.

Develop code

You can find the full Lua Nakama module function reference here.

Before you begin, create a new folder for your project and open it in an editor of your choice (e.g. VS Code).

Start by creating a new folder called modules and inside create a new file called main.lua. The code below is a simple Hello World example which uses the "Logger" to write a message.

1
2
local nk = require("nakama")
nk.logger_info("Hello World!")

Returning errors

When writing your own custom runtime code, you should ensure that any errors that occur when processing a request are passed back to the client appropriately. This means that the error returned to the client should contain a clear and informative error message as well as an appropriate HTTP status code.

Internally the Nakama runtime uses gRPC error codes and converts them to the appropriate HTTP status codes when returning the error to the client. The following table shows the mapping between gRPC code and HTTP code.

ErrorgRPC CodeHTTP Code
OK0200
CANCELED1499
UNKNOWN2500
INVALID_ARGUMENT3400
DEADLINE_EXCEEDED4504
NOT_FOUND5404
ALREADY_EXISTS6409
PERMISSION_DENIED7403
RESOURCE_EXHAUSTED8429
FAILED_PRECONDITION9400
ABORTED10409
OUT_OF_RANGE11400
UNIMPLEMENTED12501
INTERNAL13500
UNAVAILABLE14503
DATA_LOSS15500
UNAUTHENTICATED16401

Below shows an example of how you would return appropriate errors both in an RPC call as well as in a Before Hook.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
nk.register_rpc(function(context, payload)
  -- ... check if a guild already exists and set value of `already_exists` accordingly
  local already_exists = true

  if already_exists then
    error({ "guild name is in use", 6 })
  end

  return nk.json_encode({ success = true })
end, "lua_create_guild")

nk.register_req_before(function(context, payload)
	-- Only match custom Id in the format "cid-000000"
  if not string.match(payload.account.id, "^cid%-%d%d%d%d%d%d$") then
    error({ "input contained invalid data", 3 })
  end

  return payload
end, "AuthenticateCustom")

Restrictions

Compatibility

The Lua runtime is a Lua 5.1-compatible implementation with a small set of additional packages backported from newer versions - see available functions. For best results ensure your Lua modules and any 3rd party libraries are compatible with Lua 5.1.

Lua runtime code cannot use the Lua C API or extensions. Make sure your code and any 3rd party libraries are pure Lua 5.1.

The Lua virtual machine embedded in the server uses a restricted set of Lua standard library modules. This ensures the code sandbox cannot tamper with operating system input/output or the filesystem.

The list of available Lua modules are: base module, “math”, “string”, “table”, “bit32”, and a subset of “os” (only “clock”, “difftime”, “date”, and “time” functions).

Global state

The Lua runtime code is executed in instanced contexts. You cannot use global variables as a way to store state in memory or communicate with other Lua processes or function calls.

Sandboxing

The Lua runtime code is fully sandboxed and cannot access the filesystem, input/output devices, or spawn OS threads or processes. This allows the server to guarantee that Lua modules cannot cause fatal errors - the runtime code cannot trigger unexpected client disconnects or affect the main server process.

Run the project

You can use Docker with a compose file for local development or setup a binary environment for:

When this is complete you can run the game server and have it load your code:

1
nakama --logger.level DEBUG

The server logs will show this output or similar which shows that the code we wrote above was loaded and executed at startup.

1
{"level":"info","ts":"...","caller":"server/runtime_lua_nakama.go:1742","msg":"Hello World!","runtime":"lua"}

Next steps

Have a look at the Nakama project template which covers the following Nakama features: