하프라이프 2의 시간여행하는 문 버그
(mastodon.gamedev.place)- Half-Life 2의 초반 장면에서 문이 열리지 않아 진행이 멈추는 이상한 버그가 발견됨
- 원인은 문이 열릴 때 내부에 서 있는 경비병의 발끝이 문 경로와 충돌하면서 문이 다시 닫히고 잠기는 현상
- 원본 코드에서도 동일한 충돌이 존재했지만, 2004년 x87 부동소수점 연산과 2013년 SSE 연산의 정밀도 차이로 결과가 달라짐
- x87 환경에서는 미세한 회전으로 발이 살짝 밀려 충돌이 해소되지만, SSE에서는 회전량이 부족해 문이 닫히는 결과 발생
- 이 사례는 부동소수점 정밀도와 컴파일러 차이가 실제 게임 동작에 미치는 영향을 보여주는 대표적 사례
Half-Life 2 VR 포팅 과정에서 발견된 버그
- 2013년 Valve에서 Team Fortress 2를 VR로 포팅하는 실험 중, 같은 엔진을 사용하는 Half-Life 2와 Portal 1도 VR에서 작동하게 됨
- Portal 1은 시점 왜곡으로 인해 VR에서 플레이가 불가능할 정도로 어지러웠음
- Half-Life 2는 비교적 잘 작동했으며, 보트 장면과 상자 쌓기, 맨핵(manhack) 전투 등에서 VR 특유의 몰입감이 향상됨
- 테스트 중 개발자가 게임을 처음부터 끝까지 VR로 플레이하다가 초반 열차역 장면에서 진행 불가 상태를 발견
문이 열리지 않는 원인 분석
- 원래 시나리오에서는 경비병(사실은 Barney)이 문을 두드리고 “들어가라”고 말한 뒤, 플레이어가 방에 들어가면 다음 스크립트로 진행됨
- 그러나 버그 상황에서는 문이 덜컥거리다 잠기며 완전히 닫혀버림, 경비병은 계속 문을 가리키고 플레이어는 갇히는 상태
- 원본 Half-Life 2 소스코드를 다시 빌드해도 동일한 문제가 발생해, 시간을 거슬러 생긴 버그처럼 보이는 현상으로 혼란이 발생
근본 원인: 부동소수점 연산 방식의 변화
- 2004년 출시 당시 Half-Life 2는 x87 수학 명령어 세트를 사용했으며, 32·64·80비트 정밀도가 혼재된 구조였음
- 2013년 이후 빌드에서는 SSE 명령어 세트가 기본으로 사용되어, 32비트 또는 64비트로 명확히 제한된 연산 정밀도 적용
- 두 환경 모두에서 문과 경비병의 발끝이 충돌하지만, 물리엔진의 미세한 계산 차이로 결과가 달라짐
- x87에서는 충돌 시 경비병이 아주 약간 회전해 발이 문에서 벗어나 문이 열림
- SSE에서는 회전량이 미세하게 줄어 발이 여전히 닿아 문이 다시 닫히고 잠김
수정 및 해결
- 문제를 파악한 후, 경비병의 위치를 약 1mm 뒤로 이동시키는 간단한 수정으로 해결
- 디버깅 과정에서는 오래된 도구 사용법을 다시 익히는 등 상당한 시간이 소요됨
- 이 사례는 정밀도 차이와 컴파일러 설정 변화가 과거 코드의 동작을 바꿀 수 있음을 보여줌
커뮤니티 반응
- 개발자들은 “항상 문제는 부동소수점 정밀도 때문”이라며 공감
- 일부는 Sonic 1·2·3 리메이크의 충돌 계산 변화 사례를 언급하며 유사성을 지적
- “코드는 같지만 컴파일러는 다르다”는 교훈과 함께, 레거시 코드 유지의 어려움을 상기시키는 사례로 평가됨
- 다른 개발자들은 Fallout 4 등에서 NPC가 문을 통과하지 않도록 설계한 이유를 이와 같은 문제로 연결해 언급
- 전체적으로 “가장 흥미로운 버그 스토리 중 하나”라는 반응이 다수였음
Hacker News 의견
-
예전에 게임 개발을 하던 시절, FPU 계산 오류로 특정 PC에서만 어설션 실패가 발생했던 기억이 있음
원인은 필기 입력 소프트웨어가 모든 프로세스에 DLL을 주입하면서 FPU 모드를 기본값으로 리셋하는 것이었음
결국 FPU 설정 코드를 초기화 단계에서 이벤트 루프로 옮겨, 서드파티 DLL이 끼치는 영향을 회피했음- 멋진 추적이었음. 전역 FPU 상태는 정말 많은 두통거리를 만들어왔음
-
내 목표 중 하나는 Valve가 Nix를 사용하게 만드는 것임
Windows 지원이 진행 중이라 더 매력적으로 보일 거라 생각함
이렇게 되면 원본 소스뿐 아니라 당시의 툴체인과 의존성까지 완전히 재현할 수 있게 되어, 이런 버그의 근본 원인도 훨씬 쉽게 찾을 수 있을 것 같음 -
“DOOR STUCK” 밈이 떠오름
관련 영상 -
“Half-Life 2 VR 베타”가 실제로 플레이 가능한 것인지 궁금함
만약 가능하다면 왜 몰랐는지, 아니라면 왜 아직 안 되는지 의문임
Portal VR도 꼭 해보고 싶음. 멀미가 심하다고들 하지만 나는 VR 멀미에 면역이라 시도해볼 만함- 베타는 모르겠지만, 지금 바로 즐길 수 있는 훌륭한 HL2 VR 컨버전 모드가 있음
오랜만에 HL2를 다시 하게 만들 정도로 완성도가 높음 - Portal 2 VR 모드도 있음. 끝까지 플레이했는데 의외로 편안하게 즐길 수 있었음
- 베타는 모르겠지만, 지금 바로 즐길 수 있는 훌륭한 HL2 VR 컨버전 모드가 있음
-
x87에서 SSE로 전환할 때 일부 계산이 깨지는 건 흔한 일임
TF2에서도 같은 일이 있었는데, Linux 빌드에서는 SSE를 사용해 탄약 계산이 약간 달랐음- 탄약 계산에 float을 썼다는 게 놀라움
- 실제로 눈에 띄는 차이는 엔지니어의 metal 수치 정도였음
작은 상자에서 +40 또는 +41을 주는데, 서버 OS에 따라 달랐음
새 서버에 접속할 때마다 어떤 OS인지 확인하는 재미가 있었음
-
x87에서 SSE로만 바꿔도 게임이 깨지는데, x86→ARM 변환이 어떻게 그렇게 잘 되는지 신기함
- x87 FPU만이 유일하게 특이한 부동소수점 유닛임
80비트 레지스터를 써서 정밀도가 높지만, 메모리에 spill될 때 정밀도를 잃는 등 이상한 동작을 보임 - 정밀도 차이로 인해 수치가 아주 미세하게 달라지고, 그게 문 충돌 같은 문제로 이어지는 경우가 있음
예전에 소프트웨어 신시사이저를 디버깅할 때도 비슷한 정밀도 버그를 겪었음
RC 회로 시뮬레이션이 0에 근접해야 상태가 바뀌는데, 특정 CPU에서는 denormal 값이 플러시되지 않아 조건을 만족하지 못했음
결국 0.7과 0.01 정도로 대충 임계값을 조정하니 모든 플랫폼에서 잘 동작했음
- x87 FPU만이 유일하게 특이한 부동소수점 유닛임
-
메타 이야기로, 트위터 클론을 만들 건데 여러 단락으로 블로깅하면 바로 밴시키는 기능을 넣을 생각임
- 해당 스레드를 한 페이지로 펼쳐주는 링크를 공유함
- 이런 포스팅 옵션이 없다고 해서 스레드가 사라지는 건 아니라는 의견임
- 반대로, 최소 두 단락 이상 써야 하고 LLM이 내용 검증을 하는 플랫폼을 만들자는 제안도 있었음