O que distingue um MMORPG de um jogo para um só jogador é o servidor que está por trás de tudo o que vês. Nesta primeira parte do diário de desenvolvimento (devlog) do Ancient Online: Remastered, explicamos como estamos a construir um MMORPG com Unreal Engine 5.8: como o trabalho se divide entre o cliente e o servidor, como o mundo é simulado e como conseguimos editar ao vivo um mundo único e unificado.
Duas peças: cliente e servidor
O Ancient Online é composto por dois programas distintos:
- O cliente é escrito em C++ sobre Unreal Engine 5.8. A renderização, o áudio, a interface e a entrada de comandos são com ele.
- O servidor é independente do Unreal: escrevemo-lo de raiz em C++23. Todas as regras do jogo correm aqui.
Porque é que escrevemos o servidor à parte, em vez de o fazer dentro do motor? Porque aquilo de que um servidor de MMORPG precisa é diferente daquilo de que um motor de jogo precisa: não há gráficos, mas há milhares de entidades, dados persistentes, longos períodos em funcionamento e a necessidade de um desempenho previsível. Um servidor à parte permite-nos fazer apenas o trabalho necessário e controlar cada byte.
O servidor autoritativo: não confiar no cliente
A nossa regra de base é esta: o cliente afirma, o servidor decide.
- Movimento: os comandos do jogador vão para o servidor, e é o servidor que calcula a posição. O cliente mostra a posição que o servidor lhe comunica, com uma interpolação suave. Assim, um cliente que «se teletransporta» ou atravessa uma parede não vai a lado nenhum no que ao servidor diz respeito.
- Combate: resultados como o dano, os golpes certeiros e os empurrões são calculados no servidor. Por exemplo, é o servidor que decide se um golpe de combo derruba o adversário, e transmite o resultado a todos ao mesmo tempo.
- Economia: os itens, a moeda e as transações da loja só mudam no servidor e na base de dados.
O custo desta abordagem é ter de gerir bem a latência da rede; a recompensa é um mundo onde fazer batota é muito mais difícil e onde as mesmas regras se aplicam a todos.
Um ciclo do mundo a 25 Hz
O servidor atualiza o mundo 25 vezes por segundo. Em cada passo avançam a IA dos monstros, o movimento, o combate, os efeitos das habilidades e o clima. Um passo fixo mantém a simulação previsível e garante que os mesmos comandos produzem sempre o mesmo resultado.
Nem todos os jogadores precisam de saber de tudo. O servidor divide o mundo numa grelha e envia a cada jogador apenas as atualizações das entidades que estão dentro da sua área de interesse (as que estão perto). Isto mantém o tráfego de rede razoável, mesmo numa cidade cheia de gente.
Um protocolo binário leve
Não há JSON nem REST entre o cliente e o servidor; cada mensagem é um pacote binário com uma estrutura fixa. Isso poupa tanto largura de banda como tempo de CPU. Quando adicionamos um novo sistema (como o clima, o movimento ou o serviço de contas dentro do jogo), escrevemos para ele um contrato de pacotes separado e versionado, e usamos ficheiros de cabeçalho partilhados para garantir que o cliente e o servidor trabalham a partir do mesmo contrato.
Persistência: guardar sem pôr o jogo à espera
Os dados dos jogadores vivem numa base de dados PostgreSQL. Mas não é aceitável que o ciclo do mundo fique à espera da base de dados em cada passo. Por isso, a gravação é feita em segundo plano, com persistência write-behind (escrita diferida): o servidor do mundo deposita as alterações numa fila (Redis), e um processo de escrita em segundo plano grava-as na base de dados em lotes. O ritmo do jogo não depende da velocidade da base de dados.
Os canais e o servidor de login
Os jogadores ligam-se primeiro a um servidor de login, que trata da verificação da conta, da seleção de personagem e da sessão. Depois passam para um dos quatro canais de jogo (CH1–CH4). O estado em tempo real dos canais é mostrado na página Estado do servidor do site do jogo.
Um mundo unificado, editado ao vivo
O mundo do Ancient Online é concebido como um mundo único e unificado, e não como mapas separados por ecrãs de carregamento; está dividido em células e é carregado por streaming.
O conteúdo do mundo (terreno, objetos, colocação dos monstros) não é guardado no código, mas sim na base de dados, com versões. Podemos editar este mundo com um editor de mundo dentro do jogo: as alterações são feitas primeiro num rascunho, verificadas e depois publicadas. Cada publicação cria uma nova versão, e o cliente verifica se os dados do mundo que tem coincidem com os do servidor, comparando um digest. Se não coincidirem, espera que os dados sejam atualizados antes de avançar para o jogo.
Missões: Lua como linguagem de scripting
As missões e os diálogos dos NPC são escritos em Lua 5.4 no servidor. Isto permite-nos desenvolver e testar missões sem recompilar o servidor.
As mensagens dos jogadores nunca saem dos nossos servidores
Não usamos nenhum serviço de tradução externo para a tradução do chat dentro do jogo. Corremos nos nossos próprios servidores um modelo de tradução local, que só usa CPU; as mensagens dos jogadores não são enviadas para nenhuma API externa. Assim, jogadores que falam línguas diferentes conseguem entender-se sem que as suas mensagens saiam alguma vez do estúdio.
O que se segue
A segunda parte do diário de desenvolvimento aborda as decisões que tomámos para tornar o mesmo mundo jogável com rato, com ecrã tátil e com comando. Para conheceres o estúdio, lê Quem é a Astronaut Entertainment?; para o jogo em si, consulta a página Jogos.


