Ressurreição de um jogo RTS morto de Dune: EmperorLauncher
(wheybags.com)- O RTS de 2001
Emperor: Battle for Dunetinha instalação, execução e jogo online instáveis no Windows moderno, e o EmperorLauncher é um patch que o devolve a um estado jogável - As melhorias principais se concentram em suporte a alta resolução, limite de 60 FPS, multiplayer online por IP direto, modo campanha cooperativa e contorno do processo de instalação quebrado
- A implementação é composta por um launcher substituto para
Emperor.exe, injeção de DLL direcionada aoGame.exe, patch de funções com Microsoft Detours, hook de renderização Direct3D 7, interceptação de winsock e um servidor WOL simplificado - O jogo online foi alterado para tunelar a estrutura antiga de P2P, portas aleatórias e NAT punching em uma única conexão cliente→servidor, de modo que apenas o host do servidor precise se preocupar com configuração de rede
- Ele também cuida de tudo da instalação à execução, incluindo cópia dos arquivos do CD original, extração de
.cab, aplicação do patch oficial v1.09, bypass do registro COM deWOLAPI.DLLe uma UI launcher em Win32
Onde Emperor: Battle for Dune emperrava
Emperor: Battle for Duneé um jogo de estratégia em tempo real criado em 2001 pela Westwood Studios e sucessor deDune 2000- Em sistemas modernos, vários problemas ainda atrapalham a jogabilidade
- Não roda em alta resolução adequada para telas atuais
- No multiplayer, a velocidade da simulação do jogo não é limitada e fica rápida demais
- O Westwood Online (WOL) não funciona mais, tornando difícil jogar multiplayer fora da LAN
- A campanha cooperativa é um recurso exclusivo do online, então não pode ser usada em LAN
- O instalador incluído no disco está quebrado
- Vários efeitos visuais quebram nas altas taxas de quadros dos PCs modernos
- O EmperorLauncher é um patch para resolver esses problemas, com download e código-fonte disponíveis
Substituição do Emperor.exe e inicialização do Game.exe
- O
Emperor.exedo jogo não é o executável real do jogo, mas um wrapper fino que inicia oGame.exe - Executar
Game.exediretamente não faz nada, então foi necessário analisar o processo de inicialização feito porEmperor.exee recriá-lo em um launcher substituto - A análise foi feita com IDA
- O IDA consegue desmontar executáveis e decompilar parte do código para algo parecido com C
- Em binários sem informações de tipos e structs, foi preciso rastrear chamadas de função e uso da API do Windows
- Antes de iniciar
Game.exe,Emperor.execria um mutex e um handle de mapeamento de arquivo anônimo, processa dados lidos deEmperor.date os copia para o mapeamento - O processo pai envia mensagens do Windows para o ID da thread principal do processo filho obtido via
CreateProcessA, repassando o valor do handle do mapeamento de arquivo- O ID de mensagem customizado usado era
0xBEEF - Os dados do mapeamento eram as três strings
"UIDATA,3DDATA,MAPS", repassadas ao código de carregamento de assets do jogo
- O ID de mensagem customizado usado era
- Em vez de reimplementar o código de descriptografia, foi colocado um utilitário de dump no lugar de
Game.exepara gravar os dados recebidos em disco, e depois o launcher repetiu a mesma sequência
Injeção de DLL e patch de funções
- Para aplicar os patches, era preciso executar código do usuário dentro do processo
Game.exe, e para isso foi usado o método CreateRemoteThread + LoadLibrary - O processo de injeção segue esta ordem
- Alocar um buffer na memória do processo de destino com
VirtualAllocEx - Copiar a string com o caminho da DLL usando
WriteProcessMemory - Passar o endereço de
LoadLibrarye o buffer do caminho da DLL paraCreateRemoteThread, carregando a DLL dentro do processo alvo - Quando o
DllMainda DLL roda, o código de patch entra em ação
- Alocar um buffer na memória do processo de destino com
- O processo foi iniciado em estado suspended e a DLL foi injetada antes da execução de
main, garantindo execução de código antecipada - Para modificar funções existentes, foi usado o Microsoft Detours
- O Detours substitui as instruções iniciais da função original por um salto que redireciona a chamada para a função substituta
- As instruções originais sobrescritas são copiadas para uma área separada de memória, e é criado um wrapper que depois salta para o restante da função original, permitindo chamá-la também
- Como as páginas de código das funções não são graváveis por segurança, foi necessário trocar as permissões com
VirtualProtecte chamarFlushInstructionCacheapós a modificação
Restauração dos logs de debug
- Havia chamadas no binário que pareciam logs de debug, mas a função de destino real era vazia e continha apenas
ret - Parece que, no build de release, várias funções vazias foram fundidas no mesmo código, e uma delas era o logger de debug
- No início, foi usada uma heurística que interpretava o primeiro argumento como ponteiro para string e verificava se apontava para caracteres ASCII imprimíveis
- Acessos incorretos a ponteiros eram capturados e ignorados por meio de exceções SEH do Windows
- Isso funcionava até certo ponto, mas ainda gerava falsos positivos e falsos negativos
- Depois, o recurso de patch do IDA e scripts Python foram usados para mover os pontos de chamada de log para uma função vazia separada
- Parte foi encontrada por heurística, e parte por padrões de
pushde constantes string seguidos de chamada - As centenas de pontos restantes foram anotadas manualmente
- Parte foi encontrada por heurística, e parte por padrões de
- Os logs restaurados ajudaram a depurar o multiplayer WOL
- Ao ver o log de assert
"MyId == INVALID_ID"durante o processamento deSC_MESSAGE_YOUR_DETAILS, foi possível confirmar em um dump do Wireshark que o comandoGAMEOPTestava sendo enviado incorretamente para todos os jogadores
- Ao ver o log de assert
Patch gráfico de Direct3D 7
- Emperor é um jogo baseado em Direct3D 7, e o suporte moderno do Windows ao Direct3D 7 não é completo
- O problema de alta resolução estava relacionado ao limite máximo de textura de 2048 na camada wrapper do Direct3D 7, e foi resolvido aproveitando o código de LegacyD3DResolutionHack de UCyborg
- O jogo não lida corretamente com telas fora da proporção 4:3
- A renderização em si funciona, mas a UI quebra como se tivesse sido ampliada demais
- O offset do cursor renderizado no jogo também sai do lugar dependendo da distância até o centro da tela
- A solução foi letterboxing em 4:3
- Rodando o jogo em modo janela, é possível usar resoluções arbitrárias
- O estilo da borda da janela é removido, e a janela do jogo é reparentada sobre uma janela preta em tela cheia
- Também foi adicionada captura do mouse para evitar que o scroll nas bordas quebre em multi-monitor ou modo janela
- O limite de framerate foi implementado com patch em
IDirect3DDevice7::EndScene, ajustando para 60 FPSEndSceneé chamado uma vez no fim de cada quadro, então é um bom ponto para calcular atraso e fazer a thread dormir- Como o ponteiro de
EndScenenão é exportado diretamente, foi necessário hookar em sequênciaDirectDrawCreateExeIDirect3D7::CreateDevicepara obter o ponteiro da função pela vtable
Multiplayer online e substituição do WOL
- O objetivo era ter multiplayer por IP direto sem infraestrutura de lobby ou hospedagem, usando apenas port forwarding e entrada manual de IP
- O modo LAN funciona, mas como encontra servidores via broadcast UDP, não serve bem para jogar pela internet
- O menu de LAN não tem opção de digitar o IP manualmente
- No começo, tentou-se adaptar o chat da LAN para especificar IP, mas isso foi abandonado ao se confirmar que a campanha cooperativa era exclusiva do WOL
- Para ressuscitar o WOL, eram necessárias duas coisas
- Um servidor mestre WOL falso para o jogo saber aonde se conectar e qual partida iniciar
- Um proxy para fazer os pacotes do jogo funcionarem sobre conexões por IP direto
- A arquitetura WOL original tinha, além do master server, um servidor “mangler”, que aparentemente coordenava NAT punching
- O servidor mangler original desapareceu, e o jogo travava esperando a resposta dele
- No patch, as chamadas ao mangler foram removidas
- Emperor usa um modelo de rede P2P e abre conexões bidirecionais entre cada par de jogadores, escolhendo portas aleatórias
- Como todos os clientes precisariam receber conexões em portas abertas, isso não combina com a internet moderna
- A solução foi interceptar funções winsock e tunelar todas as conexões em uma única conexão cliente→servidor
- As mensagens que o cliente tentaria enviar ao servidor ou a outros clientes são interceptadas, encapsuladas com um cabeçalho e enviadas pela conexão única
- Uma thread no lado do servidor recebe as mensagens e as redistribui ao destino correto
- O jogo continua acreditando que opera em P2P, mas na prática só o host do servidor precisa cuidar da configuração de rede
- Com essa configuração, foi possível iniciar e entrar em partidas de campanha cooperativa
Implementação de um servidor WOL simplificado
- O servidor mestre WOL tinha uma estrutura mais próxima de um servidor IRC
- Em
xwis.netexistia um servidor aparentemente operado por fãs e que, na época do texto, parecia ainda ter acesso à entrada DNS original do jogo,servserv.westwood.com- Emperor não funcionava perfeitamente com o xwis, mas ele serviu como referência para criação e entrada em lobbies
- A implementação pública handle_wol.cpp do pvpgn-server também foi usada como referência
- O motivo para criar um servidor próprio foi não depender da continuidade de servidores externos
- O objetivo não era operar uma comunidade competitiva, mas fornecer o mínimo necessário para fazer partidas multiplayer funcionarem
- O WOL mistura IRC padrão com comportamentos customizados
- O lobby do jogo é um canal especial
- As informações do lobby usam o topic do IRC
- O chat do lobby usa
PAGEem vez dePRIVMSG - A sincronização das configurações da partida usa mensagens
GAMEINFOcom conteúdo não ASCII
- A implementação básica do servidor WOL foi concluída por tentativa e erro e, embora não seja robusta fora do fluxo normal, funciona
Instalador e aplicação do patch v1.09
- O instalador original está quebrado, então era necessário um contorno em que o usuário copia o conteúdo do CD para o disco rígido e sobrescreve o setup por um alternativo
- O EmperorLauncher inclui uma função de instalação que copia os arquivos do CD original e extrai arquivos
.cab.cabé um formato de arquivo compactado semelhante a zip, e o Windows já fornece uma interface para extração
- Aplicar o último patch oficial, o v1.09, foi mais complicado
- Não funcionou simplesmente extrair
EM109EN.EXEcom 7zip para obter os binários atualizados - Foi encontrado, dentro dos recursos do Windows, um recurso grande no qual era possível ver o cabeçalho de um executável
- Os primeiros 4 bytes desse recurso eram o tamanho do arquivo, e o restante era o arquivo real
- Não funcionou simplesmente extrair
EM109EN.EXEextrai uma DLL embutida para um arquivo temporário, carrega essa DLL e depois executa a funçãoRTPatch32@12dentro delaRTPatchera uma ferramenta de patch binário por diff- A ferramenta myRTP foi usada como referência para carregar e executar a DLL embutida diretamente
- A DLL lia o caminho da instalação do registry, em vez de usar o caminho recebido por argumento, então foi necessário criar a chave de registry esperada para aplicar o patch
Tratamento dos Westwood Online Shared Internet Components
- A instalação original inclui, além do Emperor em si, os Westwood Online Shared Internet Components
- Sem esse componente, o WOL não funciona, e o arquivo principal é
WOLAPI.DLL WOLAPI.DLLé uma biblioteca de classes COM, e o Emperor cria os objetos COM dela comCoCreateClass- O registro COM tradicional registra CLSIDs e o caminho da DLL para todo o sistema em
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID- Esse método exige privilégios de administrador
- E afeta o sistema inteiro além do processo do jogo
- No patch, isso foi resolvido com redirecionamento de registry e
OaEnablePerUserTLibRegistration, fazendo um registro por usuário- Não foi encontrada uma forma de registrar a class library apenas no escopo de um único processo
- Tentar usar
DllGetClassObjectdiretamente não funcionou
UI do launcher e resultado final
- A etapa final foi uma UI simples de launcher para inserir IP e alterar configurações básicas
- A UI foi escrita com controles Win32 puros
- Para uma interface estática e simples, isso foi suficiente, embora a experiência de montar UI diretamente em Win32 tenha sido áspera
- No fim, o EmperorLauncher se tornou uma ferramenta que reúne execução em sistemas modernos, alta resolução, limite de 60 FPS, multiplayer por IP direto, campanha cooperativa, instalação e aplicação de patch
1 comentários
Comentários do Hacker News
Este jogo tem um peso considerável para todo o gênero de estratégia em tempo real. Normalmente, quando se fala em RTS, pensa-se naquela estrutura em que camponeses coletam recursos e você os protege, e o RTS de Dune estava bem próximo desse modelo original
Mas essa estrutura também acabou sendo assim por causa do romance original. Se não fosse isso, o gênero talvez tivesse seguido um caminho totalmente diferente. Por exemplo, a base em si poderia extrair recursos, enquanto o oponente faria pressão assediando construções, e a recompensa por controlar o mapa poderia vir em outra forma além do acesso a recursos
Dune 1 também ajudou a preparar o terreno. No começo ele é mais próximo de uma aventura point-and-click, mas mais para o fim vira um jogo com gerenciamento de recursos e mineração, produção de tropas, combate e terraformação
A parte final era completamente confusa para mim. O objetivo parecia ser tornar o planeta verde de novo, mas quando ele ficava verde não saía spice. Só que o imperador continuava exigindo mais entregas de spice, e se você não atingisse a cota era game over
Também é bem possível que eu simplesmente não tenha entendido direito porque joguei isso quando era criança. Eu precisaria revisitar, mas pelos padrões de hoje talvez não tenha envelhecido tão bem, ou talvez leve bastante tempo para terminar
Edit: eu não sabia que Dune 1 e 2 saíram no mesmo ano. Se for esse o caso, então o desenvolvimento de Dune 2 já estava em andamento, ou eles fizeram a engine e o jogo em menos de um ano. Mesmo hoje, com indies e ferramentas mais rápidas, é difícil imaginar
Foi lançado em junho de 1993, então alguns meses depois de Dune, mas se os dois estavam sendo desenvolvidos ao mesmo tempo, talvez tenha sido um caso de vários “inventores” chegando à mesma ideia. Dizem que The Settlers foi influenciado por jogos de “deus” como Populous(https://en.wikipedia.org/wiki/Populous_(video_game)). Nesse modelo, o jogador tem poderes divinos, como alterar o terreno, mas não controla diretamente as unidades
Não é algo exclusivo de Dune; eu diria que a própria história em geral funciona um pouco assim
Ótimo texto e excelente trabalho. Eu fiz algo parecido uns 10 anos atrás, mas com Tiberian Sun, e o trabalho era corrigir o código de rede
Entrar assim no código de outra pessoa dá uma sensação de conexão compartilhada. Descobri, para meu horror, que havia uma pilha totalmente separada para jogo por modem. Não era simplesmente TCP/IP passando por cima do modem
Alguém deve ter passado meses escrevendo código sob medida para enquadramento, sincronização, tratamento de erros e o que fazer ao rediscar quando a conexão caísse. E, no entanto, quando o jogo foi lançado, esse código já estava praticamente obsoleto
Nunca usei isso pessoalmente, mas muitos jogos antigos tinham essa opção
Muito bom. Esta parte do texto me chamou a atenção:
“Westwood Online (WOL) não funciona mais, então não dá para jogar multiplayer fora de LAN”
Eu gostava de Command & Conquer quando era criança e conheço um pouco do lado cliente do Westwood Online
Se bem me lembro, depois que o WOL saiu do ar, o XWIS.net deu bastante suporte. Talvez valha a pena o autor entrar em contato com a pequena comunidade de desenvolvedores de lá. Embora, a esta altura, também possa ser algo realmente em desaparecimento
O trabalho feito pelo pessoal do XWIS chegou a ser reconhecido pela EA e, pelo que me lembro, ajudou bastante a manter o suporte ao WOL em C&C Renegade
Também existe o projeto FreeRA, que é uma espécie de ancestral direto dos relançamentos recentes de C&C na Steam e em outras plataformas. Talvez eles também possam ajudar a reviver o WOL
Como o WOL era encaixado por meio de uma biblioteca própria, é bem possível que substituir a biblioteca seja muito mais fácil do que fazer engenharia reversa de toda a pilha do WOL
Edit: continuei lendo e vi que os componentes do WOL também foram corrigidos. Melhor ainda
Excelente texto. O autor parece uma pessoa tão divertida e inteligente que dá vontade de sair para tomar uma com ele à noite
Eu gostei muito das explicações expansíveis, que são fofas e úteis ao mesmo tempo. Ler o texto parecia quase jogar um RPG de aventura com escolhas, o que foi uma experiência bem diferente
E, sobre a observação “CS:GO só foi aposentado em 2023”, eu achei que CS:GO tivesse sido apenas rebatizado como CS2; estou enganado?
Ouvi relatos de que até em PCs de torneio o CS2 não conseguia manter uma taxa de quadros decente, apesar de o mesmo hardware rodar CS:GO muito bem. Também há muitos relatos de usuários compartilhando resultados parecidos até em PCs de alto desempenho
A Valve queria que o CS2 parecesse uma continuação de CS:GO, mas em vez de criar um jogo melhor e deixá-lo substituí-lo naturalmente, forçou a mudança sobre a base de jogadores. Como CS:GO era um jogo excelente, eu e outras pessoas ainda vamos ficar ressentidos por um bom tempo
É realmente divertido ver jogos antigos clássicos superando esses jogos modernos cheios de publicidade, monetização agressiva e feitos para pagar para vencer
Basta a ajuda de um único hacker para o público abandonar esses jogos lixo. Em uma mídia duradoura, parece que as boas coisas do passado acabam vencendo as coisas medíocres do presente
Excelente texto e excelente esforço. Talvez dê para integrar isso de alguma forma com o nosso trabalho no CnCNet. Seria legal aparecer por lá para conversar
“vem com um modem de 28.8 BPS”
Matriz ativa, claro. Um milhão de cores psicodélicas
Texto muito interessante e profundo. A quantidade de detalhes e conhecimento compartilhado sobre como fazer engenharia reversa e aplicar patches em jogos abandonados foi realmente ótima
Eu vi esse jogo numa loja de usados do bairro, mas como só tinha jogado Dune II RTS, deixei passar. Agora pretendo pegar com certeza
Relacionado a isso, há um jogo moderno de estratégia em tempo real de Dune na Steam
https://store.steampowered.com/app/1605220/Dune_Spice_Wars/
“Design de UI é a minha paixão”
Muito bom mesmo. Sinto falta desse tipo de escrita. Em vários aspectos, isso me lembrou posts de blog do Steve Yegge