Como posso armazenar todos os meus dados de nível em um único arquivo em vez de distribuídos por muitos arquivos?

8

No momento, estou gerando meus dados de nível e salvando em disco para garantir que todas as modificações feitas no nível sejam salvas.

Estou armazenando "pedaços" de 2048x2048 pixels em um arquivo. Sempre que o player se move sobre uma seção que não possui um arquivo associado à posição, um novo arquivo é criado.

Isso funciona muito bem e é muito rápido. Meu problema é que, enquanto você reproduz, a contagem de arquivos aumenta cada vez mais.

Eu estou querendo saber quais são as técnicas que podem ser usadas para aliviar a contagem de arquivos, sem sofrer um impacto no desempenho. Estou interessado em como você pode armazenar / buscar / atualizar esses dados em um único arquivo, em vez de vários arquivos com eficiência.

jgallant
fonte
5
Você basicamente teria que escrever um sistema de arquivos em miniatura para poder armazenar tudo em um único arquivo. Isso aumentará a complexidade e poderá não valer a pena.
thedaian
11
Eu realmente não tenho muita experiência com o seguinte, mas talvez um db nosql baseado em arquivo ( stackoverflow.com/questions/2403174/… ) possa ser uma opção.
Chris
De quantos arquivos estamos falando aqui? Os jogadores estão gerando dezenas de milhares de arquivos ou apenas centenas? O que os jogadores estão mudando no pedaço? É pedaço gerar um passo caro (ou seja, fazer o que você precisa para armazenar em cache todo o pedaço vs apenas um diff?)
Leniência
O Minecraft passou por essa conversão também em algum momento. Eu acredito que ele começou como um mod e foi incorporado à versão principal. Vale a pena investigar. minecraftwiki.net/wiki/Region_file_format
MichaelHouse
11
@thedaian um pouco mais de complexidade, mas você pode fazer algumas coisas legais e realmente reduzir os tempos de busca, se estiver disposto a algum trabalho extra na memória, para que o sistema de arquivos não precise fazê-lo no disco.
ClassicThunder

Respostas:

7

A maneira mais rápida de fazer isso é armazenar tudo em um arquivo e pular o cursor para o pedaço que você deseja ler. Depois que você bate no disco, lendo uma sequência a partir dele, seu argumento é bem rápido.

As várias ocorrências em diferentes INodes para encontrar a localização do arquivo no volume físico, o que está demorando na maior parte do tempo e também o que é pouco dimensionado.

Além disso, como isso é dinâmico, você também precisará de um mapa que armazene o deslocamento no arquivo para cada bloco.

No disco

[Chunk 1][Chunk 2][Chunk 3][Chunk 4][Chunk 5][Chunk 6][Chunk 7][Chunk 8][Chunk 9]

Visível

[7][8][9]
[6][1][2]
[5][4][3]

Depois, basta abrir um fluxo que lê o arquivo, mas não impede que outros fluxos / processos o acessem. Então você precisa ler o deslocamento correto para a distância correta. Eu acredito em c # é o abaixo.

var chunk = new byte[4194304];
using (var file = new FileStream (openFileDialog1.FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    using (var reader = new StreamReader (file, Encoding.Unicode)) {
        reader.Read(chunk, offset * 4194304, 4194304);
    }
}

Agora, devido ao fato de você ter aberto o fluxo no modo somente leitura e permitir que outras pessoas leiam / gravem, você pode continuar adicionando novos pedaços ao final. Basta acompanhar o número de deslocamento e não tentar lê-los antes que eles estejam lá.

PS: você não desejará usar o bloco using, pois desejará apenas 1 fluxo de leitura ao longo da vida do nível em que estiver usando. Além disso, você provavelmente precisará salvar o mapeamento de partes em outro arquivo na saída, mas isso é apenas uma carga quando você carrega seu nível.

ClassicThunder
fonte
Isso está me dando ótimas idéias. Esse método exigiria que você escrevesse para cada pedaço exatamente o mesmo número de bytes? Eu imaginaria, como a busca por compensação exigiria.
jgallant
Contanto que você tenha o primeiro byte e quanto tempo cada bloco tenha, eles não terão o mesmo tamanho. Você só precisa ter algo na memória para acompanhar esses dois dados.
ClassicThunder
11
Obviamente, se você não os tivesse com o mesmo tamanho de byte, seria necessário fazer muitas alterações para aumentar o tamanho.
MichaelHouse
1

Dependendo do tempo necessário para gerar um pedaço, você pode apenas armazenar diffs ou um estado atual (locais inimigos, etc.). À medida que o jogador volta para um pedaço, ele gera novamente usando uma semente armazenada e carrega as alterações feitas no arquivo.

Se os jogadores puderem fazer alterações significativas, isso pode ser lento e o arquivo diff ainda será bastante grande, mas apenas para pequenas alterações, deve ser uma operação barata. Diversas diferenças de partes também podem ser consolidadas em um único arquivo - algo com tamanho razoável que pode ser carregado na memória.

Você provavelmente não desejaria mover todas as diferenças para um único arquivo - isso abre uma série de outros problemas com a memória ou altera o meio do arquivo.

Clemência
fonte
1

Eu sei que esse é um tópico bastante antigo - mas eu gostaria de dizer que acho que um arquivo ZIP pode ser a melhor maneira de seguir aqui. Você obtém compactação com seus dados (se estiver usando bitmaps brutos, principalmente), legibilidade no sistema operacional e obtém o arquivo único conforme desejado.

Vaughan Hilts
fonte
0

Que tal uma varredura de diretório para verificar o registro de data e hora dos arquivos no diretório de dados de nível versus o arquivo ativo atual e fornecer uma descrição do arquivo anterior e do arquivo a cada 10 segundos mais ou menos e o que não estiver sendo usado apenas exclua-os.

A menos que você exija que o jogador volte. Em seguida, basta limpar os dados do nível após a conclusão ou o ponto de verificação? Poderia ficar grande, com certeza, mas acho que não há muitas opções disponíveis aqui

RedactedProfile
fonte
0

Que tal vários pedaços por arquivo? Você diz que seus pedaços são 2048 x 2048, que tal colocar 16384 x 16384 em um arquivo. Sinalize quais existem de alguma forma para que você saiba se precisa criá-lo.

Loren Pechtel
fonte
0

Se você pode gerar os pedaços com rapidez suficiente, à medida que o player explora de qualquer maneira, não é necessário armazená-los em disco; tudo o que você precisa fazer é armazenar a semente das funções de ruído permanente que você está usando para gerar seu conteúdo processual novamente sob demanda.

Eles podem ser armazenados em um único arquivo e podem ser gravados sequencialmente e classificados na RAM quando carregados; não há necessidade de uma estrutura classificada complicada no próprio arquivo em disco. Você pode lê-lo apenas na inicialização e escrever nele enquanto gera novas 'páginas' (como são denominadas) no mundo do jogo.

Vai
fonte