35. 지연 보상(lag compensation) 되감기의 입력 검증·동시 접근 결함 (C#)

난이도 상 해설 보기 →

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

결함 코드 · C#
// 시나리오: MMORPG 액션 전투 — 서버 권위(authoritative) 히트 판정에 지연 보상(lag compensation)을 적용한다.
// 서버는 엔티티별 최근 위치를 링버퍼에 기록하고(시뮬레이션 틱 스레드, 50ms 주기),
// 사격 패킷이 오면 사수 클라이언트가 보낸 렌더 시각 clientRenderTimeMs 로 대상을
// 과거 위치로 되감아 그 시점에서 명중 여부를 판정한다("favor the shooter").
//
// 요구사항:
//   - 사격 처리 스레드는 대상의 과거 위치를 조회해 히트박스 교차를 검사한다.
//   - 위치 스냅샷은 매 틱 시뮬레이션 스레드가 Record() 로 추가한다.
//   - clientRenderTimeMs 는 클라이언트가 보내는, 신뢰할 수 없는 입력이다.
//   - 되감기 허용 한계는 MaxRewindMs(=300ms) 로 한다.
//
// 과제: (A)(B) 지점을 검토하라. 특정 입력·타이밍에서 되감기가 엉뚱한 대상을 맞히거나
//       비정상 좌표를 읽을 수 있다. 어떤 조건에서 그렇게 되는지 밝히고 고쳐라.

public struct Vec2 { public float X, Y; }
public struct Snapshot { public ulong TimeMs; public Vec2 Pos; }

public sealed class PositionHistory
{
    public const int   N            = 32;   // 32틱 ≈ 1.6초 보관
    public const ulong TickMs       = 50;
    public const ulong MaxRewindMs  = 300;

    private readonly Snapshot[] _buf = new Snapshot[N];
    private ulong _head  = 1;   // 데모: 최소 1개 기록되어 있다고 가정
    private ulong _count = 1;

    // 시뮬레이션 틱 스레드가 매 틱 호출
    public void Record(ulong timeMs, Vec2 pos)
    {
        int slot = (int)(_head % N);
        _buf[slot].TimeMs = timeMs;
        _buf[slot].Pos    = pos;
        _head++;                            // 다음 기록 위치(단조 증가)
        if (_count < N) _count++;
    }

    // 사격 처리 스레드가 호출: 서버 현재시각 nowMs, 사수가 보낸 clientRenderTimeMs
    public Vec2 SampleAt(ulong nowMs, ulong clientRenderTimeMs)
    {
        ulong newest = _buf[(int)((_head - 1) % N)].TimeMs;
        ulong age    = newest - clientRenderTimeMs;          // (A)
        int   back   = (int)(age / TickMs);
        int   slot   = (int)((_head - 1 - (ulong)back) % N); // (B)
        return _buf[slot].Pos;
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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