-- 采集前触发
function collectmonex(actor, mon_idx, mon_name, mon_uid)
func_collectmonex(actor, mon_idx, mon_name, mon_uid)
end
-- 采集跳转
function func_collectmonex(actor, mon_idx, mon_name, mon_uid)
local playerId = getbaseinfo(actor, 3) -- 获取玩家
🤖 回答引擎: 后端Lua |
🕒 时间: 2026-06-17 20:33:56 |
👁️ 阅读: 11
我是lua后端老师!
小仙,你的问题很有意思,这确实是个典型的并发锁设计问题。根据你提供的代码,关键在于锁的键(`lockKey`)构建方式。
当前,你的锁键是用 `玩家账号ID` + `怪物UID` 来构建的,例如 `"123456_789"`。
在检查玩家是否已经锁定怪物时,你遍历了整个锁表,寻找键中是否包含 `玩家账号ID` 的字符串(`string.find(key, playerId .. "_")`)。
**问题所在:**
因为你是用同一个账号登录了两个角色,所以这两个角色的 `玩家账号ID`(通过 `getbaseinfo(actor, 3)` 获取)是**完全相同**的。
当角色A开始采集并成功写入锁 `"账号ID_怪物UID1"` 后,角色B在开始采集前的检查中,会遍历到角色A创建的锁,发现键中包含自己的账号ID,于是就会触发“你已经在采集一个灵兽蛋了”的提示。
**解决方案:**
我们需要将锁的粒度从“账号级别”细化到“角色级别”。也就是说,锁不应该和账号绑定,而应该和具体的游戏角色绑定。
最直接的角色唯一标识是 **玩家对象(`actor`)本身**,它是一个字符串形式的ID,在同一时间、同一服务器内是绝对唯一的,即使同账号下的不同角色也不同。
因此,我们需要修改锁键的构建逻辑和检查逻辑:
1. **修改锁键**:将锁键从 `playerId .. "_" .. mon_uid` 改为 `actor .. "_" .. mon_uid`。这样锁就与具体角色绑定了。
2. **修改检查逻辑**:在 `func_collectmonex` 函数中,检查玩家是否已锁定时,不再查找 `playerId`,而是查找 `actor`。
下面是修改后的关键代码部分:
```lua
-- 采集跳转
function func_collectmonex(actor, mon_idx, mon_name, mon_uid)
-- 不再使用 playerId 构建锁键
-- local playerId = getbaseinfo(actor, 3) -- 这行可以注释或删除,如果其他地方不用的话
local lockKey = actor .. "_" .. mon_uid -- 关键修改:使用 actor 作为锁键的一部分
-- 灵兽蛋采集逻辑
if mon_name == "灵兽蛋[初级]" or mon_name == "灵兽蛋[中级]" or mon_name == "灵兽蛋[高级]" then
-- ... [前面的每日次数检查等逻辑不变] ...
-- 3. 检查这个怪物是否已被其他玩家(任何角色)锁定 (这步检查保留,防止多个角色抢同一个怪)
for key, timestamp in pairs(tbl) do
if string.find(key, "_" .. mon_uid) then
sendmsg(actor, 1, '{"Msg":"<font color=\'#ff0000\'>当前灵兽蛋已被其他玩家锁定!!</font>","Type":9}')
return
end
end
-- 4. 检查这个【角色】(而不是账号)是否已经锁定了一个怪物
for key, timestamp in pairs(tbl) do
-- 关键修改:查找键中是否包含当前角色的 actor ID
if string.find(key, actor .. "_") then
sendmsg(actor, 1, '{"Msg":"<font color=\'#ff0000\'>你已经在采集一个灵兽蛋了!!</font>","Type":9}')
return
end
end
-- ... [后面的逻辑,包括获取怪物、写入锁、存储变量、显示进度条等均不变] ...
-- 注意:存储到玩家变量的信息不需要改,因为那是角色独立的。
end
-- ... [酒神材料采集逻辑] ...
end
```
```lua
-- 灵兽蛋采集成功触发
function caiji_true(actor)
-- local playerId = getbaseinfo(actor, 3) -- 这行不再需要
local monsterUid = getplaydef(actor, "S$采集怪物UID")
local lockKey = actor .. "_" .. monsterUid -- 关键修改:锁键与 actor 对应
-- ... [后面的原子化读取、验证锁、执行成功逻辑、清理锁和变量等均不需要改动] ...
-- 因为清理锁时是根据 lockKey 来删除的,而 lockKey 已经改为 actor 相关,所以能正确清理当前角色的锁。
end
```
```lua
-- 灵兽蛋采集失败/中断触发
function caiji_false(actor)
-- local playerId = getbaseinfo(actor, 3) -- 这行不再需要
local monsterUid = getplaydef(actor, "S$采集怪物UID")
if monsterUid == "" then
return
end
local lockKey = actor .. "_" .. monsterUid -- 关键修改
-- ... [后面的清理逻辑不需要改动] ...
end
```
**总结一下修改点:**
* 将构建 `lockKey` 时使用的 `playerId` 替换为 `actor`。
* 在 `func_collectmonex` 中检查“角色是否已锁定怪物”时,查找条件从 `playerId .. "_"` 改为 `actor .. "_"`。
这样修改后,同一个账号下的不同角色,因为 `actor` 值不同,他们的锁就完全独立了,可以同时采集不同的灵兽蛋,互不干扰。但是,他们仍然不能同时采集**同一个**灵兽蛋(因为第3步的怪物UID检查依然有效),这符合你的设计预期。
快去试试吧,小仙!如果还有不明白的,随时可以再来问我,或者请教我的师傅浮生梦尊者哦!
← 返回问答库