Skip to content

玩家系统示例

本示例展示如何使用 Sleet ORM 构建一个完整的 FiveM 玩家管理系统,包括注册、登录、数据管理和经验系统。

Schema 设计

lua
-- server/schema.lua
local sl = Sleet

local players = sl.table('players', {
    id = sl.serial().primaryKey().comment('玩家ID'),
    identifier = sl.varchar(64).notNull().unique().comment('Steam/Discord标识符'),
    name = sl.varchar(255).notNull().comment('玩家名称'),
    money = sl.int().default(5000).comment('现金'),
    bank = sl.int().default(25000).comment('银行存款'),
    level = sl.int().default(1).comment('等级'),
    experience = sl.bigint().default(0).comment('经验值'),
    playtime = sl.int().default(0).comment('游戏时长(分钟)'),
    is_admin = sl.boolean().default(false).comment('管理员'),
    is_banned = sl.boolean().default(false).comment('封禁状态'),
    banned_until = sl.timestamp().comment('封禁到期时间'),
    metadata = sl.json().comment('扩展数据'),
    last_seen = sl.timestamp().defaultNow().comment('最后在线时间'),
    created_at = sl.timestamp().defaultNow().comment('创建时间'),
    updated_at = sl.timestamp().defaultNow().onUpdate(sl.sql('NOW()')).comment('更新时间')
})

-- 成就表
local achievements = sl.table('achievements', {
    id = sl.serial().primaryKey(),
    name = sl.varchar(100).notNull().unique().comment('成就名称'),
    label = sl.varchar(255).notNull().comment('显示名称'),
    description = sl.text().comment('成就描述'),
    icon = sl.varchar(255).comment('图标'),
    reward_money = sl.int().default(0).comment('奖励金钱'),
    reward_exp = sl.int().default(0).comment('奖励经验'),
    is_active = sl.boolean().default(true).comment('是否启用')
})

-- 玩家成就表
local player_achievements = sl.table('player_achievements', {
    id = sl.serial().primaryKey(),
    player_id = sl.int().notNull().references(players.id).comment('玩家ID'),
    achievement_id = sl.int().notNull().references(achievements.id).comment('成就ID'),
    earned_at = sl.timestamp().defaultNow().comment('获得时间')
})

return {
    players = players,
    achievements = achievements,
    player_achievements = player_achievements
}

核心功能实现

玩家管理类

lua
-- server/player.lua
local sl = Sleet
local s = require 'server.schema'
local db = sl.connect()

local PlayerManager = {}

-- 获取或创建玩家
function PlayerManager.GetOrCreate(identifier, name)
    -- 先尝试获取现有玩家
    local existing = db.select()
        .from(s.players)
        .where(sl.eq(s.players.identifier, identifier))
        .limit(1)
        .execute()[1]
    
    if existing then
        -- 更新最后登录时间和名称
        db.update(s.players)
            .set({
                name = name,
                last_seen = sl.sql('NOW()')
            })
            .where(sl.eq(s.players.id, existing.id))
            .execute()
        
        return existing
    else
        -- 创建新玩家
        local newId = db.insert(s.players)
            .values({
                identifier = identifier,
                name = name,
                metadata = {
                    first_join = os.date('%Y-%m-%d %H:%M:%S')
                }
            })
            .execute()
        
        -- 返回新创建的玩家数据
        return db.select()
            .from(s.players)
            .where(sl.eq(s.players.id, newId))
            .limit(1)
            .execute()[1]
    end
end

-- 获取玩家信息
function PlayerManager.GetById(playerId)
    return db.select()
        .from(s.players)
        .where(sl.eq(s.players.id, playerId))
        .limit(1)
        .execute()[1]
end

function PlayerManager.GetByIdentifier(identifier)
    return db.select()
        .from(s.players)
        .where(sl.eq(s.players.identifier, identifier))
        .limit(1)
        .execute()[1]
end

-- 更新玩家金钱(安全方式)
function PlayerManager.AddMoney(playerId, amount, reason)
    reason = reason or '未知原因'
    
    local affected = db.update(s.players)
        .set({
            money = sl.sql('money + ?', { amount }),
            updated_at = sl.sql('NOW()')
        })
        .where(sl.and_(
            sl.eq(s.players.id, playerId),
            sl.gte(sl.sql('money + ?', { amount }), 0) -- 防止负数
        ))
        .execute()
    
    if affected > 0 then
        print(string.format('玩家 %d 金钱变化: %+d (原因: %s)', playerId, amount, reason))
        return true
    end
    
    return false
end

-- 银行操作
function PlayerManager.AddBank(playerId, amount, reason)
    reason = reason or '银行操作'
    
    local affected = db.update(s.players)
        .set({
            bank = sl.sql('bank + ?', { amount }),
            updated_at = sl.sql('NOW()')
        })
        .where(sl.and_(
            sl.eq(s.players.id, playerId),
            sl.gte(sl.sql('bank + ?', { amount }), 0)
        ))
        .execute()
    
    return affected > 0
end

return PlayerManager

经验等级系统

lua
-- server/experience.lua
local sl = Sleet
local s = require 'server.schema'
local db = sl.connect()

local ExperienceManager = {}

-- 计算等级所需经验(每级需要的经验递增)
function ExperienceManager.GetRequiredExp(level)
    return math.floor(100 * (level ^ 1.5))
end

-- 根据经验计算等级
function ExperienceManager.CalcLevel(experience)
    local level = 1
    while experience >= ExperienceManager.GetRequiredExp(level + 1) do
        level = level + 1
    end
    return level
end

-- 给予经验
function ExperienceManager.GiveExperience(playerId, amount, reason)
    reason = reason or '获得经验'
    
    -- 获取当前玩家数据
    local player = db.select({ s.players.level, s.players.experience })
        .from(s.players)
        .where(sl.eq(s.players.id, playerId))
        .limit(1)
        .execute()[1]
    
    if not player then
        return false, '玩家不存在'
    end
    
    local newExp = player.experience + amount
    local newLevel = ExperienceManager.CalcLevel(newExp)
    local oldLevel = player.level
    
    -- 更新经验和等级
    db.update(s.players)
        .set({
            experience = newExp,
            level = newLevel,
            updated_at = sl.sql('NOW()')
        })
        .where(sl.eq(s.players.id, playerId))
        .execute()
    
    -- 检查是否升级
    if newLevel > oldLevel then
        print(string.format('玩家 %d 升级: %d -> %d', playerId, oldLevel, newLevel))
        -- 这里可以触发升级奖励
        ExperienceManager.OnLevelUp(playerId, oldLevel, newLevel)
    end
    
    return true, newLevel
end

-- 升级回调
function ExperienceManager.OnLevelUp(playerId, oldLevel, newLevel)
    -- 升级奖励金钱
    local rewardMoney = (newLevel - oldLevel) * 1000
    
    local PlayerManager = require 'server.player'
    PlayerManager.AddMoney(playerId, rewardMoney, '升级奖励')
    
    -- 这里可以触发其他升级效果
    TriggerClientEvent('player:levelUp', playerId, {
        oldLevel = oldLevel,
        newLevel = newLevel,
        rewardMoney = rewardMoney
    })
end

return ExperienceManager

更多内容请查看完整实现...

Released under the MIT License.