Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE] feat#263 강퇴구현, 모니터링 도입 #267

Merged
merged 3 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion BE/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module.exports = {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
ignorePatterns: ['.eslintrc.js', 'main.ts'],
rules: {
"eqeqeq": "off",
"curly": "error",
Expand Down
403 changes: 398 additions & 5 deletions BE/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions BE/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"pinpoint-node-agent": "^0.9.0-next.0",
"redis": "^4.7.0",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
Expand Down
3 changes: 2 additions & 1 deletion BE/src/common/constants/exception-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export const ExceptionMessage = {
ONLY_HOST: '방장이 아닙니다.',
GAME_NOT_STARTED: '게임이 시작되지 않았습니다.',
EXCEEDS_QUIZ_SET_LIMIT: '선택된 퀴즈 수가 퀴즈셋에 있는 퀴즈 수를 초과했습니다.',
GAME_ALREADY_STARTED: '게임이 이미 시작되었습니다.'
GAME_ALREADY_STARTED: '게임이 이미 시작되었습니다.',
PLAYER_NOT_FOUND: '존재하지 않는 플레이어입니다.'
};
33 changes: 17 additions & 16 deletions BE/src/common/constants/socket-events.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
const SocketEvents = {
CHAT_MESSAGE: 'chatMessage',
UPDATE_POSITION: 'updatePosition',
CREATE_ROOM: 'createRoom',
UPDATE_ROOM_OPTION: 'updateRoomOption',
UPDATE_ROOM_QUIZSET: 'updateRoomQuizset',
JOIN_ROOM: 'joinRoom',
START_GAME: 'startGame',
END_GAME: 'endGame',
END_QUIZ_TIME: 'endQuizTime',
START_QUIZ_TIME: 'startQuizTime',
UPDATE_SCORE: 'updateScore',
EXIT_ROOM: 'exitRoom'
} as const;

export default SocketEvents;
const SocketEvents = {
CHAT_MESSAGE: 'chatMessage',
UPDATE_POSITION: 'updatePosition',
CREATE_ROOM: 'createRoom',
UPDATE_ROOM_OPTION: 'updateRoomOption',
UPDATE_ROOM_QUIZSET: 'updateRoomQuizset',
JOIN_ROOM: 'joinRoom',
START_GAME: 'startGame',
END_GAME: 'endGame',
END_QUIZ_TIME: 'endQuizTime',
START_QUIZ_TIME: 'startQuizTime',
UPDATE_SCORE: 'updateScore',
EXIT_ROOM: 'exitRoom',
KICK_ROOM: 'kickRoom'
} as const;

export default SocketEvents;
10 changes: 10 additions & 0 deletions BE/src/game/dto/kick-room.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IsString, Length } from 'class-validator';

export class KickRoomDto {
@IsString()
@Length(6, 6, { message: 'PIN번호는 6자리이어야 합니다.' })
gameId: string;

@IsString()
kickPlayerId: string;
}
7 changes: 7 additions & 0 deletions BE/src/game/game.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { GameChatService } from './service/game.chat.service';
import { GameRoomService } from './service/game.room.service';
import { WsJwtAuthGuard } from '../auth/guard/ws-jwt-auth.guard';
import { GameActivityInterceptor } from './interceptor/gameActivity.interceptor';
import { KickRoomDto } from './dto/kick-room.dto';

@UseInterceptors(GameActivityInterceptor)
@UseFilters(new WsExceptionFilter())
Expand Down Expand Up @@ -111,6 +112,12 @@ export class GameGateway {
await this.gameService.startGame(startGameDto, client.id);
}

@SubscribeMessage(SocketEvents.KICK_ROOM)
@UsePipes(new GameValidationPipe(SocketEvents.KICK_ROOM))
async handleKickRoom(@MessageBody() kickRoomDto: KickRoomDto, @ConnectedSocket() client: Socket) {
await this.gameRoomService.kickRoom(kickRoomDto, client.id);
}

afterInit() {
this.logger.verbose('WebSocket 서버 초기화 완료했어요!');

Expand Down
12 changes: 12 additions & 0 deletions BE/src/game/redis/subscribers/player.subscriber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ export class PlayerSubscriber extends RedisSubscriber {
case 'Disconnect':
await this.handlePlayerDisconnect(playerId, playerData, server);
break;
case 'Kicked':
await this.handlePlayerKicked(playerId, playerData, server);
break;
}
}

Expand Down Expand Up @@ -80,4 +83,13 @@ export class PlayerSubscriber extends RedisSubscriber {
});
this.logger.verbose(`Player disconnected: ${playerId} from game: ${playerData.gameId}`);
}

private async handlePlayerKicked(playerId: string, playerData: any, server: Server) {
server.to(playerData.gameId).emit(SocketEvents.KICK_ROOM, {
playerId
});
this.logger.verbose(`Player kicked: ${playerId} from game: ${playerData.gameId}`);
//클라에서 exitRoom도 주기를 원함
await this.handlePlayerDisconnect(playerId, playerData, server);
}
}
18 changes: 17 additions & 1 deletion BE/src/game/service/game.room.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { UpdateRoomOptionDto } from '../dto/update-room-option.dto';
import { UpdateRoomQuizsetDto } from '../dto/update-room-quizset.dto';
import { Cron, CronExpression } from '@nestjs/schedule';
import { Socket } from 'socket.io';
import { KickRoomDto } from '../dto/kick-room.dto';

@Injectable()
export class GameRoomService {
Expand Down Expand Up @@ -150,7 +151,6 @@ export class GameRoomService {

// 플레이어 제거
pipeline.srem(REDIS_KEY.ROOM_PLAYERS(roomId), clientId);
// pipeline.del(REDIS_KEY.PLAYER(clientId));
// 1. 플레이어 상태를 'disconnected'로 변경하고 TTL 설정
pipeline.hmset(REDIS_KEY.PLAYER(clientId), {
disconnected: '1',
Expand Down Expand Up @@ -227,4 +227,20 @@ export class GameRoomService {

await pipeline.exec();
}

async kickRoom(kickRoomDto: KickRoomDto, clientId: string) {
const { gameId, kickPlayerId } = kickRoomDto;

const roomKey = REDIS_KEY.ROOM(gameId);
const room = await this.redis.hgetall(roomKey);
this.gameValidator.validateRoomExists(SocketEvents.KICK_ROOM, room);
this.gameValidator.validatePlayerIsHost(SocketEvents.KICK_ROOM, room, clientId);

const targetPlayerKey = REDIS_KEY.PLAYER(kickPlayerId);
const targetPlayer = await this.redis.hgetall(targetPlayerKey);
this.gameValidator.validatePlayerExists(SocketEvents.KICK_ROOM, targetPlayer);
await this.redis.set(`${targetPlayerKey}:Changes`, 'Kicked', 'EX', 6000); // 해당플레이어의 변화정보 10분 후에 삭제

await this.handlePlayerExit(kickPlayerId);
}
}
6 changes: 6 additions & 0 deletions BE/src/game/validations/game.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,10 @@ export class GameValidator {
throw new GameWsException(eventName, ExceptionMessage.GAME_ALREADY_STARTED);
}
}

validatePlayerExists(eventName: string, targetPlayer: any) {
if (!targetPlayer) {
throw new GameWsException(eventName, ExceptionMessage.PLAYER_NOT_FOUND);
}
}
}
5 changes: 5 additions & 0 deletions BE/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { config } from 'dotenv';
import { join } from 'path';
import 'pinpoint-node-agent';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Logger } from '@nestjs/common';
import { GameActivityInterceptor } from './game/interceptor/gameActivity.interceptor';
// env 불러오기
config({ path: join(__dirname, '..', '.env') }); // ../ 경로의 .env 로드
Comment on lines +8 to +9
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거는 env 로딩 안 되는 문제때문에 시도하신걸까요?


async function bootstrap() {
const app = await NestFactory.create(AppModule);
Expand Down