Wild Witch Project - Um game que você nunca viu

Neste jogo você precisa ajudar uma bruxa adolescente a salvar seu mundo da Mortífera Bunda Assassina do Espaço Sideral

Estudando o Gamekit OGRE

http://code.google.com/p/gamekit/

Este projeto me chama atenção, pois já implementa OGRE e Lua numa tacada só.
 
Possui um grupo completo de 7 integrantes em seu trabalho e uma atividade realmente alta no desenvolvimento (até o fechamento deste post eles atualizavam o Google Code todos os dias).

Não tem muita documentação no site, mas ao baixar o source ele vem junto com toda a API de Lua.

Lua
Bastante completa, e com um modelo de programação lindíssimo.
Notei até que possui um objeto de maquina de estado já pronto ^_^.

Nos exemplos que vem no doc a coisa parece bem interessante.

Demo = BaseClass(OgreKit.Engine)
function Demo:constructor()
   self.scene = self:getActiveScene()
end
function Demo:OnUpdate(delta)
         OgreKit.DebugPrint("Demo main loop running ==> " .. self.scene:getName())
end
demo = Demo()
demo:connect(OgreKit.EVT_TICK, demo, Demo.OnUpdate)

C++
Na pasta Engine tem muitos arquivos que indicam as potencialidades da engine.
gkEngine.h mostra que ele usa uma instância de OGRESingleton.
 
E o gkSceneManager parece receber um nome como parâmetro. Apesar de eu ainda não achar na API Lua onde se pode influenciar isso, já é algo promissor.


Dá pra alterar facilmente conforme minhas necessidades?
Olhando com atenção o mecanismo de carga de arquivos, vê-se que ele é bem estruturado em várias classes.
A função loadFile na verdade é apenas um manipulador caso ocorra algum erro.
gkBlendFile *gkBlendLoader::loadFile(const gkString &fname, int options, const gkString &inResourceGroup)
{
       bool resetLoad = false;
       try {

               return loadAndCatch(fname, options, inResourceGroup);
       }

Depois o arquivo é pedido emloadAndCatch:
gkBlendFile *gkBlendLoader::loadAndCatch(const gkString &fname, int options, const gkString &inResourceGroup)
{
       m_activeFile = getFileByName(fname);
       if (m_activeFile != 0)
               return m_activeFile;


       m_activeFile = new gkBlendFile(fname, inResourceGroup);


Ela tem um mecanismo de segurança para evitar que o arquivo em execução atualmente seja recarregado. Não vejo um motivo muito sustentável para essa segurança, mas trata-se de uma engine predestinada a usuário intermediário talvez por isso.

Essa classe de carregamento é na verdade um manipulador de recursos, já que nada mais é que um gerente para uma lista de arquivos.

Indo reversamente ao código acho:
bool gkBlendFile::parse(int opts)
{

       utMemoryStream fs;
       fs.open(m_name.c_str(), utStream::SM_READ);
Esse objeto utMemoryStream tem um nome sinistro pra quem procura um stream de arquivo...
Mas o prefixo indica que esta na pasta Utils então espero que seja mesmo útil XD
class utMemoryStream : public utStream
{
public:
       utMemoryStream();
       ~utMemoryStream();

       void clear(void);

       void open(const char *path, utStream::StreamMode mode);
       void open(const utFileStream &fs, utStream::StreamMode mode);
       void open(const void *buffer, UTsize size, utStream::StreamMode mode);

       bool    isOpen(void)    const   {return m_buffer != 0;}
       bool    eof(void)       const   {return !m_buffer || m_pos >= m_size;}
       UTsize  position(void)  const   {return m_pos;}
       UTsize  size(void)      const   {return m_size;}

       UTsize read(void *dest, UTsize nr) const;
       UTsize write(const void *src, UTsize nr);


       void    seek(const UTsize pos, int dir) const;

       void            *ptr(void)          {return m_buffer;}
       const void      *ptr(void) const    {return m_buffer;}

protected:

       void reserve(UTsize nr);

       char            *m_buffer;
       mutable UTsize  m_pos;
       UTsize          m_size, m_capacity;
       int             m_mode;
};
Notem o método sobrecarregado open, a criança parece "bonbadinha". Mas aqui já parece que estou encontrando as referências a stream de arquivo clássicas, o lance é subistituilas por um manipulador de buffer (que a classe já tem).
void utFileStream::open(const char *p, utStream::StreamMode mode)
{
       if (m_handle != 0 && m_file != p)
               utFileWrapper::close(m_handle);


       m_file = p;
       m_handle= utFileWrapper::open(m_file.c_str(), mode);
       if (m_handle)
       {
               if (!(mode & SM_WRITE))
                       m_size= utFileWrapper::size(m_handle);
       }
}

Vamos ao utFileWrapper, uma função grandinha:
utFileHandle utFileWrapper::open(const char *filename, int mode)
{
#if UT_PLATFORM == UT_PLATFORM_WIN32 && defined(UT_WIN32_FILE)

       DWORD dwDesiredAccess= 0;
       if (mode & utStream::SM_READ)
               dwDesiredAccess |= GENERIC_READ;
       if (mode & utStream::SM_WRITE)
               dwDesiredAccess |= GENERIC_WRITE;

       DWORD dwShareMode= 0;
       if (mode & utStream::SM_READ)
               dwShareMode |= FILE_SHARE_READ;
       if (mode & utStream::SM_WRITE)
               dwShareMode |= FILE_SHARE_WRITE;


       DWORD dwCreationDisposition= OPEN_EXISTING;
       if (mode == utStream::SM_WRITE)
               dwCreationDisposition= CREATE_ALWAYS;

       HANDLE h= ::CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, 0,
                              dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, 0);

       return h == INVALID_HANDLE_VALUE ? 0 : h;

#else
       char fm[3] = {0,0,0};

       char *mp = &fm[0];

       if (mode & utStream::SM_READ)
               *mp++ = 'r';
       else if (mode & utStream::SM_WRITE)
               *mp++ = 'w';
       *mp++ = 'b';
       fm[2] = 0;
       return fopen(filename, fm);
#endif
}
80% dela é para o manipulador no ambiente Windows (Windows não se divide em módulos bonitinhos e fáceis de organizar) oque não deve fazer muita diferênça já que a ideia é desviar para um manipulador SQLite.

Ok, deste ponto em diante é um stream de arquivo simples. Em teoria basta trocar as chamadas de arquivo por um manipulador de campo BLOB do SQLite (sqlite3_blob_open(), sqlite3_blob_read()...)

Até aqui parece humanamente fácil, pesquizei um pouco mais para ver se não havia como diminuir a possibilidade de criar buffers de dados em excesso. E aparentemente não tem sem ter um arduo trabalho que poderia se justificar em portes para o iPhone (por exemplo a segunda geração conta com 256MB de memória apenas por tanto não se pode esperar muito!)