Skip to content

Repository files navigation

🐉 Sekiryu

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.

Built with Tauri React Rust TypeScript


📖 Sumário


🎯 Sobre o projeto

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.


✨ Funcionalidades

🎴 Tabuleiro e cartas

  • 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

🧮 Contadores e estado

  • 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

🃏 Decks e cartas

  • 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, .sekiryu backup 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

👥 Multiplayer

  • 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 R pra voltar)
  • Informação oculta preservada — sua mão e biblioteca só você vê; oponentes veem contagem
  • Avatares e nicknames customizáveis por perfil

🎨 UX

  • 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

🛠️ Stack técnica

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

🌐 Multiplayer em rede local

⚠️ 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.

Por que a restrição de rede existe

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:

1. Descoberta automática via UDP Broadcast (porta 9091)

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).

2. Conexão direta via WebSocket (porta 9090 por padrão)

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.

As três formas suportadas de jogar junto

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)

Firewall

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.

Por que Tailscale e não UPnP / hole punching?

  • Tailscale dá IPs fixos na faixa 100.x.x.x que 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.


📦 Instalação

Usuários finais

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.


🧑‍💻 Desenvolvimento

Pré-requisitos

Clonar e rodar

# 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 dev

O primeiro cargo build baixa dependências Rust (dura alguns minutos). As próximas execuções ficam em segundos.

Comandos úteis

# 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 Rust

Release

Releases são publicadas automaticamente ao fazer push de uma tag vX.Y.Z:

git tag v0.4.0
git push origin v0.4.0

O workflow do GitHub Actions compila os bundles pros três OS e cria um draft de release.


📁 Estrutura do projeto

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

⌨️ Atalhos e controles

Board 3D

  • 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

Board 2D

  • 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

❓ FAQ

Meu amigo tá na rede de casa dele, consigo jogar com ele?

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.

Por que não funciona via internet direto?

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.

O app envia meus dados pra algum servidor?

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.

Posso jogar sozinho / testar um deck?

Sim. Abra uma sala como host e simplesmente não convide ninguém — você tem controle total de todas as zonas.

As imagens das cartas ocupam muito espaço?

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.

Qual a diferença entre .sekiryu e importação de texto?

  • 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.

Posso usar proxies / cartas custom?

Imagens custom só através do editor de sleeves por enquanto. Suporte a proxies custom está no roadmap.


🗺️ 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.


🤝 Contribuindo

Contribuições são super bem-vindas! Fluxo recomendado:

  1. Abra uma issue descrevendo o bug/feature antes de começar
  2. Fork + branch a partir de master
  3. Siga as convenções do projeto (veja CLAUDE.md pra detalhes arquiteturais)
  4. Commits em inglês ou português, no formato tipo: descrição (feat, fix, chore, docs, refactor)
  5. Abra PR descrevendo mudanças e como testar

Convenções importantes

  • 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

📄 Licença

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!

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages