传奇沙巴克成员工资自动领取脚本制作教程与防冒领安全配置

来源: 作者: 点击:
冒领沙城成员工资的本质风险是:工资系统按角色名、行会名或账号发钱,攻击者用同名角色、离线角色、已退会角色、会长小号重复领取。脚本要把发薪权限、领薪资格、发薪周期、角色唯一标识、已领标记、日志六件事锁死。角色名可重复的小服,必须以账号为主键,角色名为辅助;只有行会名没有唯一会标的版本,还要加会长密码或会长在线二次确认。

一、目录与文件规划

Mir200\Envir\Market_Def放NPC,例如“沙城财务官.txt”。
Mir200\Envir\QuestDiary\沙城工资放主脚本,例如“Main.txt”。
Mir200\Envir\QuestDiary\沙城工资\成员名单放成员账号和资格。
Mir200\Envir\QuestDiary\沙城工资\工资配置放金额、周期、税点、发薪日。
Mir200\Envir\QuestDiary\沙城工资\已领记录按角色或账号记录发薪周期。
Mir200\Envir\QuestDiary\沙城工资\日志放发薪、拒绝、异常尝试。
Mir200\Envir\Guild放行会文件,但脚本不要直接频繁改GuildBase;行会名变化时由GM命令或会长功能同步名单。

Market_Def与QuestDiary目录名要一致。若NPC用“沙城工资”目录,Market_Def的Merchant最后一段必须相同,否则NPC点开空白。

二、NPC注册

;沙城财务官
3 330 330 沙城财务官 0 0 沙城工资 1 1 0 0 0 0 0 0

地图号和坐标按土城、沙城安全区或皇宫入口调整。正式开放前放测试地图,用普通成员、会长、退会成员、同名角色各建一个测试号。

三、行会资格判断基础

Hero/GOM/GEE类引擎通常提供CHECKCASTLEWARGUILD判断当前沙巴克占领行会,或CHECKCASTLEGUILD按指定沙城判断。入口先卡城主行会,非占领行会成员不显示领薪入口。

[@main]
SAY

沙城成员工资系统\ \
仅沙巴克占领行会成员可领\ \
<查看我的工资/@check_pay>
<领取本月工资/@get_pay>
<会长发放工资/@master_pay>

[@check_pay]
IF

CHECKCASTLEWARGUILD 沙巴克
ACT

SENDMSG 5 当前占领行会:[沙巴克占领行会],请继续领薪
GOTO @show_detail
ELSEACT

SAY

当前行会不是沙巴克占领行会,无法领薪\

部分引擎CHECKCASTLEWARGUILD不带参数,只检测玩家当前所在行会是否为沙巴克占领方。若本服没有该命令,改用行会文件判断:玩家进NPC时,脚本取行会名与GuildBase或GuildList里沙巴克占领行会名比对,但这样要处理行会文件锁和IO延迟。

四、防止角色名冒领:用账号做主键

同名角色风险来自角色名不唯一。工资记录不能只写角色名,要写账号。Hero变量可用账号变量GLOBAL ACCOUNT,或在登录脚本用账号名建文件。

[@show_detail]
ACT

VAR Integer ACCOUNT AC_PAY_FLAG
LOADVAR ACCOUNT AC_PAY_FLAG ..\QuestDiary\沙城工资\已领记录\Pay.txt
IF

CHECKVAR ACCOUNT AC_PAY_FLAG = 1
ACT

SENDMSG 5 本月工资已领取,下月刷新后开放
ELSEACT

ACT

SENDMSG 5 本月可领取工资,请由会长先发放
CLOSE

账号变量AC_PAY_FLAG存于账号层,同账号下角色切换不会改变;角色名变量HUMAN则不同角色独立,反而可用于“每角色发一次”。若工资按角色发,允许同账号多角色各领一次;若工资按账号发,只能用ACCOUNT层变量,禁止用HUMAN层。规则必须写进NPC说明,不能既按账号又按角色。

五、成员名单与工资等级

成员名单按账号记录,每行一个账号,后面跟等级和金额。示例:

账号A 核心 500000
账号B 成员 200000
账号C 新兵 100000

实际可用Tab或空格分隔。名单只放沙城占领行会成员,退会自动由会长刷新功能删除对应账号行。

工资配置放金额规则:

[工资]
核心=500000
成员=200000
新兵=100000
周期=每月1日刷新
税点=10

“每月刷新”不要依赖真实日期解析,若引擎没有完整日期函数,可用M2机器人每月1日执行清标志脚本,或在玩家领取时按服务器开服天数、周数判断。更稳的是会长点“刷新工资月”,生成新的发放批次号,例如202610,玩家已领记录写批次号202610,下次刷新成202611;没有领过的账号即使文件存在旧批次也不能领。

六、会长发薪功能

会长身份用CHECKCASTLEMASTER或本服行会权限命令判断。只有会长能改批次、导入名单、设置金额。会长输入密码再执行高风险动作,防止会长号被盗后一次清空金库。

[@master_pay]
IF

CHECKCASTLEMASTER
ACT

GOTO @master_menu
ELSEACT

SAY

仅沙巴克占领行会会长可操作\

[@master_menu]
SAY

会长工资管理\ \
1.刷新发放批次\ \
2.导入当前成员\ \
3.设置金额\ \
4.一键发放\ \
5.查看金库\ \
<刷新批次/@new_batch>
<导入成员/@import_mem>
<设置金额/@set_money>
<一键发放/@pay_all>
<查看金库/@show_bank>

[@new_batch]
IF

CHECKVAR HUMAN PWD_OK = 1
ACT

GOTO @do_new_batch
ELSEACT

ACT

InputString MASTER_PWD 0 20
IF

CHECKSTRING S$MASTER_PWD = 会长密码
ACT

MOV HUMAN.PWD_OK 1
SAVEVAR HUMAN PWD_OK ..\QuestDiary\沙城工资\PwdOK.txt
GOTO @do_new_batch
ELSEACT

SAY

会长密码错误\

[@do_new_batch]
ACT

CALCVAR GLOBAL BATCH + 1
SAVEVAR GLOBAL BATCH ..\QuestDiary\沙城工资\工资配置\Batch.txt
RECORDNPCLOG 沙城工资 刷新批次 会长: 新批次:
SENDMSG 6 工资批次已刷新,旧批次记录作废

会长密码不要明文写在NPC脚本正文,可放在只读权限的文本,由GM首次通过M2命令写入。普通管理员和GM账号另设权限,不使用同一套会长密码。

七、导入当前成员并防退会冒领

导入成员从行会文件读占领行会成员列表,把账号写入成员名单。具体读行会命令按本服,常见有LoadGuildMember、GUILDMEMBERLIST,或脚本直接文本解析GuildList。这里给文本逻辑:

会长点导入;
脚本读取沙巴克占领行会文件;
对每个账号判断是否在名单,不在则追加;
对已在名单但已退出占领行会的账号,从名单移除;
导入后名单行数必须等于当前占领行会成员数,否则报错。

[@import_mem]
ACT

IF

EXISTFILE ..\QuestDiary\沙城工资\工资配置\guild_tmp.txt
ACT

CLEARFILE ..\QuestDiary\沙城工资\工资配置\guild_tmp.txt
ELSEACT

SAY

空
ACT

LOADGUILDMEMBER 沙巴克 ..\QuestDiary\沙城工资\工资配置\guild_tmp.txt
IF

EXISTFILE ..\QuestDiary\沙城工资\工资配置\guild_tmp.txt
ACT

RECORDNPCLOG 沙城工资 导入成员 会长: 临时名单已生成
SENDMSG 5 成员导入完成,请继续核对
ELSEACT

SAY

无法读取沙巴克行会成员,请检查行会文件

下一步把临时名单与成员名单比较:新成员加入,退会成员删除,已领本批次的保留已领标记但不影响退会判断。脚本层做diff容易出错,会长导入后显示“新增N人、移除N人、当前N人”,让会长确认再写正式名单。

八、个人领取主逻辑

[@get_pay]
ACT

VAR Integer ACCOUNT AC_BATCH
LOADVAR ACCOUNT AC_BATCH ..\QuestDiary\沙城工资\已领记录\Batch.txt
VAR Integer GLOBAL CUR_BATCH
LOADVAR GLOBAL CUR_BATCH ..\QuestDiary\工资配置\Batch.txt
IF

CHECKCASTLEWARGUILD 沙巴克
ACT

GOTO @check_mem
ELSEACT

SAY

你已不属于沙巴克占领行会\

[@check_mem]
IF

CHECKVAR ACCOUNT AC_BATCH = CUR_BATCH
ACT

SENDMSG 5 本批次工资已领取
CLOSE
ELSEACT

ACT

GOTO @check_role

[@check_role]
IF

CHECKINLIST ..\QuestDiary\沙城工资\成员名单\list.txt 账号
ACT

GOTO @calc_pay
ELSEACT

SAY

账号不在沙城成员名单,请联系会长刷新\

[@calc_pay]
ACT

VAR Integer AC_PAY_GOT
LOADVAR ACCOUNT AC_PAY_GOT ..\QuestDiary\沙城工资\已领记录\Pay.txt
IF

CHECKVAR ACCOUNT AC_PAY_GOT >= 1
ACT

SENDMSG 5 账号已领取过工资,请勿重复操作
CLOSE
ELSEACT

ACT

MOV HUMAN.PAY_LEVEL 成员
MOV HUMAN.PAY_MONEY 200000
IF

CHECKINLIST ..\QuestDiary\沙城工资\成员名单\core.txt 账号
ACT

MOV HUMAN.PAY_LEVEL 核心
MOV HUMAN.PAY_MONEY 500000
IF

CHECKINLIST ..\QuestDiary\沙城工资\成员名单\new.txt 账号
ACT

MOV HUMAN.PAY_LEVEL 新兵
MOV HUMAN.PAY_MONEY 100000
ACT

MOV HUMAN.PAY_TAX 0
CALCVAR HUMAN PAY_TAX = HUMAN.PAY_MONEY * 10 / 100
CALCVAR HUMAN PAY_FINAL = HUMAN.PAY_MONEY - HUMAN.PAY_TAX
IF

CHECKGAMEGOLD < GLOBAL.BANK_GOLD
ACT

SENDMSG 5 行会金库余额不足,请联系会长补充
CLOSE
ELSEACT

ACT

GAMEGOLD -HUMAN.PAY_FINAL
GOTO @pay_done

变量计算命令按本服换。若本服不支持在NPC直接把变量表达式结果赋给另一变量,改用M2自定义函数或分步变量,不要在一行写复杂运算。工资金币字段用本服实际字段,可能GAMEGOLD、GameGold或其他自定义。

[@pay_done]
ACT

GameGold +HUMAN.PAY_FINAL
MOV ACCOUNT.AC_BATCH GLOBAL.CUR_BATCH
SAVEVAR ACCOUNT AC_BATCH ..\QuestDiary\沙城工资\已领记录\Batch.txt
MOV ACCOUNT.AC_PAY_GOT 1
SAVEVAR ACCOUNT AC_PAY_GOT ..\QuestDiary\沙城工资\已领记录\Pay.txt
CALCVAR GLOBAL BANK_GOLD - HUMAN.PAY_FINAL
SAVEVAR GLOBAL BANK_GOLD ..\QuestDiary\沙城工资\工资配置\Bank.txt
RECORDNPCLOG 沙城工资 发薪 账号: 角色: 等级: 金额:
SENDMSG 6 工资发放完成:税前 税后 已领批次

先扣金库再给玩家,且扣款、发币、写批次、写已领、写日志全部成功才算完成。若本服没有事务性脚本,先写“待发”标记,发放成功后清标记;发放失败保留待发,登录脚本下次补发。不要先给玩家钱再扣金库,若中间断线会造成白拿。

九、按角色发工资与同账号多角色控制

如果规则是“每个角色一份工资”,用HUMAN变量记录角色级批次:

VAR Integer HUMAN ROLE_BATCH
LOADVAR HUMAN ROLE_BATCH ..\QuestDiary\沙城工资\已领记录\RoleBatch.txt

领取时比对GLOBAL当前批次,已领就拒绝。问题在于同账号多角色各领一次可能被钻空子:玩家建多个角色进占领行会,全部领一次。解决方案是账号级“多角色发薪额度”,例如主角色领全额,同账号第二个角色领50%,第三个角色领20%,且总发薪额写入账号变量:

MOV ACCOUNT.AC_TOTAL_PAY 0
CALCVAR ACCOUNT.AC_TOTAL_PAY + HUMAN.PAY_FINAL
SAVEVAR ACCOUNT AC_TOTAL_PAY ..\QuestDiary\沙城工资\已领记录\TotalPay.txt

每次领取先算本账号本批次累计,超过会长设定的账号上限就拒绝。若工资按角色发,账号上限不可少;若工资按账号发,只用一个账号变量即可。

十、行会金库与权限

金库余额放GLOBAL变量,例如BANK_GOLD,所有成员读取,会长充值、发放扣除。会长充值:

[@add_bank]
IF

CHECKCASTLEMASTER
ACT

InputInteger ADD_GOLD 1 10000000
ACT

CALCVAR GLOBAL BANK_GOLD + ADD_GOLD
SAVEVAR GLOBAL BANK_GOLD ..\QuestDiary\沙城工资\工资配置\Bank.txt
RECORDNPCLOG 沙城工资 充值金库 会长: 金额:
SENDMSG 6 金库已补充,当前余额:

玩家领薪只读余额,禁止任何普通成员NPC分支调用GIVE、GAMEGOLD、CALCVAR BANK_GOLD。金库文件只有会长和GM可写,操作前备份Bank.txt。每日或每月定时导出Bank.txt与发薪日志,做余额对账:

期末余额=期初余额+会长充值+税收返还-实际发放总额

差额不为0说明存在脚本并发、手动改文件、金额字段错乱,先冻结工资再查日志。

十一、离线角色与遗产冒领

玩家离线后,不能用离线角色名直接领工资。脚本只在玩家本人在线点NPC时发放;若要支持离线成员,由会长一键发放,目标账号写入“待发工资”,上线时登录脚本检测并发放。

[@OnLogin]
ACT

VAR Integer ACCOUNT AC_PENDING
LOADVAR ACCOUNT AC_PENDING ..\QuestDiary\沙城工资\待发\Pending.txt
IF

CHECKVAR ACCOUNT AC_PENDING > 0
ACT

IF

CHECKCASTLEWARGUILD 沙巴克
ACT

GameGold +AC_PENDING
RECORDNPCLOG 沙城工资 上线补发 账号: 金额:
MOV ACCOUNT.AC_PENDING 0
SAVEVAR ACCOUNT AC_PENDING ..\QuestDiary\沙城工资\待发\Pending.txt
ELSEACT

ACT

SENDMSG 5 你有待发工资,但当前不属于占领行会,保留待发

离线名单只写账号,不写角色名,避免同名角色冒领。上线补发时必须再次验证仍在占领行会,退会成员不补发。

十二、会长一键发放

会长一键发放只把金额写入账号待发,不在线也保留。脚本遍历成员名单用FOR或循环命令,不同引擎写法差异大,下面给逻辑:

读取成员名单每行;
对每行账号写待发金额;
记录已待发账号;
统计待发总额;
检查金库是否足够;
不足则回滚全部待发,足够则扣金库、保留待发、记录批次。

Hero/GOM纯脚本遍历文本的能力有限,常见做法是用机器人逐行扫描成员名单文件,或用外部程序生成“账号=金额”的待发文件,玩家上线或点NPC时按账号读取。纯文本脚本没有事务,建议先冻结领取入口,再写待发,最后统一扣金库;任何步骤失败,清空本轮待发并恢复金库。

[@pay_all]
ACT

MOV GLOBAL.PAYING 1
SAVEVAR GLOBAL PAYING ..\QuestDiary\沙城工资\工资配置\Paying.txt
RECORDNPCLOG 沙城工资 一键发放开始 会长:
IF

EXISTFILE ..\QuestDiary\沙城工资\成员名单\list.txt
ACT

GOTO @pay_all_step
ELSEACT

SAY

成员名单为空\

由于FOR循环实现依赖具体引擎,推荐用机器人每秒处理一个账号:

机器人标签:
[@pay_loop]
IF

CHECKVAR GLOBAL PAYING = 1
ACT

READNEXTLINE ..\QuestDiary\沙城工资\成员名单\list.txt S$LINE
IF

CHECKSTRING S$LINE = 结束
ACT

MOV GLOBAL.PAYING 0
RECORDNPCLOG 沙城工资 一键发放完成
ELSEACT

ACT

解析S$LINE取账号和等级;
按等级算金额;
写账号待发;
累计待发总额;
到达名单末尾后判断金库并扣款。

机器人扫描要做好幂等:中途重跑不会重复给已发账号发钱。用“账号已发本批次”标记文件判断,每处理一个账号立即写标记,机器人重启不重复发。

十三、防冒领硬规则

角色名不唯一,绝不用角色名做工资主键,账号才是领薪主体。
已领标记必须带批次号,刷新批次后旧标记不能领,旧标记也不应污染新批次。
领薪瞬间再次校验行会,退会成员即使有旧标记也不补发。
同账号多角色发工资要设账号总额上限,防小号军团。
会长发薪需密码或二次确认,密码与GM后台分离。
金额和金库只由会长、GM修改,普通成员NPC入口只有查询和领取。
所有发薪动作先冻结并发、再扣金库、再写待发、最后写日志;失败可回滚。
成员名单与行会文件强一致,会长导入后比对人数,退会自动清出。
离线补发用账号待发,上线再校验占领行会。
日志至少保留账号、角色、行会、批次、税前金额、税后金额、发薪时间、IP,对账时可查。

十四、日志与异常告警

每次发放写NPC日志:

RECORDNPCLOG 沙城工资 PAY 账号:角色:批次:等级:税前:税后:时间见M2

异常行为单独记:

RECORDNPCLOG 沙城工资 WARN 非成员尝试领薪 账号:角色:行会:时间
RECORDNPCLOG 沙城工资 WARN 批次不一致 账号:记录批次:当前批次
RECORDNPCLOG 沙城工资 WARN 金库余额异常 当前余额:待发总额

连续多次非成员尝试、同名角色尝试、会长导入名单后立即大量领取,可触发临时冻结:脚本设置GLOBAL.PAY_FREEZE,玩家点领取时若FREEZE=1直接提示系统维护。冻结只由GM命令解除,避免脚本自动解封造成二次冒领。

十五、常见排错

角色名重复导致两人领一份:确认记录文件用账号命名,不用角色名命名;若不得不用角色名,先开启服务端角色名唯一规则。

已领标记不生效:变量声明HUMAN/GLOBAL/ACCOUNT类型用错;只声明没SAVEVAR;变量名大小写不一致。

刷新批次后仍领旧工资:旧批次写在角色变量,新批次写全局变量,两者没同时更新;统一在会长刷新时写GLOBAL当前批次和ACCOUNT已领批次。

退会成员能领:领薪脚本只查名单,没查CHECKCASTLEWARGUILD;名单导入后行会变化但没重新导入。

同名角色在名单里出现两次:文本导入没去重,脚本应只在账号不存在时追加。

金库扣了钱没到账:发币命令字段错、变量未解析成数字、玩家背包满导致部分货币处理异常;先测试GM命令发该货币。

日志找不到:RECORDNPCLOG路径与M2日志设置不符;改用本服NPC日志目录或外部文本记录。

机器人重复发:没写每账号已发标记,或标记写盘前就重跑;机器人每次只处理一个账号并立即落盘标记。

十六、部署与测试清单

先在测试地图建沙巴克占领行会,会长1人、核心2人、成员5人、新兵2人。
导入成员后核对名单人数=行会成员数,退会一人再导入,人数减一。
同账号建两个角色,角色A领全额,角色B按规则领全额或折扣,检查账号总额上限。
用同名角色名测试,只账号匹配者能领,角色名相同但账号不同按名单独立处理。
退会后领薪,应提示“已不属于占领行会”;会长重新导入后能否按新规则补发要预先定好。
离线成员走会长一键发放,上线且仍在行会才补发,上线已退会保留待发。
模拟金库不足,发放应整体失败,不出现部分发、金库只扣部分。
连续点领取,第二次应提示本批次已领。
会长错误密码不能刷新批次,正确密码后才可发。
GM账号不能走普通成员领薪,管理员账号和工资账号分开。
每月刷新后,旧批次标记不生效,新批次首次领取正常。

最终脚本要把权限、主键、批次、金额、日志分成五个独立模块:NPC界面只做展示和收集输入,会长模块只做名单和批次,发薪模块只做资格和金额计算,金库模块只做余额变更,日志模块记录每一次状态变化。任何一环都不要同时改角色名、账号、行会、批次、金额多个状态,减少并发错乱。冒领问题不是靠把金额写小解决,而是确保“谁领、领哪一批、领多少、从哪扣、是否还能领”每一步都有唯一且可审计的记录。