Atualmente, estou tentando projetar a arquitetura de um novo jogo para celular MMORPG para minha empresa. Este jogo é semelhante ao Mafia Wars, iMobsters ou RISK. A idéia básica é preparar um exército para combater seus oponentes (usuários online).
Embora eu tenha trabalhado anteriormente em vários aplicativos móveis, isso é algo novo para mim. Depois de muita luta, criei uma arquitetura ilustrada com a ajuda de um fluxograma de alto nível:
Decidimos seguir com o modelo cliente-servidor. Haverá um banco de dados centralizado no servidor. Cada cliente terá seu próprio banco de dados local que permanecerá sincronizado com o servidor. Este banco de dados atua como um cache para armazenar coisas que não mudam frequentemente, como mapas, produtos, inventário etc.
Com esse modelo, não tenho certeza de como lidar com os seguintes problemas:
- Qual seria a melhor maneira de sincronizar bancos de dados de servidores e clientes?
- Um evento deve ser salvo no banco de dados local antes de atualizá-lo no servidor? E se o aplicativo terminar por algum motivo antes de salvar as alterações no banco de dados centralizado?
- Solicitações HTTP simples servirão ao propósito de sincronização?
- Como saber quais usuários estão conectados no momento? (Uma maneira seria manter o cliente enviando uma solicitação ao servidor após cada x minutos para notificar que está ativo. Caso contrário, considere um cliente inativo).
- As validações do lado do cliente são suficientes? Caso contrário, como reverter uma ação se o servidor não validar algo?
Não tenho certeza se esta é uma solução eficiente e como será dimensionada. Eu realmente aprecio se as pessoas que já trabalharam nesses aplicativos puderem compartilhar suas experiências, o que pode me ajudar a encontrar algo melhor. Desde já, obrigado.
Informação adicional:
O lado do cliente é implementado no mecanismo de jogos C ++ chamado marmelada. Esse é um mecanismo de jogo de plataforma cruzada, o que significa que você pode executar seu aplicativo em todos os principais SOs móveis. Certamente, podemos realizar rosqueamento, o que também é ilustrado no meu diagrama de fluxo. Estou planejando usar o MySQL para servidor e SQLite para cliente.
Este não é um jogo baseado em turnos, portanto não há muita interação com outros jogadores. O servidor fornecerá uma lista de jogadores on-line e você poderá batalhá-los clicando no botão de batalha e, após alguma animação, o resultado será anunciado.
Para sincronização de banco de dados, tenho duas soluções em mente:
- Armazene o registro de data e hora para cada registro. Além disso, acompanhe quando o banco de dados local foi atualizado pela última vez. Ao sincronizar, selecione apenas as linhas que possuem um carimbo de data / hora maior e envie para o banco de dados local. Mantenha um sinalizador isDeleted para linhas excluídas, para que cada exclusão simplesmente se comporte como uma atualização. Mas tenho sérias dúvidas sobre o desempenho, pois, para cada solicitação de sincronização, teríamos que verificar o banco de dados completo e procurar linhas atualizadas.
- Outra técnica pode ser manter um log de cada inserção ou atualização que ocorra com um usuário. Quando o aplicativo cliente solicitar sincronização, vá para esta tabela e descubra quais linhas de qual tabela foram atualizadas ou inseridas. Depois que essas linhas forem transferidas com sucesso para o cliente, remova esse log. Mas então penso no que acontece se um usuário usa outro dispositivo. De acordo com a tabela de logs, todas as atualizações foram transferidas para esse usuário, mas na verdade isso foi feito em outro dispositivo. Portanto, talvez tenhamos que acompanhar o dispositivo também. A implementação dessa técnica consome mais tempo, mas não tem certeza se ela executa a primeira.
fonte
Respostas:
Se não é um jogo em "tempo real", no sentido de que os jogadores não precisam ver o resultado imediato das ações de outro jogador na cena do jogo, você deve concordar com as solicitações HTTP. Mas lembre-se da sobrecarga do HTTP.
Dito isto, usar HTTP não impedirá que você projete seu protocolo de comunicação com cuidado. Mas se você é responsável pelo servidor e pelo lado do cliente, tem sorte, pois pode ajustar o protocolo quando necessário.
Para sincronizar entre o banco de dados mestre e o banco de dados cliente, você pode usar qualquer protocolo de transporte ao qual tenha acesso, HTTP ou outros. A parte importante é a lógica por trás da sincronização. Para uma abordagem direta, basta agrupar no servidor todas as alterações mais recentes necessárias ao cliente desde o último registro de data e hora no banco de dados do cliente. Aplique-o ao banco de dados do cliente e siga com isso. Se houver mais alterações no lado do cliente, faça o upload, se ainda for relevante, caso contrário, descarte.
Alguns jogos nem usam um banco de dados local, eles simplesmente acompanham o status, agrupando as informações relevantes do servidor quando necessário.
Se perder eventos locais não for aceitável, sim, você deve ter armazenamento local e salvá-lo o mais rápido possível. Você pode tentar fazer isso antes de cada rede enviar.
Para verificar se há usuários ativos, costumávamos fazer ping com HTTP a cada 20 segundos em um jogo de sucesso ... Esse valor subiu imediatamente, pois os servidores foram sobrecarregados :( A equipe do servidor não pensou em sucesso. uma mensagem ou algum tipo de cabeçalho especial em seu protocolo de comunicação que permitirá reconfigurar seus clientes (para balanceamento de carga de frequência de ping e outros valores relacionados à comunicação).
Validações do lado do cliente são suficientes se você não se importa de trapaceiros, hackers e outros scripts automatizados que atacam seu jogo. O servidor pode simplesmente recusar sua ação e enviar uma mensagem de erro com alguns detalhes. Você lida com essa mensagem de erro revertendo as alterações locais ou não as aplicando ao armazenamento local, se puder esperar até que o servidor responda para realmente salvar as alterações.
Usamos o padrão de comando em nosso jogo para permitir a reversão simples e eficiente de ações com falha ou inválidas do usuário. Os comandos foram executados localmente enviados aos pares ou traduzidos para a mensagem do servidor e, em seguida, aplicados nos pares ou verificados no servidor. No caso de um problema, os comandos não foram reproduzidos e a cena do jogo voltou ao estado inicial com uma notificação.
Usar HTTP não é uma má idéia, pois, mais tarde, permitirá a integração fácil com clientes Flash ou HTML5, é flexível, você pode usar qualquer tipo de linguagem de script de servidor e, com técnicas básicas de balanceamento de carga, adicionar mais servidores posteriormente sem muito esforço para dimensionar seu back-end.
Você tem muito o que fazer, mas é um trabalho divertido ... Então aproveite!
fonte
A maneira mais fácil é implementar o banco de dados como um único arquivo que você pode transferir. Se você tentar comparar as diferenças entre os bancos de dados, é um mundo doloroso, e falo por experiência própria.
Observe que seu servidor não deve confiar nas decisões que o cliente toma com base nesse banco de dados local, porque o cliente pode alterá-lo. Ele deve existir apenas para detalhes de apresentação.
Não. E se o servidor decidir que o evento nunca aconteceu? O cliente não deve tomar decisões sobre eventos de qualquer maneira, porque não é possível confiar nele.
Percebo que você também está falando sobre o banco de dados local de duas maneiras: uma, para "coisas que não mudam frequentemente" e duas, para eventos. Eles realmente não devem estar no mesmo banco de dados, pelos motivos acima - você não deseja começar a mesclar ou diferenciar linhas de dados individuais nos bancos de dados. Por exemplo, a integridade referencial se torna um problema quando um cliente tem uma referência a um item que você decide remover do servidor. Ou se o cliente altera uma linha e o servidor altera uma linha, que alteração tem precedência e por quê?
Sim, desde que sejam pouco frequentes ou pequenos. O HTTP não é eficiente em largura de banda, portanto, lembre-se disso.
Se você usa um protocolo transitório como HTTP, é uma idéia razoável. Quando o servidor recebe uma mensagem de um cliente, você pode atualizar o horário da 'última vez' para esse cliente.
Não, não mesmo. O cliente está nas mãos do inimigo. Como reverter uma ação depende inteiramente do que você considera uma ação e de quais efeitos ela pode ter. A rota mais fácil é não implementar a ação até que o servidor responda para permitir. Uma rota um pouco mais complicada é garantir que cada ação tenha um recurso de reversão para desfazer a ação e armazenar em cache todas as ações não reconhecidas no cliente. Quando o servidor os reconhecer, exclua-os do cache. Se uma ação for recusada, reverta cada ação na ordem inversa até e inclusive a recusada.
fonte