# DPMT桌面版 **Repository Path**: greenptt/dpmt-desktop-edition ## Basic Information - **Project Name**: DPMT桌面版 - **Description**: DPMT(DataPackMoreTool)的桌面版 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2026-07-13 - **Last Updated**: 2026-08-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # DPMT 生成工具 一个基于**数据驱动**架构的 Minecraft 数据包/资源包可视化生成工具。所有数据类型、UI 绑定、导出路径、命令语法均由 JSON/XML 配置文件定义,核心代码不硬编码任何业务规则。 --- ## 目录 - [项目简介](#项目简介) - [目录结构](#目录结构) - [快速开始](#快速开始) - [架构概览](#架构概览) - [运行原理细节](#运行原理细节) - [启动流程](#启动流程) - [静态文件服务](#静态文件服务) - [版本同步机制](#版本同步机制) - [数据驱动配置加载](#数据驱动配置加载) - [数据驱动格式](#数据驱动格式) - [tool_data.json](#tool_datajson) - [pack.dpmtmeta](#packdpmtmeta) - [paths/\*.json(UI 绑定)](#pathsjsonui-绑定) - [jsonTypes/\*.json(JSON 格式定义)](#jsontypesjsonjson-格式定义) - [choices/\*.json(选择列表)](#choicesjson选择列表) - [commandTypes/\*.xml(命令语法)](#commandtypesxml命令语法) - [工作区格式(Workspace V2)](#工作区格式workspace-v2) - [导出原理](#导出原理) - [导入原理](#导入原理) - [API 接口列表](#api-接口列表) - [配置文件汇总](#配置文件汇总) --- ## 项目简介 - **前端**:原生 JavaScript(ES Module)+ CSS,无构建步骤,浏览器直接加载 - **后端**:Python 3.13+ 标准库(`http.server`),无第三方依赖 - **ZIP 处理**:前端使用 [JSZip](https://stuk.github.io/jszip/)(MIT/GPLv3 双协议,已随项目分发 LICENSE) - **核心理念**:一切业务规则数据驱动,代码只提供运行时引擎 支持的数据类型涵盖:原版数据包(配方、进度、战利品表、谓词、函数、附魔、修饰符等)、原版资源包(纹理、模型、语言文件、pack.mcmeta)、DatapackMore 扩展(自定义物品、方块、状态效果)。 --- ## 目录结构 ``` src/main/ # 应用程序根目录(运行时目录) ├── launcher.py # 打包/exe 启动入口 ├── backend/ # Python 后端 │ ├── main.py # HTTP 服务器 + 路由分发 │ ├── config.py # 路径常量、版本同步逻辑 │ ├── logger.py # 日志会话管理 │ └── handlers/ │ ├── api_handler.py # 工具数据、数据驱动配置、命令定义等 API │ ├── import_handler.py # ZIP 导入 → Workspace 转换 │ └── launcher.py # 游戏启动 │ ├── frontend/ # 前端静态资源 │ ├── index.html # 单页入口 │ ├── css/ │ │ ├── main.css # 主样式 │ │ ├── visualEditor.css # 可视化编辑器样式 │ │ └── imageEditor.css # 图片编辑器样式 │ ├── js/ │ │ ├── main.js # 应用主逻辑(App 类) │ │ ├── config.js # API 端点配置 │ │ ├── components/ │ │ │ ├── editor.js # 字段编辑器 │ │ │ ├── fieldRenderer.js # 字段渲染器(数据驱动) │ │ │ ├── wizard.js # 新建项目向导 │ │ │ ├── projectTree.js # 项目树 │ │ │ ├── typeRegistry.js # JSON 类型注册表(带缓存) │ │ │ ├── commandBlockBuilder.js # 命令方块构建器 │ │ │ ├── commandTextEditor.js # 命令文本编辑器(语法补全) │ │ │ ├── imageEditor.js # 图片编辑器 │ │ │ ├── modal.js # 通用模态框 │ │ │ ├── blocklyEditor.js # Blockly 积木编辑器 │ │ │ └── visualBlockEditor.js # 可视化积木编辑器 │ │ ├── views/projectView.js │ │ ├── utils/ │ │ │ ├── api.js # 后端 API 封装 │ │ │ ├── i18n.js # 国际化 │ │ │ ├── validation.js # 命名空间/modId 校验 │ │ │ ├── choiceModal.js # 选择弹窗 │ │ │ └── tutorialModal.js # 教程弹窗 │ │ └── lib/ │ │ ├── jszip.min.js # JSZip 库 │ │ ├── jszip-LICENSE.txt │ │ └── jszip.LICENSE │ └── assets/ # 图标等静态资源(bootstrap-icons) │ ├── data/ # 运行时数据目录(自动同步,勿手改) │ ├── jsonTypes/ # JSON 格式定义(核心) │ │ ├── advancement/ # 进度定义 │ │ ├── core/ # 核心类型(stringlist、itemslist等) │ │ │ ├── stringlist.json │ │ │ ├── itemslist.json │ │ │ ├── floatlist.json │ │ │ ├── click_event.json │ │ │ ├── hover_event.json │ │ │ ├── hover_entity_content.json │ │ │ ├── score_component.json │ │ │ ├── text_component.json │ │ │ ├── text_component_obj.json │ │ │ ├── text_component_list.json │ │ │ └── text_component_types.json │ │ ├── datapackmore/ # DatapackMore 扩展定义 │ │ ├── enchantment/ # 附魔定义 │ │ ├── loot/ # 战利品表定义 │ │ ├── modifier/ # 修饰符定义 │ │ ├── pack/ # pack.mcmeta 定义 │ │ ├── predicate/ # 谓词定义 │ │ ├── recipe/ # 配方定义 │ │ │ ├── master.json │ │ │ ├── result.json │ │ │ ├── ingredient.json │ │ │ ├── shaped_ingredients.json │ │ │ ├── shapeless_ingredients.json │ │ │ ├── trim_pattern.json │ │ │ └── components/ │ │ └── tag/ # 标签定义 │ ├── paths/ # UI 绑定定义 │ ├── choices/ # 选择列表数据 │ │ ├── items/ # 物品ID选择列表 │ │ │ ├── all.json # 物品分类(子菜单) │ │ │ ├── normal.json # 普通物品列表(带图片) │ │ │ ├── blocks.json # 方块物品列表(带图片) │ │ │ └── tagsall.json # 物品标签列表(普通) │ │ ├── minecraft/ # Minecraft官方ID选择列表 │ │ │ ├── recipe_types/ # 配方类型选择列表 │ │ │ │ └── all.json │ │ │ ├── enchantments.json │ │ │ ├── advancements.json │ │ │ ├── damage_types.json │ │ │ ├── biome.json │ │ │ └── ...(40+ 个选择文件) │ │ ├── datapackmore/ # DatapackMore扩展选择列表 │ │ │ ├── block_model_parents.json │ │ │ ├── effect_categories.json │ │ │ ├── face_direction.json │ │ │ ├── face_rotation.json │ │ │ ├── item_model_parents.json │ │ │ ├── item_model_types.json │ │ │ ├── model_rotation_angle.json │ │ │ └── rotation_axis.json │ │ ├── modifier/ # 修饰符选择列表 │ │ │ ├── operation.json │ │ │ └── slots.json │ │ └── core/ # 核心选择列表 │ │ ├── click_event_actions.json │ │ ├── hover_event_actions.json │ │ ├── text_colors.json │ │ ├── nbt_sources.json │ │ ├── sprite_atlases.json │ │ ├── sprite_types.json │ │ └── text_component_types.json │ ├── commandTypes/ # 命令类型定义(XML,新格式) │ │ ├── manifest.json # 命令类型清单 │ │ ├── execute.xml │ │ ├── give.xml │ │ ├── tellraw.xml │ │ ├── scoreboard.xml │ │ ├── advancement.xml │ │ ├── attribute.xml │ │ ├── clear.xml │ │ ├── damage.xml │ │ ├── data.xml │ │ ├── effect.xml │ │ ├── fill.xml │ │ ├── kill.xml │ │ ├── say.xml │ │ ├── setblock.xml │ │ ├── spawnpoint.xml │ │ ├── summon.xml │ │ ├── time.xml │ │ ├── tp.xml │ │ └── weather.xml │ ├── commandValueTypes/ # 命令参数值类型定义 │ │ ├── criterion.json │ │ └── item.json │ ├── assets/ # 原版资源包资源(用于参考) │ ├── mod/ # 模组相关数据 │ │ └── datapackmore-1.0.0.jar │ ├── templates/ # 新建文件模板 │ │ ├── enchantment/ │ │ └── recipe/ │ ├── tempid.json # 当前 data/ 对应的版本 ID 缓存 │ └── tempinjarpath.json # JAR 内路径临时记录 │ ├── version/ # 版本数据源(开发时编辑这里) │ ├── mcje_datapack_1_20_1/ │ ├── mcje_datapack_1_21_11/ │ ├── mcje_datapackmore_items_1_20_1/ │ ├── mcje_datapackmore_items_1_21_1/ │ ├── mcje_datapackmore_items_1_21_11/ │ ├── mcje_datapackmore_items_26_1/ │ └── mcje_datapackmore_items_26_2/ │ ├── pack.dpmtmeta # 版本元数据 │ └── data/ # 该版本的 jsonTypes/paths/choices │ ├── better.py # 版本管理辅助脚本 │ └── cleanbak.py # 版本清理脚本 │ ├── userdata/ # 用户数据 │ ├── tool_data.json # 工具配置(base_path、export_root、上次路径等) │ └── settings/ │ ├── version.json # 选中的数据格式版本 │ └── language.json # 选中的语言 │ ├── assets/ # 应用资源 │ └── launch.png # 启动画面 │ └── log/ # 运行日志(按会话分目录) ``` **项目顶层结构(源代码仓库):** ``` DPMT_WinEdition/ ├── src/main/ # 应用程序源码(上述详细结构) ├── src/tools/toggle_tool/ # 切换工具 ├── scripts/build/ # 构建脚本(build.py、DPMT.spec、pack.bat 等) ├── scripts/utils/ # 实用脚本 ├── docs/ # 文档 ├── imports/minecraft/ # 导入资源(如 zh_cn.json) ├── releases/ # 发布目录 ├── resources/ # 应用图标资源(app.ico、app.png) └── README.md # 本文件 ``` --- ## 快速开始 ### 环境要求 - Python 3.13+(注意:`cgi` 模块在 3.13 已移除,本项目已不再依赖它) - 现代浏览器(支持 ES Module) ### 启动 ```bash python launcher.py ``` 服务器监听 `http://localhost:9999`,浏览器访问即可。启动时会自动同步所选版本的数据到 `data/` 目录。 --- ## 架构概览 ``` ┌─────────────────────────────────────────────────────┐ │ 浏览器(前端) │ │ index.html + main.js + components/ │ │ ├─ 数据驱动 UI 渲染(fieldRenderer) │ │ ├─ JSZip 导出/导入(前端生成 ZIP,无需后端往返) │ │ ├─ 命令语法补全(commandTextEditor) │ │ └─ 文件浏览器(前端弹窗,替代 tkinter) │ └──────────────────────┬──────────────────────────────┘ │ HTTP(同域,相对路径) ┌──────────────────────▼──────────────────────────────┐ │ Python 后端(单进程多线程) │ │ socketserver.ThreadingTCPServer :9999 │ │ ├─ 静态文件服务(frontend/ + data/) │ │ ├─ API 路由(/api/*) │ │ ├─ multipart/form-data 手动解析(不依赖 cgi) │ │ └─ 版本同步(version/{id}/data → data/) │ └─────────────────────────────────────────────────────┘ ``` **前后端职责划分:** - 前端:所有 UI 渲染、数据编辑、ZIP 生成/解析、文件路径选择、modId 校验 - 后端:静态文件服务、文件系统读写(保存到用户选定路径)、版本同步、游戏启动 --- ## 运行原理细节 ### 启动流程 1. `src/main/launcher.py` 调用 `backend.main.start()` 2. 创建日志会话(`log/{时间戳}/`) 3. 读取 `userdata/settings/version.json` 获取选中的数据格式版本 ID 4. 调用 `sync_version_data()`:将 `version/{id}/data/` 拷贝到根 `data/`(基于 `based_on` 递归合并) 5. 写入 `data/tempid.json` 记录当前版本 ID 6. 启动 `ThreadingTCPServer` 监听 9999 端口 7. 前端加载后调用 `/api/tool-data` 和 `/api/datadriven-config` 获取配置 ### 静态文件服务 `UnifiedHandler._get_file_path()` 处理两类路径: - `/data/*` → 映射到项目根的 `data/` 目录(数据驱动配置文件) - 其他路径 → 映射到 `frontend/` 目录(HTML/CSS/JS/图片) **关键点**:使用 `urlparse(self.path).path` 去除查询字符串(如 `?v=20260630` 缓存版本号),否则文件查找会失败。路径中包含 `..` 直接拒绝(防路径遍历)。 ### 版本同步机制 版本同步系统通过 `based_on` 字段支持**无限层级继承**,实现版本间的数据共享和扩展。 **同步流程**(`backend/config.py: sync_version_data`): 1. 读取目标版本的 `pack.dpmtmeta`,解析 `based_on` 字段 2. 递归追溯继承链,构建从根版本到当前版本的完整路径 - 示例:`mcje_datapackmore_items_1_21_11` → `mcje_datapack_1_21_11` 3. 清空运行时 `data/` 目录(保留 `tempid.json`) 4. 按继承链顺序依次拷贝: - 先拷贝根版本的 `data/` 到运行时 `data/` - 再拷贝中间版本的 `data/`(同名文件覆盖) - 最后拷贝当前版本的 `data/`(同名文件覆盖) 5. 编译 `data/jsonTypes/` 中的 `*flatup` 字段——将 Key 为 `"*flatup"` 的字段展开为被引用类型的所有 parts 6. 写入 `data/tempid.json` 记录当前版本ID,避免重复拷贝 **递归合并规则**: - **文件级别覆盖**:完全同路径的文件会被覆盖 - **目录级别合并**:目录内的不同子项全部保留 - **新增保留**:当前版本独有的文件/目录直接添加 **继承链示例**: ``` version/mcje_datapackmore_items_1_21_11/ ├── pack.dpmtmeta { "based_on": "mcje_datapack_1_21_11" } └── data/ └── jsonTypes/ └── datapackmore/ └── item.json ← 扩展版本新增 version/mcje_datapack_1_21_11/ ├── pack.dpmtmeta { "based_on": null } └── data/ └── jsonTypes/ ├── core/ │ ├── stringlist.json ← 基版本提供 │ └── itemslist.json ├── recipe/ │ ├── master.json ← 基版本提供 │ ├── result.json │ └── shaped_ingredients.json 同步后的运行时 data/jsonTypes/: ├── core/ ← 来自基版本 │ ├── stringlist.json │ └── itemslist.json ├── recipe/ ← 来自基版本 │ ├── master.json │ ├── result.json │ └── shaped_ingredients.json └── datapackmore/ ← 扩展版本新增 └── item.json ``` **性能优化**: - 通过 `tempid.json` 缓存当前版本,启动时快速比对避免重复拷贝 - 用户可在设置中强制重新同步(重启生效) - 最大继承深度限制为100层(防止无限循环) #### *flatup 扁平引用编译机制 ##### 设计目的 `*flatup` 解决的核心问题是**并列字段的复用与组合**:有一组字段需要在多个地方被引用,且引用后必须跟目标类型的其他字段处于**同一层级**(不能嵌套)。 用 `From:` 引用虽然能复用,但前端会渲染为嵌套容器——字段被包在子容器里,用户需要点开折叠才能看到。有些数据天然是扁平结构的(如实体的全部 NBT 字段),不应该有嵌套容器。 `*flatup` 在**编译时**(同步到 `data/` 的阶段)将被引用类型的所有字段平铺到当前文件中,使开发者可以: - **按逻辑拆分字段组**:把一组相关的字段定义在一个独立的 jsonTypes 文件中,各文件各司其职 - **在多个类型中组合引用**:同一个字段组可以被多个不同的类型通过 `*flatup` 引用 - **编译期扁平化**:前端看到的 jsonTypes 是展开后的平坦 `parts` 数组,无需处理嵌套 - **源文件变更自动跟进**:字段组文件增删字段,所有引用它的类型重新编译后自动更新 **典型场景**: - 实体 NBT 继承链(Entity → LivingEntity → Mob → ... → Bee)——每层的字段都是并列关系 - 多个类型共享一组公共字段(如所有容器共有的 inventory 字段) - 任何需要"把这些字段原样摆在这里"而非"把这些字段包在一个框里"的场景 ##### 与 `based_on` 版本继承的区别 | 对比项 | `based_on` 版本继承 | `*flatup` 字段级引用 | |--------|---------------------|---------------------| | 作用域 | 版本包之间(跨目录) | 同一版本内的 jsonTypes 文件之间 | | 解决的问题 | 版本间数据复用(模组扩展继承原版) | 类型层级间字段复用(实体继承链) | | 粒度 | 文件级别 | 字段级别 | | 触发时机 | 同步时文件拷贝 | 同步时文件内 `parts` 展开 | | 编译方式 | 后覆盖前(同名文件替换) | 递归展开(平铺到同一数组) | ##### 机制详解 在 version 源文件中用 `"*flatup"` 字段引用另一个类型的所有 parts,在同步到 `data/` 时将其展开为被引用类型的全部字段。 **典型用法**:实体数据类型的继承链展开(如蜜蜂实体字段由 Entity → LivingEntity → Mob → Breedable → Animal → Bee 逐层继承): ```json { "id": "entity_data", "parts": [ { "Key": "*flatup", "Type": ["From:predicate/entity_data/entity"] }, { "Key": "*flatup", "Type": ["From:predicate/entity_data/living_entity"] }, { "Key": "*flatup", "Type": ["From:predicate/entity_data/mob"] }, { "Key": "*flatup", "Type": ["From:predicate/entity_data/breedable"] }, { "Key": "*flatup", "Type": ["From:predicate/entity_data/animal"] }, { "Key": "*flatup", "Type": ["From:predicate/entity_data/bee"] } ] } ``` **编译规则**: | 规则 | 说明 | |------|------| | 展开替换 | `Key: "*flatup"` 的字段被替换为被引用类型 `parts` 中的所有字段(包括被引用类型内部的 `*flatup`) | | 递归展开 | 如果被引用类型中也有 `*flatup`,递归展开,最大深度 10 层 | | 重复键名 | 展开后出现同名 `Key`,后出现的字段 `Key` 和 `Des` 尾部添加 "(repeat)" 后缀 | | 跳过条件 | 引用文件不存在、格式错误、Type 不是单一 `From:` 路径、被引用类型是 TIL 列表类型时,保持 `*flatup` 不变 | **边界行为**:如果继承链中的某个层级类型文件缺失(如动物通用标签引用了一个不存在的文件),该 `*flatup` 保持原样不展开,剩余的 `*flatup` 正常展开,不影响其他字段。 **工作原理**(`backend/config.py` 中的 `_compile_flatup` 系列函数): 1. 遍历 `data/jsonTypes/` 下所有 JSON 文件 2. 对每个文件检查 `parts` 中是否有 `Key: "*flatup"` 的字段 3. 解析 `Type[0]` 获取 `From:` 引用路径,加载被引用文件的内容 4. 递归展开被引用文件内部的 `*flatup` 5. 将展开后的字段列表替换原 `*flatup` 位置 6. 扫描展开后的字段列表,处理重复键名 ### 数据驱动配置加载 前端启动时并行请求: | API | 返回 | 来源目录 | |-----|------|---------| | `/api/tool-data` | 工具配置 | `userdata/tool_data.json` | | `/api/datadriven-config` | `{ui: [...], paths: {...}}` | `data/paths/*.json` 合并 | | `/api/commands` | 命令定义(旧格式) | `data/commands/` | **合并 API 优化**:`/api/datadriven-config` 一次性读取 `data/paths/` 下所有 JSON,同时填充 `ui`(数组)和 `paths`(字典),减少 50% IO。 前端按需异步加载 `data/jsonTypes/{path}.json`(通过 `typeRegistry.loadType`,带内存缓存),避免启动时加载全部类型定义。 --- ## 数据驱动格式 ### tool_data.json 位于 `userdata/tool_data.json`,工具全局配置: ```json { "version": "1.0.0", "base_path": "datapack/data/{namespace}", "export_root": "datapack", "types": {}, "in_app": true, "lastPaths": { "exportZip": "C:\\Users\\PC\\Downloads\\xx.zip", "exportModJar": "C:\\Users\\PC\\Downloads\\xx.jar" } } ``` | 字段 | 说明 | |------|------| | `base_path` | 传统类型导出路径模板,`{namespace}` 替换为命名空间 | | `export_root` | 导出 ZIP 的最外层根目录名(如 `datapack`),导出时所有文件加此前缀 | | `types` | 传统类型定义(已基本被数据驱动取代,保留兼容) | | `in_app` | 是否应用内启动模式 | | `lastPaths` | 各前端弹窗的上次选择路径,下次弹窗默认定位到此 | ### pack.dpmtmeta 版本元数据,位于 `version/{id}/pack.dpmtmeta`: **基础版本示例**(原版数据包): ```json { "id": "mcje_datapack_1_21_11", "name": "mcjava版1.21.11原版数据包" } ``` **扩展版本示例**(带继承和模组路径映射): ```json { "id": "mcje_datapackmore_items_1_21_11", "name": "mcjava版fabric1.21.11模组", "based_on": "mcje_datapack_1_21_11", "injarpath": { "datapack/datapack/data": "data", "datapack/datapackmore/data": "assets/datapackmore/data", "datapack/resourcepack/assets": "assets" } } ``` | 字段 | 说明 | |------|------| | `id` | 版本唯一标识 | | `name` | 用户可见的显示名 | | `based_on` | 可选,指向基版本 ID(支持无限层级继承) | | `injarpath` | 可选,导出模组JAR时的路径映射字典 | **injarpath 路径映射说明**: - 键:Workspace中的路径前缀 - 值:JAR内的实际路径 - 示例:`"datapack/datapack/data": "data"` 表示Workspace中 `datapack/datapack/data/xxx` 目录下的文件在JAR中映射为 `data/xxx` - 用途:DatapackMore模组需要将数据包和资源包文件合并到同一个JAR中,通过此映射实现正确的路径转换 ### paths/\*.json(UI 绑定) 位于 `data/paths/`,每个文件定义一类数据在 UI 中的位置和导出路径。 ```json { "UI_Path": "原版数据包/配方", "Types": [ { "save_path": "datapack/data/__NS__/recipe", "BN": "json", "Type": "json:recipe/master", "templates": [ { "name": "蛋糕配方", "data": "recipe/cake.json" } ] } ] } ``` | 字段 | 说明 | |------|------| | `UI_Path` | UI 树路径,`/` 分隔两层(如「原版数据包/配方」) | | `Types[].save_path` | 导出 ZIP 内路径,`__NS__` 替换为命名空间 | | `Types[].BN` | 文件后缀名(`json`/`mcfunction`/`png`/`mcmeta`) | | `Types[].Type` | 数据类型,格式见下表 | | `Types[].templates` | 可选,新建时提供的模板列表 | **Type 字段取值:** | 取值 | 含义 | 对应编辑器 | |------|------|-----------| | `json:{path}` | JSON 数据,引用 `data/jsonTypes/{path}.json` | 可视化字段编辑器 | | `mcfunction` | 函数文件 | 命令文本编辑器(带语法补全) | | `png` | 图片文件 | 图片编辑器 | **特殊路径规则(__NS__指命名空间):** - `datapack/data/__NS__/...` → 数据包文件 - `resourcepack/assets/__NS__/...` → 资源包文件 - `datapackmore/data/__NS__/...` → DatapackMore 扩展文件 ### jsonTypes/\*.json(JSON 格式定义) 位于 `data/jsonTypes/`,定义 JSON 数据的字段结构。这是整个数据驱动系统的**核心**,决定了可视化编辑器的所有字段显示和验证逻辑。 #### 顶层结构 ```json { "id": "<类型唯一标识>", "parts": [...], // 对象型定义(不存在TIL时使用) "TIL": [...] // 列表型定义(存在时parts失效) } ``` | 字段 | 说明 | |------|------| | `id` | 类型唯一标识,供 `From:` 引用 | | `parts` | 对象型定义的字段列表(每个字段对应一个键值对) | | `TIL` | 列表型定义(存在时 `parts` 失效),每项表示列表支持的数据类型 | #### 完整示例:配方定义(recipe/master.json) 以下是一个真实的配方主定义文件,展示了所有核心特性: ```json { "id": "mc_recipe_master", "parts": [ { "Key": "type", "Type": ["S:配方类型¥minecraft/recipe_types/all"], "MBW": true, "Des": "配方类型标识,决定后续字段显示" }, { "Key": "category", "Type": ["S"], "Des": "配方在游戏配方书的分类(如misc/blocks)" }, { "Key": "pattern", "Type": ["From:core/stringlist"], "MBW": true, "When": "type", "WD": [ { "type": { "id": "type", "Value": "minecraft:crafting_shaped" } } ], "Des": "有序合成工作台图案(如[\"###\",\"#A#\",\"###\"])" }, { "Key": "key", "Type": ["From:recipe/shaped_ingredients"], "MBW": true, "When": "type", "WD": [ { "type": { "id": "type", "Value": "minecraft:crafting_shaped" } } ], "Des": "有序合成符号与物品的对应关系" }, { "Key": "ingredients", "Type": ["From:recipe/shapeless_ingredients"], "MBW": true, "When": "type", "WD": [ { "type": { "id": "type", "Value": "minecraft:crafting_shapeless" } } ], "Des": "无序合成材料列表" }, { "Key": "ingredient", "Type": [ "S:单个物品ID¥items/all", "From:core/itemslist:多个物品列表" ], "MBW": true, "When": "type1||type2", "WD": [ { "type1": { "id": "type", "Value": "minecraft:smelting" } }, { "type2": { "id": "type", "Value": "minecraft:blasting" } } ], "Des": "熔炉/高炉的烧炼原料" }, { "Key": "experience", "Type": ["F"], "MBW": true, "When": "type1||type2", "WD": [ { "type1": { "id": "type", "Value": "minecraft:smelting" } }, { "type2": { "id": "type", "Value": "minecraft:blasting" } } ], "Des": "烧炼完成后玩家获得的经验值(如0.7)" }, { "Key": "result", "Type": ["From:recipe/result"], "MBW": true, "Des": "所有配方的最终输出结果(物品ID+数量)" } ] } ``` **关键特性解析**: 1. **基础类型字段**:`type` 字段使用 `S:配方类型¥minecraft/recipe_types/all` - `S` 表示字符串类型 - `配方类型` 是在选择弹窗标题中显示的描述 - `¥minecraft/recipe_types/all` 表示选择列表路径(指向 `data/choices/minecraft/recipe_types/all.json`) 2. **复合类型引用**:`pattern` 字段使用 `From:core/stringlist` - `From:` 表示引用其他jsonTypes定义 - `core/stringlist` 引用 `data/jsonTypes/core/stringlist.json` - 该文件定义了一个字符串列表类型 3. **条件显示机制**:`pattern` 字段只在有序合成时显示 - `When: "type"` 引用WD中定义的布尔变量 - `WD` 定义条件:当 `type` 字段的值为 `minecraft:crafting_shaped` 时显示 4. **多类型字段**:`ingredient` 字段支持两种类型 - 用户可以选择"单个物品ID"或"多个物品列表" - 前端会弹出类型选择对话框 5. **多条件组合**:`ingredient` 字段的 `When: "type1||type2"` - 使用 `||` 表示"或"逻辑 - 同时支持熔炉和高炉两种配方类型 #### parts[] 字段详解 | 字段 | 说明 | 示例 | |------|------|------| | `Key` | 导出 JSON 的键名 | `"Key": "type"` → 导出为 `{ "type": "..." }` | | `Key: undefined` | 用户自定义键名 | `"Key": "undefined"` → 用户输入键名(如"A"、"B") | | `Key: "*flatup"` | 编译时扁平展开引用 | `"Key": "*flatup"` → 同步时被替换为引用类型的所有 parts 字段 | | `id` | 不定键名的ID标识 | `"id": "symbol"` → 存储 key 为 `undefined_symbol_A` | | `Type` | 允许的类型列表 | `["S", "I"]` → 用户选择字符串或整数类型 | | `MBW` | 是否必填 | `true` → 必填字段,空值会提示错误 | | `When` | 条件显示正则 | `"type"` → 当WD中type为true时显示 | | `WD` | 条件调色板 | 定义When中可用的布尔变量 | | `Des` | 字段描述 | 显示给用户的说明文字 | #### Type 取值格式 | 格式 | 含义 | 实际示例 | |------|------|---------| | `S` | 字符串(无描述) | `"Type": ["S"]` | | `I` | 整数 | `"Type": ["I"]` | | `F` | 浮点数 | `"Type": ["F"]` | | `B` | 布尔值 | `"Type": ["B"]` | | `S:描述` | 带描述的字符串 | `"S:配方类型"` → 选择时显示"配方类型" | | `S:描述¥路径` | 带选择列表的字符串 | `"S:配方类型¥minecraft/recipe_types/all"` → 弹窗选择 | | `From:path/id` | 引用复合类型 | `"From:core/stringlist"` → 引用列表定义 | | `From:path/id:描述` | 带描述的引用 | `"From:core/itemslist:多个物品"` → 多类型时显示"多个物品" | | `From:path/id#(条件)` | 带条件的引用 | `"From:recipe/master#(type=s)"` → 自动推断类型 | | `From:*empty` | 存在性标记(不渲染UI) | `"Type": ["From:*empty"]` → 不显示编辑器,只要这个字段存在就生效,不管内容是什么 | > **存在性标记(`From:*empty`)**:有些字段不需要用户编辑任何值,只要这个字段在 JSON 里以空对象 `{}` 形式存在就生效,不写就表示不生效(比如 `minecraft:glider`——写了 `"glider": {}` 就滑翔,不写就不滑翔)。软件用"一个描述为空、可选的对象字段"来实现这个概念。要让字段类型为对象,就得用 `From:` 引用一个目标。目标可以是: > - `*empty` 关键词:`"Type": ["From:*empty"]`,告诉前端这是一个存在性标记,不渲染编辑界面,导出时生成 `{}` > - 一个 `parts: []`(空数组)的 JSON 格式文件:引用它时前端不渲染容器 > - 一个 `parts` 里所有字段都因 When 条件不符合而被隐藏的 JSON 格式文件:运行时所有字段被挡掉,容器整体不显示 > > 三种方式效果一样——字段存在就生成 `{}` 并生效,不写就跳过,前端不给用户编辑界面。 #### 列表型定义(TIL) 当JSON数据是列表而非对象时,使用 `TIL` 字段: **示例:字符串列表(core/stringlist.json)** ```json { "id": "stringlist", "TIL": ["S"] } ``` - 定义一个字符串列表类型 - 前端渲染为可添加/删除的列表UI - 用户点击"+添加"按钮输入字符串 **示例:物品列表(core/itemslist.json)** ```json { "id": "itemslist", "TIL": ["S:物品ID¥items/all"] } ``` - 定义一个物品ID列表类型 - 每项都从选择列表中选择物品ID - `¥items/all` 指向 `data/choices/items/all.json` #### 不定键名机制(undefined) 当字段的 `Key` 为 `undefined` 时,表示用户需要自定义键名: **示例:有序合成符号映射(recipe/shaped_ingredients.json)** ```json { "id": "crafting_shaped_ingredients", "parts": [ { "Key": "undefined", "id": "symbol", "Type": [ "S:单个物品¥items/all", "S:单个标签¥items/tagsall", "From:core/itemslist:多选物品列表" ], "MBW": true, "Des": "有序合成符号对应的物品ID/多选物品列表" } ] } ``` **工作原理**: 1. 前端渲染时显示"添加符号"按钮 2. 用户点击后输入符号(如"A"、"B"、"#") 3. 然后选择该符号对应的物品/标签/物品列表 4. 存储格式:`"undefined_symbol_A": "minecraft:diamond"` 5. 导出时自动还原为 `"A": "minecraft:diamond"` **用途**:适用于需要用户自定义键名的场景,如: - 有序合成的符号映射(pattern中的符号) - 自定义物品属性 - 条件判断的字段名 #### When/WD 条件显示机制 通过 `WD` 定义布尔变量,`When` 引用这些变量实现字段的条件显示: **WD 定义语法**: ```json "WD": [ { "变量名": { "id": "字段路径", "Value": "匹配值" } } ] ``` **id 字段路径语法**(向上递归查找): - `type` → 默认 `?0`,取当前层的 `type` 字段 - `type?1` → 向上1层取 `type` 字段 - `type?2` → 向上2层取 `type` 字段 - `0?1` → 向上1层到列表,取第[0]项 - `trigger?1` → 向上1层取 `trigger` 字段 **层级计数规则**:所有容器(对象 `{}` 和列表 `[]`)都算一层。 **When 正则表达式语法**: - `type` → 单变量引用 - `type1||type2` → 或逻辑 - `type1&&type2` → 与逻辑 - `!type` → 非逻辑 - `(type1||type2)&&type3` → 括号分组 **实际示例**:配方中的烧炼字段 ```json { "Key": "ingredient", "When": "type1||type2", // 当type1或type2为true时显示 "WD": [ { "type1": { "id": "type", "Value": "minecraft:smelting" } }, { "type2": { "id": "type", "Value": "minecraft:blasting" } } ] } ``` **执行逻辑**: 1. 前端监听 `type` 字段的值变化 2. 当值为 `minecraft:smelting` 时,`type1` 变为 `true` 3. 当值为 `minecraft:blasting` 时,`type2` 变为 `true` 4. `type1||type2` 为 `true` 时显示 `ingredient` 字段 #### 条件表达式(用于自动推断) 当 `Type` 列表中有多个 `From` 类型时,可以用条件表达式区分: **语法**: ``` 原子: (key) → key 字段存在 (key=type) → key 字段是特定类型(s/i/f/b/o/l) 逻辑: &&(与) ||(或) !(非) ()(分组) ``` **类型代码**: - `s` = string(字符串) - `i` = int(整数) - `f` = float(浮点) - `b` = bool(布尔) - `o` = object/dict(对象) - `l` = list/array(列表) **示例**: - `(type)` → type字段存在即可 - `(version=i)` → version字段是整数 - `(name&&!(fallback))` → name存在且fallback不存在 - `(data=o||items=l)` → data是对象或items是列表 **用途**:在导入ZIP转换为Workspace时,自动判断JSON数据应该匹配哪个类型定义。 #### 复合类型嵌套示例 **配方输出结果(recipe/result.json)**: ```json { "id": "mc_result", "parts": [ { "Key": "id", "Type": ["S:物品ID¥items/all"], "MBW": true, "Des": "输出物品的命名空间ID" }, { "Key": "count", "Type": ["I"], "Des": "输出物品数量,默认值为1" }, { "Key": "components", "Type": ["From:recipe/components/index"], "Des": "物品组件数据(如附魔、自定义名称)" } ] } ``` **嵌套关系**: - `recipe/master.json` 的 `result` 字段引用此类型 - `mc_result` 的 `components` 字段再引用 `recipe/components/index` - 形成多层嵌套的复合类型结构 **前端渲染**: - 用户填写 `result` 时,看到嵌套的字段编辑器 - `id` 字段:物品ID选择弹窗 - `count` 字段:整数输入框 - `components` 字段:展开显示更多子字段 #### 完整工作流程示例:创建钻石剑配方 以下展示从UI创建到导出的完整流程,演示数据驱动系统的实际应用: **步骤1:用户在UI树中创建配方文件** - 用户在左侧项目树中点击"原版数据包/配方" - 点击"+添加"按钮 - 输入文件名:`diamond_sword` - 前端根据 `paths/recipes.json` 的 `Type: "json:recipe/master"` 加载配方定义 **步骤2:前端加载配方定义** - `typeRegistry.loadType("recipe/master")` 加载 `data/jsonTypes/recipe/master.json` - 解析 `parts` 字段,渲染可视化编辑器 - 渲染第一个字段:`type`(配方类型) **步骤3:用户选择配方类型** - 用户点击 `type` 字段 - 前端加载 `data/choices/minecraft/recipe_types/all.json` - 弹窗显示:有序合成、无序合成、熔炼、高炉冶炼... - 用户选择:"有序合成" - 实际写入:`"type": "minecraft:crafting_shaped"` **步骤4:前端动态显示字段** - 监听 `type` 字段变化,触发 `WD` 条件检查 - 检测到 `type` 的值为 `minecraft:crafting_shaped` - WD中的 `type` 变量变为 `true` - 显示 `pattern` 和 `key` 字段(When条件匹配) **步骤5:用户填写pattern** - `pattern` 字段引用 `From:core/stringlist` - 加载 `data/jsonTypes/core/stringlist.json`(TIL类型) - 前端渲染为列表编辑器,显示"+添加"按钮 - 用户添加3行: - `###` - `#D#` - `###` - 实际写入:`"pattern": ["###", "#D#", "###"]` **步骤6:用户填写key(符号映射)** - `key` 字段引用 `From:recipe/shaped_ingredients` - 加载 `data/jsonTypes/recipe/shaped_ingredients.json` - 该字段 `Key` 为 `undefined`,用户自定义键名 - 前端显示"添加符号"按钮 - 用户点击,输入符号:`#` - 选择类型:`S:单个物品¥items/all` - 弹窗加载物品列表,选择:"钻石" - 实际写入:`"undefined_symbol_#": "minecraft:diamond"` - 用户继续添加符号 `D`,选择物品"钻石" - 实际写入:`"undefined_symbol_D": "minecraft:diamond"` **步骤7:用户填写result** - `result` 字段引用 `From:recipe/result` - 加载 `data/jsonTypes/recipe/result.json` - 嵌套显示 `id`、`count`、`components` 字段 - 用户选择物品:"钻石剑" - 用户输入数量:1 - 实际写入: ```json "result": { "id": "minecraft:diamond_sword", "count": 1 } ``` **步骤8:Workspace存储格式** 最终Workspace中存储的数据(包含复合标签): ```json { "_fileMeta": { "uiPath": "原版数据包/配方", "typeIndex": 0, "filenameId": "diamond_sword" }, "_d": { "type": { "_t": 0, "_d": "minecraft:crafting_shaped" }, "pattern": { "_t": 0, "_d": [ { "_t": 0, "_d": "###" }, { "_t": 0, "_d": "#D#" }, { "_t": 0, "_d": "###" } ] }, "undefined_symbol_#": { "_t": 0, "_d": "minecraft:diamond" }, "undefined_symbol_D": { "_t": 0, "_d": "minecraft:diamond" }, "result": { "_t": 0, "_d": { "id": { "_t": 0, "_d": "minecraft:diamond_sword" }, "count": { "_t": 0, "_d": 1 } } } } } ``` **步骤9:导出ZIP** - 用户点击"导出ZIP" - 前端遍历所有Workspace数据 - 对每个配方文件: - 移除 `_t` 和 `_d` 包装,还原纯JSON - 还原 `undefined_symbol_X` 为 `X` - 根据命名空间和 `paths/recipes.json` 的 `save_path` 计算路径 - 最终生成ZIP文件: ``` datapack.zip └── datapack/ └── data/ └── mymod/ └── recipe/ └── diamond_sword.json ``` **最终导出的diamond_sword.json**: ```json { "type": "minecraft:crafting_shaped", "pattern": ["###", "#D#", "###"], "key": { "#": "minecraft:diamond", "D": "minecraft:diamond" }, "result": { "id": "minecraft:diamond_sword", "count": 1 } } ``` 这个完整示例展示了数据驱动系统如何: - 通过 `jsonTypes` 定义字段结构和验证规则 - 通过 `choices` 提供友好的选择界面 - 通过 `WD/When` 实现动态字段显示 - 通过 `From` 实现类型引用和嵌套 - 通过 `undefined` 实现用户自定义键名 - 最终导出符合Minecraft标准的JSON文件 ### choices/\*.json(选择列表) 位于 `data/choices/`,为 `S:描述¥路径` 类型提供下拉选项。支持三种选项格式,提供友好的用户选择界面。 #### 选项格式 choices文件支持三种不同的选项格式,根据字段类型自动识别: **1. 普通选项**:用户选择后写入 `RN` 值 ```json { "RN": "minecraft:crafting_shaped", "SN": "有序合成" } ``` **2. 带图片选项**:显示物品/方块图标 ```json { "RN": "minecraft:apple", "SN": { "text": "苹果", "img": "textures/item/apple" } } ``` **3. 子菜单选项**:跳转到下级分类菜单 ```json { "to": "items/normal", "SN": "普通物品" } ``` | 字段 | 说明 | |------|------| | `RN` | 实际写入JSON的真实值(Real Name) | | `SN` | 选择弹窗显示的友好名称 | | `SN.text` | 带图片选项的显示文本 | | `SN.img` | 图片路径(相对于 `data/assets/`,不带 `.png` 后缀) | | `to` | 子菜单跳转路径(指向另一个choices文件) | #### 完整示例:配方类型选择列表 **文件路径**:`data/choices/minecraft/recipe_types/all.json` ```json [ { "SN": "有序合成", "RN": "minecraft:crafting_shaped" }, { "SN": "无序合成", "RN": "minecraft:crafting_shapeless" }, { "SN": "熔炼", "RN": "minecraft:smelting" }, { "SN": "高炉冶炼", "RN": "minecraft:blasting" } ] ``` #### 完整示例:物品分类选择(带子菜单) **文件路径**:`data/choices/items/all.json` ```json [ { "to": "items/normal", "SN": "普通物品" }, { "to": "items/blocks", "SN": "方块物品" } ] ``` 用户选择"普通物品"后,弹窗跳转到 `items/normal.json`,显示具体的物品列表。 #### 完整示例:物品选择列表(带图片) **文件路径**:`data/choices/items/normal.json` ```json [ { "RN": "minecraft:music_disc_precipice.desc", "SN": { "text": "Aaron Cherof - Precipice", "img": "textures/item/music_disc_precipite.desc" } }, { "RN": "minecraft:apple", "SN": { "text": "苹果", "img": "textures/item/apple" } } ] ``` #### 前端交互流程 **普通选项流程**: 1. 用户在配方编辑器中点击"配方类型"字段 2. 前端根据 `¥minecraft/recipe_types/all` 路径加载此JSON文件 3. 弹窗显示选项列表:"有序合成"、"无序合成"、"熔炼"等 4. 用户选择"有序合成" 5. 实际写入JSON:`"type": "minecraft:crafting_shaped"` **子菜单流程**: 1. 用户点击"物品"字段,加载 `¥items/all` 2. 弹窗显示分类:"普通物品"、"方块物品"、"工具"等 3. 用户选择"普通物品" 4. 弹窗跳转到 `items/normal.json`,显示具体物品列表 5. 用户选择"苹果" 6. 实际写入JSON:`"id": "minecraft:apple"` **带图片流程**: 1. 用户选择物品时,弹窗显示图标+名称 2. 图片从 `data/assets/textures/item/apple.png` 加载 3. 如果图片加载失败,退化为纯文字显示 4. 用户看到图标和名称,更容易识别物品 #### 常用选择列表路径 | 路径 | 内容 | 格式 | |------|------|------| | `items/all` | 物品分类 | 子菜单(跳转到子目录) | | `items/normal` | 普通物品列表 | 带图片选项 | | `items/blocks` | 方块物品列表 | 带图片选项 | | `items/tagsall` | 物品标签列表 | 普通选项 | | `blocks/all` | 方块分类 | 子菜单 | | `minecraft/recipe_types/all` | 配方类型 | 普通选项 | | `minecraft/enchantments/all` | 附魔类型 | 带图片选项 | #### 组织结构 选择列表按照逻辑分类组织在子目录中: ``` data/choices/ ├── items/ # 物品相关 │ ├── all.json # 物品分类(子菜单) │ ├── normal.json # 普通物品列表(带图片) │ ├── blocks.json # 方块物品列表(带图片) │ └── tagsall.json # 物品标签列表(普通) ├── blocks/ # 方块相关 │ ├── all.json # 方块分类(子菜单) │ └── normal.json # 普通方块列表(带图片) ├── minecraft/ # Minecraft官方类型 │ ├── recipe_types/ # 配方类型 │ │ └── all.json # 普通选项 │ └── enchantments/ # 附魔类型 │ └── all.json # 带图片选项 ├── datapackmore/ # DatapackMore扩展类型 │ ├── item_types/ │ │ └── all.json # 自定义物品类型 │ └── block_model_parent.json # 方块模型父类 └── core/ # 核心类型 ├── click_event_actions.json # 点击事件类型 └── text_colors.json # 文本颜色 ``` ### commandTypes/\*.xml(命令语法) 位于 `data/commandTypes/`,定义 Minecraft 命令的语法树,用于命令文本编辑器的语法补全和高亮,以及图形化编辑器积木渲染。 **支持换行**:template 字段可以换行书写,解析时自动去除换行和缩进。 ```xml 0x38a112 0x55FF55 ``` **命令树语法:** | 语法 | 含义 | |------|------| | `字面量` | 按原样输入,可作为 Tab 补全候选 | | `字面量#中文#` | 带中文描述,图形化编辑器显示中文 | | `<参数>` | 用户输入值替换,不可 Tab 补全(仅提示) | | `<参数#中文#>` | 带中文描述,图形化编辑器显示中文 | | `[项]` | 可选 | | `(A\|B\|C)` | 必选其一 | | `[A\|B\|C]` | 可选其一 | | `[A\|B@...]` | 无限重复:可选其一,可重复任意次数 | | `(A\|B@...)` | 无限重复:必选其一,可重复任意次数 | | 空格 | token 分隔符 | **color 节点:** - `literal`:字面量颜色(`0xRRGGBB` 或 `#RRGGBB`) - `argument`:参数颜色 **@... 无限重复示例:** - `/execute` 命令的 `as/at/in/if/unless` 可以重复任意次,用 `[as |at |in @...]` 表示 - 文本编辑器会根据用户输入判断是否还在重复项中,直到输入 `run` 或其他命令 - 图形化编辑器会显示"+ 添加"按钮,允许用户添加多个重复项 **运行原理:** 1. `CommandTypesRegistry` 通过 `manifest.json` 或默认列表加载所有 XML 2. `CommandTreeParser` 用递归下降解析器将 template 解析为语法树(sequence/choice/optional/argument/literal/repeatable 节点) 3. `CommandMatcher.getCandidates(tree, tokens)` 基于已输入 token 序列返回当前候选,支持 repeatable 节点循环匹配 4. 编辑器实时匹配并弹出补全列表,Tab 补全字面量,参数仅提示 5. 图形化编辑器 `BlocklyEditor` 将语法树渲染为积木块,repeatable 节点显示为可添加列表 --- ## 工作区格式(Workspace V2) 项目以专用文件格式保存,便于识别和管理: **文件类型**: - **项目文件**:`.dpmtproject` 后缀,存储是成品zip - **工作区文件**:`.dpmtworkfile` 后缀,是供工具读取的json,有每项的类型等数据,加载更快更稳定 **文件命名规则**: - 项目文件:`{项目名}.dpmtproject` - 工作区文件:`{项目名}_workspace.dpmtworkfile` 格式详见 `WORKSPACE_V2.md`,核心结构: ```json { "version": "2.0", "metadata": { "exportTime": "2026-05-10T15:00:00Z", "datapackMoreVersion": "0.2.0", "projectName": "我的数据包" }, "namespaces": { "mymod": { "_datadriven": { "原版数据包/配方": [ { "_fileMeta": { "uiPath": "原版数据包/配方", "typeIndex": 0, "filenameId": "cake" }, "_d": { "type": { "_t": 0, "_d": "minecraft:crafting_shaped" }, "pattern": { "_t": 0, "_d": ["###", "#E#", "###"] } } } ] } } } } ``` **复合标签**:所有值统一为 `{"_t": typeIndex, "_d": actualData}` 格式 - `_t`:在 `Type` 数组中的索引(单类型字段固定为 0) - `_d`:实际数据值 - 嵌套对象/列表的每个元素也都是复合标签 **不定键名**:用户自定义键存储为 `undefined_{partId}_{userKey}`,导入时逆向还原。 **文件级多类型**:通过 `_fileMeta.typeIndex` 记录该文件在 `Types[]` 中选择的类型索引。 --- ## 导出原理 DPMT支持三种导出方式,分别用于不同的使用场景: ### 1. 导出数据包ZIP(用于Minecraft) 导出完全在前端完成(`main.js: _generateZipBlob`),无需后端往返: 1. 创建 `JSZip` 实例 2. 根据 `tool_data.export_root` 创建根目录(如 `datapack/`),所有文件加到此目录下 3. 遍历项目的每个命名空间: - **传统类型**(`tool_data.types`):用 `base_path` 模板替换 `{namespace}` 生成路径,模板内容替换 `{id}` 后写入 - **数据驱动类型**(`_datadriven`): - `mcfunction` → 原文写入 `save_path/filename.mcfunction` - `png` → base64 解码后写入 `save_path/filename.png` - `json:path` → 加载 `data/jsonTypes/path.json` 格式定义,递归生成 JSON 内容(解开复合标签,解析 `From:` 引用,处理 `undefined_` 不定键名),写入 `save_path/filename.json` 4. `zip.generateAsync({ type: 'blob', compression: 'DEFLATE' })` 生成 Blob 5. 通过 `/api/save-dialog` 让用户选择保存路径,后端写入磁盘 6. `lastPaths.exportZip` 记录上次路径 **导出 ZIP 结构示例**(`export_root = "datapack"`): ``` datapack/ ← export_root 包裹层 ├── datapack/ │ ├── data/ │ │ └── mymod/ │ │ ├── recipe/cake.json │ │ ├── function/main.mcfunction │ │ └── advancement/root.json │ └── pack.mcmeta └── resourcepack/ └── assets/ └── mymod/ ``` ### 2. 导出工作区文件(用于保存项目) 导出工作区文件用于保存整个项目,方便后续继续编辑: **文件格式**:`.dpmtworkfile` **命名规则**:`{项目名}_workspace.dpmtworkfile` **导出流程**(`main.js: _exportProject`): 1. 收集当前项目的所有Workspace数据(包含 `_t` `_d` 复合标签) 2. 添加元数据(导出时间、项目名、版本等) 3. 生成JSON格式的工作区数据 4. 通过 `/api/save-dialog` 让用户选择保存路径 5. 前端生成 `Blob` 并触发下载 6. 文件保存为 `{项目名}_workspace.dpmtworkfile` **用途**: - 保存完整的项目状态 - 跨设备分享项目 - 后续导入继续编辑 ### 3. 导出项目文件(轻量级保存) 导出项目文件仅存储基本信息,不含实际文件内容: **文件格式**:`.dpmtproject` **命名规则**:`{项目名}.dpmtproject` **用途**: - 快速创建项目引用 - 项目模板分享 - 轻量级项目记录 --- ## 导入原理 DPMT支持三种导入方式,分别对应不同的数据来源: ### 1. 导入工作区文件(`.dpmtworkfile`) 导入工作区文件用于恢复之前保存的完整项目状态: **文件格式识别**:前端通过 `` 限制只能选择工作区文件 **导入流程**(`main.js: _importProject`): 1. 用户选择 `.dpmtworkfile` 文件 2. 前端读取文件内容(JSON格式的Workspace数据) 3. 解析 `metadata`、`namespaces` 等字段 4. 将数据直接加载到当前项目中 5. 恢复所有文件的状态(包含 `_t` `_d` 复合标签) 6. 渲染项目树和编辑器 **用途**: - 恢复之前保存的项目 - 导入他人分享的项目 - 跨设备同步项目 ### 2. 导入项目文件(`.dpmtproject`) 导入项目文件仅恢复基本信息,不含实际文件内容: **文件格式**:`.dpmtproject` **导入流程**: 1. 用户选择 `.dpmtproject` 文件 2. 前端读取项目基本信息(名称、创建时间等) 3. 创建空项目或加载项目框架 4. 需要后续添加具体文件内容 **用途**: - 快速创建项目模板 - 项目引用恢复 ### 3. 导入数据包ZIP(用于从现有数据包创建项目) 将现有Minecraft数据包ZIP转换为Workspace项目: **导入流程**(`/api/import-zip`): 1. 前端 FormData 上传 ZIP 到后端 2. 后端 `_parse_multipart_zip` 手动解析 multipart/form-data(**不依赖已移除的 `cgi` 模块**),提取 `zipfile` 字段 3. `ZipToWorkspaceConverter.convert_zip_to_workspace`: - 解压到临时目录 - 递归遍历 `.json`/`.mcmeta` 文件 - `_normalize_zip_path` 规范化路径(处理 `datapack/datapack/` 嵌套前缀,兼容 `data/` 无前缀) - `_match_ui_definition` 用 `save_path` 模板构建正则反推 UI 定义和命名空间 - 多候选时根据 JSON 内容(对象/列表、字段条件)区分 - `_reverse_convert` 将原版 JSON 逆向转换为 Workspace 复合标签格式 4. 返回 Workspace JSON,前端预览后可下载或直接载入 **用途**: - 从现有数据包创建可视化编辑项目 - 批量转换原版JSON文件 - 逆向工程现有数据包结构 ### 项目 ZIP 提取(前端 JSZip) `doExtractDatapackZip` / `doExtractResourcepackZip` 在前端用 JSZip 处理: - 识别 `datapack/datapack/` 前缀(即 `export_root` 包裹层),提取内层 - 重新打包为只含 `datapack/` 或 `resourcepack/` 的 ZIP,供直接放入游戏目录 --- ## API 接口列表 ### GET | 路径 | 说明 | |------|------| | `/api/tool-data` | 工具配置 | | `/api/datadriven-config` | 合并的数据驱动配置(ui + paths) | | `/api/datadriven-ui` | UI 配置(向后兼容) | | `/api/datadriven-paths` | 路径配置(向后兼容) | | `/api/commands` | 命令定义(旧格式) | | `/api/versions` | 可用数据格式版本列表 | | `/api/current-version` | 当前选中版本 | | `/api/game-versions` | 扫描到的游戏版本 | | `/api/saves?version=...` | 指定游戏的存档列表 | | `/api/datapacks?save=...` | 指定存档的 datapacks 列表 | | `/api/list-dirs?path=...&include_files=0/1` | 目录浏览(前端文件浏览器用) | | `/api/list-mods` | mods 目录下的 JAR 列表 | | `/api/export-mod-status` | 导出 Mod 功能是否可用 | | `/api/languages` | 可用语言列表 | | `/api/languages/current` | 当前语言包数据 | | `/api/settings/{filename}` | 读取设置文件 | | `/data/{path}` | 静态访问 `data/` 目录下文件 | | `/api/launch?...` | 启动游戏 | | `/api/launch-progress?...` | 启动进度查询 | | `/api/debug-logs?...` | 调试日志 SSE 流 | | `/api/startup-reminders` | 获取启动提醒设置 | | `/api/extract-textures` | 提取Minecraft版本纹理(POST) | ### POST | 路径 | 说明 | |------|------| | `/api/import` | 导入空项目模板 | | `/api/import-zip` | 上传 ZIP 转 Workspace | | `/api/import-datapack` | 导入 datapack ZIP 到存档 | | `/api/import-resourcepack` | 导入资源包到游戏 | | `/api/select-version` | 选择数据格式版本 | | `/api/force-update` | 强制重新同步版本数据 | | `/api/datapack/delete` | 删除存档中的 datapack | | `/api/export-mod-jar` | 导出 Mod JAR | | `/api/add-mod` | 添加 JAR 到 mods 目录 | | `/api/delete-mod` | 删除 mods 中的 JAR | | `/api/save-dialog` | 保存文件到前端选定路径 | | `/api/tool-data/last-path` | 更新某弹窗的上次路径 | | `/api/install-version-package` | 上传 ZIP 安装版本包 | | `/api/delete-version` | 删除版本 | | `/api/set-launch-mode` | 设置启动模式(in_app) | | `/api/set-shortcuts` | 保存图片编辑器快捷键 | | `/api/log/frontend` | 前端日志上报 | | `/api/language` | 设置语言 | | `/api/language/refresh` | 刷新语言包 | | `/api/settings/{filename}` | 保存设置文件 | | `/api/startup-reminders` | 更新启动提醒设置 | | `/api/extract-textures` | 提取Minecraft版本纹理 | --- ## 配置文件汇总 | 文件/目录 | 作用 | 可手改 | |-----------|------|--------| | `userdata/tool_data.json` | 工具全局配置 | 是 | | `userdata/settings/version.json` | 选中的数据格式版本 ID | 通过 UI 修改 | | `userdata/settings/language.json` | 选中的语言 | 通过 UI 修改 | | `version/{id}/pack.dpmtmeta` | 版本元数据 | 是 | | `version/{id}/data/` | 版本的数据定义源 | 是(开发时编辑这里) | | `data/` | 运行时数据(自动同步) | 否(会被覆盖) | | `data/tempid.json` | 当前版本 ID 缓存 | 否 | | `frontend/js/lib/jszip-LICENSE.txt` | JSZip 开源协议 | 需随库保留 | --- ## 许可证 本项目使用 JSZip(MIT/GPLv3 双协议),其 LICENSE 文件保留于 `frontend/js/lib/jszip-LICENSE.txt`。 ## 依赖库 - **pywebview** — 应用窗口模式(`in_app: true` 时启用),用于创建桌面窗口加载前端 - **标准库** — HTTP 服务器 (`http.server` + `socketserver`)、日志、ZIP 处理、PNG 解析等均使用 Python 标准库,无第三方依赖 > 注意:启动画面使用 ctypes + struct + zlib 纯标准库实现,不依赖 PIL/tkinter。