BTYN

Resolvendo problemas

← Índice da documentação

Sintomas na ordem em que as pessoas realmente esbarram neles.

Infinite yield possible on 'ReplicatedStorage:WaitForChild("BTYN")'

O runtime não está no jogo, ou não está onde o código gerado procura.

Os módulos gerados chegam ao runtime pela chave de config runtime, que por padrão aponta para ReplicatedStorage.BTYN. Alguma coisa precisa colocar src/ lá. Neste repositório, isso é o default.project.json:

{
  "name": "BTYN",
  "tree": {
    "$className": "DataModel",
    "ReplicatedStorage": {
      "BTYN": { "$path": "src" }
    }
  }
}

O que produz:

ReplicatedStorage
└── BTYN              (ModuleScript, de src/init.luau)
    ├── Stream
    ├── Transport
    ├── Channel
    ├── RateLimiter
    └── Request

Se o seu jogo guarda pacotes em outro lugar, deixe onde estão e aponte o schema para lá:

config {
    runtime = "game.ReplicatedStorage.Packages.BTYN"
}

O valor é uma expressão Luau colada dentro de um require nos dois lados, então qualquer coisa que resolva funciona.

attempt to call a nil value em Net.Alguma.fire ou .on

Você está do lado errado do from.

A API gerada é assimétrica de propósito — cada lado recebe só a metade da qual participa, então chamar a errada falha alto em vez de silenciosamente não fazer nada.

Declaração Cliente recebe Servidor recebe
event X from client .fire .on
event X from server .on .to .all .list .except
request X from client .call .try .on
request X from server .on .call .try
channel X .on .onRemove .get .of

Se a intenção era o outro sentido, mude o from no schema e recompile.

btyn: unknown opcode 7 chegando no onAbuse

Os dois lados foram gerados a partir de versões diferentes do schema.

Opcodes são atribuídos por nome de pacote em ordem alfabética, então adicionar um pacote chamado Aaa renumera tudo depois dele. Isso não é problema — os dois arquivos saem da mesma execução — mas significa que os módulos do servidor e do cliente precisam ir juntos. Um servidor atualizado sem o cliente, ou um cliente em cache de um build anterior, vão discordar sobre o que é o opcode 7.

Recompile e publique os dois. Se isso aparecer em produção, verifique se o que copia os arquivos gerados não está tratando os dois como artefatos independentes.

Um channel não envia nada

A audiência dele está vazia, que é o padrão.

local health = Net.Health.of(id)
health.set({ hp = 100 })     -- não vai a lugar nenhum: ninguém está escutando

Um channel replica para os jogadores que você nomear e para mais ninguém — essa é a gestão de interesse, e o padrão vazio é o seguro. Diga quem se importa:

health.audience(playersWithin(120, position))

Jogadores que entram na audiência recebem um keyframe automaticamente; os que saem são avisados para descartar a entidade.

Nada é enviado, e manual = true

Com manual ligado, nada é enviado sozinho. Chame:

Net.flush()

Os batches acumulam até você chamar. Se você ligou manual para controlar quando o tráfego sai, garanta que todo caminho capaz de enfileirar um pacote chegue a um flush — inclusive os que só rodam de vez em quando.

btyn: 'hp' is not a field of this channel

Um typo no set, ou um campo que pertence a outro channel. O erro nomeia a chave que recebeu; o bloco channel do schema é a lista do que é válido.

Isso é verificado em runtime e não pelo sistema de tipos porque set recebe uma tabela parcial, e Luau não consegue expressar “algum subconjunto destas chaves” com precisão suficiente para pegar no ponto da chamada.

Um valor chega truncado ou limitado

Ou o tipo declarado é estreito demais, ou as checagens de escrita estão desligadas.

Escrever 300 num u8 trunca para 44. Com write_checks ligado — o padrão — você recebe um erro no ponto da chamada:

btyn: hp must be between 0 and 255, got 300

Se você desligou write_checks para builds de produção e algo está chegando errado, ligue de volta num build de desenvolvimento e reproduza lá. O lado que recebe sempre valida, independente dessa configuração, então o pacote seria rejeitado no caminho em vez de entregue errado — confira o onAbuse.

Tipos quantizados perdem precisão por definição: fixed(-1, 1, 2) tem ~65536 passos na faixa, angle resolve cerca de 0,0001 radiano, e unit é normalizado na chegada. Se você precisa do float exato de volta, use f32.

Um request sempre estoura o prazo

Três causas, em ordem de probabilidade.

O respondedor retornou nil. Esse é o caminho de rejeição, e chega a quem chamou como falha e não como timeout — call lança erro, try retorna false. Se a intenção era responder, retorne uma tabela.

O respondedor lançou erro. O handler dele roda dentro de um pcall, então um erro vira rejeição. Procure o erro em si na saída do servidor.

Nada está sendo enviado. Veja manual acima. Requests andam no batch comum, então um request enviado sem flush nunca sai.

O prazo é request_timeout, 10 segundos por padrão. Uma resposta que chega depois disso é descartada em vez de entregue atrasada.

exceeded its rate limit em tráfego que parece razoável

O bucket guarda um segundo de orçamento. rate 10 permite uma rajada de dez e reabastece a dez por segundo — então dez chamadas num frame estão de bom tamanho, e vinte não estão, mesmo que vinte ao longo de dois segundos estivessem.

Se uma rajada legítima é maior que a taxa sustentada, aumente o rate para cobrir a rajada, não a média. Se a rajada é um laço, pense se aquilo não deveria ser um pacote carregando um array em vez de muitos pacotes.

batch carries more than 256 packets

Um cliente enviou mais pacotes num frame do que o teto permite. De um cliente comum isso não acontece: um frame movimentado de input são alguns poucos pacotes.

Se você tem um caso legítimo — um jogo de construção enviando uma edição grande como muitos pacotes pequenos — aumente o teto:

config { max_packets_per_batch = 1024 }

Prefira reestruturar antes. Um pacote carregando [Edit; 512] custa uma fração de 512 pacotes carregando uma edição cada, tanto em bytes quanto em CPU.

Um campo Instance chega nil

O receptor ainda não recebeu aquele objeto por streaming, ou ele foi destruído entre o envio e a chegada.

Isso é inerente a referências de Instance, não algo que o BTYN possa resolver — é por isso que entity existe. Um id u32 que o seu jogo controla sempre chega, significa a mesma coisa nos dois lados, e custa quatro bytes sem array lateral. Prefira ele.

A saída typescript quebra o build

error: `typescript` output is not implemented yet

Não está implementado, e falhar é proposital — aceitar a flag e emitir nada em silêncio deixaria um build roblox-ts descobrir as declarações faltando muito depois. Remova a chave. O Luau gerado é totalmente tipado e usável a partir de Luau hoje.

O Studio não vê a recompilação

O Rojo sincroniza os arquivos que está observando. Verifique se os caminhos do seu bloco config ficam dentro da árvore que o default.project.json mapeia, e se o rojo serve está rodando. Compilar escreve os arquivos independente de alguém estar sincronizando.

Rodar o compilador em --watch ao lado do rojo serve é exatamente o fluxo para o qual isso foi desenhado — veja compilador e editor.

Outra coisa

O teste de integração no Studio exercita todo tipo de pacote contra remotes reais e imprime a contagem de bytes no caminho. Rodá-lo num place de rascunho costuma ser mais rápido do que instrumentar o seu próprio jogo, e os números batem com o que o --check prevê.


Próximo: compilador e editor · API · segurança