1. 数据常量(RationalityData.lua)
lua
M.INITIAL_VALUE = 100 -- 新玩家初始理性值
M.MAX_VALUE = 100 -- 理论上限
M.REMEDY_MAX_VALUE = 90 -- 药剂恢复封顶
M.REMEDY_REQUIRED_MAX_VALUE = 89 -- 药剂可用阈值(理性≤89)
M.REMEDY_COST = 3 -- 药剂价格(宝石)
M.REMEDY_RECOVERY = 10 -- 药剂恢复量
2. 事件与持久化 Key(Shared.lua)
lua
-- 远程事件
SHOP_RESULT = "BullBear_ShopResult",
RATIONALITY_REMEDY_RESULT = "BullBear_RationalityRemedyResult",
-- serverCloud 持久化 key
ACCOUNT_GEMS_KEY = "friendship_total_gems",
REMEDY_CHARGES_KEY = "friendship_remedy_charges",
RATIONALITY_KEY = "friendship_rationality_value",
3. 服务端内存缓存(Server.lua 全局 S 表)
lua
S.userIdRationalityCache_ = {} -- userId → 理性值
S.userIdAccountGemsCache_ = {} -- userId → 账户宝石
S.userIdRemedyChargesCache_ = {} -- userId → 背包药剂数
4. 核心增量写入(ServerRationality.lua ApplyValue)
lua
local function ApplyValue(p, value, reason)
local oldValue = p.rationality or Data.INITIAL_VALUE
local newValue = Data.Clamp(value)
p.rationality = newValue
-- ... tier/weather/revision 更新 ...
-- 1. 内存缓存(WASM刷新不丢失,服务端进程持续)
if p.userId then
S.userIdRationalityCache_[p.userId] = newValue
end
-- 2. 云端增量写入(serverCloud:Add,和宝石/药剂一致的可靠方式)
if p.userId and serverCloud and reason ~= "cloud_load" and reason ~= "cache_load" then
local cloudBase = p.rationalityCloudValue or 0
local delta = newValue - cloudBase
if delta ~= 0 then
serverCloud:Add(p.userId, Shared.CONFIG.RATIONALITY_KEY, delta, {
ok = function(_, iscores)
local synced = newValue
if iscores and iscores[Shared.CONFIG.RATIONALITY_KEY] then
synced = iscores[Shared.CONFIG.RATIONALITY_KEY]
end
p.rationalityCloudValue = synced
end,
})
p.rationalityCloudValue = newValue
end
end
return newValue ~= oldValue
end
5. 两层读取(Server.lua LoadRationality)
lua
function Server.LoadRationality(slot)
local p = S.players_[slot]
local userId = p.userId
local key = Shared.CONFIG.RATIONALITY_KEY
-- 第1层:内存缓存命中 → 零延迟同步发送
local cached = S.userIdRationalityCache_[userId]
if cached ~= nil then
p.rationalityCloudValue = cached
Server.RationalityApplyValue(slot, cached, "cache_load")
Server.SendPlayerInfoToConnection(p.connection)
end
-- 第2层:异步 serverCloud:Get
serverCloud:Get(userId, key, {
ok = function(scores, iscores)
local cloudRaw = (iscores and iscores[key]) or 0
local value = cloudRaw > 0 and cloudRaw or RationalityData.INITIAL_VALUE
p.rationalityCloudValue = cloudRaw
if cached == nil then
-- 无缓存:以云端值为准
S.userIdRationalityCache_[userId] = value
Server.RationalityApplyValue(slot, value, "cloud_load")
-- 新玩家(cloudRaw=0)首次写入100
if cloudRaw == 0 then
serverCloud:Add(userId, key, RationalityData.INITIAL_VALUE, {})
end
else
-- 缓存命中:云端过期则 Add(delta) 回写
if cloudRaw ~= cached then
serverCloud:Add(userId, key, cached - cloudRaw, {})
end
end
end,
})
end
6. 购买药剂(ServerRationality.lua HandleBuyRemedy)
lua
-- 单线程内存权威扣费
p.accountGems = (p.accountGems or 0) - cost
p.remedyCharges = (p.remedyCharges or 0) + 1
-- 更新内存缓存
if p.userId then
S.userIdAccountGemsCache_[p.userId] = p.accountGems
S.userIdRemedyChargesCache_[p.userId] = p.remedyCharges
end
-- 异步持久化两个 key(serverCloud:Add 增量)
serverCloud:Add(userId, ACCOUNT_GEMS_KEY, -cost, { ... })
serverCloud:Add(userId, REMEDY_CHARGES_KEY, 1, { ... })
7. 使用药剂(ServerRationality.lua HandleRationalityRemedy)
lua
-- 检查背包 > 0、阶段/局次限制、理性 ≤ 89
local newValue = math.min(current + Data.REMEDY_RECOVERY, Data.REMEDY_MAX_VALUE)
p.remedyCharges = p.remedyCharges - 1
-- ApplyValue 内部自动写内存缓存 + serverCloud:Add(delta)
ApplyValue(p, newValue, "remedy")
-- 更新药剂缓存
S.userIdRemedyChargesCache_[p.userId] = p.remedyCharges
serverCloud:Add(p.userId, REMEDY_CHARGES_KEY, -1, { ... })
8. 关键设计经验
| 原则 | 说明 |
| 内存缓存优先 | S.userIdXxxCache_[userId] 在每次变化时立即更新,WASM刷新不丢失 |
| 增量写入(Add 替代 Set) | 整数持久化用 serverCloud:Add(key, delta),原子累加,比 Set 覆盖可靠 |
| cloudValue 追踪基准 | p.rationalityCloudValue 记录上次同步到云端的值,计算 delta 避免重复累加 |
| cloud_load/cache_load 不回写 | 从云端/缓存加载时 reason 标记,ApplyValue 跳过云端写入避免循环 |
| 连接时不重置 | ClientConnected 不再调用 RationalityResetPlayer,等 LoadRationality 加载正确值 |
| 人类玩家跨局保留 | ResetPlayer(p, force) 检查 p.userId,有 userId 时跳过重置(AI 仍重置) |
| 服务端进程不重启 | WASM 刷新只重启客户端,服务端进程持续运行,内存缓存跨刷新存活 |