Pub/Sub Examples
Real-world examples of using Raddish's Pub/Sub system for cross-server communication in your Roblox game.
Global Announcements
Send announcements to all servers at once.
lua
local Raddish = require(game.ReplicatedStorage.Raddish)
-- Server Script: Admin command to announce
local function announceToAllServers(message, sender)
Raddish.Publish("global:announcement", {
message = message,
sender = sender,
timestamp = os.time()
})
end
-- All servers listen for announcements
Raddish.Subscribe("global:announcement", function(data)
-- Show to all players on this server
for _, player in ipairs(game.Players:GetPlayers()) do
game.ReplicatedStorage.Remotes.ShowAnnouncement:FireClient(player, {
title = "Server Announcement",
message = data.message,
from = data.sender
})
end
print("[Announcement]", data.sender .. ":", data.message)
end)
-- Admin command
game.Players.PlayerAdded:Connect(function(player)
player.Chatted:Connect(function(message)
if player:GetRankInGroup(123456) >= 250 then -- Admin rank
if message:sub(1, 10) == "/announce " then
local announcement = message:sub(11)
announceToAllServers(announcement, player.Name)
end
end
end)
end)Cross-Server Trading System
Allow players to trade across different servers.
lua
local Raddish = require(game.ReplicatedStorage.Raddish)
local HttpService = game:GetService("HttpService")
-- List item for trade (broadcasts to all servers)
function listItemForTrade(seller, itemName, price)
local listingId = HttpService:GenerateGUID(false)
Raddish.Publish("trading:new_listing", {
listingId = listingId,
sellerId = seller.UserId,
sellerName = seller.Name,
itemName = itemName,
price = price,
serverJobId = game.JobId,
timestamp = os.time()
})
-- Store locally
Raddish.HSet("listing:" .. listingId, "seller", seller.UserId)
Raddish.HSet("listing:" .. listingId, "item", itemName)
Raddish.HSet("listing:" .. listingId, "price", price)
Raddish.Expire("listing:" .. listingId, 3600) -- Expire after 1 hour
return listingId
end
-- All servers listen for new listings
Raddish.Subscribe("trading:new_listing", function(data)
-- Add to global trading board
Raddish.ZAdd("trading:active", data.timestamp, data.listingId)
-- Notify players on this server
for _, player in ipairs(game.Players:GetPlayers()) do
game.ReplicatedStorage.Remotes.NewTradeListing:FireClient(player, {
seller = data.sellerName,
item = data.itemName,
price = data.price,
listingId = data.listingId
})
end
print("[Trading] New listing:", data.sellerName, "selling", data.itemName, "for", data.price)
end)
-- Purchase item (cross-server)
function purchaseItem(buyer, listingId)
Raddish.Publish("trading:purchase_request", {
listingId = listingId,
buyerId = buyer.UserId,
buyerName = buyer.Name,
timestamp = os.time()
})
end
-- Listen for purchase requests
Raddish.Subscribe("trading:purchase_request", function(data)
local listing = Raddish.HGetAll("listing:" .. data.listingId)
if listing and listing.seller then
-- Process on seller's server
local seller = game.Players:GetPlayerByUserId(tonumber(listing.seller))
if seller then
-- Transfer item and coins
Raddish.Publish("trading:completed", {
listingId = data.listingId,
sellerId = listing.seller,
buyerId = data.buyerId,
itemName = listing.item,
price = listing.price
})
-- Remove listing
Raddish.ZRem("trading:active", data.listingId)
Raddish.Delete("listing:" .. data.listingId)
end
end
end)
-- Listen for completed trades
Raddish.Subscribe("trading:completed", function(data)
print("[Trading] Trade completed:", data.itemName, "sold for", data.price)
-- Update UI for all players
for _, player in ipairs(game.Players:GetPlayers()) do
game.ReplicatedStorage.Remotes.TradeCompleted:FireClient(player, data)
end
end)Live Events System
Start events that run across all servers simultaneously.
lua
local Raddish = require(game.ReplicatedStorage.Raddish)
-- Start a global event
function startGlobalEvent(eventName, duration, rewards)
Raddish.Publish("events:start", {
eventName = eventName,
duration = duration,
rewards = rewards,
startTime = os.time(),
endTime = os.time() + duration
})
end
-- All servers listen for event start
Raddish.Subscribe("events:start", function(data)
print("[Event] Starting:", data.eventName)
-- Notify all players
for _, player in ipairs(game.Players:GetPlayers()) do
game.ReplicatedStorage.Remotes.EventStarted:FireClient(player, {
title = data.eventName,
duration = data.duration,
rewards = data.rewards
})
end
-- Start event logic on this server
spawnEventBosses(data.eventName)
-- Schedule event end
task.delay(data.duration, function()
endEvent(data.eventName)
end)
end)
-- Update event progress globally
function updateEventProgress(eventName, progress)
Raddish.Publish("events:progress", {
eventName = eventName,
progress = progress, -- 0-100
timestamp = os.time()
})
end
Raddish.Subscribe("events:progress", function(data)
-- Update progress bar for all players
for _, player in ipairs(game.Players:GetPlayers()) do
game.ReplicatedStorage.Remotes.EventProgress:FireClient(player, data.progress)
end
end)
-- Event completion
function completeEvent(eventName, winners)
Raddish.Publish("events:complete", {
eventName = eventName,
winners = winners,
completionTime = os.time()
})
end
Raddish.Subscribe("events:complete", function(data)
print("[Event] Completed:", data.eventName)
print("Winners:", table.concat(data.winners, ", "))
-- Award winners across all servers
for _, winnerId in ipairs(data.winners) do
local player = game.Players:GetPlayerByUserId(winnerId)
if player then
giveReward(player, "Legendary Sword")
end
end
end)
-- Example: 2x XP Event
startGlobalEvent("Double XP Weekend", 172800, {"2x XP", "Exclusive Badge"}) -- 48 hoursServer Status Monitoring
Monitor all game servers from a central dashboard.
lua
local Raddish = require(game.ReplicatedStorage.Raddish)
-- Each server reports its status every 30 seconds
task.spawn(function()
while true do
local stats = {
serverId = game.JobId,
placeId = game.PlaceId,
playerCount = #game.Players:GetPlayers(),
maxPlayers = game.Players.MaxPlayers,
memory = game:GetService("Stats"):GetTotalMemoryUsageMb(),
ping = game:GetService("Stats").DataReceiveKbps,
uptime = workspace.DistributedGameTime,
region = game.PrivateServerId ~= "" and "Private" or "Public",
timestamp = os.time()
}
Raddish.Publish("monitoring:server_status", stats)
task.wait(30)
end
end)
-- Monitoring server collects all server data
Raddish.Subscribe("monitoring:server_status", function(data)
-- Store server stats
Raddish.HSet("servers:" .. data.serverId, "players", data.playerCount)
Raddish.HSet("servers:" .. data.serverId, "memory", data.memory)
Raddish.HSet("servers:" .. data.serverId, "uptime", data.uptime)
Raddish.HSet("servers:" .. data.serverId, "lastUpdate", os.time())
Raddish.Expire("servers:" .. data.serverId, 120) -- Expire if no update in 2 min
-- Check for issues
if data.memory > 3000 then
warn("[Monitoring] High memory on server:", data.serverId)
-- Alert to Discord
Raddish.SendDiscordWebhook("YOUR_WEBHOOK_URL", {
title = "⚠️ High Memory Alert",
description = "Server " .. data.serverId .. " is using " .. data.memory .. " MB",
color = 0xFFAA00,
fields = {
{name = "Players", value = tostring(data.playerCount), inline = true},
{name = "Uptime", value = tostring(math.floor(data.uptime/60)) .. " min", inline = true}
}
})
end
if data.playerCount == data.maxPlayers then
print("[Monitoring] Server full:", data.serverId)
end
end)
-- Get all active servers
function getActiveServers()
local serverKeys = Raddish.Keys("servers:*")
local servers = {}
for _, key in ipairs(serverKeys) do
local serverData = Raddish.HGetAll(key)
table.insert(servers, serverData)
end
return servers
end
-- Get total player count across all servers
function getTotalPlayers()
local servers = getActiveServers()
local total = 0
for _, server in ipairs(servers) do
total = total + (tonumber(server.players) or 0)
end
return total
end
print("Total players across all servers:", getTotalPlayers())Matchmaking Queue
Cross-server matchmaking with skill-based matching.
lua
local Raddish = require(game.ReplicatedStorage.Raddish)
-- Player joins matchmaking queue
function joinQueue(player, gameMode)
local skillRating = getPlayerSkill(player) -- Your skill calculation
Raddish.Publish("matchmaking:join", {
userId = player.UserId,
username = player.Name,
gameMode = gameMode,
skillRating = skillRating,
serverId = game.JobId,
timestamp = os.time()
})
print(player.Name, "joined", gameMode, "queue with rating", skillRating)
end
-- Matchmaking server handles queue
local QUEUE_CHECK_INTERVAL = 2 -- Check every 2 seconds
local SKILL_RANGE = 100 -- ±100 rating for matching
Raddish.Subscribe("matchmaking:join", function(data)
-- Add to queue
Raddish.ZAdd("queue:" .. data.gameMode, data.skillRating, data.userId)
-- Store player data
Raddish.HSet("queue:player:" .. data.userId, "username", data.username)
Raddish.HSet("queue:player:" .. data.userId, "skillRating", data.skillRating)
Raddish.HSet("queue:player:" .. data.userId, "serverId", data.serverId)
Raddish.Expire("queue:player:" .. data.userId, 300) -- 5 min timeout
print("[Matchmaking]", data.username, "joined queue")
end)
-- Process matchmaking queue
task.spawn(function()
while true do
task.wait(QUEUE_CHECK_INTERVAL)
local gameModes = {"casual", "ranked", "competitive"}
for _, gameMode in ipairs(gameModes) do
local queueSize = Raddish.ZCard("queue:" .. gameMode)
if queueSize >= 2 then
-- Get all players in queue
local players = Raddish.ZRange("queue:" .. gameMode, 1, -1, true)
-- Try to match players with similar skill
for i = 1, #players - 1 do
local player1 = players[i]
for j = i + 1, #players do
local player2 = players[j]
-- Check if skill ratings are close
if math.abs(player1.score - player2.score) <= SKILL_RANGE then
-- Create match!
createMatch(gameMode, {player1.member, player2.member})
-- Remove from queue
Raddish.ZRem("queue:" .. gameMode, player1.member)
Raddish.ZRem("queue:" .. gameMode, player2.member)
break
end
end
end
end
end
end
end)
function createMatch(gameMode, playerIds)
local matchId = game:GetService("HttpService"):GenerateGUID(false)
Raddish.Publish("matchmaking:match_found", {
matchId = matchId,
gameMode = gameMode,
players = playerIds,
serverId = game.JobId,
timestamp = os.time()
})
print("[Matchmaking] Match created:", matchId)
end
-- Players receive match notification
Raddish.Subscribe("matchmaking:match_found", function(data)
for _, userId in ipairs(data.players) do
local player = game.Players:GetPlayerByUserId(tonumber(userId))
if player then
-- Teleport to match server
game.ReplicatedStorage.Remotes.MatchFound:FireClient(player, {
matchId = data.matchId,
gameMode = data.gameMode
})
print(player.Name, "matched! Teleporting...")
end
end
end)
-- Player leaves queue
function leaveQueue(player, gameMode)
Raddish.Publish("matchmaking:leave", {
userId = player.UserId,
gameMode = gameMode
})
end
Raddish.Subscribe("matchmaking:leave", function(data)
Raddish.ZRem("queue:" .. data.gameMode, data.userId)
Raddish.Delete("queue:player:" .. data.userId)
end)Global Chat System
Cross-server chat channels.
lua
local Raddish = require(game.ReplicatedStorage.Raddish)
local TextService = game:GetService("TextService")
-- Send message to global chat
function sendGlobalMessage(player, message, channel)
channel = channel or "global"
-- Filter message
local success, filteredMessage = pcall(function()
return TextService:FilterStringAsync(message, player.UserId):GetNonChatStringForBroadcastAsync()
end)
if not success then
return
end
Raddish.Publish("chat:" .. channel, {
userId = player.UserId,
username = player.Name,
message = filteredMessage,
channel = channel,
timestamp = os.time()
})
-- Store in chat history
Raddish.RPush("chat:history:" .. channel, {
userId = player.UserId,
username = player.Name,
message = filteredMessage,
timestamp = os.time()
})
-- Keep only last 100 messages
if Raddish.LLen("chat:history:" .. channel) > 100 then
Raddish.LPop("chat:history:" .. channel)
end
end
-- All servers listen for chat messages
Raddish.Subscribe("chat:global", function(data)
-- Display to all players on this server
for _, player in ipairs(game.Players:GetPlayers()) do
game.ReplicatedStorage.Remotes.GlobalChatMessage:FireClient(player, {
username = data.username,
message = data.message,
timestamp = data.timestamp
})
end
end)
-- VIP channel
Raddish.Subscribe("chat:vip", function(data)
for _, player in ipairs(game.Players:GetPlayers()) do
if player:GetAttribute("IsVIP") then
game.ReplicatedStorage.Remotes.VIPChatMessage:FireClient(player, {
username = data.username,
message = data.message
})
end
end
end)
-- Get chat history when player joins
function loadChatHistory(player, channel)
local history = Raddish.LRange("chat:history:" .. channel, -50, -1) -- Last 50 messages
game.ReplicatedStorage.Remotes.LoadChatHistory:FireClient(player, history)
end
game.Players.PlayerAdded:Connect(function(player)
loadChatHistory(player, "global")
end)PvP Statistics Tracking
Track PvP stats across all servers.
lua
local Raddish = require(game.ReplicatedStorage.Raddish)
-- Player wins PvP match
function recordPvPWin(winner, loser, gameMode)
Raddish.Publish("pvp:match_result", {
winnerId = winner.UserId,
winnerName = winner.Name,
loserId = loser.UserId,
loserName = loser.Name,
gameMode = gameMode,
timestamp = os.time()
})
end
-- All servers update global stats
Raddish.Subscribe("pvp:match_result", function(data)
-- Update winner stats
Raddish.IncrementWithCache(
{UserId = data.winnerId},
"PvP_Wins",
1,
0
)
Raddish.IncrementWithCache(
{UserId = data.winnerId},
"PvP_Rating",
25,
1000
)
-- Update loser stats
Raddish.IncrementWithCache(
{UserId = data.loserId},
"PvP_Losses",
1,
0
)
Raddish.IncrementWithCache(
{UserId = data.loserId},
"PvP_Rating",
-15,
1000
)
-- Update global leaderboard
local winnerRating = Raddish.GetWithCache({UserId = data.winnerId}, "PvP_Rating", 1000)
Raddish.ZAdd("leaderboard:pvp", winnerRating, data.winnerId)
local loserRating = Raddish.GetWithCache({UserId = data.loserId}, "PvP_Rating", 1000)
Raddish.ZAdd("leaderboard:pvp", loserRating, data.loserId)
print("[PvP]", data.winnerName, "defeated", data.loserName)
end)Best Practices
- Use clear channel naming conventions (
category:action) - Always include timestamps in your data
- Keep payloads small (<1KB recommended)
- Handle subscription errors with pcall
- Use
external = falsefor server-local events
Rate Limits
MessagingService has a limit of 150 + 60 * number_of_players messages per minute. Raddish automatically batches messages to stay under this limit.