7、交易与拍卖行校验:异常价格检测、刷钱行为识别、物品复制漏洞防范

交易系统和拍卖行,说白了就是游戏经济的「大动脉」。我见过太多项目,战斗系统做得再牛,经济一崩盘,玩家全跑了。今天咱们就聊聊怎么守住这条生命线。

7.1 异常价格检测:别让市场变成过山车

价格异常,是刷钱和转移资产的前兆。我习惯从三个维度来卡这个口子。

7.1.1 静态价格阈值

每个物品都有个「合理价格区间」。比如一把新手剑,系统定价100金币,那成交价就不能低于10金币,也不能高于1000金币。这个阈值怎么定?

  • 基础价:策划填表时给的参考价
  • 浮动范围:我一般设 ±80%,太宽了等于没设
  • 特殊物品:绑定物品、任务物品,直接禁止交易

核心逻辑:任何交易价格超出 [basePrice * 0.2, basePrice * 5] 这个区间,直接拦截并报警。

7.1.2 动态价格基线

静态阈值太死板了。你想想看,一件极品装备,全服就一把,卖贵点很正常。这时候就需要动态基线。

我做过一个方案:

// 伪代码:动态价格基线计算
class PriceBaseline {
    // 滑动窗口,取最近100笔成交记录
    windowSize = 100;
    
    // 计算中位数,排除极端值
    getBaseline(itemId) {
        prices = getRecentTrades(itemId, this.windowSize);
        sorted = prices.sort();
        // 去掉最高最低各10%
        trimmed = sorted.slice(10, 90);
        return median(trimmed);
    }
    
    // 当前价格偏离基线超过300%,直接拒绝
    validate(price, itemId) {
        baseline = this.getBaseline(itemId);
        if (price > baseline * 4 || price < baseline * 0.25) {
            return false; // 异常
        }
        return true;
    }
}

嗯,这里要注意:新物品刚上线时,历史数据是空的。我建议先用策划填的参考价作为初始基线,等攒够50笔真实交易后再切换到动态模式。

7.1.3 频次与总量控制

价格正常,不代表交易正常。我曾经遇到过一个案例:一个玩家用1金币买了1000个稀有材料,每个材料市场价100金币。单看价格,1金币确实异常,但更诡异的是——这个卖家在10秒内上架了1000个材料。

所以,频次和总量必须一起查:

维度 阈值 说明
单角色日交易次数 ≤ 200次 超过直接临时封禁
单角色日交易总额 ≤ 等级 * 10000 等级越低,上限越低
同IP交易对 ≤ 50次/天 防止小号刷钱
新角色交易限制 注册满7天 防脚本批量建号

7.2 刷钱行为识别:从源头掐断

刷钱,说白了就是「低买高卖」的极端版本。但外挂不会傻到直接刷金币,他们通常会走一套「洗钱流程」。

7.2.1 资金流向追踪

我建议给每个角色建一个「资金流向图」。不是实时全量,而是采样记录:

  • 收入来源:打怪、任务、交易、拍卖行
  • 支出去向:购买装备、学习技能、交易给他人
  • 异常模式:收入全是交易所得,支出全是转给同一个号

个人经验:我见过最隐蔽的刷钱方式,是用两个号互相买卖垃圾材料,每次价格只偏离10%,但一天交易几千次。这种「温水煮青蛙」式的刷钱,单看价格完全正常,但资金流向图一画就露馅了——两个号之间形成了闭环。

7.2.2 行为特征分析

刷钱脚本和正常玩家,行为模式差别很大。我总结了几条:

  1. 操作间隔:脚本的操作间隔几乎恒定,比如每0.5秒点一次。正常玩家会有波动。
  2. 鼠标轨迹:脚本的鼠标移动是直线,玩家是曲线。这个在客户端可以采集。
  3. 在线时长:脚本可以24小时在线,玩家做不到。
  4. 多开检测:同IP下超过3个角色同时交易,基本就是工作室。

为什么会这样?因为写外挂的人,通常只关注「功能实现」,很少去模拟「人类行为」。你想想看,正常人谁会凌晨4点准时上线,用同样的速度操作2小时?

7.2.3 经济模型校验

从宏观层面,游戏经济有个「总产出 = 总消耗 + 总沉淀」的恒等式。如果某个服务器的金币总量突然暴涨,那一定有问题。

我习惯每天跑一次全服经济快照:

-- SQL示例:每日经济快照
INSERT INTO economy_snapshot (server_id, snapshot_date, total_gold, total_items, active_users)
SELECT 
    server_id,
    CURDATE(),
    SUM(gold),
    COUNT(item_id),
    COUNT(DISTINCT user_id)
FROM player_assets
WHERE last_login >= DATE_SUB(NOW(), INTERVAL 7 DAY);

如果某天金币总量环比增长超过20%,我会立刻拉出所有大额交易记录,逐条排查。

7.3 物品复制漏洞防范:最致命的漏洞

物品复制,是游戏界的「核武器」。一旦出现,经济体系直接崩溃。我经历过一次,那感觉……嗯,不想再经历第二次。

7.3.1 复制漏洞的常见成因

漏洞类型 典型场景 危害等级
网络延迟导致的双花 玩家在交易确认前断线,物品扣除了但没给对方
数据库事务未加锁 并发请求下,同一物品被多次取出 极高
拍卖行竞拍回滚 竞拍结束时,利用回滚机制同时拿到物品和金币
邮件系统漏洞 附件物品在领取时,服务器未做幂等校验

7.3.2 核心防御:事务与锁

所有涉及物品转移的操作,必须放在数据库事务里。我个人的铁律是:

-- 伪代码:安全的物品转移
BEGIN TRANSACTION;
    -- 第一步:锁定双方行
    SELECT * FROM player_items WHERE owner_id = ? FOR UPDATE;
    SELECT * FROM player_items WHERE owner_id = ? FOR UPDATE;
    
    -- 第二步:检查物品是否存在
    IF (source_item.count < 1) THEN
        ROLLBACK;
        RETURN '物品不存在';
    END IF;
    
    -- 第三步:转移
    UPDATE player_items SET count = count - 1 WHERE id = ?;
    UPDATE player_items SET count = count + 1 WHERE id = ?;
    
    -- 第四步:记录日志
    INSERT INTO trade_log (from_id, to_id, item_id, time) VALUES (?, ?, ?, NOW());
COMMIT;

注意:千万不要用「先扣再还」的逻辑。比如玩家A给B物品,先扣A的,如果B的背包满了,再还给A。这个「还」的过程一旦出问题,物品就凭空消失了——或者更糟,被复制了。

7.3.3 幂等性与唯一ID

每个交易请求,必须携带一个全局唯一的请求ID。服务器收到请求后,先查这个ID是否处理过。如果处理过,直接返回上次的结果,不再执行。

我见过一个案例:玩家用脚本快速点击「领取邮件附件」,由于网络抖动,同一个请求被发送了两次。服务器第一次处理成功,第二次又处理了一次——结果玩家拿到了双倍物品。

解决方案很简单:

// 幂等校验
if (redis.exists(requestId)) {
    return redis.get(requestId); // 返回上次结果
}
// 执行真正的业务逻辑
result = doTransfer();
// 缓存结果
redis.set(requestId, result, 3600);
return result;

7.3.4 物品唯一标识与追踪

对于高价值物品(比如极品装备、稀有材料),我建议使用「物品实例ID」。每个物品从产出到销毁,都有一个唯一的ID。这样,即使出现复制,也能通过ID追踪到所有副本。

举个例子:

  • 物品A的实例ID是 item_123456
  • 正常情况下,全服只能有一个 item_123456
  • 如果数据库里出现了两个 item_123456,那一定是复制了

我习惯每天跑一次全服物品实例去重检查:

-- 检测重复实例
SELECT instance_id, COUNT(*) as cnt
FROM player_items
WHERE instance_id IS NOT NULL
GROUP BY instance_id
HAVING cnt > 1;

一旦发现重复,立刻冻结所有相关账号,人工介入处理。

7.4 架构层面的统一校验

说了这么多,其实核心就一句话:所有交易行为,必须经过统一的经济校验服务。不要在每个业务模块里各自写校验逻辑,那样容易漏,也容易不一致。

我画了一张图,展示校验流程:

交易与拍卖行校验架构 交易/拍卖请求 统一经济校验服务 价格校验模块 行为校验模块 复制检测模块 静态阈值 动态基线 频次/总量控制 资金流向追踪 操作行为分析 经济模型校验 事务与锁 幂等性校验 物品实例追踪 通过 → 执行交易 / 拒绝 → 记录日志+报警

你看,所有请求先进统一校验服务,然后并行跑三个模块。任何一个模块返回拒绝,整个交易就终止。这样设计的好处是:

  • 扩展性强:以后加新的校验规则,加一个模块就行
  • 性能可控:三个模块可以部署在不同的进程里,并行执行
  • 日志完整:每个请求的校验结果都记录,方便事后追溯

避坑指南:我曾经把校验逻辑写在业务代码里,结果业务迭代时,产品经理说「这个功能先上线,校验后面再加」——然后就没有然后了。所以,校验服务一定要独立部署,独立迭代,不受业务排期影响。

好了,交易与拍卖行的校验,核心就是这些。记住:价格、行为、复制,三个维度缺一不可。别等到经济崩了才想起来补漏洞,那时候就晚了。


公众号:蓝海资料掘金营,微信deep3321