오늘의 목표
⏱️ 오늘의 일정
최종프로젝트 진행
📜 최종프로젝트 진행
최종프로젝트를 진행했다. 저번에 이어 게임 준비와 게임 시작 까지 완성해 게임의 기본적인 틀을 완성했다.
게임 준비 요청 처리
import net from 'net';
import { GlobalFailCode } from '../enumTyps.js';
import {
CustomSocket,
RedisUserData,
Room,
User,
} from '../../interface/interface.js';
import { config } from '../../config/config.js';
import { sendPacket } from '../../packet/createPacket.js';
import {
getRoomByUserId,
getRooms,
getSocketByUser,
getUserBySocket,
setCharacterInfoInit,
setRedisData,
} from '../handlerMethod.js';
export const gamePrepareHandler = async (
socket: CustomSocket,
payload: Object,
) => {
try {
// requset 보낸 유저
const user: RedisUserData = await getUserBySocket(socket);
// 유저가 있는 방 찾기
if (user !== undefined) {
const room: Room | null = await getRoomByUserId(user.id);
if (room === null) {
return;
}
if (room.users.length <= 1) {
console.error('게임을 시작 할 수 없습니다(인원 부족).');
const responseData = {
success: false,
failCode: GlobalFailCode.INVALID_REQUEST,
};
sendPacket(
socket,
config.packetType.GAME_PREPARE_RESPONSE,
responseData,
);
} else {
// 게임준비 시작 요건 충족
const responseData = {
success: true,
failCode: GlobalFailCode.NONE,
};
sendPacket(
socket,
config.packetType.GAME_PREPARE_RESPONSE,
responseData,
);
// 방에있는 유저들 캐릭터 랜덤 배정하기
room.users = setCharacterInfoInit(room.users);
const rooms: Room[] | null = await getRooms();
if (!rooms) {
return;
}
// 변경한 정보 덮어쓰기
for (let i = 0; i < rooms.length; i++) {
if (rooms[i].id === room.id) {
rooms[i] = room;
break;
}
}
// 레디스에 있는 룸 배열에서 user가 속해 있는 방을 수정하고,
// 위에서 수정한 방이 포함되어 있는 전체 배열을 넣음
await setRedisData('roomData', rooms);
// 방에있는 유저들에게 notifi 보내기
for (let i = 0; i < room.users.length; i++) {
const userSocket = await getSocketByUser(room.users[i]);
if (!userSocket) {
console.error('gamePrepareHandler: socket not found');
return;
}
//console.dir(room, { depth: null });
sendPacket(userSocket, config.packetType.GAME_PREPARE_NOTIFICATION, {
room,
});
}
}
} else {
console.error('위치: gamePrepareHandler, 유저를 찾을 수 없습니다.');
const responseData = {
success: false,
failCode: GlobalFailCode.INVALID_REQUEST,
};
sendPacket(socket, config.packetType.GAME_PREPARE_RESPONSE, responseData);
}
} catch (err) {
const responseData = {
success: false,
failCode: GlobalFailCode.INVALID_REQUEST,
};
sendPacket(socket, config.packetType.GAME_PREPARE_RESPONSE, responseData);
console.error('gameStartHandler 오류', err);
}
};
위 코드는 게임 준비 요청을 클라가 전송하면 서버에서 처리를 해주는 코드로,
우선 클라가 속한 방을 찾은 후, 방이 게임 준비 요건에 맞는지 검사한다.
방이 게임 준비 요건에 맞으면, 각 캐릭터들의 역할을 랜덤하게 서버가 정해주고 방 정보를 저장한 후,
방 안에 있는 유저들에게 noti를 날려 게임 준비가 완료되었다고 알려준다.
게임 준비가 완료되어 캐릭터와 역할이 정해진 모습
게임시작 요청 처리
import net from 'net';
import {
CharacterPositionData,
CustomSocket,
RedisUserData,
} from '../../interface/interface.js';
import { GlobalFailCode, PhaseType } from '../enumTyps.js';
import { sendPacket } from '../../packet/createPacket.js';
import { config } from '../../config/config.js';
import {
getRedisData,
getRoomByUserId,
getSocketByUser,
getUserBySocket,
setRedisData,
} from '../handlerMethod.js';
export const gameStartHandler = async (
socket: CustomSocket,
payload: Object,
) => {
try {
// requset 보낸 유저
const user: RedisUserData = await getUserBySocket(socket);
// 유저가 있는 방 찾기
if (user !== undefined) {
const room = await getRoomByUserId(user.id);
if (room === null) {
const responseData = {
success: false,
failCode: GlobalFailCode.INVALID_REQUEST,
};
sendPacket(socket, config.packetType.GAME_START_RESPONSE, responseData);
return;
}
if (room.users.length <= 1) {
console.error('게임을 시작 할 수 없습니다.(인원 부족)');
}
const responseData = {
success: true,
failCode: GlobalFailCode.NONE,
};
sendPacket(socket, config.packetType.GAME_START_RESPONSE, responseData);
let characterPositionDatas = await getRedisData('characterPositionDatas');
if (!characterPositionDatas) {
characterPositionDatas = { [room.id]: [] };
} else {
characterPositionDatas[room.id] = [];
}
for (let i = 0; i < room.users.length; i++) {
// 위치 데이터 로직 필요
const characterPositionData: CharacterPositionData = {
id: room.users[i].id,
x: 0 + 1 * i,
y: 0 - 1 * i,
};
characterPositionDatas[room.id].push(characterPositionData);
}
await setRedisData('characterPositionDatas', characterPositionDatas);
// 방에있는 유저들에게 notifi 보내기
for (let i = 0; i < room.users.length; i++) {
const userSocket = await getSocketByUser(room.users[i]);
if (!userSocket) {
console.error('gameStartHandler: socket not found');
const responseData = {
success: false,
failCode: GlobalFailCode.INVALID_REQUEST,
};
sendPacket(
socket,
config.packetType.GAME_START_RESPONSE,
responseData,
);
return;
}
// noti 데이터
const now = Date.now() + 300000;
const gameStateData = { phaseType: PhaseType.DAY, nextPhaseAt: now };
const notifiData = {
gameState: gameStateData,
users: room.users,
characterPositions: characterPositionDatas[room.id],
};
sendPacket(
userSocket,
config.packetType.GAME_START_NOTIFICATION,
notifiData,
);
}
} else {
console.error('위치: gameStartHandler, 유저를 찾을 수 없습니다.');
const responseData = {
success: false,
failCode: GlobalFailCode.INVALID_REQUEST,
};
sendPacket(socket, config.packetType.GAME_START_RESPONSE, responseData);
}
} catch (err) {
const responseData = {
success: false,
failCode: GlobalFailCode.INVALID_REQUEST,
};
sendPacket(socket, config.packetType.GAME_START_RESPONSE, responseData);
console.log('gameStartHandler 오류', err);
}
};
위 코드는 게임 시작 요청을 클라가 전송하면 서버에서 처리를 해주는 코드로,
우선 클라가 속한 방을 찾은 후, 방이 게임 준비 요건에 맞는지 검사한다.
방이 게임 시작 요건에 맞으면, 각 캐릭터들의 좌표를 설정해주고, 레디스에 좌표를 저장 한 후
게임 시작에 필요한 정보를 설정하고, 방안에 있는 유저들에게 noti를 날려 게임시작이 완료되었다고 알려준다.
게임시작이 완료되어 캐릭터가 생성된 모습
게임 시작에서 캐릭터의 시작 위치를 랜덤하게 설정해줬는데,
추후에 미리 스폰 위치를 여러개 설정하고, 스폰 위치 중에서 랜덤하게 선택해 유저가 스폰될 수 있도록 고쳐야 겠다.