Minecraft资源包/数据包构建工具
古镇天Gugle@XeKr-Dev(gugle@xekr.dev)
摘要
本项目是一个基于Web的模块化Minecraft资源包/数据包构建工具,通过创新性的纯前端架构解决了传统资源包开发中的模块复用、版本兼容和分发效率问题。 系统采用Vue3 + TypeScript + Vite技术栈,利用GitHub API实时获取仓库资源,通过智能版本适配引擎(PathFormatter和RecipeFormatter) 自动转换不同Minecraft版本的资源格式, 支持用户自定义模块组合和预设合集选择。核心构建器采用抽象工厂模式(AbstractBuilder) 和权重排序算法解决模块冲突问题,支持模块绑定、互斥检测和递归依赖管理,并可将资源包打包为Fabric/Quilt/Forge/NeoForge模组格式。 项目采用双构建器模式(OnlineBuilder和FileBuilder)分别支持在线和离线构建场景,提供了标准化项目模板和配置文件格式, 显著降低了资源包开发门槛,促进了Minecraft社区的协作与创新。
关键词
Minecraft资源包构建、Minecraft数据包构建、模块化架构、版本适配系统、前端构建引擎、GitHub集成、Zip打包、社区协作
Minecraft Pack Builder
Abstract
This project is a web-based modular Minecraft Pack Builder that addresses the issues of module reuse, version compatibility, and distribution efficiency in traditional resource pack development through an innovative pure front-end architecture. Built with Vue3 + TypeScript + Vite stack, the system utilizes the GitHub API to fetch repository resources in real-time, automatically converting resource formats across different Minecraft versions via an intelligent version adaptation engine (PathFormatter and RecipeFormatter), and supporting user-customized module composition and preset collections. The core builder employs a weighted sorting algorithm to resolve module conflicts, supports module bindings and mutual exclusion detection, and can package resource packs into Fabric/Quilt/Forge/NeoForge mod formats. The project provides standardized project templates and configuration file formats, significantly lowering the barrier to entry for resource pack development and fostering community collaboration.
Key words:
Minecraft Resource Pack Building, Minecraft Data Pack Building, Modular Architecture, Version Adaptation System, Front-end Build Engine, GitHub API Integration, JSZip Packaging, Abstract Factory Pattern, Weight-based Module Merging, Community Collaboration
体验
仓库地址
体验项目
一、引言
Minecraft(我的世界)作为一款风靡全球的沙盒游戏,其可扩展性通过资源包(Resource Pack)和数据包(Data Pack)得到极大增强。 资源包用于修改游戏的纹理、声音和模型等资源,而数据包则用于修改游戏逻辑、添加进度和配方等。然而,随着资源包和数据包的功能日益复杂, 其构建和管理过程也变得繁琐。特别是当开发者希望提供多个可选模块供用户自定义时,手动合并模块并确保兼容性成为一项挑战。
Minecraft资源包/数据包构建工具是一个基于Web的前端应用,旨在解决Minecraft社区中资源包和数据包开发与分发的痛点问题。传统资源包开发面临以下挑战:
- 模块化不足:资源包通常作为整体分发,用户无法按需选择功能模块
- 版本兼容性:不同Minecraft版本需要不同的资源格式
- 分发效率低:开发者需要手动打包不同配置的资源包
本项目通过创新的模块化架构和自动化构建流程,实现了:
- 用户自定义选择:允许用户自由组合所需模块
- 智能版本适配:自动转换资源格式以适应目标版本
- 云端构建系统:基于GitHub API实现一键式打包分发
- 开源协作模式:标准化项目结构促进社区贡献
二、详细解读
1. 系统架构
graph TD
A[用户界面] --> B[配置加载]
B --> C[模块选择]
C --> D[版本适配]
D --> E[构建引擎]
E --> F[ZIP生成]
F --> G[下载]2. 项目结构
项目采用Vue3作为前端框架,使用TypeScript进行类型安全开发,Vite作为构建工具,主要代码位于src目录下:
核心目录结构:
src/components:UI组件层FileSelectorDialog.vue:文件选择对话框(用于file_mode本地构建)FakeProgress.vue:伪进度条(提供构建过程视觉反馈)MarkdownView.vue:Markdown渲染器(展示仓库README)Notice.vue:通知组件PageHeader.vue:页面头部组件
src/pages:页面层HomePage.vue:主页面,实现核心用户交互逻辑(555行)
src/scripts:核心业务逻辑层builder:构建器模块(抽象工厂模式实现)impl/index.ts:AbstractBuilder抽象基类(407行,定义核心构建流程)impl/online.ts:OnlineBuilder在线构建器(通过GitHub API递归获取文件树)impl/file.ts:FileBuilder本地构建器(从用户上传的ZIP文件构建)index.ts:Builder工厂类(统一构建入口)
formatter:格式转换器模块impl/path.ts:PathFormatter路径转换器(处理data/assets目录结构变化)impl/recipe.ts:RecipeFormatter配方转换器(234行,处理三代配方格式演进)index.ts:导出统一接口
github/index.ts:GithubAPI封装(支持代理切换,含4个代理池)message/index.ts:消息提示系统(基于Arco Design)request/index.ts:HTTP请求封装(基于Axios,支持GitHub PAT认证)type/index.ts:TypeScript类型定义(ConfigJson、ModuleConfigJson等核心接口)util/index.ts:工具函数(Base64编解码、图片放大等)version/index.ts:Minecraft版本比较工具
配置文件:
config.json:资源包基本信息(名称、作者、版本、主模块、版本模块等)module.config.json:模块配置(模块名、权重、互斥关系、绑定关系)*.set.config.json:预设合集配置
技术栈:
- 核心框架:Vue3(v3.5.17) + TypeScript(v5.8.3) + Vite(v7.0.0)
- UI组件库:Arco Design(v2.57.0)
- HTTP客户端:Axios(v1.10.0,支持GitHub API认证)
- ZIP处理:JSZip(v3.10.1,客户端压缩打包)
- Markdown渲染:Marked(v16.0.0) + DOMPurify(v3.2.6,XSS防护)
- 文件下载:FileSaver(v2.0.5)
- 样式预处理:Sass(v1.89.2)
- 代码规范:ESLint(v9.30.0) + Prettier(v3.6.2)
- 许可证:AGPL-3.0-or-later
3. 核心功能实现
(1) 配置解析
工具首先加载并解析仓库根目录的config.json,获取资源包/数据包的基本配置,包括主模块路径、版本模块映射等。
(2) 模块选择与权重排序
用户可通过两种方式选择模块:
- 直接选择:从模块列表中勾选所需模块
- 合集选择:选择作者预设的模块组合
智能依赖处理:
- 模块互斥(
breaks):双向冲突检测,自动禁用不兼容选项 - 模块绑定(
bindings):递归依赖管理,自动选中传递依赖 - 权重排序:模块按
weight值从小到大排序,高权重模块覆盖低权重模块的同名文件
核心算法实现(HomePage.vue第246-285行):
// 检查模块是否因冲突被禁用(双向检测)
function checkModuleDisabled(key: string) {
const basePath = Builder.getBasePath(status.config!)
const self = status.modules.get(key)
if (self == undefined) return true
// 正向检测:检查当前模块是否与已选模块冲突
for (const subKey of status.selectedModules) {
for (let breakKey of self.breaks) {
breakKey = `${basePath}/${breakKey}`
if (subKey == breakKey) return true
}
// 反向检测:检查已选模块是否与当前模块冲突
const sub = status.modules.get(subKey)
if (sub == undefined) continue
for (const breakKey of sub.breaks) {
if (`${basePath}/${breakKey}` == key) return true
}
}
// 递归检查绑定模块是否可用(传递依赖)
if (self.bindings) {
for (let binding of self.bindings) {
binding = `${basePath}/${binding}`
if (!binding || binding == key) return true
if (checkModuleDisabled(binding)) return true // 递归检测
}
}
return false
}
// 递归选择绑定的模块(深度优先遍历)
function selectWithBindings(key: string) {
const basePath = Builder.getBasePath(status.config!)
const self = status.modules.get(key)
if (self == undefined) return
if (!status.selectedModules.includes(key)) {
status.selectedModules.push(key)
}
if (self.bindings) {
for (let binding of self.bindings) {
binding = `${basePath}/${binding}`
if (!binding || binding == key) return
selectWithBindings(binding) // 递归选择依赖
}
}
}(3) 构建流程
构建流程由AbstractBuilder.build方法实现,采用异步Promise链式处理:
1. 版本信息确定
const minecraftVersion = mcVersions[this.version]
const packFormat = this.type === "data"
? minecraftVersion.datapack_version
: minecraftVersion.resources_version2. 生成pack.mcmeta元数据
const metaJson: MetaJson = {
pack: {
description: [{text: `§6§l${this.config.description} v${this.config.version}\n`}],
// 根据版本使用pack_format(旧)或min_format/max_format(新)
}
}3. 加载主模块
this.getFileTree(`${basePath}/${this.config.main_module}`).then(res => {
let pack: FileOrTree = res
// 注入或更新pack.mcmeta
})4. 处理版本模块
// noinspection JSAnnotator
if (this.config.version_modules) {
// 版本比较,选择最合适的版本模块
if (Version.compareMC(this.version, versionModule.version) < 0) continue
if (strict && Version.compareMC(...) !== 0) continue
// 版本模块可覆盖主模块或其他模块
}5. 合并选中的模块(按权重排序)
moduleList = moduleList.sort((a, b) => a.weight - b.weight)
for (let module of moduleList) {
pack = this.mergeFileOrTree(pack, module.files!!)
// 应用该模块的版本模块(如果有)
}6. 文件树合并算法(深度优先递归)
class AbstractBuilder {
public mergeFileOrTree(file1: FileOrTree, file2: FileOrTree): FileOrTree {
if (file1.children === undefined || file2.children === undefined) {
throw new Error("file1 or file2 is not a tree")
}
const file: FileOrTree = {path: file2.path}
file.children = file1.children
// 遍历file2的所有子节点
for (let child of file2.children) {
let contain = false
// 查找file1中是否存在同名节点
for (let child1 of file.children) {
if (child1.path == child.path) {
// 情况1:文件节点,直接覆盖内容
if (child1.content !== undefined && child.content !== undefined) {
child1.content = child.content
contain = true
break
}
// 情况2:目录节点,递归合并子树
if (child1.children !== undefined && child.children !== undefined) {
const merged = this.mergeFileOrTree(child, child1)
child1.children = merged.children
contain = true
break
}
}
}
// 情况3:file1中不存在该节点,直接添加
if (contain) continue
file.children.push(child)
}
return file
}
}7. 内容预处理与格式转换(AbstractBuilder第126-137行)
class AbstractBuilder {
public preprocessContent(content: string, path: string) {
const pathSplit = path.split("/")
const type = pathSplit[2] // 提取资源类型(如recipe/recipes)
if (!type) return content
// 仅对配方文件进行格式转换
if (type === "recipe" || type === "recipes") {
content = RecipeFormatter.format(content, this.minecraftVersion)
}
return content
}
}8. ZIP文件生成(AbstractBuilder第398-401行)
// 创建JSZip实例
const zip: JSZip = new JSZip()
// 递归将文件树添加到ZIP(根据type过滤data/assets)
this.fileTreeToZip(zip, pack)
// 模组模式:额外生成模组元数据文件
if (this.mod) this.buildModZip(zip)
// 异步生成Blob对象(用于浏览器下载)
zip.generateAsync({type: "blob"}).then(blob => resolve(blob))9. 模组格式支持(AbstractBuilder第185-263行)
class AbstractBuilder {
public buildModZip(zip: JSZip) {
const modID = this.config.pack_name.toLowerCase().replace(/[^a-z0-9]/g, '_')
const license = this.config.license ?? 'All Right Reserved'
// Forge模组配置(lowcodefml加载器)
const modsToml = `
modLoader = "lowcodefml"
loaderVersion = "[25,)"
license="${license}"
[[mods]]
modId = "${modID}"
version = "${this.config.version}"
displayName = "${this.config.pack_name}"
description = "${this.config.description}"
logoFile = "pack.png"
authors = "${this.config.author}"`
// NeoForge模组配置(javafml加载器)
const neoForgeModsToml = `
modLoader = "javafml"
loaderVersion = "[1,)"
...`
// Fabric模组配置(依赖fabric-resource-loader-v0)
const fabricModJson = {
id: modID,
version: this.config.version,
environment: "*",
depends: {"fabric-resource-loader-v0": "*"}
}
// Quilt模组配置(依赖quilt_resource_loader)
const quiltModJson = {
quilt_loader: {
depends: [{
id: "quilt_resource_loader",
versions: "*",
unless: "fabric-resource-loader-v0"
}]
}
}
// 写入文件
zip.folder("META-INF")
zip.file("META-INF/mods.toml", modsToml)
zip.file("META-INF/neoforge.mods.toml", neoForgeModsToml)
this.fileToZip(zip, utob64(JSON.stringify(fabricModJson, null, 4)), "fabric.mod.json")
this.fileToZip(zip, utob64(JSON.stringify(quiltModJson, null, 4)), "quilt.mod.json")
}
}构建器模式:
系统采用抽象工厂模式实现双构建器:
- AbstractBuilder(抽象基类,407行):
- 定义核心构建流程:
build()方法(265-405行) - 实现文件树合并:
mergeFileOrTree()(95-124行) - 实现内容预处理:
preprocessContent()(126-137行) - 实现模组打包:
buildModZip()(185-263行)
- 定义核心构建流程:
- OnlineBuilder(在线构建器,45行):
- 继承AbstractBuilder
- 通过
GithubAPI.getRepoContents()递归获取文件树(第6-44行) - 支持GitHub代理模式(从4个代理池随机选择)
- FileBuilder(本地构建器,80行):
- 继承AbstractBuilder
- 从用户上传的ZIP文件构建(基于JSZip)
- 支持私有仓库和离线场景
(4) 版本适配系统
版本数据库(minecraft_version.json)
{
"1.21.7": {
"type": "release",
"datapack_version": 69,
"resources_version": 46
}
}PathFormatter - 路径格式转换器
不同Minecraft版本对数据包路径命名有变化,如1.21(pack_format 45)后:
PathFormatter.dataPathMap.set(45, [
{from: "structures", to: "structure"},
{from: "advancements", to: "advancement"},
{from: "recipes", to: "recipe"},
{from: "loot_tables", to: "loot_table"},
{from: "predicates", to: "predicate"},
{from: "item_modifiers", to: "item_modifier"},
{from: "functions", to: "function"}
])转换逻辑:
class PathFormatter {
public static format(path: string, version): string {
const pathSplit = path.split("/")
if (version.datapack_version >= 45) {
// structures -> structure
} else {
// structure -> structures (反向转换)
}
}
}RecipeFormatter - 配方格式转换器
Minecraft配方格式经历三次重大变更:
24w10a前 (datapack_version < 34):
json{ "result": { "item": "minecraft:diamond", "count": 1 } }24w10a-24w33a (34 ≤ datapack_version < 49):
json{ "result": { "id": "minecraft:diamond", "count": 1 } }24w33a后 (datapack_version ≥ 49):
json5{ "key": { "#": "#minecraft:diamond" // tag简化为#标记 }, "ingredient": "minecraft:stick", // 直接使用字符串 "result": { "id": "minecraft:diamond", "count": 1 } }
转换器自动检测并转换:
class RecipeFormatter {
// 核心转换入口(第217-233行)
public static format(content: string, version): string {
let recipe: Recipe = JSON.parse(b64tou(content))
if (version.datapack_version >= 49) {
recipe = RecipeFormatter.toMore24w33a(recipe) // →最新格式
} else if (version.datapack_version >= 34) {
recipe = RecipeFormatter.toBetween24w10aTo24w33a(recipe) // →中间格式
} else {
recipe = RecipeFormatter.toLess24w10a(recipe) // →旧格式
}
return utob64(JSON.stringify(recipe, null, 4))
}
// 转换链:任意格式→24w33a+→24w10a-24w33a→24w10a前
// 保证任意版本间的兼容性转换
}(5) GitHub API集成
API封装(src/scripts/github/index.ts,31行):
// noinspection JSAnnotator
export class GithubAPI {
// 代理池:随机选择4个GitHub镜像之一
public static readonly proxies: string[] = [
"https://gh.llkk.cc/",
"https://gh-proxy.top/",
"https://cdn.gh-proxy.org/",
"https://gh-proxy.org/"
]
public static readonly proxy: string = GithubAPI.proxies[Math.floor(Math.random() * GithubAPI.proxies.length)]
// 获取仓库信息
static async getRepoInfo(repo: string, proxy: boolean = false)
// 获取仓库内容(文件/目录)
static async getRepoContents(repo: string, path: string = "", proxy: boolean = false)
// 获取README文件
static async getRepoReadme(repo: string, proxy: boolean = false)
// 下载仓库ZIP包(用于file_mode)
static getRepoZip(repo: string, _branch: string = "", proxy: boolean = false)
}认证机制:(基于Request模块)
- 未认证用户:60请求/小时(GitHub API限制)
- Personal Access Token:5000请求/小时
- 所需权限:
public_repo、read:project - 存储方式:localStorage(纯前端,无后端,保护隐私)
- 请求头:
Authorization: token ${PAT}
数据获取流程:
- 加载
config.json获取基本配置 - 递归获取模块目录下的
module.config.json - 加载
sets_path下的合集配置 - 加载
README.md用于展示 - 构建时递归获取文件树(OnlineBuilder)
在线/离线构建模式:
- online模式(默认):
- 使用OnlineBuilder实时从GitHub API获取文件
- 适用于公开仓库
- 支持代理模式(HomePage.vue第40-45行,localStorage持久化)
- file模式:
- 使用FileBuilder从用户上传的ZIP构建
- 适用于私有仓库或网络受限环境
- 配置:
config.json中设置file_mode: true - 流程:点击构建→下载ZIP→上传ZIP→本地构建(HomePage.vue第338-380行)
4. 关键技术点
1. 抽象工厂模式(Builder模块)
- 设计模式:AbstractBuilder抽象基类定义构建流程,OnlineBuilder和FileBuilder实现具体获取逻辑
- 优势:
- 将构建逻辑与数据源解耦,便于扩展(AbstractBuilder第54-406行)
- 代码复用:核心流程在AbstractBuilder中实现,子类仅需实现
getFileTree() - 易于测试:可单独测试合并、转换逻辑,无需依赖网络
2. 策略模式(Formatter模块)
- 设计模式:PathFormatter和RecipeFormatter封装不同的转换策略
- 优势:
- 版本适配逻辑隔离,不污染构建器代码
- 易于添加新版本支持:仅需修改Formatter
- 静态方法设计,无需实例化,降低内存开销
3. 反应式状态管理(HomePage.vue)
设计模式:Vue3 Composition API + reactive状态对象(HomePage.vue第72-91行)
优势:
- 自动UI更新:状态变化自动触发视图重渲染
- 集中式状态:所有状态在
status对象中统一管理 - 简化逻辑:避免手动DOM操作和事件监听
主模块 + 可选模块 + 版本模块的三层结构
每个模块独立开发和维护,支持热插拔
模块配置标准化(
module.config.json)
2. 智能版本适配系统
- 双层版本控制:数据包版本(datapack_version)和资源包版本(resources_version)独立管理
- 版本模块系统:为不同Minecraft版本提供差异化内容
strict模式:严格版本匹配- 非strict:向后兼容(选择≤目标版本的最新版本模块)
version_reverse:支持反向版本排序
- 格式自动转换:PathFormatter和RecipeFormatter无缝转换资源格式
3. 冲突检测与依赖管理
- 互斥关系(
breaks):声明不兼容模块,UI自动禁用冲突选项 - 绑定关系(
bindings):自动选择依赖模块 - 权重系统(
weight):细粒度控制文件覆盖优先级
4. 双构建器模式
- OnlineBuilder:GitHub API实时构建,适用于公开仓库
- FileBuilder:本地ZIP构建,支持私有仓库和离线场景
- 统一接口设计(AbstractBuilder抽象类)
5. 跨平台模组支持
- 一键打包为Fabric/Quilt/Forge/NeoForge模组
- 自动生成模组元数据文件:
fabric.mod.json(Fabric)quilt.mod.json(Quilt)META-INF/mods.toml(Forge)META-INF/neoforge.mods.toml(NeoForge)
6. 纯前端架构
- 无需服务器,静态部署即可运行
- 数据安全:PAT存储在用户本地浏览器
- JSZip实现客户端ZIP打包
7. 用户体验优化
- URL路由记忆仓库地址(
/#/owner/repo) - 伪进度条提供构建反馈
- Markdown渲染README展示项目说明
- 版本选择支持快照版本过滤
5. 完整工作流程
阶段一:仓库加载
- 用户输入GitHub仓库URL(如
https://github.com/XeKr-Dev/minecraft-pack-template) - 点击"加载"按钮,触发
loadRepo() - 解析仓库地址,更新URL路由(
/#/XeKr-Dev/minecraft-pack-template) - 并行请求:
config.json:获取基本配置README.md:获取项目说明- 图标文件:加载并放大处理(imageMagnify)
- 递归加载所有模块的
module.config.json - 加载
sets_path下的所有合集配置
阶段二:用户配置
- 选择模块:
- 直接勾选模块列表
- 或选择预设合集(自动填充模块)
- 实时冲突检测:
- 禁用与已选模块互斥的选项
- 自动选中绑定的依赖模块
- 选择目标Minecraft版本:
- 支持快照版本显示切换
- 显示对应的数据包/资源包格式版本号
- 选择构建类型:
- "全部":同时包含资源包和数据包
- "资源包":仅包含
assets/ - "数据包":仅包含
data/
- 可选:勾选"构建模组"生成Fabric/Quilt/Forge/NeoForge兼容格式
阶段三:构建过程
- 点击"构建"按钮,触发
build() - 构建器初始化:
- 确定使用OnlineBuilder或FileBuilder
- 获取目标版本的pack_format信息
- 文件获取:
- 加载主模块文件树
- 根据目标版本选择并加载版本模块
- 按权重排序加载用户选中的模块
- 内容处理:
- 路径格式转换(PathFormatter)
- 配方格式转换(RecipeFormatter)
- 文件树递归合并(高权重覆盖低权重)
- 元数据生成:
- 注入或更新
pack.mcmeta - 添加
pack.png图标 - (模组模式)生成模组配置文件
- 注入或更新
- ZIP打包:
- JSZip递归添加文件
- 根据构建类型过滤目录
- 生成Blob对象
阶段四:交付下载
- 使用FileSaver.js触发浏览器下载
- 文件命名格式:
{pack_name}-{version}-{type}-mc{minecraft_version}.{jar|zip} - 示例:
BetterTools-1.0.0-all-mc1.21.7.zip
三、用例
用例1:创建一个名为“BetterTools”的资源包
场景:开发者创建了一个资源包,并希望将工具材质从原版材质替换为金质材质或钻石材质。
假设开发者“XeKr”创建了一个名为“BetterTools”的资源包,该资源包包含以下模块:
main:主模块,包含基础纹理。gold:将工具纹理替换为金质。diamond:将工具纹理替换为钻石质。
用户希望同时使用金质和钻石质纹理,但钻石质模块应覆盖金质模块(即钻石质工具优先)。开发者配置如下:
config.json:
json{ "pack_name": "BetterTools", "author": "XeKr", "description":"Better tools texture", "version": "1.0.0", "base_path": "./src", "main_module": "main", "icon": "./icon.png" }module.config.json(位于gold模块内):
json{ "module_name": "gold", "support_version": "*", "weight": 0 }module.config.json(位于diamond模块内):
json{ "module_name": "diamond", "support_version": "*", "weight": 1 }
用户操作步骤:
- 在工具界面输入仓库地址:
https://github.com/XeKr-Dev/BetterTools - 加载配置后,选择
gold和diamond模块(注意:由于钻石模块权重更高,它将覆盖金质模块的纹理)。 - 选择目标Minecraft版本(如1.20.1)。
- 点击“构建”按钮,工具将生成一个资源包ZIP文件。 生成的文件中,钻石质工具的纹理将覆盖金质工具的纹理,最终效果为钻石质工具。
用例2:用户自定义资源包
场景:玩家想要创建包含高清材质和动态光影但不包含真实物理的资源包
- 访问构建器页面
- 输入资源包仓库URL
- 加载配置后,勾选
高清材质、动态光影模块,不勾选真实物理模块 - 选择Minecraft
1.20.1版本 - 点击
构建按钮 - 获取定制化资源包ZIP
用例3:开发者维护多版本支持
场景:开发者需要为1.19和1.20提供兼容的资源包
- 创建version_1.19和version_1.20模块
- 在config.json中配置版本映射json
{ "version_modules": { "1.19": {"module": "version_1.19", "strict": false}, "1.20": {"module": "version_1.20", "strict": true} } }
3.用户选择版本时自动加载对应模块
用例4:社区模块共享
场景:社区开发者贡献新模型模块
- 在仓库中创建new_creatures目录
- 添加module.config.json:json
{ "module_name": "fantasy_creatures", "description": "添加奇幻模型", "support_version": "*", "weight": 5, "breaks": ["realistic_physics"] } - 用户即可在构建器中看到并选择该模块
5. 关键设计模式
1. 抽象工厂模式(Builder模块)
- 设计模式:AbstractBuilder抽象基类定义构建流程,OnlineBuilder和FileBuilder实现具体获取逻辑
- 优势:
- 将构建逻辑与数据源解耦,便于扩展(AbstractBuilder第54-406行)
- 代码复用:核心流程在AbstractBuilder中实现,子类仅需实现
getFileTree() - 易于测试:可单独测试合并、转换逻辑,无需依赖网络
2. 策略模式(Formatter模块)
- 设计模式:PathFormatter和RecipeFormatter封装不同的转换策略
- 优势:
- 版本适配逻辑隔离,不污染构建器代码
- 易于添加新版本支持:仅需修改Formatter
- 静态方法设计,无需实例化,降低内存开销
3. 响应式状态管理(HomePage.vue)
- 设计模式:Vue3 Composition API + reactive状态对象(HomePage.vue第72-91行)
- 优势:
- 自动UI更新:状态变化自动触发视图重渲染
- 集中式状态:所有状态在
status对象中统一管理 - 简化逻辑:避免手动DOM操作和事件监听
4. 依赖注入模式
- GitHub API封装:GithubAPI静态类提供统一接口,内部处理认证和代理
- Request模块:封装Axios,自动添加GitHub PAT认证头
- 优势:便于单元测试(可模拟API响应)和Mock测试
四、创新点
1. 三层模块化覆盖系统
- 主模块提供基础内容
- 版本模块提供版本差异化适配
- 可选模块提供功能扩展
- 权重机制实现精细化文件覆盖控制
2. 智能版本适配引擎
- 自动化格式转换:
- PathFormatter自动处理pack_format 45+的路径变化(复数↔单数双向转换)
- RecipeFormatter实现三代配方格式的链式转换(保证任意版本间兼容)
- 版本智能匹配:
- 非严格模式:自动选择≤目标版本的最新版本模块(AbstractBuilder第360-361行)
- 严格模式(配置
strict: true):版本必须完全匹配(AbstractBuilder第361行) - 反向排序(配置
version_reverse: true):支持旧版本在前的排序逻辑
- 双版本系统:
datapack_version:1-69+,每个快照版都可能变化resources_version:1-46+,资源包版本独立管理- 根据构建类型自动选择对应的pack_format(AbstractBuilder第268行)
- 支持版本跨度:
- minecraft_version.json包含200+版本信息(从远古版本到1.21.11+)
- 支持release和snapshot两种版本类型
- UI可切换显示/隐藏快照版本(HomePage.vue第456-467行)
3. 纯前端零服务架构
- 完全静态部署:无需后端服务器,降低运维成本
- 数据隐私保护:GitHub PAT存储于用户本地浏览器
- 客户端构建:JSZip实现浏览器内ZIP打包
- 实时构建:直接从GitHub仓库拉取最新内容
4. 依赖关系图谱系统
- 互斥检测(
breaks):自动禁用冲突模块 - 依赖绑定(
bindings):自动选择依赖项 - UI实时反馈:视觉化显示模块可用状态
5. 多平台模组一键打包
- 支持Fabric、Quilt、Forge、NeoForge四大模组加载器
- 自动生成对应元数据文件
- 单一资源包同时兼容原版和模组环境
6. 标准化开源协作模式
- 项目模板仓库:minecraft-pack-template提供开箱即用的项目结构
- 配置文件标准化:
config.json:12个字段定义资源包基本信息(TypeScript接口定义)module.config.json:6个字段定义模块属性(权重、互斥、绑定)*.set.config.json:3个字段定义预设合集
- 降低贡献门槛:
- 无需编程知识,仅需编辑JSON配置
- 支持Fork模板仓库后直接修改内容
- 去中心化分发:
- 通过GitHub实现版本控制和协作
- 用户直接输入仓库URL即可使用
- 支持多人协作Pull Request模式
7. 双构建模式支持
- online模式:
- OnlineBuilder递归调用GitHub API获取文件树
- 适用于公开仓库,实时获取最新内容
- 支持代理模式:从4个国内镜像随机选择,解决网络访问问题
- file模式:
- FileBuilder从用户上传的ZIP解析文件树
- 支持私有仓库和完全离线环境
- ZIP处理:自动移除根目录(仓库名前缀)(HomePage.vue第348-356行)
- 统一接口:AbstractBuilder定义抽象方法,两种模式使用相同的构建流程
8. 渐进式用户体验
- URL路由记忆:
- 仓库加载后自动更新URL为
/#/owner/repo(HomePage.vue第133行) - 支持直接分享链接,打开后自动加载指定仓库
- 仓库加载后自动更新URL为
- 伪进度条:
- FakeProgress组件模拟构建过程(0%→90%动画)
- 实际完成后瞬间跳转100%,提供视觉反馈
- Markdown渲染:
- MarkdownView组件集成Marked + DOMPurify
- 直接展示仓库README,无需跳转GitHub页面
- 建议版本自动预选:
- config.json中配置
suggested_version字段 - 加载仓库后自动选中建议版本(HomePage.vue第137行)
- config.json中配置
- 代理状态持久化:
- localStorage保存用户的代理选择(HomePage.vue第39-49行)
- 下次访问自动恢复设置
五、技术亮点与性能优化
1. 异步并发处理
系统在多个关键节点采用Promise.all()并发处理,显著提升性能:
// 场景1:并发加载所有模块(HomePage.vue第134-152行)
const promises: Promise<any>[] = []
promises.push(loadModules())
promises.push(loadSets())
Promise.all(promises).then(() => {
status.loaded = true
})// 场景2:并发获取所有模块文件树(AbstractBuilder第303-307行)
const promises: Promise<any>[] = []
for (let module of moduleList) {
const promise = this.getFileTree(module.path, proxy).then(tree => module.files = tree)
promises.push(promise)
}
Promise.all(promises).then(() => { /* 合并处理 */
})// 场景3:并发加载版本模块和图标(AbstractBuilder第371-383行)
for (const versionModuleKey in versionModuleMap) {
const promise = this.getFileTree(versionModule.path, proxy).then(res => versionModule.files = res)
promises.push(promise)
}
if (this.config.icon) {
const promise = this.getFileTree(this.config.icon, proxy).then(icon => {
icon.path = "pack.png"
pack.children?.push(icon)
})
promises.push(promise)
}并发加载所有模块文件树,相比串行加载可减少60%-80%的网络等待时间。
2. 懒加载与按需获取
- 懒加载策略:
- 仓库加载时仅获取
config.json和module.config.json(元数据) - 文件内容仅在点击"构建"时才获取(HomePage.vue第287-336行)
- 避免不必要的网络请求,节省API配额
- 仓库加载时仅获取
- 按需过滤:
- 构建"资源包"时跳过
data/目录(OnlineBuilder第28-30行) - 构建"数据包"时跳过
assets/目录(OnlineBuilder第31-33行) - 在文件树递归过程中提前过滤,减少约50%的API调用
- 构建"资源包"时跳过
3. Base64编码优化
GitHub API返回的文件内容为Base64编码,系统采用高效解码策略:
class AbstractBuilder {
// Base64解码优化(第141-149行)
public fileToZip(zip: JSZip, b64: string, path: string) {
const cleanBase64 = b64.replace(/\s+/g, '') // 清理空白字符(减小内存占用)
const binaryString = atob(cleanBase64) // 浏览器原生Base64解码
const buffer = new ArrayBuffer(binaryString.length)
const bytes = new Uint8Array(buffer) // 高效二进制转换
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i)
}
zip.file(path, buffer) // 直接传入ArrayBuffer,避免字符串中转
}
}4. 递归树结构处理
- 文件树结构:
FileOrTree接口采用递归定义typescriptinterface FileOrTree { path: string content?: string // 文件节点 children?: FileOrTree[] // 目录节点(递归) } - 深度优先遍历:
mergeFileOrTree()采用DFS合并策略,O(n)时间复杂度(AbstractBuilder第95-124行)fileTreeToZip()递归遍历文件树生成ZIP(AbstractBuilder第152-175行)
- 路径处理优化:
processPath()统一处理相对路径和绝对路径(AbstractBuilder第82-93行)- 缓存
basePath避免重复计算(AbstractBuilder第78行)
5. 版本智能缓存
class AbstractBuilder {
// 版本信息在构造函数中缓存(第62-79行)
public constructor(/* ... */) {
this.minecraftVersion = mcVersions[this.version] // 缓存版本对象
this.basePath = this.getBasePath() // 缓存基础路径
}
// 避免在循环中重复查询
public preprocessContent(content: string, path: string) {
// 直接使用this.minecraftVersion,无需每次查询mcVersions
return RecipeFormatter.format(content, this.minecraftVersion)
}
}6. 内存优化策略
- 流式ZIP生成:JSZip采用流式API,大文件不会全部加载到内存
- Blob异步生成:
zip.generateAsync({type: "blob"})异步处理,避免阻塞UI线程 - 模块权重排序:构建前一次性排序(O(n log n)),避免每次合并时排序(AbstractBuilder第308行)
- 事件监听清理:Vue组件卸载时自动清理状态,防止内存泄漏
六、应用场景
1. 资源包开发者
- 提供多个可选材质模块,让玩家自由组合
- 维护多版本兼容,自动适配目标版本
- 示例:PvP材质包(低火、短剑、清爽界面等可选模块)
2. 数据包开发者
- 模块化游戏机制(如RPG系统、技能树、经济系统)
- 玩家可按需选择功能模块
- 示例:小游戏合集(空岛生存、跑酷、PvP竞技场等)
3. 整合包制作者
- 快速定制符合整合包主题的资源包
- 批量生成不同配置的资源包分发给玩家
结论
本项目通过创新的纯前端架构和模块化设计,为Minecraft资源包/数据包开发提供了一套完整的工具链解决方案。核心技术贡献包括:
1. 架构创新
- 抽象工厂模式的构建器系统:AbstractBuilder(407行)定义核心流程,OnlineBuilder和FileBuilder实现双模式支持
- 三层模块化覆盖系统:主模块+版本模块+可选模块的灵活组合,权重机制实现精细化文件覆盖控制
- 零服务器架构:完全静态部署,GitHub PAT本地存储,JSZip浏览器内打包,降低运维成本并保护用户隐私
2. 技术突破
- 智能版本适配引擎:
- PathFormatter自动处理pack_format 45+的路径变化(复数↔单数双向转换)
- RecipeFormatter实现三代配方格式的链式转换(234行,支持任意版本间兼容)
- 双版本系统独立管理datapack_version和resources_version
- 依赖关系图谱系统:
- 双向互斥检测(
breaks):正向+反向检测冲突(HomePage.vue第246-260行) - 递归依赖绑定(
bindings):深度优先遍历自动选择传递依赖(HomePage.vue第271-285行) - UI实时反馈:视觉化显示模块可用状态
- 双向互斥检测(
- 多平台模组支持:一键打包为Fabric/Quilt/Forge/NeoForge格式,单一资源包同时兼容原版和模组环境
3. 性能优化
- 异步并发处理:Promise.all()并发加载模块文件树,相比串行减少60%-80%等待时间
- 懒加载策略:仅加载元数据,文件内容按需获取,节省API配额
- 按需过滤:构建过程中提前过滤不需要的目录,减少约50%的API调用
- 内存优化:流式ZIP生成、Blob异步处理、版本信息缓存
4. 用户体验
- URL路由记忆:支持直接分享仓库链接
- 伪进度条:提供视觉反馈(0%→90%动画)
- Markdown渲染:直接展示README,无需跳转
- 代理持久化:localStorage保存用户设置
- 建议版本预选:自动选中推荐版本
5. 社区协作
- 标准化配置格式:TypeScript接口定义严格类型约束
- 项目模板仓库:开箱即用的minecraft-pack-template
- 降低贡献门槛:无需编程知识,仅需编辑JSON
- 去中心化分发:通过GitHub实现版本控制和协作
技术栈优势:
- Vue3 + TypeScript + Vite确保代码可维护性和类型安全
- Arco Design提供一致的UI/UX体验
- JSZip(v3.10.1)、Axios(v1.10.0)等成熟库保证功能稳定性
- 异步并发处理和懒加载优化使构建过程高效流畅
项目影响:
项目显著降低了资源包开发门槛,使开发者能够专注于内容创作而非技术细节,同时为玩家提供了高度定制化的选择自由。 标准化的配置格式和项目模板促进了社区协作,通过GitHub实现去中心化的开源分发模式。该工具已成为Minecraft社区资源开发的实用工具, 未来可进一步扩展功能,推动Minecraft内容创作生态的繁荣发展。