查看“︁工程参考/回收系统”︁的源代码
来自雪舞天龙知识库
←
工程参考/回收系统
跳到导航
跳到搜索
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
__NOTOC__ <div class="lua-reference"> <div class="lua-lead"> <div role="heading" aria-level="2" class="lua-h1">回收系统</div> <div class="lua-sub">2 张表 · 214 条回收价 · 13 个结果状态</div> </div> 回收系统把背包里的物品按表价换成元宝:客户端选物品 → 服务端逐格报价 → 玩家确认 → 在同一个背包事务里扣物品、加元宝、记当日额度。 查一件物品能回收多少 → [[工程参考/配置文件/Public/ItemRecycle.txt|ItemRecycle.txt]];改每日上限 → [[工程参考/配置文件/Public/ItemRecycleSettings.txt|ItemRecycleSettings.txt]];判断某件物品为什么收不了 → 看下面的[[#资格判定|资格判定]]。 == 先分清三个「回收」 == 名字里都有「回收」,但货币、配置表和代码路径完全不同,先对号入座再改表。 {| class="wikitable" ! scope="col" style="width:18%" | 系统 ! scope="col" style="width:26%" | 玩家看到的效果 ! scope="col" style="width:38%" | 数据来源 ! scope="col" | 入口 |- | 物品回收(本页) | 背包物品按表价换成元宝 | ItemRecycle.txt、ItemRecycleSettings.txt | 客户端回收界面 |- | 玩家商店收购 | 把物品卖给店主换交子或金币 | RecycleItem.txt、RecycleItemClass.txt、RecycleItemType.txt | 玩家商店的收购菜单 |- | 物品箱回收次数 | 控制副本掉落箱允许被回收的次数 | 无配置表 | 副本脚本调 Lua 接口 |} <code>RecycleItem.txt</code> 那三张是商店收购白名单,和元宝回收没有任何关系;把物品加进那三张表不会让它变成可回收物品。 == 两张表 == {| class="wikitable" ! scope="col" style="width:26%" | 文件 ! scope="col" style="width:26%" | 结构 ! scope="col" style="width:36%" | 关键约束 ! scope="col" | 当前样本 |- | <code>ItemRecycle.txt</code> | 2 列 INT:<code>ItemID</code>、<code>YuanBao</code> | ItemID 必须严格递增;价格 1 到 2000000000 | 214 条,2 到 25000 元宝 |- | <code>ItemRecycleSettings.txt</code> | 2 列 INT,恰好 1 行:<code>ID</code>、<code>DailyLimit</code> | ID 必须为 1;DailyLimit 1 到 2000000000 | 1 行,10000000 |} 字段逐条解释见两张表的单独页面。两张表都放在 <code>Public/Config/</code> 下,客户端不加载,价格与资格全部由服务端算完随包下发。 == 加载与生效 == {| class="wikitable" ! scope="col" style="width:22%" | 环节 ! scope="col" style="width:50%" | 实现 ! scope="col" style="width:28%" | 结果 |- | 打开文件 | <code>GameTable.cpp</code> 里 <code>SetStrictIntegers(true)</code> 后 <code>OpenFromTXT</code> | 非整数直接解析失败 |- | 校验并载入 | <code>ItemRecycle::Config().Load(recycleItems, recycleSettings)</code> | 任一约束不满足,返回 FALSE 并清空已读价格 |- | 失败后果 | <code>TableInit::Init()</code> 返回 FALSE,调用方是 <code>Server.cpp</code> 的 <code>Assert(ret)</code> | 表错是起不了服,不是关掉功能 |} * 没有热重载入口:改完表要停服、替换实际 <code>Public/Config/</code> 副本、重启 Server。 * 没有功能开关:<code>FeatureOpen.ini</code> 里没有回收项,表加载成功系统就可用。 * 没有 GM 指令:<code>GMList.ini</code> 里没有回收命令,验证只能走客户端界面。 == 一次回收的五个动作 == {| class="wikitable" ! scope="col" style="width:12%" | 动作 ! scope="col" style="width:60%" | 服务端做什么 ! scope="col" style="width:28%" | 可能的返回 |- | Open | 分配会话 context,把整包 240 个格子逐格描述(编号、数量、单价、不可回收原因)回给客户端 | Catalog、Busy、Processing |- | Quote | 按客户端选中的格子做快照与报价,算当日已用额度 | Quoted、Changed、DailyExceeded、BalanceExceeded、Invalid |- | Commit | 复核快照后原子结算 | Succeeded、Failed、Expired、Changed、Busy |- | Query | 查询某个请求的最终状态,用于断线或超时后确认 | 原状态、Processing、OutcomeUnknown |- | Close | 清掉 context 与报价 | 不返回结果 |} 时序约束: * 两次报价间隔小于 200 毫秒会被拒(Busy),防止连点刷价。 * 报价有效期 30000 毫秒;超时提交返回 Expired。 * 提交一旦匹配上报价,'''无论后续校验是否通过,这份报价都会被消费掉''';重试必须重新报价。 * context 绑定打开时的场景;换场景后心跳会清空 context,旧报价全部作废。 == 资格判定 == === 整次回收的门禁 === 下列任一不满足,打开回收界面只会拿到 Busy: {| class="wikitable" ! scope="col" style="width:30%" | 条件 ! scope="col" style="width:70%" | 说明 |- | 角色与场景 | 玩家对象、Player、所在场景都存在,且处于可执行逻辑的状态 |- | 没有进行中的回收 | 同一个角色同时只能有一个回收会话 |- | 元宝入口空闲 | 元宝票没有待发邮件,<code>CurrencyBlocked</code> 为假 |- | 不在切场景 | <code>IsHaveChangeSceneFlag</code> 为假 |- | 没有待领奖 | <code>IsHaveAskPrizeFlag</code> 为假 |- | 不在摆摊 | <code>IsInStall</code> 为假 |- | 交易盒空闲 | <code>m_ExchangBox.m_Status</code> 为 EXCHANGE_NONE |- | 没有特定锁位 | <code>m_BitLock.m_uLowFlags</code> 的 0x800 位为 0 |- | 数据库不忙 | 不处于 DDZ 奖励结算的忙碌状态(<code>HappyDDZRewardBusy</code> 为假) |} === 逐格判定 === 服务端对每个背包格按固定顺序判定,命中即停,这就是客户端提示「不可回收」的原因: {| class="wikitable" ! scope="col" style="width:10%" | 顺序 ! scope="col" style="width:44%" | 判定 ! scope="col" style="width:20%" | 返回原因 ! scope="col" style="width:26%" | 玩家看到的含义 |- | 1 | 物品类型是任务物品(<code>ICLASS_TASKITEM</code>) | Quest | 任务物品永不回收 |- | 2 | 物品已绑定 | Bound | 绑定物品不可回收 |- | 3 | 有密码锁或物品锁 | Locked | 先解锁 |- | 4 | 已加锁或正在交易 | Occupied | 物品被占用 |- | 5 | 表里查不到价格 | NotConfigured | 这件物品没配回收价 |- | 6 | 装备强化过或镶了宝石 | 保护位 | 需要玩家勾选确认后才报价 |- | 7 | 以上都不命中 | Eligible | 可以回收 |} 价格查不到与价格为 0 是同一件事:服务端用 <code>Config().Price()</code> 取价,取不到就是 0,直接判 NotConfigured。但把价格写成 0 会让整表加载失败,所以要让某件物品不可回收只能删行。 == 价格、额度与上限 == * 单格收益 = 表价 × 该格数量。按格计算,不跨格合并,也不看物品在其它格子的堆叠。 * 每日额度取 <code>ItemRecycleSettings.txt</code> 的第二列,按'''角色'''记在任务数据第 180、181 槽:180 存日序号,181 存当日已得元宝。 * 日序号 = (Unix 秒 + 8 小时)÷ 86400,即东八区自然日。跨天不写回 0,而是「180 与今天不相等时,已用额度按 0 算」。 * 报价与提交都校验三件事:当日额度、元宝上限 2000000000、以及两者相加不溢出。 * 当前样本:额度 10000000(一千万)元宝每天,表内最高单价 25000 元宝,价格分布最集中的是 490 元宝 60 条、99 元宝 27 条、149 元宝 22 条。 额度是「每天最多换出多少元宝」,不是「每天能回收几次」。单价高的物品一两件就能打满,之后当天所有报价都会被判 DailyExceeded。 == 结算是一致性事务 == # 报价时把每个选中格子的完整 <code>_ITEM</code> 结构存进事务,不是只存编号和数量。 # 提交时重新逐格比对:GUID、物品编号、可回收原因、保护确认,以及整块 <code>_ITEM</code> 的内存比较。任一不同判 Changed。 # 通过后走 <code>CommitBagPlan</code>,扣物品、<code>SetYuanBao(..., YUANBAO_ITEM_RECYCLE, "物品回收")</code>、写 180 与 181 三件事在同一个事务里;任一步失败整体回滚,元宝与额度一起还原。 # 成功后客户端收到「回收成功,获得N元宝。」的系统提示。 元宝流水里这笔记录的备注是「物品回收」,回滚是「物品回收回滚」,对账时按这两个备注筛。 报价里还记了配置版本(FNV-1a,含额度)和额度值,提交时再比一次,不一致判 Expired。当前没有热重载入口,同一进程内这两项不会变;这段比较是给将来加热重载或跨进程留的保险。 == 结果状态 == {| class="wikitable" ! scope="col" style="width:18%" | 状态 ! scope="col" style="width:48%" | 含义 ! scope="col" style="width:34%" | 客户端处理 |- | Catalog | 打开成功,回包带整包目录 | 渲染列表 |- | Quoted | 报价成功,回包带本次选中的格子与总收益 | 展示确认框 |- | Processing | 上一次请求还在结算 | 等待,可继续发 Query |- | Succeeded | 结算成功 | 刷新背包与元宝 |- | Failed | 背包事务提交失败,已整体回滚 | 提示失败,可重新报价 |- | Changed | 选中物品或角色状态与报价时不一致 | 重新报价 |- | Expired | 报价超过 30 秒;或提交时配置版本、额度与报价时不一致(当前没有热重载,正常流程只会因超时触发) | 重新报价 |- | Busy | 门禁不满足、请求过快或正在结算 | 稍后重试 |- | DailyExceeded | 本次收益超过当日剩余额度 | 提示今日额度已满 |- | BalanceExceeded | 元宝余额加上本次收益会超过上限 | 提示元宝已达上限 |- | Invalid | 事务结构校验失败 | 重新报价 |- | OutcomeUnknown | 找不到对应的请求(重登、请求号不匹配) | 重新打开界面 |- | Unavailable | 结果结构未初始化 | 正常流程不会收到 |} == 和其余系统的互斥 == 回收会话进行中时,服务端会拦掉所有可能动到「已预留格子」的包:使用物品、拆分、背包换位、存仓与取仓、交易加物品、丢弃、卖给商店、摆摊上架;元宝相关包整类拦掉。被拦时提示「物品回收正在结算,相关物品暂不能操作。」 另外,元宝票有邮件在处理时整次回收直接判 Busy,避免两个元宝入口同时改余额。 == 常见改法 == === 让一件物品可回收 === 在 <code>ItemRecycle.txt</code> 追加一行,物品编号必须大于当前最后一条(50613006),价格填 1 到 2000000000。停服重启后生效。 === 改价 === 只改第二列,保持第一列顺序不动。服务端不读客户端,改完重启即可,客户端不需要更新。 === 改每日额度 === 改 <code>ItemRecycleSettings.txt</code> 的第二列,第一列必须保持 1,且文件只能有一行数据。 === 只清某个角色的额度 === 把该角色任务数据第 180 槽改成 0 或昨天、181 槽改成 0。服务端读到时发现日序号不等于今天,就把已用额度按 0 算。 === 停用整个系统 === 服务端没有开关,也没有「空表即关闭」的写法:<code>Load()</code> 要求物品表至少一条记录,清空同样会让启动失败。要停用只能改客户端脚本,让它不再调用打开回收界面的方法。 == 容易踩的坑 == === 是「严格递增」,不是「唯一」 === <code>Load()</code> 逐行比较当前 ItemID 必须大于上一行。乱序、重复、倒序都会让整表清空并导致启动失败。插入新条目只能追加到末尾,或者整段重排。 === 表头与类型是硬约束 === 两张表都必须恰好 2 列且两列都是 INT;<code>SetStrictIntegers(true)</code> 下任何空值、非整数都会让解析失败。不要加第三列备注,注释行也无法保留。 === settings 只能有一行 === <code>Load()</code> 要求 <code>GetRecordsNum() == 1</code>,且第一列必须是 1。多写一行、把 ID 写成 2 都会让服务端起不来。 === 额度按角色,不按账号 === 额度存在角色的任务数据里,换角色重新计算;同账号不同角色各自有额度。清额度也要按角色清。 === 只认背包 === 回收遍历的是 0 到 239 号背包格(数组长度 240)。装备栏、银行、临时背包里的物品不会出现在回收列表里,也不会被回收。 === 回收日志不是流水 === 结算只写 <code>CacheLog</code> 的调试日志,没有可重放的账本。道具找回、玩家申诉不能拿这份日志当依据,要查元宝流水里的「物品回收」备注。 === 材料价与单测基线绑定 === 20500、20501、20502 三个家族(精铁、棉布、秘银)的 1 到 3 级,样本价是 8、40、200,与 <code>ItemRecycle::BuiltinPrice()</code> 的基准值一致。改这几条价格,<code>Common/tests/ItemRecycle</code> 的基线断言会失败,需要同步测试。 == 相关页面 == * [[工程参考/配置文件/Public/ItemRecycle.txt|ItemRecycle.txt]]:回收价目表,逐列说明与修改实例 * [[工程参考/配置文件/Public/ItemRecycleSettings.txt|ItemRecycleSettings.txt]]:每日额度 * [[工程参考/配置文件索引|配置文件索引]]:所有配置表按用途分组 * [[Lua接口/服务端/SetItemBoxRecycleTimes|SetItemBoxRecycleTimes]]:物品箱回收次数,和本系统无关,别混用 </div>
返回
工程参考/回收系统
。
导航菜单
页面操作
页面
讨论
阅读
查看源代码
历史
页面操作
页面
讨论
更多
工具
个人工具
创建账号
登录
导航
首页
最近更改
随机页面
特殊页面
搜索
工具
链入页面
相关更改
页面信息