这是一个面向 Mahjong-JP.exe 的本地研究工具,用本地代理在客户端侧补全角色、皮肤、称号、装扮、加载 CG 等“已拥有”数据,并通过可选 MITM WebSocket 补丁让对局内角色与装扮保持一致。
项目当前按授权 CTF / 本地研究环境设计,不依赖原后端源码,不需要把伪造的完整拥有列表提交给远端服务器。实际保存的角色、称号、装扮方案等状态默认写入本地配置。
- 解锁全部角色与皮肤。
- 解锁全部装扮:立直棒、牌背、桌布、特效、立直特效、BGM、场景、牌面、头像框、桌边框、个人头像、画廊背景、牌桌手、对局按钮样式等。
- 解锁非连续 ID 的特殊装扮:会合并服务器返回、内置目录、游戏资源文件名、
lua_config物品名配置中发现的 ID。 - 解锁称号、头像、头像框、加载 CG。
- 支持星标角色、自定义昵称、本地大厅方案。
- 支持本地保存装扮方案。
- 支持对局 WebSocket 数据补丁,避免大厅已换装但进入对局仍显示默认角色/默认装扮。
LocalUnlockTool/
├─ one_click_launcher.py # 一键启动器:环境检查、证书安装、Proxifier 规则自动配置、代理链启动
├─ riichicity_unlock_proxy.py # 本地 HTTP/HTTPS 代理与解锁逻辑
├─ start_unlock_proxy.ps1 # 源码版启动脚本
├─ start_unlock_proxy_exe.ps1 # 编译版启动脚本
├─ requirements.txt # 运行依赖
├─ requirements-build.txt # 构建依赖
├─ config/
│ ├─ decor_catalog.json # 装扮目录
│ ├─ RiichiCity_OneClick.ppx # 备用 Proxifier profile;启动器会自动生成/更新
│ ├─ unlock_state.example.json # 公开模板;首次运行会复制为 unlock_state.json
│ └─ user_settings.example.json # 公开模板;首次运行会复制为 user_settings.json
├─ mitm/
│ ├─ riichicity_mitm_capture.py # mitmdump 插件,用于 WebSocket 对局数据补丁
│ └─ start_mitm_capture.ps1 # MITM 启动脚本
└─ scripts/
├─ decrypt_check_data.py # AES 加解密辅助脚本
├─ test_local_endpoints.ps1 # 本地接口自测
├─ build_one_click_launcher.ps1 # 只编译一键启动器
└─ build_release.ps1 # PyInstaller 编译与 Release zip 打包
不会提交到公开仓库的运行数据:
logs/mitm/logs/%USERPROFILE%\.mitmproxy/,默认共享的 mitmproxy CA 与私钥目录(不得提交)mitm/backups/config/user_settings.jsonconfig/unlock_state.json- 截图、抓包、Proxifier 备份、pyc、构建产物
普通装扮 ID 通常由“前两位类型 + 后三位序号”组成。特殊装扮不一定连续,因此程序额外扫描资源与配置补齐。
| ID | NAME_ENG | NAME_CHN |
|---|---|---|
| 13 | RiichiStick | 立直棒 |
| 14 | CardBack | 牌背 |
| 15 | Tablecloth | 桌布 |
| 16 | SpecialEffect | 特效 |
| 17 | LiZhiEffect | 立直特效 |
| 18 | HallBGM | 大厅 BGM |
| 19 | RankBGM | 段位战 BGM |
| 20 | LZBGM | 立直 BGM |
| 24 | SceneEffect | 场景特效 |
| 25 | HallBg | 大厅背景 |
| 26 | CardFace | 牌面 |
| 30 | AvatarFrame | 头像框 |
| 36 | TableEdge | 牌桌边框 |
| 37 | PersonalHead | 个人头像 |
| 38 | Gallery | 画廊背景 |
| 39 | TableHand | 牌桌手 |
| 40 | TableButtonStyle | 牌桌按钮样式 |
当前代码也兼容 22、27、28、31、32、33、34、35、42 等特殊或扩展装扮类型。
- 从 GitHub Releases 下载
RiichiCityLocalUnlockTool-<version>-win-x64.zip。 - 解压到一个固定目录,例如:
F:\一番街Q\LocalUnlockTool
不要把文件散放到桌面根目录。
- 推荐直接运行一键启动器:
.\RiichiCityOneClickUnlock.exe启动器会自动检查依赖、生成并安装 MITM CA 证书、自动设置本机已安装 Proxifier 的代理配置规则、启动 49020 MITM 和 49010 本地解锁代理。窗口保持打开即可;关闭窗口即停止代理链。
一键启动器默认不会自动启动游戏。等窗口显示“本地解锁代理已监听”后,通过 Steam 或桌面入口手动启动游戏即可。
Release 包里会看到两个编译程序,普通用户只需要启动 RiichiCityOneClickUnlock.exe。
| 文件 | 作用 | 是否需要手动启动 |
|---|---|---|
RiichiCityOneClickUnlock.exe |
一键启动器。负责检查/修复环境、生成和检测 MITM CA 证书、自动配置 Proxifier、启动 MITM、启动本地解锁代理。 | 需要。正常只启动这个。 |
RiichiCityLocalUnlockProxy.exe |
本地解锁代理核心服务。负责监听 127.0.0.1:49010,处理游戏请求,返回/补丁本地解锁数据。 |
不需要。会被一键启动器自动拉起。 |
不要同时手动启动这两个 EXE。手动启动 RiichiCityLocalUnlockProxy.exe 只适合排错或高级调试,否则可能造成 49010 端口占用、代理链重复、日志混乱。
MITM CA 默认固定保存在 %USERPROFILE%\.mitmproxy,只需要安装一次。后续启动器会检测当前用户 Root 证书库里是否已有同一证书指纹,已安装则直接跳过。更换或删除工具目录不会再生成新 CA;只有删除共享 CA 目录或显式指定新的 --mitm-conf-dir 时才会重新生成。
工具不会内置、复制或安装 Proxifier 程序本体。它只会定位你本机已有的 Proxifier.exe,写入 %APPDATA%\Proxifier4\Profiles\RiichiCity_OneClick.ppx,并设置当前 profile 为 RiichiCity_OneClick。如果 Proxifier 弹出“切换/导入/加载 profile”的确认框,启动器会默认自动点击“确定/是/OK/Yes”。写入前会把原 profile 备份到:
config\proxifier_backups\
如果你使用的是绿色版或自定义路径 Proxifier,可以显式指定:
.\RiichiCityOneClickUnlock.exe --proxifier-exe "E:\Proxifier\Proxifier.exe"如需调试确认框行为,可以禁用自动确认:
.\RiichiCityOneClickUnlock.exe --no-proxifier-auto-confirm如果你确实想让工具在代理链就绪后自动启动游戏,可以显式传入:
.\RiichiCityOneClickUnlock.exe --launch-game默认要求上游代理 127.0.0.1:7897 可用。如果你的环境不需要上游代理,可以运行:
.\RiichiCityOneClickUnlock.exe --remote-proxy-port 0如果 Proxifier 没有自动应用配置,请先确认 Proxifier 已安装/已启动;仍失败时再手动导入备用 profile:
config\RiichiCity_OneClick.ppx
- 手动方式:如果只需要大厅/背包界面解锁,启动本地代理:
powershell -ExecutionPolicy Bypass -File .\start_unlock_proxy_exe.ps1 -ListenPort 49010 -RemoteProxyPort 7897- 如果需要进入对局后角色与装扮也生效,先启动 MITM:
powershell -ExecutionPolicy Bypass -File .\mitm\start_mitm_capture.ps1 -ListenPort 49020 -UpstreamProxy http://127.0.0.1:7897默认复用当前用户的 %USERPROFILE%\.mitmproxy CA 目录。需要把其中的 mitmproxy-ca-cert.cer 安装到 Windows 的“受信任的根证书颁发机构”。可通过 --mitm-conf-dir 或环境变量 RIICHICITY_MITM_CONF_DIR 指定独立目录。
然后启动编译版代理并开启 MITM 路由:
powershell -ExecutionPolicy Bypass -File .\start_unlock_proxy_exe.ps1 -ListenPort 49010 -RemoteProxyPort 7897 -UseMitm- Proxifier 增加规则:
Program : Mahjong-JP.exe
Proxy : HTTP 127.0.0.1:49010
Action : 走本地代理
- 确认上游代理可用。如果你的网络环境依赖 Clash Verge / mihomo,默认要求
127.0.0.1:7897正在监听。 - 通过 Steam 正常启动游戏。
安装依赖:
cd /d F:\一番街Q\LocalUnlockTool
python -m pip install -r .\requirements.txt只跑本地解锁代理:
powershell -ExecutionPolicy Bypass -File .\start_unlock_proxy.ps1 -ListenPort 49010 -RemoteProxyPort 7897跑完整链路,包含对局 WebSocket 补丁:
powershell -ExecutionPolicy Bypass -File .\mitm\start_mitm_capture.ps1 -ListenPort 49020 -UpstreamProxy http://127.0.0.1:7897
powershell -ExecutionPolicy Bypass -File .\start_unlock_proxy.ps1 -ListenPort 49010 -RemoteProxyPort 7897 -UseMitm首次运行时程序会自动从 example 生成本地运行配置:
config/unlock_state.json
config/user_settings.json
常用配置在 config/user_settings.json:
current.roleID:当前角色 IDcurrent.skinID:当前皮肤 IDcurrent.titleID:当前称号 IDcurrent.headID:当前头像 IDcurrent.profileFrameID:当前头像框 IDcurrent.nickname:本地自定义昵称current.equip:当前装扮,key 为装扮类型 ID,value 为物品 IDstar_chars:星标角色列表homepage:本地大厅展示方案equipsuit:本地装扮方案列表
如果游戏安装路径不是默认路径,可以通过环境变量指定资源扫描目录:
$env:RIICHICITY_GAME_ASSET_ROOTS = "D:\SteamLibrary\steamapps\common\RiichiCity\Mahjong-JP_Data\StreamingAssets;D:\SteamLibrary\steamapps\common\RiichiCity\Mahjong-JP_Data\_Data\Mahjong"如果 lua_config 路径不在默认目录,也可以指定:
$env:RIICHICITY_LUA_CONFIG_PATHS = "D:\SteamLibrary\steamapps\common\RiichiCity\Mahjong-JP_Data\StreamingAssets\Base\lua_config"- 启动上游代理,例如
127.0.0.1:7897。 - 启动
mitm/start_mitm_capture.ps1。 - 启动
start_unlock_proxy.ps1 -UseMitm或start_unlock_proxy_exe.ps1 -UseMitm。 - Proxifier 把
Mahjong-JP.exe指向HTTP 127.0.0.1:49010。 - 进入游戏大厅,打开背包检查装扮是否显示已获得。
- 更换角色、皮肤、称号、牌背、桌布、桌边框、特效、BGM 等。
- 创建房间或进入对局,确认对局内不是默认角色/默认装扮。
- 观察代理日志:
logs/
mitm/logs/
正常进入对局且 WebSocket 补丁命中时,mitmdump 日志里应能看到类似:
[rc:ws:self]
[rc:ws:patch]
启动本地代理后运行:
powershell -ExecutionPolicy Bypass -File .\scripts\test_local_endpoints.ps1 -Port 49010典型结果会显示已补全的数量,例如:
userItemList : 1369
getTitleList : 227
userEquip : 15
roleList : 63
数量会随游戏版本和本地资源扫描结果变化。重点不是固定数字,而是关键接口能返回非空数据且未报错。
源码目录下安装构建依赖:
python -m pip install -r .\requirements-build.txt构建 zip:
powershell -ExecutionPolicy Bypass -File .\scripts\build_release.ps1 -Version v0.1.7只构建一键启动器:
powershell -ExecutionPolicy Bypass -File .\scripts\build_one_click_launcher.ps1输出位置:
release\RiichiCityLocalUnlockTool-v0.1.7-win-x64.zip
userItemList 只代表“已拥有饰品列表”,装饰品方案走的是另一个接口:/backpack/userEquipSuitV2。
如果本地 config/user_settings.json 里的 equipsuit 为空,或者某个方案的 items 为空,客户端就会显示空方案。当前版本会在启动后自动迁移:
- 首次运行会根据当前穿戴生成一个“本地默认方案”;
- 旧配置里已有空方案时,会自动把当前角色、皮肤、称号、头像框、牌背、桌布、立直棒等穿戴项补进
items; - 新建/编辑方案时,如果客户端没有传方案明细,会自动用当前穿戴生成快照。
如果仍显示为空,关闭工具后删除:
Remove-Item .\config\user_settings.json再重新启动 RiichiCityOneClickUnlock.exe,工具会重新生成本地方案。
称号主要走 /users/getTitleList,部分自定义/方案 UI 还会读取同一份本地穿戴状态。
当前版本会对称号接口做兼容补齐:
data保持为称号数组;- 同时补充
list、titleList、total,兼容不同客户端读取方式; - 每个称号同时带
titleID、titleId、TitleID; - 按客户端枚举语义返回
titleState/state/State:1=已拥有、2=正在使用、3=锁定; - 每个称号补充
type分类字段,避免称号面板因为缺分类而把数据全部过滤掉; - 自动补齐当前客户端
title_config中存在但旧本地列表缺失的特殊称号; - 每个称号强制标记为已拥有、未过期、可装备;
- 保存称号时兼容
titleID、titleId、title_id、id、itemID等字段。
如果游戏里仍显示没有任何称号,优先看:
Get-ChildItem .\logs\*users_getTitleList*.json | Sort-Object LastWriteTime -Descending | Select-Object -First 3确认日志里的响应解密后 data 数量不是 0,并且每条非默认称号都有 type 字段。当前版本内置的称号配置正常返回约 227 条,数量会随游戏版本变化。
优先检查:
- Proxifier 是否把
Mahjong-JP.exe指向127.0.0.1:49010。 - 本地代理是否正在监听:
netstat -ano | findstr ":49010"- 如果使用上游代理,确认
127.0.0.1:7897正在监听:
netstat -ano | findstr ":7897"- 如果开启
-UseMitm,确认127.0.0.1:49020正在监听:
netstat -ano | findstr ":49020"- mitmproxy 证书是否已安装到受信任根证书。
通常是启动阶段 HTTP/HTTPS 链路没有通:
- 先关闭
-UseMitm只启动普通代理,确认能进大厅。 - 检查
RemoteProxyPort是否与你的实际上游代理一致。 - 如果不需要上游代理,把
-RemoteProxyPort 0传给启动脚本。 - 查看
logs/*users_checkVersion*.json是否有响应。
确认游戏是从 Steam 正常启动,而不是直接双击 Mahjong-JP.exe。如果直启,Steam 登录态可能不可用。
这是 WebSocket 对局数据没有被补丁,重点检查:
- 是否启动
mitm/start_mitm_capture.ps1。 - 本地代理是否带
-UseMitm。 mitm/conf/mitmproxy-ca-cert.cer是否已安装并信任。mitm/logs/mitmdump*.stdout.log是否出现[rc:ws:patch]。- Proxifier 规则是否没有绕过
aga-alb.mahjong-jp.net。
当前逻辑会把本地补齐的装扮设置为:
isOwn = true
isCanGet = true
isLock = false
isExpired = false
expiredAt = 0
expirationTime = 0
如果仍然提示过期:
- 重启游戏,清掉客户端旧缓存。
- 删除本地运行状态后重新生成:
Remove-Item .\config\user_settings.json- 重新启动代理并进入背包刷新。
- 如果只在“自定义饰品方案保存/使用”里触发,保留对应
logs/*equipSuit*.json再分析接口字段。
优先检查游戏资源扫描:
- 确认游戏安装目录存在。
- 设置
RIICHICITY_GAME_ASSET_ROOTS。 - 设置
RIICHICITY_LUA_CONFIG_PATHS。 - 重启代理后再打开背包。
程序会从服务器原始返回、decor_catalog.json、资源文件名、lua_config item_name_xxx 合并 ID。特殊 ID 不连续时,lua_config 是最关键的补齐来源。
查看占用进程:
netstat -ano | findstr ":49010"
netstat -ano | findstr ":49020"换端口启动:
powershell -ExecutionPolicy Bypass -File .\start_unlock_proxy.ps1 -ListenPort 49110同时记得修改 Proxifier 规则。
建议使用 python -X utf8。本项目脚本已经默认使用 UTF-8 运行主程序。路径中有中文时,尽量在 PowerShell 里使用 -LiteralPath 或先 cd 到项目目录。
首次启动会把 config/settings.example.yaml 复制为 config/settings.yaml。修改后需要重启一键启动器;命令行参数优先于 YAML。
常用项:
features:分别开关角色、皮肤、称号、装扮、头像、头像框和主页方案。appearance:填写角色/皮肤/称号等 ID 可强制本地显示;保持null则跟随游戏内保存值。decorations.allowed_item_types:只显示指定的可装备装扮类型,空列表表示全部。decorations.blocked_item_ids:隐藏指定装扮 ID。network.remote_proxy_port:0表示直连,不需要上游代理。network.mitm_ca_dir:默认统一使用%USERPROFILE%\.mitmproxy。logging:控制流量索引、正文保存和日志预览长度。
普通道具、礼物、觉醒材料和活动消耗品的数量始终保留服务器原值。这是安全约束,不提供可关闭的配置项。
指定其他配置文件:
.\RiichiCityOneClickUnlock.exe --config .\config\my-settings.yaml配置解析错误、未知字段、代理异常与方案应用记录分别写入:
logs/one_click_launcher.*.log
logs/errors.jsonl
logs/events.jsonl
mitm/logs/patch_errors.jsonl
mitm/logs/flows.jsonl
收集完整诊断包:
powershell -ExecutionPolicy Bypass -File .\scripts\collect_diagnostics.ps1旧项目中恢复到的 AES 参数:
key = idpwepjzsjbg18sdf25as4bsefls944a
iv = DkHu8vuy/ye7cd7k
解密 Base64 字段:
python -X utf8 .\scripts\decrypt_check_data.py --decrypt-base64 --text "<base64>"加密 JSON:
python -X utf8 .\scripts\decrypt_check_data.py --encrypt-json --text '{"code":0,"data":{}}'- 游戏版本更新后,接口字段、WebSocket 命令或资源命名可能变化,需要重新抓包和补字段。
- 对局内生效依赖 MITM WebSocket 补丁;只跑普通 HTTP 代理时,背包/大厅可能生效,但对局内可能回到服务器原始状态。
mitm/conf/里的 CA 私钥是本机运行产物,不能提交到公开仓库。