01Contexto
Eu estava viciado em Baldur’s Gate 3 e tinha uma viagem chegando. O plano parecia simples: continuar jogando no Xbox Cloud Gaming pelo MacBook, usando os Joy-Con do meu Nintendo Switch 2 como controle.
Não foi. Os Joy-Con antigos apareciam como controles Bluetooth comuns. Os do Switch 2 usam um protocolo Bluetooth LE proprietário, com pareamento próprio: o macOS não consegue parear, e mesmo conectado não sabe o que fazer com os dados. A solução mais próxima que encontrei foi o JoyCon2Mac, um app nativo que já fazia o protocolo funcionar. Só que ele depende de um driver de sistema que, sem autorização da Apple, só carrega com o System Integrity Protection e o AMFI desligados. Reiniciar em modo de recuperação para jogar um jogo pareceu um pouco demais.
O Chrome conversa com dispositivos Bluetooth LE pelo Web Bluetooth, e os jogos leem controles pela Gamepad API. O Joy2Cloud fica no meio: conecta os dois Joy-Con a partir de uma aba de controle, decodifica as leituras e faz a página do jogo enxergar um controle de Xbox comum, com botões, analógicos, calibração e vibração. Desenhei, construí, testei e publiquei sozinho, e o código é aberto, sob licença MIT.
Nada disso existiria sem quem fez a engenharia reversa desses controles: o switch2_controller_research, do ndeadly, pelo protocolo, e o JoyCon2Mac, pela codificação da vibração e pela ideia original.
Eu só queria jogar Baldur’s Gate 3 nas férias. Acabei publicando uma extensão para o Chrome.
02Restrições
Sistema operacional
Nada instalado no macOS e nada desligado. A solução nativa que existia exigia desativar o System Integrity Protection e o AMFI.
Protocolo
Os Joy-Con do Switch 2 usam um protocolo Bluetooth LE proprietário, não o HID padrão, e recusam o pareamento normal que o computador tenta.
Navegador
Service workers de extensão não acessam Web Bluetooth, e o Chrome limita os timers de abas em segundo plano a um por segundo.
Time
Só eu, construindo para jogar numa viagem.
03Arquitetura
Entrada
Vibração
04Decisões
D-01
A ponte fica no navegador, não no sistema
Por quê
O cloud gaming já roda no navegador. O Web Bluetooth lê os controles e a Gamepad API é como o jogo os lê de volta, então o caminho inteiro cabe numa extensão.
Custo
Só Chrome e outros navegadores Chromium (Safari e Firefox não têm Web Bluetooth), e só serve para jogos que rodam numa aba.
D-02
O jogo vê um controle de Xbox virtual
Por quê
Um script na página sobrescreve o navigator.getGamepads() com o mapeamento padrão do Xbox e um atuador de vibração. Todo serviço lê controles assim, então não há código específico por site.
Custo
O script precisa carregar antes da página, e cada serviço ainda precisa ser testado na mão.
D-03
Uma aba de controle cuida da conexão Bluetooth
Por quê
Service workers não acessam Web Bluetooth e o Chrome ancora o seletor de dispositivos na barra de endereço, então uma aba comum segura os dois Joy-Con e junta os dois num único controle.
Custo
A aba de controle precisa ficar aberta durante o jogo, mesmo em segundo plano.
D-04
Vibração enviada a cada leitura do controle, não por timer
Por quê
Uma aba em segundo plano só ganha um tick de timer por segundo. As leituras dos Joy-Con não param de chegar, então ir junto com elas mantém a vibração em sincronia com o jogo.
Custo
O formato de HD rumble do Switch 2 não é totalmente documentado, então a vibração é uma aproximação do que o console faz.
05Resultado
0
Drivers instalados ou proteções desativadas
1
Permissão pedida ao navegador (storage)
4
Idiomas na interface
90s
Janela de reconexão automática
06O que eu mudaria
Antes de publicar, só testei no Xbox Cloud Gaming. GeForce NOW, Amazon Luna e Boosteroid leem controles pela mesma Gamepad API, então devem funcionar, mas "devem" não é teste. Da próxima vez, eu publicaria com uma matriz de compatibilidade de verdade e um jeito fácil de as pessoas reportarem resultados desde o primeiro dia.