Examples
Game Systems
Leaderboards, achievements, and matchmaking examples
These examples are AI-generated and has not been reviewed for accuracy. Use them as a starting point and verify correctness before deploying in production.
Game Systems
Examples for common game systems using MongoDB.
Leaderboard System
--[[
Leaderboard System
Score tracking and rankings
]]
local Leaderboard = {}
function Leaderboard.Initialize(db)
Leaderboard.db = db
Leaderboard.scores = db:Collection("leaderboard")
-- Create indexes
Leaderboard.scores:CreateIndex({ category = 1, score = -1 }, false, "category_score")
Leaderboard.scores:CreateIndex({ steamid = 1, category = 1 }, true, "player_category")
end
-- Submit a score
function Leaderboard.SubmitScore(steamid, username, score, category, callback)
category = category or "default"
-- Upsert - update if higher, insert if new
Leaderboard.scores:FindOneAsync(
{ steamid = steamid, category = category },
function(err, existing)
if existing and existing.score >= score then
-- Existing score is higher
if callback then callback(false, "Existing score is higher") end
return
end
local doc = {
steamid = steamid,
username = username,
category = category,
score = score,
submitted_at = os.time()
}
if existing then
-- Update existing
Leaderboard.scores:UpdateOneAsync(
{ steamid = steamid, category = category },
{ ["$set"] = doc },
function(err)
if callback then callback(not err, err) end
end
)
else
-- Insert new
Leaderboard.scores:InsertOneAsync(doc, function(err)
if callback then callback(not err, err) end
end)
end
end
)
end
-- Get top scores
function Leaderboard.GetTop(category, limit, callback)
category = category or "default"
limit = limit or 10
Leaderboard.scores:AggregateAsync({
{ ["$match"] = { category = category } },
{ ["$sort"] = { score = -1 } },
{ ["$limit"] = limit },
{
["$project"] = {
steamid = 1,
username = 1,
score = 1,
submitted_at = 1,
_id = 0
}
}
}, function(err, results)
callback(results or {})
end)
end
-- Get player rank
function Leaderboard.GetRank(steamid, category, callback)
category = category or "default"
Leaderboard.scores:FindOneAsync(
{ steamid = steamid, category = category },
function(err, player)
if not player then
callback(nil, 0)
return
end
-- Count players with higher scores
Leaderboard.scores:CountAsync(
{ category = category, score = { ["$gt"] = player.score } },
function(err, higher)
callback(higher + 1, player.score)
end
)
end
)
end
-- Get player's scores in all categories
function Leaderboard.GetPlayerScores(steamid, callback)
Leaderboard.scores:FindAsync(
{ steamid = steamid },
100,
function(err, results)
callback(results or {})
end
)
end
-- Get leaderboard with ranks around player
function Leaderboard.GetAroundPlayer(steamid, category, range, callback)
category = category or "default"
range = range or 5
Leaderboard.GetRank(steamid, category, function(rank, score)
if not rank then
callback({}, nil)
return
end
local skip = math.max(0, rank - range - 1)
Leaderboard.scores:AggregateAsync({
{ ["$match"] = { category = category } },
{ ["$sort"] = { score = -1 } },
{ ["$skip"] = skip },
{ ["$limit"] = range * 2 + 1 },
{
["$project"] = {
steamid = 1,
username = 1,
score = 1,
_id = 0
}
}
}, function(err, results)
-- Add rank numbers
for i, entry in ipairs(results) do
entry.rank = skip + i
end
callback(results, rank)
end)
end)
end
return Leaderboard
Achievement System
--[[
Achievement System
Track and award player achievements
]]
local Achievements = {}
-- Define achievements
Achievements.Definitions = {
first_kill = {
name = "First Blood",
description = "Get your first kill",
points = 10
},
kill_streak_5 = {
name = "On Fire",
description = "Get a 5 kill streak",
points = 25
},
level_10 = {
name = "Rising Star",
description = "Reach level 10",
points = 50
},
level_50 = {
name = "Veteran",
description = "Reach level 50",
points = 100
},
playtime_10h = {
name = "Dedicated",
description = "Play for 10 hours",
points = 75
},
credits_100k = {
name = "Rich",
description = "Accumulate 100,000 credits",
points = 100
}
}
function Achievements.Initialize(db)
Achievements.db = db
Achievements.col = db:Collection("player_achievements")
Achievements.col:CreateIndex({ steamid = 1 }, false, "steamid_index")
end
-- Check if player has achievement
function Achievements.Has(steamid, achievementId, callback)
Achievements.col:FindOneAsync(
{ steamid = steamid },
function(err, doc)
if doc and doc.achievements then
for _, ach in ipairs(doc.achievements) do
if ach.id == achievementId then
callback(true, ach)
return
end
end
end
callback(false, nil)
end
)
end
-- Award achievement
function Achievements.Award(steamid, achievementId, callback)
local def = Achievements.Definitions[achievementId]
if not def then
if callback then callback(false, "Invalid achievement") end
return
end
-- Check if already has it
Achievements.Has(steamid, achievementId, function(has)
if has then
if callback then callback(false, "Already earned") end
return
end
local achievement = {
id = achievementId,
name = def.name,
points = def.points,
earned_at = os.time()
}
Achievements.col:UpdateOneAsync(
{ steamid = steamid },
{
["$push"] = { achievements = achievement },
["$inc"] = { total_points = def.points }
},
function(err)
if callback then callback(not err, achievement) end
end
)
end)
end
-- Get player achievements
function Achievements.GetAll(steamid, callback)
Achievements.col:FindOneAsync(
{ steamid = steamid },
function(err, doc)
if doc then
callback(doc.achievements or {}, doc.total_points or 0)
else
callback({}, 0)
end
end
)
end
-- Get achievement progress/stats
function Achievements.GetProgress(steamid, callback)
Achievements.GetAll(steamid, function(earned, points)
local total = 0
local totalPoints = 0
for _, def in pairs(Achievements.Definitions) do
total = total + 1
totalPoints = totalPoints + def.points
end
callback({
earned = #earned,
total = total,
points = points,
max_points = totalPoints,
percentage = (#earned / total) * 100
})
end)
end
-- Check achievements based on player stats
function Achievements.CheckAndAward(steamid, playerData, callback)
local awarded = {}
local function checkNext(checks, index)
if index > #checks then
callback(awarded)
return
end
local check = checks[index]
if check.condition then
Achievements.Award(steamid, check.id, function(success, ach)
if success then
table.insert(awarded, ach)
end
checkNext(checks, index + 1)
end)
else
checkNext(checks, index + 1)
end
end
local checks = {
{ id = "first_kill", condition = playerData.stats.kills >= 1 },
{ id = "level_10", condition = playerData.level >= 10 },
{ id = "level_50", condition = playerData.level >= 50 },
{ id = "playtime_10h", condition = playerData.playtime >= 36000 },
{ id = "credits_100k", condition = playerData.credits >= 100000 }
}
checkNext(checks, 1)
end
return Achievements
Matchmaking System
--[[
Simple Matchmaking System
Queue players and create balanced matches
]]
local Matchmaking = {}
function Matchmaking.Initialize(db)
Matchmaking.db = db
Matchmaking.queue = db:Collection("matchmaking_queue")
Matchmaking.matches = db:Collection("matches")
-- Index for efficient queries
Matchmaking.queue:CreateIndex({ skill_rating = 1 }, false, "skill_index")
Matchmaking.queue:CreateIndex({ queued_at = 1 }, false, "queue_time")
end
-- Add player to queue
function Matchmaking.JoinQueue(steamid, username, skillRating, callback)
-- Remove from queue if already in it
Matchmaking.queue:DeleteOneAsync({ steamid = steamid }, function()
-- Add to queue
Matchmaking.queue:InsertOneAsync({
steamid = steamid,
username = username,
skill_rating = skillRating,
queued_at = os.time()
}, function(err)
if callback then callback(not err) end
end)
end)
end
-- Remove player from queue
function Matchmaking.LeaveQueue(steamid, callback)
Matchmaking.queue:DeleteOneAsync(
{ steamid = steamid },
function(err, deleted)
if callback then callback(deleted > 0) end
end
)
end
-- Find match for player
function Matchmaking.FindMatch(steamid, skillRating, skillRange, callback)
skillRange = skillRange or 100
Matchmaking.queue:FindAsync({
steamid = { ["$ne"] = steamid },
skill_rating = {
["$gte"] = skillRating - skillRange,
["$lte"] = skillRating + skillRange
}
}, 100, function(err, candidates)
if #candidates == 0 then
callback(nil, "No players in range")
return
end
-- Sort by closest skill rating
table.sort(candidates, function(a, b)
return math.abs(a.skill_rating - skillRating) <
math.abs(b.skill_rating - skillRating)
end)
callback(candidates[1])
end)
end
-- Create a match
function Matchmaking.CreateMatch(player1, player2, callback)
local match = {
players = {
{
steamid = player1.steamid,
username = player1.username,
skill_rating = player1.skill_rating
},
{
steamid = player2.steamid,
username = player2.username,
skill_rating = player2.skill_rating
}
},
status = "pending",
created_at = os.time(),
skill_diff = math.abs(player1.skill_rating - player2.skill_rating)
}
-- Remove players from queue
Matchmaking.queue:DeleteManyAsync({
steamid = { ["$in"] = { player1.steamid, player2.steamid } }
}, function()
-- Create match record
Matchmaking.matches:InsertOneAsync(match, function(err, matchId)
if err then
callback(nil, err)
else
match._id = matchId
callback(match)
end
end)
end)
end
-- Get queue statistics
function Matchmaking.GetQueueStats(callback)
Matchmaking.queue:AggregateAsync({
{
["$group"] = {
_id = nil,
count = { ["$sum"] = 1 },
avg_skill = { ["$avg"] = "$skill_rating" },
min_skill = { ["$min"] = "$skill_rating" },
max_skill = { ["$max"] = "$skill_rating" },
avg_wait = { ["$avg"] = {
["$subtract"] = { os.time(), "$queued_at" }
}}
}
}
}, function(err, results)
if results and #results > 0 then
callback(results[1])
else
callback({
count = 0,
avg_skill = 0,
min_skill = 0,
max_skill = 0,
avg_wait = 0
})
end
end)
end
-- Process queue and create matches
function Matchmaking.ProcessQueue(callback)
local created = 0
Matchmaking.queue:FindAsync({}, 1000, function(err, players)
if #players < 2 then
callback(0)
return
end
-- Sort by queue time
table.sort(players, function(a, b)
return a.queued_at < b.queued_at
end)
local function processNext(index)
if index > #players then
callback(created)
return
end
local player = players[index]
-- Check if still in queue
Matchmaking.queue:FindOneAsync({ steamid = player.steamid }, function(err, stillQueued)
if not stillQueued then
processNext(index + 1)
return
end
-- Find match
Matchmaking.FindMatch(player.steamid, player.skill_rating, 200, function(opponent)
if opponent then
Matchmaking.CreateMatch(player, opponent, function(match)
if match then
created = created + 1
print("Created match:", player.username, "vs", opponent.username)
end
processNext(index + 1)
end)
else
processNext(index + 1)
end
end)
end)
end
processNext(1)
end)
end
return Matchmaking
Usage Example
-- Initialize systems
require("mongo")
local client = MongoDB.Client("mongodb://localhost:27017")
local db = client:Database("gameserver")
Leaderboard.Initialize(db)
Achievements.Initialize(db)
Matchmaking.Initialize(db)
-- Submit a score
Leaderboard.SubmitScore("STEAM_0:1:12345", "Player1", 1500, "kills", function(success)
print("Score submitted:", success)
end)
-- Get top 10
Leaderboard.GetTop("kills", 10, function(top)
print("=== Top 10 ===")
for i, entry in ipairs(top) do
print(i .. ". " .. entry.username .. " - " .. entry.score)
end
end)
-- Award achievement
Achievements.Award("STEAM_0:1:12345", "first_kill", function(success, ach)
if success then
print("Achievement unlocked:", ach.name)
end
end)
-- Matchmaking
Matchmaking.JoinQueue("STEAM_0:1:12345", "Player1", 1500)
Matchmaking.JoinQueue("STEAM_0:1:67890", "Player2", 1450)
timer.Create("ProcessMatchmaking", 5, 0, function()
Matchmaking.ProcessQueue(function(created)
if created > 0 then
print("Created", created, "matches")
end
end)
end)
Next Steps
- Administration - Bans and logging
- API Reference - Complete method documentation