Velocity 4 rewrite of the BungeeCord left4chat-bungee-1.0-jar-with-dependencies.jar
that ran on the old proxy. No source was available, so the original was decompiled
from the shipped jar and reimplemented against the Velocity API.
Unchanged from the Bungee version:
- Broadcasts
[+] <player> joined/[-] <player> leftnetwork-wide. - Tells the old server
[<] <player> switched to <server>and the new one[>] <player> switched from <server>. - Publishes
{"type":"join|leave","name":"<player>"}on theminecraft.chatRedis channel for the Discord bot. - Keeps the
minecraft.playersRedis key holding a JSON array of{username, uuid, server}for everyone online. - Sends the welcome MOTD on first connect.
- Suppresses all of the above for players LiteBans is about to kick.
Added since the port:
- Server alias commands (
/hub,/survival, ...) — the old proxy's BungeeAliases plugin, folded in. - Reconnects players to the server they were last on, via a Redis hash.
- Vote rewards: when NuVotifier (running on this proxy) receives a vote, a
configurable message is broadcast network-wide and configurable console
commands are published to
minecraft.console.<server>.in, where the Paper-side Left4Chat's console relay runs them. By default the survival server runseco give <player> 100. Requires NuVotifier on the proxy; without it the feature logs a warning and stays off.
Needs JDK 25 — Velocity 4.0.0's class files are major version 69, so an older compiler cannot read them.
./gradlew shadowJar
# -> build/libs/left4chat-velocity-2.0.0.jarDrop the jar in the proxy's plugins/ directory. On first start it writes
plugins/left4chat/config.yml; the deployment copy for the OVH proxy is kept at
../ovh-server/velocity/plugins/left4chat/config.yml.
libs/litebans-api-2.19.0.jar is the litebans.api package lifted out of
LiteBans.jar, because LiteBans publishes no public Maven artifact. Regenerate it
when LiteBans is upgraded:
unzip -q LiteBans.jar 'litebans/api/*' -d /tmp/lb
(cd /tmp/lb && jar cf litebans-api-<version>.jar litebans)libs/nuvotifier-api-2.7.3.jar is the Vote model and Velocity VotifierEvent
lifted out of the NuVotifier release jar, because NuVotifier's JitPack builds
are broken for every 2.7.x tag. Regenerate it when NuVotifier is upgraded:
curl -LO https://github.com/NuVotifier/NuVotifier/releases/download/v<version>/nuvotifier.jar
unzip -q nuvotifier.jar 'com/vexsoftware/votifier/velocity/event/*' \
'com/vexsoftware/votifier/model/Vote.class' -d /tmp/nv
(cd /tmp/nv && jar cf nuvotifier-api-<version>.jar com)decompiled/me/sisko/left4chat/ holds the CFR output for Main,
PlayerListUpdater and MOTDSender — the entire original plugin, about 120
lines. Kept for reference when diffing behaviour.
| BungeeCord | Velocity 4 |
|---|---|
Plugin subclass + plugin.yml |
@Plugin-annotated class, descriptor generated by the API's annotation processor |
onEnable() |
@Subscribe ProxyInitializeEvent |
PostLoginEvent |
PostLoginEvent (com.velocitypowered.api.event.connection) |
PlayerDisconnectEvent |
DisconnectEvent |
ServerSwitchEvent, getFrom() null on first join |
ServerConnectedEvent, getPreviousServer() empty on first join |
ProxyServer.broadcast(BaseComponent[]) |
ProxyServer.sendMessage(Component) — ProxyServer is an Audience |
ServerInfo.getPlayers() |
RegisteredServer.getPlayersConnected() |
ProxiedPlayer.getServer().getInfo().getName() |
Player.getCurrentServer() → Optional<ServerConnection> |
ProxiedPlayer.getDisplayName() |
Player.getUsername() — Velocity has no proxy-side display name |
ChatColor.translateAlternateColorCodes('&', s) |
LegacyComponentSerializer.legacyAmpersand().deserialize(s) |
getProxy().getScheduler().schedule(plugin, task, 1, SECONDS) |
proxy.getScheduler().buildTask(plugin, task).delay(...).schedule() |
litebans.api.Database |
unchanged — the LiteBans API is platform-neutral |
Everything here is a deliberate departure from the decompiled original. Each one can be reverted through config except where noted.
-
Redis host, password, MOTD and the four message prefixes are now configuration. They were compiled into the old jar, pointing at the dead
10.8.0.2VPN address. That also means the old Redis password was shipped inside a distributed jar and should be treated as compromised. -
Connections are pooled. The original constructed a
new Jedis(...), re-authenticated, issued one command and closed the socket — on every join, every leave and every player-list refresh. Now a single pooledRedisClientis held for the plugin's lifetime. -
Redis and LiteBans work happens off the event thread. The original ran
Database.get().isPlayerBanned(...)— a SQL round trip — inline in the Bungee login sequence. That was survivable against a LAN database; against PlanetScale it would add its latency to every login. -
Redis failures no longer propagate. They are logged and swallowed, so a Redis outage costs announcements rather than breaking joins.
-
The MOTD is sent on first
ServerPostConnectEventinstead of on a one-second timer after login. The original's delay was working around the player not being on a backend server yet; this waits for the actual event.motd.delay-millisadds a delay on top if something else clears chat on join. -
Player-list writes are coalesced. Ten simultaneous joins scheduled ten identical Redis writes before; now overlapping requests collapse into one.
-
A player with no current server no longer breaks the player list. Bungee's
getServer()returnednullmid-handshake and the resulting NPE aborted the whole update, leaving the key stale. Theirserverfield is nownull. -
refresh-interval-secondsoptionally republishes the player list on a timer as a safety net. Off by default in the bundled config, set to 60 in the OVH deployment config. Set to 0 for the old behaviour. -
The player list key is cleared on proxy shutdown, instead of leaving the Discord bot showing a full server until the proxy comes back.
-
The switching player no longer receives their own "switched from" message. The original excluded them from the old server's broadcast but not the new server's — asymmetric, and almost certainly unintended. Not configurable; revert in
Left4Chat#onServerConnectedif the old behaviour is wanted.
Nothing. The original had no commands, no permissions and no other listeners.