코드 샘플 #

이 페이지는 Lua 런타임을 사용하여 프로젝트를 개발할 때 템플릿으로 사용할 수 있는 기능에 대한 공통적인 예시를 제공합니다.

대결 핸들러 #

대결 핸들러는 권한 보유 멀티플레이어 대결에 대한 게임 입력과 운영을 처리하기 위한 서버 측 함수를 모두 표시합니다. 대결 핸들러 API대결 런타임 API 참조 페이지에서 대결 핸들러 기능을 살펴봅니다.

이것은 탁구 대결 핸들러에 대한 예시입니다. 서버에 수신된 메시지는 메시지를 발송한 사람한테 다시 전달됩니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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

컨텍스트 #

1
2
3
4
5
6
7
8
9
-- 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를 생성하고 사용자 지정 SQL 쿼리는 이 ID를 users 테이블로 입력합니다.

1
2
3
4
5
6
7
8
9
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 메시지를 전송하고, 서버에서 함수를 실행하여 결과를 반환합니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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")

사전 후크 #

아래의 코드 예시는 현재 사용자의 프로필을 가져와서 "{level: 12}"(으)로 인코딩된 JSON으로 추정되는 메타데이터를 확인합니다. 사용자의 수준이 낮은 경우, 오류가 발생하여 친구 추가 메시지가 서버에서 전달되지 않도록 합니다:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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")

사후 후크 #

아래의 예시 코드는 사용자가 친구를 추가할 때 사용자의 저장소에 레코드를 작성합니다. 함수에서 반환되는 모든 데이터는 삭제됩니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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를 사용하여 “pokeapi.lua"라고 하는 모듈을 만들어보겠습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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 호출을 생성할 수 있습니다:

Client
1
2
3
curl "http://127.0.0.1:7350/v2/rpc/get_pokemon" \
  -H 'authorization: Bearer <session token>'
  -d '"{"PokemonName": "dragonite"}"'
270

Client
1
2
3
4
const payload = { "PokemonName": "dragonite"};
const rpcid = "get_pokemon";
const pokemonInfo = await client.rpc(session, rpcid, payload);
console.log("Retrieved pokemon info: %o", pokemonInfo);
270

Client
1
2
3
4
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);
270

Client
1
2
3
4
5
6
7
8
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);
270

Client
1
2
3
4
5
6
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());
270

Client
1
2
3
4
5
6
7
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)])
270

Client
1
2
3
4
5
6
7
8
POST /v2/rpc/get_pokemon
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "PokemonName": "dragonite"
}
270

Code snippet for this language Defold has not been found. Please choose another language to show equivalent examples.

Related Pages