NOTICE: VIBE CODED This project has been vibe coded as a small project meant for me and a few friends to use. This isn't meant to be used in big deployment. USE AT YOUR OWN DESCRETION At a glance I don't se any vulnerabilities.
Lightweight clickable server-selector NPCs for LOOHP Limbo and Velocity.
A click sends a small, versioned plugin message to Velocity. The bridge validates the source, player, and destination, then runs the normal server <name> command as that player. It does not bypass Velocity permissions, command events, ServerPermissions, or pre-connect plugins.
| Artifact suffix | Minecraft range | Limbo API baseline | Java |
|---|---|---|---|
26 |
26.1–26.2 | 2026.0.1-ALPHA |
21 |
1.21 |
1.21–1.21.11 | 0.7.10-ALPHA |
17+ |
1.20 |
1.20–1.20.6 | 0.7.5-ALPHA |
17+ |
Velocity 3.4.x is supported. Each range is compiled against its oldest Limbo API baseline; packet IDs and spawn-packet differences are adapted at runtime.
Build every compatibility range:
./gradlew buildAllVersions
# or: ./scripts/build-all.shThe six distributable jars are collected in build/artifacts/:
LimboNPC-Velocity-26.jar
LimboNPC-Velocity-1.21.jar
LimboNPC-Velocity-1.20.jar
LimboNPC-Limbo-26.jar
LimboNPC-Limbo-1.21.jar
LimboNPC-Limbo-1.20.jar
To build one range only:
./gradlew clean build -PminecraftVersion=1.21Install the matching Limbo and Velocity jars in the corresponding servers' plugins directories. On first start, Limbo creates plugins/LimboNPC/; Velocity creates plugins/limbo-npc/.
A reproducible Java 21 build environment is included:
docker compose run --rm --build buildArtifacts are written to the host's build/artifacts/ directory. The named gradle-cache volume preserves downloaded dependencies.
.github/workflows/build.ymltests all three ranges on pushes and pull requests and uploads each artifact pair..github/workflows/release.ymlbuilds all six jars forv*tags and attaches them to the GitHub release.
channel: "limbo-npc:main"
trusted-limbo-servers:
- "limbo"
debug: falseThe backend name must exactly identify the Limbo server in Velocity's server registry. The channel must match the Limbo configuration.
/limbonpc create <id> [server]
/limbonpc remove <id>
/limbonpc move <id>
/limbonpc server <id> <server>
/limbonpc name <id> <MiniMessage...>
/limbonpc skin <id> username <Minecraft name>
/limbonpc skin <id> texture <value> <signature>
/limbonpc skin <id> clear
/limbonpc hologram <id> add <MiniMessage...>
/limbonpc hologram <id> set <1-based line> <MiniMessage...>
/limbonpc hologram <id> remove <1-based line>
/limbonpc hologram <id> clear
/limbonpc info <id>
/limbonpc list
/limbonpc reload
Alias: /lnpc. IDs are lowercase and must match [a-z0-9_-]{1,32}. Mutations are persisted atomically to plugins/LimboNPC/npcs.yml.
Grant all administration with:
limbo-npc.npc.*
Or grant individual actions under limbo-npc.npc.<action> (create, remove, move, server, name, skin, hologram, info, list, reload). LimboNPC explicitly checks both the wildcard and action node because Limbo permissions use exact matching.
Players need no LimboNPC permission to click NPCs. They still need the normal Velocity /server permission and any destination permission required by your proxy plugins.
The Velocity bridge marks matching messages handled and rejects client-originated messages, untrusted backends, malformed protocol data, mismatched player UUIDs, stale backend connections, and unknown destinations. Only a canonical registered server name can reach CommandManager.executeAsync(player, "server " + name).