cross-server command execution for Minecraft networks
define commands in YAML on Velocity, dispatch them across all connected backends over WebSocket or Redis. no plugin messaging, no player-online requirements, no limitations.
CommandBridge is a proxy-to-backend command bridge for Minecraft networks running on Velocity. you write commands as YAML scripts on the proxy, CB validates them, registers them on whichever servers you specified, and handles dispatch. a player runs /lobby on a backend, the proxy picks it up. an admin runs /alert on Velocity, every backend executes it. commands go through instantly.
the whole reason this exists: plugin messages need a connected player to work. if a server is empty, you can't send commands to it. CB uses persistent WebSocket connections (or Redis) to fix that. commands work regardless of player presence.
one jar works everywhere. install the same file on Velocity and all your backends. it auto-detects the platform.
for the full writeup with visuals check the documentation, the Modrinth page, Hangar, or SpigotMC.
| scripting | YAML command definitions with aliases, permissions, cooldowns, 22 argument types |
| transport | WebSocket (default, TLS built in) or Redis |
| execution | CONSOLE, PLAYER, OPERATOR modes |
| security | HMAC-SHA256 mutual auth, TLS 1.3 (PLAIN / TOFU / STRICT) |
| offline queue | queues commands for offline players, survives restarts |
| multi-proxy | primary + client mode for additional proxies |
| player tracking | network-wide, real-time |
| admin CLI | /cb help, info, scripts, reload, list, ping, debug, dump, migrate |
| developer API | typed message channels, event subscriptions, player locator |
| optional integrations | PlaceholderAPI, PacketEvents |
everything gets shaded into a single fat jar. there are a few modules but you only ever deal with one file.
core is the shared library that both sides depend on. networking (WebSocket via Undertow, Redis via Jedis), the YAML scripting engine with validation, security (TLS, auth), config management, and logging. anything that isn't platform-specific lives here.
velocity is the proxy-side plugin. command registration, the dispatch pipeline, the admin CLI, and the UI output all live here. this is the "server" side of the bridge.
backends is the shared client library for all backend platforms. it handles connecting to the proxy, routing incoming messages, and platform abstraction. underneath it there are platform-specific adapters for Bukkit, Paper, Folia, and Velocity-as-client. the adapters are kept thin on purpose, they only deal with thread dispatch and platform-specific API calls. shared logic stays in the parent module.
api is the public developer API for other plugins. channels, events, platform info. more on that below.
dist handles shadow JAR packaging and publishing to Modrinth and Hangar.
requires JDK 21 (Temurin recommended).
git clone https://github.com/objz/CommandBridge.git
cd CommandBridge
git checkout v3
./gradlew shadowJar
# output: dist/build/libs/CommandBridge-<version>-all.jarother useful commands:
# full build (compile + shadow JAR)
./gradlew build
# run checkstyle (this is what CI runs)
./gradlew check
# checkstyle for a specific module
./gradlew :core:checkstyleMain
./gradlew :velocity:checkstyleMain
# clean
./gradlew cleanCB has a public API module for other plugins to interact with the bridge network. source is at api/, full documentation will be on cb.objz.dev.
import static dev.objz.commandbridge.api.platform.Platform.backend;
import static dev.objz.commandbridge.api.platform.Platform.velocity;
CommandBridgeAPI api = CommandBridgeProvider.get();
MessageChannel<CommandPayload> commands = api.channel(CommandPayload.class);
// send a command as console to a specific backend
commands.to(List.of(backend("survival-1")))
.send(new CommandPayload("say hello", RunAs.CONSOLE));
// send to multiple servers
commands.to(List.of(backend("survival-1"), backend("creative-1")))
.send(new CommandPayload("say hello", RunAs.CONSOLE));
// broadcast to all connected servers
commands.toAll().send(new CommandPayload("say maintenance in 5 minutes", RunAs.CONSOLE));
// listen for incoming messages
Subscription sub = commands.listen((ctx, payload) -> {
Platform.ServerTarget from = ctx.from();
String command = payload.command();
});
sub.cancel();
// server events and state
api.onServerConnected(server -> {
// called when a backend connects
}).ifPresent(sub -> subscriptions.add(sub));
api.onServerDisconnected(server -> {
// called when a backend disconnects
}).ifPresent(sub -> subscriptions.add(sub));
api.onConnectionStateChanged(state -> {
if (state.canSend()) { /* ready to send messages */ }
});| Library | What it does |
|---|---|
| Undertow | WebSocket server |
| Jackson | JSON serialization |
| Jedis | Redis client |
| OkHttp | HTTP client on backends |
| Configurate | YAML config loading |
| SnakeYAML | YAML parsing |
| Adventure MiniMessage | chat formatting |
| BouncyCastle | TLS and crypto |
| bStats | anonymous usage metrics |
| Library | Required | What it does |
|---|---|---|
| CommandAPI | yes | argument parsing and tab completion on all servers |
| PacketEvents | no | enables extra argument types (like PLAYERS and TIME) on Velocity |
| PlaceholderAPI | no | external placeholder data (stats, economy, etc.) |
| PapiProxyBridge | no | bridges PlaceholderAPI to the proxy side |
-
backend platform support
- Bukkit support
- Paper support
- Folia support
- Velocity-as-client support (for multi-proxy)
- automatic platform detection
-
WebSocket transport
- WebSocket server on Velocity
- WebSocket client on backends
- session and client management
- rate limiting
- automatic reconnection on disconnect (configurable)
-
Redis transport
- Redis config and connection management
- Redis implementation in core (SendOperation)
- channel mapping on Velocity
- sending/receiving on all platforms
-
security
- mutual authentication (HMAC-SHA256)
- TLS SPKI key pinning
- TLS 1.3 encryption
- multiple TLS modes (PLAIN, TOFU, STRICT)
-
YAML scripting
- YAML command definitions with strict validation
- aliases, permissions, and cooldowns
- 22 argument types (strings, numbers, players, locations, items, entities, etc.)
- custom argument types via PacketEvents
- script migration tooling (
/cb migrate) - schema versioning (currently
version: 4) - duplicate script name detection
- more custom argument types
-
cross-server command execution
- remote and local command dispatch
- automatic command registration sync to backends
- placeholder and PlaceholderAPI support
- console, player, and operator execution modes
- offline command queue (persists across restarts)
- network-wide player tracking and UUID resolution
-
admin CLI (
/cb)-
/cb help,/cb info,/cb scripts -
/cb reload,/cb list,/cb ping -
/cb debug,/cb dump,/cb migrate - dual-mode output (chat + console)
- dump export to file or paste service
- automatic update checker
- bStats integration
-
-
developer API
- public API module (
api/) - typed message channels with target builder, send, request, broadcast, and listen
- multi-target and mixed-platform dispatch
- server connect/disconnect event subscriptions
- connection state tracking
- player locator service
- more channel types and lifecycle hooks
- public API module (
-
web interface
- ...
-
admin GUI
- ...
see the open issues for bugs and feature requests.
| Version | |
|---|---|
| Java | 21+ |
| Velocity | 3.4 - 3.5 |
| Minecraft | 1.20 - 1.21.x |
contributions are welcome. fork it, branch it, PR it. make sure ./gradlew check, ./gradlew test and ./gradlew build pass before opening a PR.
GPL-3.0. see LICENSE.