Português English

Programação

Open Asset Package feature
2026.06.16

Open Asset Package

Jogos usam uma quantidade impressionante de arquivos (assets). Texturas, malhas, áudio, shaders, cenas, scripts — um título moderno pode facilmente conter dezenas de milhares de assets individuais. Gerenciar esse “custo por assets” é um dos desafios silenciosos do desenvolvimento de jogos.

Formatos de arquivo tradicionais como Tar e ZIP continuam sendo alternativas comuns, mas foram projetados para ambientes muito diferentes. O Tar surgiu no final da década de 1970 para sistemas de fita magnética, enquanto o ZIP foi introduzido no final dos anos 1980 para melhorar a eficiência de armazenamento em disquetes. Ambos continuam sendo formatos de uso geral úteis, mas nenhum foi projetado para a forma como motores de jogo modernos carregam e fazem streaming de assets.

À medida que os projetos crescem, os motores frequentemente adotam sistemas de empacotamento especializados. A Unreal Engine utiliza arquivos .pak, a Unity depende de Asset Bundles, a Godot distribui projetos exportados em arquivos .pck, e o raylib oferece o formato aberto rres. Cada um resolve uma parte dos mesmos problemas, mas geralmente está atrelado a ecossistemas, ferramentas ou suposições específicas sobre como os assets devem ser organizados.

O Open Asset Package (OAP) é uma tentativa de resolver esses desafios com um formato aberto e independente de motor, projetado especificamente para fluxos de trabalho modernos de assets em jogos.

O problema central

Motores de jogo geralmente precisam de três capacidades de um contêiner de assets:

  1. Acesso aleatório, permitindo que um único asset seja localizado rapidamente sem percorrer todo o arquivo.
  2. Suporte a streaming, permitindo que assets sejam obtidos incrementalmente via requisições de intervalo HTTP em vez de baixar arquivos monolíticos inteiros.
  3. Identidade estável, garantindo que as referências a assets permaneçam válidas mesmo quando nomes de arquivo, estruturas de diretório ou estratégias de empacotamento evoluem.

Os formatos de arquivo tradicionais atendem apenas parcialmente a esses requisitos.

O Tar foi projetado para mídias sequenciais. Encontrar um arquivo específico exige percorrer as entradas do arquivo uma por uma, resultando em tempos de busca lineares. Quando combinado com compressão de fluxo como tar.gz, extrair um único asset requer descomprimir todo o arquivo.

O ZIP melhora substancialmente ao introduzir um diretório central que permite buscas diretas. No entanto, o diretório fica no final do arquivo, os cabeçalhos locais duplicam metadados, e o suporte a criptografia evoluiu através de múltiplas extensões ao longo dos anos. O ZIP continua sendo um formato de arquivo de uso geral flexível, e não um sistema otimizado para streaming de assets e identidades estáveis.

Os formatos de motores de jogo frequentemente resolvem essas limitações de formas que refletem as prioridades de seus respectivos ecossistemas.

Os Asset Bundles da Unity priorizam a integração com o editor e o runtime da Unity. Implementações iniciais dependiam fortemente de compressão LZMA monolítica, enquanto versões mais recentes introduziram compressão LZ4 em blocos para melhorar o comportamento de carregamento. Apesar desses avanços, o formato permanece intimamente ligado à Unity.

O formato PAK da Unreal é uma das abordagens mais ricas em recursos, com suporte a métodos avançados de compressão, criptografia e montagem em camadas de pacotes. No entanto, está profundamente integrado ao sistema de arquivos virtual e ao pipeline de assets da Unreal.

O formato PCK da Godot foca na simplicidade e na integração perfeita com o motor. Os assets são identificados principalmente por caminho, e o sistema de empacotamento é projetado em torno do processo de exportação da Godot, não para gerenciamento geral de assets.

O rres do raylib é talvez o parente filosoficamente mais próximo do OAP. Oferece uma especificação aberta com suporte a compressão e criptografia, mantendo uma implementação leve. Intencionalmente deixa questões como gerenciamento de dependências e identidade de assets em grande escala para sistemas de nível superior.

O OAP foi projetado para atender a esses requisitos diretamente: acesso aleatório rápido, streaming eficiente, identidades estáveis de assets e relações de dependência explícitas em um formato independente de qualquer motor específico.

Destaques do design

Cabeçalho de tamanho fixo: O OAP organiza os pacotes com um layout simples onde o índice fica no final. Um cabeçalho de tamanho fixo é armazenado no início do arquivo, enquanto um índice ordenado fica em um deslocamento conhecido próximo ao final. Os dados dos assets ocupam o espaço intermediário. Abrir um pacote torna-se um processo simples: ler o cabeçalho, ir diretamente ao índice e fazer uma busca binária para localizar o asset desejado. Não é necessário percorrer o conteúdo dos assets. Quando os pacotes são hospedados remotamente, localizar um asset geralmente requer apenas duas requisições de intervalo HTTP: uma para o cabeçalho e outra para o índice.

Recursos independentes por asset: Compressão e criptografia são definidas por asset, não no nível do pacote. Um asset pode ser armazenado sem compressão para carregamento rápido, enquanto outro pode usar compressão DEFLATE e criptografia ChaCha20. Ler um único asset nunca requer descomprimir ou descriptografar o arquivo inteiro. As chaves de criptografia são intencionalmente excluídas do pacote, oferecendo proteção contra extração casual sem tentar funcionar como um sistema de DRM.

Identidade de assets: O OAP separa a identidade do asset da organização do sistema de arquivos. Os assets são identificados por identificadores estáveis de 128 bits, enquanto os caminhos legíveis por humanos são tratados como metadados, não como chaves primárias. Isso viabiliza fluxos de trabalho práticos para patches e conteúdo para download. Um pacote pequeno contendo apenas assets modificados pode ser montado junto a um pacote existente, e as buscas são resolvidas usando identificadores de assets em vez de nomes de arquivo. Renomear ou reorganizar arquivos não invalida mais referências em todo o projeto.

Grafo de dependências de assets: O OAP permite que assets declarem dependências diretamente no índice do pacote. Materiais podem referenciar texturas, cenas podem referenciar malhas, e prefabs podem referenciar recursos de suporte. Ao tornar esses relacionamentos explícitos, os motores podem construir grafos de carregamento, realizar análise de dependências e implementar estratégias de streaming mais sofisticadas sem depender de sistemas de metadados separados.

Especificação técnica

O formato OAP é documentado independentemente em SPEC.md. A especificação é agnóstica de linguagem e intencionalmente compacta: um leitor em conformidade requer apenas parsing de inteiros em little-endian, uma implementação de CRC-32/ISO-HDLC e um decodificador DEFLATE bruto — todos disponíveis em qualquer biblioteca padrão principal. Um leitor mínimo tem algumas centenas de linhas em qualquer linguagem com acesso a buffers de bytes. Implementações alternativas em outras linguagens são explicitamente bem-vindas; o repositório inclui um guia de portabilidade (docs/implementing.md) e um fixture de teste canônico (testdata/sample.oap) para validação.

Comparações

Formatos de arquivo de uso geral

Recurso tar.gz ZIP OAP
Especificação aberta Sim Sim Sim
Busca de assets Sequencial O(n) Diretório central O(1) Índice com busca binária O(log n)
Amigável a range HTTP Fraco Limitado Excelente
Compressão por asset Não (arquivo inteiro) Sim Sim
Criptografia por asset Apenas externa Legada / extensões AES ChaCha20 por asset
Identidade estável do asset Não Não Sim (ID de 128 bits)
Verificação de integridade Nenhuma CRC-32 opcional CRC-32 obrigatório
Rastreamento de dependências Não Não Sim
Overhead típico por entrada 512 bytes ~76+ bytes 64 bytes fixos

Formatos de assets de motores de jogo

Recurso Unity
Asset Bundles
raylib
rres
Godot
PCK
Unreal
PAK
OAP
Especificação aberta Não Sim Sim Não Sim
Busca de assets Indexada O(1) Sequencial O(n) Sequencial O(n) Indexada Busca binária O(log n)
Amigável a range HTTP Limitado Moderado Limitado Moderado Excelente
Compressão por asset Em blocos (LZ4) Sim Não Sim Sim
Criptografia por asset Não Sim Nível de pacote Sim Sim (ChaCha20)
Identidade estável do asset Não IDs de 32 bits Baseada em caminho Hashes de caminho IDs de 128 bits
Verificação de integridade Sim Sim Sim Sim CRC-32 obrigatório
Rastreamento de dependências Externo (Addressables) Não Externo Externo Embutido
Ferramentas independentes de motor Não Sim Limitado Não Sim

O OAP não tenta substituir os pipelines de assets específicos de cada motor. Em vez disso, foca em oferecer um formato de empacotamento portátil que incorpora capacidades comumente encontradas em soluções especializadas, mantendo-se simples de implementar e independente de qualquer ecossistema de motor específico.

Desempenho

A tabela a seguir compara o OAP com tar e tar.gz em uma carga de trabalho sintética de assets de jogos: 360 assets totalizando aproximadamente 114 MB de dados brutos, misturando conteúdo compressível (scripts, configurações, descrições de cenas) com blobs incompressíveis (áudio e texturas pré-comprimidos). Todos os valores OAP usam uma build otimizada com ReleaseFast em x86_64 Linux; os valores do tar incluem o overhead de inicialização do subprocesso.

Formato Tempo de
empacotamento
Tamanho do
arquivo
vs bruto Leitura
sequencial
Acesso
aleatório
OAP — auto (deflate onde benéfico) 153 ms 89,6 MB −21,8% 72 ms 71 ms
OAP — store (sem compressão) 100 ms 114,6 MB +0,0% 65 ms 66 ms
tar 155 ms 114,9 MB +0,2% 80 ms N/D
tar.gz 1.399 ms 89,6 MB −21,8% 123 ms N/D

O OAP com compressão automática empacota no mesmo tempo que o tar simples enquanto atinge a mesma taxa de compressão do tar.gz — que leva nove vezes mais para produzir e quase o dobro do tempo para ler. A leitura sequencial é mais rápida do que ambas as variantes tar. O acesso aleatório — indisponível para formatos tar sem extração completa — completa em aproximadamente o mesmo tempo que uma leitura sequencial completa, graças à busca binária O(log n) sobre o índice ordenado.

Ferramenta de linha de comando da implementação de referência

A implementação de referência é distribuída como um único executável chamado oap, concebido tanto como utilitário prático quanto como exemplo de uso do formato em aplicações reais.

A biblioteca principal consiste em aproximadamente 500 linhas de código e depende exclusivamente da biblioteca padrão do Zig para funcionalidades de compressão e criptografia. O processo de build produz tanto uma biblioteca estática (liboap.a) quanto o utilitário de linha de comando oap. Aplicações escritas em C, C++, C# ou outras linguagens compatíveis com FFI podem integrar o OAP sem exigir Zig no restante do código.

PACK: O empacotamento é feito com o comando pack, que percorre recursivamente um diretório e produz um arquivo .oap: oap pack assets/ game.oap. A compressão pode ser habilitada ou desabilitada conforme necessário, os metadados do pacote podem ser customizados, e os assets podem ser opcionalmente criptografados com ChaCha20. Por padrão, os identificadores de assets são gerados deterministicamente a partir dos caminhos virtuais, garantindo identidades consistentes entre builds.

INSPECT: Diversos comandos estão disponíveis para inspecionar o conteúdo dos pacotes. O comando inspect exibe metadados do pacote, como versão do formato, informações do manifesto, contagem de assets, localização do índice e status de criptografia. O comando list fornece um inventário mais detalhado dos assets empacotados, incluindo identificadores, métodos de compressão e criptografia, tamanhos e caminhos virtuais.

UNPACK: Os pacotes podem ser extraídos com o comando unpack, restaurando os assets conforme seus caminhos virtuais. Pacotes criptografados podem ser desempacotados fornecendo a chave de criptografia adequada.

VALIDATE: Para fluxos de trabalho automatizados, o comando validate verifica tanto os metadados do pacote quanto o checksum CRC-32 de cada asset contido no arquivo: oap validate game.oap. Isso o torna adequado para uso em pipelines de integração contínua ou para validar pacotes distribuídos por redes de entrega de conteúdo.

Parte do Turian

O OAP se originou no ecossistema do motor de jogo Turian, mas foi projetado como um formato independente, adequado para qualquer motor ou aplicação que necessite de empacotamento eficiente de assets.

Código-fonte e a especificação completa

Open Asset Package
Lançando Turian feature
2026.06.13

Lançando Turian

Finalmente chegou a hora de falar sobre o que eu andei cozinhando. Depois de uma enorme mudança técnica e muitas horas de “cientista maluco” no laboratório, estou super empolgado em anunciar que o Turian está oficialmente no ar.

O Turian é um motor de jogo (game engine) 3D construído inteiramente em Zig. Mas para entender por que ele existe, precisamos olhar para onde eu estava há apenas alguns meses.

O Caminho pelo C#

Se você acompanhou meu trabalho, sabe que tenho um orgulho imenso da Guinevere, a biblioteca de GUI em C# que eu projetei. Eu amo esse projeto. Mas quando tentei usá-lo para construir um estúdio de motor completo, as coisas ficaram… complicadas. Eu até tentei uma abordagem híbrida usando Avalonia — que é uma peça de tecnologia sólida como uma rocha — mas simplesmente não parecia certo para o que eu queria alcançar. Além disso, sejamos honestos, a cena de motores de jogo em C# já está bem lotada.

Quando comecei minha “missão secundária” no Zig, algo clicou. Programar em Zig era simplesmente divertido. Encontrei uma biblioteca Zig IMGUI existente que me permitiu começar a construir o Turian Studio desde o primeiro dia. Ela não é tão poderosa quanto eu quero ainda — provavelmente acabarei fazendo um fork ou investindo pesado em melhorá-la — mas deu o pontapé inicial.

Um Pouco de Magia Técnica

Um dos maiores obstáculos no Zig (comparado ao C#) é a falta de reflexão. Eu precisava que o editor pudesse “ver” e manipular o código. Fico feliz em dizer que resolvi a maior parte disso! Agora, o editor pode inspecionar suas structs e fornecer uma interface adequada para ajustá-las.

Mas o recurso de que mais me orgulho? O Modo Play no Editor. Consegui implementar isso compilando o código de jogo do usuário em uma biblioteca e carregando-a em tempo de execução. Parece exatamente como trabalhar no Unity ou Godot — você aperta o play e entra no jogo instantaneamente. Quando você está pronto para distribuir, o jogo final ainda é compilado como um executável nativo único e enxuto.

No momento, você pode criar um projeto do zero, soltar seus modelos 3D, adicionar scripts e “cozinhar” o jogo final com apenas alguns cliques.

O Roteiro (Roadmap)

Estamos avançando rápido. O motor está evoluindo a cada dia. Aqui está o plano geral para o futuro próximo:

  • Refinando o 3D: Atualmente estamos focados em melhorar materiais, iluminação e sombras.
  • Poder do Studio: Tornando o editor mais robusto, amigável e poderoso.
  • Os Três Grandes: Em breve, investiremos pesado em Áudio, Física e um sistema de GUI interna adequado.

O objetivo é dar a você tudo o que precisa para construir um jogo completo e funcional sem sair do ecossistema.

Uma Nova MEGA4

Este lançamento marca um reinício para a iniciativa MEGA4. Como no plano original, estamos abraçando a filosofia de “Uma Linguagem” e, desta vez, o Turian é o nosso ponto de partida. Em vez de começar pela GUI e construir para cima, estamos pulando alguns degraus e indo direto para o fundo da pilha.

Definitivamente não sou um especialista em Zig, e construir um motor de jogo definitivamente não é uma tarefa fácil, então não falta empolgação.

É totalmente de código aberto (GPLv3). Estou me divertindo muito programando este projeto. Confira em turian.mass4.org!

Uma Aventura em Zig-Zag feature
2026.06.10

Uma Aventura em Zig-Zag

Eu sou um grande fã de C#. Passei anos dominando a linguagem e, honestamente, sou muito bom nela. E o C# está melhor do que nunca: a cada lançamento, fica mais rápido e robusto — a este ponto, rivalizando com C++ e Rust em muitos benchmarks com quase nenhuma diferença. Eu ainda o amo, e muitos dos meus projetos são (e continuarão sendo) construídos com ele. É minha “casa”.

Mas, ultimamente, senti que precisava de um novo desafio — algo mais “cru”. Foi quando comecei a brincar com Zig como um projeto de hobby/educacional. E bem, as coisas escalaram.

Por que Zig? (E por que não Rust?)

Eu tentei o Rust, mas, sinceramente? Simplesmente não é divertido para mim. É rígido demais. Em vez de focar na lógica do código ou no “quadro geral”, eu passava horas lutando contra a gramática e o borrow checker. E nem me faça falar da sintaxe — é cheia de comandos abreviados e pontuações esotéricas que fazem meus olhos doerem.

O Zig, por outro lado, é simples, direto, e o que você vê é o que você tem. Sem runtime oculto, sem regras pesadas. Ele respeita sua inteligência.

Nem Tudo São Flores

Vamos ser realistas: mudar para o Zig tem sido uma batalha épica em alguns aspectos. Vindo do luxo do C#, eu sinto muita falta de coisas como Attributes e Reflection. Sim, reflexão é mais lenta, mas, cara, é incrivelmente poderosa para construir ferramentas. O comptime do Zig é incrível e cobre muito terreno, mas é uma fera completamente diferente.

Depois, há o ecossistema. O Zig ainda é jovem e a documentação é… digamos, “escassa”. Mesmo os LLMs, que costumam ser minha salvação para respostas rápidas, lutam constantemente para dar as soluções certas porque simplesmente não existem exemplos suficientes por aí ainda.

A ABI não é estável. Algumas coisas que seriam triviais em C não são possíveis em Zig.

E nem me fale do suporte de IDE. Mudei a maior parte da minha programação para o editor Zed ultimamente. Eu amo o quão rápido ele é, mas definitivamente não é o Rider. Quando você está acostumado com a inteligência profunda do “sabe-tudo” das ferramentas da JetBrains para C#, programar em Zig parece um pouco como pilotar um avião com metade dos instrumentos faltando.

O Projeto Paralelo Perfeito

Então, por que fazer isso? Porque é uma aventura. Sinto que estou explorando um mundo novo, compartilhando minhas descobertas com a comunidade e expandindo os limites. É como jogar um jogo lançado no primeiro dia, sem nenhum detonado — você está apenas tentando descobrir as coisas por conta própria.

O que começou como um projeto de fim de semana do tipo “vamos ver como isso funciona” se transformou em algo muito maior. Tem sido uma experiência de aprendizado incrível que me forçou a afiar minhas habilidades. E esse projeto de hobby? Acabou se tornando um motor completo. Mas vou guardar os detalhes desse lançamento “acidental” para o próximo post.

A Incerteza do NUKE: Quando o Maestro se Afasta feature
2026.04.27

A Incerteza do NUKE: Quando o Maestro se Afasta

Acordei outro dia com notícias inquietantes no ecossistema .NET. Rumores estão circulando — e alguns posts em blogs já começam a corroborar — que o NUKE, o sistema de build no qual passei a confiar para quase todos os meus projetos em C#, está sendo efetivamente abandonado.

O comentário geral é que o desenvolvedor principal, Matthias Koch, chegou ao seu limite. Depois de anos dedicando alma e coração ao projeto, a falta de incentivo financeiro e o peso esmagador de manter uma peça tão crítica de infraestrutura cobraram seu preço. O que é ainda mais preocupante é o boato de que qualquer desenvolvimento futuro pode acontecer em um fork que seria de código fechado ou passaria para um modelo pago.

Isso me atinge em cheio. Acabei de escrever sobre o quanto eu amo o Nuke, e agora a base parece estar tremendo sob meus pés.

A Busca por um Substituto

Quando uma ferramenta da qual você depende entra em “modo de manutenção” ou muda seus termos de licenciamento, o primeiro instinto é procurar um bote salva-vidas.

Curiosamente, o Cake 6.0 foi lançado recentemente, e parece que a equipe de lá estava atenta. Eles introduziram o Cake.Sdk, que parece muito uma resposta à filosofia “code-first” do NUKE. Ele se afasta dos antigos scripts .cake e se aproxima de arquivos .cs padrão com uma abordagem baseada em projetos. Ele tem aquela integração com a IDE e a sensação de tipagem forte que me fez mudar para o NUKE em primeiro lugar.

É o suficiente para me fazer pular do barco? Ainda não. Por enquanto, vou continuar com o NUKE. O código existente é licenciado sob MIT e, mesmo que o repositório oficial seja arquivado, a versão atual ainda funciona. Mas a viabilidade a longo prazo é, definitivamente, uma incógnita.

O Custo do “Grátis”

Essa situação lança um holofote cruel sobre o ecossistema de código aberto. Nós — desenvolvedores e empresas — nos tornamos incrivelmente confortáveis construindo impérios sobre o trabalho gratuito de outros. Dependemos desses “pequenos” projetos mantidos por uma ou duas pessoas e frequentemente esquecemos que há um ser humano do outro lado daquele nome no GitHub.

Se nos importamos com essas ferramentas, precisamos apoiá-las. Seja através do GitHub Sponsors, Open Collective ou licenças corporativas, o “grátis” sempre tem um custo. Se os mantenedores não conseguem pagar suas contas ou encontrar um motivo para continuar além do “espírito comunitário”, o projeto acaba entrando em colapso.

Estou em uma encruzilhada aqui. Já está testando as águas com o Cake 6 ou talvez algo como o Modular Pipelines? Por enquanto, manterei meus scripts do Nuke rodando, mas estarei de olho no horizonte.

Meu Maestro em C# feature
2023.06.25

Meu Maestro em C#

Eu sempre fui obcecado por uma coisa: consistência. Seja construindo código na minha máquina local ou vendo-o rodar nas entranhas de um servidor do GitLab, eu quero exatamente o mesmo comportamento. Eu queria um processo de build que não se importasse com onde estava — ele deveria ser tão confortável no meu notebook quanto em um servidor de terceiros.

Quando comecei o projeto SuCoS, eu sabia que precisava de um sistema de build que pudesse acompanhar minha ambição. Não era apenas compilar e testar; eu queria que ele cuidasse de tudo — gerar tags, criar releases e até preparar uma imagem Docker novinha toda semana.

Depois de várias sessões de codificação tarde da noite — daquelas regadas a muitas xícaras de café e muitos esboços de design amassados — finalmente encontrei minha resposta no Nuke. Em vez de espalhar minha pipeline por uma dúzia de estágios separados no YAML do GitLab, deixei o Nuke entrar como meu maestro onipotente.

O Maestro em C#

No fundo, o Nuke é um maestro da automação. É como ter um regente para o seu código, garantindo que cada tarefa seja executada no momento perfeito. E a melhor parte? Esse maestro fala minha linguagem favorita: C#.

Antes do Nuke, eu tinha que comandar manualmente o GitLab para executar cada passo — dotnet restore, dotnet clean, dotnet build, dotnet test. Agora, eu só digo uma coisa ao GitLab: nuke test. Esse comando mestre é como um encantamento poderoso que aciona o Nuke para lidar com todo o resto.

Como ele é escrito em C# em vez de YAML bagunçado, tenho muito mais confiança nos meus builds. O C# me dá aquela sensação de solidez que eu tanto busco. Adoro o fato de que quando chamo o nuke, a primeira coisa que ele faz é compilar a si mesmo antes de construir qualquer outra coisa. É um eco de autoafirmação que me faz sentir em casa.

Para manter as coisas em ordem, também trouxe o GitVersion para lidar com o Versionamento Semântico (SemVer). É como ter um bibliotecário diligente que examina meus commits, aplica os padrões de Semantic Commit e atribui o número de versão perfeito sem que eu precise mexer um dedo.

Domando a Fera

Claro, nenhuma jornada é isenta de solavancos. Embora o Nuke seja poderoso, encontrei alguns obstáculos pelo caminho.

Algumas tarefas, como criar tags e releases, são muito centradas no GitLab e dependem da API deles. Isso significa que elas não são tão portáteis quanto eu gostaria e exigiram alguns ajustes extras para funcionarem corretamente. Foi um pequeno preço a pagar pela automação que ganhei.

À medida que o SuCoS crescia, minha classe de build começou a crescer como erva daninha. Estava se tornando um pesadelo de gerenciar — uma verdadeira confusão. Minha solução? Dividi a class Build : Nuke em várias classes parciais (partial classes). Isso transformou uma multidão indisciplinada de código em uma equipe organizada e disciplinada. Agora, minha lógica de build está limpa e fácil de navegar.

A beleza dessa configuração é que se alguém fizer um fork do SuCoS no GitHub, o sistema de build ainda funcionará quase perfeitamente com apenas alguns pequenos ajustes.

Csharp nuke building system

Bruno MASSA