18. 레이드 페이즈 전환과 플레이어 입·퇴장 경합 (C#)

난이도 최상 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 레이드 페이즈 전환과 플레이어 입·퇴장 경합
// ----------------------------------------------------------------------------
// 시나리오:
//   - 레이드 인스턴스는 보스 페이즈(P1 -> P2 -> P3 ...)로 진행한다. 보스 HP 가
//     임계치 아래로 내려가면 다음 페이즈로 전환하며, 전환 시 추가 몹 소환,
//     강화(enrage) 버프, 플레이어 위치 리셋 같은 "페이즈 효과"를 모든 참가자에게
//     적용한다.
//   - 전환을 검사/실행하는 것은 보스 전투 처리 스레드다(데미지 적중마다 검사).
//   - 동시에 다른 스레드는 플레이어의 입장(지각 입장/재접속 복귀)과 퇴장(사망
//     아웃/접속종료)을 처리해 참가자 목록을 변경한다.
//   - 지각 입장한 플레이어는 "현재 페이즈" 상태로 합류해야 한다(페이즈 효과를
//     놓치면 디싱크: 안 소환된 몹/안 걸린 enrage 로 전투가 어긋난다).
//
// 요구사항:
//   - 페이즈 전환은 임계치를 한 번 통과할 때 정확히 한 번만 일어나야 한다.
//   - 전환 도중 입·퇴장이 일어나도 예외로 죽지 않고, 새 참가자는 올바른 현재
//     페이즈 상태로 동기화돼야 한다.
//   - 페이즈 효과(소환/enrage)가 누락되거나 중복 적용되면 안 된다.
//
// 과제:
//   이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 깨지는지(동시 인터리빙 포함)
//   설명하고, 수정안과 더 나은 설계를 제시하라.
//   (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================

using System;
using System.Collections.Generic;

public class Player
{
    public long Id;
    public int  SyncedPhase;     // 이 플레이어가 동기화된 페이즈
    public void ApplyPhaseEffects(int phase) { /* 소환 인지/enrage/위치리셋 적용 */ }
    public void SendPhaseState(int phase)    { /* 현재 페이즈 상태 전송 */ }
}

public class RaidInstance
{
    public long Boss_Hp;
    public int  Phase = 1;
    public readonly int[] PhaseThresholds;          // 각 페이즈 전환 HP 임계치
    public List<Player> Members = new List<Player>();

    public RaidInstance(int[] thresholds) { PhaseThresholds = thresholds; }
}

public class RaidService
{
    // 보스 전투 스레드: 데미지 적중마다 호출
    public void OnBossDamaged(RaidInstance raid, long dmg)
    {
        raid.Boss_Hp -= dmg;

        // (A) 다음 페이즈 임계치 통과 검사
        int next = raid.Phase + 1;
        if (next <= raid.PhaseThresholds.Length &&
            raid.Boss_Hp <= raid.PhaseThresholds[next - 1])
        {
            raid.Phase = next;
            StartPhase(raid, next);
        }
    }

    // 페이즈 전환: 모든 참가자에게 페이즈 효과 적용
    private void StartPhase(RaidInstance raid, int phase)
    {
        // (B) 참가자 전원에게 페이즈 효과 적용
        foreach (var m in raid.Members)
        {
            m.ApplyPhaseEffects(phase);
            m.SyncedPhase = phase;
        }
    }

    // 입장 스레드: 지각 입장/재접속 복귀
    public void OnPlayerJoin(RaidInstance raid, Player p)
    {
        raid.Members.Add(p);

        // (C) 현재 페이즈 상태로 합류
        p.SendPhaseState(raid.Phase);
        p.SyncedPhase = raid.Phase;
    }

    // 퇴장 스레드: 사망 아웃/접속종료
    public void OnPlayerLeave(RaidInstance raid, Player p)
    {
        raid.Members.Remove(p);
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.