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++
// 시나리오: MMORPG 액션 전투 — 서버 권위(authoritative) 히트 판정에 지연 보상(lag compensation)을 적용한다.
// 서버는 엔티티별 최근 위치를 링버퍼에 기록하고(시뮬레이션 틱 스레드, 50ms 주기),
// 사격 패킷이 오면 사수 클라이언트가 보낸 렌더 시각 clientRenderTimeMs 로 대상을
// 과거 위치로 되감아 그 시점에서 명중 여부를 판정한다("favor the shooter").
//
// 요구사항:
// - 사격 처리 스레드는 대상의 과거 위치를 조회해 히트박스 교차를 검사한다.
// - 위치 스냅샷은 매 틱 시뮬레이션 스레드가 record() 로 추가한다.
// - clientRenderTimeMs 는 클라이언트가 보내는, 신뢰할 수 없는 입력이다.
// - 되감기 허용 한계는 maxRewindMs(=300ms) 로 한다.
//
// 과제: (A)(B) 지점을 검토하라. 특정 입력·타이밍에서 되감기가 엉뚱한 대상을 맞히거나
// 비정상 좌표를 읽을 수 있다. 어떤 조건에서 그렇게 되는지 밝히고 고쳐라.
#include <array>
#include <cstdint>
struct Vec2 { float x = 0, y = 0; };
struct Snapshot { uint64_t timeMs = 0; Vec2 pos; };
class PositionHistory {
public:
static constexpr int N = 32; // 32틱 ≈ 1.6초 보관
static constexpr uint64_t TICK_MS = 50;
static constexpr uint64_t MAX_REWIND_MS = 300;
// 시뮬레이션 틱 스레드가 매 틱 호출
void record(uint64_t timeMs, Vec2 pos) {
int slot = (int)(head_ % N);
buf_[slot].timeMs = timeMs;
buf_[slot].pos = pos;
++head_; // 다음 기록 위치(단조 증가)
if (count_ < N) ++count_;
}
// 사격 처리 스레드가 호출: 서버 현재시각 nowMs, 사수가 보낸 clientRenderTimeMs
Vec2 sampleAt(uint64_t nowMs, uint64_t clientRenderTimeMs) const {
uint64_t newest = buf_[(int)((head_ - 1) % N)].timeMs;
uint64_t age = newest - clientRenderTimeMs; // (A)
int back = (int)(age / TICK_MS);
int slot = (int)((head_ - 1 - back) % N); // (B)
return buf_[slot].pos;
}
private:
std::array<Snapshot, N> buf_{};
uint64_t head_ = 1; // 데모: 최소 1개 기록되어 있다고 가정
uint64_t count_ = 1;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.