- En una escena temprana de Half-Life 2, se descubrió un extraño bug en el que una puerta no se abre y detiene el progreso
- La causa es que, al abrirse la puerta, la punta del pie del guardia que está dentro choca con la trayectoria de la puerta, lo que hace que la puerta se cierre de nuevo y se bloquee
- El mismo choque ya existía en el código original, pero el resultado cambia por la diferencia de precisión entre las operaciones de punto flotante x87 de 2004 y las operaciones SSE de 2013
- En el entorno x87, una rotación mínima empuja ligeramente el pie y resuelve la colisión, pero en SSE la rotación no alcanza y la puerta termina cerrándose
- Este caso muestra de forma ejemplar cómo la precisión de punto flotante y las diferencias del compilador pueden afectar el comportamiento real de un juego
Bug descubierto durante el proceso de portar Half-Life 2 a VR
- En 2013, durante un experimento en Valve para portar Team Fortress 2 a VR, también lograron ejecutar en VR Half-Life 2 y Portal 1, que usan el mismo motor
- Portal 1 provocaba tanto mareo en VR por la distorsión de la perspectiva que era prácticamente injugable
- Half-Life 2 funcionaba relativamente bien, y la inmersión propia de VR mejoraba escenas como el bote, apilar cajas y el combate contra manhacks
- Durante las pruebas, un desarrollador jugó todo el juego en VR y encontró un estado de progreso bloqueado en la escena inicial de la estación de tren
Análisis de la causa por la que la puerta no se abre
- En la secuencia original, el guardia (en realidad Barney) golpea la puerta, dice “entra” y, cuando el jugador entra a la sala, avanza el siguiente script
- Sin embargo, en la situación con el bug, la puerta tiembla, se bloquea y termina cerrándose por completo; el guardia sigue señalando la puerta y el jugador queda atrapado
- Incluso al recompilar el código fuente original de Half-Life 2 ocurría el mismo problema, lo que generó confusión porque parecía un bug surgido al viajar en el tiempo
Causa raíz: cambio en la forma de operar con punto flotante
- Cuando Half-Life 2 salió en 2004, usaba el conjunto de instrucciones matemáticas x87, con una estructura donde se mezclaban precisiones de 32, 64 y 80 bits
- En los builds posteriores a 2013, el conjunto de instrucciones SSE pasó a usarse por defecto, aplicando una precisión de cálculo claramente limitada a 32 o 64 bits
- En ambos entornos, la puerta y la punta del pie del guardia chocan, pero el resultado cambia por pequeñas diferencias de cálculo en el motor físico
- En x87, al producirse la colisión, el guardia rota apenas un poco y el pie se aparta de la puerta, así que la puerta se abre
- En SSE, la rotación se reduce mínimamente, el pie sigue tocando y la puerta vuelve a cerrarse y bloquearse
Corrección y solución
- Tras identificar el problema, lo resolvieron con un cambio simple: mover la posición del guardia unos 1 mm hacia atrás
- El proceso de depuración tomó bastante tiempo, incluido volver a aprender a usar herramientas antiguas
- Este caso muestra que las diferencias de precisión y los cambios en la configuración del compilador pueden alterar el comportamiento de código antiguo
Reacción de la comunidad
- Los desarrolladores coincidieron con frases como “el problema siempre es la precisión de punto flotante”
- Algunos mencionaron casos similares de cambios en el cálculo de colisiones en los remakes de Sonic 1, 2 y 3
- Junto con la lección de que “el código es el mismo, pero el compilador es distinto”, se evaluó como un caso que recuerda la dificultad de mantener código legado
- Otros desarrolladores conectaron esto con la razón por la que en juegos como Fallout 4 se diseñó para que los NPC no atravesaran puertas
- En general, muchos lo consideraron “una de las historias de bugs más interesantes”
1 comentarios
Comentarios en Hacker News
Cuando antes trabajaba en desarrollo de juegos, recuerdo que en ciertas PC fallaban assertions solo por errores de cálculo del FPU
La causa era que un software de entrada manuscrita inyectaba DLL en todos los procesos y hacía reset del modo del FPU a su valor predeterminado
Al final movimos el código de configuración del FPU de la fase de inicialización al loop de eventos, para evitar el impacto de DLL de terceros
Uno de mis objetivos es lograr que Valve use Nix
Creo que se verá más atractivo ahora que el soporte para Windows está avanzando
Si eso pasa, sería posible reproducir por completo no solo el código fuente original, sino también el toolchain y las dependencias de esa época, y probablemente sería mucho más fácil encontrar la causa raíz de bugs como este
Me recuerda al meme de “DOOR STUCK”
Video relacionado
Me pregunto si la “beta de Half-Life 2 VR” realmente se puede jugar
Si sí, no sé por qué no me enteré; si no, me pregunto por qué todavía no existe
También me gustaría muchísimo probar Portal VR. Dicen que marea bastante, pero yo soy inmune al mareo en VR, así que valdría la pena intentarlo
Tiene tanta calidad que hizo que volviera a jugar HL2 después de mucho tiempo
Cuando se cambia de x87 a SSE, es común que algunos cálculos se rompan
En TF2 pasó lo mismo: en el build de Linux se usaba SSE, así que el cálculo de la munición salía un poco distinto
Las cajas pequeñas daban +40 o +41, y eso cambiaba según el OS del servidor
Era divertido averiguar qué OS usaba cada vez que te conectabas a un servidor nuevo
Si un juego ya se rompe solo con cambiar de x87 a SSE, impresiona que la traducción de x86 a ARM funcione tan bien
Usa registros de 80 bits, así que tiene más precisión, pero se comporta de formas raras, como perder precisión cuando hace spill a memoria
Hace tiempo, al depurar un sintetizador por software, también me topé con un bug de precisión parecido
La simulación de un circuito RC tenía que acercarse a 0 para que cambiara el estado, pero en ciertos CPU los valores denormal no se flush-eaban y la condición nunca se cumplía
Al final ajusté el umbral más o menos a 0.7 y 0.01, y así terminó funcionando bien en todas las plataformas
Como meta-comentario, si hiciera un clon de Twitter, le pondría una función para banear de inmediato a quien intente bloguear en varios párrafos