소개 #

Nakama에는 사용자 지정 로직을 JavaScript 번들, Go 플러그인, Lua 모듈로 작성하기 위한 빠른 내장형 코드 런타임이 포함됩니다.

런타임 프레임워크는 게임이나 앱에서 서버 측 로직을 작성하는 데 필수적입니다. 클라이언트 장치 또는 브라우저에서 실행하고 싶지 않을 코드를 작성할 때 사용합니다. 서버에 배포하는 코드는 클라이언트에서 즉각 사용하여 비행 시 행동을 변경하고 새로운 기능을 더 빨리 추가할 수 있습니다. 이 코드는 권한 보유 로직을 실행하거나 유효성 검사를 실시하고 HTTPS를 통해 다른 서비스와 통합할 때 사용할 수 있습니다.

사용자의 친구 또는 가입할 수 있는 그룹과 같은 다양한 기능에서 규칙을 설정하려면 서버 측 코드를 사용합니다.

새로운 기능을 추가하거 행동을 커스터마이즈할 때 Nakama 소스 코드를 수정하여 다시 작성하는 것을 권장하지 않습니다. 내장된 런타임을 사용하는 것을 권장합니다.

이 페이지는 Nakama 런타임 프레임워크에서 사용할 수 있는 주요 개념과 기능에 대해서 다룹니다.

모듈 로딩 #

Tip
Heroic Lab에서는 JavaScript VM을 사용하는 것이 좋습니다.

기본적으로 서버는 서버 파일이나 시작 시 YAML 구성에서 지정된 폴더에 대해서 data/modules 폴더 내의 모든 파일을 스캔합니다. 서버를 시작할 때 명령 플래그를 통해서 모듈 폴더를 지정할 수도 있습니다.

런타임 경로 폴더에 있는 .lua, .so, .js 확장자 파일은 시작 시퀀스의 일부로 로드되고 평가됩니다. 각 런타임은 Nakama API에 액세스하여 클라이언트에서 메시지를 운영하고 필요 시 로직을 실행합니다.

다양하게 지원되는 언어는 Go, Lua, JavaScript의 우선순위에 따라 로드됩니다. 이렇게 하면 대결 핸들러 또는 RPC 함수/후크가 여러 개의 런타임에 등록되는 경우에도 가장 적합한 런타임을 사용하여 원활한 작업이 가능하도록 유연성을 제공합니다. 예를 들어, JavaScript 런타임에서 RPC 함수를 정의하여 Go에 작성된 대결 핸들러 세트와 대결을 생성할 수 있습니다.

JavaScript 런타임 #

JavaScript 런타임에는 index.js 파일이 필요합니다. 런타임 경로 내에서 코드가 로드되는 상대 파일 경로의 이름을 변경하려면 서버 YML 내에서 설정하거나 명령 플래그로 설정합니다.

1
nakama --runtime.js_entrypoint "some/path/index.js"

이 경로는 기본 또는 설정된 런타임 경로와 관련이 있어야 합니다.

Go 런타임 #

Go 런타임은 Go 플러그인 .so 공유 개체 파일을 찾습니다.

사용자 지정 Go 런타임 코드로 이 파일을 생성하는 방법은 Go 공유 개체 빌드하기를 참조하십시오.

Lua 런타임 #

Lua 런타임은 하위 디렉터리에 있는 파일을 포함한 모든 .lua 파일을 해석하고 로드합니다. 상대 경로를 포함하는 모듈로 참조할 수 있습니다.

각각의 Lua 파일은 모듈을 표시하고 각 모듈에 있는 모든 코드를 실시하여 함수를 등록할 수 있습니다.

런타임 컨텍스트 #

모든 런타임에서 등록된 함수는 context을(를) 첫 번째 인수로 받습니다. 코드가 언제, 어떻게 실행되는지에 따라 다른 필드가 포함됩니다. 요청이나 컨텍스트에서 요청하는 사용자에 대한 정보를 추출할 수 있습니다:

Server
1
local user_id = context.user_id
Server
1
2
3
4
userId, ok := ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)
if !ok {
  // User ID not found in the context.
}
Server
1
let userId = ctx.userId;

Lua에서 런타임 코드를 작성하는 경우, context 테이블을 통해서 필드에 직접 액세스할 수 있습니다. Go 런타임 컨텍스트는 표준 context.Context 유형이며 필드는 위와 같은 방법으로 액세스할 수 있습니다. JavaScript에서 컨텍스트는 속성을 포함하는 기본 개체입니다.

Go Context KeyGo TypeLua Context KeyJavaScript Context PropertyPurpose
RUNTIME_CTX_ENVmap[string]stringenvenvA table of key/value pairs which are defined in the YAML configuration of the server. This is useful to store API keys and other secrets which may be different between servers run in production and in development.
RUNTIME_CTX_MODEstringexecution_modeexecutionModeThe mode associated with the execution context. It’s one of these values: “run_once”, “rpc”, “before”, “after”, “match”, “matchmaker”, “leaderboard_reset”, “tournament_reset”, “tournament_end”.
RUNTIME_CTX_VERSIONstringversionversionThe Nakama server version.
RUNTIME_CTX_QUERY_PARAMSmap[string]stringquery_paramsqueryParamsQuery params that was passed through from HTTP request.
RUNTIME_CTX_SESSION.IDstringsession_idsessionIdThe user session associated with the execution context.
RUNTIME_CTX_USER_IDstringuser_iduserIdThe user ID associated with the execution context.
RUNTIME_CTX_USERNAMEstringusernameusernameThe username associated with the execution context.
RUNTIME_CTX_USER_SESSION_EXPint64user_session_expuserSessionExpThe user session expiry in seconds associated with the execution context.
RUNTIME_CTX_CLIENT_IPstringclient_ipclientIpThe IP address of the client making the request.
RUNTIME_CTX_CLIENT_PORTstringclient_portclientPortThe port number of the client making the request.
RUNTIME_CTX_MATCH_IDstringmatch_idmatchIdThe match ID that is currently being executed. Only applicable to server authoritative multiplayer.
RUNTIME_CTX_MATCH_NODEstringmatch_nodematchNodeThe node ID that the match is being executed on. Only applicable to server authoritative multiplayer.
RUNTIME_CTX_MATCH_LABELstringmatch_labelmatchLabelLabels associated with the match. Only applicable to server authoritative multiplayer.
RUNTIME_CTX_MATCH_TICK_RATEintmatch_tick_ratematchTickRateTick rate defined for this match. Only applicable to server authoritative multiplayer.

Go 컨텍스트 #

런타임 컨텍스트는 Go 컨텍스트와 구별됩니다. 유효하지 않은 요청(즉, 연결이 해제된 사용자의 요청)으로 인한 잠재적인 서버 과부하를 방지하기 위해서 모든 서버 요청에 Context 유형이 포함되는 것이 중요합니다.

사용자의 서버에 대한 HTTP 연결이 차단될 경우, Context이(가) 포함되면 컨텍스트를 취소할 수 있습니다. 요청 사항을 전체 체인에 전파해서 유효하지 않은 요청사항이 처리되지 않도록 합니다.

데이터베이스 핸들러 #

런타임에는 기본적인 게임 데이터베이스에서 사용할 수 있는 데이터베이스 개체가 포함됩니다. 이렇게 하면 게임 디자인과 로직의 일부로 사용자 지정 SQL 쿼리가 포함될 수 있습니다.

데이터베이스 핸들러가 데이터베이스에 연결할 수 있는 개수에 제한이 있습니다. 다른 오류와 함께 사용자에 대한 반응 시간이 느려질 수 있습니다. 이러한 문제점을 방지하기 위해서는 연관되는 열이 완료된 후 사용자 지정 SQL 쿼리가 연결을 정확하게 해제해야 합니다.

db.QueryContext() 또는 db.Query()을(를) 사용하는 경우, 데이터베이스 열 데이터 작업을 완료한 후 row.Close()을(를) 호출해야 합니다.

db.QueryRow() 또는 db.QueryRowContext()을(를) 사용하는 경우, 데이터베이스 열 데이터 작업을 완료한 후 row.Scan 또는 row.Close()을(를) 호출해야 합니다.

Nakama의 내장된 기능을 사용할 경우, 사용자 지정 SQL을 사용하지 않는 것이 좋습니다. 사용자 지정 테이블도 생성하면 안됩니다. 게임 디자인에서 이러한 옵션이 필요한 경우, 진행하기 전에 Heroic Labs에 문의하시기 바랍니다.

로거 #

서버 런타임에 포함된 로거 인스턴스를 통해 INFO, WARN, ERROR, DEBUG와(과) 같은 심각도를 사용하여 서버 코드에 있는 로그 메시지에 액세스하여 메시지를 작성할 수 있습니다.

TypeScript, Go, Lua 런타임에 대한 예시를 봅니다.

Nakama 모듈 #

Nakama 모듈은 서버에 내장된 코드 런타임에 포함되어 있습니다. 이 모듈은 사용자 지정 로직과 행동을 실행하기 위한 다양한 함수에 대한 액세스를 제공합니다.

사용 가능한 함수를 보려면 선호하는 언어에서 함수 참조를 보십시오:

기능 #

RPC 함수 #

원격 프로시저 호출(RPC)을 통해 런타임 코드에 등록된 함수를 호출하여 클라이언트에서 수신된 메시지를 작동하거나 필요 시 사용자 지정 로직을 실행하여 예와 같이 채팅 메시지에서 비속어를 필터링할 수도 있습니다.

RPC 함수는 클라이언트와 서버 간 호출을 통해서 호출할 수 있습니다.

후크 #

모든 런타임 코드는 서버 시작 시 평가되며 함수를 등록할 때 사용할 수 있습니다. 이러한 함수는 후크라고 합니다. 사전 후크를 등록하여 클라이언트 메시지를 인터셉트하고 활성화할 수 있고, 사후 후크를 등록하여 이벤트가 진행된 후에 함수를 호출할 수 있으며, 사용자 지정 RPC 후크는 클라이언트에서 호출할 수 있습니다.

런타임 내에서 함수를 등록하는 다양한 방법이 있지만, 각각의 방법은 클라이언트와 서버 사이에서 특정한 행위를 처리하기 위해서 사용됩니다. 예:

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
-- NOTE: Function arguments have been omitted in the example.
-- If you are sending requests to the server via the real-time connection, ensure that you use this variant of the function.
nk.register_rt_before()
nk.register_rt_after()

-- Otherwise use this.
nk.register_req_after()
nk.register_req_before()

-- If you'd like to run server code when the matchmaker has matched players together, register your function using the following.
nk.register_matchmaker_matched()

-- If you'd like to run server code when the leaderboard/tournament resets register your function using the following.
nk.register_leaderboard_reset()
nk.register_tournament_reset()

-- Similarly, you can run server code when the tournament ends.
nk.register_tournament_end()
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// NOTE: All Go runtime registrations must be made in the module's InitModule function.
//       Function arguments have been omitted in the example.

// If you are sending requests to the server via the real-time connection, ensure that you use this variant of the function.
initializer.RegisterBeforeRt()
initializer.RegisterAfterRt()

// Otherwise use the relevant before / after hook, e.g.
initializer.RegisterBeforeAddFriends()
initializer.RegisterAfterAddFriends()
// (...)

// If you'd like to run server code when the matchmaker has matched players together, register your function using the following.
initializer.RegisterMatchmakerMatched()

// If you'd like to run server code when the leaderboard/tournament resets register your function using the following.
initializer.RegisterLeaderboardReset()
initializer.RegisterTournamentReset()

// Similarly, you can run server code when the tournament ends.
initializer.RegisterTournamentEnd()
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// NOTE: All JavaScript runtime registrations must be made in the bundle's InitModule function.
//       Function arguments have been omitted in the example.

// If you are sending requests to the server via the real-time connection, ensure that you use this variant of the function.
initializer.registerRtBefore()
initializer.registerRtAfter()

// Otherwise use the relevant before / after hook, e.g.
initializer.registerBeforeAddFriends()
initializer.registerAfterAddFriends()
// (...)

// If you'd like to run server code when the matchmaker has matched players together, register your function using the following.
initializer.registerMatchmakerMatched()

// If you'd like to run server code when the leaderboard/tournament resets register your function using the following.
initializer.registerLeaderboardReset()
initializer.registerTournamentReset()

// Similarly, you can run server code when the tournament ends.
initializer.registerTournamentEnd()

사용 가능한 후크의 전체 목록을 보려면 메시지 이름을 참조합니다.

사전 후크 #

모든 함수는 클라이언트에서 수신된 메시지를 인터셉트하도록 등록할 수 있으며 사용자 지정 로직을 통해 작동(또는 거절)할 수 있습니다. 서버에서 표준 기능에 더해서 특정한 규칙을 실행할 때 유용합니다.

해당 기능이 입력될 것으로 기대되는 경우, Go에 있는 각 후크에서는 서버에서 처리되는 데이터를 포함하는 struct으로 요청 사항이 입력됩니다. Lua에서 두 번째 인수는 서버에서 처리되어 수신되는 데이터를 포함하는 payload이(가) 됩니다. JavaScript에서 네 번째 인수는 payload입니다.

수신된 동일한 구조 내에서 함수의 마지막 부분에 페이로드를 반환해야 합니다. payload(또는 Go에서 0이 아닌 error) 대신에 nil(Lua) 또는 null|undefined(JavaScript)을(를) 반환하기로 선택하면 서버에서 메시지 처리가 중단됩니다. 이것은 서버가 특정한 메시지를 수락하거나 특정 서버 기능을 비활성화/차단하여 서버를 중단시키는 데 유용할 수 있습니다.

사후 후크 #

사전 후크와 유사하게, 함수를 추가하여 메시지를 작동할 수 있습니다. 파이프라인에서 메시지가 처리되고 나면 등록된 함수가 호출됩니다. 응답 메시지가 클라이언트로 전송되고 난 후에 사용자 지정 코드가 비동기적으로 실행됩니다.

두 번째 인수는 요청에 대한 서버 응답을 포함하는 “출력 페이로드"입니다. 세 번째 인수는 요청 사항에 대해서 서버로 전달되는 데이터를 포함하는 “입력 페이로드"를 포함합니다.

사후 후크는 클라이언트로 다시 전달되는 응답 페이로드를 변경할 수 없으며, 오류는 응답이 전송되는 것을 방지할 수 없습니다.

RPC 후크 #

클라이언트와 서버 사이의 로직은 클라이언트가 실행할 수 있는 RPC 함수로 쉽게 해결됩니다. 이러한 목적으로 Nakama에서는 사용자 지정 RPC 후크의 등록을 지원합니다.

클라이언트 코드 내에서 등록된 RPC의 ID를 사용하여 RPC 메시지를 전송하고, 서버에서 함수를 실행하여 결과를 반환합니다.

Go 런타임 코드에서 결과는 (string, error)(으)로 반환됩니다. Lua 런타임 코드에서 결과는 항상 Lua 문자열(또는 nil)로 반환됩니다. JavaScript 런타임 코드에서 결과는 항상 문자열, null이거나 누락(정의되지 않음)됩니다.

서버 대 서버 #

RPC 함수가 클라이언트 또는 서버 간 호출인지 확인하기 위해서 컨텍스트에 사용자 ID를 확인할 수 있습니다. 서버 간 호출에는 사용자 ID가 없습니다. 클라이언트에서 액세스할 수 없도록 함수의 범위를 조정하려면 컨텍스트에서 사용자 ID를 찾고 오류를 반환합니다.

서버 런타임 예시를 참조하십시오.

1회 실행 #

런타임 환경에서는 1회만 실행해야 하는 코드를 실행할 수 있습니다. 이것은 실행하거나 제3자 서비스로 등록해야 하는 사용자 지정 SQL 쿼리가 있는 경우 유용합니다.

TypeScript, Go, Lua에 대한 실행 예시를 참조하십시오.

메시지 이름 #

런타임 코드가 Go에 있는 경우, 런타임 패키지에서 사용할 수 있는 후크의 전체 목록은 인터페이스 정의를 참조하십시오.

다음 요청 사항 이름을 사용하여 사전사후 후크를 등록합니다:

Request NameDescription
AddFriends사용자 계정에 ID 또는 사용자 이름으로 친구를 추가합니다.
AddGroupUsers그룹에 사용자를 추가합니다.
AuthenticateCustom서버에 대해 사용자 지정 ID로 사용자를 인증합니다.
AuthenticateDevice장치에 대해 사용자 지정 ID로 사용자를 인증합니다.
AuthenticateEmail서버에 대해 이메일+암호로 사용자를 인증합니다.
AuthenticateFacebook서버에 대해 Facebook OAuth 토큰으로 사용자를 인증합니다.
AuthenticateGameCenter서버에 대해 Apple의 GameCenter로 사용자를 인증합니다.
AuthenticateGoogle서버에 대해 Google로 사용자를 인증합니다.
AuthenticateSteam서버에 대해 Steam으로 사용자를 인증합니다.
BlockFriendsID 또는 사용자 이름으로 한 명 이상의 사용자를 차단합니다.
CreateGroup현재 사용자를 소유자로 새 그룹을 생성합니다.
DeleteFriendsID 또는 사용자 이름으로 한 명 이상의 사용자를 삭제합니다.
DeleteGroupID로 하나 이상의 그룹을 삭제합니다.
DeleteLeaderboardRecord리더보드 기록을 삭제합니다.
DeleteNotifications현재 사용자에 대해 하나 이상의 알림을 삭제합니다.
DeleteStorageObjectsID 또는 사용자 이름으로 하나 이상의 개체를 삭제합니다.
GetAccount현재 사용자의 계정을 가져옵니다.
GetUsersID 및/또는 사용자 이름으로 0명 이상의 사용자를 가져옵니다.
Healthcheck부하 분산 장치가 서비스 확인에 사용할 수 있는 상태 검사 기능.
ImportFacebookFriendsFacebook 친구를 가져와 사용자 계정에 추가합니다.
JoinGroup공개 그룹에 즉시 참여하거나 비공개 그룹에 참여 요청을 합니다.
KickGroupUsers그룹에서 사용자 집합을 강퇴시킵니다.
LeaveGroup사용자가 멤버로 참여하고 있는 그룹에서 나갑니다.
LinkCustom현재 사용자 계정의 소셜 프로필에 사용자 지정 ID를 추가합니다.
LinkDevice현재 사용자 계정의 소셜 프로필에 장치 ID를 추가합니다.
LinkEmail현재 사용자 계정의 소셜 프로필에 이메일+암호를 추가합니다.
LinkFacebook현재 사용자 계정의 소셜 프로필에 Facebook을 추가합니다.
LinkGameCenter현재 사용자 계정의 소셜 프로필에 Apple의 GameCenter를 추가합니다.
LinkGoogle현재 사용자 계정의 소셜 프로필에 Google을 추가합니다.
LinkSteam현재 사용자 계정의 소셜 프로필에 Steam을 추가합니다.
ListChannelMessages채널의 메시지 기록을 나열합니다.
ListFriends현재 사용자의 모든 친구를 나열합니다.
ListGroups주어진 필터를 기반으로 그룹을 나열합니다.
ListGroupUsers그룹에 속한 모든 사용자를 나열합니다.
ListLeaderboardRecords리더보드 기록을 나열합니다.
ListMatches실행 중인 매치의 목록을 가져옵니다.
ListNotifications알림 목록을 가져옵니다.
ListStorageObjects주어진 컬렉션에서 공개적으로 읽을 수 있는 저장소 개체를 나열합니다.
ListUserGroups현재 사용자가 속한 그룹을 나열합니다.
PromoteGroupUsers그룹의 사용자 집합을 다음 역할로 승격합니다.
DemoteGroupUsers그룹의 사용자 집합을 더 낮은 역할로 강등합니다.
ReadStorageObjects저장소 개체를 가져옵니다.
UnlinkCustom현재 사용자 계정의 소셜 프로필에서 사용자 지정 ID를 제거합니다.
UnlinkDevice현재 사용자 계정의 소셜 프로필에서 장치 ID를 제거합니다.
UnlinkEmail현재 사용자 계정의 소셜 프로필에서 이메일+암호를 제거합니다.
UnlinkFacebook현재 사용자 계정의 소셜 프로필에서 Facebook을 제거합니다.
UnlinkGameCenter현재 사용자 계정의 소셜 프로필에서 Apple의 GameCenter를 제거합니다.
UnlinkGoogle현재 사용자 계정의 소셜 프로필에서 Google을 제거합니다.
UnlinkSteam현재 사용자 계정의 소셜 프로필에서 Steam을 제거합니다.
UpdateAccount현재 사용자 계정의 필드를 업데이트합니다.
UpdateGroup주어진 그룹의 필드를 업데이트합니다.
WriteLeaderboardRecord리더보드에 기록을 씁니다.
WriteStorageObjects저장소 엔진에 개체를 씁니다.

이름은 대소문자를 구분합니다. 자세한 내용은 apigrpc.proto을 참조하십시오.

실시간 사전 및 사후 후크에 대해서는 다음의 메시지 이름을 사용합니다:

Message NameDescription
ChannelJoinJoin a realtime chat channel.
ChannelLeaveLeave a realtime chat channel.
ChannelMessageSendSend a message to a realtime chat channel.
ChannelMessageUpdateUpdate a message previously sent to a realtime chat channel.
ChannelMessageRemoveRemove a message previously sent to a realtime chat channel.
MatchCreateA client to server request to create a realtime match.
MatchDataSendA client to server request to send data to a realtime match.
MatchJoinA client to server request to join a realtime match.
MatchLeaveA client to server request to leave a realtime match.
MatchmakerAddSubmit a new matchmaking process request.
MatchmakerRemoveCancel a matchmaking process using a ticket.
StatusFollowStart following some set of users to receive their status updates.
StatusUnfollowStop following some set of users to no longer receive their status updates.
StatusUpdateSet the user’s own status.

이름은 대소문자를 구분합니다. 자세한 내용은 realtime.proto을 참조하십시오.

제한 사항 #

런타임에 대한 제한 사항은 TypeScript, Go, Lua 페이지를 참조하십시오.

배경 작업 #

사용자가 없을 때 수행되는 “유효하지 않은 작업"과 불필요한 서버 로드를 방지하기 위해서 이벤트 기반 경로에서 배경 작업을 수행하면 안 됩니다. 클라이언트는 사용자가 반환할 경우 RPC 호출을 실행하고, 요청된 함수에서 게임 로직에 의해서 업데이트가 필요하고 사용 사례가 실행될 경우에 호출이 실행됩니다.

예약된 배경 작업은 사용자의 활동 여부와 무관하게 전체 사용자에 대해서 업데이트를 실행하지만, 이 방법은 게임 내에서 사용자가 활성화되어 있는 경우에만 작업이 실행되도록 합니다.

배경 작업을 사용하면 모든 작업이 하나의 Nakama 인스턴스에 제한되어 모든 인스턴스에서 복제나 인스턴스에 비균질성 워크로드가 필요한 다른 문제가 발생할 수 있습니다.

Related Pages