Maneiras de gerenciar a alteração dos dados do designer juntamente com a alteração dos dados do player

14

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

Kylotan
fonte
1
Imagino que a resposta para isso possa depender, pelo menos em parte, de que tipo de dados os designers estão adicionando e o que os players estão adicionando.
lathomas64
Poderia, mas estou mais interessado em soluções genéricas para duas partes, contribuindo para um único armazenamento de dados.
Kylotan
1
Muitas pessoas estão perdendo o objetivo dessa pergunta - não são as mudanças dos jogadores que são conflitantes: atualizações de conteúdo etc. quebram os layouts existentes. @Kylotan, estou certo?
31416 Jonathan Dickinson
É aí que as atualizações de conteúdo do desenvolvedor podem entrar em conflito com as atualizações de conteúdo do player. Eu não estou realmente interessado em soluções alternativas de design de jogos (por exemplo, permita apenas que os jogadores construam em determinados lugares), mas em soluções alternativas de estrutura de dados (por exemplo, permita que os jogadores criem coisas com IDs maiores que 1 milhão).
Kylotan
2
Você não especificou, espera fazer atualizações em tempo real em um mundo ao vivo? Uma observação lateral: duas partes que contribuem para um único armazenamento de dados são um banco de dados, é isso que os bancos de dados fazem, você não pode contornar esse fato e seria tolice ignorar décadas de conhecimento sobre como evitar problemas com dados compartilhados.
Patrick Hughes

Respostas:

2

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:

  • Incentive o uso liberal de espaços de nome para cercar o conteúdo de um autor / mod / equipe específico.
  • Onde o conteúdo precisar interagir, estabeleça convenções claras de chamada / uso, convenções de nomenclatura e outras 'regras' frouxas que orientam o desenvolvimento para facilitar a integração. Forneça ferramentas que permitam aos criadores de conteúdo saber se estão seguindo essas regras, idealmente integradas à própria criação de conteúdo.
  • Forneça ferramentas de relatório / análise para detectar possíveis falhas de mesclagem antes que elas aconteçam. A correção da pós-mesclagem provavelmente é muito dolorosa. Faça com que seja possível verificar um determinado conteúdo e deixar tudo claro como pronto para mesclagem, para que a mesclagem seja indolor
  • Torne sua fusão / integração robusta. Permitir reversões fáceis. Faça testes rigorosos do conteúdo mesclado: se ele falhar no teste, não o mescle! Itere o conteúdo deles ou o seu até que a mesclagem prossiga de forma limpa.
  • Evite usar IDs inteiros incrementais para qualquer coisa (você não tem uma maneira confiável de distribuí-los aos criadores). Isso só funciona em um banco de dados porque o próprio banco de dados é um provedor canônico de IDs para que você nunca obtenha duplicatas; no entanto, também apresenta um único ponto de falha / carga no seu sistema.
  • Em vez disso, use GUIDs - eles custam mais para armazenar, mas são específicos da máquina e, portanto, não causam colisões. Como alternativa, use identificadores de seqüência de caracteres, isso é muito mais fácil para depurar / resolver, mas é mais caro para armazenamento e comparação.
MrCranky
fonte
Infelizmente, parte disso não é útil para o meu problema (por exemplo, fazer com que os jogadores sigam certas regras, pois tudo isso deve ser feito automaticamente no servidor) e não acho que seja prático oferecer suporte ao grau de gerenciamento de transações e transações semântica mencionada, mas a abordagem geral de alocar IDs garantidos exclusivos, talvez GUIDs, é provavelmente o mais próximo do que eu irei usar.
Kylotan
Ah entendo. Bem, desde que você controla suas ferramentas de construção, pelo menos impor essas abordagens amigáveis ​​para mesclagem (espaços para nome etc.) é algo que você pode fazer sem que os jogadores tenham que concordar.
21412 MrCranky
O que você faz se dois jogadores criam conteúdo duplicado? Ou instâncias separadas do seu mundo de jogo são tratadas como únicas? Nesse caso, talvez seja uma abordagem útil verificar automaticamente todas as instâncias exclusivas que você conhece em relação à ramificação principal / da linha principal em busca de conflitos que ocorrerão quando você enviar essas alterações para as instâncias. Se você não pode controlar os jogadores, pode pelo menos avisar sua equipe interna de que o trabalho que eles estão fazendo entra em conflito com a instância X do mundo, no início do desenvolvimento.
21412 MrCranky
O conceito de namespaces não é tanto o problema - escolher espaços de nomes adequados no namespace de todos os namespaces possíveis! :) E conteúdo duplicado para mim não é um problema - são apenas duas instâncias de algo equivalente. O importante é que não ocorram fusões ou substituições prejudiciais. Quanto às verificações automáticas de colisão, isso interrompe o dano causado na gravação, mas não resolve o problema de nomenclatura original. (Renomeando coisas, para evitar uma colisão pode ser não-trivial, devido ao cruzamento de dados.)
Kylotan
Ah, sim, entendo agora, não são os próprios espaços de nomes, mas a escolha do nome. Nesse caso, os GUIDs provavelmente são a resposta novamente - mantêm o conteúdo efetivamente mantido em sua própria área pequena. Um nome decorativo pode ser dado, mas o jogo usaria o GUID.
21412 MrCranky
1

Armazene tudo como um atributo (ou decorador) - com pontos de montagem. Vamos dar uma casa que o jogador projetou como exemplo:

o House: { Type = 105 } // Simple square cottage.
 o Mount point: South Wall:
  o Doodad: Chair { Displacement = 10cm }
   o Mount point: Seat:
    o Doodad: Pot Plant { Displacement = 0cm, Flower = Posies } // Work with me here :)
 o Mount point: North Wall:
  o Doodad: Table { Displacement = 1m }
    o Mount point: Left Edge:
     o Doodad: Food Bowl { Displacement = 20cm, Food = Meatballs}

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

Jonathan Dickinson
fonte
Isso se concentra um pouco demais no posicionamento de objetos, o que não é realmente o principal problema que estou tentando resolver. É mais sobre ter identificadores únicos em conjuntos de dados simultâneos e precisar mesclar aqueles sem nenhum risco possível de conflito. Eu adicionei 2 exemplos ao meu post no outro dia para tentar explicar um pouco mais.
Kylotan
1

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:

  1. Como garantir que os IDs de objetos sejam exclusivos
  2. Como garantir que os namespaces de script não colidem

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

assignFrenchAccent

Torna-se

_Z11assignFrenchAccent
Erro 454
fonte
0

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.

lathomas64
fonte
0

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 123456assim 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 script assignFrenchAccentdurante 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.

SkimFlux
fonte
Geralmente, não é prático bloquear dados globalmente - os jogadores podem estar editando o mundo apenas com a reprodução normal, e você não gostaria que a reprodução impedisse os designers de trabalhar. Se você permitir bloqueios globais, uma operação de mesclagem será basicamente uma operação de substituição, o que é fácil - mas se você não tiver bloqueios globais, o que acontecerá?
Kylotan
Como o lathomas64 mencionou, a resposta dependeria de que tipo de dados você está falando. Sem bloqueios globais, eu pensaria que você teria que ter um sistema de controle de versão e um conjunto de regras para resolver qualquer conflito - essas regras dependeriam dos requisitos de dados e jogabilidade. Depois de ter essas, acho que toda mesclagem se reduz a uma operação de substituição simples.
SkimFlux
0

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.

Karmington
fonte
0

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.

Woody Zantzinger
fonte
Eu acho que se fosse puramente para mods fornecidos pelo usuário, você estaria certo. Mas alguns jogos são explicitamente sobre o conteúdo criado pelo usuário e, portanto, você não pode simplesmente destruí-lo.
Kylotan
Em seguida, deixe espaço no sistema para o conteúdo que você pode adicionar posteriormente. Se você estiver usando números de identificação, reserve 1-1000. Ou, se os usuários puderem nomear seus ativos, não permita que eles iniciem o nome com "FINAL-" ou algo assim (reserve-o para seus próprios ativos). EDIT: ou melhor ainda, fazê-lo em sentido inverso, forçando o conteúdo do utilizador para uma gama ou um prefixo
Woody Zantzinger