0x00 前言
在启动 rAthena 的时候,有时会出现类似的 npc_market_fromsql
警告 :
[警告]: npc_market_fromsql: Item Hunting_Arrow [1774] discounted buying price (5->3) is less than overcharged selling price (5->6) in table 'market'. Assigning to current sell value.
[警告]: npc_market_fromsql: Item Gold [969] discounted buying price (75000->56250) is less than overcharged selling price (75000->93000) in table 'market'. Assigning to current sell value.
在 rAthena 论坛也有人问类似的问题《[BUG] Failure to purchase some items》,但是没解决。
0x10 分析
其实这个警告是在提醒某些道具的 低价买进价格 比 高价卖出价格 还低。
rAthena 为避免玩家刷钱,在 src/npc.cpp
中有其实现代码:
if (list.value * 0.75 < id->value_sell * 1.24) { // Exploit possible: you can buy and sell back with profit
ShowWarning("npc_market_fromsql: Item %s [%u] discounted buying price (%d->%d) is less than overcharged selling price (%d->%d) in table '%s'. Assigning to current sell value.\n",
id->name.c_str(), list.nameid, list.value, (int)(list.value * 0.75), id->value_sell, (int)(id->value_sell * 1.24), market_table
);
list.value = id->value_sell;
}
从代码中可以看到与 market_table
有关,这个其实就是数据库的 market
表。
继续翻查写入 market_table
代码逻辑:
void npc_market_tosql(const char *exname, struct npc_item_list *list) {
SqlStmt* stmt = SqlStmt_Malloc(mmysql_handle);
if (SQL_ERROR == SqlStmt_Prepare(stmt, "REPLACE INTO `%s` (`name`,`nameid`,`price`,`amount`,`flag`) VALUES ('%s','%u','%d','%d','%" PRIu8 "')",
market_table, exname, list->nameid, list->value, list->qty, list->flag) ||
SQL_ERROR == SqlStmt_Execute(stmt))
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
}
可以发现当 NPC 脚本调用了 marketshop
函数时,就会把 NPC 脚本设定的道具价格缓存到数据库的 market
表。
查 script_commands.txt
得到 marketshop
函数的语法:
<map name>,<x>,<y>,<facing>%TAB%marketshop%TAB%<NPC Name>%TAB%<sprite id>,<itemid>:<price>:<stock>{,<itemid>:<price>:<stock>...}
marketshop
函数设定的售价 <price>
会缓存到 market
表的 price
字段。
而道具本身在 item_db_*.yml
有设置其 Buy
属性的买入价,出售价 Sell
属性一般无需指定,默认为 Buy/2
。
当 <price>
和 Buy
的值满足以下公式时:
price * 0.75 < Buy/2 * 1.24
(低买 < 高卖)
就会触发文中开头的警告提示。
0x20 解决
正常情况下,其实只要针对警告的道具,提高 NPC 脚本中 marketshop
函数所设定的售价、或者干脆设置为 -1
,就可以解决此问题。
当把
<price>
设为-1
则,默认用Buy/2
作为售价。
但是某些情况下,REPLACE INTO
没有成功替换缓存值,导致数据库的 market
表依然为错误的值,于是启动时还是会弹出警告。
正确的做法是:
- 修正 NPC 脚本的
marketshop
函数售价,确保其不满足低买 < 高卖
公式 - 清空数据库的
market
的表缓存
即可解决此问题。