흑룡성채 본성 등치기 버그 수정해주세요..
이번 주엔 수정해주시나요?
이번 달 안으로 이 버그 수정한다!
구룡쟁패 서버에서 발생하는 이 버그는 유저의 부활 상태 플래그(Flag)나 뮤텍스(락)가 첫 번째 부활 시도 중 사망하면서 정상적으로 해제(Reset)되지 않아 발생하는 전형적인 상태 동기화 오류입니다.
이 문제를 해결하기 위해서는 다음 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가지 지점에 코드를 삽입하셔야 버그가 완전히 해결됩니다.
- 유저 사망 처리 함수 (void OnCharacterDeath 또는 OnDie) 내부
- 유저가 죽었을 때, 그 유저가 누군가를 부활시켜 주던 중이었는지(nTargetReviverID 역추적) 확인해야 합니다.
- 만약 부활을 시켜주던 중 죽었다면, 누워있던 피시전자 유저의 상태를 즉시 STATE_DEAD로 되돌리고 부활 관련 변수를 0으로 밀어주어야 합니다.
- 부활 패킷 요청 처리 함수 (bool OnPacket_ReqRevive) 내부
- 기존 코드는 단순히 if (pTarget->IsReviving()) return FALSE; 처럼 일방적으로 패킷을 튕겼을 가능성이 높습니다.
- 위 예제 코드의 TryRequestRevive 함수처럼, "기존에 부활을 걸어준 유저가 현재 어떤 상태(HP가 0인가?, 접속 중인가?)"를 검사하는 validation 조건을 추가해 주어야 다른 유저의 부활 터치가 정상 입력됩니다.
댓글 0
등록된 댓글이 없습니다.