Segredos da Indústria de games
Um roteiro para interessados em portar jogos para o Linux feito por um especilista
Ryan C. Gordon é programador da Loki Entertainment Software. Ele acabou de portar o editor do Heroes of Might e Magic 3 da MFC para GTK+, e quer trabalhar com algo mais simples, como o Descent 3.
Na Loki Entertainment, ganhamos a vida portando para Linux os jogos baseados no Windows. Construir um negócio apoiado totalmente em portes de programas não é um conceito novo, mesmo na indústria dos videogames. Por exemplo, eu me lembro, anos atrás, de rodar o Wolfenstein-3D em um Macintosh, graças aos esforços de porte da MacPlay.
A maioria dos portes não vem com os extras para outros sistemas. Enquanto o Quake 3 roda em MacOS, Linux e Windows,
o editor de mapas QARadiant
é executado apenas em Win32. E muitos outros títulos sofrem do mesmo problema, e não podem ser considerados produtos portados.
E há uma simples razão para isso: a indústria dos videogames é muito volátil. Uma empresa pode gastar milhões de dólares por meses na criação de um game, e este ser um fracasso. É por isso que se duplicam as boas idéias. Assim, não é por acaso que, atualmente, a maioria dos games legais sejam baseados
ou parecidos com o Quake.
Para entender a volatilidade do mercado, veja o destino da Sierra e da Origin, antes gigantes na área, agora meros fantasmas. É um círculo, e a volatilidade financeira é uma constante. Por isso, aqueles que se esforçam (como a id e Epic), não ganham nunca para portar os extras.
Alguns devem pensar que o porte de um editor de mapas é simples e barato, comparado ao porte de um game completo. Nem sempre. O Heroes of
Might e Magic 3 tem 250.000 linhas em código C++. Seu editor de mapas apenas 75.000, e tornou-se beta em 4 meses. O game levou menos de um quarto desse tempo para chegar à versão final, e o mesmo para "Fear" e "Loathing", os editores de game lançados com Myth 2.
Por que isso? Primeiro, pode-se responsabilizar a Microsoft Foundation Classes. Este API é uma barreira de incompatibilidade. Em segundo, podemos culpar as extensões do Visual C++ para a linguagem.
Entretanto, não posso culpar apenas a Microsoft, pois o GCC nem sempre funciona como esperado. A Standard Template Library (STL) e o tempo de execução do C++ tendem a mover-se vagarosamente. E apesar de não ser uma falha, o Linux e o Windows têm paradigmas muito diferentes sobre uso e desenvolvimento que precisam ser acessados pelo programador.
A MFC representa 90% dos problemas de porte. Mas os jogos geralmente não a usam, por isso, a maior parte desse porte está focalizada na criação de gráficos baseados em Linux e placas de som para integração com a base de códigos. Assim, os editores tomam mais tempo: quase sempre você terá que reescrever o programa ao se deparar com o código MFC.
É possível eliminar quase completamente essa dor de cabeça com a ajuda de bibliotecas de suporte como Twine ou MainWin, mas surgirão novos problemas para substituir os antigos. A MFC 4.2, uma versão antiga, é a última revisão de código que a Microsoft legalmente permite que se compile para as plataformas diferentes do Windows. O fonte para as revisões mais recentes da MFC vem com o Visual
Studio, da Microsoft. Sua consciência e seus advogados podem decidir se você deve tentar a sorte com estes fontes.
Questões legais à parte, não se esqueça dos usuários finais. Não bastasse os wrappers win32 serem considerados "traidores" pela comunidade Linux, ninguém quer rodar um aplicativo que pareça com um aplicativo Windows. Além disso, se quiséssemos usar os programas Windows, rodaríamos ele primeiro e evitaríamos toda essa luta. Seus usuários exigem mais de você. Não os engane.
Problemas do GCC
O Projeto GNU produziu um compilador C de altíssima qualidade. Infelizmente o C++ oferecido não é tão bom. Com código sintaticamente estável, o compilador funciona bem, mas é raro termos o controle da qualidade do código que estamos portando. O GCC exibirá mensagens de erros ocultas ao se deparar com usos inevitáveis das extensões Microsoft para C++.
Algumas dicas, ferramentas e táticas para tornar os portes menos dolorosos: consiga as ferramentas certas.
Há chances de que sua distribuição favorita do Linux venha com EGCS 1.1. Você poderá atualizar imediatamente para GCC 2.95.2. A revisão da Standard Template Library está certamente desatualizada. Quando esse artigo foi escrito, a última versão do SGI (www.sgi.com/Technology/STL/) era a 3.2. Certifique-se de usar essa versão, e especificá-la de forma explícita com a opção "-I" no GCC durante a compilação. Caso você ache que o comportamento do compilador ou da biblioteca parecem incorretos, pode ser um bug no programa GNU. Não tenha receio de obter uma versão recente do compilador, o que evita os problemas extras.
Os usuários do Slackware 7, e talvez outros, irão querer atualizar suas cópias do depurador GNU para uma versão que compreenda programas de múltiplas linhas (multithreaded). Quando escrevia esse artigo, o gdb ainda era confundido pelas classes que usam os templates e a herança múltipla, por isso, verifique novamente a versão do CVS, se for o problema.
Depois, você irá querer um editor de textos que possa destacar a sintaxe do código C++. Há diversos editores de alta qualidade para Linux que fazem isso. Eu prefiro o FTE (fte.sourceforge.net/). A razão desse destaque é dar uma dica visual para diferenciar um comentário de um código real. Neste caso, o resto do destaque da sintaxe é menos importante. Ao portar, nunca elimine uma linha de código. Insira um comentário e abaixo efetue suas alterações. Se for necessário, comente todas as funções. Você acha que ter uma referência imediata ao código original é útil, mas este texto pode ser muito confuso. O destaque de sintaxe torna tudo mais fácil.
Use o CVS
Ter um repositório de CVS, mesmo para apenas um desenvolvedor, é essencial. Determinar que não haverá alterações para o código é uma ajuda, em especial se uma tática de porte usada em um subsistema destrói outra. Efetue commit de seu trabalho com regularidade.
Visual C++ e MSDN do lado
Seu pior inimigo é também seu melhor recurso. Deixe uma caixa do Windows do lado ou use o VMware. Quando o programa se comporta mal, não há nada que ajude mais que conseguir acessar o gdb e o Visual Studio simultaneamente. E antes mesmo de o fonte tocar um filesystem ext2, seria bom ver se a base de código original compila para Win32. Não ria, aconteceu mais de uma vez. Trate de ter todas as bibliotecas e cabeçalhos necessários.
MSDN, a última documentação para Win32, ocupa 2 CD-ROMs. Caso você não a tenha, ela está disponível em msdn.microsoft.com. Sempre atente para classes e funções não familiares, pois muitas apresentam efeitos colaterais não óbvios e inesperados.
As referências do Glib, GDK e GTK+, que também são inestimáveis, apesar de incompletas, estão em www.gtk.org. Tenha o fonte dessas bibliotecas por perto para preencher as falhas caso falte documentação.
Use o Glade
O Glade (glade.pn.org) é um construtor de interface do usuário para GTK+ e Gnome. Ele armazena interfaces em um documento XML que pode ser gerado no tempo de execução com a biblioteca libglade. As alterações na interface do usuário podem ser feitas com um editor de textos e não necessitam de uma recompilação. O benefício real, é que, como ocorre com o Visual C++, pode-se criar rapidamente o UI. Apesar do Glade conseguir gerar o código C e C++, aconselha-se usar o libglade e escrever uma camada única C para C++ para seus callbacks de sinal. Lembre-se de que o
Glade e libglade são projetos diferentes de autores distintos.
Assim, assegure-se de enviar perguntas e relatos de bug aos destinatários corretos. E, como acontece com o GCC, os pacotes do Glade que acompanham sua distribuição devem estar desatualizados. Atualize-os.
Faça uso liberal das asserções
Os aplicativos GUI para qualquer plataforma esperam
a ocorrência de certos eventos e que acontecem em uma ordem específica. Caso contrário, as variáveis não são ajustadas, eventos encadeados não são ativados e o programa não se comporta como deveria, ou trava. Vá para cada classe baseada na MFC, busque os processadores de eventos (OnMouseMove, OnPaint, etc.) e preencha condições óbvias. Procure por dados que o processador usa. Por exemplo, um método OnSize da classe poderia querer alocar um buffer fora da tela que combina o novo tamanho dela, para uso posterior. No início da adoção desse método, seria aconselhável que a nova largura e altura da janela fossem superiores a zero. Cheque os detalhes que não poderiam estar errados; sem isso eles não serão detectados.
Elimine o cruft
Os símbolos, como por exemplo "BEGIN_MESSAGE_MAP" e "afx_msg" só têm sentido no ambiente Visual C++. Em vez de eliminar tais símbolos manualmente, adicione um arquivo chamado "stdafx.h" ao seu projeto e coloque alguns
#defines para converter esses símbolos em espaço em branco. A maioria dos arquivos-fonte baseados na MFC já tem esse arquivo, por isso essa é uma correção própria. Outro lixo do Visual C++ deve ser comentado, tal como a construção DECLARE_MESSAGE_MAP. Para projetos bem grandes, muito tempo seria poupado com um script Perl para processar esse trabalho maçante.
Os bitmaps não são
os mesmos no X
No Windows, há o conceito do "bitmap", um bloco de memória que contém um gráfico.
O Win32 API provê primitivos de desenhos, como linhas e retângulos, que podem ser aplicados a bitmaps, mas você também os manipula no nível de byte. Na prática, funciona bem para um sistema que não tem facilidades para exibição remota.
O X, e pela extensão GDK, separa seus gráficos em "desenhos" e "imagens". Os desenhos estão para primitivos de alto nível e as imagens para a manipulação de byte de baixo nível. Você pode desenhar um conteúdo da imagem para um desenho ou descarregar um conteúdo de desenho para uma imagem. Com essa habilidade de conversão de formatos, você pode achar que seria um meio conveniente para emular os bitmaps Win32. Não. Os desenhos são armazenados no servidor X e as imagens, por sua vez, no cliente. Para comutar entre formatos, a totalidade dos dados deve ser transferida. Dependendo do seu tamanho, pode causar atrasos consideráveis, especialmente através de conexões remotas e ssh.
A solução é escolher ou um ou outro. Caso você consiga usar apenas os desenhos, faça isso. Se precisar de manipulação de byte de baixo nível, use apenas imagens e reescreva os primitivos. As funções como gdk_draw_rectangle() são realmente muito simples para se reescrever para uma GdkImage.
Implemente as coisas pequenas e abandone as grandes
Há um design subconsciente para quase todo programa. As rotinas usadas com mais freqüência são as mais simples de se reimplementar. O objetivo é descobrir o equilíbrio entre o que você pode preencher com sucesso (CPoint, CRect) e o que é melhor substituído no fonte (objetos CDC para chamadas GDK). O mesmo acontece para o win32 API: as funções como SetTimer e GetClientRect são simples o suficiente para serem reescritas em minutos com as equivalentes do GTK+ e podem ser usadas em qualquer lugar.
O equilíbrio é importante: se você quer reimplementar todas as partes da MFC que seu programa usa, então espere que o trabalho leve algumas semanas e não dias.
Este é o único aviso básico para que você inicie. A única verdade no porte do código MFC para GTK+ é essa: não importa o quanto você está preparado, o design do programa MFC lhe dará algo que você não esperava. Apenas se lembre de que você gastará duas vezes mais tempo para o projeto do que o esperado inicialmente.