# 사용 사례 샘플: 참여한 로비에 있는 모든 플레이어에게 레벨 업 알림

> Announce a player's level up to all other players in joined lobbies through a push message.

이 사용 사례 샘플에서는 플레이어가 있는 모든 로비에서 Triggers를 사용하여 모든 다른 플레이어에게 플레이어의 레벨 업을 알리는 방법을 보여 줍니다. 샘플은 [Cloud Save](/cloud-save.md) 서비스에서 발생하는 `key-saved` 이벤트를 사용합니다. 샘플에서는 [푸시 메시지](/cloud-code/modules/how-to-guides/push-messages.md)를 통해 레벨 업한 플레이어와 동일한 [Lobby](/lobby.md)에 있는 플레이어에게 이 소식을 알립니다.

트리거는 이벤트 페이로드를 평가하는 데 필터를 사용하므로 플레이어가 레벨 업하고 플레이어 엔티티가 Cloud Save 이벤트를 발생시킬 때만 Cloud Code 모듈이 트리거됩니다.

> **Note:**
>
> **참고**: Cloud Code 모듈에서만 [푸시 메시지](/cloud-code/modules/how-to-guides/push-messages.md)를 사용할 수 있습니다.

> **Note:**
>
> **참고:** UGS CLI는 필터를 사용하는 트리거를 지원하지 않습니다. 이 샘플에서는 [Triggers 관리자 API](../define-triggers/rest-api)를 사용하여 트리거를 생성합니다.

## 필수 조건##prerequisites

먼저 필요한 액세스 역할로 서비스 계정을 생성해야 합니다.

### 서비스 계정을 사용하여 인증##authenticate-using-a-service-account

Triggers 서비스를 호출하려면 먼저 서비스 계정을 사용하여 인증해야 합니다.

1. [Unity Dashboard](https://cloud.unity.com)로 이동합니다.
2. **Administration** > **Service Accounts**를 선택합니다.
3. **New** 버튼을 선택하고 서비스 계정의 이름과 설명을 입력합니다.
4. **Create**를 선택합니다.

다음과 같이 제품 역할을 추가하고 키를 만듭니다.

1. **Manage product roles**를 선택합니다.
2. 다음 역할을 서비스 계정에 추가합니다.
   * LiveOps 드롭다운에서 **Triggers Configuration Editor**와 **Triggers Configuration Viewer**를 선택합니다.
   * Admin 드롭다운에서 **Unity Environments Viewer**를 선택합니다.
3. **Save**를 선택합니다.
4. **Add Key**를 선택합니다.
5. base64 인코딩을 사용하여 **Key ID**와 **Secret key**를 인코딩합니다. 포맷은 ‘key\_id:secret\_key’입니다. 이 값을 기록해 둡니다.

자세한 내용은 [인증](../../authentication)을 참고하십시오.

## `key-saved` 이벤트 확인##examine-the-key-saved-event

Cloud Save 서비스는 Cloud Save 키가 저장될 때 `key-saved` 이벤트를 발생시킵니다. 이벤트 페이로드의 예시는 다음과 같습니다.

```json
{
  "id": "7LpyhpsIvEczGkDI1r8J6gHhHezL",
  "idType": "player",
  "key": "LEVEL",
  "value": 1,
  "valueIncluded": true,
  "writeLock": "7b8920a57912509f6b5cbb183eb7fcb0",
  "accessClass": "default",
  "modifiedDate": "2021-03-04T09:00:00Z"
}
```

이벤트는 이벤트 페이로드를 파라미터로 Cloud Code에 전달합니다.

자세한 내용은 [Cloud Save: 키 저장](/triggers/manage-events/supported-ugs-events.md#key-saved)을 참고하십시오.

## Cloud Code 설정##set-up-cloud-code

플레이어가 레벨 업하면 로비에 있는 모든 플레이어에게 푸시 메시지를 전송하는 모듈 엔드포인트를 정의합니다.

아래와 같은 내용으로 `AnnounceLevelUp` 모듈 함수를 생성합니다.

**C#** onfigure-the-ugs-cli

```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Unity.Services.CloudCode.Apis;
using Unity.Services.CloudCode.Core;
using Unity.Services.CloudCode.Shared;
using Unity.Services.Lobby.Model;

namespace AnnounceLevelUp;

public class AnnounceLevelUp
{
    private const string ServiceId = "cloud-code";
    private readonly ILogger<AnnounceLevelUp> _logger;

    public AnnounceLevelUp(ILogger<AnnounceLevelUp> logger)
    {
        _logger = logger;
    }

    [CloudCodeFunction("AnnounceLevelUp")]
    public async Task Announce(IExecutionContext ctx, PushClient pushClient, IGameApiClient gameApiClient,
        string id)
    {
        try
        {
            var playerList = await GetPlayersFromLobbies(ctx, gameApiClient, id);
            if (playerList.Count == 0)
            {
                _logger.LogInformation("The player {playerId} has not joined any lobbies", id);
                return;
            }

            await SendPushMessage(ctx, pushClient, playerList);
        }
        catch (ApiException e)
        {
            _logger.LogError("Failed to announce level up for player {playerId}. Error: {Error}", id,
                e.Message);
            throw new Exception($"Failed to announce level up for player {id}. Error: {e.Message}");
        }
    }

    public async Task SendPushMessage(IExecutionContext ctx,  PushClient pushClient, List<Player> lobbyPlayers)
    {

        foreach (var lobbyPlayer in lobbyPlayers)
        {
            try
            {
                var message = $"The player {lobbyPlayer.Id} has leveled up!";

                _logger.LogInformation("Sending push notification to player {lobbyPlayer}", lobbyPlayer.Id);
                await pushClient.SendPlayerMessageAsync(ctx, message, "Level up!", lobbyPlayer.Id);
            }
            catch (ApiException e)
            {
                _logger.LogError("Failed to send push notification to player {playerId}. Error: {Error}", lobbyPlayer.Id,
                    e.Message);
            }
        }
    }

    public async Task<List<Player>> GetPlayersFromLobbies(IExecutionContext ctx, IGameApiClient gameApiClient, string playerId)
    {
        var playersToNotify = new List<Player>();
        try
        {
            var lobbyList = await gameApiClient.Lobby.GetJoinedLobbiesAsync(ctx, ctx.ServiceToken, ServiceId,playerId );
            foreach (var lobby in lobbyList.Data)
            {
                var players = await GetLobbyData(ctx, gameApiClient, lobby);
                playersToNotify.AddRange(players);

            }

            return playersToNotify;
        }
        catch (Exception e)
        {
            _logger.LogError("Failed to get players from lobbies. Error: {Error}",  e.Message);
            throw new Exception($"Failed to get players from lobbies. Error: {e.Message}");
        }
    }

    public async Task<List<Player>> GetLobbyData(IExecutionContext ctx, IGameApiClient gameApiClient, string lobbyId)
    {
        try
        {
            var response = await gameApiClient.Lobby.GetLobbyAsync(ctx, ctx.ServiceToken, lobbyId, "cloud-code");
            return response.Data.Players;
        }
        catch (Exception e)
        {
            _logger.LogError("Failed to get players from lobby: {lobbyId}. Error: {Error}", lobbyId, e.Message);
            throw new Exception($"Failed to get players from lobby: {lobbyId}. Error: {e.Message}");
        }
    }


    public class ModuleConfig : ICloudCodeSetup
    {
        public void Setup(ICloudCodeConfig config)
        {
            config.Dependencies.AddSingleton(GameApiClient.Create());
            config.Dependencies.AddSingleton(PushClient.Create());
        }
    }
}
```

모듈을 배포합니다.

모듈을 배포하는 방법을 알아보려면 [Hello World 배포](/cloud-code/modules/getting-started.md#deploy-the-module)를 참고하십시오.

> **Note:**
>
> **참고:** UGS CLI를 사용하여 모듈을 배포하는 경우 서비스 계정 역할 `Cloud Code Editor`를 추가해야 합니다.

## 트리거 구성##configure-a-trigger

Cloud Code 리소스를 Cloud Save `key-saved` 이벤트에 연결하려면 트리거를 생성합니다. 예를 들어 플레이어가 키를 저장할 때마다 이벤트가 실행되면 트리거가 Cloud Code 모듈을 실행합니다.

트리거 구성의 필터는 이벤트 페이로드를 평가하여 플레이어가 레벨 업하고 플레이어 엔티티가 Cloud Save 이벤트에서 발생할 때만 Cloud Code 모듈을 트리거합니다.

Triggers 서비스에 cURL 요청을 보내 트리거를 만듭니다.

앞서 만든 서비스 계정 자격 증명을 사용하여 `key_id:secret_key` 포맷의 base64 문자열로 이를 인코딩해 인증합니다.

```bash
curl 'https://services.api.unity.com/triggers/v1/projects/<PROJECT_ID>/environments/<ENVIRONMENT_ID>/configs' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic <SERVICE_ACCOUNT_CREDENTIALS_ENCODED>' \
--data '{
  "name": "announce-level-up",
  "eventType": "com.unity.services.cloud-save.key-saved.v1",
  "actionType": "cloud-code",
  "actionUrn": "urn:ugs:cloud-code:AnnounceLevelUp/AnnounceLevelUp",
  "filter": "data[\"idType\"] == \"player\" && data[\"key\"] == \"LEVEL\""
}'
```

플레이어가 `LEVEL` 키에 값을 저장하면 샘플 트리거가 Cloud Code 모듈 함수를 실행합니다.

## Lobby 설정##set-up-lobby

샘플대로 따르려면 [Lobby를 만들어야](/lobby/create-a-lobby.md) 합니다. [Unity Dashboard](https://cloud.unity.com)의 Cloud Code 헬퍼 스크립트를 사용하여 진행할 수 있습니다.

### JavaScript##c

```javascript
const { LobbyApi } = require("@unity-services/lobby-1.2");

module.exports = async ({ params, context, logger }) => {
  const lobbyApi = new LobbyApi(context);
  const serviceId = "cloud-code";
  try {
    // Create a private lobby without any initial players.
    const { data: lobby } = await lobbyApi.createLobby(serviceId, null, {
      name: "sample-lobby",
      maxPlayers: 4,
    });

    return lobby.id;
  } catch (err) {
    logger.error(`Error while calling out to Lobby: ${JSON.stringify(err.message)}`);
    throw err;
  }
};
```

로비 ID를 기록해 둡니다.

## 로비에 플레이어 추가##add-players-to-a-lobby

다음 Cloud Code 스크립트를 실행하여 [Unity Dashboard](https://cloud.unity.com)에서 만든 로비에 플레이어를 추가할 수 있습니다. 새 플레이어를 추가하려면 모든 테스트 실행에서 플레이어 ID 토큰을 재생성해야 합니다.


**Frame:**
![](/api/media?file=/cloud-code/media/images/cloud-code-generate-button.png)

로비에 추가한 플레이어의 ID 중 하나를 기록해 둡니다. 나중에 Cloud Save 데이터를 업데이트하여 결과를 확인하는 데 이 ID를 사용합니다.

스크립트는 `lobbyId`를 파라미터로 사용합니다.

### JavaScript##javascript

```javascript
const { LobbyApi } = require("@unity-services/lobby-1.2");
module.exports = async ({ params, context, logger }) => {
  const lobbyApi = new LobbyApi(context);
  const serviceId = "cloud-code";

  try {
    await lobbyApi.joinLobbyById(params.lobbyId, serviceId, context.playerId);
  } catch (err) {
    logger.error("Failed to join lobby", { "error.message": err.message }, { lobbyId: params.lobbyId });
    throw err;
  }
};
```

> **Note:**
>
> **참고:** 로비에 플레이어를 추가할 때 로비에 허용되는 최대 플레이어 수를 기억하고 있어야 합니다. Unity 프로젝트에서 인증된 플레이어를 추가하여 나중에 샘플을 테스트할 수 있도록 여유 슬롯을 남겨둬야 하기 때문입니다.

## 결과 확인##validate-the-result

결과를 확인하려면 푸시 메시지를 구독하는 Unity 프로젝트를 설정하고 [이전에 생성한 플레이어 추가 Cloud Code 스크립트](#add-players-to-a-lobby)를 사용하여 인증된 플레이어가 로비에 참여할 수 있도록 합니다.

### 필수 조건##javascript

푸시 메시지를 구독하려면 Cloud Code SDK를 설치하고 [Unity Gaming Services 프로젝트](/cloud/projects.md)를 Unity 에디터에 연결해야 합니다.

#### 프로젝트 연결##link-project

[Unity Gaming Services 프로젝트](/cloud/projects.md)를 Unity 에디터와 연결합니다. Unity Dashboard에서 UGS 프로젝트 ID를 확인할 수 있습니다.

1. Unity 에디터에서 **Edit** > **Project Settings** > **Services**를 선택합니다.

2. 프로젝트를 연결합니다.
   프로젝트에 Unity 프로젝트 ID가 없는 경우 다음 단계를 진행합니다.

   1. **Create a Unity Project ID** > **Organizations**를 선택한 다음 드롭다운에서 조직을 선택합니다.
   2. **Create project ID**를 선택합니다.

   Unity 프로젝트 ID가 있는 경우 다음 단계를 진행합니다.

   1. **Use an existing Unity project ID**를 선택합니다.
   2. 드롭다운에서 조직과 프로젝트를 선택합니다.
   3. **Link project ID**를 선택합니다.

Unity 프로젝트 ID가 표시되고 이제 Unity 서비스에 프로젝트가 연결됩니다. `UnityEditor.CloudProjectSettings.projectId` 프로퍼티를 사용하여 Unity 에디터 스크립트에서 프로젝트 ID에 액세스할 수도 있습니다.

#### SDK 설치##sdkinstallation

다음 단계를 따라 Unity 에디터용 최신 Cloud Code 패키지를 설치합니다.

1. Unity 에디터에서 **Window** > **Package Manager**를 엽니다.
2. 패키지 관리자에서 **Unity Registry** 목록 뷰를 선택합니다.
3. `com.unity.services.cloudcode`를 검색하거나 목록에서 Cloud Code 패키지를 찾습니다.
4. 패키지를 선택한 후 **Install**을 선택합니다.

> **Note:**
>
> [Unity - 매뉴얼: Package Manager 창](https://docs.unity3d.com/Manual/upm-ui.html)을 확인하여 Unity 패키지 관리자 인터페이스를 익혀 두십시오.

> **Note:**
>
> Cloud Code SDK 버전 2.4.0 이상에서 메시지를 구독할 수 있습니다.

### `Monobehaviour` 스크립트 생성##prerequisites

플레이어 레벨 메시지를 구독하려면 `Monobehaviour` 스크립트를 설정합니다. 자세한 내용은 [푸시 메시지 전송](/cloud-code/modules/how-to-guides/push-messages.md)을 참고하십시오.

`MonoBehaviour` 스크립트 아래의 다음 샘플 코드를 사용할 수 있습니다.

```csharp
using System;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Unity.Services.Authentication;
using Unity.Services.CloudCode;
using Unity.Services.CloudCode.Subscriptions;
using Unity.Services.Core;
using UnityEngine;

namespace CloudCode
{
    public class CloudCodePushExample : MonoBehaviour
    {
        async void Start()
        {
            await UnityServices.InitializeAsync();
            await AuthenticationService.Instance.SignInAnonymouslyAsync();
            Debug.Log(AuthenticationService.Instance.PlayerId);
            await SubscribeToPlayerMessages();
        }

        // This method creates a subscription to player messages and logs out the messages received,
        // the state changes of the connection, when the player is kicked and when an error occurs.
        Task SubscribeToPlayerMessages()
        {
            // Register callbacks, which are triggered when a player message is received
            var callbacks = new SubscriptionEventCallbacks();
            callbacks.MessageReceived += @event =>
            {
                Debug.Log(DateTime.Now.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK"));
                Debug.Log(
                    $"Got player subscription Message: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
            };
            callbacks.ConnectionStateChanged += @event =>
            {
                Debug.Log(
                    $"Got player subscription ConnectionStateChanged: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
            };
            callbacks.Kicked += () => { Debug.Log($"Got player subscription Kicked"); };
            callbacks.Error += @event =>
            {
                Debug.Log(
                    $"Got player subscription Error: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
            };
            return CloudCodeService.Instance.SubscribeToPlayerMessagesAsync(callbacks);
        }
    }
}
```

처음으로 `Monobehaviour` 스크립트를 실행하면 플레이어 ID가 기록됩니다. 이 값을 기록해 둡니다.

### 로비 참여##create-a-monobehaviour-script

[이전에 생성한 플레이어 추가 Cloud Code 스크립트](#add-players-to-a-lobby)를 사용하여 인증된 플레이어가 로비에 참여하도록 할 수 있습니다. [이전에 생성한 로비 ID](#set-up-lobby)를 `lobbyId` 파라미터로 전달하고, Unity 프로젝트를 실행할 때 기록해 둔 플레이어 ID를 `playerId` 파라미터로 전달합니다.

### Cloud Save에서 `Level` 키 업데이트##join-a-lobby

다음 스크립트를 실행하여 Cloud Code 모듈을 트리거하도록 Cloud Save 키를 업데이트할 수 있습니다.

`playerId` 문자열 파라미터와 `level` 숫자 파라미터를 사용합니다.

> **Note:**
>
> **참고:** 이벤트 값이 있는 필터를 사용하는 커스텀 트리거가 있으면 `level` 파라미터를 숫자로 지정합니다. 특정 유형의 파라미터가 정의되어 있지 않으면 파라미터는 문자열로 전달됩니다. 필터에서 값을 숫자로 평가했는데 전달은 문자열로 이뤄지면 트리거가 작동하지 않습니다.
> c type parameter defined, the parameter is passed as a string. If your filter evaluates the value as a number, and you pass in a string, the trigger won't work.

앞서 로비에 추가한 플레이어의 ID를 사용합니다. 이렇게 하면 Unity 프로젝트에서 인증된 플레이어가 동일한 로비에 있으므로 해당 플레이어에게 메시지가 전송됩니다.

#### JavaScript##javascript

```javascript
const { DataApi } = require("@unity-services/cloud-save-1.4");

module.exports = async ({ params, context, logger }) => {
  const cloudSaveApi = new DataApi(context);

  try {
    await cloudSaveApi.setItem(context.projectId, params.playerId, {
      key: "LEVEL",
      value: params.level,
    });

    result = await cloudSaveApi.getItems(context.projectId, params.playerId);

    return result.data;
  } catch (err) {
    logger.error("Error while calling out to Cloud Save", { "error.message": err.message });
    throw err;
  }
};
```

### 결과 확인##update-the-level-key-in-cloud-save

Unity 프로젝트가 실행 중이고 Unity 프로젝트의 인증된 플레이어가 로비에 있는지 확인하고, Cloud Save의 `LEVEL` 키를 업데이트합니다.

Unity 에디터에서 플레이어의 점수가 떨어졌을 때 전송되는 푸시 메시지는 다음과 같이 표시됩니다.

```yaml
Got player subscription Message:
  {
    "data_base64": <BASE64-ENCODED-DATA>,
    "time": "2023-11-13T16:21:35.102058117Z",
    "message": "The player Z96pNb4wfgdaMLqMQfWpwXEclaRZ has leveled up!",
    "specversion": "1.0",
    "id": <ID>,
    "source": "https://cloud-code.service.api.unity.com",
    "type": "com.unity.services.cloud-code.push.v1",
    "projectid": <PROJECT-ID>,
    "environmentid": <ENVIRONMENT-ID>,
    "correlationid": <CORRELATION-ID>,
    "messagetype": "Level up!",
  }
```
