日期:2026-10-07 13:15<br>交付:D:/AI空间/rxjh/旧版本更新_修复后_v4.e(2,808,883 字节,md5 18ac3480c2a7b58193e671856b77d6d8)<br>编译源:D:/AI空间/rxjh/旧版本更新.代码
你原来的旧版本更新_修复后.e(md5fa40cdab…)一个字节都没动。_v2.e(只修常量值)和这一版功能重复,可以删;_v3.e有问题已删除。
加一个「角色配置」文件上传下载,只上传下载当前角色的配置文件
编译 v3 后:你之前摆好的 上传脚本按钮 / 下载脚本按钮 不见了,新加的两个按钮也不在最上层。
反编译你的 旧版本更新_修复后.e 做真值对比,发现:
| 你的 .e | 旧的文本主本 | |
|---|---|---|
| 上传脚本按钮 | 索引 1,顶层控件(不属于任何分组框) | 索引 379,挂在 分组框2 下 |
| 下载脚本按钮 | 索引 2,挂在 补给_分组框_死亡处理 下 | 索引 380,挂在 分组框2 下 |
| 分组框2 的 Children | 不含这两个按钮 | 末尾是 …, 358, 379, 380 |
易语言的层级规则:控件列表里越靠前 = 越靠上层。<br>旧主本把它们挂在 分组框2 的 Children 最末尾 = 最底层 → 被 超级列表框_喊话列表 盖住;<br>我的 v3 又把两个新按钮也追加在同样位置 → 同样被压在最底。<br>这就是"搞没了"和"不在最顶层"的同一个原因。
上一次"对拍"我比的是 e2t(v2) vs e2t(v3)——两边都源自同一个过期主本,归一化噪声互相抵消,所以什么都没发现。<br>正确的比对对象应该是 e2t(你的 .e)。
旧版本更新_修复后.e 取真值(0 错误 / 0 警告)窗口/MainDialogWnd.form.json对拍结果:你的 381 个控件在新 .e 里"未原样保留的个数 = 0"(名称/类型/坐标/宽高/可见性/禁用/扩展数据/父子关系全部一致)。
wwwroot/yxzh_script.asp)新增维度参数 kind:
| 请求 | 含义 |
|---|---|
?op=list&token=… | 列坐标脚本(老行为不变) |
?op=list&token=…&kind=role&role=<角色名> | 列该角色的配置 |
?op=get&token=…&name=<文件名> / &kind=role&role=… | 下载 |
?op=put&token=…&name=<文件名> / &kind=role&role=… | 上传(POST body) |
服务器落点(三者隔离):
C:\inetpub\wwwroot\坐标脚本\ ← 坐标脚本(原有)
C:\inetpub\wwwroot\坐标脚本\_bak\
C:\inetpub\wwwroot\角色配置\<角色名>\ ← ★ 新增
C:\inetpub\wwwroot\角色配置\_bak\ ← ★ 新增,留底名:<时间戳>_<角色名>_<原名>
SafeRole())角色名必须"纯名字":长度 1–20;禁 \ / : ? " < > |;禁 .(.. 穿越直接出局);<br>禁 空格 & % + #(会破坏 URL 查询串);禁 _ 开头(占住 _bak 内部目录)。<br>文件名继续只放行 .ini 且不含路径成分。DoPut 会分级补建目录(FSO 只能建一层)。
| 索引 | 控件名 | 标题 | Left | Top | 宽 | 高 | 归属 |
|---|---|---|---|---|---|---|---|
| 1 | 上传角色配置按钮 | 上传角色配置 | 280 | 44 | 78 | 32 | 顶层控件 |
| 2 | 下载角色配置按钮 | 下载角色配置 | 361 | 44 | 78 | 32 | 顶层控件 |
| 3 | 上传脚本按钮 | 上传脚本 | 280 | 8 | 70 | 32 | 顶层(你原来的,未动) |
| 4 | 下载脚本按钮 | 下载脚本 | 359 | 8 | 70 | 32 | 你的归属关系未动 |
四个按钮排成 2×2。两个新按钮索引 1、2 = 整窗口最上面两层,不会被任何面板/分组框盖住。
代码/窗口程序集_MainDialogWnd.form.e.txt 末尾纯追加(你的 752,720 字节原封不动,新文件 = 你的 + 10,130 字节):
.子程序 _上传角色配置按钮_被单击
.子程序 _下载角色配置按钮_被单击
.子程序 上传角色配置到服务器
.子程序 下载角色配置到本地
「只同步当前角色」怎么保证:全程只用
角色目录 = 角色配置目录 + “\” + 全局_人物名称 ' 当前角色名,读游戏内存得到
上传只枚举它下面的 *.ini,下载只写回同一目录,其余角色目录绝不触碰。<br>没取到角色名(没进游戏 / 没点「加载」)直接中止写日志,不会误写别的角色。
复用:常量 #脚本接口地址 / #脚本接口令牌、全局锁 脚本同步运行中(脚本同步与角色配置互斥)、<br>脚本上传线程 / 脚本下载线程、取同步时间戳()。<br>下载覆盖前的旧配置备份到 角色配置\<角色名>\_同步备份\<时间戳>\。<br>日志前缀 【角色同步】(区别于坐标脚本的 【脚本同步】)。
| 项目 | 结果 |
|---|---|
| t2e | 0 错误 / 0 警告 → _tmp/out45.e |
| e2t | 0 错误 / 0 警告 |
| 你的控件是否保留 | 未原样保留的个数 = 0(381 个全对) |
分组框2 是否还挂脚本按钮 | 否(与你的 .e 一致) |
下载脚本按钮 的归属 | 补给_分组框_死亡处理.Children = [4, 5, 86, 87, …](原 [2, 3, 84, 85, …],重映射后一致) |
| 代码文件 | 你的 752,720 字节逐字节相同,之后纯追加 10,130 字节 |
| 二进制常量 | http://139.196.21.118/yxzh_script.asp、yxzhApi2026Rxjh 各 1 次,全角引号 0 次 |
| 新子程序 | 4 个全部反编译回读成功 |
| 服务端 HTTP | 坐标脚本 list 回归 21 文件;角色 put→get md5 一致;二次覆盖生成留底;10 条非法用例全拒 |
旧版本更新_修复后_v4.e(t2e 0 错误 ≠ 能编译,这条没变)ERR no base)旧版本更新.代码/窗口/MainDialogWnd.form.json.bak_按钮位置回写前_20261007_124140
旧版本更新.代码/窗口/MainDialogWnd.form.json.bak_加角色配置按钮前_20261007_130340
旧版本更新.代码/窗口/MainDialogWnd.form.json.bak_以你的e为基准重排前_20261007_131312 ← v3 前的旧结构
旧版本更新.代码/代码/窗口程序集_MainDialogWnd.form.e.txt.bak_加角色配置同步前_20261007_130450
旧版本更新.代码/常量.e.txt.bak_常量值修复前_20261007_124140
_tmp/e2t_user_now/ 你的 .e 的反编译真值(诊断用,别删)
tools/yxzh_script.asp 服务端源码(GBK 落盘副本 tools/_yxzh_script.gbk.asp)
tools/_deploy_script_api_role.py 部署 + 验证脚本,可重复跑
e2t 用户当前 .e 取真值,把文本主本对齐过去,而不是相信主本里的旧结构。用户会用 IDE 直接改界面,主本随时可能过期。
e2t(v_a) vs e2t(v_b) 这种对拍有盲区——两边同源时噪声抵消,查不出结构性问题。必须有一条链路是 e2t(用户当前 .e)。
要让控件在最上层,就插到索引靠前的位置,并且不要挂进会被裁剪的容器。
窗口/MainDialogWnd.form.json 序列化参数固定为 indent=4 + CRLF + UTF-8 BOM,否则和 e2txt 原始输出对不上(indent=2 会差 4 万+字节)。
e2txt 1.2 - 易代码文本,横杠两侧都有空格,少一个就 127。_v6.e)行数组 = 分割文本 (...)根因是声明写法,不是代码逻辑。
| 写法 | 含义 |
|---|---|
.局部变量 X, 文本型, , "0"(第3字段空、第4字段 "0") | 真数组 ✅ 工程第 265 行一直是这种,从没报过错 |
.局部变量 X, 文本型, 数组(只有第3字段) | 标量 ❌ t2e 不报错但把数组属性丢掉 |
判据来源:反编译能编译的老工程 Mydll_202303211.e,里面的数组全是 , , "0" 形式。
全工程扫描 1167 处 , 数组 局部变量,真的按数组用的只有 4 行(其余 1163 处是历史冗余标注,变量本就是标量、代码也按标量用,不动):
下载脚本到本地 → 行数组、段数组下载角色配置到本地 → 行数组、段数组顺带纠正一个此前的错误结论:e2t反编译产物不能用来判断数组性(它会无差别给出, 数组)。所以以前拿e2t(v4) vs e2t(v5)对拍根本查不出这个问题。
结果 = 时间_取现行时间戳 (真)根因:三个易模块都有同名公开子程序,参数个数各不相同,易语言解析到的是 0 参那一版。
| 模块 | 参数 |
|---|---|
| 薏米模块_2023 | 2 个(生成10位 / 参数时间,均可空) |
| 精易模块 | 1 个(是否取十位时间戳,可空) |
| 魔鬼作坊VIP模块 V8.0 | 0 个 ← 实际解析到这个 |
解析规则是从既有代码反推出来的:十到十六 在薏米是 2 参、魔鬼作坊是 1 参,而全工程既有 3 处调用全是 1 参 → 魔鬼作坊胜出,和这次报错完全吻合。
修复:改成 结果 = 时间_取现行时间戳 ()。三家参数都可空、默认都返回 13 位毫秒时间戳,不传参对三家都兼容,不赌解析结果。
这条规律以后要记住:本项目里编码_URL编码、目录_创建、网页_访问、文件_枚举都是多模块同名。
已核对:编码_URL编码(x, 真, 假)(3参) 与网页_访问在本次编译中已通过(报错行号在它们之后);文件_枚举当初就是踩到这个坑才换成核心库寻找文件。
_v4.e 重建你 13:16 在 IDE 里把 v4 打开又挪了按钮并保存(文件从 2.8MB 变 7MB),我核对了差异:
上传脚本按钮 (203, 552) W=70
下载脚本按钮 (282, 552) W=70
上传角色配置按钮 (189, 600) W=89
下载角色配置按钮 (281, 600) W=100
v6 已按这个布局重建,校验结果:_v6.e 反编译出的 form.json 与你的 _v4.e 逐字段完全相同(True,383 个控件)。
你在 IDE 里保存后,常量.e.txt会从 136 条变成 257 条 —— 多出的 121 条是模块常量被物化进来的
(标准/低/AMF0/网页事件_*等)。这是 IDE 的正常行为,不要把这些回写进文本主本,
否则模块常量会被重复定义。同理全局变量/自定义类型/成员上多出来的, 数组也是 e2txt 的渲染噪声。
代码/窗口程序集_MainDialogWnd.form.e.txt 4 行数组声明改为 , , "0";1 处 时间_取现行时间戳(真)→()
窗口/MainDialogWnd.form.json 整体换成你 _v4.e 反编译出的版本(= 你的最新界面)
旧版本更新_修复后.e 6,993,827 B md5 fa40cdab… ← 你的原文件,始终没动
旧版本更新_修复后_v4.e 7,005,673 B md5 b50b21b4… ← 你 IDE 保存的,含 10044 错误,别编译
旧版本更新_修复后_v6.e 2,809,205 B md5 be0863c8… ← ✅ 编译这个
_v5.e 已删除(含 10041 错误),避免误编译。
新增工具(可复用):
tools/yxzh_script.asp 服务端接口源码(UTF-8,部署时自动转 GBK 落盘)
tools/_deploy_fix_saferole.py 部署 + 全量验证(178 角色 / 往返 / 安全 / 回归)
tools/_audit_resolve.py 扫描多模块同名命令 + 从既有调用反推解析规则
tools/_audit_cmds.py 审计指定命令在各模块的定义与参数
tools/_scan_array_decl.py 扫描"声明是标量但代码按数组用"的变量
tools/_verify_v6.py 交付前全面校验(界面对拍 / 常量 / 代码 / 二进制串)
tools/_what_user_changed.py 分析用户在 IDE 里改了什么
tools/_read_iislog.py 翻 IIS 访问日志,看客户端实际发出的 URL
ERR bad role(服务端,无需重新编译)【角色同步】开始上传当前角色【江南雨替我说别】的配置到服务器…
【角色同步】上传失败:UserConfig.ini;服务器返回【ERR bad role】
【角色同步】上传失败:组队名单.ini;服务器返回【ERR bad role】
【角色同步】上传失败:购买物品列表.ini;服务器返回【ERR bad role】
【角色同步】上传结束:角色【江南雨替我说别】成功 0 个,失败 3 个
AscW 返回 16 位有符号整数,中文被误判成控制字符SafeRole() 里我写了一条防控制字符的判断:
If AscW(ch) < 33 Then Exit Function ' ← 错的就是这一行
AscW 在 VBScript 里返回的是 16 位有符号整数。码位 ≥ U+8000 的汉字会溢出成负数:
| 字符 | 码位 | AscW 实际返回 | < 33 ? |
|---|---|---|---|
| 江 | U+6C5F | 27743 | 否 ✓ 通过 |
| 南 | U+5357 | 21335 | 否 ✓ 通过 |
| 雨 | U+96E8 | 38632 → −26904 | 是 ✗ 被拒 |
| 替 | U+66FF | 26367 | 否 ✓ 通过 |
| 我 | U+6211 | 25105 | 否 ✓ 通过 |
| 说 | U+8BF4 | 35828 → −29708 | 是 ✗ 被拒 |
| 别 | U+522B | 21035 | 否 ✓ 通过 |
所以「江南雨替我说别」整串被拒 → CurDir() 返回空 → ERR bad role。
约一半的汉字(码位 ≥ U+8000)都会中招 —— 实测 178 个角色里 89 个含这类字。
AAA1A 这类纯 ASCII 角色名,中文名一次都没跑过role=不存在的角色XYZ,它返回 ERR bad role 我当成了"正确拒绝",实际上它是被这个 bug 拒绝的 —— 假阳性掩盖了真 bug
SafeRole 逐字符逐条件回放 → 只有 雨 / 说 的 AscW(ch) < 33 为 Truerole=%BD%AD%C4%CF%D3%EA%CC%E6%CE%D2%CB%B5%B1%F0(= 江南雨替我说别 的 GBK 编码,编码完全正确)
顺带确认:客户端编码_URL编码 (名, 真, 假)第 3 参「是否UTF8」= 假 → GBK;
服务端@CodePage=936能正确解码 GBK 百分号编码(诊断页实测ASCW=6C5F 5357 96E8 66FF 6211 8BF4 522B)。
客户端一行都不用改。
' ★ 这里【绝对不能用 AscW(ch) < 33】——
' VBScript 的 AscW 返回的是 16 位【有符号】整数,码位 >= U+8000 的汉字
' (雨 U+96E8 / 说 U+8BF4 / 星 U+661F ...)会溢出成负数,被误判成控制字符
If ch < " " Then Exit Function
If ch = Chr(127) Then Exit Function
VBScript 的比较运算符按码位逐字符比较,不受符号影响。SafeName() 也补了同样的两条。
op=get 失败不再返回文本客户端是把 op=get 的响应整体当文件内容落盘的。原来失败时返回 ERR no file 文本,<br>万一文件在 list 和 get 之间消失,就会把 ERR no file 写进本地 UserConfig.ini 造成污染。
改为返回 200 + 真空 body,原因放 X-Script-Err 响应头:<br>客户端会走到「下载失败(无数据)」分支,不会污染文件。
⚠️ 踩坑记录:一开始改成Response.Status = "404"+ 空 body,
结果 IIS 用自己的 HTML 错误页替换了 body,客户端会把整段 HTML 写进.ini,比原来更糟。
必须保持 200,只让 body 为空。
tools/_deploy_fix_saferole.py)[1] 部署 yxzh_script.asp utf-8 → gbk 7,6xx 字节,无 BOM
[2] 逐个验收 178 个真实角色名 → 被拒 0 个 ✅(其中 89 个含码位>=U+8000 的汉字)
[3] 角色【江南雨替我说别】往返 → 3/3 文件 md5 一致 ✅
[4] 服务器落盘目录名 → wwwroot\角色配置\江南雨替我说别(中文名正确)✅
[5] 安全性回归(.. / _bak / a/../b / a\b / a:b / a b / 各类非法 name)→ 全部拒绝 ✅
[6] 坐标脚本 op=list 回归 → 21 个文件 ✅
[7] get 失败 → 200 + body 为空 + X-Script-Err 头 ✅
留底命名实测:20261007_135620_江南雨替我说别_UserConfig.ini。
这次是纯服务端修复,DLL 不用重新编译。 直接再点一次「上传角色配置」就行。
(服务器上现在已经有一个 wwwroot\角色配置\江南雨替我说别\ 目录,里面是刚才测试上传的 3 个文件,<br>内容和你本地逐字节相同,可以当第一次同步的正常结果留着。)