저장소
대부분의 게임의 경우, 중요한 플레이어 데이터(예: 인벤토리 또는 게임 통계)를 계속 추적해야 합니다. 싱글플레이어 게임의 경우, 클라이언트에 로컬로 정보를 저장하면 되지만, 멀티플레이어 게임에서는 데이터를 동시에 여러 개의 클라이언트로 전송하거나 변경되지 않도록 보호해야 합니다.
Nakama 저장소 엔진 을 통해 서버에서 사용자 정보를 안전하게 저장하고 액세스할 수 있습니다.
이 섹션에서는 아래 항목에 대해서 살펴봅니다:
- 서버 데이터베이스에서 읽기 및 쓰기
- 가상 화폐를 보유하기 위한 지갑 생성
- 지갑 장부를 통해 트랜잭션 관리
- 지갑 내에 사용자 지정 메타 데이터 포함
모음 #
Nakama에서 사용자 지정 데이터는 특정 사용자에 대한 데이터를 보유하는 objects의 그룹을 포함하는 collections(으)로 구성됩니다. 이 구조를 통해 유사한 정보를 저장하고 쉽게 액세스할 수 있습니다.
Pirate Panic의 경우, 플레이어는 대결에서 사용할 카드 덱을 들고 있습니다:

덱의 상태를 서버에 저장하여 사용자가 게임을 종료했다가 다시 참여해도 덱의 정보가 남아있도록 합니다.
서버에서 읽기 및 쓰기 #
먼저, 각 플레이어가 시작할 카드를 설정합니다:
deck.ts
| |
여기서, 기존의 덱 데이터는 개체 문자 배열로 표시됩니다. 각각의 개체에는 카드 유형과 카드가 업그레이드된 수준을 표시하는 두 개의 숫자 속성이 포함됩니다.
카드의 유형과 수준은 Pirate Panic에만 표시되기 때문에 게임에서는 자체적으로 사용자 지정 정보가 포함될 확률이 높습니다. 예를 들어, 각각의 개체에는 취득한 날짜, 특별한 변이, 플레이어가 알아야 할 특이사항 등이 포함될 수 있습니다.
그런 다음, uuidv4() 함수를 사용하여 고유한 ID를 부여하고, 데이터를 저장소 개체로 변환해야 합니다. 이 작업은 동일한 카드의 상태를 추적할 때 필요합니다.
이 덱이 있는 경우, storageWrite 함수를 사용하여 저장할 수 있습니다:
deck.ts
| |
다음 구조를 사용하는 집합을 생성합니다:
| |
나중에 이 카드에 액세스하려면 요청 개체를 사용하여 storageRead(을)를 호출할 수 있습니다:
deck.ts
| |
단일 storageRead 호출을 사용하여 여러 StorageReadRequest을(를) storageRead의 배열에 추가하고, 여러 요청 사항을 동시에 생성할 수 있습니다.
클라이언트에서 읽기 및 쓰기 #
Unity 클라이언트에서 집합과 인터페이스를 적용하기 위해서 친구 찾기 에서와 동일하게 RPC를 사용합니다. 이렇게 하면 사용자의 요청 사항 유형과 모든 민감한 작업이 서버에서만 진행되도록 제어할 수 있습니다.
로컬 플레이어가 함수 ReadStorageObjectsAsync을(를) 직접 사용하여 데이터를 읽을 수 있습니다:
ProfilePanel.cs
| |
여기서는, 찾고자 하는 개체를 식별할 때 사용하는 StorageObjectId struct(으)로 Collection, UserId, Key 속성을 전달합니다.
권한 추가 #
Nakama는 민감한 데이터에 승인되지 않은 액세스를 방지하기 위해서 데이터베이스에서 읽기 및 쓰기 권한 처리를 지원합니다.
세 가지 permissionRead 수준을 통해 개체 읽기를 보호할 수 있습니다:
0: 아무도 개체를 읽을 수 없습니다(서버 제외).1: 해당 개체(userId대결)를 보유한 사용자만 읽을 수 있습니다.2: 아무나 이 개체를 읽을 수 있습니다.
쓰기에는 두 가지 수준이 있습니다. 0은(는) 쓰기가 허용되지 않으며, 1은(는) 소유자에 대해서만 쓰기만 허용됩니다. 쓰기의 경우, 서버는 권한을 건너뛰고 모든 개체를 작성할 수 있습니다. 사용자는 수준 0을(를) 사용하여 데이터를 직접 편집하지 않고 서버에서 쓰기 요청 사항을 확인합니다.
예를 들어, 사용자 통계를 모든 사람이 볼 수 있도록 하려고 하지만, 서버에서만 변경할 수 있습니다:
| |
조건부 읽기 및 쓰기 #
여러 사용자가 동일한 개체에 액세스(쓰기 포함)가 필요할 수 있습니다.
수정 사항이 충돌하지 않도록 version 속성을 모든 쓰기 요청으로 전달할 수 있습니다. 서버에서 writeStorageObjects을(를) 사용하거나 클라이언트에서 WriteStorageObjectsAsync을(를) 사용하면, 입력된 버전이 데이터베이스에 저장된 버전과 일치할 때에만 쓰기 작업을 할 수 있습니다.
이 기능은 Pirate Panic에 표시되지 않지만, 조건부 저장 에 대해서 배우고 게임 내에서 실행할 수 있습니다.
지갑 #
일반적인 집합 외에 Nakama에는 지갑 기능이 포함되어 게임 내 화폐로 트랜잭션을 하거나 저장할 수 있도록 지원합니다.
Pirate Panic의 경우, 보석을 화폐로 사용하기 때문에 새로운 카드를 구입할 때 보석을 사용할 수 있습니다.

지갑 수량 업데이트 #
집합과 달리, 지갑은 서버에서 직접 업데이트하여 사용자가 원하지 않거나 착취하는 형태의 트랜잭션이 발생하지 않도록 합니다.
네 개의 매개 변수를 사용하는 walletUpdate을(를) 통해 서버 측에서 지갑의 값을 변경할 수 있습니다.
- 변경할 지갑의 사용자 ID
- 업데이트할 수량과 모든 화폐 이름이 일치하는 키 값 쌍을 포함하는 개체
- 메타데이터 개체
- 장부 업데이트가 필요한 경우 서버에 알림을 제공하는 부울
Pirate Panic에는 한 개의 화폐만 있기 때문에 보석이 추가될 때마다 changeset을(를) 조정하지 않고 도움말 함수를 작성하여 숫자를 전달하면 됩니다:
economy.ts
| |
amount은(는) 현재 지갑에서 잔고를 변경할 양이고, 현재 잔고를 완전히 대체하지 않는다는 것을 유의합니다. 예를 들어, 지갑에 보석이 900개 있고 updateWallet에 100개가 호출된 경우, 새로운 잔고는 1,000(100이 아님)이 됩니다.
이 항목을 RPC에 적용하여 특정한 상황에서 Unity 클라이언트가 보석을 추가할 수 있도록 합니다. 예를 들어, 무료로 보석을 제공하는 버튼을 만들려고 합니다:

RPC는 다음과 같이 표시됩니다:
| |
Unity에서 호출하려면 다음과 같이 합니다:
| |
음의 수량을 전달하여 플레이어의 지갑에서 보석을 차감할 수도 있습니다. 예를 들어, 100개의 보석을 사용하여 카드를 구매하려면 updateWallet(nk, ctx.userId, -100, {});을(를) 호출합니다.
사용자 지정 메타데이터 #
메타데이터 객체를 사용하면 모든 사용자 지정 정보를 지갑 트랜잭션에 입력할 수 있습니다. walletLedgerList에 있는 모든 항목으로 정보가 전달되어 나중에 참조할 수 있습니다.
예를 들어, 대결 ID에 보상을 연결하여 같은 장소에서 보상이 제공되고 플레이어에 대한 전체 보상을 볼 수 있도록 하려면 다음과 같이 합니다:
| |
지갑 수량 가져오기 #
클라이언트 측의 지갑 정보는 인증 설정 시 사용한 연결 정보를 보유하는 GameConnection 클래스에서 connection.Account이(가) IApiAccount 인스턴스인 connection.Account.Wallet에 저장됩니다.
문자열로 전환된 JSON 개체 형식으로 제공되는 Wallet 변수를 얻으면, 다음 코드를 사용하여 지갑에서 많은 보석을 추출할 수 있습니다:
| |
Pirate Panic의 경우, GameConnection을(를) 생성했기 때문에 다음을 사용하여 GetGems을(를) 호출할 수 있습니다:
| |
트랜잭션 장부 #
위에서 true로 설정한 updateLedger에 한 개의 최종 매개 변수가 있습니다.
true(으)로 설정된 updateLedger와(과) 함께 walletUpdate이(가) 호출되면 새로운 장부 항목이 생성됩니다. 이 작업을 통해 이전 트랜잭션과 세부내역을 살펴볼 수 있습니다.
과거부터 가장 최근까지 정렬된 모든 트랜잭션의 목록을 보려면 walletLedgerList을(를) 호출합니다.
그런 다음, cursor을(를) 사용하여 원하는 항목으로 이동할 수 있습니다. 예를 들어, 가장 최근의 커서 업데이트를 보려면 다음과 같이 합니다:
match.ts
| |
이 작업은 현재 대결의 보상으로 이동할 때 유용합니다. 최초 보상에 커서 업데이트가 되도록 설정하고 커서 업데이트를 사용하지 않는 상태에서 증분 업데이트(예: 타워 캡처)를 지속하면 이 전략을 사용하여 가장 최신의 대결에서 모든 트랜잭션의 목록을 가져올 수 있습니다.
