Examples
Player Systems
Player data management, inventories, and progression systems
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.
Player Systems
Complete examples for managing player data in Garry's Mod.
Player Data System
A complete player data management system:
--[[
Player Data System
Handles player registration, loading, and saving
]]
local PlayerData = {}
PlayerData.client = nil
PlayerData.db = nil
-- Initialize the system
function PlayerData.Initialize(connectionString, dbName)
PlayerData.client = MongoDB.Client(connectionString)
if not PlayerData.client then
print("[PlayerData] Failed to connect to MongoDB!")
return false
end
PlayerData.db = PlayerData.client:Database(dbName)
-- Setup indexes
local players = PlayerData.db:Collection("players")
players:CreateIndex({ steamid = 1 }, true, "steamid_unique")
players:CreateIndex({ last_login = -1 }, false, "recent_login")
print("[PlayerData] System initialized")
return true
end
-- Get player collection
function PlayerData.GetCollection()
return PlayerData.db:Collection("players")
end
-- Load player data
function PlayerData.Load(steamid, callback)
local col = PlayerData.GetCollection()
col:FindOneAsync({ steamid = steamid }, function(err, data)
if err then
print("[PlayerData] Load error:", err)
callback(nil, err)
return
end
if data then
-- Update last login
col:UpdateOneAsync(
{ steamid = steamid },
{ ["$set"] = { last_login = os.time() } },
function() end
)
callback(data, nil)
else
-- Create new player
local newPlayer = {
steamid = steamid,
level = 1,
experience = 0,
credits = 1000,
playtime = 0,
created_at = os.time(),
last_login = os.time(),
stats = {
kills = 0,
deaths = 0,
assists = 0
},
settings = {
notifications = true,
music = true
},
inventory = {}
}
col:InsertOneAsync(newPlayer, function(err, id)
if err then
callback(nil, err)
else
newPlayer._id = id
callback(newPlayer, nil)
end
end)
end
end)
end
-- Save player data
function PlayerData.Save(steamid, data, callback)
local col = PlayerData.GetCollection()
col:UpdateOneAsync(
{ steamid = steamid },
{ ["$set"] = data },
function(err, modified)
if callback then
callback(err, modified > 0)
end
end
)
end
-- Update specific fields
function PlayerData.Update(steamid, updates, callback)
local col = PlayerData.GetCollection()
col:UpdateOneAsync(
{ steamid = steamid },
updates,
function(err, modified)
if callback then
callback(err, modified > 0)
end
end
)
end
-- Add experience and handle level up
function PlayerData.AddExperience(steamid, amount, callback)
local col = PlayerData.GetCollection()
-- Get current data
col:FindOneAsync({ steamid = steamid }, function(err, player)
if err or not player then
if callback then callback(false, 0) end
return
end
local newExp = player.experience + amount
local newLevel = player.level
local expRequired = newLevel * 1000
-- Level up logic
while newExp >= expRequired do
newExp = newExp - expRequired
newLevel = newLevel + 1
expRequired = newLevel * 1000
end
local leveledUp = newLevel > player.level
col:UpdateOneAsync(
{ steamid = steamid },
{
["$set"] = {
experience = newExp,
level = newLevel
}
},
function(err)
if callback then
callback(leveledUp, newLevel)
end
end
)
end)
end
-- Get player stats
function PlayerData.GetStats(steamid, callback)
local col = PlayerData.GetCollection()
col:FindOneAsync(
{ steamid = steamid },
function(err, player)
if err or not player then
callback(nil)
else
callback(player.stats)
end
end
)
end
-- Increment stat
function PlayerData.IncrementStat(steamid, stat, amount)
local col = PlayerData.GetCollection()
col:UpdateOneAsync(
{ steamid = steamid },
{ ["$inc"] = { ["stats." .. stat] = amount or 1 } },
function() end
)
end
return PlayerData
Inventory System
--[[
Player Inventory System
Manages player items with stacking and limits
]]
local Inventory = {}
Inventory.MAX_SLOTS = 50
Inventory.MAX_STACK = 999
function Inventory.Initialize(db)
Inventory.db = db
Inventory.players = db:Collection("players")
end
-- Get player inventory
function Inventory.Get(steamid, callback)
Inventory.players:FindOneAsync(
{ steamid = steamid },
function(err, player)
if err or not player then
callback({})
else
callback(player.inventory or {})
end
end
)
end
-- Add item to inventory
function Inventory.AddItem(steamid, itemId, itemName, quantity, callback)
quantity = quantity or 1
Inventory.players:FindOneAsync({ steamid = steamid }, function(err, player)
if err or not player then
if callback then callback(false, "Player not found") end
return
end
local inventory = player.inventory or {}
local added = false
-- Try to stack with existing item
for _, item in ipairs(inventory) do
if item.item_id == itemId then
local newQty = math.min(item.quantity + quantity, Inventory.MAX_STACK)
local actualAdded = newQty - item.quantity
item.quantity = newQty
added = true
quantity = quantity - actualAdded
break
end
end
-- Add as new item if not stacked (and we have remaining)
if not added and quantity > 0 then
if #inventory >= Inventory.MAX_SLOTS then
if callback then callback(false, "Inventory full") end
return
end
table.insert(inventory, {
item_id = itemId,
item_name = itemName,
quantity = math.min(quantity, Inventory.MAX_STACK),
added_at = os.time()
})
end
-- Save inventory
Inventory.players:UpdateOneAsync(
{ steamid = steamid },
{ ["$set"] = { inventory = inventory } },
function(err)
if callback then
callback(not err, err and "Save failed" or nil)
end
end
)
end)
end
-- Remove item from inventory
function Inventory.RemoveItem(steamid, itemId, quantity, callback)
quantity = quantity or 1
Inventory.players:FindOneAsync({ steamid = steamid }, function(err, player)
if err or not player then
if callback then callback(false, "Player not found") end
return
end
local inventory = player.inventory or {}
local removed = false
for i, item in ipairs(inventory) do
if item.item_id == itemId then
if item.quantity <= quantity then
table.remove(inventory, i)
else
item.quantity = item.quantity - quantity
end
removed = true
break
end
end
if not removed then
if callback then callback(false, "Item not found") end
return
end
Inventory.players:UpdateOneAsync(
{ steamid = steamid },
{ ["$set"] = { inventory = inventory } },
function(err)
if callback then
callback(not err, err and "Save failed" or nil)
end
end
)
end)
end
-- Check if player has item
function Inventory.HasItem(steamid, itemId, quantity, callback)
quantity = quantity or 1
Inventory.Get(steamid, function(inventory)
for _, item in ipairs(inventory) do
if item.item_id == itemId and item.quantity >= quantity then
callback(true, item.quantity)
return
end
end
callback(false, 0)
end)
end
-- Transfer item between players
function Inventory.Transfer(fromSteamid, toSteamid, itemId, quantity, callback)
quantity = quantity or 1
Inventory.HasItem(fromSteamid, itemId, quantity, function(has, currentQty)
if not has then
callback(false, "Insufficient items")
return
end
-- Get item name
Inventory.Get(fromSteamid, function(inv)
local itemName = ""
for _, item in ipairs(inv) do
if item.item_id == itemId then
itemName = item.item_name
break
end
end
-- Remove from source
Inventory.RemoveItem(fromSteamid, itemId, quantity, function(success)
if not success then
callback(false, "Failed to remove item")
return
end
-- Add to destination
Inventory.AddItem(toSteamid, itemId, itemName, quantity, function(success, err)
if not success then
-- Rollback
Inventory.AddItem(fromSteamid, itemId, itemName, quantity)
callback(false, "Failed to add item: " .. (err or ""))
else
callback(true)
end
end)
end)
end)
end)
end
-- Get inventory statistics
function Inventory.GetStats(steamid, callback)
Inventory.Get(steamid, function(inventory)
local stats = {
total_items = 0,
unique_items = #inventory,
slots_used = #inventory,
slots_free = Inventory.MAX_SLOTS - #inventory
}
for _, item in ipairs(inventory) do
stats.total_items = stats.total_items + item.quantity
end
callback(stats)
end)
end
return Inventory
GMod Integration
--[[
Garry's Mod Integration
Hooks and commands for player data system
]]
require("mongo")
-- Initialize on server start
hook.Add("Initialize", "InitPlayerData", function()
local success = PlayerData.Initialize(
"mongodb://localhost:27017",
"gmod_server"
)
if success then
print("[Server] Player data system ready")
else
print("[Server] WARNING: Player data system failed!")
end
end)
-- Load player data on join
hook.Add("PlayerInitialSpawn", "LoadPlayerData", function(ply)
local steamid = ply:SteamID()
PlayerData.Load(steamid, function(data, err)
if err then
print("[Server] Failed to load data for", ply:Nick())
return
end
-- Store data on player
ply.dbData = data
-- Set networked variables
ply:SetNWInt("Level", data.level)
ply:SetNWInt("Credits", data.credits)
-- Initialize inventory system
Inventory.Initialize(PlayerData.db)
print("[Server] Loaded data for", ply:Nick(), "Level:", data.level)
end)
end)
-- Auto-save every 5 minutes
timer.Create("AutoSavePlayerData", 300, 0, function()
for _, ply in ipairs(player.GetAll()) do
if ply.dbData then
local data = {
credits = ply.dbData.credits,
level = ply.dbData.level,
experience = ply.dbData.experience,
playtime = ply.dbData.playtime + 300,
stats = ply.dbData.stats
}
PlayerData.Save(ply:SteamID(), data)
end
end
print("[Server] Auto-save complete")
end)
-- Save on disconnect
hook.Add("PlayerDisconnected", "SavePlayerData", function(ply)
if ply.dbData then
PlayerData.Save(ply:SteamID(), {
credits = ply.dbData.credits,
level = ply.dbData.level,
experience = ply.dbData.experience,
stats = ply.dbData.stats,
last_logout = os.time()
})
print("[Server] Saved data for disconnecting", ply:Nick())
end
end)
-- Track kills
hook.Add("PlayerDeath", "TrackKillStats", function(victim, inflictor, attacker)
if IsValid(victim) and victim:IsPlayer() then
PlayerData.IncrementStat(victim:SteamID(), "deaths", 1)
end
if IsValid(attacker) and attacker:IsPlayer() and attacker ~= victim then
PlayerData.IncrementStat(attacker:SteamID(), "kills", 1)
-- Award experience
PlayerData.AddExperience(attacker:SteamID(), 100, function(leveledUp, newLevel)
if leveledUp then
attacker:ChatPrint("Congratulations! You reached level " .. newLevel .. "!")
attacker:SetNWInt("Level", newLevel)
end
end)
end
end)
-- Console command to check stats
concommand.Add("stats", function(ply)
if not IsValid(ply) then return end
PlayerData.GetStats(ply:SteamID(), function(stats)
if stats then
ply:ChatPrint("--- Your Stats ---")
ply:ChatPrint("Kills: " .. stats.kills)
ply:ChatPrint("Deaths: " .. stats.deaths)
ply:ChatPrint("Assists: " .. stats.assists)
end
end)
end)
Next Steps
- Game Systems - Leaderboards and achievements
- Administration - Bans and logging