구룡쟁패

자유게시판

흑룡성채 본성 등치기 버그 수정해주세요..

작성자 2gm 2026.07.07 18:16 74

이번 주엔 수정해주시나요?

이번 달 안으로 이 버그 수정한다!

 

구룡쟁패 서버에서 발생하는 이 버그는 유저의 부활 상태 플래그(Flag)나 뮤텍스(락)가 첫 번째 부활 시도 중 사망하면서 정상적으로 해제(Reset)되지 않아 발생하는 전형적인 상태 동기화 오류입니다.

이 문제를 해결하기 위해서는 다음 3가지 처리가 서버 소스코드에 반영되어야 합니다.

  1. 소생 시전자 사망 시 상태 초기화: 부활을 시켜주던 유저가 사망하면, 피시전자(죽어있는 유저)의 '부활 대기 상태' 플래그를 즉시 해제해야 합니다.
  2. 중복 부활 요청 방어 코드 수정: 이미 부활 진행 중이더라도 시전자가 유효하지 않다면(사망했다면) 다른 유저가 부활을 다시 시도할 수 있도록 허용해야 합니다.
  3. 타임아웃(Timeout) 예외 처리: 만약의 상황을 대비해 부활 요청 후 일정 시간(예: 30초) 동안 처리가 완료되지 않으면 상태를 강제로 초기화하는 안전장치가 필요합니다.

 

 

버그 수정 C++ 예제 코드 (서버 사이드)


#include <iostream>
#include <chrono>

// 유저의 상태 정의
enum eCharacterState {
   STATE_ALIVE,
   STATE_DEAD,
   STATE_REVIVING // 부활 시술을 받고 있는 상태
};

struct UserCharacter {
   int nUserID;
   std::string szName;
   eCharacterState eState;
   
   // [버그 수정 핵심 변수]
   int nTargetReviverID;    // 현재 나를 부활시켜주고 있는 유저의 ID (없으면 0)
   long long llReviveTimeOut; // 부활 잠금 타임아웃 (Unix Timestamp)
};

// 가상의 유저 매니저 (ID로 유저 객체를 찾는 함수 역할)
UserCharacter* GetUserByID(int nUserID);

// 현재 시간(초)을 구하는 함수
long long GetCurrentTimeSec() {
   return std::chrono::duration_cast<std::chrono::seconds>(
       std::chrono::system_clock::now().time_since_epoch()).count();
}

/**
* [기능 1] 부활 시전자가 사망하거나 취소했을 때, 피시전자의 상태를 완전히 리셋하는 함수
* 구룡쟁패 서버의 시전자 사망 처리 루틴(OnDeath) 내에 반드시 추가되어야 합니다.
*/
void CancelReviveProcess(UserCharacter* pTargetUser) {
   if (pTargetUser == nullptr) return;

   // 대기 상태를 해제하고 시전자 ID를 초기화합니다.
   if (pTargetUser->eState == STATE_REVIVING) {
       pTargetUser->eState = STATE_DEAD;
   }
   pTargetUser->nTargetReviverID = 0;
   pTargetUser->llReviveTimeOut = 0;
   
   std::cout << "[시스템] " << pTargetUser->szName << " 님의 부활 대기 상태가 초기화되었습니다. (다른 유저 부활 가능)\n";
}

/**
* [기능 2] 다른 유저가 사망한 유저에게 소생(부활)을 시도할 때 검증하는 함수
* 버그가 있던 기존의 조건문 로직을 수정한 버전입니다.
*/
bool TryRequestRevive(UserCharacter* pCaster, UserCharacter* pTargetUser) {
   if (pCaster == nullptr || pTargetUser == nullptr) return false;

   // 1. 피시전자가 살아있다면 부활 불가
   if (pTargetUser->eState == STATE_ALIVE) return false;

   // 2. [버그 수정 핵심] 이미 누군가 부활 중인 경우 예외 검증
   if (pTargetUser->eState == STATE_REVIVING) {
       
       // 검증 A: 나를 부활시켜주던 이전 유저의 객체를 가져옴
       UserCharacter* pPrevReviver = GetUserByID(pTargetUser->nTargetReviverID);
       
       // 검증 B: 이전 부활 시전자가 죽었거나 게임을 나갔는지 확인
       bool isPrevReviverInvalid = (pPrevReviver == nullptr || pPrevReviver->eState == STATE_DEAD);
       
       // 검증 C: 혹은 예기치 못한 오류로 타임아웃(30초)이 지났는지 확인
       bool isTimeOut = (GetCurrentTimeSec() > pTargetUser->llReviveTimeOut);

       if (isPrevReviverInvalid || isTimeOut) {
           // 보스 공격 등으로 이전 시전자가 죽었으므로 기존 부활 락을 강제로 깨부숩니다.
           std::cout << "[예외 처리] 이전 부활 시전자 사망 또는 타임아웃 감지. 락을 해제합니다.\n";
           CancelReviveProcess(pTargetUser);
       } else {
           // 이전 시전자가 정상적으로 살아있고 아직 시전 중이라면 중복 부활을 막음
           std::cout << "[거부] " << pTargetUser->szName << " 님은 이미 다른 유저가 부활 시술 중입니다.\n";
           return false;
       }
   }

   // 3. 부활 시도 성공 (락 설정)
   pTargetUser->eState = STATE_REVIVING;
   pTargetUser->nTargetReviverID = pCaster->nUserID;
   pTargetUser->llReviveTimeOut = GetCurrentTimeSec() + 30; // 30초 후 자동 타임아웃 안전장치

   std::cout << pCaster->szName << " 님이 " << pTargetUser->szName << " 님에게 부활 소생술을 시작합니다.\n";
   return true;

 

 

이 버그를 잡기 위해 서버에 적용해야 할 핵심 로직 안내

작성하시는 서버 소스코드 구조에 맞춰 아래 2가지 지점에 코드를 삽입하셔야 버그가 완전히 해결됩니다.

  1. 유저 사망 처리 함수 (void OnCharacterDeath 또는 OnDie) 내부
    • 유저가 죽었을 때, 그 유저가 누군가를 부활시켜 주던 중이었는지(nTargetReviverID 역추적) 확인해야 합니다.
    • 만약 부활을 시켜주던 중 죽었다면, 누워있던 피시전자 유저의 상태를 즉시 STATE_DEAD로 되돌리고 부활 관련 변수를 0으로 밀어주어야 합니다.
  2. 부활 패킷 요청 처리 함수 (bool OnPacket_ReqRevive) 내부
    • 기존 코드는 단순히 if (pTarget->IsReviving()) return FALSE; 처럼 일방적으로 패킷을 튕겼을 가능성이 높습니다.
    • 위 예제 코드의 TryRequestRevive 함수처럼, "기존에 부활을 걸어준 유저가 현재 어떤 상태(HP가 0인가?, 접속 중인가?)"를 검사하는 validation 조건을 추가해 주어야 다른 유저의 부활 터치가 정상 입력됩니다.

 

 

 

목록

댓글 0

등록된 댓글이 없습니다.