-`updated`: 기존 유저의 정보가 업데이트되었습니다. 해당 유저의 방장, 준비 여부가 변경되면 수신됩니다.
-`removed`: 유저가 방에서 퇴장하였습니다.
유저가 채팅을 입력했다면 `chat`을 보내면 됩니다. 다른 사람이 채팅을 입력한 경우에도 `chat`이 수신됩니다(`ServerInboundMessage<"chat">`과 `ServerOutboundMessage<"chat">`이 다르게 정의되었다는 점에 유의하세요). 자신이 보낸 채팅에 대해서는 수신되지 않으므로 이 경우 오프라인으로 메세지를 추가하여야 합니다.
유저가 채팅을 입력했다면 `chat`을 보내면 됩니다. 다른 사람이 채팅을 입력한 경우에도 `chat`이 수신됩니다(`ServerInboundMessage<"chat">`과 `ServerOutboundMessage<"chat">`이 다르게 정의되었다는 점에 유의하세요). 자신이 보낸 채팅에 대해서는 수신되지 않으므로 이 경우 오프라인으로 메세지를 추가하여야 합니다. 채팅 문자열의 양 끝 공백을 제거했을 때 문자열이 빈 문자열이거나, 채팅 문자열의 길이가 300을 초과하는 경우 요청이 실패 처리됩니다.
방을 나가고 싶으면 `leaveRoom`을 보내면 됩니다.
### 방장
방을 생성한 유저가 방장 권한을 가지게 됩니다. 방장 여부는 `joinRoom`의 반환값 또는 `updateRoomUser`을 통해 수신됩니다. 방장은 바뀔 수 있음에 유의하세요. 예를 들어 원래 방장이 방에서 나가는 경우, 다른 랜덤한 유저에게 방장 권한이 넘어갑니다. 이 경우 `updateRoomUser`가 수신됩니다.
방장이 원하는 유저에게 권한을 넘길 수 있는 기능은 아직 구현되지 않았습니다.
## 게임
### 요약
#### 게임 전
1. 방장을 제외한 모든 유저가 `ready`를 보내 준비한다.
2. 방장이 `startRound`를 보내 게임 시작을 요청한다.
#### 라운드 1 - 단어 선택 시간
(서버는 방장의 게임 시작 요청이 성공했을 경우 진입)
(클라이언트는 `startRound`를 수신받은 경우 진입)
1. 모든 유저는 `startRound`를 수신받고, 게임 화면으로 전환된다. 해당 메세지에는 모든 유저의 역할이 포함되어 있다. 이들 중 특별히 `drawer` 역할을 배정받았다면, 캔버스에 그릴 수 있도록 준비한다.
2. 만약 본인이 `drawer`라면, `wordSet` 메세지를 수신받게 되는데 여기에는 3가지 선택할 수 있는 단어가 포함되어 있다. 팝업을 띄워서 유저가 단어를 선택한다.
3. 만약 본인이 `drawer`가 아니라면, 아직 라운드의 시간이 흘러가지 않고 대기한다.
4.`drawer`는 `chooseWord`를 통해 자신이 선택하고자 하는 단어를 서버에 보낸다.
#### 라운드 1 - 라운드 진행 (60초)
(서버는 `drawer`가 `chooseWord`를 보낸 경우 진입)
(클라이언트는 `wordChosen`을 수신받은 경우 진입)
1. 모든 유저는 `wordChosen`을 수신받는데, 여기에는 정답 단어의 글자 수 만이 포함되어 있다. 따라서 정답 단어를 밑줄 개수만으로 표시한다.
2. 모든 유저는 라운드의 타이머가 시작되었다는 `timer` 메세지를 수신받는다. 이때 타이머를 동작하고 남은 시간을 동기화한다.
3.`guesser`들은 단어를 채팅에 쳐서 맞춰볼 수 있다.
4.`drawer`는 캔버스에 그림을 그릴 수 있다.
5. 만약 `guesser`가 채팅으로 정답을 보냈다면 해당 유저는 `answerAccepted` 메세지를 수신받게 되고 여기에 이번 라운드의 정답이 포함되어 있다. 그리고 `role`을 통해 해당 유저의 역할이 `winner`로 변경된다.
#### 라운드 1 - 라운드 종료 및 다음 라운드 시작 대기 (5초)
(서버는 남은 시간이 0으로 떨어지거나, `drawer`가 퇴장하거나, 모두가 답을 맞춰 남은 `guesser`가 0명이 된 경우 진입)
(클라이언트는 `finishRound`를 수신받은 경우 진입)
1. 모든 유저는 `finishRound`를 통해 이번 라운드의 정답을 알게 된다.
#### 라운드 2 - 단어 선택 시간
(위와 동일하므로 생략)
...(생략)...
#### 라운드 5 - 라운드 종료 (5초)
(위와 동일하므로 생략)
#### 게임 종료
(서버는 다음 라운드가 없으면 진입, 게임 도중 인원이 2명 미만이 되는 경우 즉시 진입)
(클라이언트는 `finishGame`을 수신받은 경우 진입)
1. 방에 접속 중인 모든 유저는 `finishGame`를 수신받는다. 이 경우, 게임이 종료되었으므로 게임 화면에서 다시 준비 화면으로 전환된다.
### 준비
방장을 제외한 모든 플레이어는 준비를 해야 게임이 시작될 수 있습니다. 서버에 `ready` 메세지를 보내서 준비 상태를 설정할 수 있습니다. 준비 상태로 설정하려면 `ready` 속성을 참, 그렇지 않으면 거짓으로 담아 보내야 합니다. 누군가 `ready`를 하면 `updateRoomUser`를 통해 해당 유저의 준비 상태가 변경됩니다. 방장에게는 준비할 수 있는 버튼 대신에 게임을 시작할 수 있는 버튼이 주어집니다. 모든 플레이어가 준비해야만 버튼이 활성화 되어야 합니다. 방장이 게임 시작 버튼을 누르면 서버에 `startGame`가 전송됩니다. 만약 게임 시작에 실패하면 Response의 `reason`값으로 실패 사유가 전달되므로 이를 유저에게 보여줄 수도 있습니다. 성공적으로 게임이 시작되면 모든 유저에게 `startRound`가 전달됩니다.
방장을 제외한 모든 플레이어는 준비를 해야 게임이 시작될 수 있습니다. 서버에 `ready` 메세지를 보내서 준비 상태를 설정할 수 있습니다. 준비 상태로 설정하려면 `ready` 속성을 참, 그렇지 않으면 거짓으로 담아 보내야 합니다. 누군가 `ready`를 하면 `updateRoomUser`를 통해 해당 유저의 준비 상태가 변경됩니다. 방장에게는 준비할 수 있는 버튼 대신에 게임을 시작할 수 있는 버튼이 주어집니다. 방에 2명 이상의 인원이 접속한 상태에서, 모든 플레이어가 준비해야만 버튼이 활성화 되어야 합니다. 방장이 게임 시작 버튼을 누르면 서버에 `startGame`가 전송됩니다. 만약 게임 시작에 실패하면 Response의 `reason`값으로 실패 사유가 전달되므로 이를 유저에게 보여줄 수도 있습니다. 성공적으로 게임이 시작되면 모든 유저에게 `startRound`가 전달됩니다.
### 라운드 진행
준비 화면에서 라운드가 시작되면 `startRound`가 수신됩니다. `round`는 현재 라운드 넘버 (1부터 시작), `duration`은 현재 라운드의 길이를 초 단위로 나타냅니다. `roles`는 각 플레이어가 이번 라운드에서 맡게 된 역할입니다(후술). 항상 라운드가 시작되면 타이머를 라운드의 길이로 맞춘 뒤 타이머를 정지해주세요. 이때 그림을 그리는 사람이 단어를 선택하게 됩니다. 단어 선택이 끝나면 타이머의 시간이 흐르게 됩니다.
준비 화면에서 라운드가 시작되면 `startRound`가 수신됩니다. 즉, `startRound`를 수신하면 게임 화면으로 전환되어야 합니다. `round`는 현재 라운드 넘버 (1부터 시작), `duration`은 현재 라운드의 길이를 초 단위로 나타냅니다. `roles`는 각 플레이어가 이번 라운드에서 맡게 된 역할입니다(후술). 이제 그림을 그리는 사람이 단어를 선택하게 됩니다. 단어 선택이 끝나면 서버에서 `timer` 메세지를 수신받고 타이머의 시간이 흐르게 됩니다.
서버는 클라이언트 타이머의 상태를 `timer`를 보내서 동기화합니다. `state`가 `started`이면 메세지를 수신한 즉시 타이머를 동작시키고, `stopped`이면 타이머를 일시 정지합니다. 이때 `time`에 남은 시간이 초 단위로 포함되므로, 항상 이 메세지를 수신할 때마다 타이머의 남은 시간을 `time`값으로 동기화해주세요. 일반적으로 이 메세지는 단어 선택이 완료되어 라운드의 시간이 흐르기 시작하는 시점과 라운드가 종료되는 시점에 전송됩니다. 라운드가 종료되면 `state: stopped`인 `timer`가 수신됩니다.
모든 플레이어가 단어를 맞추거나, 타이머의 시간이 0으로 떨어지면 라운드가 종료되면서 `finishRound`가 수신됩니다. 이 메세지는 이번 라운드의 정답을 포함하고 있습니다. 만약 진행할 라운드가 더 남았다면 몇 초 뒤에 다시 `startRound`가 수신될 것입니다. 그러나 이번 라운드가 마지막이었다면 `finishGame`가 수신됩니다. 이는 게임이 정상적으로 종료되었다는 의미이며, 다시 준비 화면으로 전환해주시면 됩니다.
예외적인 케이스로, 이전 라운드가 비정상적으로 종료되었을 때 `finishRound`를 수신받지 않고 `startRound`를 수신받게 될 수 있습니다. 이때 `startRound`의 `round` 넘버가 이전 라운드와 동일한 값으로 수신받게 될 수도 있습니다. 예를 들면 `drawer`가 단어를 선택하지 않고 방에서 나가는 경우 해당 상황이 발생하게 됩니다.
또한 라운드를 진행 도중 누군가 퇴장하여 인원이 모자르게 된 경우, 즉시 `finishGame`을 수신받고 게임이 종료될 수 있습니다.
### 역할
가능한 역할은 `drawer`, `guesser`, `winner`, `spectator`로 구분됩니다. 이는 `startRound`와 함께 수신됩니다. 만약 라운드 진행 중에 역할이 바뀌게 된다면 `role`가 수신됩니다. 이는 단순히 플레이어 목록 UI를 업데이트 하기 위해서 사용되며, 따로 고려할 게임 로직은 없습니다.
...
...
@@ -78,6 +139,7 @@
`guesser`는 정답을 채팅에 입력할 수 있습니다. 만약 정답이라면 채팅이 서버에서 무시되고 역할이 `winner`로 변경되는 `role`이 수신되고, 정답을 담고 있는 `answerAccepted`가 수신됩니다.
만약 답을 맞추지 못했다면 일반 채팅으로 전달됩니다.
시간이 지나 라운드가 종료되고 다음 라운드를 기다리는 도중 답을 채팅에 입력하면 이는 무시되어 정답 처리되지 않습니다.