Examples

Server Administration

Ban system, logging, and server monitoring 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.

Server Administration

Examples for server administration using MongoDB.

Ban System

--[[
    Ban System
    Manage player bans with expiration and history
]]

local BanSystem = {}

function BanSystem.Initialize(db)
    BanSystem.db = db
    BanSystem.bans = db:Collection("bans")
    BanSystem.banHistory = db:Collection("ban_history")

    -- Indexes
    BanSystem.bans:CreateIndex({ steamid = 1 }, true, "steamid_unique")
    BanSystem.bans:CreateIndex({ expires_at = 1 }, false, "expiry_index")
    BanSystem.banHistory:CreateIndex({ steamid = 1, banned_at = -1 }, false, "player_history")
end

-- Ban a player
function BanSystem.Ban(steamid, reason, duration, adminSteamid, callback)
    local ban = {
        steamid = steamid,
        reason = reason,
        banned_at = os.time(),
        expires_at = duration and (os.time() + duration) or nil,
        permanent = duration == nil,
        admin_steamid = adminSteamid
    }

    -- Add to active bans
    BanSystem.bans:UpdateOneAsync(
        { steamid = steamid },
        { ["$set"] = ban },
        function(err)
            if err then
                if callback then callback(false, err) end
                return
            end

            -- Add to history
            BanSystem.banHistory:InsertOneAsync({
                steamid = steamid,
                reason = reason,
                duration = duration,
                permanent = duration == nil,
                banned_at = os.time(),
                admin_steamid = adminSteamid
            }, function()
                if callback then callback(true) end
            end)
        end
    )
end

-- Unban a player
function BanSystem.Unban(steamid, adminSteamid, callback)
    BanSystem.bans:DeleteOneAsync(
        { steamid = steamid },
        function(err, deleted)
            if deleted > 0 then
                -- Log the unban
                BanSystem.banHistory:InsertOneAsync({
                    steamid = steamid,
                    action = "unban",
                    unbanned_at = os.time(),
                    admin_steamid = adminSteamid
                })
            end
            if callback then callback(deleted > 0) end
        end
    )
end

-- Check if player is banned
function BanSystem.IsBanned(steamid, callback)
    BanSystem.bans:FindOneAsync({ steamid = steamid }, function(err, ban)
        if not ban then
            callback(false, nil)
            return
        end

        -- Check if expired
        if ban.expires_at and ban.expires_at < os.time() then
            -- Remove expired ban
            BanSystem.bans:DeleteOneAsync({ steamid = steamid })
            callback(false, nil)
            return
        end

        callback(true, ban)
    end)
end

-- Get player's ban history
function BanSystem.GetHistory(steamid, callback)
    BanSystem.banHistory:FindAsync(
        { steamid = steamid },
        100,
        function(err, history)
            callback(history or {})
        end
    )
end

-- Get all active bans
function BanSystem.GetAllBans(callback)
    BanSystem.bans:FindAsync({}, nil, function(err, bans)
        callback(bans or {})
    end)
end

-- Cleanup expired bans
function BanSystem.CleanupExpired(callback)
    BanSystem.bans:DeleteManyAsync(
        {
            permanent = false,
            expires_at = { ["$lt"] = os.time() }
        },
        function(err, deleted)
            if callback then callback(deleted) end
        end
    )
end

-- Get ban statistics
function BanSystem.GetStats(callback)
    BanSystem.bans:AggregateAsync({
        {
            ["$group"] = {
                _id = nil,
                total = { ["$sum"] = 1 },
                permanent = {
                    ["$sum"] = {
                        ["$cond"] = { ["if"] = "$permanent", ["then"] = 1, ["else"] = 0 }
                    }
                },
                temporary = {
                    ["$sum"] = {
                        ["$cond"] = { ["if"] = "$permanent", ["then"] = 0, ["else"] = 1 }
                    }
                }
            }
        }
    }, function(err, results)
        if results and #results > 0 then
            callback(results[1])
        else
            callback({ total = 0, permanent = 0, temporary = 0 })
        end
    end)
end

return BanSystem

Logging System

--[[
    Logging System
    Server event logging and analytics
]]

local Logger = {}

Logger.Levels = {
    DEBUG = 0,
    INFO = 1,
    WARN = 2,
    ERROR = 3
}

Logger.Categories = {
    "player",
    "economy",
    "combat",
    "admin",
    "system",
    "chat"
}

function Logger.Initialize(db)
    Logger.db = db
    Logger.logs = db:Collection("logs")

    -- Indexes for efficient querying
    Logger.logs:CreateIndex({ timestamp = -1 }, false, "time_desc")
    Logger.logs:CreateIndex({ category = 1, timestamp = -1 }, false, "category_time")
    Logger.logs:CreateIndex({ steamid = 1, timestamp = -1 }, false, "player_time")
    Logger.logs:CreateIndex({ level = 1 }, false, "level_index")
end

-- Log an event
function Logger.Log(level, category, message, data)
    local entry = {
        level = level,
        category = category,
        message = message,
        data = data or {},
        timestamp = os.time(),
        server_id = GetHostName and GetHostName() or "unknown"
    }

    -- Fire and forget - don't block for logging
    Logger.logs:InsertOneAsync(entry, function(err)
        if err then
            print("[Logger] Failed to log:", err)
        end
    end)
end

-- Convenience methods
function Logger.Debug(category, message, data)
    Logger.Log(Logger.Levels.DEBUG, category, message, data)
end

function Logger.Info(category, message, data)
    Logger.Log(Logger.Levels.INFO, category, message, data)
end

function Logger.Warn(category, message, data)
    Logger.Log(Logger.Levels.WARN, category, message, data)
end

function Logger.Error(category, message, data)
    Logger.Log(Logger.Levels.ERROR, category, message, data)
end

-- Query logs
function Logger.Query(filter, limit, callback)
    limit = limit or 100

    Logger.logs:AggregateAsync({
        { ["$match"] = filter },
        { ["$sort"] = { timestamp = -1 } },
        { ["$limit"] = limit }
    }, function(err, results)
        callback(results or {})
    end)
end

-- Get logs by category
function Logger.GetByCategory(category, limit, callback)
    Logger.Query({ category = category }, limit, callback)
end

-- Get logs by player
function Logger.GetByPlayer(steamid, limit, callback)
    Logger.Query({ ["data.steamid"] = steamid }, limit, callback)
end

-- Get logs by time range
function Logger.GetByTimeRange(startTime, endTime, callback)
    Logger.Query({
        timestamp = {
            ["$gte"] = startTime,
            ["$lte"] = endTime
        }
    }, 1000, callback)
end

-- Get error logs
function Logger.GetErrors(limit, callback)
    Logger.Query({ level = Logger.Levels.ERROR }, limit, callback)
end

-- Get log statistics
function Logger.GetStats(hours, callback)
    hours = hours or 24
    local since = os.time() - (hours * 3600)

    Logger.logs:AggregateAsync({
        {
            ["$match"] = { timestamp = { ["$gte"] = since } }
        },
        {
            ["$group"] = {
                _id = "$category",
                count = { ["$sum"] = 1 },
                errors = {
                    ["$sum"] = {
                        ["$cond"] = {
                            ["if"] = { ["$eq"] = { "$level", Logger.Levels.ERROR } },
                            ["then"] = 1,
                            ["else"] = 0
                        }
                    }
                }
            }
        },
        { ["$sort"] = { count = -1 } }
    }, function(err, results)
        callback(results or {})
    end)
end

-- Cleanup old logs
function Logger.Cleanup(daysToKeep, callback)
    local cutoff = os.time() - (daysToKeep * 24 * 3600)

    Logger.logs:DeleteManyAsync(
        { timestamp = { ["$lt"] = cutoff } },
        function(err, deleted)
            if callback then callback(deleted) end
        end
    )
end

return Logger

Server Monitor

--[[
    Server Monitor
    Track server performance and player activity
]]

local Monitor = {}

function Monitor.Initialize(db)
    Monitor.db = db
    Monitor.snapshots = db:Collection("server_snapshots")
    Monitor.playerSessions = db:Collection("player_sessions")

    -- Indexes
    Monitor.snapshots:CreateIndex({ timestamp = -1 }, false, "time_desc")
    Monitor.playerSessions:CreateIndex({ steamid = 1, joined_at = -1 }, false, "player_sessions")
end

-- Record server snapshot
function Monitor.RecordSnapshot()
    local snapshot = {
        timestamp = os.time(),
        player_count = player and #player.GetAll() or 0,
        map = game and game.GetMap() or "unknown",
        hostname = GetHostName and GetHostName() or "unknown",
        tick_rate = 1 / engine.TickInterval(),
        -- Add more metrics as needed
    }

    Monitor.snapshots:InsertOneAsync(snapshot, function() end)
end

-- Get server statistics
function Monitor.GetStats(hours, callback)
    hours = hours or 24
    local since = os.time() - (hours * 3600)

    Monitor.snapshots:AggregateAsync({
        {
            ["$match"] = { timestamp = { ["$gte"] = since } }
        },
        {
            ["$group"] = {
                _id = nil,
                avg_players = { ["$avg"] = "$player_count" },
                max_players = { ["$max"] = "$player_count" },
                min_players = { ["$min"] = "$player_count" },
                snapshots = { ["$sum"] = 1 }
            }
        }
    }, function(err, results)
        if results and #results > 0 then
            callback(results[1])
        else
            callback({ avg_players = 0, max_players = 0, min_players = 0, snapshots = 0 })
        end
    end)
end

-- Track player session
function Monitor.PlayerJoined(steamid, username)
    Monitor.playerSessions:InsertOneAsync({
        steamid = steamid,
        username = username,
        joined_at = os.time(),
        left_at = nil,
        duration = nil
    })
end

function Monitor.PlayerLeft(steamid)
    Monitor.playerSessions:UpdateOneAsync(
        { steamid = steamid, left_at = nil },
        {
            ["$set"] = { left_at = os.time() }
        }
    )
end

-- Get player activity
function Monitor.GetPlayerActivity(steamid, days, callback)
    days = days or 30
    local since = os.time() - (days * 24 * 3600)

    Monitor.playerSessions:AggregateAsync({
        {
            ["$match"] = {
                steamid = steamid,
                joined_at = { ["$gte"] = since }
            }
        },
        {
            ["$group"] = {
                _id = nil,
                sessions = { ["$sum"] = 1 },
                total_time = {
                    ["$sum"] = {
                        ["$subtract"] = {
                            { ["$ifNull"] = { "$left_at", os.time() } },
                            "$joined_at"
                        }
                    }
                },
                first_seen = { ["$min"] = "$joined_at" },
                last_seen = { ["$max"] = "$joined_at" }
            }
        }
    }, function(err, results)
        if results and #results > 0 then
            callback(results[1])
        else
            callback({ sessions = 0, total_time = 0 })
        end
    end)
end

-- Get peak hours
function Monitor.GetPeakHours(days, callback)
    days = days or 7
    local since = os.time() - (days * 24 * 3600)

    Monitor.snapshots:AggregateAsync({
        {
            ["$match"] = { timestamp = { ["$gte"] = since } }
        },
        {
            ["$project"] = {
                hour = { ["$mod"] = {
                    { ["$floor"] = { ["$divide"] = { "$timestamp", 3600 } } },
                    24
                }},
                player_count = 1
            }
        },
        {
            ["$group"] = {
                _id = "$hour",
                avg_players = { ["$avg"] = "$player_count" }
            }
        },
        { ["$sort"] = { avg_players = -1 } }
    }, function(err, results)
        callback(results or {})
    end)
end

return Monitor

GMod Integration

--[[
    Admin System Integration for Garry's Mod
]]

require("mongo")

local client = MongoDB.Client("mongodb://localhost:27017")
local db = client:Database("gmod_server")

-- Initialize systems
BanSystem.Initialize(db)
Logger.Initialize(db)
Monitor.Initialize(db)

-- Check bans on connect
hook.Add("CheckPassword", "CheckBan", function(steamid, ip, svPassword, clPassword, name)
    -- Note: CheckPassword is synchronous, so we need a cached ban list
    -- or use a different approach
end)

-- Track player activity
hook.Add("PlayerInitialSpawn", "TrackJoin", function(ply)
    Monitor.PlayerJoined(ply:SteamID(), ply:Nick())
    Logger.Info("player", "Player joined", {
        steamid = ply:SteamID(),
        name = ply:Nick(),
        ip = ply:IPAddress()
    })
end)

hook.Add("PlayerDisconnected", "TrackLeave", function(ply)
    Monitor.PlayerLeft(ply:SteamID())
    Logger.Info("player", "Player left", {
        steamid = ply:SteamID(),
        name = ply:Nick()
    })
end)

-- Log chat messages
hook.Add("PlayerSay", "LogChat", function(ply, text)
    Logger.Info("chat", "Chat message", {
        steamid = ply:SteamID(),
        name = ply:Nick(),
        message = text
    })
end)

-- Record server snapshots every minute
timer.Create("ServerSnapshot", 60, 0, function()
    Monitor.RecordSnapshot()
end)

-- Cleanup old data daily
timer.Create("DailyCleanup", 24 * 3600, 0, function()
    Logger.Cleanup(30)  -- Keep 30 days
    BanSystem.CleanupExpired()
    print("[Server] Daily cleanup complete")
end)

-- Admin commands
concommand.Add("ban", function(ply, cmd, args)
    if not ply:IsAdmin() then return end

    local target = args[1]
    local duration = tonumber(args[2]) or nil  -- nil = permanent
    local reason = table.concat(args, " ", 3) or "No reason"

    BanSystem.Ban(target, reason, duration and duration * 60 or nil, ply:SteamID(), function(success)
        if success then
            ply:ChatPrint("Player banned successfully")
        else
            ply:ChatPrint("Failed to ban player")
        end
    end)
end)

concommand.Add("unban", function(ply, cmd, args)
    if not ply:IsAdmin() then return end

    local target = args[1]

    BanSystem.Unban(target, ply:SteamID(), function(success)
        if success then
            ply:ChatPrint("Player unbanned")
        else
            ply:ChatPrint("Player was not banned")
        end
    end)
end)

concommand.Add("serverstats", function(ply)
    Monitor.GetStats(24, function(stats)
        ply:ChatPrint("=== Server Stats (24h) ===")
        ply:ChatPrint("Avg Players: " .. string.format("%.1f", stats.avg_players))
        ply:ChatPrint("Peak: " .. stats.max_players)
    end)
end)

Next Steps