Lo que distingue a un MMORPG de un juego para un solo jugador es el servidor que está detrás de todo lo que ves. En esta primera parte del devlog de Ancient Online: Remastered explicamos cómo estamos construyendo un MMORPG con Unreal Engine 5.8: cómo se reparte el trabajo entre cliente y servidor, cómo se simula el mundo y cómo logramos editar en vivo un mundo único y unificado.
Dos piezas: cliente y servidor
Ancient Online está formado por dos programas separados:
- El cliente está escrito en C++ sobre Unreal Engine 5.8. El renderizado, el audio, la interfaz y la entrada del jugador son su trabajo.
- El servidor es independiente de Unreal: lo escribimos desde cero en C++23. Todas las reglas del juego se ejecutan aquí.
¿Por qué escribimos el servidor por separado y no dentro del motor? Porque lo que necesita un servidor de MMORPG es distinto de lo que necesita un motor de juegos: no hay gráficos, pero sí miles de entidades, datos persistentes, largos períodos en funcionamiento y la necesidad de un rendimiento predecible. Un servidor separado nos permite hacer solo el trabajo necesario y controlar cada byte.
El servidor autoritativo: no confiar en el cliente
Nuestra regla básica es esta: el cliente afirma, el servidor decide.
- Movimiento: la entrada del jugador va al servidor, y el servidor calcula la posición. El cliente muestra la posición que informa el servidor, con una interpolación suave. Así, un cliente que “se teletransporta” o atraviesa una pared no llega a ninguna parte en lo que al servidor respecta.
- Combate: resultados como el daño, los golpes y los empujones se calculan en el servidor. Por ejemplo, el servidor decide si un golpe de combo derriba al oponente y transmite el resultado a todos al mismo tiempo.
- Economía: los objetos, la moneda y las transacciones de tienda cambian únicamente en el servidor y en la base de datos.
El costo de este enfoque es tener que manejar bien la latencia de red; la recompensa es un mundo donde hacer trampa es mucho más difícil y las mismas reglas valen para todos.
Un bucle de mundo a 25 Hz
El servidor actualiza el mundo 25 veces por segundo. En cada paso avanzan la IA de los monstruos, el movimiento, el combate, los efectos de las habilidades y el clima. Un paso fijo mantiene la simulación predecible y asegura que la misma entrada produzca siempre el mismo resultado.
No todos los jugadores necesitan enterarse de todo. El servidor divide el mundo en una cuadrícula y le envía a cada jugador actualizaciones únicamente de las entidades que están dentro de su área de interés (las cercanas). Eso mantiene el tráfico de red en un nivel razonable incluso en una ciudad llena de gente.
Un protocolo binario ligero
Entre cliente y servidor no hay JSON ni REST; cada mensaje es un paquete binario con una estructura fija. Eso ahorra ancho de banda y tiempo de CPU. Cuando agregamos un sistema nuevo (como el clima, el movimiento o el servicio de cuentas dentro del juego), escribimos para él un contrato de paquetes separado y versionado, y usamos archivos de cabecera compartidos para garantizar que cliente y servidor trabajen con el mismo contrato.
Persistencia: guardar sin hacer esperar al juego
Los datos de los jugadores viven en una base de datos PostgreSQL. Pero no es aceptable que el bucle del mundo espere a la base de datos en cada paso. Por eso el guardado se hace en segundo plano, como persistencia write-behind: el servidor del mundo deja los cambios en una cola (Redis), y un proceso de escritura en segundo plano los confirma en la base de datos por lotes. El flujo del juego no depende de la velocidad de la base de datos.
Canales y el servidor de login
Los jugadores se conectan primero a un servidor de login, que se encarga de la verificación de la cuenta, la selección de personaje y la sesión. Después pasan a uno de los cuatro canales de juego (CH1–CH4). El estado en vivo de los canales se muestra en la página de estado del servidor del sitio del juego.
Un mundo unificado, editado en vivo
El mundo de Ancient Online está diseñado como un mundo único y unificado, en lugar de mapas separados por pantallas de carga; está dividido en celdas y se carga por streaming.
El contenido del mundo (terreno, objetos, ubicación de los monstruos) no se guarda en el código, sino en la base de datos, versionado. Podemos editar este mundo con un editor de mundo dentro del juego: los cambios se hacen primero en un borrador, se revisan y luego se publican. Cada publicación crea una versión nueva, y el cliente verifica que los datos del mundo que tiene coincidan con los del servidor comparando un digest. Si no coinciden, espera a que los datos se actualicen antes de continuar hacia el juego.
Misiones: Lua como lenguaje de scripts
Las misiones y los diálogos de los NPC se escriben en Lua 5.4 en el servidor. Eso nos permite desarrollar y probar misiones sin recompilar el servidor.
Los mensajes de los jugadores nunca salen de nuestros servidores
No usamos un servicio de traducción externo para la traducción del chat dentro del juego. Ejecutamos un modelo de traducción local, que usa solo CPU, en nuestros propios servidores; los mensajes de los jugadores no se envían a ninguna API externa. Así, los jugadores que hablan idiomas distintos pueden entenderse sin que sus mensajes salgan jamás del estudio.
Lo que sigue
La segunda parte del devlog trata las decisiones que tomamos para que el mismo mundo se pueda jugar con mouse, pantalla táctil y control. Para conocer el estudio, lee ¿Quién es Astronaut Entertainment?; para el juego en sí, consulta la página de Juegos.


