Redis packets
Send packets between servers, proxies and bots over Redis (Jedis pub/sub). Needs sporeapi-extra.
Connect once at startup, and disconnect on shutdown:
override fun onPluginEnable() {
SporeRedis.connect(
RedisSettings(
address = config.redis.address, // "host:port"
username = config.redis.username,
password = config.redis.password,
channel = "atlas" // everything on the same channel sees each other's packets
),
MyPlugin::class.java // scans your jar for packets and packet listeners
)
}
override fun onPluginDisable() {
SporeRedis.shutdown()
}
A packet is a class extending Packet, its fields are sent as JSON:
@RegisterPacket
class KickPlayerPacket(val uuid: String, val lines: List<String>) : Packet()
KickPlayerPacket(uuid, listOf("Banned")).sendAsync() // or send() to publish on this thread
Listen with a class full of onReceive methods, or a lambda:
@RegisterPacketListener
class NetworkListener {
fun onReceive(packet: KickPlayerPacket) { /* ... */ }
fun onReceive(packet: BroadcastPacket) { /* ... */ }
}
SporeRedis.onReceive<KickPlayerPacket> { packet -> /* ... */ }
Or let the packet handle itself, this is the packet that arrived:
@RegisterPacket
class StaffChatPacket(val sender: String, val message: String) : Packet() {
override fun onReceive() {
Bukkit.broadcast("<s_blue>[Staff] <white>$sender: $message".mm(), "myplugin.staff")
}
}
That's handy for packets only one platform uses. A packet in a shared module that Paper and Velocity handle differently still needs a @RegisterPacketListener per platform, since the shared module can't call Bukkit or Velocity code. Use @RegisterPacket(async = true) to run onReceive() off the main thread. It runs next to any listeners for the same packet, and the packet needs @RegisterPacket (or SporeRedis.register<T>()) so it's picked up.
- Main thread by default. Listeners run on SporeAPI's main dispatcher, which is the main thread on Paper, so you can touch the world straight away. Use
@RegisterPacketListener(async = true)oronReceive(async = true)for blocking work. - Reconnects by itself. If Redis goes down the subscriber retries (1s, 2s, 4s ... up to 30s) and picks up again when it's back. If Redis isn't reachable at startup it keeps retrying in the background instead of breaking your plugin.
- Errors are incidents. A listener that throws, or a packet that can't be read, shows up in debugging. The other listeners still run.
/mycommand debugshows the connection and how many packets were sent and received. - Your own packets. Redis also delivers a packet to the server that sent it. Set
receiveOwnPackets = falseto skip those. @RegisterPacketis only needed for packets nobody listens to locally: a listener registers its packet type on its own.onReceivereturns a subscription you canunregister().send()is blocking IO, so it's flagged by the main-thread IO guard. UsesendAsync()on the main thread.
The JSON format ({"type": "<class name>", "data": {...}}) is the same as Atlas uses, so packets still match as long as the class names do.