Minecraft Simple Voice Chat <-> Discord voice bridge + group music (Folia plugin + Node sidecar)
  • JavaScript 57.4%
  • Java 41%
  • Dockerfile 1.1%
  • Shell 0.5%
Find a file
2026-07-15 01:40:43 +02:00
deploy Initial public release 2026-07-15 01:40:43 +02:00
plugin Initial public release 2026-07-15 01:40:43 +02:00
sidecar Initial public release 2026-07-15 01:40:43 +02:00
.gitignore Initial public release 2026-07-15 01:40:43 +02:00
DEPLOY.md Initial public release 2026-07-15 01:40:43 +02:00
DESIGN.md Initial public release 2026-07-15 01:40:43 +02:00
GO-LIVE.md Initial public release 2026-07-15 01:40:43 +02:00
LESSONS.md Initial public release 2026-07-15 01:40:43 +02:00
LICENSE Initial public release 2026-07-15 01:40:43 +02:00
PROTOCOL.md Initial public release 2026-07-15 01:40:43 +02:00
README.md Initial public release 2026-07-15 01:40:43 +02:00

EternelVoiceHub

Bridges an in-game Simple Voice Chat general group ⇄ a Discord voice channel (both directions, including Bedrock/web players via SimpleVoice-Geyser) and streams group music into both. Targets a Luminol/Folia (Minecraft 1.21.11) server running SVC bukkit 2.6.20. The moderation/recording backend described in the design notes is intentionally not included here.

Shape

   Luminol server (Folia)                        host
┌──────────────────────────────┐   authed WS   ┌────────────────────────────────┐
│ EternelVoiceHub plugin (Java) │◀──(docker)──▶ │ voice-bridge sidecar (node 22) │
│  · persistent SVC group       │               │  · @discordjs/voice 0.19 +DAVE │
│    'general' (fixed UUID)      │               │  · one drift-corrected 20ms    │
│  · mic tap (bounded ring)      │               │    loop: paced send + inject   │
│  · injector: per-connection    │               │  · soft-limiter mix            │
│    sendStaticSoundPacketTo     │               │  · music (yt-dlp+cookies+deno  │
│    (reaches Geyser web too)    │               │    /ffmpeg loudnorm)           │
│  · /voice /vmute /music        │               │  · echo dedup via links.json   │
│  · consent gate + kill switch  │               │  · /mcmute · voice-status line │
└──────────────┬─────────────────┘               └───────────────┬────────────────┘
               │ (plugin runs INSIDE the MC container)            │ outbound UDP only
               ▼                                                  ▼
        SVC UDP                                            Discord #general VC

Repo layout

  • plugin/ — Folia plugin (Gradle Kotlin DSL + shadow, folia-api 1.21.11, voicechat-api 2.6.20).
  • sidecar/ — Node.js bridge (src/) + Dockerfile + tests (test/ incl. the send-path testgate).
  • deploy/docker-compose.yml + .env.example.
  • PROTOCOL.md — the plugin⇄sidecar wire protocol.
  • DESIGN.md / DEPLOY.md / GO-LIVE.md — design notes, deploy runbook, go-live checklist.
  • LESSONS.md — hard-won gotchas (read before touching audio or commands).

Build

  • Plugin: cd plugin && gradle clean shadowJar (JDK 25 → Java-21 bytecode).
  • Sidecar: build on an amd64 host (docker build -t eternel-voice-bridge:v1 .) — an arm64 build's native opus/sodium modules won't run on an amd64 server.

Configuration & secrets

All secrets come from the environment — nothing is hardcoded or committed:

  • DISCORD_VOICE_TOKEN — a bot token for a separate Discord application (two gateway sessions on one token conflict). Put it in the host .env only.
  • HUB_SECRET — shared between the sidecar .env and the plugin config.yml hub.secret.

See DEPLOY.md for the full deploy and LESSONS.md for the things that will bite you.

License

MIT — see LICENSE.