Protocolo de depuração
O Protocolo de Depuração Remota (Remote Debug Protocol) é um servidor JSON-RPC 2.0 leve embutido em todo build de jogo Turian que vincula o módulo debug. Ele expõe a Camada de Introspecção em Runtime via TCP para que ferramentas externas — a CLI, o servidor MCP e scripts personalizados — possam consultar e (opcionalmente) modificar o estado ativo do engine.
Formato de transmissão
Um objeto JSON por linha (terminado em \n). Sem cabeçalhos Content-Length.
Requisição:
{"jsonrpc":"2.0","id":1,"method":"entity.inspect","params":{"name":"Player"}}
Resposta de sucesso:
{"jsonrpc":"2.0","id":1,"result":{...}}
Resposta de erro:
{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"Entity not found"}}
Métodos
Cena
| Método | Parâmetros | Descrição |
|---|---|---|
scene.list |
— | Todas as cenas carregadas |
scene.inspect |
name? |
Entidades em uma cena (cena ativa se omitido) |
Entidade
| Método | Parâmetros | Descrição |
|---|---|---|
entity.find |
name?, component? |
Filtra entidades por nome ou tipo de componente |
entity.inspect |
name ou index |
Detalhes de Transform + componentes |
Componente
| Método | Parâmetros | Descrição |
|---|---|---|
component.get |
entity, component |
Valores dos campos do componente |
Assets
| Método | Parâmetros | Descrição |
|---|---|---|
asset.list |
— | Todos os assets do projeto (guid, path, type) |
asset.inspect |
guid |
guid / path / type de um asset específico |
Diagnósticos
| Método | Parâmetros | Descrição |
|---|---|---|
snapshot |
— | Snapshot completo da cena (todas as entidades + métricas) |
schema |
— | Catálogo de tipos de componentes nativos |
metrics |
— | Contadores de desempenho de runtime |
profiler.capture |
— | Frame mais recente do profiler (contadores + zonas) |
memory |
— | memory_bytes + allocation_count |
errors |
— | Entradas recentes de warn/err de std.log (as mais recentes primeiro) |
ping |
— | Teste de funcionamento (health check); retorna "pong" |
auth |
token |
Autenticar (necessário quando o servidor possui um token configurado) |
Mutações (apenas no modo de leitura-escrita)
Métodos de mutação retornam { "ok": bool, "message": string }. Eles requerem que o servidor tenha sido iniciado no modo de leitura-escrita (allow_write / CLI --rw); caso contrário, retornam READONLY. No Studio, toda mutação é roteada pela pilha de desfazer (undo stack) do editor, de modo que edições por IA/CLI podem ser desfeitas e são consistentes com a UI.
| Método | Parâmetros | Descrição |
|---|---|---|
component.set |
entity, component, field, value |
Define um campo de componente |
transform.set |
entity, channel, value ([x,y,z]) |
Define posição/rotação/escala |
entity.spawn |
name |
Cria uma nova entidade vazia |
entity.destroy |
entity |
Remove uma entidade |
asset.reload |
guid |
Executa o hot-reload de um asset |
Sessões e eventos
| Método | Parâmetros | Descrição |
|---|---|---|
session.readonly |
— | Descarta os direitos de escrita desta conexão (não é possível reatribuir) |
subscribe |
event (ou "*") |
Inscreve-se em eventos de runtime |
unsubscribe |
event (ou "*") |
Cancela o recebimento de um evento |
Clientes inscritos recebem notificações JSON-RPC (sem id) à medida que os eventos são disparados:
| Evento | Quando |
|---|---|
entity.created |
Uma entidade foi criada |
entity.destroyed |
Uma entidade foi removida |
scene.loaded |
Uma cena se tornou ativa / concluiu o carregamento |
scene.unloaded |
Uma cena foi descarregada |
resource.reloaded |
Um asset sofreu hot-reload |
fps.changed |
O valor inteiro da faixa de FPS (FPS bucket) mudou |
No Turian Studio, scene.loaded / scene.unloaded são disparados conforme você abre, altera ou fecha as abas de cenas, e fps.changed é derivado do tempo de frame do próprio editor — portanto ele oscila enquanto você edita, e não apenas no modo Play.
Códigos de erro
| Código | Nome | Significado |
|---|---|---|
-32700 |
PARSE_ERROR |
JSON inválido |
-32600 |
INVALID_REQUEST |
Campos obrigatórios ausentes |
-32601 |
METHOD_NOT_FOUND |
Método desconhecido |
-32602 |
INVALID_PARAMS |
Parâmetros ausentes ou incorretos |
-32603 |
INTERNAL_ERROR |
Erro de serialização ou falha interna |
-32000 |
NOT_FOUND |
Entidade/cena/componente não encontrado |
-32001 |
READONLY |
Tentativa de mutação em uma sessão somente leitura |
-32002 |
RATE_LIMITED |
Limite de requisições por conexão excedido (rate limit) |
Iniciando o servidor de depuração
const debug = @import("debug");
var srv = debug.Server.init(allocator, .{
.port = 7777,
.localhost_only = true,
.auth_token = "", // vazio = sem autenticação
.allow_write = false, // true para permitir mutações/escrita
.max_clients = 8,
.rate_limit_per_sec = 0, // 0 = ilimitado
.max_outbound_queue = 2048, // limite de backlog de eventos por cliente
.max_outbound_bytes = 16 << 20,
.max_inbound_queue = 4096, // requisições pendentes entre todos os clientes
});
defer srv.deinit(io);
try srv.start(io); // apenas gera o loop de aceite (accept loop)
// Uma vez por frame, na thread principal:
srv.pump(world, applier); // executa leituras/escritas enfileiradas contra o estado ativo
O loop de aceite (accept loop) roda em uma thread de segundo plano, mas cada requisição é executada na thread principal dentro de pump — assim, as leituras são livres de condições de corrida (race-free) e as escritas podem modificar o estado ativo do engine sem bloqueios (locking). Múltiplos clientes são suportados simultaneamente. As mutações são aplicadas por meio de um MutationApplier fornecido pelo host; os eventos são emitidos com srv.emit(...). Chame srv.stop(io) para desligar de forma limpa.
Respostas grandes e contrapressão (backpressure)
Um snapshot completo de uma cena grande é serializado bem além de 64 KiB. As mensagens são delimitadas por quebras de linha, mas não são limitadas a 64 KiB — tanto o cliente quanto o servidor aumentam o buffer de leitura na heap até um teto máximo de 16 MiB, de modo que respostas grandes chegam intactas em vez de falharem.
A fila de eventos de saída de cada conexão é delimitada (max_outbound_queue / max_outbound_bytes). Um assinante que parar de ler terá seus eventos mais antigos descartados (eventos são passíveis de perda por natureza) em vez de aumentar a memória do servidor sem limites; um cliente que ficar muito atrasado para receber uma resposta pendente será desconectado. A fila de entrada compartilhada é delimitada por max_inbound_queue; uma vez cheia, novas requisições são rejeitadas com um erro claro de "servidor ocupado".
Exposição de rede. Definir
localhost_only = falsevincula a porta a0.0.0.0e não há TLS — oauth_token(e todo o tráfego) é enviado de forma aberta (em texto limpo) e é a única barreira de proteção. Use isso apenas em uma rede LAN confiável por trás de um firewall/VPN; nunca exponha o servidor de depuração à internet pública.
Utilizando a partir do turian-cli
turian-cli debug connect
turian-cli debug scenes
turian-cli debug inspect Player
turian-cli debug snapshot
turian-cli debug schema
turian-cli debug metrics
turian-cli debug assets
turian-cli debug errors
# mutações (o servidor precisa estar em modo leitura-escrita):
turian-cli debug set Player Light intensity 2.5
turian-cli debug spawn Crate
turian-cli debug destroy Crate
# eventos e sessões:
turian-cli debug watch entity.created fps.changed
turian-cli debug record session.jsonl
turian-cli debug replay session.jsonl
Consulte turian-cli debug --help para ver a lista completa de subcomandos.
Porta padrão
7777. Substitua por --port tanto no servidor quanto no cliente.