Pergunta

Estou reimplementando um antigo jogo de lama do BBS em Java com permissão dos desenvolvedores originais. Atualmente, estou usando o Java EE 6 com fachadas de sessão EJB para a lógica do jogo e JPA para a persistência. Uma grande razão pela qual escolhi feijões de sessão é o JTA.

Tenho mais experiência em aplicativos da Web, nos quais, se você obtiver uma otimistalockexception, basta pegá-lo e dizer ao usuário, seus dados são obsoletos e eles precisam reaplicar/re-submissão. Responder com "Tente novamente" o tempo todo em um jogo multiusuário seria uma experiência horrível. Dado que eu esperaria que várias pessoas visassem um único monstro durante uma luta, acho que a chance de uma otimistalockexception seria alta.

Meu código de visualização, a peça que apresenta uma CLI da Telnet, é o cliente EJB. Devo estar pegando os excepções de persistência e transactionRolledBackLocalexceptions e apenas repetindo? Como você decide quando parar?

Devo mudar para bloqueio pessimista?

A persistência após cada comando do usuário exagerar? Devo estar carregando o mundo inteiro em Ram e despejando o estado a cada dois minutos?

Faço minha fachada de sessão um Singleton EJB 3.1 que funcionaria como um ponto de estrangulamento e, portanto, eliminando a necessidade de fazer qualquer tipo de bloqueio de JPA? O EJB 3.1 Singletons funciona como um design de múltiplos leitor/escritor único (você anota os métodos como leitores e escritores).

Basicamente, qual é o melhor design e a API de persistência de Java para alterações de dados altamente concomitantes em um aplicativo em que não é aceitável apresentar os avisos reenviados/repetir para o usuário?

Foi útil?

Solução

Não posso deixar de sentir que você está supercomplicando massivamente o que deve ser um problema simples.

MMOs comerciais e bem -sucedidos geralmente adotam uma abordagem como esta:

Every few minutes or after a significant action:
    copy the player data
    pass the player data to a background thread

In the background thread:
    write each piece of player data to the database

Não há alterações de dados "altamente concorrentes" porque cada jogador salva seus próprios dados, para diferentes linhas do banco de dados. (Em alguns casos, essa é literalmente uma linha - vários jogos comerciais apenas armazenam os dados do jogador como uma bolha contra o ID do usuário.) Às vezes, os dados são um pouco obsoletos, mas isso não é importante. Será apenas um problema se o servidor travar, porque, caso contrário, você acabará por colocar o estado atual no banco de dados. Isso não é transferências de crédito entre bancos que estamos falando.

Quanto aos jogos de lama e BBS, seu algoritmo teria sido ainda mais simples:

Every few minutes or after a significant action:
    For each property in the player object:
        write a property to the player's file

Novamente, não há nenhuma disputa aqui, porque cada jogador tem seu próprio arquivo.

Persistindo depois que cada comando do usuário é realmente exagerado, a menos que seu jogo seja altamente monetizado e haja um risco de as pessoas processarem você se elas perderem o +3 Ax of Awesome devido a uma falha no servidor.

E o que mais no mundo você precisa persistir além dos jogadores? Muitas vezes, existem implicações negativas para persistir para todo o resto, então geralmente você não quer fazer isso.

Quanto ao acesso 'simultâneo' ao mesmo monstro, conforme o seu exemplo, isso não importa. Se você tem um único processo, o monstro deve estar na RAM. Seu processo único arbitra que chega ao monstro e em que ordem. Este não é um trabalho para sua camada de persistência. Lidar com a lógica do jogo no jogo e persiste os resultados.

Outras dicas

Se bem me lembro, muitas das lama clássicas realmente carregaram o mundo inteiro em Ram e despejarem o estado periodicamente. Obviamente, minhas memórias são do dia em que um tamanho do processo de 16 MB foi considerado aterrorizante.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top