# 代码示例

**URL:** https://heroiclabs.com/docs/zh/nakama/server-framework/lua-runtime/code-samples/
**Summary:** Lua 运行库开发示例。

---


# 代码示例

本页提供了一些可用功能的常见示例，在使用 Lua 运行库开发项目时，可以将这些功能用作模板。

## 比赛处理程序

比赛处理程序表示所有服务器端函数，用于处理[权威多人游戏](../../../concepts/multiplayer/authoritative/)比赛的游戏输入和操作。请参阅[比赛处理程序 API](../function-reference/match-handler/) 和 [比赛运行时 API](../function-reference/match-runtime/)参考页面，了解比赛处理程序功能。

这是乒乓比赛处理程序示例。服务器接收到的消息将被广播回发送消息的对等方。

```lua
local nk = require("nakama")

local M = {}

function M.match_init(context, setupstate)
  local gamestate = {
    presences = {}
  }

  local tickrate = 1 -- per sec
  local label = ""

  return gamestate, tickrate, label
end

function M.match_join_attempt(context, dispatcher, tick, state, presence, metadata)
  local acceptuser = true
  return state, acceptuser
end

function M.match_join(context, dispatcher, tick, state, presences)
  for _, presence in ipairs(presences) do
    state.presences[presence.session_id] = presence
  end

  return state
end

function M.match_leave(context, dispatcher, tick, state, presences)
  for _, presence in ipairs(presences) do
    state.presences[presence.session_id] = nil
  end

  return state
end

function M.match_loop(context, dispatcher, tick, state, messages)
  for _, p in pairs(state.presences) do
    nk.logger_info(string.format("Presence %s named %s", p.user_id, p.username))
  end

  for _, m in ipairs(messages) do
    nk.logger_info(string.format("Received %s from %s", m.data, m.sender.username))
    local decoded = nk.json_decode(m.data)

    for k, v in pairs(decoded) do
      nk.logger_info(string.format("Key %s contains value %s", k, v))
    end

    -- PONG message back to sender
    dispatcher.broadcast_message(1, m.data, { m.sender })
  end

  return state
end

function M.match_terminate(context, dispatcher, tick, state, grace_seconds)
  local message = "Server shutting down in " .. grace_seconds .. " seconds"
  dispatcher.broadcast_message(2, message)

  return nil
end

local function match_signal(context, dispatcher, tick, state, data)
  return state, "signal received: " .. data
end

return M
```

## 上下文

```lua
-- Getting the calling user ID from the context
local user_id = context.user_id

-- Getting the environment variables from the context
local env = context.env
local secret_key = env["SECRET_KEY"]
if not secret_key then
  -- Did not find the environment variable
end
```

## 数据库处理程序

此示例创建系统 ID（不能从客户端使用的 ID），自定义 SQL 查询将其插入 `users` 表中：

```lua
nk.run_once(function(context)
  local system_id = context.env["SYSTEM_ID"]

  nk.sql_exec([[
INSERT INTO users (id, username)
VALUES ($1, $2)
ON CONFLICT (id) DO NOTHING
  ]], { system_id, "system_id" })
end)
```

## RPC

以下示例注册带有标识符 `custom_rpc_func_id` 的函数。然后可在客户端代码中使用此 ID 发送 RPC 消息，在服务器上执行函数并返回结果。

```lua
local nk = require("nakama")

local function custom_rpc_func(context, payload)
  nk.logger_info(string.format("Payload: %q", payload))

  -- "payload" is bytes sent by the client we'll JSON decode it.
  local json = nk.json_decode(payload)

  return nk.json_encode(json)
end

nk.register_rpc(custom_rpc_func, "custom_rpc_func_id")
```

## Before 挂钩

以下代码示例获取当前用户的配置文件，检查元数据。通常假定此元数据经过 JSON 编码，其中有 `"{level: 12}"`。如果用户级别太低，则会抛出一个错误，以阻止在服务器中向前传递“好友添加”消息：

```lua
local nk = require("nakama")

local function limit_friends(context, payload)
  local user = nk.users_get_id({context.user_id})[1]
  -- Let's assume we've stored a user's level in their metadata.
  if user.metadata.level < 10 then
      error("Must reach level 10 before you can add friends.")
  end
  return payload -- important!
end

nk.register_req_before(limit_friends, "AddFriends")
```

## After 挂钩

下面的示例代码在用户添加好友时将记录写入用户的存储。函数返回的任何数据都将被丢弃。

```lua
local nk = require("nakama")

local function add_reward(context, outgoing_payload, incoming_payload)
  local value = {
    user_ids = {incoming_payload.user_id}
  }

  local object = {
    collection = "rewards",
    key = "reward",
    user_id = context.user_id,
    value = value
  }

  nk.storage_write({ object })
end

nk.register_req_after(add_reward, "AddFriends")
```

## 示例模块

下例中，我们使用 [Pokéapi](http://pokeapi.co/)，并建立一个名为“pokeapi.lua”的实用模块。

```lua
local nk = require("nakama")

local M = {}

local API_BASE_URL = "https://pokeapi.co/api/v2"

function M.lookup_pokemon(name)
  local url = string.format("%s/pokemon/%s", API_BASE_URL, name)
  local method = "GET"
  local headers = {
    ["Content-Type"] = "application/json",
    ["Accept"] = "application/json"
  }

  local success, code, _, body = pcall(nk.http_request, url, method, headers, nil)

	if (not success) then
    nk.logger_error(string.format("Failed request %q", code))
    error(code)
  elseif (code >= 400) then
    nk.logger_error(string.format("Failed request %q %q", code, body))
    error(body)
  else
    return nk.json_decode(body)
  end
end

return M

-- We can import the code up to this point into another module we'll call "pokemon.lua" which will register an RPC call.
local nk = require("nakama")
local pokeapi = require("pokeapi")

local function get_pokemon(_, payload)
  -- We'll assume payload was sent as JSON and decode it.
  local json = nk.json_decode(payload)
  local success, result = pcall(pokeapi.lookup_pokemon, json.PokemonName)

	if (not success) then
    error("Unable to lookup pokemon.")
  else
    local pokemon = {
      name = result.name,
      height = result.height,
      weight = result.weight,
      image = result.sprites.front_default
    }

    return nk.json_encode(pokemon)
  end
end

nk.register_rpc(get_pokemon, "get_pokemon")
```

现在，您可以从客户端对 Pokémon 进行 RPC 调用：

{{< code type="client" >}}
```bash
curl "http://127.0.0.1:7350/v2/rpc/get_pokemon" \
  -H 'authorization: Bearer <session token>'
  -d '"{"PokemonName": "dragonite"}"'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const payload = { "PokemonName": "dragonite"};
const rpcid = "get_pokemon";
const pokemonInfo = await client.rpc(session, rpcid, payload);
console.log("Retrieved pokemon info: %o", pokemonInfo);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
var payload = JsonWriter.ToJson(new { PokemonName = "dragonite" });
var rpcid = "get_pokemon";
var pokemonInfo = await client.RpcAsync(session, rpcid, payload);
System.Console.WriteLine("Retrieved pokemon info: {0}", pokemonInfo);
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = [](const NRpc& rpc)
{
  	std::cout << "Retrieved pokemon info: " << rpc.payload << std::endl;
};

string payload = "{ "PokemonName": "dragonite" }";
string rpcid = "get_pokemon";
client->rpc(session, rpcid, payload, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
Map<String, String> payloadData = new HashMap<>();
payloadData.put("PokemonName", "dragonite");
String payload = new Gson().toJson(payloadData, payloadData.getClass());
String rpcid = "get_pokemon";
Rpc pokemonInfo = client.rpc(session, rpcid, payload);
System.out.format("Retrieved pokemon info: %s", pokemonInfo.getPayload());
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var payload = {"PokemonName": "dragonite"}
var rpc_id = "get_pokemon"
var pokemon_info : NakamaAPI.ApiRpc = yield(client.rpc_async(session, rpc_id, JSON.print(payload)), "completed")
if pokemon_info.is_exception():
	print("An error occurred: %s" % pokemon_info)
	return
print("Retrieved pokemon info: %s" % [parse_json(pokemon_info.payload)])
```
{{< / code >}}


{{< code type="client" >}}
```shell
POST /v2/rpc/get_pokemon
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "PokemonName": "dragonite"
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local payload = { PokemonName = "dragonite"}
local rpcid = "get_pokemon"
local pokemon_info = client.rpc_func(rpcid, json.encode(payload)) 
pprint("Retrieved pokemon info:", pokemon_info)
```
{{< / code >}}