23. 두 플레이어가 동시에 막타를 넣어 보상 귀속이 갈리는 상황
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 두 플레이어가 동시에 막타를 넣어 보상 귀속이 갈리는 상황
// ----------------------------------------------------------------------------
// 시나리오 (전투/판정 · 서버-클라):
// - 몬스터(또는 필드 보스)는 HP 를 가지며, 여러 플레이어가 동시에 공격한다.
// - 데미지 적용으로 HP 가 0 이하가 되면 그 몬스터는 죽고, "막타(killing blow)" 를
// 넣은 플레이어에게 처치 크레딧(경험치/드롭/퀘스트 카운트)을 귀속시킨다.
// - 서버는 여러 워커 스레드에서 데미지 패킷을 동시에 처리한다.
// - 두 플레이어의 공격이 거의 동시에 도착해 같은 순간 HP 를 0 이하로 만들 수 있다.
//
// 요구사항:
// - 한 몬스터의 죽음은 정확히 한 번만 처리돼야 한다(보상도 한 번만).
// - 막타 크레딧은 정확히 한 명(혹은 정책상 기여자들)에게만 귀속돼야 한다.
// - 이미 죽은 몬스터에 데미지가 들어와도 부활/재처치가 일어나선 안 된다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 보상 중복·이중 처치·음수 HP 가
// 발생하는지(동시 인터리빙 포함) 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Collections.Generic;
public class Monster
{
public long MonsterId;
public int Hp;
public bool Dead = false;
// 누적 기여도(플레이어 -> 가한 데미지) — 기여도 기반 분배용
public Dictionary<long, long> DamageBy = new();
}
public class CombatManager
{
private readonly Dictionary<long, Monster> _monsters = new();
private readonly Action<long, long> _grantKillCredit; // (playerId, monsterId)
public CombatManager(Action<long, long> grantKillCredit)
{
_grantKillCredit = grantKillCredit;
}
// 데미지 패킷 처리(여러 스레드에서 동시 호출)
public void ApplyDamage(long monsterId, long attackerId, int damage)
{
if (!_monsters.TryGetValue(monsterId, out var m)) return;
if (m.Dead) return; // 이미 죽었으면 무시
// (A) 데미지 적용 + 기여도 누적
m.Hp -= damage;
if (m.DamageBy.ContainsKey(attackerId)) m.DamageBy[attackerId] += damage;
else m.DamageBy[attackerId] = damage;
// (B) HP 가 0 이하가 되면 이 공격이 막타 → 처치 처리
if (m.Hp <= 0)
{
m.Dead = true;
_grantKillCredit(attackerId, monsterId); // 막타 크레딧 귀속
}
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 두 플레이어가 동시에 막타를 넣어 보상 귀속이 갈리는 상황
// ----------------------------------------------------------------------------
// 시나리오 (전투/판정 · 서버-클라):
// - 몬스터는 HP 를 가지며 여러 플레이어가 동시에 공격한다. 데미지로 HP 가 0 이하가
// 되면 죽고, 막타(killing blow)를 넣은 플레이어에게 처치 크레딧(경험치/드롭/퀘스트)을
// 귀속시킨다.
// - 서버는 여러 워커 스레드에서 데미지 패킷을 동시에 처리한다.
// - 두 플레이어의 공격이 거의 동시에 도착해 같은 순간 HP 를 0 이하로 만들 수 있다.
//
// 요구사항:
// - 한 몬스터의 죽음은 정확히 한 번만 처리돼야 한다(보상도 한 번만).
// - 막타 크레딧은 정확히 한 명(혹은 정책상 기여자들)에게만 귀속돼야 한다.
// - 이미 죽은 몬스터에 데미지가 들어와도 부활/재처치가 일어나선 안 된다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 보상 중복·이중 처치·데이터 경쟁이
// 발생하는지(동시 인터리빙 포함) 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <unordered_map>
#include <functional>
struct Monster {
std::int64_t monsterId = 0;
int hp = 0;
bool dead = false;
// 누적 기여도(플레이어 -> 가한 데미지)
std::unordered_map<std::int64_t, std::int64_t> damageBy;
};
class CombatManager {
public:
explicit CombatManager(std::function<void(std::int64_t, std::int64_t)> grantKillCredit)
: grantKillCredit_(std::move(grantKillCredit)) {}
// 데미지 패킷 처리(여러 스레드에서 동시 호출)
void ApplyDamage(std::int64_t monsterId, std::int64_t attackerId, int damage) {
auto it = monsters_.find(monsterId);
if (it == monsters_.end()) return;
Monster& m = it->second;
if (m.dead) return; // 이미 죽었으면 무시
// (A) 데미지 적용 + 기여도 누적
m.hp -= damage;
m.damageBy[attackerId] += damage;
// (B) HP 가 0 이하가 되면 이 공격이 막타 → 처치 처리
if (m.hp <= 0) {
m.dead = true;
grantKillCredit_(attackerId, monsterId); // 막타 크레딧 귀속
}
}
private:
std::function<void(std::int64_t, std::int64_t)> grantKillCredit_;
std::unordered_map<std::int64_t, Monster> monsters_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.