팀 핑·지터·손실 집계 — 가장 약한 링크가 팀을 좌우한다
팀(룸)에 모인 각 멤버가 이미 공유한 핑(RTT)·지터·손실률을 모아 멤버별 안정성 등급과 팀 전체 요약으로 환산합니다. 핵심 설계는 '팀 등급 = 가장 나쁜 멤버(최약 링크) 기준'입니다. 한타 같은 결정적 순간은 팀에서 가장 불안정한 한 명이 흔들리면 전체가 흔들리기 때문에, 평균이 아니라 최악의 멤버를 팀 대표 등급으로 삼습니다.
각 멤버의 등급은 절대 핑 수치가 아니라 '안정성' 중심으로 매겨집니다. 지터·손실이 지연보다 체감에 더 크게 작용하기 때문에, 지터(체감 시작 10ms, 러버밴딩 유발 25ms)·손실(0.5%/2.0%)·부하중 지연(버퍼블로트)·절대 지연(120ms 이상)을 각각 0~100 위험 점수로 환산하고, 그중 가장 나쁜 요인을 중심으로 종합 위험도를 산출합니다. 이 기준은 오버레이의 내 안정성 표시와 팀 집계가 완전히 동일한 분석기를 재사용하므로, '나는 안정인데 팀 표시는 다르게 나오는' 불일치가 없습니다.
결과는 '팀 네트워크 안정(N명)', '주의 — N명 주의', '불안정 — N명 위험 (최약: 이름)'처럼 상태 문장과 멤버 목록(위험도 높은 순 정렬)으로 제공됩니다. 이 집계 로직은 순수 함수로 분리되어 단위 테스트로 검증되며, 사용자에게 '지금 이렇게 하라'는 행동 지시는 하지 않고 상태만 보여줍니다.
- 최약 링크 원칙: 팀 등급 = 위험도가 가장 높은 멤버 기준
- 안정성 중심 위험 모델: 지터/손실/버퍼블로트/RTT를 각각 0~100 점수화 후 최댓값 중심 종합
- 내 안정성 표시와 팀 집계가 동일한 LolNetworkStabilityAnalyzer 재사용 → 표시 일관성
- 멤버 목록을 위험도 내림차순 정렬, 최약 멤버 이름 명시
- 순수 로직으로 분리해 단위 테스트로 검증(TeamNetworkAggregator)
- 행동 지시 없음 — 상태 표시 전용
틱 동기화 — 모든 클라이언트를 같은 프레임에 맞추기
팀 동기화 표시가 제각각 다른 순간에 갱신되면 화면이 튀어 보입니다. TeamTickAligner는 공통 기준 시각(UnixEpoch)에 맞춰 정렬된 틱을 만들어, 모든 클라이언트가 같은 틱 경계에서 갱신·콜백을 수행하도록 합니다. 기본 틱 간격은 16ms(60FPS 기준)이며 33ms(30FPS) 등으로 조정할 수 있습니다.
타이머는 '다음 정렬된 틱 시각'을 계산해 그 시점부터 일정 간격으로 발화합니다. 각 틱마다 일회성 작업과 반복 작업(N틱 간격)을 예약할 수 있어, '다음 틱에 한 번' 또는 '몇 틱마다 반복'하는 갱신을 일관된 리듬으로 처리합니다.
동시성 안전에 특히 신경 썼습니다. 잘못된 틱 간격(0 이하)은 생성 시점에 즉시 예외를 던져 0 나눗셈과 타이머 폭주를 막고(fail-fast), 틱 콜백은 락(lock) 밖에서 호출해 구독자가 무엇을 하든 동기화 락과 교차 데드락이 나지 않게 합니다. 또한 정지·스케줄 초기화가 콜백 실행 도중에 끼어들면, 세대(epoch) 카운터로 '이미 취소된 콜백'을 감지해 실행하지 않는 방식으로 해제 중 잘못된 콜백(stray callback)을 차단합니다.
- UnixEpoch 기준 정렬 틱: 모든 클라이언트가 같은 틱 경계에서 동기화
- 기본 16ms(60FPS), 33ms(30FPS) 등 틱 간격 설정 가능
- 일회성/반복(N틱 간격) 스케줄 예약 및 취소 지원
- fail-fast: 0 이하 틱 간격은 생성 시 예외로 즉시 차단
- 콜백을 락 밖에서 호출 → 재진입/교차 락 데드락 방지
- 세대(epoch) 카운터로 해제 중 stray 콜백 차단
동기화 프로토콜 — 실시간 팀 데이터를 지키는 부품들
팀 데이터가 UDP로 오가는 동안 손실·순서 뒤바뀜·변동성에 대응하기 위한 순수 로직 부품들을 갖추고 있습니다. NACK 매니저는 수신 시퀀스의 빈틈(gap)을 감지해 누락 패킷 번호를 추려내고, XOR 패리티 기반 Micro-FEC는 인접 패킷으로 소실 복구용 패리티를 만들어 재전송 없이도 일부 손실을 메꿀 여지를 둡니다.
지터 버퍼는 고정 계수 대신 네트워크 변동성(분산)에 적응합니다. 지터의 평균과 분산을 EWMA로 추정해 '평균 + 3×표준편차(약 99% 포용)'를 목표 지연으로 잡고 2~120ms 범위로 클램프합니다. 안정적일 때는 버퍼를 줄여 지연을 낮추고, 버스티할 때는 버퍼를 키워 지각 패킷을 줄이는 RFC3550류 적응 방식입니다.
그 밖에 전송 빈도 조절(손실 5% 초과 또는 지터 50ms 초과 시 전송 간격 자동 증가, 양호하면 다시 감소), 긴급/일반을 나누는 우선순위 큐, RLE 페이로드 압축, 체크섬 무결성 검사, NTP식 시계 오프셋 계산이 포함됩니다. 이들은 '상황이 나빠지면 덜 자주·더 안전하게 보내는' best-effort 대응이며, 손실이 심한 회선을 무손실로 만들어 주지는 않습니다.
- NACK 기반 gap(손실) 감지 및 누락 시퀀스 추출
- XOR 패리티 Micro-FEC로 재전송 없는 소실 복구 여지
- 적응형 지터 버퍼: EWMA 평균/분산 → 평균+3σ, 2~120ms 클램프
- 전송 빈도 자동 조절(손실>5% 또는 지터>50ms 시 간격 증가)
- 우선순위 큐(긴급/높음/보통/낮음), RLE 압축, 체크섬 무결성
- NTP식 시계 오프셋 계산으로 클라이언트 간 시각 정렬
게임 오버레이 — 게임을 건드리지 않는 별도 투명 창
오버레이는 게임 프로세스에 접근·주입·후킹하지 않습니다. 대신 별도의 투명·최상위(Topmost) WPF 창을 만들어 그 위에 HUD만 그립니다. 창에는 WS_EX_TRANSPARENT(클릭 통과) + NOACTIVATE + ToolWindow 스타일이 적용되어, 마우스·키보드 입력을 절대 가로채지 않고 게임 입력이 오버레이를 그대로 통과합니다. 안티치트 관점에서 게임을 건드리지 않는 안전 설계를 우선했습니다.
HUD 상단에는 항상 RKTUP.com 표기가 골드 톤으로 노출되고, 그 아래에 내 네트워크 안정성 칩(안정=초록/주의=주황/불안정=빨강 점과 문구), 팀(룸) 네트워크 섹션(요약 + 멤버별 색점·핑, 최대 5명), 그리고 게임 시계·스코어 행이 표시됩니다. 표시 항목은 사용자가 켠 항목과 게임이 지원하는 항목의 교집합으로 결정되며(예: LoL은 시계·스코어까지, 발로란트/CS2/기타는 네트워크 전용), 켤 항목이 하나도 없으면 빈 박스가 뜨지 않도록 창 자체를 숨깁니다.
조절 옵션도 코드에 그대로 구현되어 있습니다. 불투명도는 0.2~1.0(기본 0.7), 크기는 레이아웃 스케일 0.6~2.0 배율로 조절되며, 위치는 상단 중앙/좌상/우상/좌하/우하 5개 코너 중 선택합니다. 참고로 바론·용·전령 같은 예측 타이머는 게임 무결성 회색지대라 화면 표시(렌더링)에서 제외되었고, 게임 시계·스코어 등 '누구나 스코어보드로 보는 공개 정보'만 재표시합니다.
- 게임 프로세스 무접근/무주입/무후킹 — 별도 투명 최상위 창에 HUD만 렌더링
- WS_EX_TRANSPARENT 클릭 통과 + NOACTIVATE로 게임 입력 절대 미가로챔
- RKTUP.com 상단 표기 + 안정성/팀네트워크/시계·스코어 섹션
- 불투명도 0.2~1.0(기본 0.7), 크기 배율 0.6~2.0, 5개 코너 위치
- 표시 항목 = 사용자 설정 ∩ 게임 지원 항목, 표시 없으면 창 숨김
- 예측 타이머 제거, 공개 정보(시계/스코어)만 재표시
P2P 연결 — 스타 방식 호스트 경유와 안전한 전송
팀 데이터는 스타(star) 방식으로 호스트를 경유해 오가는 P2P 연결 위에서 전송됩니다. 연결은 ICE 후보를 우선순위 순으로 시도하며 UDP를 우선하고, 실패 시 TCP로 폴백합니다. 각 피어별로 연결 상태 머신, 패킷 재정렬 버퍼, NACK 재전송 관리자, 하트비트(5초 간격·15초 타임아웃), EWMA RTT 계산기를 붙여 연결 유지와 끊김 감지, 재연결을 처리합니다.
끊김 감지는 단계적입니다. 하트비트 타임아웃이 쌓이면 Connected→Degraded→Reconnecting으로 내려가고, 하트비트가 다시 들어오면 Connected로 복구합니다. 재연결 타임아웃이 임계(3회)를 넘으면 연결을 종료해 죽은 피어 자원을 정리합니다. 방을 나가거나 재입장할 때는 이전 방의 암호화 키·시퀀스·버퍼 등 per-peer 상태를 전부 초기화해, 같은 사용자 ID가 재배정돼도 이전 키로 복호에 실패하는 문제를 막습니다.
보안은 실사용 수준으로 구현되어 있습니다. 페이로드는 AES-256-GCM(AEAD)으로 암호화되어 기밀성과 무결성·인증을 함께 보장하고, 인증 실패(GCM 태그 불일치 = 변조·위조) 패킷은 평문으로 처리하지 않고 폐기합니다. 키는 ECDH(P-256) 교환 후 HKDF-SHA256으로 유도하며, 서버는 키 교환을 중계만 하고 실제 공유 키는 알 수 없도록 설계했습니다. 여기에 패킷 크기 제한과 전송량 레이트리밋으로 DoS·대용량 남용을 막습니다.
- 스타 방식(호스트 경유), ICE 후보 우선순위 시도 — UDP 우선/TCP 폴백
- 피어별 상태 머신·재정렬 버퍼·NACK·하트비트(5s/15s)·EWMA RTT
- 단계적 끊김 감지(Connected→Degraded→Reconnecting)와 복구, 3회 초과 시 종료
- 방 이동 시 per-peer 상태 전체 초기화로 stale 키 복호 실패 방지
- AES-256-GCM(AEAD) 페이로드 암호화, 변조 패킷 폐기
- ECDH(P-256)+HKDF-SHA256 키 유도, 서버는 키를 모름 / 패킷 크기·전송량 제한
보장되는 것: 팀 핑·지터·손실 집계와 안정성 표시, 정렬 틱 동기화, 게임을 건드리지 않는 투명·클릭통과 오버레이(투명도·크기·위치 조절, RKTUP.com 표기), AES-256-GCM 암호화 P2P 전송은 코드에 실제로 구현되어 있습니다. 조건부인 것: 지터 버퍼·FEC·전송 빈도 조절 등은 손실·변동을 줄이려는 best-effort이며 나쁜 회선을 무손실로 만들지 못하고, 오버레이는 전체화면(독점) 모드에선 보이지 않아 '테두리 없는 창모드'가 필요하며, 게임별 시계·스코어 표시는 안전한 공개 데이터 제공자가 있는 게임(현재 LoL)에서만 동작합니다. 어떤 성능도 '몇 배 빨라진다'고 보장하지 않으며 미래 핑 스파이크를 단정 예측하지 않습니다.