Showing
1 changed file
with
33 additions
and
27 deletions
1 | # Protocol | 1 | # Protocol |
2 | 2 | ||
3 | 서버와의 통신은 socket.io를 사용합니다. | 3 | 서버와의 통신은 socket.io를 사용합니다. |
4 | -모든 메세지 interface들은 `server/message/types.ts`에 정의되어 있습니다. | 4 | +모든 메세지 interface들은 `common/message.ts`에 정의되어 있습니다. |
5 | -클라이언트에서 `emit`을 사용하여 전송한 모든 메세지에 대해 서버는 `MessageResponse`로 처리 결과를 알립니다. 자세한 사항은 https://socket.io/docs/v4/emitting-events/ 의 `Acknowledgements` 항목을 참조하세요. 만약 요청에 대한 결과가 즉시 필요한 경우 `MessageResponse.result`로 결과가 전달됩니다. | 5 | +실제 socket.io단에서 송수신되는 메세지 인터페이스는 `RawMessage`입니다. `type`은 메세지의 타입이며, `message`는 실제 메세지 데이터를 담고 있습니다. `type as ServerInboundMessageKey` 또는 `type as ServerOutboundMessageKey`를 사용하여 `type: string`을 유효한 메세지 타입으로 캐스팅할 수 있습니다. |
6 | 6 | ||
7 | -메세지 타입과 그에 대한 결과 타입은 `server/message/types.ts`를 참고하세요. | 7 | +클라이언트에서 송신하는 메세지는 `ServerInboundMessage<key>`, 서버에서 송신하는 메세지는 `ServerOutboundMessage<key>`로 정의되어 있습니다. 이때 `key`값은 메세지의 타입입니다. 두 메세지 셋에 대해 `key`값이 같더라도 속성이 다를 수 있음에 유의하세요. |
8 | + | ||
9 | +클라이언트에서 송신하는 모든 메세지에 대해, 서버는 요청 결과를 반환합니다. 요청 결과 메세지의 타입은 `ServerResponse<key>`로 정의되어 있습니다. `key`값은 `ServerInboundMessage<key>`의 `key`값과 동일합니다. | ||
10 | + | ||
11 | +메세지를 보낼때는 socket.io의 `emit`을 사용하며, 이때 사용되는 이벤트명은 `msg`로 모든 메세지에 대해 동일합니다. 특히 클라이언트는 `emit`의 콜백을 사용하여 `ServerResponse<key>` 타입의 요청 결과를 수신할 수 있습니다. 자세한 사항은 https://socket.io/docs/v4/emitting-events/ 의 `Acknowledgements` 항목을 참조하세요. | ||
12 | + | ||
13 | +서버에서 메세지를 전송하는 코드는 `server/connection/Connection.ts`에 구현되어 있고, 메세지 핸들링은 `server/message/MessageHandler.ts`와 `server/message/MessageHandlerChain.ts`를 통해 이뤄집니다. | ||
8 | 14 | ||
9 | ## 로그인 | 15 | ## 로그인 |
10 | 16 | ||
11 | -서버에 `LoginMessage`를 보냅니다. 아직 로그인 로직이 구현되지 않았으므로, 임의로 사용할 `username`만을 포함하여 보내면 됩니다. | 17 | +서버에 `login`을 보냅니다. 아직 로그인 로직이 구현되지 않았으므로, 임의로 사용할 `username`만을 포함하여 보내면 됩니다. |
12 | 18 | ||
13 | ## 로비 | 19 | ## 로비 |
14 | 20 | ||
15 | -로비에서는 모든 방 목록을 확인하고 접속할 수 있습니다. 서버에 `RoomListRequestMessage`를 전송하면 `RoomDescription[]`가 반환됩니다. 이 메세지는 모든 방에 관한 정보를 가지고 있습니다. 각 방은 고유한 `uuid`값으로 구분됩니다. | 21 | +로비에서는 모든 방 목록을 확인하고 접속할 수 있습니다. 서버에 `roomList`를 전송하면 `RoomDescription[]`가 반환됩니다. 이 메세지는 모든 방에 관한 정보를 가지고 있습니다. 각 방은 고유한 `uuid`값으로 구분됩니다. |
16 | 22 | ||
17 | -특정한 방에 접속하기 위해서는 서버에 `RoomJoinMessage`를 보내면 됩니다. 요청이 성공하면 `RoomInfo`가 반환됩니다. `RoomInfo`에는 본인을 제외한 다른 플레이어들의 정보만이 담겨 있습니다. | 23 | +특정한 방에 접속하기 위해서는 서버에 `joinRoom`을 보내면 됩니다. 요청이 성공하면 `RoomInfo`가 반환됩니다. `RoomInfo`에는 본인을 제외한 다른 플레이어들의 정보만이 담겨 있습니다. |
18 | 24 | ||
19 | ### 방 | 25 | ### 방 |
20 | 26 | ||
21 | -방에 접속중인 유저의 목록에 변화가 생기면, `RoomUserUpdateMessage`가 수신됩니다. `state`는 다음 3가지 값 중 하나의 값을 가집니다. | 27 | +방에 접속중인 유저의 목록에 변화가 생기면, `updateRoomUser`가 수신됩니다. `state`는 다음 3가지 값 중 하나의 값을 가집니다. |
22 | 28 | ||
23 | - `added`: 새로운 유저가 접속하였습니다. | 29 | - `added`: 새로운 유저가 접속하였습니다. |
24 | - `updated`: 기존 유저의 정보가 업데이트되었습니다. (아직 사용되지 않음) | 30 | - `updated`: 기존 유저의 정보가 업데이트되었습니다. (아직 사용되지 않음) |
25 | - `removed`: 유저가 방에서 퇴장하였습니다. | 31 | - `removed`: 유저가 방에서 퇴장하였습니다. |
26 | 32 | ||
27 | -유저가 채팅을 입력했다면 `RoomChatMessage`를 보내면 됩니다. 이 경우 오직 `message` 필드만 채워 보내면 됩니다. 다른 사람이 채팅을 입력한 경우, `RoomChatMessage`가 수신됩니다. 자신이 보낸 채팅에 대해서는 `RoomChatMessage`가 수신되지 않으므로 이 경우 오프라인으로 메세지를 추가하여야 합니다. | 33 | +유저가 채팅을 입력했다면 `chat`을 보내면 됩니다. 다른 사람이 채팅을 입력한 경우에도 `chat`이 수신됩니다(`ServerInboundMessage<"chat">`과 `ServerOutboundMessage<"chat">`이 다르게 정의되었다는 점에 유의하세요). 자신이 보낸 채팅에 대해서는 수신되지 않으므로 이 경우 오프라인으로 메세지를 추가하여야 합니다. |
28 | 34 | ||
29 | -방을 나가고 싶으면 `RoomLeaveMessage`를 보내면 됩니다. | 35 | +방을 나가고 싶으면 `leaveRoom`을 보내면 됩니다. |
30 | 36 | ||
31 | ## 게임 | 37 | ## 게임 |
32 | 38 | ||
33 | ### 루프 | 39 | ### 루프 |
34 | 40 | ||
35 | -준비 화면에서 라운드가 시작되면 `RoundStartMessage`가 수신됩니다. `round`는 현재 라운드 넘버 (1부터 시작), `duration`은 현재 라운드의 길이를 초 단위로 나타냅니다. `roles`는 각 플레이어가 이번 라운드에서 맡게 된 역할입니다(후술). 항상 라운드가 시작되면 타이머를 라운드의 길이로 맞춘 뒤 타이머를 정지해주세요. 이때 그림을 그리는 사람이 단어를 선택하게 됩니다. 단어 선택이 끝나면 타이머의 시간이 흐르게 됩니다. | 41 | +준비 화면에서 라운드가 시작되면 `startRound`가 수신됩니다. `round`는 현재 라운드 넘버 (1부터 시작), `duration`은 현재 라운드의 길이를 초 단위로 나타냅니다. `roles`는 각 플레이어가 이번 라운드에서 맡게 된 역할입니다(후술). 항상 라운드가 시작되면 타이머를 라운드의 길이로 맞춘 뒤 타이머를 정지해주세요. 이때 그림을 그리는 사람이 단어를 선택하게 됩니다. 단어 선택이 끝나면 타이머의 시간이 흐르게 됩니다. |
36 | -서버는 클라이언트 타이머의 상태를 `RoundTimerMessage`를 보내서 동기화합니다. `state`가 `started`이면 메세지를 수신한 즉시 타이머를 동작시키고, `stopped`이면 타이머를 일시 정지합니다. 이때 `time`에 남은 시간이 초 단위로 포함되므로, 항상 이 메세지를 수신할 때마다 타이머의 남은 시간을 `time`값으로 동기화해주세요. 일반적으로 이 메세지는 단어 선택이 완료되어 라운드의 시간이 흐르기 시작하는 시점에 한 번만 전송됩니다. 라운드가 종료되면 `state: stopped`인 `RoundTimerMessage`가 수신됩니다. | 42 | +서버는 클라이언트 타이머의 상태를 `timer`를 보내서 동기화합니다. `state`가 `started`이면 메세지를 수신한 즉시 타이머를 동작시키고, `stopped`이면 타이머를 일시 정지합니다. 이때 `time`에 남은 시간이 초 단위로 포함되므로, 항상 이 메세지를 수신할 때마다 타이머의 남은 시간을 `time`값으로 동기화해주세요. 일반적으로 이 메세지는 단어 선택이 완료되어 라운드의 시간이 흐르기 시작하는 시점에 한 번만 전송됩니다. 라운드가 종료되면 `state: stopped`인 `timer`가 수신됩니다. |
37 | -모든 플레이어가 단어를 맞추거나, 타이머의 시간이 0으로 떨어지면 라운드가 종료되면서 `RoundFinishMessage`가 수신됩니다. 이 메세지는 이번 라운드의 정답을 포함하고 있습니다. 만약 진행할 라운드가 더 남았다면 몇 초 뒤에 다시 `RoundStartMessage`가 수신될 것입니다. 그러나 이번 라운드가 마지막이었다면 `GameFinishMessage`가 수신됩니다. 이는 게임이 정상적으로 종료되었다는 의미이며, 다시 준비 화면으로 전환해주시면 됩니다. | 43 | +모든 플레이어가 단어를 맞추거나, 타이머의 시간이 0으로 떨어지면 라운드가 종료되면서 `finishRound`가 수신됩니다. 이 메세지는 이번 라운드의 정답을 포함하고 있습니다. 만약 진행할 라운드가 더 남았다면 몇 초 뒤에 다시 `startRound`가 수신될 것입니다. 그러나 이번 라운드가 마지막이었다면 `finishGame`가 수신됩니다. 이는 게임이 정상적으로 종료되었다는 의미이며, 다시 준비 화면으로 전환해주시면 됩니다. |
38 | 44 | ||
39 | ### 역할 | 45 | ### 역할 |
40 | 46 | ||
41 | -가능한 역할은 `drawer`, `guesser`, `winner`, `spectator`로 구분됩니다. 이는 `RoundStartMessage`와 함께 수신됩니다. 만약 라운드 진행 중에 역할이 바뀌게 된다면 `RoundRoleMessage`가 수신됩니다. 이는 단순히 플레이어 목록 UI를 업데이트 하기 위해서 사용되며, 따로 고려할 게임 로직은 없습니다. | 47 | +가능한 역할은 `drawer`, `guesser`, `winner`, `spectator`로 구분됩니다. 이는 `startRound`와 함께 수신됩니다. 만약 라운드 진행 중에 역할이 바뀌게 된다면 `role`가 수신됩니다. 이는 단순히 플레이어 목록 UI를 업데이트 하기 위해서 사용되며, 따로 고려할 게임 로직은 없습니다. |
42 | 48 | ||
43 | - `drawer`: 그림을 그리는 사람입니다. | 49 | - `drawer`: 그림을 그리는 사람입니다. |
44 | - `guesser`: 그림을 보고 단어를 맞추는 사람입니다. 아직 단어를 맞추지 못한 상태입니다. | 50 | - `guesser`: 그림을 보고 단어를 맞추는 사람입니다. 아직 단어를 맞추지 못한 상태입니다. |
... | @@ -47,17 +53,17 @@ | ... | @@ -47,17 +53,17 @@ |
47 | 53 | ||
48 | ### drawer | 54 | ### drawer |
49 | 55 | ||
50 | -`drawer`는 라운드가 시작된 뒤 바로 `RoundWordSetMessage`를 통해 선택 가능한 단어들을 수신받습니다. 수신받는 단어 수는 3개입니다. `drawer`는 이 중 하나를 선택해서 그림을 그릴 수 있습니다. 단어를 선택하면 해당 단어를 `RoundChooseWordMessage`에 담아 서버로 송신합니다. 서버는 이를 확인하고 타이머를 동작시킵니다. 나머지 참가자들은 오직 정답의 글자수만을 담고 있는 `RoundWordChosenMessage`를 수신받게 됩니다. | 56 | +`drawer`는 라운드가 시작된 뒤 바로 `wordSet`을 통해 선택 가능한 단어들을 수신받습니다. 수신받는 단어 수는 3개입니다. `drawer`는 이 중 하나를 선택해서 그림을 그릴 수 있습니다. 단어를 선택하면 해당 단어를 `chooseWord`에 담아 서버로 송신합니다. 서버는 이를 확인하고 타이머를 동작시킵니다. 나머지 참가자들은 오직 정답의 글자수만을 담고 있는 `wordChosen`을 수신받게 됩니다. |
51 | -그림은 `drawer`의 브러시 움직임을 그대로 시뮬레이션하여 만들어집니다. `drawer`가 색깔, 굵기를 바꾸면 `PaintBrushMessage`가 서버로 전송되고, 나머지 플레이어들은 이를 수신받게 됩니다. `size`는 브러시의 지름을 픽셀 단위로 나타내고, `color`는 브러시의 색상을 6자리 소문자 16진수로 나타냅니다. 이 메세지는 `drawing` 필드도 담고 있는데, 이는 마우스가 눌린 상태인지, 즉 브러시로 칠을 하는 상태인지를 나타냅니다. 중요한 것은 `drawer`가 캔버스 위에 마우스를 누르는 순간 `drawing`이 `true`로 설정된 메세지가, 캔버스에서 마우스를 떼는 순간 `false`로 설정된 메세지가 서버로 전송되어야 한다는 점입니다. | 57 | +그림은 `drawer`의 브러시 움직임을 그대로 시뮬레이션하여 만들어집니다. `drawer`가 색깔, 굵기를 바꾸면 `setBrush`가 서버로 전송되고, 나머지 플레이어들은 이를 수신받게 됩니다. `size`는 브러시의 지름을 픽셀 단위로 나타내고, `color`는 브러시의 색상을 6자리 소문자 16진수로 나타냅니다. 이 메세지는 `drawing` 필드도 담고 있는데, 이는 마우스가 눌린 상태인지, 즉 브러시로 칠을 하는 상태인지를 나타냅니다. 중요한 것은 `drawer`가 캔버스 위에 마우스를 누르는 순간 `drawing`이 `true`로 설정된 메세지가, 캔버스에서 마우스를 떼는 순간 `false`로 설정된 메세지가 서버로 전송되어야 한다는 점입니다. |
52 | -만약 `drawer`가 캔버스 위에서 그림을 그리는 중이라면 실시간으로 `PaintMoveMessage`가 서버로 전송되어야 합니다. `x`와 `y`는 캔버스 오른쪽 아래 지점을 (0, 0)로 설정했을 때의 마우스의 좌표를 픽셀 단위로 나타냅니다. | 58 | +만약 `drawer`가 캔버스 위에서 그림을 그리는 중이라면 실시간으로 `moveBrush`가 서버로 전송되어야 합니다. `x`와 `y`는 캔버스 오른쪽 아래 지점을 (0, 0)로 설정했을 때의 마우스의 좌표를 픽셀 단위로 나타냅니다. |
53 | -다른 플레이어들은 `PaintBrushMessage`를 통해 `drawing`이 `true`로 설정된 시점부터, 다시 `drawing`이 `false`로 설정되는 시점까지, 마우스가 움직이는 모든 위치에 대해 점이 찍히게 됩니다. 정확히는 마우스의 좌표가 업데이트 될 때 이전 지점과 현재 지점을 선으로 이어 칠해주는 방식으로 보간을 해야 할 것입니다. | 59 | +다른 플레이어들은 `setBrush`를 통해 `drawing`이 `true`로 설정된 시점부터, 다시 `drawing`이 `false`로 설정되는 시점까지, 마우스가 움직이는 모든 위치에 대해 점이 찍히게 됩니다. 정확히는 마우스의 좌표가 업데이트 될 때 이전 지점과 현재 지점을 선으로 이어 칠해주는 방식으로 보간을 해야 할 것입니다. |
54 | 60 | ||
55 | 전체 과정을 정리하자면 다음과 같습니다. | 61 | 전체 과정을 정리하자면 다음과 같습니다. |
56 | 62 | ||
57 | -- 굵기, 색상을 변경하면 `PaintBrushMessage` 전송. | 63 | +- 굵기, 색상을 변경하면 `setBrush` 전송. |
58 | -- 캔버스 위에서 마우스를 누르는 시점에 현재 마우스 위치를 `PaintMoveMessage`로 **먼저** 전송한 뒤에 `drawing`을 `true`로 한 `PaintBrushMessage` 전송. (만약 Move가 먼저 전송되지 않는다면 마지막으로 브러시를 뗀 위치에서 현재 위치까지 불필요한 선이 그려질 것입니다.) | 64 | +- 캔버스 위에서 마우스를 누르는 시점에 현재 마우스 위치를 `moveBrush`로 **먼저** 전송한 뒤에 `drawing`을 `true`로 한 `setBrush` 전송. (만약 Move가 먼저 전송되지 않는다면 마지막으로 브러시를 뗀 위치에서 현재 위치까지 불필요한 선이 그려질 것입니다.) |
59 | -- 캔버스 위에서 마우스를 움직이는 동안 `PaintMoveMessage` 전송. | 65 | +- 캔버스 위에서 마우스를 움직이는 동안 `moveBrush` 전송. |
60 | -- 마우스를 떼는 시점에 `drawing`을 `false`로 한 `PaintBrushMessage` 전송. | 66 | +- 마우스를 떼는 시점에 `drawing`을 `false`로 한 `setBrush` 전송. |
61 | 67 | ||
62 | 이 정보들을 가지고 캔버스를 칠하는 컴포넌트를 만들어서, 이를 `drawer`의 클라이언트에도 동일하게 사용하는 방식으로 구현하여 `drawer`와 다른 플레이어의 캔버스가 동일하게 보이도록 해야 할 것입니다. | 68 | 이 정보들을 가지고 캔버스를 칠하는 컴포넌트를 만들어서, 이를 `drawer`의 클라이언트에도 동일하게 사용하는 방식으로 구현하여 `drawer`와 다른 플레이어의 캔버스가 동일하게 보이도록 해야 할 것입니다. |
63 | 69 | ||
... | @@ -65,7 +71,7 @@ | ... | @@ -65,7 +71,7 @@ |
65 | 71 | ||
66 | ### guesser | 72 | ### guesser |
67 | 73 | ||
68 | -`guesser`는 정답을 채팅에 입력할 수 있습니다. 만약 정답이라면 채팅이 서버에서 무시되고 역할이 `winner`로 변경되는 `RoundRoleMessage`가 수신되고, 정답을 담고 있는 `AnswerAcceptedMessage`가 수신됩니다. | 74 | +`guesser`는 정답을 채팅에 입력할 수 있습니다. 만약 정답이라면 채팅이 서버에서 무시되고 역할이 `winner`로 변경되는 `role`이 수신되고, 정답을 담고 있는 `answerAccepted`가 수신됩니다. |
69 | 만약 답을 맞추지 못했다면 일반 채팅으로 전달됩니다. | 75 | 만약 답을 맞추지 못했다면 일반 채팅으로 전달됩니다. |
70 | 76 | ||
71 | ### winner, spectator | 77 | ### winner, spectator |
... | @@ -77,12 +83,12 @@ | ... | @@ -77,12 +83,12 @@ |
77 | 게임 도중 입장한 유저에게는 다음과 같은 메세지들이 모두 전송됩니다. | 83 | 게임 도중 입장한 유저에게는 다음과 같은 메세지들이 모두 전송됩니다. |
78 | 84 | ||
79 | 1. 준비 상태에서 자신이 방에 접속했을 때 전달 받는 모든 메세지들 | 85 | 1. 준비 상태에서 자신이 방에 접속했을 때 전달 받는 모든 메세지들 |
80 | -2. 현재 라운드에 대한 정보를 담은 `RoundStartMessage` | 86 | +2. 현재 라운드에 대한 정보를 담은 `startRound` |
81 | -3. 현재 라운드 타이머와 동기화할 수 있는 `RoundTimerMessage` | 87 | +3. 현재 라운드 타이머와 동기화할 수 있는 `timer` |
82 | -4. 마지막으로 서버상으로 기록된 브러시 정보를 담은 `PaintBrushMessage` | 88 | +4. 마지막으로 서버상으로 기록된 브러시 정보를 담은 `setBrush` |
83 | -5. 마지막으로 서버상으로 기록된 브러시 위치를 담은 `PaintMoveMessage` | 89 | +5. 마지막으로 서버상으로 기록된 브러시 위치를 담은 `moveBrush` |
84 | 90 | ||
85 | 다른 플레이어에게는 다음과 같은 메세지들이 모두 전송됩니다. | 91 | 다른 플레이어에게는 다음과 같은 메세지들이 모두 전송됩니다. |
86 | 92 | ||
87 | 1. 준비 상태에서 해당 유저가 방에 접속했을 때 전달 받는 모든 메세지들 | 93 | 1. 준비 상태에서 해당 유저가 방에 접속했을 때 전달 받는 모든 메세지들 |
88 | -2. 신규 유저를 관전자로 설정하는 `RoundRoleMessage` 메세지 | 94 | +2. 신규 유저를 관전자로 설정하는 `role` 메세지 | ... | ... |
-
Please register or login to post a comment