Tabuleiro digital de Magic: The Gathering para jogar com amigos em rede local.
Aplicativo desktop multiplataforma que simula uma mesa de MTG em tempo real — com cartas do Scryfall, board 2D e 3D, contadores, zonas completas e suporte a decks importados. Tudo rodando P2P na sua rede, sem servidor central.
- Sobre o projeto
- Funcionalidades
- Stack técnica
- Multiplayer em rede local
- Instalação
- Desenvolvimento
- Estrutura do projeto
- Atalhos e controles
- FAQ
- Roadmap
- Contribuindo
- Licença
Sekiryu é um tabuleiro virtual de Magic: The Gathering pensado para reproduzir a experiência de jogar na mesa, sem as limitações que plataformas oficiais impõem:
- ✅ Qualquer formato, qualquer deck — você constrói e importa, nós renderizamos
- ✅ Controle total das cartas — move, tapa, gira, cria tokens, manipula zonas livremente
- ✅ Zero dependência de servidor — nada é enviado pra nuvem; partidas são P2P
- ✅ Funciona offline (depois do primeiro fetch de cartas do Scryfall)
- ✅ Open source e gratuito
O projeto é ideal para jogar Commander, Cube, formatos caseiros ou simplesmente testar decks com amigos sem depender de MTGO/Arena.
- Dois modos de visualização: Top-Down (2D) e Isométrico (3D via Three.js), com alternância a qualquer momento
- Zonas completas: battlefield, mão, biblioteca, graveyard, exílio, stack e command zone
- Drag-and-drop fluido entre todas as zonas, inclusive entre 2D ↔ 3D
- Cartas de duas faces (DFC) com botão de transformar e preview das duas faces
- Tokens e copies criados dinamicamente
- Tap/untap, flip, rotação com animações suaves
- Contador de vida, veneno, energia, experiência, tickets e contadores genéricos
- Commander damage rastreado por oponente
- Mulligan com opções gratuito e −1 (London Mulligan), sincronizado em multiplayer
- Log de ações da partida
- Deck Editor com busca em tempo real
- Cache local do Scryfall em SQLite — primeira busca baixa, depois tudo fica offline
- Importação de decklists (formato texto,
.sekiryubackup com merge inteligente) - Cache de imagens em disco com fallback automático para URL do Scryfall
- Sleeves customizáveis (corte e upload de imagens próprias)
- Commander destacado no Party Frame com thumbnail
- P2P via WebSocket — um jogador hospeda, os outros conectam
- Descoberta automática de salas na rede via UDP broadcast
- Spectate 3D — clique em um jogador no Party Frame e a câmera anima pra perspectiva dele (pressione
Rpra voltar) - Informação oculta preservada — sua mão e biblioteca só você vê; oponentes veem contagem
- Avatares e nicknames customizáveis por perfil
- Temas claro e escuro (light warm parchment / dark minimalist gold)
- Vários board backgrounds pra personalizar sua mesa
- Titlebar customizada (botões minimizar/maximizar/fechar integrados ao design no Windows)
- Minimalismo em toda UI — bordas sutis, cores contidas, ouro como único acento
| Camada | Tecnologia |
|---|---|
| Shell desktop | Tauri 2 (Rust + webview nativo) |
| Frontend | React 19 + TypeScript 5 + Vite 7 |
| 3D | Three.js + @react-three/fiber + drei |
| Estado (frontend) | Zustand |
| Backend | Rust + Tokio |
| Persistência | SQLite via rusqlite |
| Rede | tokio-tungstenite (WebSocket) + tokio::net::UdpSocket (discovery) |
| Dados de cartas | Scryfall API |
| Package manager | Bun |
⚠️ IMPORTANTE: Todos os jogadores precisam estar na mesma rede — seja LAN física (Wi-Fi/Ethernet do mesmo roteador) ou uma rede virtual Tailscale compartilhada. O Sekiryu não usa servidor intermediário.
A arquitetura de rede do Sekiryu foi desenhada pra ser simples, privada e sem custo operacional — nada de servidor central, relay ou conta em serviço terceiro. Pra isso, usamos duas mecânicas que por natureza ficam restritas ao mesmo segmento de rede:
Quando você abre uma sala, o app envia pacotes UDP em broadcast (255.255.255.255:9091 e 100.255.255.255:9091 pra faixa Tailscale) anunciando a sessão. Outros jogadores escutam essa porta e mostram a sala na lista.
Broadcast UDP não atravessa roteadores nem NAT — ele morre no primeiro gateway. Por isso o anúncio só chega a quem está no mesmo link layer (mesma Wi-Fi, mesma LAN, mesma tailnet).
Uma vez que o convidado encontra (ou digita manualmente) o IP do host, o cliente abre um WebSocket direto pra ws://<ip-do-host>:9090. Não há stun/turn, não há relay — pacotes viajam direto entre as máquinas.
Como é conexão direta ponto-a-ponto, ambas as máquinas precisam se enxergar via IP — o que não acontece através da internet pública sem port forwarding ou VPN.
| Cenário | Como funciona | Setup necessário |
|---|---|---|
| 🏠 Mesma rede Wi-Fi/LAN | Todos conectados no mesmo roteador | Nenhum — só abrir o app |
| 🌍 Rede virtual Tailscale | Tailnet compartilhada simulando LAN pela internet | Instalar Tailscale em cada máquina e logar na mesma conta/tailnet |
| 🔌 IP manual | Convidado digita o IP do host direto | Host compartilha o IP (LAN interno, Tailscale 100.x.x.x, ou público se houver port forwarding da 9090) |
Se a descoberta automática não funcionar, libere as portas no firewall da máquina host:
- TCP 9090 — conexões WebSocket dos convidados
- UDP 9091 — resposta ao ping de descoberta
No Windows, a primeira vez que o app abrir uma porta o próprio sistema pergunta — autorize redes privadas.
- Tailscale dá IPs fixos na faixa
100.x.x.xque se comportam como LAN mesmo atravessando NAT/internet, mantendo a arquitetura atual sem complicar código. - Hole punching / NAT traversal exigiria servidor de sinalização, STUN/TURN e tratamento de casos simétricos — overhead incompatível com o escopo atual.
- Port forwarding manual funciona pra quem quer expor, mas pedir isso ao usuário médio é hostil.
Tailscale é a melhor relação esforço/resultado: instalação de um clique, funciona através de qualquer NAT, e o Sekiryu não precisa saber que está atravessando a internet.
Baixe a última release em Releases e rode o instalador para seu sistema operacional:
| OS | Arquivo |
|---|---|
| Windows | Sekiryu_x.y.z_x64-setup.exe ou .msi |
| macOS | Sekiryu_x.y.z_aarch64.dmg (Apple Silicon) ou _x64.dmg (Intel) |
| Linux | sekiryu_x.y.z_amd64.AppImage ou .deb |
Na primeira execução o app cria o banco SQLite local e baixa as cartas conforme você busca. Primeira busca demora (hits no Scryfall), as seguintes são instantâneas.
- Node.js 20+
- Bun (
curl -fsSL https://bun.sh/install | bash) - Rust toolchain (
rustup) - Dependências de sistema do Tauri — veja o guia oficial para seu OS
# 1. Clone o repositório
git clone https://github.com/Wylp/Sekiryu.git
cd Sekiryu
# 2. Instale dependências do frontend
bun install
# 3. Rode em modo dev (inicia Vite + Tauri)
bun run tauri devO primeiro cargo build baixa dependências Rust (dura alguns minutos). As próximas execuções ficam em segundos.
# Dev
bun run tauri dev # app completo (frontend + backend) em modo dev
bun run dev # só o frontend Vite (porta 1420) — útil pra testar UI sem Tauri
# Build
bun run build # frontend: tsc + vite build
bun run tauri build # bundle completo da aplicação
# Rust (de dentro de src-tauri/)
cargo check # type-check rápido sem compilar binário
cargo build # compilar backend
cargo test # rodar testes RustReleases são publicadas automaticamente ao fazer push de uma tag vX.Y.Z:
git tag v0.4.0
git push origin v0.4.0O workflow do GitHub Actions compila os bundles pros três OS e cria um draft de release.
sekiryu/
├── src/ # Frontend React
│ ├── components/
│ │ ├── board/ # GameBoard, Board3D, Party Frame, zonas
│ │ ├── CachedCardImage.tsx # renderização de carta com fallback
│ │ ├── DeckEditor.tsx # construtor de deck + busca Scryfall
│ │ ├── DeckList.tsx # lista de decks salvos
│ │ ├── ManaIcons.tsx # ícones de mana em SVG
│ │ ├── SleeveCropper.tsx # editor de sleeves customizadas
│ │ ├── UserProfile.tsx # avatar/nickname
│ │ └── WindowControls.tsx # titlebar custom (min/max/close)
│ ├── hooks/ # useCardImage, useMultiplayer, useNetworkActions
│ ├── stores/ # Zustand (gameStore.ts) — estado do jogo
│ ├── types/ # card.ts, game.ts, messages.ts
│ └── utils/ # helpers
├── src-tauri/
│ └── src/
│ ├── main.rs # entry point
│ ├── lib.rs # registro de comandos Tauri
│ ├── db.rs # SQLite (schema + queries)
│ ├── scryfall.rs # cliente HTTP + rate limiter (8 req/s)
│ ├── websocket.rs # WS server (host) e client (guest)
│ └── discovery.rs # UDP broadcast pra descoberta de salas
├── docs/ # assets e documentação
├── public/ # assets estáticos do frontend
├── BREAFING.md # spec técnica original (pt-BR)
├── CLAUDE.md # instruções para assistentes de IA
└── MTG_RULES.md # notas sobre regras implementadas
- Scroll do mouse — zoom
- Clique direito / botão do meio + arrastar — pan
- Clique esquerdo + arrastar — mover carta
- Duplo clique — virar carta
R— voltar à visão local após spectate- Clique num jogador no Party Frame — animar câmera pra perspectiva dele
- Arrastar carta entre zonas para mover
- Clique direito na carta — menu de contexto (tap, flip, transform, criar token, mandar pra zona X, etc.)
- Hover — preview ampliado
- Scroll — zoom do board
Só se vocês compartilharem uma tailnet Tailscale. Instale Tailscale nos dois PCs, loguem na mesma conta (ou aceitem convite de tailnet), e o Sekiryu passa a enxergar o outro como se estivesse na mesma LAN. É gratuito pra uso pessoal.
Porque o Sekiryu conecta direto de máquina a máquina. Sem IP público ou port forwarding, o roteador do host bloqueia a conexão vinda de fora. Tailscale resolve isso criando um túnel criptografado entre as máquinas.
Não. Dados de cartas vêm do Scryfall (API pública oficial) e ficam cacheados localmente em SQLite. Partidas trafegam direto entre jogadores via WebSocket — nada passa por servidor nosso.
Sim. Abra uma sala como host e simplesmente não convide ninguém — você tem controle total de todas as zonas.
O cache de imagens fica em card_cache/ dentro do AppData do seu OS. Cada imagem tem ~100-200KB; decks de 100 cartas ocupam ~10-20MB. Pode limpar pelas configurações do app.
- Importação de texto — decklist padrão (
4 Lightning Bolt, um por linha); o app busca cada carta no Scryfall. .sekiryu— backup completo (decks + perfis + sleeves + configs) com merge inteligente na reimportação, ou seja, não apaga dados existentes.
Imagens custom só através do editor de sleeves por enquanto. Suporte a proxies custom está no roadmap.
- v0.1 — MVP: board 2D, deck builder, Scryfall cache
- v0.2 — Multiplayer WebSocket, UDP discovery, titlebar custom
- v0.3 — Board 3D, DFC, Mulligan, Spectate, Commander no Party Frame
- v0.4 — Stack interativo, sistema de triggers, priority passing
- v0.5 — Replay de partidas, sistema de eventos
- v1.0 — Suporte a proxies custom, formatos alternativos, polimento geral
Veja Issues pra acompanhar tarefas abertas.
Contribuições são super bem-vindas! Fluxo recomendado:
- Abra uma issue descrevendo o bug/feature antes de começar
- Fork + branch a partir de
master - Siga as convenções do projeto (veja
CLAUDE.mdpra detalhes arquiteturais) - Commits em inglês ou português, no formato
tipo: descrição(feat, fix, chore, docs, refactor) - Abra PR descrevendo mudanças e como testar
- Nunca chamar Scryfall direto — sempre via
ScryfallClient(rate limiter) - Imagens de cartas sempre com fallback — use
CachedCardImage - Estado canônico vive no host — clientes mandam intenções, host valida e redistribui
- Design minimalista — cores contidas, ouro como único acento
Licenciado sob a GNU Affero General Public License v3.0 — copyleft forte que também cobre uso em rede (SaaS).
Resumo prático:
- ✅ Uso, modificação, distribuição e uso comercial permitidos
- ✅ Acesso ao código-fonte garantido pra todo mundo, sempre
⚠️ Forks e derivados devem também ser AGPL-3.0 (copyleft "viral")⚠️ Se você rodar uma versão modificada acessível por rede, precisa oferecer o código-fonte aos usuários dessa rede⚠️ Ao distribuir, você deve fornecer o código-fonte e manter os avisos de copyright/licença⚠️ Mudanças em arquivos devem ser marcadas- ❌ Sem garantias — software fornecido "as is"
- ❌ Ninguém pode pegar esse código e transformar em produto proprietário fechado
Pra detalhes completos, veja LICENSE e NOTICE na raiz do repositório, ou gnu.org/licenses/agpl-3.0.
Magic: The Gathering é marca registrada da Wizards of the Coast. Este é um fan project não oficial, sem afiliação com a WotC. Dados de cartas cortesia do Scryfall.
Feito com ☕ e muito aggro por @Wylp
Se você usa e curte, dá uma ⭐ no repo!