공동 몬스터 레이드 만들기
여러 회원이 같은 몬스터를 공격하고 체력이 0이 될 때까지 결과를 공유하는 실시간 레이드를 구성합니다. Webspace의 공통 shared-resource 처리기가 공격, 체력 변경과 행동별 보상을 서버에서 함께 처리합니다.
시작하기 전에
- 스키마와 API를 배포할 권한이 필요합니다.
- 실시간 기능의
send와receive권한을 사용할 회원에게 부여해야 합니다. - 레이드 대상 생성 API는 관리자만 사용하도록 구성합니다.
완성된 스키마로 시작하기
공동 자원 게임 스키마(.sfn) 다운로드에는 이 문서에서 사용하는 세 테이블의 컬럼과 권한, shared-resource 처리기 설정이 들어 있습니다.
- 파일을 내려받아 팀 폴더 또는 개인 폴더에 올리고 스키마 편집기로 엽니다.
API 빌드의 배포 설정에서데이터베이스 식별자를 확인합니다. 기본값resource_game이 기존 데이터베이스와 겹치지 않도록monster_raid_team_a처럼 새 식별자로 바꿉니다.- 권한과 처리기 설정을 검토한 뒤 스키마를 배포합니다.
이 파일은 공동 자원 백엔드 구조 예제입니다. includeClient가 false이므로 게임 화면은 자동으로 만들어지지 않으며, 초기 몬스터 데이터도 포함하지 않습니다. 아래 API와 WebSocket 이벤트를 사용하는 화면을 별도로 만들고 레이드 대상도 생성해야 합니다.
데이터를 세 테이블로 나누기
공동 자원 구조는 다음 세 종류의 데이터를 분리합니다.
| 테이블 | 저장 내용 |
|---|---|
game_resources | 몬스터 코드, 상태, 현재·최대 체력, 버전과 보상 아이템 |
game_actions | 행동 ID, 공격한 회원, 적용 피해, 결과 체력과 보상 수량 |
game_inventory | 회원별 아이템과 누적 수량 |
game_actions의 행동 ID는 재시도를 구분합니다. 같은 행동 ID를 다시 보내도 체력과 보상이 두 번 반영되지 않습니다.
실시간 처리기 설정하기
실시간 기능을 켜고 이벤트 처리기로 shared-resource를 선택합니다. 기본 테이블 이름을 사용한다면 처리기 설정은 다음 구조입니다.
{
"resourcesTable": "game_resources",
"actionsTable": "game_actions",
"inventoryTable": "game_inventory",
"actionPower": 1,
"rewardPerAction": 1,
"maxClientAmount": 1,
"broadcast": "namespace"
}
broadcast가 namespace이면 같은 Webspace 실시간 네임스페이스에 연결된 클라이언트가 결과 이벤트를 함께 받습니다. actor이면 행동한 회원에게만 결과를 보냅니다.
레이드 대상 만들기
관리자 권한으로 game_resources/create를 호출해 몬스터를 만듭니다.
{
"code": "RAID-1",
"kind": "monster",
"status": "active",
"current_value": 10000,
"max_value": 10000,
"version": 0,
"reward_item_code": "monster-core"
}
화면을 열 때 game_resources 조회 API에서 현재 체력과 버전을 읽습니다. 새로 연결하거나 연결이 복구된 뒤에도 REST API를 다시 조회하면 현재 서버 상태를 기준으로 화면을 맞출 수 있습니다.
WebSocket으로 공격 보내기
로그인한 회원이 실시간 연결을 구독한 뒤 resource-game.act 이벤트를 보냅니다.
{
"type": "webspace:send",
"data": {
"dbName": "monster_raid_team_a",
"event": "resource-game.act",
"payload": {
"actionId": "attack-00000001",
"resourceCode": "RAID-1",
"amount": 1
}
}
}
dbName에는 배포할 때 지정한 데이터베이스 식별자를 사용합니다.
actionId는 영문, 숫자, _, -를 조합한 8~80자 값으로 만듭니다. amount를 생략하면 1이며, 값을 보낼 때는 처리기의 maxClientAmount 범위 안의 정수를 사용합니다.
서버는 처리가 끝난 뒤 resource-game.action-applied를 보냅니다.
{
"actionId": "attack-00000001",
"resourceId": 1,
"currentValue": 9999,
"version": 1,
"rewardQuantity": 1,
"duplicate": false
}
화면은 이 결과를 받은 뒤에만 체력을 바꿉니다. 클라이언트가 resource-game.action-applied를 직접 보내는 요청은 거부됩니다.
체력과 보상 표시하기
resourceId가 현재 보고 있는 몬스터인지 확인합니다.- 이전 결과보다 큰
version인지 확인합니다. currentValue로 체력 표시를 갱신합니다.currentValue가 0이면 공동 처치 완료 상태를 표시합니다.- 개인 아이템은
game_inventory/list에서 다시 조회합니다.
game_inventory에는 회원 본인 데이터만 읽을 수 있는 소유자 권한을 적용합니다. 결과 이벤트의 rewardQuantity만으로 다른 회원의 보유량을 추정하지 않습니다.
알아두기
- 공격력과 보상은 클라이언트 값이 아니라 서버의 처리기 설정으로 결정됩니다.
- 자원 변경, 행동 기록과 보상 적립은 하나의 트랜잭션으로 처리됩니다.
- 현재 공통 구조는 행동별 보상을 제공합니다. 처치 순위나 처치자 전용 보상은 별도로 설계해야 합니다.
- 응답이 늦어도 성공으로 간주하지 않습니다. 같은
actionId로 재확인하면 중복 적용을 막을 수 있습니다.