Skip to content

Commit

Permalink
Merge pull request #14 from Crazy-Cow/feature/CD-30
Browse files Browse the repository at this point in the history
feat: 대기실 출입 시 broadcastRoomState [CD-30]
  • Loading branch information
ddubbu-dev authored Nov 14, 2024
2 parents 955da6e + a9ca4bf commit 95da9c7
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 27 deletions.
65 changes: 52 additions & 13 deletions client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,67 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Socket.IO Client</title>
<script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<h1>Socket.IO Client</h1>
<label>별명</label>
<input id="nickname" />
<button id="checkNicknameButton">별명 등록</button>
<span id="nicknameStatus"></span>
<br />
<br />
<button id="joinRoomButton">Join Room</button>
<button id="leaveRoomButton">Leave Room</button>

<br />
<div id="roomState"></div>
<script>
const socket = io('http://localhost:8000') // Default namespace
let socketId = ''
const socket = io.connect('http://localhost:8000')
socket.on('connect', () => {
socketId = socket.id
})

document
.getElementById('joinRoomButton')
.addEventListener('click', function () {
socket.emit('room.enter')
console.log('emit (room.enter)')
$('#checkNicknameButton').click(function () {
const nickname = document.getElementById('nickname').value
fetch('http://localhost:8000/user/enter', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId: socketId,
nickName: nickname,
}),
})
.then((response) => {
if (!response.ok) {
throw new Error(
`HTTP error! status: ${response.status}`
)
}
return response.json()
})
.then((data) => {
$('#nicknameStatus').text(`등록 성공: ${data.nickName}`)
})
.catch((error) => {
$('#nicknameStatus').text('오류 발생')
})
})

document
.getElementById('leaveRoomButton')
.addEventListener('click', function () {
socket.emit('room.leave')
console.log('emit (room.leave)')
})
$('#joinRoomButton').click(function () {
socket.emit('room.enter')
console.log('emit (room.enter)')
})

$('#leaveRoomButton').click(function () {
socket.emit('room.leave')
console.log('emit (room.leave)')
})

socket.on('room.changeState', (data) => {
console.log('Received room.changeState:', data)
$('#roomState').text(`Room State: ${JSON.stringify(data)}`)
})
</script>
</body>
</html>
14 changes: 9 additions & 5 deletions src/service/rooms.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { User } from './users'
import util from './rooms.util'

type RoomState = 'initial' | 'waiting' | 'playing' | 'gameOver'
export type RoomState = 'initial' | 'waiting' | 'playing' | 'gameOver'

// 그룹 (대기실 | 게임중)
class Room {
export class Room {
roomId: string
players: User[] = []
createdAt: Date
Expand Down Expand Up @@ -73,17 +73,21 @@ class RoomPool {
}

joinRoom(user: User) {
const roomUserIn = this.waitingRoom
this.waitingRoom.addPlayer(user)

if (this.waitingRoom.canStartGame()) {
this.waitingRoom.state = 'playing'
this.gameRooms.push(this.waitingRoom)
this.waitingRoom = new Room({})
}

return roomUserIn
}

leaveRoom(userId: string) {
this.waitingRoom.removePlayer(userId)
return this.waitingRoom
}
}

Expand All @@ -103,12 +107,12 @@ class RoomService {
return this.instance
}

joinRoom(player: User) {
this.roomPool.joinRoom(player)
joinRoom(player: User): Room {
return this.roomPool.joinRoom(player)
}

leaveRoom(userId: string) {
this.roomPool.leaveRoom(userId)
return this.roomPool.leaveRoom(userId)
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/service/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ class UserService {
return user
}

removeUser(userId: string) {
this.userPool.removeUser(userId)
}

checkDuplicatedNickName(nickName: string) {
return this.userPool.checkDuplicatedNickName(nickName)
}
Expand Down
2 changes: 2 additions & 0 deletions src/socket/constant.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export const SOCKET_ON_EVT_TYPE = {
CONNECT: 'connection',
ROOM_ENTER: 'room.enter', // 빠른 시작
ROOM_LEAVE: 'room.leave', // 대기실 나가기
DISCONNECT: 'disconnect',
} as const

export const SOCKET_EMIT_EVT_TYPE = {
Expand Down
41 changes: 33 additions & 8 deletions src/socket/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Server, Socket } from 'socket.io'
import { SOCKET_ON_EVT_TYPE } from './constant'
import { SOCKET_EMIT_EVT_TYPE, SOCKET_ON_EVT_TYPE } from './constant'
import { SocketOnEvtData } from './type'
import roomService from '../service/rooms'
import roomService, { Room } from '../service/rooms'
import userService from '../service/users'
import util from './index.util'

class SocketImplement {
socket: Socket
Expand All @@ -13,22 +14,46 @@ class SocketImplement {
}

private register = () => {
this.socket.on(SOCKET_ON_EVT_TYPE.DISCONNECT, this.handleDisconnect)
this.socket.on(SOCKET_ON_EVT_TYPE.ROOM_ENTER, this.handleRoomEnter)
this.socket.on(SOCKET_ON_EVT_TYPE.ROOM_LEAVE, this.handleRoomLeave)
this.logger('event handler register')
this.logger('eventHandlers registered')
}

private handleDisconnect = (args: SocketOnEvtData['disconnect']) => {
this.logger('disconnect', args)
const userId = this.socket.id
const room = roomService.leaveRoom(userId)
userService.removeUser(userId)
this.broadcastRoomState(room)
}

private handleRoomEnter = (args: SocketOnEvtData['room.enter']) => {
this.logger(SOCKET_ON_EVT_TYPE.ROOM_ENTER, args)
this.logger('room.enter', args)
const userId = this.socket.id
const player = userService.findUserById(userId)
roomService.joinRoom(player)
const room = roomService.joinRoom(player)
this.socket.join(room.roomId)
this.broadcastRoomState(room)
}

private handleRoomLeave = (args: SocketOnEvtData['room.leave']) => {
this.logger(SOCKET_ON_EVT_TYPE.ROOM_LEAVE, args)
this.logger('room.leave', args)
const userId = this.socket.id
roomService.leaveRoom(userId)
const room = roomService.leaveRoom(userId)
this.broadcastRoomState(room)
}

private broadcastRoomState = (room: Room) => {
const data = util.getRoomStateDto(room)

// self
this.socket.emit(SOCKET_EMIT_EVT_TYPE.ROOM_CHANGE_STATE, data)

// the other
this.socket
.to(room.roomId)
.emit(SOCKET_EMIT_EVT_TYPE.ROOM_CHANGE_STATE, data)
}

public logger = (msg: string, args?: SocketOnEvtData) => {
Expand All @@ -41,7 +66,7 @@ class SocketImplement {
export function initSocket(io: Server): void {
const root = io.of('/')

root.on('connection', (socket: Socket) => {
root.on(SOCKET_ON_EVT_TYPE.CONNECT, (socket: Socket) => {
const instance = new SocketImplement(socket)
instance.logger('complete connection')
})
Expand Down
12 changes: 12 additions & 0 deletions src/socket/index.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Room } from '../service/rooms'
import { SocketEmitEvtData } from './type'

const getRoomStateDto = (room: Room): SocketEmitEvtData['room.changeState'] => {
return {
playerCnt: room.players.length,
state: room.state,
remainTime: 0, // TODO: 시간 줄이기 필요할까?
}
}

export default { getRoomStateDto }
4 changes: 3 additions & 1 deletion src/socket/type.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { RoomState } from 'service/rooms'
import { SOCKET_ON_EVT_TYPE, SOCKET_EMIT_EVT_TYPE } from './constant'

export type SocketOnEvtType = keyof typeof SOCKET_ON_EVT_TYPE
export type SocketOnEvtData = {
[SOCKET_ON_EVT_TYPE.DISCONNECT]: undefined
[SOCKET_ON_EVT_TYPE.ROOM_ENTER]: undefined
[SOCKET_ON_EVT_TYPE.ROOM_LEAVE]: undefined
}
Expand All @@ -10,7 +12,7 @@ export type SocketEmitEvtType = keyof typeof SOCKET_EMIT_EVT_TYPE
export type SocketEmitEvtData = {
[SOCKET_EMIT_EVT_TYPE.ROOM_CHANGE_STATE]: {
playerCnt: number
state: 'possible' | 'impossible'
state: RoomState
remainTime: number
}
[SOCKET_EMIT_EVT_TYPE.GAME_START]: undefined
Expand Down

0 comments on commit 95da9c7

Please sign in to comment.