socket io 타워 디펜스

변우영·2024년 10월 16일

Socket IO를 이용한 타워디펜스 게임 만들기!


링크텍스트

📂 src/
 ┣ 📂 init/
 ┃ ┣ 📜 assets.js
 ┃ ┣ 📜 redis.js
 ┃ ┣ 📜 socket.js
 ┣ 📂 handlers/
 ┃ ┣ 📜 game.handler.js
 ┃ ┣ 📜 tower.handler.js
 ┃ ┣ 📜 monster.handler.js
 ┃ ┣ 📜 stage.handler.js
 ┣ 📂 manager/
 ┃ ┣ 📜 monster.manager.js
 ┣ 📂 middlewares/
 ┃ ┣ 📜 auth.middleware.js
 ┃ ┣ 📜 error-handler.middleware.js
 ┣ 📂 models/
 ┃ ┣ 📜 tower.model.js
 ┃ ┣ 📜 monster.model.js
 ┣ 📂 routes/
 ┃ ┗ 📜 account.router.js
 ┣ 📂 utils/
 ┃ ┗ 📂 joi/
 ┃   ┗ 📜 valid.schema.js

타워 디펜스 프로젝트: 이벤트 핸들러 함수 분석

타워 디펜스 프로젝트의 이벤트 핸들러 함수들을 다뤄보겠습니다. 이 함수들은 게임 내에서 플레이어와 서버 간의 상호작용을 관리하며, 타워 배치, 몬스터 소환, 스테이지 진행 등의 핵심 기능을 담당합니다. 제공된 파일에 맞추어 각 핸들러의 역할을 자세히 설명해드리겠습니다.


📝 게임 시작 및 종료 핸들러

2. gameStart - 게임 시작

📌 개요

gameStart 함수는 유저가 게임을 시작할 때 초기 게임 데이터를 설정하고, 클라이언트에 해당 데이터를 전달하는 역할을 합니다. 유저의 골드, 타워, 스테이지 정보 등을 Redis에 동기화하고, 몬스터 생성 작업을 처리합니다.

🔑 매개변수

  • uuid: 유저의 고유 식별자.
  • payload: 클라이언트로부터 받은 데이터로, 타임스탬프를 포함.
  • socket: 클라이언트와 서버 간 실시간 통신을 위한 소켓 객체.

🚀 주요 동작

  1. 게임 자산 및 유저 정보 조회

    • getGameAssets()를 호출해 초기 게임 데이터를 불러옵니다.
    • getUserInfo(uuid)를 통해 유저 정보를 조회하고, 유저의 현재 골드, HP, 타워, 스테이지 정보를 불러옵니다.
  2. 게임 시작 데이터 설정

    • 유저가 보유한 골드와 HP 정보를 초기화 데이터와 비교해 설정합니다.
    • 초기 타워 개수, 몬스터 생성 주기 등을 설정하고, 유저의 스테이지와 인벤토리 정보를 초기화합니다.
  3. 타임스탬프 유효성 검사

    • payload.timeStamp가 없는 경우, 게임 초기 데이터 검증 실패 에러를 발생시킵니다.
  4. Redis 동기화 작업

    • syncTowersToRedis(socket)syncTowerStatsToRedis()를 통해 타워와 타워 스탯을 Redis에 동기화합니다.
    • spawnMonsters(uuid)를 호출해 스테이지에 맞는 몬스터를 생성하고, 해당 데이터를 클라이언트에 전달합니다.
    • syncStageToRedis()로 스테이지 정보를 Redis에 저장합니다.
  5. 클라이언트로 게임 시작 이벤트 전송

    • 설정된 게임 시작 정보를 클라이언트로 전송하여 게임을 시작합니다.

🔄 반환 값

  • 성공 시: { status: 'success', message: '게임 시작!', ... }

2. gameEnd - 게임 종료

📌 개요

gameEnd 함수는 유저가 게임을 종료할 때 DB에 현재 스테이지와 골드 정보를 저장하고, 캐시에서 관련 데이터를 삭제하는 역할을 합니다.

🔑 매개변수

  • uuid: 유저의 고유 식별자.
  • payload: 클라이언트로부터 받은 데이터로 게임 종료 시 필요 정보 포함.

🚀 주요 동작

  1. 초기 데이터 및 캐시에서 스테이지 정보 조회

    • getGameAssets()를 호출해 초기 데이터를 불러오고, 초기 골드 값을 설정합니다.
    • RedisManager.getUserData(uuid)를 호출해 캐시에서 유저가 도달한 스테이지 정보를 가져옵니다.
  2. DB에 유저 데이터 저장

    • userDataClient.users.update()를 통해 유저의 골드와 스테이지 정보를 DB에 저장합니다.
  3. 캐시 삭제

    • RedisManager.deleteCache()를 호출해 유저의 게임 정보 및 인벤토리 관련 캐시 데이터를 삭제합니다.

🔄 반환 값

  • 성공 시: { status: 'success' }

📝 타워 핸들러

1. initTowerHandler - 타워 초기화

📌 개요

타워를 초기화할 때 사용되는 함수로, 유저의 인벤토리에 새로운 타워 데이터를 설정합니다.

🔑 매개변수

  • userId: 유저의 고유 식별자.
  • payload: 클라이언트로부터 받은 데이터로 타워 인벤토리를 포함.

🚀 주요 동작

  1. 유저 정보를 getUserInfo(userId)를 통해 불러옵니다.
  2. 유저의 인벤토리를 payload.towerInven으로 갱신합니다.
  3. updateUserInventory(userId, user.inventory)를 호출해 갱신된 인벤토리를 저장합니다.
  4. 타워 초기화 성공 메시지를 반환합니다.

🔄 반환 값

  • 성공 시: { status: 'success', message: '타워 초기화 성공', inventory: user.inventory }

2. purchaseTowerHandler - 타워 구매

📌 개요

유저가 타워를 구매할 때 골드를 차감하고 인벤토리에 해당 타워를 추가하는 함수입니다.

🔑 매개변수

  • userId: 유저의 고유 식별자.
  • payload: 클라이언트에서 전송된 데이터로, 타워 비용과 인벤토리 정보를 포함.

🚀 주요 동작

  1. 유저 정보를 getUserInfo(userId)로 불러옵니다.
  2. 유저 정보가 없으면 실패 메시지를 반환합니다.
  3. 유저의 골드가 타워 비용보다 적으면 실패 메시지를 반환합니다.
  4. 유저의 골드를 차감하고, 인벤토리에 새로운 타워를 추가합니다.
  5. updateUserGold(userId, user.gold)로 유저의 골드 정보를 갱신합니다.
  6. updateUserInventory(userId, user.inventory)로 갱신된 인벤토리를 저장합니다.

🔄 반환 값

  • 성공 시: {status: 'success', message: '타워 구매 성공'}
  • 실패 시: {status: 'fail', message: '골드가 부족합니다.'} 또는 {status: 'fail', message: '사용자 정보를 찾을 수 없습니다.'}

3. upgradeTowerHandler - 타워 업그레이드

📌 개요

유저가 보유한 타워를 업그레이드할 때 골드를 차감하고 인벤토리에 업그레이드된 타워 정보를 저장하는 함수입니다.

🔑 매개변수

  • userId: 유저의 고유 식별자.
  • payload: 클라이언트에서 전송된 데이터로, 업그레이드된 타워 인벤토리와 비용 정보를 포함.

🚀 주요 동작

  1. 유저 정보를 getUserInfo(userId)로 불러옵니다.
  2. 유저의 인벤토리를 payload.towerInven으로 갱신합니다.
  3. 유저의 골드를 업그레이드 비용만큼 차감합니다.
  4. updateUserGold(userId, user.gold)로 갱신된 골드 정보를 저장합니다.
  5. updateUserInventory(userId, user.inventory)로 갱신된 인벤토리를 저장합니다.

🔄 반환 값

  • 성공 시: {status: 'success', message: '타워 업그레이드 성공'}

📝 몬스터 핸들러 함수

📌 개요

killMonsterHandler 함수는 플레이어가 몬스터를 처치했을 때 서버와 클라이언트 간의 데이터를 처리하는 역할을 수행합니다. 이 함수는 서버에 저장된 몬스터 리스트에서 해당 몬스터를 제거하고, 유저에게 골드를 지급하는 등의 작업을 합니다.


🔑 매개변수

  • uuid: 유저의 고유 식별자.
  • payload: 클라이언트에서 전송된 데이터로, 몬스터 목록과 처치된 몬스터의 인덱스를 포함.
  • socket: 클라이언트와 서버 간 실시간 통신을 위한 소켓 객체.

🚀 주요 동작

  1. 서버의 몬스터 정보 및 유저 정보 조회

    • getMonsters(uuid)를 통해 Redis에 저장된 서버의 몬스터 목록을 불러옵니다.
    • getUserInfo(uuid)를 호출해 유저의 현재 골드를 포함한 정보를 가져옵니다.
  2. 몬스터 존재 여부 확인

    • payload.index로 받은 인덱스를 기준으로 해당 몬스터가 서버에 존재하는지 확인합니다.
    • 만약 해당 몬스터가 존재하지 않으면 처리 실패 메시지를 반환하고 함수 종료.
  3. 몬스터 제거

    • 몬스터를 리스트에서 제거하고, 해당 인덱스 자리에 0을 삽입해 갱신합니다.
  4. 골드 보상 지급

    • 처치된 몬스터가 보유한 골드를 유저의 현재 골드에 더한 후, updateUserGold(uuid, newGold)로 유저 정보를 업데이트합니다.
  5. Redis에 갱신된 몬스터 리스트 저장

    • 수정된 몬스터 리스트를 다시 Redis에 저장합니다. (setMonsters(uuid, serverMonsters))
  6. 클라이언트로 보상 이벤트 전송

    • socket.emit('getGold', { gold: newGold })로 클라이언트에 골드 획득 이벤트를 보냅니다.

🔄 반환 값

  • 성공 시: { message: '몬스터 처치' }
  • 실패 시: { status: '몬스터 처치 처리 실패' }

📝 스테이지 이동 핸들러

📌 개요

moveStageHandler 함수는 유저가 특정 스테이지에서 다음 스테이지로 이동할 때 처리하는 로직을 담당합니다. 스테이지 이동 시, 일정 시간 동안의 지연을 관리하고, 해당 스테이지에 등장할 몬스터를 생성하여 클라이언트에 전송합니다.


🔑 매개변수

  • uuid: 유저의 고유 식별자.
  • payload: 클라이언트로부터 받은 데이터로, 현재 스테이지와 이동할 스테이지 정보를 포함.
  • socket: 클라이언트와의 실시간 통신을 위한 소켓 객체.

🚀 주요 동작

  1. 유저 정보 및 스테이지 정보 확인

    • getUserInfo(uuid)를 호출해 유저 정보를 가져옵니다.
    • payload에서 현재 스테이지(currentStage)와 이동할 스테이지(targetStage) 정보를 추출하여, 값이 없는 경우 실패 메시지를 반환합니다.
  2. 스테이지 이동 시간 제한 확인

    • 현재 시간과 마지막 스테이지 이동 시간을 비교하여, 설정된 시간 지연(STAGE_MOVE_DELAY)보다 빠르게 요청이 들어올 경우 "Stage move action too fast" 메시지를 반환합니다.
  3. 스테이지 이동 처리

    • updateStage(uuid, targetStage)를 호출해 유저의 스테이지 정보를 업데이트합니다.
    • spawnMonsters(uuid)를 통해 새 스테이지에 맞는 몬스터를 생성합니다.
  4. 클라이언트로 스테이지 이동 이벤트 전송

    • 일정 지연 후(setTimeout 500ms), 소켓을 통해 클라이언트에 스테이지 이동 중 메시지와 몬스터 정보를 전송합니다.

🔄 반환 값

  • 성공 시: { status: 'success', message: '스테이지 이동 완료' }
  • 실패 시: { status: 'fail', message: 'User Not Found' }, { status: 'fail', message: 'Not Exist Stage Info' }, 또는 { status: 'fail', message: 'Stage move action too fast' }
profile
개발자로 한걸음!

0개의 댓글