Quick Comparison
full snapshot은 client가 이전 상태를 몰라도 해석할 수 있지만 byte가 큽니다. delta snapshot은 client가 가진 baseline과 달라진 field만 보내므로 작지만, server와 client가 같은 baseline을 안다고 확인해야 합니다. packet을 하나 잃었다고 무조건 다음 delta가 해석 불가능한 것은 아니지만, 어떤 snapshot을 기준으로 압축했는지가 불명확하면 상태가 영구히 갈라질 수 있습니다.
| 전송 방식 | 장점 | 비용·위험 | 쓰기 좋은 지점 |
|---|---|---|---|
| full snapshot | 독립적으로 복구 가능 | 대역폭 큼 | 입장, reconnect, 주기적 resync |
| 이전 tick 대비 delta | 구현 단순 | 중간 packet 유실에 약함 | reliable stream 또는 짧은 상태 |
| 승인 baseline 대비 delta | 압축 효율 좋음 | client ack와 baseline 관리 필요 | 빈번한 world replication |
| event | 의미가 명확 | 유실·중복 policy 필요 | death, score, round change |
server: client가 확인한 baseline #120을 기억
-> current #126에서 달라진 field만 delta로 전송
client: #120 위에 delta 적용 후 #126을 ack
-> server: 다음 delta의 baseline을 #126으로 전진
-> ack가 끊기면 마지막 확인 baseline 또는 full snapshot으로 복구baseline은 client마다 다르다
client A가 #120을 받았고 client B가 #118까지만 받았다면, 두 client에 같은 delta bytes를 보낼 수 없습니다. server는 connection별로 마지막으로 확인된 baseline을 기억합니다. socket write가 성공했거나 UDP packet을 보냈다는 사실은 client가 state를 적용했다는 acknowledgment가 아닙니다.
baseline에는 entity의 lifecycle version과 관심 영역도 포함됩니다. A에게는 spawn된 entity가 B에게는 아직 없을 수 있으므로, world tick #126 하나만으로 같은 baseline이라고 말할 수 없습니다. client별 replication state에 known entity, last acknowledged state, pending spawn/despawn, baseline ID를 함께 둡니다.
delta header에는 current state version과 baseline ID를 넣습니다. client가 모르는 baseline을 참조하는 delta를 받으면 임의로 적용하지 않고 full resync 또는 다음 independent snapshot을 요청합니다. 잘못된 baseline에 적용한 값은 이후 delta의 입력이 되어 오류가 누적됩니다.
유실과 resync
unreliable 전송에서는 snapshot 유실이 일어납니다. 최신 delta가 마지막으로 ack된 baseline을 참조한다면, 중간 delta를 잃어도 새 delta를 적용할 수 있습니다. 반대로 바로 이전 packet을 기준으로 delta를 만들면 하나의 유실이 이후 연쇄 해석 실패로 이어집니다.
resync는 예외 처리로 미루지 않습니다. 입장·재접속·teleport·client memory reset·baseline mismatch에 필요한 full state 경로를 처음부터 만듭니다. full snapshot을 너무 자주 보내면 delta의 이점이 사라지고, 너무 드물게 보내면 state가 갈라진 client가 오래 잘못된 화면을 봅니다. mismatch 수, full resync byte, baseline age를 측정해 주기를 조정합니다.
field를 보내지 않는다는 뜻
delta에서 field가 없다는 것은 보통 "변하지 않았다"입니다. health: 0처럼 실제 값이 zero인 경우와 "field가 없다"를 구분할 encoding이 필요합니다. optional field, bit mask, changed-field list 중 무엇을 쓸지 정하고, protocol version이 다른 client가 모르는 field를 어떻게 건너뛸지도 설계합니다.
모든 field를 delta 대상으로 만들지 않습니다. name, avatar, static config처럼 spawn baseline에만 필요한 값과 tick마다 바뀌는 transform·velocity를 나누면 packet과 client state가 단순해집니다. 민감한 hidden state는 관심 영역에 들어도 보내지 않는다는 원칙도 유지합니다.
delta 압축의 기준은 "server가 마지막으로 보낸 state"가 아니라 "client가 마지막으로 확인한 state"입니다. 이 둘을 같은 값으로 가정하면 packet loss에서 복제 상태가 조용히 갈라집니다.
참고 링크
2 sources