# Essenciais de programação

## As ferramentas de desenvolvimento

A um nível muito alto, o Decentraland **Software Development Kit** (SDK) permite que você faça o seguinte:

* Gerar um *projeto* padrão contendo uma scene do Decentraland, incluindo todos os assets necessários para renderizar e executar seu conteúdo.
* Construir, testar e visualizar o conteúdo da sua scene localmente no seu navegador web — completamente offline, e sem precisar fazer transações Ethereum ou possuir LAND.
* Escrever código TypeScript usando a API do Decentraland para adicionar comportamento interativo e dinâmico à scene.
* Fazer upload do conteúdo da sua scene para o content server.
* Vincular seus tokens LAND à URL do conteúdo que você fez upload.

Nosso SDK inclui o seguinte:

* **O Creator Hub**: Um aplicativo independente que, entre outras coisas, permite criar scenes com uma interface fácil de arrastar e soltar. Você pode executar previews, debug, editar código e publicar. [Leia mais](https://docs.decentraland.org/creator/content-creator-pt/scene-editor/comecar/about-editor)
* **O Decentraland ECS**: Um pacote TypeScript contendo o framework de métodos auxiliares que permite criar experiências interativas. Use-o para criar e manipular objetos na scene e também para facilitar transações in-world entre players ou outros aplicativos. ( [referência mais recente do ECS](https://github.com/decentraland/ecs-reference/blob/master/docs-latest/decentraland-ecs.md))
* **Exemplos de scenes**: Inspire-se e aproveite boas práticas de programação dos [exemplos de scenes](https://studios.decentraland.org/resources?sdk_version=SDK7).

Outras ferramentas legadas:

* **O Web Editor**: Uma ferramenta baseada na web para criar scenes simples e publicá-las.

## Requisitos

Para desenvolver uma scene localmente, você não precisa possuir tokens LAND. Desenvolver e testar uma scene pode ser feito completamente offline, sem a necessidade de implantar uma scene na rede Ethereum (o sistema que o Decentraland usa para estabelecer a propriedade de LAND e de um Decentraland Name) ou no content server.

Você deve ter:

* **O Creator Hub**: Um aplicativo independente que, entre outras coisas, permite criar scenes com uma interface fácil de arrastar e soltar. Você pode executar previews, debug, editar código e publicar. [Leia mais](https://docs.decentraland.org/creator/content-creator-pt/scene-editor/comecar/about-editor).

Se você planeja editar o código da scene, também precisará instalar um dos seguintes:

* <img src="https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-72733d889cecbb74400d2c9292b43a212b253969%2Fvscode.png?alt=media" alt="VS Code" data-size="line"> **Visual Studio Code**: Faça o download dele [aqui](https://code.visualstudio.com/). Ele ajuda você a escrever código muito mais rápido e com menos erros. Um editor de código-fonte marca erros de sintaxe, completa automaticamente enquanto você escreve e até mostra sugestões inteligentes que dependem do contexto em que você está. Você também pode clicar em um objeto no código para ver a definição completa da sua class e quais atributos ela suporta.
* <img src="https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-2c1650edc9dbc881f65cd788d7c8ccfa2c27575c%2Fcursor-icon.png?alt=media" alt="Cursor" data-size="line"> **Cursor AI**: Faça o download dele [aqui](https://www.cursor.com/). Um poderoso editor de código integrado com IA. Ele permite que você escolha diferentes modelos de IA para ajudar a escrever código; todos eles são gratuitos. O assistente de IA não apenas completa automaticamente enquanto você escreve, como também permite pedir para refatorar uma grande base de código, escrever documentação e mais.

{% hint style="info" %}
**💡 Dica**: Você pode usar assistentes de IA como Cursor, OpenDCL ou Claude Code para criar scenes inteiras a partir de descrições em linguagem natural — não é necessária experiência com TypeScript. Veja [Vibe Coding com IA](https://github.com/decentraland/docs/blob/main/creator/sdk7/getting-started/vibe-coding.md) para começar.
{% endhint %}

## Idiomas e sintaxe suportados

O Decentraland usa [TypeScript (.ts)](https://www.typescriptlang.org/docs/handbook/jsx.html) como idioma padrão para escrever scenes.

TypeScript é um superconjunto de JavaScript, então se você está familiarizado com JavaScript vai ver que é quase a mesma coisa, mas TypeScript inclui declarações de tipo. Graças às declarações de tipo, é possível ter recursos como autocomplete e melhores dicas de debug; isso acelera o tempo de desenvolvimento e permite a criação de uma base de código mais sólida. Esses recursos são componentes essenciais para uma boa experiência de desenvolvimento.

Quando uma scene é construída, o código TypeScript que você escreveu é compilado para JavaScript minimizado, para deixá-lo mais leve. O código-fonte original em TypeScript nunca é enviado para os servers, apenas a versão JavaScript compilada.

### Outros idiomas

Você pode usar outra ferramenta ou linguagem em vez de TypeScript e compilá-la para JavaScript, desde que seus scripts compilados estejam contidos em um único arquivo JavaScript chamado *game.js*. Todas as declarações de tipo fornecidas são feitas em TypeScript, e outras linguagens e transpilers não são oficialmente suportados.

## Scenes

O conteúdo que você implanta no seu LAND é chamado de **scene**. Uma scene é um programa interativo que renderiza conteúdo 3D; isso pode ser um jogo, uma experiência interativa, uma galeria de arte, o que você quiser!

As scenes são implantadas em LAND virtual no Decentraland. LAND é um asset escasso e não fungível mantido em um smart contract Ethereum. Faça deploy em um único **parcel**, um lote de LAND de 16 metros por 16 metros, ou em múltiplos parcels adjacentes.

Quando os players visitam o Decentraland, eles baixam e renderizam o conteúdo de cada scene à medida que caminham pelo mapa. Eles descarregam as scenes à medida que se afastam delas.

Você também pode executar uma scene localmente na sua máquina executando uma preview a partir do CLI.

## Entities e Components

Scenes tridimensionais no Decentraland são baseadas em uma arquitetura [Entity-Component-System](https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system) , em que tudo em uma scene é uma *entity*. Entities têm *components*, cada component dá à entity à qual pertence propriedades específicas. Uma entity de porta provavelmente terá pelo menos um component Transform (que define position, rotation e scale) e outro para fornecer uma shape. Components são apenas um lugar para armazenar dados, eles não executam ações por conta própria.

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-ee29a02358e859a30079072e8da958b899aea659%2Fecs-components-new%20\(1\).png?alt=media)

```ts
export function main() {
	// Criar uma entity
	const door = engine.addEntity()

	// Dar à entity uma position por meio de um component transform
	Transform.create(door, {
		position: Vector3.create(5, 1, 5),
	})

	// Dar à entity uma shape visível por meio de um component GltfContainer
	GltfContainer.create(door)
}
```

Entities podem ser aninhadas dentro de outras entities para formar uma estrutura em árvore. Se você está familiarizado com desenvolvimento web, talvez ache útil pensar em entities como elementos em uma árvore DOM e em components como os atributos de cada um desses elementos.

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-8b6c8cd679f648a41400eb6f29889cd09ede8947%2Fecs-nested-entities-new%20\(1\).png?alt=media)

Entities são um conceito abstrato. Uma entity é apenas um id, usado como referência para agrupar diferentes components.

Veja [Entities e components](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/arquitetura/entities-components) para uma análise detalhada desses dois conceitos e de como eles são usados pelas scenes do Decentraland.

### Custom components

O conjunto padrão de components (como `Transform`, `GltfContainer`, `Material`, etc.) é interpretado pelo engine e tem consequências diretas sobre como a entity ficará, sua position, se emite sons, etc.

Você também pode definir *custom components* para armazenar dados que possam ser úteis para as mecânicas da sua scene. O engine não saberá interpretar o que os valores desses components significam, eles não terão consequências diretas sobre como a scene é renderizada. No entanto, você pode escrever lógica no código da sua scene para monitorar esses valores e responder a eles. Por exemplo, você pode definir um component customizado "doorState" para acompanhar o estado aberto/fechado da porta. Nesse caso, o component não é mais do que um lugar para armazenar um valor que acompanha esse estado. Para ver a porta abrir e fechar na sua scene, você precisa então implementar separadamente a lógica que usa esses valores para afetar a rotation da porta, um valor do `Transform` component que o engine sabe interpretar.

Veja [Custom Components](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/arquitetura/custom-components) para mais informações.

### Buscar entities pelo nome

As entities adicionadas ao arrastar e soltar no Scene Editor no Creator Hub também podem ser acessadas via código para editá-las mais e adicionar comportamento.

Use `engine.getEntityOrNullByName()` para buscar uma entity, passando o nome atribuído à entity na UI do Scene Editor. Cada uma deve ter um nome único.

```ts
function main() {
	const door = engine.getEntityOrNullByName('door3')
}
```

Depois você pode fazer qualquer coisa que quiser com essa entity, como adicionar novos components, modificar seus components existentes, duplicá-la ou excluí-la.

Veja [Obter entity pelo nome](https://docs.decentraland.org/creator/content-creator-pt/arquitetura/entities-components#get-an-entity-by-name) para mais informações.

Se a entity for um [Smart item](https://docs.decentraland.org/creator/content-creator-pt/scene-editor/interatividade/smart-items), você também pode chamar suas **Actions** ou se inscrever em seus **Triggers** via código. Veja [Reference Items](https://docs.decentraland.org/creator/content-creator-pt/scene-editor/expandir-com-codigo/reference-items).

## Systems

Entities e components são lugares para armazenar informações sobre os objetos em uma scene. *Systems* contêm funções que alteram as informações armazenadas nos components ao longo do tempo.

Systems são onde implementamos a lógica do jogo; elas executam as ações que precisam ser atualizadas ou verificadas periodicamente a cada tick do game loop.

Um system é uma função pura e simples que é chamada uma vez a cada tick (até 30 vezes por segundo), seguindo o [*padrão de atualização*](http://gameprogrammingpatterns.com/update-method.html).

```ts
// System básico
function mySystem() {
	console.log('my system is running')
}

engine.addSystem(mySystem)

// System com dt
function mySystemDT(dt: number) {
	console.log('time since last frame:  ', dt)
}

engine.addSystem(mySystemDT)
```

Uma única scene pode ter 0 ou muitos systems executando ao mesmo tempo. Os systems podem ser ativados ou desativados em diferentes momentos durante a duração da scene. Em geral, é uma boa prática manter comportamentos independentes em systems separados.

Veja [Systems](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/arquitetura/systems) para mais detalhes sobre como os systems são usados em uma scene.

### O game loop

O [game loop](http://gameprogrammingpatterns.com/game-loop.html) é a base do código de uma scene no Decentraland. Ele percorre parte do código em intervalos regulares e faz o seguinte:

* Ouvir a entrada do player
* Atualizar a scene
* Renderizar novamente a scene

Na maioria dos programas de software tradicionais, todos os eventos são acionados diretamente pelas ações do player. Nada no estado do programa mudará até que o player clique em um botão, abra um menu etc.

Mas ambientes interativos e jogos são diferentes disso. Nem todas as mudanças na scene são necessariamente causadas pelas ações de um player. Sua scene pode ter objetos animados que se movem sozinhos ou até personagens não jogáveis que têm sua própria IA. Algumas ações do player também podem levar vários ticks para serem concluídas; por exemplo, se a abertura de uma porta precisar levar um segundo inteiro, a rotation da porta deve ser atualizada incrementalmente cerca de 30 vezes enquanto ela se move.

Chamamos cada iteração do loop de *tick*. Scenes do Decentraland são renderizadas a 30 ticks por segundo, sempre que possível. Se a máquina estiver com dificuldade para renderizar cada tick, isso pode resultar em atualizações menos frequentes.

Em cada tick, a scene é atualizada; depois a scene é renderizada novamente, com base nos valores atualizados.

Nas scenes do Decentraland, não há um game loop explicitamente declarado, mas sim os [Systems](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/arquitetura/systems) da scene compõem o game loop.

A compilação e a renderização da scene são realizadas no backend; você não precisa lidar com isso enquanto desenvolve sua scene.

## Consultando components

Você pode [consultar components](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/arquitetura/querying-components) com o método `engine.getEntitiesWith(...components)` para acompanhar todas as entities na scene que têm certos components.

Muitas vezes faz sentido consultar components dentro de um [system](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/arquitetura/systems), e então percorrer cada uma das entities retornadas e executar o mesmo conjunto de ações em cada uma.

Se você tentar iterar sobre todas as entities da scene a cada tick do game loop, isso pode ter um custo significativo de performance. Ao se referir apenas às entities retornadas por uma consulta, você garante que está lidando apenas com aquelas que são relevantes.

```ts
// Definir um System
function boxHeightSystem(dt: number) {
	// consulta por entities que incluem os components MeshRenderer e Transform
	for (const [entity] of engine.getEntitiesWith(MeshRenderer, Transform)) {
		const transform = Transform.get(entity)
		console.log('a box is at height:  ', transform.position.y)
	}
}

// Adicionar o system ao engine
engine.addSystem(rotationSystem)
```

## Ciclo de vida da scene

Se você começar a escrever linhas soltas de código diretamente em `index.ts`, seu código pode estar sem algum contexto importante. Por exemplo, você não terá informações sobre a player entity ou sobre entities que foram adicionadas via arrastar e soltar no Creator Hub. No momento em que suas linhas de código forem lidas, essas coisas ainda não terão sido carregadas.

Para evitar esse cenário, é sempre recomendado escrever o código de carregamento inicial da sua scene usando a `main()` function (no `index.ts` file) como ponto de entrada. Essa function é executada somente depois que todo o contexto inicial da scene já estiver carregado; isso inclui qualquer coisa adicionada pela UI do Scene Editor.

Você pode escrever seu código fora da `main()` function quando:

* O código é chamado indiretamente por `main()`
* O código define um system, ou adiciona um system ao engine
* O código está dentro de uma [async function](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/padroes-de-programacao/async-functions)

{% hint style="warning" %}
**📔 Nota**: No momento em que o código dentro de uma async function ou de um system é executado pela primeira vez, tudo na scene já está devidamente inicializado.

[Custom Component](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/arquitetura/custom-components) as definições são uma exceção; elas devem sempre ser escritas fora da `main()` function, em um arquivo separado. Elas precisam ser interpretadas antes que `main()` seja executado.
{% endhint %}

## Mutabilidade

Você pode escolher lidar com versões mutáveis ou imutáveis (somente leitura) de um component. A `.get()` function em um component retorna uma versão imutável do component. Você só pode ler seus valores, mas não pode alterar nenhuma de suas propriedades.

O `.getMutable()` function retorna uma representação do component que permite alterar seus valores. Use versões mutáveis apenas quando planejar fazer alterações em um component. Lidar com versões imutáveis de components resulta em um enorme ganho de performance.

```ts
// buscar uma versão imutável (somente leitura)
const immutableTransform = Transform.get(myEntity)

// o seguinte NÃO funciona:
// 	immutableTransform.position.y = 2

const mutableTransform = Transform.getMutable(myEntity)

// o seguinte ALTERA a position da entity
mutableTransform.position.y = 2
```

Veja [dados mutáveis](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/padroes-de-programacao/mutable-data) para mais detalhes.

## Juntando tudo

O *engine* é o que fica entre *entities*, e *components* de um lado e *systems* do outro.

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-de62a18d51b28bebd55c86e8ef5a8a783467ae62%2Fecs-big-picture%20\(1\).png?alt=media)

Todos os valores armazenados nos components da scene representam o estado da scene naquele ponto no tempo. A cada tick do game loop, o engine executa as funções de cada um dos systems para atualizar os valores armazenados nos components.

Depois que todos os systems são executados, os components em cada entity terão novos valores. Quando o engine renderizar a scene, ele usará esses novos valores atualizados e os players verão as entities mudarem para corresponder aos seus novos estados.

```ts
export function main() {
	// Criar uma entity
	const cube = engine.addEntity()

	// Dar à entity uma position por meio de um component transform
	Transform.create(cube, {
		position: Vector3.create(5, 1, 5),
	})

	// Dar à entity uma shape visível por meio de um component MeshRenderer
	MeshRenderer.setBox(cube)
}

// Definir um System
function rotationSystem(dt: number) {
	// consulta por entities que incluem os components MeshRenderer e Transform
	for (const [entity] of engine.getEntitiesWith(MeshRenderer, Transform)) {
		const transform = Transform.getMutable(entity)
		transform.rotation = Quaternion.multiply(
			transform.rotation,
			Quaternion.fromAngleAxis(dt * 10, Vector3.Up())
		)
	}
}

// Adicionar o system ao engine
engine.addSystem(rotationSystem)
```

No exemplo acima, uma `cube` entity e um `rotationSystem` system são adicionados ao engine. A `cube` entity tem um `Transform`, e um `MeshRenderer` component. Em cada tick do game loop, o `rotationSystem` system é chamado, e ele altera os valores de rotation no `Transform` component da `cube` entity.

Observe que a maior parte do código acima é executada apenas uma vez, ao carregar a scene. A exceção é o `rotationSystem` system, que é chamado a cada tick do game loop.

## Desacoplamento da Scene

Suas scenes não são executadas no mesmo contexto do engine (também conhecido como a main thread). Criamos o SDK de forma totalmente desacoplada do rendering engine. Projetamos isso dessa maneira tanto por segurança quanto por performance.

Por causa desse desacoplamento, o código da sua scene não tem acesso ao DOM nem ao objeto `window` , então você não pode acessar dados como o navegador ou a localização geográfica do player.

O desacoplamento funciona usando o protocolo RPC; esse protocolo atribui uma pequena parte do client apenas para renderizar a scene e controlar eventos.

Também abstraímos o protocolo de comunicação. Isso nos permite executar as scenes localmente em um WebWorker.

Não queremos que developers interfiram com os detalhes internos do engine nem sequer precisem saber o que existe dentro dele. Precisamos garantir uma experiência consistente para os players em todo o mapa do Decentraland, e é mais provável que erros aconteçam nesse nível "baixo".

Esse desacoplamento também é importante para impedir que scenes vizinhas interfiram na experiência dos players enquanto eles estão na scene de outra pessoa. Um player pode ter várias scenes próximas carregadas ao mesmo tempo, cada uma executando seu próprio código. Algumas ações (como abrir links externos ou mover o player) só são permitidas quando o player está em pé naquela scene específica, e não se a scene estiver carregada mas o player estiver fora dela.

## Tree Shaking

Ao converter o código-fonte em TypeScript para o código compilado em JavaScript minimizado, o processo executa [tree shaking](https://en.wikipedia.org/wiki/Tree_shaking) para garantir que apenas as partes do código que realmente estão sendo usadas sejam convertidas. Isso ajuda a manter o código final da scene o mais leve possível. Isso é especialmente útil ao usar bibliotecas externas, já que muitas vezes essas bibliotecas incluem muita funcionalidade que não é usada e que, de outra forma, deixaria a scene mais pesada.

Como consequência do tree shaking, qualquer código que você queira que a sua scene execute precisa ser referenciado de uma forma ou de outra pelos pontos de entrada do seu código: a `main()` function em `index.ts`. Systems também podem, alternativamente, ser adicionados ao engine no `index.ts` file, sem referenciar `main()`. Qualquer código que não seja explicitamente ou indiretamente referenciado por esses files não fará parte da scene.

Por exemplo, suponha que você tenha um file chamado `extraContent.ts` com o seguinte conteúdo; a entity não será renderizada e o system não começará a ser executado:

```ts
// extraContent.ts

const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: { x: 8, y: 0, z: 8 },
})
MeshRenderer.setBox(myEntity)

function mySystem(dt: number) {
	console.log('system running')
}

engine.addSystem(mySystem)
```

Para fazê-lo executar como parte da sua scene, você pode referenciar de `index.ts` da seguinte maneira:

```ts
// em extraContent.ts

export function addEntities() {
	const myEntity = engine.addEntity()
	Transform.create(myEntity, {
		position: { x: 8, y: 0, z: 8 },
	})
	MeshRenderer.setBox(myEntity)
}

export function mySystem(dt: number) {
	console.log('system running')
}

/////////////////////////////

// em index.ts

import { addEntities, mySystem } from '/extraContent'

export function main() {
	addEntities()
}

engine.addSystem(mySystem)
```

A exceção a essa regra são as definições de custom components. Elas não devem ser acessadas via o `main()` function entry point, pois precisam ser interpretadas antes de tudo o mais.

## Imports

Todas as functions, objects, components e outros elementos usados pela scene devem ser importados para cada file para poderem ser usados. Isso é uma consequência do [tree-shaking](#tree-shaking), pois isso evita empacotar o SDK inteiro e, em vez disso, inclui apenas as partes que a scene usa.

Os snippets ao longo da documentação omitem as linhas de import no início de cada file para mantê-los limpos, mas, para que funcionem, você precisa adicioná-las à scene.

Ao usar o VS Studio Code para escrever suas scenes, as opções inteligentes de autocompletar devem cuidar de tratar os imports para você enquanto você escreve, sem que precise estar ciente disso.

Quando você cola um snippet na sua scene, porém, provavelmente verá alguns elementos marcados em vermelho, que não foram importados para aquele file. Para corrigir isso:

* Clique em cada palavra sublinhada
* Clique no ícone de lâmpada à esquerda da linha
* Selecione **Add Import From**
* Uma linha de import aparece no início do file.

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-7a9b5a49dd3ff062e5609e6205f2a41c55d9ea55%2Fimports.png?alt=media)

Se houver muitas coisas diferentes para importar, você também pode selecionar **Add all missing imports** no mesmo dropdown.

Observe que os imports devem ser feitos em cada file em que um elemento é usado.

O VS Studio Code deve conseguir resolver os caminhos corretos para os seus imports sozinho. Se, por qualquer motivo, ele estiver com dificuldade para fazer isso, um truque é colar as seguintes instruções de import vazias no início do seu file. O VS Studio deverá conseguir seguir a partir daí.

```ts
import {} from '@dcl/sdk/ecs'
import {} from '@dcl/sdk/math'
```

## Versões do SDK

Ao desenvolver uma nova scene, você usa a versão `@latest` estável do SDK por padrão.

Você pode instalar a versão `@next` do SDK se quiser aproveitar ou visualizar recursos futuros que ainda não entraram na versão estável mais recente.

Para fazer isso, abra o file `package.json` da sua scene e altere as seguintes linhas:

```json
  "devDependencies": {
    "@dcl/js-runtime": "next",
    "@dcl/sdk": "next"
  },
```

Em seguida, execute o seguinte comando na pasta do projeto da sua scene:

```
npm i
```

Veja [gerenciar dependências](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/libraries/manage-dependencies) para mais detalhes.

{% hint style="warning" %}
**📔 Nota**: Tenha em mente que a versão @next pode apresentar problemas de tempos em tempos. A sintaxe e o nome de novos recursos podem mudar antes de serem lançados em uma versão estável.
{% endhint %}
