Files
90CNServer/docs/开发文档.md
T
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

24 KiB
Raw Blame History

DNF90 单机模拟器 · 整合启动器 + GM 工具 开发文档

本文档用于换机后快速恢复开发环境,覆盖环境搭建、项目结构、架构原理、接口清单、打包部署、常见问题等全部关键信息。

最后更新:2026-11-03


目录

  1. 项目概述
  2. 开发环境搭建
  3. 项目结构与关键文件
  4. 技术架构与原理
  5. 服务端 API 契约
  6. 启动器后端 API 清单
  7. 关键业务逻辑与约定
  8. 打包与部署
  9. 参考资料位置
  10. 常见问题与坑

一、项目概述

一个单 EXE 的 DNF90 单机模拟器启动器,内置完整的 GM 管理工具。

  • 技术栈:Python 3.15 + pywebview 6.x(WebView2 内嵌窗口)+ 纯标准库 HTTP 后端 + 原生 HTML/CSS/JS 前端
  • 交付物:launcher/dist/DNF90启动器.exe(单文件,约 14.57MB)
  • 部署位置:EXE 始终放在 Z:\90server\ 根目录(服务端目录内)

核心功能

启动器主界面(4 按钮):

  • ▶ 开始游戏:configure-client → 启动服务端 → 注册账号 → 自动登录进游戏
  • ⚙ 打开 GM:切换到内置 GM 管理工具界面
  • ■ 关闭服务:安全停止服务端
  • ♥ 赞助我们:弹出赞助二维码
  • 🔄 一键换装:启动 LoadoutTool/DNFLoadout.exe

内置 GM 工具(功能页):

  • 控制台(服务端/数据库/Native API 状态)
  • 角色管理(等级/金币/职业转职/无限疲劳/删除角色)
  • 道具发放(装备/道具两分区 + 武器子类型筛选 + 品级筛选)
  • 邮件系统 / 背包装备 / 任务管理 / 副本控制 / 钱包点券(含黑钻)
  • 删档管理 / 连接设置(SQLite / Native 数据源切换)

二、开发环境搭建

2.1 Python 环境

本机有两个 Python,务必使用 Python 3.15:

Python 路径 用途
Python 3.15.2 C:\Python315-32\python.exe 主开发环境(必须用这个)
Python 3.13.14 python(PATH 默认) WorkBuddy 内置,勿用于本项目

⚠️ 所有命令用 C:\Python315-32\python.exe,不要用裸 python(会指向 3.13)。

2.2 依赖包安装

# 切到 Python 3.15
C:/Python315-32/python.exe -m pip install pywebview pythonnet clr-loader pyinstaller pillow requests fastapi

关键依赖及版本:

包 版本 用途
pywebview 6.2.1 内嵌 WebView2 窗口
pythonnet 3.2.1 .NET CLR 互操作(pywebview 依赖)
clr-loader (latest) pythonnet 的 CLR 加载器
pyinstaller 6.22.3 打包单 EXE
pillow 13.0.0 二维码裁剪处理
requests 2.34.2 Native API 客户端(native.py 用)
fastapi 0.162.0 仅 gmtool 旧版用(launcher 主项目不用)

2.3 pip 国内源(换机后需重新配置)

C:\Users\Administrator\AppData\Roaming\pip\pip.ini:

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
extra-index-url = https://mirrors.aliyun.com/pypi/simple/
trusted-host = pypi.tuna.tsinghua.edu.cn

2.4 换机后检查清单

  • 安装 Python 3.15(任意位置,更新 .bat 和打包命令里的路径)
  • 配置 pip 清华源
  • 安装上述依赖包
  • 确认目标电脑有 WebView2 运行时(Win10/11 自带;Win7 需手动装)
  • 将整个项目目录拷贝到新机(含 launcher/ 源码和 90server/ 运行环境)

三、项目结构与关键文件

3.1 工作区目录(开发源码所在地)

C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/
├── launcher/                  # ★ 主项目(启动器 + GM 工具)
│   ├── server.py              # ★ 整合后端(启动器动作 + GM HTTP 服务,纯标准库)
│   ├── native.py              # ★ Native API 客户端(封装服务端全部 GM 端点)
│   ├── db.py                  # ★ SQLite 数据访问层(删角色/删档/疲劳)
│   ├── index.html             # 双视图界面(启动器 + GM 工具)
│   ├── style.css              # 深色 DNF 风格主题
│   ├── app.js                 # ★ 前端逻辑(启动器 + GM 路由)
│   ├── sponsor.png            # 赞助二维码(已裁剪)
│   ├── sponsor_full_backup.png# 二维码原图备份
│   ├── sponsor_qr.png         # 二维码中间产物
│   ├── DNF90启动器.spec       # ★ PyInstaller 打包配置
│   ├── build/                 # 打包中间产物(可删)
│   └── dist/                  # 打包产物 DNF90启动器.exe
├── gmtool/                    # 旧版 FastAPI GM 工具(已弃用,仅参考)
│   ├── main.py
│   ├── gm_client.py
│   └── static/
├── README.md                  # 项目简介(较简略)
├── 开发文档.md                # 本文档
├── 2026-11-03.md              # 当日详细工作日志(历史记录)
├── 启动启动器.bat             # 源码方式运行启动器
├── 启动GM工具.bat             # 源码方式运行旧 GM 工具(已弃用)
└── .workbuddy/                # WorkBuddy 项目数据(勿删)
    └── memory/                # 工作记忆(日志)

3.2 运行环境(服务端所在,非源码)

Z:/90server/                   # ★ 可运行模拟器 + 启动器部署位置
├── DNF90启动器.exe            # ★ 打包后的启动器(放这里运行)
├── Server/                    # 服务端
│   └── runtime/
│       ├── bin/DNF90Control.exe   # 服务端控制器(start/stop/configure-client/launch-client/account)
│       ├── config/instance.json   # Native API token 配置
│       └── data/dnf90.db          # SQLite 数据库(首次启动生成)
├── Client/                    # 游戏客户端
└── LoadoutTool/DNFLoadout.exe # 一键换装工具

3.3 服务端源码(Go,仅供参考 API 契约)

C:/Users/Administrator/Downloads/90轮回源码/90轮回源码/go-server/
└── internal/
    ├── app/dnf90/
    │   ├── gm_tool_admin.go      # ★ GM 工具 HTTP 路由(GET 查询类)
    │   └── gm_admin.go           # ★ 黑钻等写操作
    ├── services/dnfbridge/
    │   ├── gm_tool.go            # GM 业务逻辑(职业/任务结构定义)
    │   ├── gm_tool_extended.go   # 角色详情、分类逻辑
    │   ├── gm_tool_items_extra.go# 道具发放 grant-options
    │   ├── gm_tool_dungeon.go    # 副本难度
    │   └── gm_tool_item_customization.go
    └── modules/dnf/
        ├── itemquality/quality.go  # 品级定义
        └── profession/profile.go   # 职业 Option 结构

四、技术架构与原理

4.1 整体架构

┌─────────────────────────────────────────────────┐
│  DNF90启动器.exe(单文件,pywebview WebView2)   │
│                                                 │
│  ┌──────────────┐    HTTP(127.0.0.1:19527)     │
│  │ 前端 app.js   │ ◄──────────────────────────► │
│  │ index.html   │                               │
│  └──────────────┘                               │
│          ▲                                      │
│          │ api()/post()                         │
│  ┌───────┴────────────────────────────────────┐ │
│  │ server.py(纯标准库 BaseHTTPRequestHandler)│ │
│  │  ├─ 启动器动作 flow_*()                     │ │
│  │  ├─ GM HTTP 路由 _api_*()                   │ │
│  │  ├─ SQLite 直读(DB, db.py)                │ │
│  │  └─ Native 客户端(NATIVE, native.py)      │ │
│  └───────┬──────────────────┬─────────────────┘ │
└──────────┼──────────────────┼───────────────────┘
           │ SQLite 直读       │ HTTP(127.0.0.1:18111, X-Admin-Token)
           ▼                   ▼
   Server/runtime/data/dnf90.db    DNF90Control.exe(运行中的服务端)

4.2 双数据源机制(核心设计)

GM 工具支持两种数据源,通过「连接设置」页切换:

数据源 条件 特点
Native API 服务端运行时 实时操作,127.0.0.1:18111 + X-Admin-Token
SQLite 直读 服务端停止时 直接读写 dnf90.db,可删角色/删档/改疲劳
  • 判断逻辑:_use_sqlite() 返回 True 时走 SQLite,否则走 Native
  • 删角色/删档/改疲劳必须停服(服务端运行时数据库被锁)

4.3 路径自动识别(重要)

SERVER_ROOT 按以下优先级自动定位(server.py 模块加载时执行一次):

  1. 环境变量 DNF90_SERVER_ROOT(手动覆盖)
  2. EXE 自身所在目录(若其下存在 Server/)—— 正常场景命中
  3. 向上逐级查找含 Server/ 的目录(最多 4 层)
  4. 兜底 EXE 所在目录 BASE_DIR

派生路径(全部基于 SERVER_ROOT):

CONTROL    = SERVER_ROOT + r"\Server\runtime\bin\DNF90Control.exe"
CLIENT_DIR = SERVER_ROOT + r"\Client"
LOADOUT_EXE = SERVER_ROOT + r"\LoadoutTool\DNFLoadout.exe"
INSTANCE_PATH = SERVER_ROOT + r"\Server\runtime\config\instance.json"
DB_PATH    = SERVER_ROOT + r"\Server\runtime\data\dnf90.db"

关键约定:启动器 EXE 永远放在 90server 根目录,路径解析完全锚定 EXE 自身位置,不依赖任何写死盘符。

4.4 Native API 认证机制

  • 服务端 GM API 监听 127.0.0.1:18111
  • 认证头:X-Admin-Token
  • 两套路由:/dnf/gm/*(新版)、/local/dnf-gm/*(兼容版)
  • token 通过 AUTO_GENERATE 机制生成,存于 instance.json
  • 危险写操作需 X-Admin-Confirm + X-Admin-Idempotency-Key 头(_post 默认 confirm=True)

五、服务端 API 契约

服务端是 Go 编写的二进制,源码在 90轮回源码/go-server/。以下契约是从源码反推,供 native.py 和 server.py 对接。

5.1 职业转职

GET  /dnf/gm/characters/{id}/career   → GMCareerState
POST /dnf/gm/characters/{id}/career   {job, first, awakening}

GMCareerState 字段:job、first_grow(分支,0=未转职)、awakening、profession_name、options[]、skills_reset、quest_reset_ids

options 层级结构:

Option { job, name, branches[] }
BranchOption { value, name, awakenings[] }
AwakeningOption { value, name }

5.2 任务查询

GET /dnf/gm/characters/{id}/quests           → {character, active[], completed[]}
GET /dnf/gm/quests/search?query=...          → GMQuestSummary[]
GET /dnf/gm/quests/{id}/detail?quest_id=...  → 详情
POST(任务操作 accept/ready/give-up/complete/uncomplete)

GMQuestSummary 字段:quest_id、name、grade、type、level_min、level_max、status、active、completed、acceptable

5.3 爆率 / 副本 / 黑钻

# 爆率(游戏玩法设置)
GET  /dnf/gm/gameplay                           → GMGameplaySettings
POST /dnf/gm/gameplay/hell-drop-rate            {percent}
POST /dnf/gm/gameplay/epic-potion-test-multiplier {percent}

# 副本
GET  /dnf/gm/characters/{id}/dungeons           → {difficulty_dungeons, difficulty_dungeons_unlocked, hell_areas, hell_permission_quests, hell_permission_quests_cleared}
POST /dnf/gm/characters/{id}/dungeons/unlock-difficulties
POST /dnf/gm/characters/{id}/dungeons/unlock-hell

# 黑钻
GET  /local/dnf-gm/character/black-diamond
POST /local/dnf-gm/character/black-diamond      {character_id, days}

GMGameplaySettings 字段:hell_epic_drop_rate_percent(当前爆率%)、minimum_percent、maximum_percent(上限 15000%)、epic_drop_rate_potion_test_multiplier_percent

黑钻返回:active、expires_at(时间戳,0=未开启)、remaining_days、fatigue_limit、black_diamond_fatigue_limit

5.4 品级(rarity)枚举

rarity 0~6 共 7 档,权威映射:

值 品级 示例装备
0 普通 粗布衬衫
1 高级 棉布衬衫
2 稀有 荣誉者长袍
3 神器 (旧) 雕翎长袍
4 史诗 (旧) 银月少女T恤衫
5 传说 贤者的智慧之衣
6 神话 首领萨斯的阴影长袍

⚠️ 注意:4=史诗、5=传说、6=神话(顺序易记错)。「勇者」不是独立品级档,是异界装备称呼,实际归类于神器(3)。 物品搜索的 rarity 参数是精确匹配(item.Rarity != rarity 过滤),范围 -1~6,-1=不限。

5.5 品级机制(装备发放时)

  • equipment_quality 仅接受 default / random / top 三个值
  • 品级本质是 quality_seed 数值:1=最下级,999999998(TopSeed)=最上级
  • 直接进背包路径(grant-options)忽略品级参数,只有邮件(mail)路径品级才真正生效
  • 服务端不支持「最下级」,只能最上级(top)或随机(random)

5.6 装备分类与武器子类型

  • 服务端 kind 值:"equipment"(装备)、"stackable"(道具)
  • 武器子类型通过 q=/weapon/<sub>/ 匹配 PVFPath(如 equipment/character/swordman/weapon/katana/)

六、启动器后端 API 清单

server.py 提供,前端通过 api()(GET)和 post()(POST)调用。端口 19527。

6.1 GET 路由

路径 说明
/api/status 启动器状态(含 server_root/client_dir/各文件是否存在/native_available)
/api/logs 运行日志
/api/gm/health GM 健康检查
/api/accounts-config 账号配置
/api/accounts 账号列表
/api/characters 角色列表(支持 ?account= 筛选)
/api/character 角色详情
/api/character/inventory 背包
/api/character/equipment 装备
/api/character/mailbox 邮箱
/api/character/fatigue 疲劳(mode=query)
/api/character/career 职业信息(转职查询)
/api/character/dungeons 副本状态
/api/character/black-diamond 黑钻状态
/api/gameplay 爆率设置查询
/api/quests 任务概览
/api/quests/search 任务搜索
/api/quests/detail 任务详情
/api/wipe/preview 删档预览
/api/items/categories 道具分类
/api/item/search 道具搜索

6.2 POST 路由

路径 说明
/api/action 启动器动作(start/stop/gm/loadout)
/api/accounts-config 账号配置写操作
/api/source 切换数据源(SQLite/Native)
/api/character/save 保存角色
/api/character/stat 改属性
/api/character/fatigue 改疲劳
/api/character/gold 改金币
/api/character/level 改等级
/api/character/skill-points 改技能点
/api/character/career 应用转职
/api/dungeon/unlock 解锁副本难度
/api/black-diamond/activate 开启黑钻
/api/gameplay/hell-drop-rate 设置爆率
/api/gameplay/epic-potion-multiplier 设置药剂倍率
/api/quests/action 任务操作
/api/character/grant 发放道具
/api/character/delete 删除角色
/api/account/delete 删除账号
/api/item/search 道具搜索(POST body)
/api/item/grant 发放道具
/api/item/grant-options 发放道具(高级选项)
/api/item/mail 邮件发放道具
/api/mail/send 发送邮件
/api/mailbox/clear 清空邮箱
/api/inventory/clear 清空背包
/api/inventory/delete 删除背包物品
/api/wallet/query 钱包查询
/api/cera 点券充值

6.3 native.py 方法清单(服务端 GM 端点封装)

native.py 是服务端 GM API 的完整 Python 客户端封装,方法列表:

  • 账号:list_accounts / get_account / set_account_value
  • 角色:list_characters / get_character / get_character_detail
  • 物品/背包:get_inventory / get_equipment / get_mailbox / get_item_detail / get_item_mail_options / get_item_categories / search_items / search_avatar_sets
  • 道具发放:grant_item / grant_item_options / grant_item_with_emblems / send_item_mail
  • 删除:delete_item / delete_inventory_item / clear_inventory / unequip_equipment / delete_equipment
  • 属性修改:set_gold / set_level / set_skill_bonus / set_career / get_career / set_cera / adjust_revive_coin
  • 钱包:query_wallet / update_wallet / set_account_cube / get_account_cubes
  • 邮件:send_system_mail / clear_mailbox / delete_mailbox_mails
  • 任务:get_quest_overview / search_quests / get_quest_detail / accept_quest / mark_quest_ready / give_up_quest / complete_quest / uncomplete_quest / complete_quest_batch
  • 副本:get_dungeons / unlock_dungeon_difficulties / unlock_hell_challenges
  • 黑钻:get_black_diamond / activate_black_diamond
  • 爆率:get_gameplay_settings / set_hell_drop_rate / set_epic_potion_multiplier
  • 宠物:update_pet

七、关键业务逻辑与约定

7.1 疲劳值机制

  • 角色 stats 里的字段:fatigue(当前疲劳)、fatigue_limit(上限,默认156/黑钻188)、fatigue_display_update(已用)
  • 每天 6 点重置回上限
  • 无限疲劳 = 把 fatigue 和 fatigue_limit 都设为超大值(999999)
  • 服务端无独立疲劳查询 GM 接口,从 get_character_detail() 的 stats 里读

7.2 账号配置

  • accounts.json 存 EXE 同目录(BASE_DIR)
  • load_accounts / save_accounts / get_default_account
  • 默认账号 dnfplayer 不可删除

7.3 窗口与 UI 约定

  • GM 窗口打开直接最大化,但不用 maximize()(会遮挡任务栏)
  • 用 _get_work_area()(ctypes 调 SystemParametersInfoW(SPI_GETWORKAREA=0x0030))获取扣除任务栏的工作区,再 move() + resize()
  • 主窗口和 GM 窗口都屏幕居中(webview.screens[0].width/height 计算 x/y)
  • 深色 DNF 风格 + 紫青渐变 + 毛玻璃质感

7.4 危险操作约定

  • 删除角色/删档/改疲劳:需先停服(SQLite 直读模式)
  • 危险操作二次确认:输入确认词(如 DELETE、转职)

7.5 进程回收

  • 关闭后需 os._exit(0) 强制退出 + _kill_webview2_children()(清理 WebView2 子进程)
  • 打包前需先 taskkill /F /IM "DNF90启动器.exe"(否则 dist exe 被锁定)

八、打包与部署

8.1 打包命令

用 spec 文件打包(推荐,配置已固化):

cd "C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/launcher"

# 先杀掉残留进程(避免文件锁定)
taskkill /F /IM "DNF90启动器.exe"

# 打包
C:/Python315-32/python.exe -m PyInstaller --clean --noconfirm "DNF90启动器.spec"

产物:launcher/dist/DNF90启动器.exe

8.2 拷贝到运行目录

cp -f "C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/launcher/dist/DNF90启动器.exe" "Z:/90server/DNF90启动器.exe"

8.3 spec 文件要点

DNF90启动器.spec 关键配置:

  • datas:打包 index.html、style.css、app.js、sponsor.png、db.py、native.py
  • hiddenimports:clr_loader、pythonnet
  • collect_all('webview'):pywebview 全部资源
  • console=False:windowed 模式(无控制台)
  • upx=True:UPX 压缩
  • name='DNF90启动器'

如果不用 spec,等价命令行(注意 Windows 分隔符是 ;)见 README.md。

8.4 部署要求

  • EXE 放在 90server 根目录
  • 目标电脑需 WebView2 运行时(Win10/11 自带)
  • 首次运行请求管理员权限(UAC,启动服务端需要)
  • 无需安装 Python 或其他依赖(单文件已全打包)

8.5 验证方法

打包后在同批命令内验证(后台进程在命令结束时会被回收):

cd "Z:/90server" && ./DNF90启动器.exe > /dev/null 2>&1 &
sleep 8
curl -s --noproxy "*" "http://127.0.0.1:19527/api/status"

预期返回:server_root=Z:\90server、db_exists=true、native_available=true、control_exists=true、server_dir_exists=true、client_dir_exists=true。


九、参考资料位置

资料 位置
服务端源码(Go) C:/Users/Administrator/Downloads/90轮回源码/90轮回源码/go-server/
旧 GM 工具源码备份 WorkBuddy 资料库节点 sfEHKi5MJIh0D9lSykGVoT(DNF90工具与资料备份 web 页)
旧 GM 工具(运行版) Z:/90server/gm_tool/(已打包 DNF90GM工具.exe)
可运行模拟器 Z:/90server/
当日工作日志 C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/2026-11-03.md

读取资料库参考资料的方法

# 查询节点信息
python3 "C:/Users/Administrator/AppData/Local/Programs/WorkBuddy/resources/app.asar.unpacked/resources/plugins/workbuddy-builtin/skills/library/space_api.py" space.workspace.node-info --node-id sfEHKi5MJIh0D9lSykGVoT

# 下载 artifacts
python3 ".../library/page/download_page_artifacts.py" --node-id "sfEHKi5MJIh0D9lSykGVoT" --out-dir "/tmp/gmref" --path "..."

十、常见问题与坑

10.1 打包相关

问题 解决
打包报 OSError safe-delete 失败 dist 的 exe 被占用,先 taskkill /F /IM "DNF90启动器.exe"
--add-data 分隔符 Windows 用 ;(不是 :)
打包后窗口变浏览器 pywebview transparent=True 在 Windows 不支持,会导致降级

10.2 pywebview 相关

问题 解决
无边框窗口 maximize 遮挡任务栏 用 _get_work_area() 获取工作区再 resize,不要用 maximize()
关闭后 WebView2 子进程残留 os._exit(0) + _kill_webview2_children()
create_window() 报错 必须在 webview.start() 之前调用

10.3 后端参数解析

重要教训:_api_character_fatigue 曾有个 bug——用 if "id" in data_or_qs 判断,导致 GET 请求的 parse_qs 结果(值是 list)被当成标量,char_id 变成 ['1'] 列表,报「角色 ['1'] 不存在」。

正确写法:GET 请求统一用 qs.get("id", [""])[0] 取第一个值;POST body 才直接 data.get("id")。

10.4 数据源相关

问题 解决
删角色/删档报数据库锁定 需先停服(点「关闭服务」)
疲劳值读不出 服务端无独立疲劳接口,用 get_character_detail() 的 stats.fatigue
道具搜索 404 前端用 post('/api/item/search') 但后端原来只有 GET 路由,需补 POST 路由

10.5 品级相关

  • rarity 精确匹配(-1 = 不限),枚举 0~6:普通/高级/稀有/神器/史诗/传说/神话
  • 品级 equipment_quality 只接受 default/random/top
  • 品级只有邮件路径生效,直接进背包路径忽略品级

10.6 测试相关

  • 后台进程在命令结束时会被回收(非交互模式),验证必须把启动和测试放同一批命令内
  • 服务端 DNF90Server.exe 是常驻进程,测试时不要误杀
  • 默认测试角色:character_id=1,名「测试」,职业神枪手,1级

附:快速上手速查

# 1. 源码方式运行启动器(开发调试)
cd "C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/launcher"
C:/Python315-32/python.exe server.py

# 2. 校验语法
C:/Python315-32/python.exe -c "import ast; ast.parse(open('server.py',encoding='utf-8').read())"
node --check app.js

# 3. 打包 + 拷贝(换机后重复此流程)
taskkill /F /IM "DNF90启动器.exe"
cd "C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/launcher"
C:/Python315-32/python.exe -m PyInstaller --clean --noconfirm "DNF90启动器.spec"
cp -f dist/DNF90启动器.exe "Z:/90server/DNF90启动器.exe"

# 4. 用默认测试角色验证(同批命令内)
cd "Z:/90server" && ./DNF90启动器.exe > /dev/null 2>&1 &
sleep 8 && curl -s --noproxy "*" "http://127.0.0.1:19527/api/status"