找回密码
 register
搜索
查看: 11|回复: 0

洱海月 帮会递交过期银票没有反应修复

[复制链接]
  • 打卡等级:本地老炮
  • 打卡总天数:533
  • 打卡月天数:22
  • 打卡总奖励:530
  • 最近打卡:2026-06-24 01:45:59
Waylee 发表于 2026-4-10 11:49 | 显示全部楼层 |阅读模式 | Google Chrome | Windows 10

马上注册,查看网站隐藏内容!!

您需要 登录 才可以下载或查看,没有账号?register

×
20/04/26 23:49:34.70 [:000000a8] lua call [382 to :a8 : 0 msgsz = 203] error : ./framework/lualib/skynet.lua:988: ./framework/lualib/skynet.lua:452: ./lualib/iostream.lua:163: bad argument #2 to 'pack' (unsigned overflow)
stack traceback:
    [C]: in function 'string.pack'
    ./lualib/iostream.lua:163: in method 'writeuint'
    ./services/game/packet.lua:10638: in method 'bos'
    ./lualib/net.lua:196: in function 'net.send2client'
    ./services/msgagent.lua:457: in local 'f'
    ./services/msgagent.lua:3284: in upvalue 'f'
    ./framework/lualib/skynet.lua:402: in function <./framework/lualib/skynet.lua:374>
stack traceback:
    [C]: in function 'assert'
    ./framework/lualib/skynet.lua:988: in function 'skynet.dispatch_message'

帮会城市递交过期银票报 unsigned overflow 的最终修法

最近在补帮会城市老脚本时,又遇到一个很有代表性的兼容问题。

玩家在钱庄 NPC 处点击“递交过期银票”后,服务端直接抛出一段很吓人的错误日志:

bad argument #2 to 'pack' (unsigned overflow)

表面上看,很多人第一反应会怀疑:

  • 是不是银票金额太大了
  • 是不是帮会资金溢出了
  • 是不是广播消息字符串有问题

但这次真正的根因,其实和金额、广播都没关系,而是一个非常典型的“老脚本负数哨兵值”和 Lua uint32 打包规则不兼容的问题。

这篇就不写中间排查过程了,只写最终稳定方案,方便后面直接照着改。

一、先说最终结论

这次最终只需要修改一个文件:

  • \home\ubuntu\Game2\lualib\script_base.lua

不需要改客户端。
也不建议只在 city0_building5.lua 单点硬修。

最终修法的核心思路只有一句话:

老脚本里传入的 -1,在需要写入 uint32 的脚本事件封包里,先转换成 0xFFFFFFFF 再发给客户端。

这样做以后:

  • “递交过期银票”恢复正常
  • 不会再触发 string.pack("I4", -1)unsigned overflow
  • 以后其他仍然沿用老脚本写法的同类场景,也能一起受益

二、问题到底出在哪

当前帮会城市钱庄脚本里,“递交过期银票”的入口在:

  • \home\ubuntu\Game2\services\scripts\city0_building5.lua

关键代码是这段:

elseif index == 7 then
    self:BeginEvent(self.script_id)
    self:AddText("请把过期银票拖入到第一个物品格中!")
    self:EndEvent()
    self:DispatchMissionDemandInfo(selfId, targetId, self.script_id, -1, 2)

注意第四个参数:

-1

这在老脚本时代其实是很常见的写法,它表示一个“无任务 ID”或“占位任务 ID”的哨兵值。

问题在于,Lua 版服务端这里最终走的是:

  • script_base:DispatchMissionDemandInfo
  • packet.GCScriptCommand
  • m_nCmdID == 3

而这个分支在发包时,会把 m_idMission 当成 uint32 去写:

stream:writeuint(self.m_idMission)

Lua 的 string.pack("I4", n) 不能直接接受 -1
所以一旦这里把 -1 原样塞进去,就会立刻触发:

bad argument #2 to 'pack' (unsigned overflow)

三、为什么我不建议直接改 city0_building5.lua

有人会说,那不是很简单吗?
把这里的:

-1

直接改成:

0

或者别的正数不就好了。

这种改法短期能躲过这一个点,但我不建议这样做。

原因很简单:

  • 老脚本里用 -1 作为哨兵值是历史惯例
  • 今天是 city0_building5.lua,明天别的脚本也可能继续这样传
  • 如果只改单个脚本,后面同类问题还会在别处重复出现

更稳的做法,是在兼容层把这类老写法统一吃掉。

也就是说:

  • 老脚本仍然可以继续传 -1
  • Lua 服务端在真正发 uint32 封包前,自动把负数转换成无符号值

这才是更适合 skynet Lua 迁移环境的修法。

四、最终修改写法

本次最终修改的位置在:

  • \home\ubuntu\Game2\lualib\script_base.lua

主要改了两个函数:

  • DispatchMissionInfo
  • DispatchMissionDemandInfo

1. DispatchMissionInfo 的最终写法

function script_base:DispatchMissionInfo(selfId, targetId, script_id, mission_id)
    local m_nCmdID = 1
    local ret = packet_def.GCScriptCommand.new()
    ret.m_nCmdID = m_nCmdID
    ret.event = self.event
    local target_value = tonumber(targetId or define.INVAILD_ID) or define.INVAILD_ID
    if target_value < 0 then
        target_value = target_value % 0x100000000
    end
    ret.target_id = target_value
    ret.size = #ret.event
    ret.m_objID = script_id
    ret.m_idMission = mission_id
    ret.m_yBonusCount = #self.m_aBonus
    self.scene:send2client(selfId, ret)
end

这里先把 target_id 做了数值归一化。

如果目标 ID 是负数,比如老接口里的 -1,就统一转成:

target_value % 0x100000000

也就是把它转换成 uint32 语义下的值。

2. DispatchMissionDemandInfo 的最终写法

***付费内容***
这才是这次修复的真正核心。

这里我做了三层兼容:

  1. targetId 先转成数字
  2. mission_id 先转成数字
  3. done 先转成数字

然后统一做一条规则:

如果值小于 0,就转成 uint32 语义下的等价值

也就是:

value = value % 0x100000000

举个最典型的例子:

-1 % 0x100000000

结果就是:

4294967295

这正好就是 0xFFFFFFFF,也正是老 C++ 时代里很多无效 ID 在无符号 32 位下的表达方式。

所以这样改完后:

  • 老脚本仍然保持原样
  • 发包层拿到的是合法的 uint32
  • string.pack("I4", n) 不会再报溢出

五、为什么这套修法更稳

我这次没有去动 city0_building5.lua 的那句 -1,原因很明确。

因为问题不是“这个脚本特例写错了”,而是:

当前 Lua 兼容层没有把老脚本里常见的负数哨兵值处理好。

如果只改单点脚本,会有几个明显问题:

  • 以后别的老脚本还会继续踩同样的坑
  • 同类问题会在 DispatchMissionInfoDispatchMissionDemandInfo 这类封包分发函数外不断重复
  • 逻辑会越来越碎,后面不好维护

而把修复放到 script_base.lua 的好处是:

  • 一次修复,所有同类脚本共同受益
  • 老脚本可以继续按历史写法保留
  • 兼容层职责更清晰

这就是为什么我把它定义成“兼容层修复”,而不是“单脚本热补”。

付费看帖
剩余 12% 内容需要支付 50.00 金币 后可完整阅读
支持付费阅读,激励作者创作更好的作品。
您需要登录后才可以回帖 登录 | register

本版积分规则

QQ|雪舞知识库 ( 浙ICP备15015590号-1 | 萌ICP备20232229号|浙公网安备33048102000118号 )|天天打卡

GMT+8, 2026-6-24 05:29 , Processed in 0.071468 second(s), 25 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表