Eu tenho um jogo online no qual os jogadores podem moldar o mundo de alguma forma - por exemplo. Habitação da Ultima Online, onde você constrói suas casas diretamente em certas partes do mapa do mundo. Essas são mudanças que devem persistir ao longo do tempo como parte do mundo persistente.
Ao mesmo tempo, a equipe de design está adicionando novo conteúdo e alterando o conteúdo antigo para melhorar e estender o jogo para novos jogadores. Eles farão isso em um servidor de desenvolvimento primeiro durante o teste e depois terão que mesclar seu trabalho com o "trabalho" dos jogadores no servidor ativo.
Assumindo que corrigimos os problemas de design do jogo - por exemplo, os jogadores só podem construir em áreas designadas, para que nunca entrem em conflito geográfico com as edições do designer - quais são as boas maneiras de manipular os dados ou organizar as estruturas de dados para evitar conflitos quando novos dados do designer são mesclados com os dados do novo jogador?
Exemplo 1: um jogador cria um novo tipo de item e o jogo atribui o ID 123456 a ele. Todas as instâncias desse item se referem a 123456. Agora imagine que os designers de jogos tenham um sistema semelhante, e um designer crie um novo item também numerado como 123456. Como isso pode ser evitado?
Exemplo 2: alguém faz um mod popular que dá a todos os seus dragões um sotaque francês. Ele inclui um script com um novo objeto chamado assignFrenchAccent
que eles usam para atribuir os novos recursos de voz a cada objeto dragão. Mas você está prestes a implantar seu DLC "Napoleon vs Smaug", que tem um objeto com o mesmo nome - como você pode fazer isso sem muitos problemas de atendimento ao cliente?
Pensei nas seguintes estratégias:
- Você pode usar 2 arquivos / diretórios / bancos de dados separados, mas suas operações de leitura são significativamente complicadas. "Mostrar todos os itens" deve executar uma leitura no banco de dados do designer e uma leitura no banco de dados do player (e ainda precisa distinguir entre os 2, de alguma forma).
- Você pode usar 2 namespaces diferentes em uma loja, por exemplo. usando strings como chave primária e prefixando-as com "DESIGN:" ou "PLAYER:", mas a criação desses espaços para nome pode não ser trivial e as dependências não são claras. (por exemplo. Em um RDBMS, você pode não conseguir usar seqüências de caracteres de forma eficiente como chaves primárias. Você pode usar números inteiros e alocar todas as chaves primárias abaixo de um determinado número, por exemplo, 1 milhão, para serem dados de designer e tudo acima desse ponto. Mas essas informações são invisíveis para o RDBMS e os links de chave estrangeira cruzam a 'divisão', o que significa que todas as ferramentas e scripts precisam trabalhar explicitamente com isso.)
- Você sempre pode trabalhar no mesmo banco de dados compartilhado em tempo real, mas o desempenho pode ser ruim e o risco de danos aos dados do player pode ser aumentado. Também não se estende a jogos executados em mais de um servidor com dados mundiais diferentes.
- ... outras idéias?
Ocorre-me que, embora esse seja um problema principalmente para jogos online, os conceitos também podem se aplicar à modificação, onde a comunidade cria mods ao mesmo tempo em que os desenvolvedores corrigem o jogo. Existem estratégias usadas aqui para reduzir a chance de quebra de mod quando novos patches são lançados?
Também marquei isso como "controle de versão" porque, em um nível, é isso: dois ramos do desenvolvimento de dados que precisam ser mesclados. Talvez algumas idéias possam vir dessa direção.
EDIT - alguns exemplos adicionados acima para ajudar a esclarecer o problema. Estou começando a pensar que o problema é realmente um namespacing, que pode ser implementado em uma loja por meio de chaves compostas. Isso simplifica a estratégia de mesclagem, pelo menos. Mas pode haver alternativas que não estou vendo.
fonte
Respostas:
Acho que as respostas que propõem soluções de banco de dados estão saltando para uma implementação específica sem entender o problema. Os bancos de dados não facilitam as mesclagens, apenas fornecem uma estrutura para armazenar seus dados. Um conflito ainda é um conflito, mesmo que esteja em um banco de dados. E fazer check-out é a solução de um homem pobre para o problema - ele funcionará, mas com um custo paralisante para a sua usabilidade.
O que você está falando aqui se enquadra no modelo de desenvolvimento distribuído de problemas. O primeiro passo que acredito não é pensar em jogadores e designers como sendo tipos separados de criadores de conteúdo. Isso remove uma dimensão artificial do seu problema que não afeta a solução.
Efetivamente, você tem sua linha principal - a versão canônica aprovada pelo desenvolvedor. Você pode (provavelmente) também ter outras filiais - servidores ativos onde as pessoas estão ativamente desenvolvendo e compartilhando mods. O conteúdo pode ser adicionado em qualquer filial. Fundamentalmente, seus designers não são nada de especial aqui - eles são apenas criadores de conteúdo que moram internamente (e você pode encontrá-los e atingi-los quando estragam).
Aceitar o conteúdo gerado pelo usuário é um problema de mesclagem padrão. Você deve puxar as alterações de volta para a linha principal, mesclar e empurrar novamente ou puxar as alterações da linha principal para sua ramificação e mesclagem (deixando a linha principal 'limpa' das coisas geradas pelo usuário). Como sempre, puxar para o seu ramo e consertá-lo é mais amigável do que pedir a outras pessoas que façam suas alterações e depois tentar consertá-las remotamente.
Depois de trabalhar com esse tipo de modelo, todos os processos normais sobre como evitar conflitos de mesclagem se aplicam. Alguns dos mais óbvios:
fonte
Armazene tudo como um atributo (ou decorador) - com pontos de montagem. Vamos dar uma casa que o jogador projetou como exemplo:
Portanto, cada entidade pode ter um ou mais pontos de montagem - cada ponto de montagem pode aceitar zero ou mais outros componentes. Esses dados seriam armazenados com a versão em que foram salvos, juntamente com as propriedades relevantes (como Deslocamento etc. no meu exemplo) - o NoSQL provavelmente se encaixaria muito bem aqui (Chave = ID da entidade, Valor = Binário serializado Dados).
Cada componente precisaria ser capaz de 'atualizar' os dados antigos de uma versão anterior (nunca remova os campos dos dados serializados - apenas os 'nula') - essa atualização ocorre no minuto em que é carregada (seria então imediatamente armazenada novamente em a versão mais recente disponível). Digamos que nossa casa teve suas dimensões alteradas. O código de atualização determinaria relativamente a distância entre as paredes norte e sul e alteraria proporcionalmente os deslocamentos de todas as entidades contidas. Como outro exemplo, nossa tigela de carne pode ter o campo 'Comida' removido e, em vez disso, obter uma 'Variedade' (Carne) e 'Receita' (Bolas). O script de atualização transformaria 'Meat Balls' em 'Meat', 'Balls'. Cada componente também deve saber como lidar com alterações nos pontos de montagem - por exemplo,
Isso tudo deixa exatamente um problema em aberto: o que acontece se dois objetos se chocam entre si (e não o contêiner - os pontos de montagem protegem você disso)? Após uma atualização, verifique as colisões e tente resolvê-las (separando as coisas, um pouco como o SAT). Se você não conseguir descobrir como resolver a colisão, remova um dos objetos e coloque-o em um esconderijo - onde eles podem comprar esses itens removidos (de graça) ou vendê-los (a preço total); e, obviamente, notifique o jogador que a atualização quebrou parte de seu layout - possivelmente com um recurso de 'ampliar' para que eles possam ver o problema.
Por fim, você deve deixar alterações complexas nas mãos dos jogadores (falha rápida), pois nenhum algoritmo pode explicar a estética - você deve apenas ser capaz de fornecer ao jogador o contexto de onde o item costumava estar (para que eles se lembrem, não apenas aterrar com todos esses itens em seu estoque e não saber onde eles estavam).
fonte
Estou tentando associar isso a algo que entendo, então estou pensando em termos de Minecraft agora. Estou imaginando um servidor ativo com jogadores fazendo alterações em tempo real enquanto os desenvolvedores executam em um servidor de teste corrigindo / criando novos conteúdos.
Sua pergunta parece quase duas perguntas únicas:
Eu tentaria resolver # 1 através de um sistema de referência temporário. Por exemplo, quando um novo objeto é criado por alguém, ele pode ser marcado como volátil ou temporário. Eu imaginaria que todo o novo conteúdo criado no servidor de teste seria marcado como volátil (embora também possa fazer referência a conteúdo não volátil).
Quando você estiver pronto para trazer novo conteúdo para o servidor ativo, seu processo de importação localizará os objetos voláteis e atribuirá a eles os IDs de objetos do servidor ativo definidos como pedra. Isso é diferente de uma importação / mesclagem direta, porque você precisa fazer referência a objetos não voláteis existentes, caso precise corrigi-los ou atualizá-los.
Para o número 2, parece que você realmente precisa ter algum nível de transmutação de script intermediária que possa fazer o hash do nome da função em um espaço de nome exclusivo. ie
Torna-se
fonte
Se os arquivos para os dados forem texto, em vez de binários, e os designers e players estiverem modificando áreas diferentes, você poderá tentar uma mesclagem SVN.
fonte
Eu acho que um banco de dados / sistema de arquivos replicado em ambientes com um procedimento de 'check-out' seria o melhor.
Portanto, sempre que um designer deseja fazer alguma modificação no mundo, ele faz check-out / bloqueia todos os ativos que deseja criar / modificar em todas as cópias do banco de dados (desenvolvimento e produção), para que nenhum outro jogador ou designer possa modificá-lo . Ele trabalharia no banco de dados de desenvolvimento até que o novo design fosse concluído e, naquele momento, as alterações seriam mescladas com o banco de dados de produção e esses ativos seriam verificados / desbloqueados em todos os ambientes.
As edições do player funcionariam da mesma maneira, exceto que as funções do banco de dados / sistema de arquivos seriam revertidas - elas funcionam no banco de dados de produção e todas as atualizações são carregadas no dev quando terminadas.
O bloqueio de ativos pode ser limitado às propriedades nas quais você deseja garantir nenhum conflito: no Exemplo 1, você bloqueará
ID 123456
assim que o jogador começar a elaborá-lo, para que os desenvolvedores não recebam esse ID. No Exemplo 2, seus desenvolvedores teriam bloqueado o nome do scriptassignFrenchAccent
durante o desenvolvimento; portanto, o jogador teria que escolher um nome diferente ao desenvolver sua modificação (esse pequeno incômodo pode ser reduzido pelo espaço para nome, mas isso por si só não evitará conflitos, a menos que você dê a cada usuário / desenvolvedor de um espaço para nome específico e, em seguida, você terá o mesmo problema ao gerenciar os espaços para nome). Isso significa que todo o desenvolvimento precisaria ler de um único banco de dados on-line, mas tudo o que você precisa desse banco de dados nesses exemplos são os nomes dos objetos, portanto o desempenho não deve ser um problema.Em termos de implementação, ter uma única tabela com todas as chaves e estado do ativo (disponível, bloqueado do dev, bloqueado do prod) sincronizado / acessível em tempo real nos ambientes deve ser suficiente. Uma solução mais complexa implementaria um sistema completo de controle de versão - você pode usar um sistema existente como o CVS ou o SVN se todos os seus ativos estiverem em um sistema de arquivos.
fonte
Penso que o objetivo aqui é aceitar de maneira limpa sua responsabilidade. 1) O servidor diz o que é atualmente aceitável e a API para acessar. O banco de dados está sendo modificado, de acordo com certas regras. 2) Os criadores têm permissão para criar conteúdo, mas ele deve ser reproduzido após as atualizações. Isso é de sua inteira responsabilidade: qualquer atualização deve poder analisar estruturas de dados antigas, de preferência o mais limpo e fácil possível.
A ideia do ponto de montagem tem méritos se você estiver interessado em acompanhar itens e posições únicos dentro de uma estrutura maleável, especialmente se aceitarmos que toda a estrutura 'doméstica' de um jogador passará por uma mudança dramática, e você deseja mantê-la pequenas coisas de decoração em seus respectivos armários.
É uma questão extremamente complicada, boa sorte! Provavelmente não existe nenhuma resposta.
fonte
Eu não acho que isso tenha um grande problema como você está fazendo.
Gostaria apenas de substituir os mods criados pelo usuário, com um aviso observando que "o Mod X pode não funcionar corretamente com esta versão", deixando aos criadores do mod alterar seu trabalho. Eu não acho que essa seja uma expectativa irreal de que as atualizações possam desativar certos mods.
O mesmo para o conteúdo criado pelo usuário, basta fazer um backup e substituir.
Não tenho experiência nisso, apenas fazendo sugestões.
fonte