이벤트 #

이벤트는 게임 서버에 데이터를 보내고 서버의 백그라운드에서 처리할 데이터를 만드는 강력한 방법입니다. Analytics, Ads, 인앱 구매 등과 같은 타사 서비스로 전달할 수 있는 데이터를 만들고 받을 수 있습니다

게임 스튜디오와 개발자는 이를 통해 다양한 장점을 활용할 수 있습니다.

  • 게임 클라이언트를 축소하는 클라이언트 측 SDK의 수를 줄입니다. FTUE와 게임을 다운로드할 플레이어의 수를 개선하는 데 좋습니다.
  • 게임 클라이언트에서 다양한 타사 서비스에 사용하는 네트워크 트래픽이 적습니다.
  • 개발팀이 소비하는 통합 시간과 활성 유지 관리 비용이 크게 줄어듭니다.

좋은 이용 사례는 클라이언트 또는 서버측 기능에서 발생하는 이벤트를 사용하여 게임 분석 또는 LiveOps를 구현하는 것입니다. 이런 이벤트는 Nakama 서버에서 백그라운드로 처리됩니다. 플레이어는 게임 플레이 중단 경험이 최소한으로 줄어들고 개발자는 분석을 통한 피드백으로 게임을 최적화하고 개선할 수 있습니다.

내부적으로 이 기능은 수신된 이벤트를 저장하기 위한 고성능 순환 버퍼와, 이벤트 핸들러가 따라잡을 수 없는 경우 수신된 많은 이벤트에 의해 서버 과부하가 발생하지 않도록 소비자 작업자 풀(이벤트 핸들러)로 구현됩니다.

이벤트 생성 #

이벤트를 게임 클라이언트에서 서버로 보내거나 서버에서 생성할 수 있습니다. 이벤트에는 추가 정보로 이벤트를 장식하는 속성 맵과 이름이 있습니다.

이벤트 보내기 #

이벤트 API를 사용하여 서버로 보냅니다.

Client
1
curl -vvv -H "Authorization: Bearer $SESSION"  http://127.0.0.1:7350/v2/event -d '{"name": "my_event", "properties": {"my_key": "my_value"}}'

Code snippet for this language .NET/Unity has not been found. Please choose another language to show equivalent examples.
Code snippet for this language C++/Unreal/Cocos2d-x has not been found. Please choose another language to show equivalent examples.
Code snippet for this language JavaScript/Cocos2d-js has not been found. Please choose another language to show equivalent examples.
Code snippet for this language gdscript has not been found. Please choose another language to show equivalent examples.
Code snippet for this language Java/Android has not been found. Please choose another language to show equivalent examples.
Code snippet for this language Defold has not been found. Please choose another language to show equivalent examples.
Code snippet for this language REST has not been found. Please choose another language to show equivalent examples.

이벤트 생성 #

서버측 모듈을 사용하여 Go 코드에 이벤트를 생성합니다.

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// import "github.com/heroiclabs/nakama-common/api"
// import "github.com/heroiclabs/nakama-common/runtime"

// ctx context.Context, nk runtime.NakamaModule
evt := &api.Event{
    Name:       "event_name"
    Properties: map[string]string{
       "my_key": "my_value",
    },
    External:   true,
}
if err := nk.Event(ctx, evt); err != nil {
    // Handle error.
}
Server
1
2
3
4
5
6
7
8
9
local nk = require("nakama")

local properties = {
  my_key = "my_value"
}
local timestamp = nk.time
local external = false

nk.event("event_name", properties, timestamp, external)
Server
1
2
3
4
5
let properties = {
  my_key: 'my_value'
};

nk.event('event_name', properties);

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

서버의 다른 기능에서 이벤트를 보내려는 경우 서버 런타임의 후크 이후를 활용할 수도 있습니다. 예를 들어 사용자 계정이 업데이트되고 사용자가 처리할 이벤트를 보내려는 경우를 들 수 있습니다.

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func afterUpdateAccount(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, in *api.UpdateAccountRequest) error {
    evt := &api.Event{
        Name:       "account_updated",
        Properties: map[string]string{},
        External:   false, // used to denote if the event was generated from the client
    }
    return nk.Event(context.Background(), evt)
}

func InitModule(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, initializer runtime.Initializer) error {
    if err := initializer.RegisterAfterUpdateAccount(afterUpdateAccount); err != nil {
        return err
    }
    return nil
}
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
local nk = require("nakama")

local function after_update_account(ctx, payload)
  local properties = {
    my_key = "my_value"
  }
  local timestamp = nk.time
  local external = false  -- used to denote if the event was generated from the client
  return nk.event("event_name", properties, timestamp, external)
end

nk.register_req_after(after_update_account, "UpdateAccount")
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function InitModule(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, initializer: nkruntime.Initializer) {
  let afterUpdateAccount: nkruntime.AfterHookFunction<void, nkruntime.UserUpdateAccount> = function(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, data: void, payload: nkruntime.UserUpdateAccount) {
    let properties = {
      my_key: 'my_value'
    };

    nk.event('event_name', properties);
  }
  initializer.registerAfterUpdateAccount(afterUpdateAccount);
}

기본 제공 이벤트 #

서버는 SessionStartSessionEnd 작업을 위해 특별히 처리할 수 있는 기본 제공 이벤트를 생성합니다. 이러한 특수 이벤트는 서버에 새 소켓 세션이 시작되고 종료될 때 발생합니다. 이러한 이벤트를 처리하는 방법은 아래를 참조하십시오.

이벤트 처리 #

이벤트는 서버 시작 시 런타임 이니셜라이저에 등록된 함수로 처리할 수 있습니다. 이벤트에는 클라이언트가 생성한 것처럼 외부 필드를 true(으)로 표시합니다.

Lua/TypeScript 런타임 함수를 통한 이벤트 처리는 아직 지원되지 않습니다.
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func processEvent(ctx context.Context, logger runtime.Logger, evt *api.Event) {
    switch evt.GetName() {
    case "account_updated":
        logger.Debug("process evt: %+v", evt)
        // Send event to an analytics service.
    default:
       logger.Error("unrecognised evt: %+v", evt)
    }
}

// initializer runtime.Initializer
if err := initializer.RegisterEvent(processEvent); err != nil {
    return err
}

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

기본 제공 이벤트 #

내부적으로 생성된 이벤트에는 고유한 등록 기능이 있습니다. SessionStart는 서버에서 새 소켓 연결이 열릴 때 생성됩니다.

Server
1
2
3
4
5
6
7
8
func eventSessionStart(ctx context.Context, logger runtime.Logger, evt *api.Event) {
    logger.Debug("process event session start: %+v", evt)
}

// initializer runtime.Initializer
if err := initializer.RegisterEventSessionStart(eventSessionStart); err != nil {
    return err
}

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

SessionEnd 이벤트는 세션에 대한 소켓 연결이 닫힐 때 생성됩니다. 소켓은 여러 가지 이유로 닫혔을 수 있지만 반응은 관찰 가능합니다.

Server
1
2
3
4
5
6
7
8
func eventSessionEnd(ctx context.Context, logger runtime.Logger, evt *api.Event) {
   logger.Debug("process event session end: %+v", evt)
}

// initializer runtime.Initializer
if err := initializer.RegisterEventSessionEnd(eventSessionEnd); err != nil {
   return err
}

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

고급 설정 #

서버의 성능을 보호하기 위해 이벤트 처리 하위 시스템은 이벤트 처리에 할당되는 리소스를 제한하도록 설계되었습니다. 이러한 구성 설정을 통해 이 하위 시스템에 할당되는 리소스를 조정할 수 있습니다.

구성 키설명
runtime.event_queue_sizeint이벤트 큐 버퍼의 크기. 기본값은 65536입니다.
runtime.event_queue_workersint이벤트의 동시 처리에 사용할 작업자 수. 기본값은 8입니다.

다른 구성 설정을 검토하려면 문서를 참조하십시오.

#

SessionStart, SessionEnd 및 사용자 계정이 업데이트될 때 생성되는 이벤트를 처리하는 완전한 샘플 플러그인입니다.

 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
package main

import (
    "context"
    "database/sql"
    "github.com/heroiclabs/nakama-common/api"
    "github.com/heroiclabs/nakama-common/runtime"
)

func afterUpdateAccount(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, in *api.UpdateAccountRequest) error {
    evt := &api.Event{
        Name:       "account_updated",
        Properties: map[string]string{},
        External:   false,
    }
    return nk.Event(context.Background(), evt)
}

func processEvent(ctx context.Context, logger runtime.Logger, evt *api.Event) {
    switch evt.GetName() {
    case "account_updated":
        logger.Debug("process evt: %+v", evt)
        // Send event to an analytics service.
    default:
        logger.Error("unrecognised evt: %+v", evt)
    }
}

func eventSessionEnd(ctx context.Context, logger runtime.Logger, evt *api.Event) {
   logger.Debug("process event session end: %+v", evt)
}

func eventSessionStart(ctx context.Context, logger runtime.Logger, evt *api.Event) {
    logger.Debug("process event session start: %+v", evt)
}

//noinspection GoUnusedExportedFunction
func InitModule(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, initializer runtime.Initializer) error {
    if err := initializer.RegisterAfterUpdateAccount(afterUpdateAccount); err != nil {
       return err
    }
    if err := initializer.RegisterEvent(processEvent); err != nil {
        return err
    }
    if err := initializer.RegisterEventSessionEnd(eventSessionEnd); err != nil {
       return err
    }
    if err := initializer.RegisterEventSessionStart(eventSessionStart); err != nil {
       return err
    }
    logger.Info("Server loaded.")
    return nil
}