Essa é a arquitetura certa para o nosso jogo para celular MMORPG?


14

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:

Diagrama de fluxo 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:

  1. 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.
  2. 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.

2
Gostaria de fazer uma avaliação do MsSQL e mexer com ele - não tenho certeza do que o MySQL fornece em termos de replicação, mas o MsSQL 2008 R2 oferece várias técnicas de replicação (5-6) para você escolher. Apenas um pensamento, não odiando o MySQL nem nada, mas a Microsoft é realmente boa em cluster.
Jonathan Dickinson

1
@ Jonathan Dickinson: Existe o MySQL Cluster: P
Coyote

1
Observe também o PostgreSQL - a versão 9 possui tecnologia de replicação, o PostgreSQL tem uma excelente reputação de longa data por lidar muito bem com cargas pesadas, muitos dos desenvolvedores parecem ser otimistas e, o melhor de tudo, é de código aberto e totalmente gratuito para todos os usos (incluindo comercial).
Randolf Richardson

o fato é que não podemos usar o mesmo banco de dados no lado do cliente e do servidor. Acho que o banco de dados mais adequado para o cliente seria o SQLite, para que ele não consuma muitos recursos, pois o aplicativo seria executado em dispositivos móveis. Enquanto a pessoa que trabalha na parte do servidor conhece apenas o MySQL, é por isso que optamos por isso. Também ouvi dizer que a maioria dos jogos MMO usa banco de dados MySQL.
'18:

1
@umair: O lado do cliente pode ser SQLite (no iPhone, por exemplo, ele já está disponível). Mas você pode optar por simplesmente armazenar apenas os dados relevantes necessários em um arquivo (o que o SQLite faz de qualquer maneira) e não se preocupar com o banco de dados do lado do cliente, como faria com um aplicativo Javascript.
Coyote

Respostas:


15

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!


3
+1 para 'HTML provavelmente sendo bom o suficiente' porque provavelmente é :).
Jonathan Dickinson

1
@ Coiote: obrigado por tirar um tempo e fornecer uma resposta tão detalhada. Gosto muito da sua idéia HTTP para fornecer suporte a outros clientes.
umair

1
Que a força esteja com você :) #
599 Coyote

5

Qual seria a melhor maneira de sincronizar bancos de dados de servidores e clientes?

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.

Um evento deve ser salvo no banco de dados local antes de atualizá-lo no servidor?

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ê?

Solicitações HTTP simples servirão ao propósito de sincronização?

Sim, desde que sejam pouco frequentes ou pequenos. O HTTP não é eficiente em largura de banda, portanto, lembre-se disso.

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).

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.

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, 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.


1
Você acertou em cheio. Você está certo sobre a sincronização de bancos de dados +1. O cliente nunca deve alterar o banco de dados por conta própria ... Basta chamar funções executadas no servidor e atualizar o banco de dados usando as lógicas do jogo e recuperar os resultados.
Coyote

@Kylotan: Obrigado por apontar flui no meu modelo
umair

@Kylotan: "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." isso significa que todos os dados são baixados toda vez que o aplicativo é iniciado. Há coisas que não mudam com muita frequência e, portanto, pode não ser aceitável baixá-las sempre. Isso também aumentará consideravelmente o tempo de inicialização do aplicativo. Alguma ideia?
umair

Se os dados forem pequenos, o problema desaparecerá. Eu ficaria surpreso se seu banco de dados centralizado de dados estáticos fosse muito grande. De qualquer forma, se você dividir os dados estáticos em partes menores, precisará enviar apenas os que foram alterados desde a última vez. Claro, você pode fazer isso por linha, se desejar manter um tempo de modificação em cada linha. Geralmente, prefiro ter dados estáticos fora do banco de dados relacional, para permitir substituí-los facilmente.
Kylotan
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.