Como o mmorpg armazena dados?


22

Eu quero usar o banco de dados sql no meu server.exe.

digamos 1000 usuários online. E os jogadores mudam seus dados quando jogam. E o servidor precisa salvar essas atualizações.

Mas como ?

Eu acho que há duas maneiras:

1) o servidor salvará no RAM em tempo de execução e, em algum momento ou a cada hora, o servidor gravará dados (atualização) no banco de dados sql a partir do RAM. Mas se a eletricidade diminuir ou, de alguma forma, o servidor desligar, as atualizações não serão salvas. claro que não quero perder atualizações.

2) o servidor salvará as atualizações no banco de dados em tempo de execução. mas o que acontecerá com a velocidade? Eu pensei em um método. eu vou te mostrar abaixo com imagem. Posso obter velocidade suficiente para 1000 jogadores online com este método? insira a descrição da imagem aqui


11
Você já fez algum benchmark? O que faz você pensar que as operações do banco de dados seriam um problema para o seu jogo?
congusbongus 27/06

3
Se o seu estado do jogo é realmente relacional? Tem certeza de que deseja um banco de dados SQL?
Pare de prejudicar Monica

6
Leia também como lidar com senhas usando hashes. Ou pelo menos notifique o usuário de que sua senha está sendo salva como texto sem formatação, para que ele esteja ciente de não usar outra senha segura.
TomTsagk 27/06

24
Como uma observação lateral, se você deseja criar um MMO comercial e está fazendo perguntas como essa, provavelmente deve começar com algo menor. Os MMOs são incrivelmente difíceis de fazer, muito menos fazê-los da maneira certa, e eles não são tão bons do ponto de vista comercial. Se isso é apenas uma coisa pessoal e você deseja aumentar o número de jogadores, comece com algo como 64 jogadores e, em seguida, suba. Será muito mais fácil do que tentar projetar a arquitetura "perfeita" do zero, sem experiência. Veja isso .
Fund Monica's Lawsuit

1
Você pode escrever uma API geral que é independente de armazenamento. Em seguida, escreva um adaptador que armazene em cache os dados na RAM para armazenamento atrasado. Ou você pode gravar o adaptador que armazena em cache os dados no arquivo temporário antes de liberá-los no SQL DB. Dessa forma, você pode ativar e desativar o cache para descobrir qual é o melhor. Você não precisa escrever muito código extra, se a sua arquitetura for construída corretamente.
0xC0DEGURU

Respostas:


37

Na maioria dos MMOs (ou projetos que usam arquitetura semelhante), trabalhei ou conheço o primeiro método: os servidores de jogos trabalham principalmente com a RAM nas máquinas que executam os processos do servidor e serializam periodicamente apenas os dados relevantes do jogo em um banco de dados SQL para arquivamento (se um for usado).

Os problemas que você observa em relação à falta de energia são válidos, mas menos prováveis ​​do que problemas como falhas no processo (a confiabilidade do tempo de atividade geralmente é uma daquelas coisas pelas quais você pagaria ao datacenter). Mas ainda. Você mitiga esses problemas por meio de ajustes no intervalo de salvamento (para que uma falha custe no máximo alguns minutos de progresso), distribuindo as filas de salvamento em várias máquinas, analisando agressivamente a quantidade de dados que precisa ser salva e implementando o salvamento reinicializável filas.

De um modo geral, o uso do próprio servidor SQL como memória principal será desagradavelmente lento (e complicado). Não vejo detalhes suficientes em sua proposta para poder dizer com certeza se funcionaria para apenas cerca de 1.000 jogadores - provavelmente seria bom. Mas não acredito que você possa torná-lo agressivamente escalável.

Além disso, isso não resolve o problema. Uma falha de energia durante o processo de salvamento ainda perderá ou corromperá os dados, a menos que você faça o trabalho para implementar o mesmo tipo de fila de salvamento reinicializável (que, mesmo assim, isso reduz severamente a probabilidade de perda de dados catastrófica, não o impede) .

Eu recomendo que você siga a primeira abordagem.


10
Em relação a "ajustar o intervalo de salvamento", o último MMO em que trabalhei teve um intervalo de salvamento com base no número de jogadores ativos. Sabíamos que ele poderia lidar com o salvamento dos dados dos X players por segundo sem gargalos perceptíveis; portanto, economizávamos um pouco menos dos X players por segundo em uma base rotativa. Geralmente, acabava economizando para cada jogador a cada dois minutos em dias agitados, e talvez duas vezes por minuto durante os tempos lentos.
Meanbits 27/06

4
"Fila de salvamento reinicializável" soa como um diário, conforme implementado por muitos sistemas de arquivos e mecanismos de banco de dados?
grawity 27/06

6
Estes problemas não são exclusivos para MMOs, ou mesmo jogos de vídeo, em tudo . Quase todo ORM existente fornece algum tipo de mecanismo de armazenamento em cache / lote. Não há necessidade de lançar sua própria solução.
BlueRaja - Danny Pflughoeft

3
Se você acabar salvando em um disco em um determinado intervalo, eu recomendaria projetar um sistema onde as coisas possam ser salvas imediatamente, para quando as pessoas tiverem uma rara queda / morte / etc. Dessa forma, se houver uma falha no servidor ou algo assim, as pessoas que realizaram algo grande não ficarão muito chateadas.
Jon

5
@ Jon, e então você se depara com o problema de as pessoas serem capazes de duplicar objetos. Idealmente, você salva todo o estado do jogo periodicamente como uma única transação. Depois de começar a salvar por partes, você termina com situações em que uma falha pode causar dados inconsistentes; jogadores que podem desencadear um acidente sob demanda ou até prever quando é provável que ocorra um acidente podem explorar isso a seu favor.
Mark

15

1000 jogadores podem ou não ser um problema. Depende de quantas vezes você precisa atualizar o banco de dados. No entanto, existe uma solução simples: coloque o banco de dados em seu próprio servidor.


Eu dei uma olhada em como o sistema de banco de dados funciona, um jogo que as pessoas chamariam de MMO-Lite - qual eu não divulgarei -, mas posso dizer que ele tem consistentemente mais de 1000 jogadores, eis o resumo:

  • Eles usam um banco de dados "No-SQL" baseado em documento para obter informações de caracteres particulares (inventário, equipamento, semelhante).
  • Eles usam um banco de dados relacional mais tradicional para informações de caráter público que raramente são atualizadas (nome, realizações, etc.) e estatísticas.

Usar um banco de dados baseado em documentos acaba sendo uma boa idéia para o desempenho nesse caso. É bom para acessar os dados, mesmo que seja ruim para fazer junções e agregações ... mas eles não farão isso com os dados que estão lá.

Por outro lado, quando eles precisam fazer algo como alterar um objeto por outro para todos, a necessidade de executar um script em segundo plano que seja caractere por caractere e os atualize um a um, leva um tempo perceptível.


Os dados do caractere existem no cliente e no servidor, e o sistema foi projetado para mantê-los sincronizados, executando as mesmas operações nos dois lados.

Agora, quando um jogador faz uma transação com o sistema - digamos, troque um item por outro via NPC ou similar - isso tem os seguintes estágios:

  • Os clientes verificam se a operação é válida
  • O cliente envia a operação para o servidor (operação assíncrona)
  • O cliente atualiza seu modelo na RAM
  • O servidor verifica se a operação é válida
  • O servidor atualiza seu modelo na RAM
  • O servidor atualiza o banco de dados (operação assíncrona)
  • O servidor informa ao cliente que a alteração ocorreu

Notas :

  • As atualizações do banco de dados, embora assíncronas, são feitas imediatamente. Se, por qualquer motivo, o servidor do jogo cair, não há muito a perder.
  • A degradação do desempenho não é grande coisa graças à colocação dos bancos de dados em seus próprios servidores.

Os negócios entre jogadores são tratados da mesma forma, com regras extras para impedir que jogadores enganem outros jogadores e rastreabilidade extra no caso de uma transação precisar ser desfeita. Ouvi dizer que existem logs especiais de todas as transações que são mantidas por algum tempo (para resolver problemas quando alguém reclama), não sei mais de como essa parte funciona.


Às vezes, algo dá errado, isso tem dois resultados possíveis:

  • Se o erro ocorrer imediatamente, o servidor informará o cliente, o que reverterá a operação (o jogador vê a operação desfeita).
  • A interface do usuário diz que o jogador possui um item, mas o servidor não concorda. Se o usuário tentar usá-lo, ele perguntará ao servidor e falhará, tornando o objeto falso inutilizável. O objeto falso desaparece quando o inventário é recarregado, o que é usado como uma oportunidade para sincronizar o modelo.

Em ambos os casos, o cliente está ciente do erro e o registrará junto com tudo o que o jogador estava fazendo. O registro do lado do cliente está acontecendo o tempo todo. Em seguida, no logout, se houver um erro, o cliente envia os logs para o servidor.


Este jogo não usa o tempo de inatividade de manutenção diária ou semanal tradicional que a maioria dos MMORPG possui. Essa manutenção agendada é frequentemente usada para redefinir contadores, temporizadores, executar procedimentos de banco de dados. Eles também são a oportunidade de trocar as versões do servidor por uma atualização.


Obrigado, mas e os movimentos dos jogadores, por exemplo, se um jogador sair da sua posição atual: x:10,y:10,z:10Para x:11,y:11,z:11, isso deve ser salvo no banco de dados imediatamente? e se o jogador continuar correndo, isso significa que salvamos sua posição atual a cada 1 segundo ou menos e, se houver milhares de jogadores, isso significa milhões de consultas ao banco de dados a cada segundo. É assim que funciona ?
dwix 27/06

2
A posição do jogador deve ser salva muito raramente no banco de dados. Você só quer isso em ocasiões especiais, como abrir o menu, iniciar uma fogueira, descansar etc. Dependendo do jogo, nem será salvo o detalhe. Alguns jogos fazem você sempre aparecer na cidade mais próxima, etc, de modo a economizar o trabalho de salvar a posição também. Mas isso passa a ser baseado em opiniões.
Nico

1
@dwix no caso do jogo de que falo, a posição do jogador nem é salva. Se você sair e se conectar, ou se o servidor cair, o jogador será colocado de volta na posição conhecida segura (edite: é uma instância privada, "casa" se você quiser, mas é o mesmo para todos). No entanto, alguns jogos mantêm a posição exata do jogador, mesmo em desconexões acidentais. Para isso, a abordagem usual é armazenar essas informações com menos frequência. Independentemente, você não faz o que o servidor está esperando no banco de dados.
Theraot

1
@dwix ao falar sobre a posição do avatar, você realmente não se importa com valores históricos, não é? Bem, se o banco de dados for um gargalo, estrangule os dados. Em vez de armazenar cada marca de servidor, armazene com menos frequência. Isso significa que o banco de dados não terá o valor mais atualizado, mas não afetará tanto o desempenho. Enquanto isso, o servidor ainda manterá o valor da atualização na RAM, e é com isso que ele funciona. Adendo: honestamente, eu aconselho a não armazenar as posições, mas alguns jogos fazem isso.
Theraot

1
@dwix see meanbits comment
Theraot

7

Ambas as abordagens são usadas com MMORPGs. Manter tudo na memória e verificar periodicamente apontá-lo para o disco parece ser a opção mais popular, pelo menos para jogos mais antigos. Ele tem a vantagem de ser bastante simples de implementar e escalar razoavelmente bem, mas torná-lo confiável depende totalmente do desenvolvedor. Os bancos de dados SQL fornecem propriedades ACID que facilitam a confiabilidade, mas geralmente são mais complicadas de implementar e podem ter problemas com o dimensionamento.

O EVE Online é um exemplo de MMO em que tudo é armazenado em um banco de dados SQL. Eu acho que chegou a algo em torno de 60.000 usuários simultâneos e teve que dedicar algum hardware caro aos servidores de banco de dados ao longo dos anos para acompanhar a carga. No entanto, o EVE armazena muito mais dados por usuário do que a maioria dos MMORPGs e possui transações muito mais frequentes e variadas. As propriedades ACID do banco de dados permitem que todo o banco de dados maciço e complicado seja sempre mantido em um estado consistente.

Por exemplo, considere o caso quando alguém entrega um item a outro jogador. O banco de dados do EVE garante que, mesmo em caso de queda ou falta de energia, o item acabe no inventário de apenas um jogador. Ou seja, a transação do banco de dados que remove o item do inventário de um jogador e o adiciona ao outro é atômica. A transação é totalmente concluída ou não acontece. Você não pode terminar em um estado onde o item não existe no inventário de nenhum jogador ou em ambos.

Um MMORPG que mantém os dados do jogador na memória e verifica periodicamente os pontos necessários para implementar essa atomicidade. Uma maneira de fazer isso seria verificar os dados de todos os jogadores ao mesmo tempo, garantindo que o novo ponto de verificação seja totalmente comprometido com o disco antes de ser considerado o ponto de verificação mais recente. O jogo também deve garantir que os dados do jogador não sejam alterados enquanto estiver sendo verificado. Com uma grande quantidade de jogadores ativos, o desafio se torna fazer tudo isso sem causar um atraso suficiente para que os jogadores notem.

Não subestime a importância da consistência. Quando você está desenvolvendo seu jogo, ele vai travar bastante. Você não precisa rastrear por que os itens estão desaparecendo e / ou duplicando, não quando o problema é um bug não relacionado que está causando o travamento do jogo em um momento ruim. Além disso, quando o jogo entra no ar, ele trava muito mais do que você espera. Seu servidor que estava funcionando perfeitamente em teste, subitamente expõe vários bugs sob carga total e coisas dos jogadores que você não esperava.

Observe que a maioria dos MMORPGs usa bancos de dados SQL para informações relacionadas à conta, mesmo que não usem os dados reais do jogo. Ainda mais importante do que manter o estado do jogo em estado consistente é manter o estado de cobrança consistente. O pior caso para o banco de dados do jogo ficar corrompido é que você precisa restaurar o banco de dados a partir de um backup diário. O pior caso para o banco de dados da conta ficar corrompido é que você vai à falência por causa de todos os estornos.

Para o seu projeto, você provavelmente pode ir de qualquer maneira. Ter 1000 usuários simultâneos provavelmente não ultrapassará os limites do que um servidor SQL pode suportar em um PC convencional hoje em dia, mas muito dependerá da natureza das transações. Se você conhece o SQL e o design de banco de dados relacional, isso pode funcionar para você. Você quer minimizar as transações o máximo possível; para algo como os pontos de vida atuais do jogador, você pode apenas salvá-los periodicamente no banco de dados, já que estatísticas como essas não precisam necessariamente ser consistentes. (No jogo PvP eles podem ...) Não armazene coisas como o monstro HP no banco de dados, na maioria dos jogos, apenas os dados relacionados aos jogadores são persistentes.

Por outro lado, se você não é um assistente de SQL, talvez considere manter todos os dados na memória muito mais simples para trabalhar de maneira confiável. Os bancos de dados SQL facilitam a consistência e a confiabilidade, mas não são automáticas. Um banco de dados mal projetado pode ter um desempenho ruim e levar a inconsistências. As transações não serão atômicas, a menos que você as faça. Se você não usar declarações parametrizadas religiosamente, você se abrirá para ataques de injeção de SQL.


0

Vários jogos armazenam as coisas de maneiras diferentes. Freqüentemente, as empresas de jogos criam alguma maneira de fazer isso, e a maioria de seus jogos usa da mesma maneira. Obviamente, diferentes estúdios costumam usar maneiras diferentes. O SQL certamente é usado para jogos, por exemplo, o CCP (EVE) possui (ou pelo menos tinha) uma rede de servidores SQL, não sei ao certo como eles fazem isso agora. Outros, basta usar muitos arquivos.

Talvez comece criando um agnóstico "mecanismo de intermediário de dados", para lidar com várias transações de dados de jogos? Como você está apenas testando de qualquer maneira, crie um mecanismo que permita que você não precise se preocupar muito com o armazenamento, do ponto de vista do próprio jogo. Ou seja, concentre-se em como, no aplicativo host, você vai lidar com isso.

Pessoalmente, acho que seria um grande trunfo se você pudesse mudar a loja real, sem ter que reescrever o jogo e o próprio corretor. Apenas mude para outro módulo com a mesma interface para o corretor falar.

O armazenamento de dados em si provavelmente não será um problema, por si só. O envio eficiente de dados, de / para essa loja, entre o host e todos os clientes, pode ser mais complicado. Envio todo o conjunto de dados do reprodutor ou, se o dividir em partes, por qual granularidade escolho particionar, etc. Qual deles consome mais recursos em qual situação, etc.

Um formato para enviar os dados pode ser XML. Dessa forma, você pode mais facilmente ser dinâmico na maneira como pode fragmentá-lo. Um caractere versus vários caracteres ou um item versus uma coleção de itens etc. Você pode "armazenar" o XML como XML (no SQL) e / ou fazer com que o SQL o distribua de maneira mais transacional a partir do XML, para como você deseja que os dados sejam realmente armazenados.

Outra maneira é o binário, que é mais eficiente em termos de frete, mas pode incorrer em mais custos em outras situações.

Com 1.000 clientes, você pode começar e armazenar facilmente 10 MB por cliente e usar apenas 10 GB de RAM efetiva + adicionar alguma RAM administrativa do sistema para gerenciar esses dados, digamos, outro GB ou dois. Você pode manter isso na RAM no host já na estrutura de dados pronta para uso. E carregue / salve dinamicamente, dependendo de quem está online, em várias frequências, dependendo da atividade, etc.

Você pode até armazenar as informações de cada cliente em um arquivo separado, e assim por diante.


0

Mantenha os jogadores * online * em memória ram e envie-os para um banco de dados (SQLite? MySQL?) Quando eles se desconectarem. se você estiver preocupado com a perda de IO durante o logout, forneça a cada logout seu próprio thread, não faça isso no thread principal / do jogo (na verdade, eu mantenho um thread de player dedicado para cada player online, isso tornou o código de rede muito mais fácil do que fazer a abordagem de rede assíncrona io)

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.