Guia de Migração para Lore VCS: Resolvendo Custos Excessivos do Git LFS e Conflitos de Assets no Unreal
À medida que o projeto cresce, o Git LFS torna-se um desastre. Corrigir algumas linhas de código é instantâneo, mas quando assets de arte na casa dos gigabytes começam a se misturar, o ícone de carregamento gira por dezenas de segundos a cada commit. Devido à estrutura lenta do LFS, que copia e acumula arquivos grandes inteiros, o armazenamento do repositório ultrapassa rapidamente a marca de terabytes e a fatura de largura de banda exibe números assustadores.
O Lore, disponibilizado como open source pela Epic Games, prioriza o tratamento de arquivos binários. Ele utiliza o método FastCDC para dividir arquivos em blocos e eliminar redundâncias, reduzindo drasticamente o tamanho dos pacotes e a largura de banda de transmissão. Organizei métodos práticos de migração e otimização para o Lore que podem ser aplicados imediatamente, mesmo por equipes pequenas que não possuem especialistas em DevOps.
1. Migração gradual do Git LFS para o Lore
Não há necessidade de descartar o histórico de commits do código-fonte acumulado no repositório Git existente. Uma estratégia híbrida é mais realista: manter o código leve no Git e migrar apenas os assets binários pesados para o repositório Lore.
O script abaixo identifica a lista de arquivos rastreados pelo Git LFS, cria uma estrutura de diretórios espelhada e compara os hashes SHA-256 das cópias para transferir os dados para o Lore sem perdas.
`bash
#!/usr/bin/env bash
set -euo pipefail
GIT_REPO_DIR="HOME/projects/my−game−git"LOREREPODIR="HOME/projects/my-game-lore"
LORE_SERVER_URL="lore://127.0.0.1:41337/my-project"
echo "[1/5] Inicializando repositório Lore e vinculação remota..."
if [ ! -d "LOREREPODIR"];thenmkdir−p"LORE_REPO_DIR"
cd "LOREREPODIR"lorerepositorycreate"LORE_SERVER_URL"
else
cd "$LORE_REPO_DIR"
fi
echo "[2/5] Coletando lista de arquivos rastreados pelo Git LFS..."
cd "GITREPODIR"LFSFILES=(git lfs ls-files -n | grep -E ".(uasset|umap|fbx|png|psd)$" || true)
if [ -z "$LFS_FILES" ]; then
echo "Não há arquivos LFS para migrar."
exit 0
fi
echo "[3/5] Copiando arquivos e gerando manifesto para verificação..."
echo "LFSFILES"∣whileread−rfilepath;doif[−f"filepath" ]; then
dest_dir="LOREREPODIR/(dirname "filepath")"mkdir−p"dest_dir"
cp -p "filepath""LORE_REPO_DIR/filepath"sourcehash=(openssl dgst -sha256 "$filepath" | awk '{print 2}')
echo "filepath:sourcehash">>"LORE_REPO_DIR/.lore_migration_manifest"
fi
done
echo "[4/5] Desvinculando Git LFS e staging no Lore..."
cd "GITREPODIR"echo"LFS_FILES" | while read -r filepath; do
git rm --cached "$filepath"
done
sed -i.bak -E 's/filter=lfs diff=lfs merge=lfs//g' .gitattributes
cd "LOREREPODIR"echo"LFS_FILES" | while read -r filepath; do
lore stage "$filepath"
done
lore commit -m "Migration: Transfer heavy assets from Git LFS to Lore"
lore push
echo "[5/5] Verificação de integridade dos dados..."
lore repository verify state
lore repository verify fragment
if [ -f ".lore_migration_manifest" ]; then
migration_errors=0
while IFS=":" read -r file sha; do
if [ -f "file"];thencurrenthash=(openssl dgst -sha256 "$file" | awk '{print 2}')
if [ "sha" != "$current_hash" ]; then
echo "Arquivo corrompido detectado: file"migrationerrors=((migration_errors + 1))
fi
fi
done < .lore_migration_manifest
if [ $migration_errors -eq 0 ]; then
echo "Migração concluída. Todos os dados foram recuperados com 100% de integridade."
rm .lore_migration_manifest
else
echo "Falha na verificação: $migration_errors arquivos apresentam problemas de integridade."
exit 1
fi
fi
`
Abra o terminal e salve o conteúdo acima como migrate.sh. Após conceder permissão de execução com chmod +x migrate.sh, modifique as variáveis conforme seu caminho local e execute-o.
Após este procedimento, o uso de armazenamento LFS na nuvem será reduzido instantaneamente. De acordo com casos de implementação da própria Epic Games, essa tecnologia de desduplicação em nível de bloco permite reduzir os custos de manutenção de infraestrutura em 50%.
2. Implementando "on-demand hydration" em pipelines CI/CD
Pipelines que baixam centenas de gigabytes de assets de jogos a cada ciclo de build são os principais responsáveis por sobrecarregar placas de rede e entradas/saídas de disco. Para reduzir o tempo de build, o Lore oferece suporte a "On-demand Hydration" (recuperação sob demanda) baseado em sistema de arquivos virtual.
Na etapa inicial do clone, apenas a cadeia de estrutura de metadados e a árvore de diretórios são replicadas como uma "casca". Os chunks binários necessários são transmitidos dinamicamente e preenchidos no disco apenas quando o compilador os lê durante o processo de "Cook".
`yaml
name: Game Project On-Demand Production Build
on:
push:
branches: [ release-candidate ]
env:
LORE_SERVER: "lore://10.0.1.50:41337/game-depot"
SHARED_STORE_PATH: "/var/lib/lore_shared_chunk_store"
WORKSPACE_PATH: "game_build_workspace"
jobs:
assemble-assets:
runs-on: self-hosted
steps:
- name: Preparando Persistent Shared Store
run: |
if [ ! -d "env.SHAREDSTOREPATH"];thensudomkdir−p"{{ env.SHARED_STORE_PATH }}"
sudo chmod 777 "env.SHAREDSTOREPATH"loreshared−storecreate"{{ env.SHARED_STORE_PATH }}"
fi
- name: Sparse Bare Clone via Shared Cache
run: |
lore clone \
--bare \
--use-shared-store \
--shared-store-path "${{ env.SHARED_STORE_PATH }}" \
"${{ env.LORE_SERVER }}" \
"${{ env.WORKSPACE_PATH }}"
- name: Excluindo fontes cinematográficas desnecessárias via view filter
run: |
cd "${{ env.WORKSPACE_PATH }}"
echo "+ /SourceCode/" > .lore/view
echo "+ /Config/" >> .lore/view
echo "+ /Content/Core_Assets/" >> .lore/view
echo "- /Content/Cinematic_RAW/" >> .lore/view
- name: Sincronizando apenas os chunks essenciais para o disco local
run: |
cd "${{ env.WORKSPACE_PATH }}"
lore sync
- name: Executando Unreal Engine Cooker
run: |
cd "${{ env.WORKSPACE_PATH }}"
./RunUAT.sh BuildCookRun \
-project="$(pwd)/GameProject.uproject" \
-platform=Win64 \
-clientconfig=Development \
-cook -stage -pak -archive
`
Ter um armazenamento de cache persistente, como /var/lib/lore_shared_chunk_store, em um servidor dedicado de build maximiza a eficácia. Após obter as informações de índice rapidamente com o comando lore clone --bare, basta configurar os caminhos necessários no arquivo de filtro .lore/view e executar lore sync. Ao consolidar essa estrutura, o tráfego enviado para os nós de build é reduzido em mais de 85% e o tempo total de build é encurtado em cerca de 30%.
3. Prevenção de conflitos de assets com sistema de travamento de arquivos (Locking)
Assets binários, como .uasset ou arquivos de mapa de nível do Unreal Engine, não podem ser mesclados automaticamente no branch principal como o código de texto. Se dois artistas modificarem o mesmo asset simultaneamente, o artista que enviar o push por último pode sobrescrever o trabalho do anterior ou causar confusão na linha do tempo.
O Lore fornece uma função de "Lock" (travamento) que realiza controle exclusivo com base no registro imutável do servidor remoto.
`bash
Solicitar permissão de edição exclusiva para um arquivo de modelo de personagem binário
lore lock acquire Content/Characters/HeroMesh.uasset --branch main
Verificar o status de posse de bloqueio de recursos dentro de uma pasta específica
lore lock status Content/Characters/HeroMesh.uasset
Buscar todos os arquivos bloqueados por um usuário específico no repositório remoto
lore lock query --branch main --owner developer_artist_03
Devolver a permissão após concluir o trabalho e enviar o push
lore lock release Content/Characters/HeroMesh.uasset --branch main
`
Deve-se estabelecer a regra de que o artista deve adquirir o bloqueio com o comando lore lock acquire antes de tocar no arquivo. Quando a modificação terminar e o commit e o push forem refletidos com sucesso no remoto, o bloqueio deve ser liberado com lore lock release para que o próximo colaborador possa assumi-lo.
Se os designers e artistas se sentirem desconfortáveis com comandos de terminal, pode-se integrar esses caminhos a ferramentas de GUI de desktop, como o Anchorpoint, para que os comandos de travamento sejam executados apenas com um clique do botão direito do mouse.
4. Guia de ajuste de cache por especificação de hardware
É necessário ajustar as configurações do cliente de acordo com as especificações do ambiente de trabalho para evitar gargalos de E/S de arquivos.
Ajuste do cliente local por especificação de PC de trabalho
- Sistemas de alto desempenho (NVMe SSD, 32GB RAM ou mais, CPU multi-core): Permite operações diretas baseadas em mapeamento de memória via técnicas de mapeamento de memória virtual do SO. Expanda o número de threads de download paralelo máximo e mantenha o período de retenção do cache de disco longo para eliminar atrasos de sincronização.
- Sistemas básicos e de artistas (SATA SSD ou HDD, 16GB ou menos de RAM): Desative o método de mapeamento de memória para evitar travamentos do motor causados pelo esgotamento de memória. Ative as flags
--direct-file-write e --direct-file-io para gravar diretamente no disco e aumente o intervalo de agendamento da coleta de lixo (garbage collection) para evitar fenômenos de preempção de disco inesperados durante o trabalho.
Análise de desempenho de acordo com a escolha do algoritmo de compressão
Consultando os resultados da reunião de decisão de arquitetura do Lore (ADR-00016), definir a especificação Zstandard Level 6 como motor de compressão padrão é o mais equilibrado em termos de eficiência.
| Tipo de algoritmo de compressão |
Taxa de compressão final (%) |
Velocidade de compressão (MiB/s) |
Velocidade de descompressão (MiB/s) |
Portabilidade e Características |
| LZ4 Default |
47.6% |
718.7 |
2494.5 |
Rápido, mas com alto desperdício de espaço de armazenamento |
| Zstd Level 1 |
34.5% |
602.0 |
1363.1 |
Um compromisso aceitável entre velocidade e preservação de espaço |
| Zstd Level 6 (Recomendado) |
28.9% |
136.5 |
1284.9 |
Mantém a melhor eficiência de processamento em relação à economia de espaço |
| Oodle Kraken 3 (Fast) |
28.2% |
95.6 |
1413.1 |
Exige biblioteca proprietária Oodle da Epic Games |
| Oodle Kraken 6 (Padrão anterior) |
23.9% |
2.2 |
1312.6 |
Velocidade cai severamente ao transferir gigabytes |
O Oodle Kraken Level 6, frequentemente usado no passado, era o principal motivo de aumentar o tempo de espera dos artistas antes de sair, com a velocidade de processamento de compressão durante o processo de serialização de back-end caindo para 2.2 MiB por segundo.
Por outro lado, o Zstd Level 6 oferece uma taxa de compressão próxima ao Oodle Kraken 6, enquanto a velocidade de compressão de blocos de chunk local é de 136.5 MiB/s, sendo cerca de 62 vezes mais rápida. Se houver muitos funcionários em filiais estrangeiras ou em regime de home office e a interferência na largura de banda for severa, considere a configuração de uma topologia de servidor de duas camadas, estabelecendo um nó de cache de borda proxy dentro da rede local.