diff --git a/.github/workflows/packer.yml b/.github/workflows/packer.yml
index 870327a997ce..77bfe7be8dd1 100644
--- a/.github/workflows/packer.yml
+++ b/.github/workflows/packer.yml
@@ -3,100 +3,71 @@ name: Packer
on:
workflow_dispatch:
push:
- branches: [main]
+ branches: [ main ]
paths:
- - 'config/packer.json'
- - 'config/fontmap.txt'
+ - 'src/**'
+ - 'config/packer/**'
- 'projects/**'
+
+
jobs:
- build:
+ build-packer:
+ if: github.repository == 'CFPAOrg/Minecraft-Mod-Language-Package'
+ name: Build / Cache Packer
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- with:
- fetch-depth: 20
- ref: main
- - name: Check changed path on 1.12
- uses: MarceloPrado/has-changed-path@v1.0
- id: changed-1122
+ # 缓存程序。一方面,在不同job之间需要这么做;另一方面,大约可以改善运行时间?
+ # actions/cache的逻辑会在job末尾缓存打包程序;如果不命中,就自行构造程序。
+ - name: Cache Packer
+ id: cache-packer
+ uses: actions/cache@v3
with:
- paths: projects/1.12.2
+ key: ${{ runner.os }}-${{ hashFiles('src/Pakcer/**') }}
+ path: Packer.exe
+ lookup-only: true
- - name: Check changed path on 1.16
- uses: MarceloPrado/has-changed-path@v1.0
- id: changed-116
- with:
- paths: projects/1.16
+ # 构造程序
+ - name: Build Packer if not cached
+ if: steps.cache-packer.outputs.cache_hit != 'true'
+ run: dotnet publish .\src\Packer\Packer.csproj -o ./ -r win-x64 -p:PublishSingleFile=true
- - name: Check changed path on 1.16 fabric
- uses: MarceloPrado/has-changed-path@v1.0
- id: changed-116-fabric
- with:
- paths: projects/1.16-fabric
-
- - name: Check changed path on 1.18
- uses: MarceloPrado/has-changed-path@v1.0
- id: changed-118
- with:
- paths: projects/1.18
+ build-uploader:
+ if: github.repository == 'CFPAOrg/Minecraft-Mod-Language-Package'
+ name: Build / Cache Uploader
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v2
- - name: Check changed path on 1.18 fabric
- uses: MarceloPrado/has-changed-path@v1.0
- id: changed-118-fabric
+ # 缓存程序。一方面,在不同job之间需要这么做;另一方面,大约可以改善运行时间?
+ # actions/cache的逻辑会在job末尾缓存打包程序;如果不命中,就自行构造程序。
+ - name: Cache Uploader
+ id: cache-uploader
+ uses: actions/cache@v3
with:
- paths: projects/1.18-fabric
+ key: ${{ runner.os }}-${{ hashFiles('src/Uploader/**') }}
+ path: Uploader.exe
+ lookup-only: true
- - name: Check changed path on 1.19
- uses: MarceloPrado/has-changed-path@v1.0
- id: changed-119
- with:
- paths: projects/1.19
-
- # - name: Check changed path on 1.19 fabric
- # uses: MarceloPrado/has-changed-path@v1.0
- # id: changed-119-fabric
- # with:
- # paths: projects/1.19-fabric
-
- - name: Build Packer
- run: dotnet publish .\src\Packer\Packer.csproj -o ./ -r win-x64 -p:PublishSingleFile=true
+ # 构造程序
+ - name: Build Uploader if not cached
+ if: steps.cache-uploader.outputs.cache_hit != 'true'
+ run: dotnet publish .\src\Uploader\Uploader.csproj -o ./ -r win-x64 -p:PublishSingeFile=true
- - name: Run Packer for 1.12.2
- run: ./Packer --version="1.12.2"
- if: steps.changed-1122.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Run Packer for 1.16
- run: ./Packer --version="1.16"
- if: steps.changed-116.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Run Packer for 1.16-fabric
- run: ./Packer --version="1.16-fabric"
- if: steps.changed-116-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Run Packer for 1.18
- run: ./Packer --version="1.18"
- if: steps.changed-118.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Run Packer for 1.18-fabric
- run: ./Packer --version="1.18-fabric"
- if: steps.changed-118-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Run Packer for 1.19
- run: ./Packer --version="1.19"
- if: steps.changed-119.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- # - name: Run Packer for 1.19-fabric
- # run: ./Packer --version="1.19-fabric"
- # if: steps.changed-119-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
+ initialize-release:
+ if: github.repository == 'CFPAOrg/Minecraft-Mod-Language-Package'
+ name: Initialize Release
+ runs-on: windows-latest
+ steps:
- name: Create timestamp
id: create_timestamp
run: echo "::set-output name=timestamp::$(date '+%Y%m%d%H%M%s')"
shell: bash
-
- # Create the release: https://github.com/actions/create-release
+
+ # Create the release: https://github.com/actions/create-release
- name: Create release
id: create_release
uses: actions/create-release@v1
@@ -107,95 +78,95 @@ jobs:
release_name: 汉化资源包-Snapshot-${{ steps.create_timestamp.outputs.timestamp }}
draft: false
prerelease: false
-
- # Upload release asset: https://github.com/actions/upload-release-asset
- - name: Update release asset for 1.12
- id: upload-release-asset1
- if: steps.changed-1122.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ outputs:
+ upload-url: ${{ steps.create_release.outputs.upload_url }}
+
+
+ pack:
+ if: github.repository == 'CFPAOrg/Minecraft-Mod-Language-Package'
+ name: Pack Resources and Upload Artifacts/Releases
+ needs: [ build-packer, initialize-release ] # 显然,需要存在打包程序,才能打包。
+ strategy:
+ matrix:
+ # 版本列表。将对这里的每个版本判断,按需打包。
+ # 如需添加新版本,在这里添加即可。
+ version: [ "1.12.2", "1.16", "1.16-fabric", "1.18", "1.18-fabric", "1.19", "1.20", "1.20-fabric" ]
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v2
with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: Minecraft-Mod-Language-Package-1.12.2.zip
- asset_name: Minecraft-Mod-Language-Package-1.12.2.zip
- asset_content_type: application/zip
-
- - name: Update release asset for 1.16
- id: upload-release-asset2
- if: steps.changed-116.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ fetch-depth: 20 # 显然,需要有提交历史才能比较提交。20这个数是任意的。
+
+ # 由于Github的限制,这里需要重新拉取打包程序。
+ - name: Restore Packer
+ id: cache-restore
+ uses: actions/cache/restore@v3
with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: Minecraft-Mod-Language-Package-1.16.zip
- asset_name: Minecraft-Mod-Language-Package-1.16.zip
- asset_content_type: application/zip
+ key: ${{ runner.os }}-${{ hashFiles('source/Pakcer/**') }}
+ path: Packer.exe
+ fail-on-cache-miss: true # 前一步理应构造过的。如果不命中,肯定有问题,不如直接挂掉。
-
- - name: Update release asset for 1.16-fabric
- id: upload-release-asset3
- if: steps.changed-116-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Check changed path on ${{ matrix.version }}
+ uses: MarceloPrado/has-changed-path@v1.0
+ id: check-changes
with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: Minecraft-Mod-Language-Package-1.16-fabric.zip
- asset_name: Minecraft-Mod-Language-Package-1.16-fabric.zip
- asset_content_type: application/zip
-
- - name: Update release asset for 1.18
- id: upload-release-asset4
- if: steps.changed-118.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ # 判断位置:该版本文件、该版本配置、代码
+ paths: >
+ projects/${{ matrix.version }}
+ config/packer/${{ matrix.version }}.json
+ src/**
+
+ - name: Run Packer for ${{ matrix.version }}
+ # 分发包中应当包含全部内容
+ run: ./Packer --version="${{ matrix.version }}"
+ # 运行逻辑:内容有更改 或 手动运行
+ if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
+
+ # Artifact 上传内容:每个版本一个压缩包,包含了资源包和md5校验文件
+ - name: Upload Artifact for ${{ matrix.version }}
+ uses: actions/upload-artifact@v3.0.0
with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: Minecraft-Mod-Language-Package-1.18.zip
- asset_name: Minecraft-Mod-Language-Package-1.18.zip
- asset_content_type: application/zip
+ name: Minecraft-Mod-Language-Package-${{ matrix.version }}
+ path: |
+ Minecraft-Mod-Language-Package-${{ matrix.version }}.zip
+ ${{ matrix.version }}.md5
+ if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
- - name: Update release asset for 1.18-fabric
- id: upload-release-asset5
- if: steps.changed-118-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
+ # Upload release asset: https://github.com/actions/upload-release-asset
+ - name: Update release asset for ${{ matrix.version }}
+ id: upload-release-asset
+ if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: Minecraft-Mod-Language-Package-1.18-fabric.zip
- asset_name: Minecraft-Mod-Language-Package-1.18-fabric.zip
+ upload_url: ${{ needs.initialize-release.outputs.upload-url }}
+ asset_path: Minecraft-Mod-Language-Package-${{ matrix.version }}.zip
+ asset_name: Minecraft-Mod-Language-Package-${{ matrix.version }}.zip
asset_content_type: application/zip
+
- - name: Update release asset for 1.19
- id: upload-release-asset6
- if: steps.changed-119.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: Minecraft-Mod-Language-Package-1.19.zip
- asset_name: Minecraft-Mod-Language-Package-1.19.zip
- asset_content_type: application/zip
+ upload:
+ if: github.repository == 'CFPAOrg/Minecraft-Mod-Language-Package'
+ name: Upload Resource Packs to Remote Server
+ needs: [ pack, build-uploader ] # 显然,需要打包完成,并且存在上传程序,才可以上传给分发服务器
+ runs-on: windows-latest
+ steps:
- # - name: Update release asset for 1.18-fabric
- # id: upload-release-asset7
- # if: steps.changed-119-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
- # uses: actions/upload-release-asset@v1
- # env:
- # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- # with:
- # upload_url: ${{ steps.create_release.outputs.upload_url }}
- # asset_path: Minecraft-Mod-Language-Package-1.19-fabric.zip
- # asset_name: Minecraft-Mod-Language-Package-1.19-fabric.zip
- # asset_content_type: application/zip
-
- - name: Build Uploader
- run: dotnet publish .\src\Uploader\Uploader.csproj -o ./ -r win-x64 -p:PublishSingeFile=true
+ # 由于Github的限制,这里需要重新拉取上传程序。
+ - name: Restore Uploader
+ id: cache-restore
+ uses: actions/cache/restore@v3
+ with:
+ key: ${{ runner.os }}-${{ hashFiles('source/Uploader/**') }}
+ path: Uploader.exe
+ fail-on-cache-miss: true # 前一步理应构造过的。如果不命中,肯定有问题,不如直接挂掉。
+
+ # 还原artifact(资源包)
+ - name: Restore Artifacts
+ uses: actions/download-artifact@v3
+ with:
+ path: artifacts/
- name: Run Uploader
run: .\Uploader --host="${{ secrets.SSH_IP }}" --name="${{ secrets.SSH_USER }}" --password="${{ secrets.SSH_PWD }}"
\ No newline at end of file
diff --git a/.github/workflows/pr-packer.yml b/.github/workflows/pr-packer.yml
index d9b39803ca3f..1980258285ff 100644
--- a/.github/workflows/pr-packer.yml
+++ b/.github/workflows/pr-packer.yml
@@ -1,163 +1,98 @@
-# This is a basic workflow to help you get started with Actions
-
name: PR Packer
-# Controls when the workflow will run
on:
workflow_dispatch:
pull_request:
branches: [ main ]
paths:
- - 'config/packer.json'
- - 'config/fontmap.txt'
+ - 'src/**'
+ - 'config/packer/**'
- 'projects/**'
jobs:
build:
+ if: github.repository == 'CFPAOrg/Minecraft-Mod-Language-Package'
+ name: Build / Cache Packer
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- with:
- fetch-depth: 20
- - name: Check changed path on 1.12
- uses: MarceloPrado/has-changed-path@v1.0
- id: changed-1122
+ # 缓存程序。一方面,在不同job之间需要这么做;另一方面,大约可以改善运行时间?
+ # actions/cache的逻辑会在job末尾缓存打包程序;如果不命中,就自行构造程序。
+ - name: Cache Packer
+ id: cache-packer
+ uses: actions/cache@v3
with:
- paths: projects/1.12.2
+ key: ${{ runner.os }}-${{ hashFiles('src/Pakcer/**') }}
+ path: Packer.exe
+ lookup-only: true
- - name: Check changed path on 1.16
- uses: MarceloPrado/has-changed-path@v1.0
- id: changed-116
- with:
- paths: projects/1.16
+ # 构造程序
+ - name: Build Packer if not cached
+ if: steps.cache-packer.outputs.cache_hit != 'true'
+ run: dotnet publish .\src\Packer\Packer.csproj -o ./ -r win-x64 -p:PublishSingleFile=true
- - name: Check changed path on 1.16 fabric
- uses: MarceloPrado/has-changed-path@v1.0
- id: changed-116-fabric
+
+ pack:
+ if: github.repository == 'CFPAOrg/Minecraft-Mod-Language-Package'
+ name: Pack Resources and Upload Artifacts
+ needs: build # 显然,需要存在缓存/已经构造,才能打包。
+ strategy:
+ matrix:
+ # 支持版本列表。将对这里的每个版本判断,按需打包。
+ # 如需添加新版本,在这里添加即可。
+ version: ["1.12.2", "1.16", "1.16-fabric", "1.18", "1.18-fabric", "1.19", "1.20", "1.20-fabric"]
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v2
with:
- paths: projects/1.16-fabric
-
- - name: Check changed path on 1.18
- uses: MarceloPrado/has-changed-path@v1.0
- id: changed-118
+ fetch-depth: 0 # 问题在于下文需要与main比较,就只有全部拉过来了
+
+ # 由于Github的限制,这里需要重新拉取打包程序。
+ - name: Restore Packer
+ id: cache-restore
+ uses: actions/cache/restore@v3
with:
- paths: projects/1.18
+ key: ${{ runner.os }}-${{ hashFiles('src/Pakcer/**') }}
+ path: Packer.exe
+ fail-on-cache-miss: true # 前一步理应构造过的。如果不命中,肯定有问题,不如直接挂掉。
- - name: Check changed path on 1.18 fabric
+ - name: Check changed path on ${{ matrix.version }}
uses: MarceloPrado/has-changed-path@v1.0
- id: changed-118-fabric
+ id: check-changes
with:
- paths: projects/1.18-fabric
+ # 判断位置:该版本文件、该版本配置、代码
+ paths: >
+ projects/${{ matrix.version }}/**
+ config/packer/${{ matrix.version }}.json
+ src/**
- - name: Check changed path on 1.19
+ - name: Check critical path on ${{ matrix.version }}
uses: MarceloPrado/has-changed-path@v1.0
- id: changed-119
+ id: check-critical-changes
with:
- paths: projects/1.19
-
- # - name: Check changed path on 1.19 fabric
- # uses: MarceloPrado/has-changed-path@v1.0
- # id: changed-119-fabric
- # with:
- # paths: projects/1.19-fabric
-
- - name: Build Packer
- run: dotnet publish .\src\Packer\Packer.csproj -o ./ -r win-x64 -p:PublishSingleFile=true
-
- - name: Run Packer for 1.12.2
- run: ./Packer --version="1.12.2"
- if: steps.changed-1122.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Run Packer for 1.16
- run: ./Packer --version="1.16"
- if: steps.changed-116.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Run Packer for 1.16-fabric
- run: ./Packer --version="1.16-fabric"
- if: steps.changed-116-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Run Packer for 1.18
- run: ./Packer --version="1.18"
- if: steps.changed-118.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Run Packer for 1.18-fabric
- run: ./Packer --version="1.18-fabric"
- if: steps.changed-118-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Run Packer for 1.19
- run: ./Packer --version="1.19"
- if: steps.changed-119.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- # - name: Run Packer for 1.19-fabric
- # run: ./Packer --version="1.19-fabric"
- # if: steps.changed-119-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Create timestamp
- id: create_timestamp
- run: echo "::set-output name=timestamp::$(date '+%Y%m%d%H%M%s')"
- shell: bash
-
- # 额 要两分半才能传完 然而 GitHub actions 直接传的话 会压成 zip 内套 zip 就这样了吧 --cy
+ # 判断位置:该版本配置、代码
+ paths: >
+ config/packer/${{ matrix.version }}.json
+ src/**
+
+ - name: Run Packer for ${{ matrix.version }}
+ # 部分包原则:Packer和配置均没有改动
+ run: ./Packer --version="${{ matrix.version }} --increment=${{ !steps.check-critical-changes.outputs.changed }}"
+ # 运行逻辑:内容有更改 或 手动运行
+ if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
+
+ # 额 要两分半才能传完 然而 GitHub actions 直接传的话 会压成 zip 内套 zip 就这样了吧 --cy
- name: Unzip Files
- run: |
- unzip -q Minecraft-Mod-Language-Package-1.19.zip -d Minecraft-Mod-Language-Package-1.19 || echo 0
- unzip -q Minecraft-Mod-Language-Package-1.16.zip -d Minecraft-Mod-Language-Package-1.16 || echo 0
- unzip -q Minecraft-Mod-Language-Package-1.18.zip -d Minecraft-Mod-Language-Package-1.18 || echo 0
- unzip -q Minecraft-Mod-Language-Package-1.16-fabric.zip -d Minecraft-Mod-Language-Package-1.16-fabric || echo 0
- unzip -q Minecraft-Mod-Language-Package-1.18-fabric.zip -d Minecraft-Mod-Language-Package-1.18-fabric || echo 0
- unzip -q Minecraft-Mod-Language-Package-1.12.2.zip -d Minecraft-Mod-Language-Package-1.12.2 || echo 0
- # unzip -q Minecraft-Mod-Language-Package-1.19-fabric.zip -d Minecraft-Mod-Language-Package-1.19-fabric || echo 0
+ run: unzip -q Minecraft-Mod-Language-Package-${{ matrix.version }}.zip -d Minecraft-Mod-Language-Package-${{ matrix.version }} || echo 0
shell: bash
+ if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
- - name: Upload Artifact 1.12.2
- uses: actions/upload-artifact@v3.0.0
- with:
- name: Minecraft-Mod-Language-Package-1.12.2
- path: Minecraft-Mod-Language-Package-1.12.2/*
- if: steps.changed-1122.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Upload Artifact 1.16
- uses: actions/upload-artifact@v3.0.0
- with:
- name: Minecraft-Mod-Language-Package-1.16
- path: Minecraft-Mod-Language-Package-1.16/*
- if: steps.changed-116.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Upload Artifact 1.16 fabric
- uses: actions/upload-artifact@v3.0.0
- with:
- name: Minecraft-Mod-Language-Package-1.16-fabric
- path: Minecraft-Mod-Language-Package-1.16-fabric/*
- if: steps.changed-116-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Upload Artifact 1.18
- uses: actions/upload-artifact@v3.0.0
- with:
- name: Minecraft-Mod-Language-Package-1.18
- path: Minecraft-Mod-Language-Package-1.18/*
- if: steps.changed-118.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Upload Artifact 1.18 fabric
- uses: actions/upload-artifact@v3.0.0
- with:
- name: Minecraft-Mod-Language-Package-1.18-fabric
- path: Minecraft-Mod-Language-Package-1.18-fabric/*
- if: steps.changed-118-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- - name: Upload Artifact 1.19
+ - name: Upload Artifact for ${{ matrix.version }}
uses: actions/upload-artifact@v3.0.0
with:
- name: Minecraft-Mod-Language-Package-1.19
- path: Minecraft-Mod-Language-Package-1.19/*
- if: steps.changed-119.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
- # - name: Upload Artifact 1.19 fabric
- # uses: actions/upload-artifact@v3.0.0
- # with:
- # name: Minecraft-Mod-Language-Package-1.19-fabric
- # path: Minecraft-Mod-Language-Package-1.19-fabric/*
- # if: steps.changed-119-fabric.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
-
+ name: Minecraft-Mod-Language-Package-${{ matrix.version }}
+ path: Minecraft-Mod-Language-Package-${{ matrix.version }}/*
+ if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index bc114d1993e9..8b53ce563096 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,27 @@
+# CFPA-specifics
+Minecraft-Mod-Language-Package-*.zip
+*.md5
+Packer.exe
+Packer.xml
+
+# CFPA-backlogs
+
+cfr.jar
+Formatter.exe
+Spider.exe
+clrcompression.dll
+clrjit.dll
+coreclr.dll
+mscordaccore.dll
+/src/Packer/Properties/launchSettings.json
+
+# libgit2sharp
+
+git2-*.dll
+
+# artifact temporary files
+artifacts/
+
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
@@ -363,11 +387,3 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd
-cfr.jar
-Formatter.exe
-Spider.exe
-clrcompression.dll
-clrjit.dll
-coreclr.dll
-mscordaccore.dll
-/src/Packer/Properties/launchSettings.json
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5bf8f719feba..f9e6ce101c3f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -192,7 +192,7 @@ projects 文件夹下只标出的大版本号,其中的模组翻译文件应
### Packer
-路径:[./config/packer.json](./config/packer.json)
+路径:`./config/packer/[version].json`(如1.12的文件在[1.12.2.json](./config/packer/1.12.2.json))
该文件内放置了**所有**正在维护的版本的打包配置。
不要随意*删去*内容,除非你知道它为什么弃用。
@@ -204,6 +204,9 @@ projects 文件夹下只标出的大版本号,其中的模组翻译文件应
- 增加新翻译版本
- 需要将所有项填写一遍,同时需要更新`.github/workflows/packer.yml`、`.github/workflows/pr-packer.yml`、`.github\boring-cyborg.yml`,以及 [CFPABot](https://github.com/Cyl18/CFPABot) 等相关服务。没有规划最好不要乱动。
+- 更改字符替换表
+ - 修改`replacementMap`,格式与已有文本一致。Unicode*基础多语种平面(BMP)*以外的字符需要使用**UTF-16代理对**输入,否则可能无法识别。
+ - 同时可能需要修改字体文件。
- 处理非文本文件
1. 如果该文件所在的文件夹与`lang`文件夹同级,且对**任何模组都**不会有文本文件(如font\),将该文件夹加入对应版本的`noProcessNamespace`中。
2. 否则,将该模组的`CurseForge 项目名称`或`命名空间`中的一个(具体选哪一个看具体情况)加入`modNameBlackList`或`domainBlackList`,并将**所有**受影响的文件的相对位置加入`additionalContents`。
diff --git a/Packer-Doc.md b/Packer-Doc.md
new file mode 100644
index 000000000000..0f1e18b78d0e
--- /dev/null
+++ b/Packer-Doc.md
@@ -0,0 +1,123 @@
+# 打包机制文档
+
+## 注意事项
+- 文件地址中,目录分隔符**一律使用正斜杠**!
+- 下述说明中,**完整地址**永远指从**仓库根目录**算起的地址,例如根目录下的`CONTRIBUTING.md`即为`CONTRIBUTING.md`,1.12版本资源包的`pack.png`即为`projects/1.12.2/pack.png`。
+- 下述说明中,**相对地址**永远指从**特定命名空间的文件夹**算起的地址,例如仓库中的`projects/1.18/assets/minecraft/minecraft/font/default.json`即为`font/default.json`。
+- 下述说明中,**目标地址**永远指分发的资源包中,该文件应当被放置的位置,例如上一条中提及的文件就是`assets/minecraft/font/default.json`。
+- 本次打包器更新以后,对于**非文本文件**无需特殊处理;打包器会将所有*未知的*拓展名按照非文本文件处理,无需特殊配置。
+ - 目前而言,打包器会将`lang/`下的`.lang`和`.json`作为语言文件,其余的`.txt`, `.md`, `.json`作为普通文本文件,其余文件都作为非文本文件。
如果出现其他格式,可以后续添加——尽管这一部分没有添加显式的配置项。
+
+
+
+## 配置文件
+
+配置文件分为两类:
+- 放置在`./config/packer`下的**全局**配置文件,用作整个打包流程的基础配置;
+- 放置在各命名空间下的**局域**配置文件,用于对特定命名空间(以及模组)提供特殊的配置项。
+
+### 文件格式
+
+**全局**配置文件`./config/packer/.json`的格式如下:
+
+- 根标签 object
+ - `base` object
打包流程中的*不变配置*,不能被文件结构中的**局域配置文件**改写。包含的内容都是**不低于**命名空间层级的,因为局域配置文件就是放在命名空间一级中的。
+ - `version` string
该配置文件指向的版本,以`projects/`中的文件夹名称为准。原则上*应当*与文件名中的[version]一致。
+ - `targetLanguages` list
打包的目标语言,即最终在资源包中存在的语言。
+ - string
目标语言,即该版本使用的语言标识符。
以在`lang`下的语言文件的文件名,以及其他文件的路径中,标明语言的部分为准。目前而言,使用的是`zh_cn`,尽管有消息称将要改用`zho_Hans-CN`。
+ - `exclusionMods` list
被打包器排除的模组文件夹。
如果因为某些原因需要移除某个模组的翻译(如移交官方/其他团体等),但意图保留原有翻译,可能需要这一项。
+ - string
排除的模组。
+ - `exclusionNamespaces` list
被打包器排除的 **[namespace]** | **(命名空间)**。
暂时闲置,以待后续需求。
+ - string
排除的命名空间。
+ - `floating` object
打包流程中的*可变配置*,可能被文件结构中的**局域配置文件**改写。包含的内容都是**低于**命名空间层级的,因为局域配置文件就是放在命名空间一级中的。
+ - `inclusionDomains` list
强制包含的 **[domain]**。
一般而言,用于通常不会包含**语言标识符**的`domain`。
一般而言会包含`font`与`textures`,因为这两处往往包含非文本文件(尽管也可能有文本文件),且字体修复已经需要用到这两个domain;其他内容多半会出现在*局域配置*中。
+ - string
强制包含的domain名称。
+ - `exclusionDomains` list
强制排除的 **[domain]**。
暂时闲置,或可用于排除一些策略相关的零散文件。
+ - string
强制排除的domain名称。
+ - `exclusionPaths` list
强制排除的 **[相对地址]**。
+ - string
强制排除的文件的**相对地址**。
一般而言,在主配置中只会放置通用的忽略对象,例如`packer-policy.json`和`local-config.json`;其余条目最好放在*局域配置*中。
+ - `inclusionPaths` list
强制包含的 **[相对地址]**。
暂时闲置,可以用于添加零散的无语言标记文件。
+ - string
强制包含的文件的**相对地址**。
+ - `characterReplacement` object
打包时采用的字符替换表。用于将部分字符替换至特殊位点,也可单纯用于简化输入。目前而言,包含了字体修复的有关内容。
+ - `<查询语句>` string
用以替换**正则表达式**`<查询语句>`匹配对象的内容,可以是一个或多个字符,甚至可以在这里用**正则替换语句**。
主要用于*字体修复包*所需的**符号替换**,此时,查询语句通常是字面量,替换内容一般而言总是以四位*Unicode转义码*填写;对于**基础多语种平面(BMP)**以外的字符,最好用**UTF-16代理对**书写。
+ - `destinationReplacement` object
打包时采用的目标地址替换。
可以用于移动文件,但暂时闲置。
+ - `<查询语句>` string
用以替换**正则表达式**`<查询语句>`匹配对象的内容,可以是一个或多个字符,甚至可以在这里用**正则替换语句**。
+
+**局域**配置文件`./projects//assets///local-config.json`的格式与全局配置文件中,`floating`标签下的内容(*浮动配置*)一致。
+
+### 文件容斥顺序
+
+介于在配置文件中出现了多种包含/排除文件的配置项,有必要说明以下这些项生效的顺序:
+1. `exclusionMods`和`exclusionNamespaces`在进入命名空间前即会排除相应的文件夹——甚至不会加载其中的`local-config.json`。
当然,如果是通过*检索策略*访问的,则这一项不会生效。
+2. 在剩下的命名空间中,检索文件。下面的配置项可能会被*局域配置*修改,除了`targetLanguages`以外。
+3. 在所有检索到的文件中,排除掉`exclusionPaths`指定的文件,即便是通过*检索策略*访问的。
+4. 在剩下的文件中,直接包含`inclusionPaths`和`inclusionDomains`指定的文件。
+5. 在剩下的文件中,排除掉`exclusionDomains`指定的文件。
+6. 在剩下的文件中,仅包含由`targetLanguages`指定的,在路径中任意位置包含有*简体中文语言标记*的文件,其他文件不予保存。
+
+### 局域配置文件的重写规则
+
+- 如果在某个命名空间内检测到存在`local-config.json`,打包器将会在全局配置的基础上,在其*可变配置*中**添加**该文件中的内容,并用这一修改后的配置执行**该命名空间下的**检索工作。
+- 最好不要与全局配置中的内容重复。尽管理论上这样子可以运行,但是重复项保留哪一个或许不容易断定。
+- 需要注意的是,如果通过*检索策略***引用其他命名空间**,打包器**只**会加载目标命名空间的局域配置,而**不会**加载原空间的局域配置;不过,在原位进行的检索工作不受影响。
+
+## 检索策略
+
+对于每个**命名空间文件夹**(注意这个概念和**命名空间**有着细微差别),打包器除了可以原位检索文件以外,还可以**使用不同的检索方式**。目前,可用的检索方式有两种:
+1. **引用**给定的命名空间。
+2. 从给定的**组合**文件,直接生成语言文件(或部分)。
+
+计划中,将对*非语言文件的文本文件*添加一个**修改包**策略,但是这个策略暂时还没实现,部分原因是在上一版打包器中,这个策略还没被用过。
+
+单独看起来,这或许没什么用(Packer的上一版中,功能还要多些);但有一点很重要:
+这些加载策略(包括**原位**加载)是可以**串联**、**递归**的!于是,通过这三种策略,应该可以满足许多需求。
+
+- **串联**:在一个策略文件中,可以放置**多条策略**。策略将会从前往后执行,**前者**优先——和*Minecraft资源包*顺序差不多。不过,如果有需要,在策略文件中也预留了一个字段,用来**覆盖**前序文件。
+- **递归**:如果**引用**了其他命名空间文件夹,那里的策略文件**也会生效**。这意味着可以实现*连续引用*——尽管前提是不出现**循环引用**。
+
+部分案例被放在了`./projects/packer-example/`这一虚拟的“版本”下。很明显,我们**并不会**分发这一版本,但如果有条件,可以在本地构造打包器,并用这一版本做试验。
+
+### 策略相关文件的格式
+
+#### packer-policy.json
+
+对于每个**命名空间文件夹**,策略文件为`./projects//assets///packer-policy.json`。
+若找不到该策略,默认策略文件内容为`[{"type": "direct"}]`。
+
+- 根标签 list
打包器需要执行的策略,**从前往后执行**。如果有冲突内容,默认以**前者**优先——当然这是可以配置的。
+ - object
单项策略。部分参数可变。
+ - `overrides` bool
是否可以用本步的文件覆盖前序文件。如果无此项,默认为`false`。
关于这里的“覆盖”,对于**非语言文件**(不在`lang\`下),指的是**全文**覆盖;对于**语言文件**,则是**按条目**覆盖。
+ - `type` string
策略的类型。可为以下选项之一:
+ - `direct` 默认选项。不进行特殊处理,直接按照此处的文件结构打包。
+ - `indirect` 引用给定的命名空间。对于这些文件,其*目标地址*中的*命名空间*将会自动替换为本策略所在的命名空间。
+ - `source` string
引用命名空间所在文件夹的**完整地址**。
+ - `composition`
从给定的*组合文件*,直接生成语言文件(或部分)。
这些组合文件可能不会被自动排除;可以考虑使用*局域配置*处理。
+ - `source` string
引用组合文件的**完整位置**。
+ - `destType` string
需要生成的语言文件的类型。可以为`json`或`lang`。
+
+#### [组合文件].json
+
+- 根标签 object
+ - `target` string
生成的语言文件的**目标地址**。
+ * `entries` list
需要生成的组合项。这些项将会分别执行组合以后,连接起来。
**如果存在键冲突,打包器会在此崩溃!**有计划在后期更改这一行为。
+ * object
单项策略。
+ * `templates` object
组合所用的模板。所有内容采用**C#格式化模式**填写。
粗略地说,其中的格式符有形式`{0}, {1}, {2},...`;完整的定义可见 *.net文档*。
+ - `<键模板>` string
`<键模板>`对应的值模板。
+ * `parameters` list
组合所用的参数表。参数按照模板中的**索引**顺序排列。不支持嵌套,必须字面量。
+ * object
每个索引位置上可用的参数。
+ - `<键参数>` string
`<键参数>`对应的值参数。
+
+### 组合文件
+
+组合文件用来生成“组合型”的**语言文件/语言文件片段**,也就是那些有大量重复文本、有明显的格式的语言文件片段。
+组合文件的工作原理如下:
+1. 获取`entries`中的全部条目,每个条目代表一种组合模式。
+2. 每个条目中,由`templates`中的所有条目充当模板,`parameters`中的所有条目充当参数,生成若干组合后的条目。
+ - 在`parameter`中,有时会出现多于一组参数;这种情况下,每组参数都会自由组合。
+ - 同样的,`templates`也会和每一套参数自由组合。
+3. 将所有组合后的条目汇总,生成语言文件。
+ - 在这一过程中,如果出现了**键冲突**,目前而言,**打包器会在此崩溃!**不过,如果后续观察表明确实存在此种需要,也会考虑修改这一行为。
+
+组合文件可以和其他打包策略混合使用,以修改组合中效果不好的部分,或者添加非组合的内容。
+
+组合文件理论上可以放在任何位置,使用任何名称;因此,打包器的*基础配置*没有办法排除掉这些文件。不过,为了方便,最好将其汇总在一个位置,采用明确的名称,以便在*局域配置*中排除。
\ No newline at end of file
diff --git a/Packer-Index-Doc.md b/Packer-Index-Doc.md
deleted file mode 100644
index 5ce65628ece1..000000000000
--- a/Packer-Index-Doc.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# 打包机制-自定义文件检索策略
-
-本仓库的打包器支持对不同模组使用不同的**检索策略**。
-## 注意事项
-- 检索策略仅对**文本文件**有效;**非文本文件**无法通过默认的检索途径,需要在`config/packer.json`中设置绕过检索。详见[此处](./CONTRIBUTING.md#configpackerjson)。
-- 检索策略目前不支持递归调用;所有对于其余位置的引用都不会读取引用位置的`packer-policy.json`。
-
-## 策略配置
-
-对于每个**asset-domain**,策略文件为`./projects//assets///packer-policy.json`。
-若找不到该策略,默认策略文件为`{"type": "noaction"}`。
-
-### 策略文件的格式
-
-packer-policy.json
-
-- 根标签
- - `type` string -> 策略的类型。可为以下选项之一:
- - `noaction` 默认选项。不进行特殊处理,直接按照此处的文件结构打包。如果没有对文件同步或版本对照的特殊要求,使用该类型。如:[示例文件](./projects/1.19/assets/0-example-nop/nop/packer-policy.json)
- - `plainclone` 直接引用另一位置的文件结构。如果需要文本完全同步,使用该类型。如:[示例文件](./projects/1.19/assets/0-example-simple-clone/clone/packer-policy.json)
- - `source` string -> 复制的源地址。需要从本仓库的根目录开始计算,使用`./`前缀。
- - `clonemissing` 使用此处的文件结构,但在检索完文件后,根据另一位置的文件结构**补充**文本。对于出现冲突的条目,若为`lang/`下的内容,将会按照`key`合并,冲突项采用此处的文本;若为其他位置的文件,直接采用此处的文件。如:[示例文件](./projects/1.19/assets/0-example-mixed-clone/mixed-clone/packer-policy.json)
- - `source` string -> 补充文件的源地址。需要从本仓库的根目录开始计算,使用`./`前缀。
- - `backport` 使用此处的文件结构,但在此基础上,从另一位置的文件结构**更新**已有的文本。对于`lang/`下的内容,仅会对已有的`key`更新内容,原本不存在的`key`不会新增;对于其他位置的文件,仅会对已有的文件进行替换,原本不存在的文件不会新增。例如,如果需要从高版本将文件同步至低版本,使用该类型。如:[示例文件](./projects/1.19/assets/0-example-port/port/packer-policy.json)
- - `source` string -> 更新文件的源地址。需要从本仓库的根目录开始计算,使用`./`前缀。
- - `patch` 引用另一位置的文件结构,但在其中的部分文件上额外应用自定义的修改。修改使用[Google Diff-Match-Patch算法](https://github.com/google/diff-match-patch)生成;尽管原则上可以放在任意位置、采用任意后缀名,建议将修改文件放在被修改文件相应的位置,采用`.patch`后缀,以保持统一性。如:[示例文件](./projects/1.19/assets/0-example-patch/patch/packer-policy.json)
- - `source` string -> 复制的源地址。需要从本仓库的根目录开始计算,使用`./`前缀。
- - `patches` object -> 修改文件,以及对应的修改目标。
- - `修改目标的相对路径` string -> 修改文件的源地址。需要从本仓库的根目录开始计算,使用`./`前缀;`修改目标的相对路径`需要为在复制源地址的`/`下方的相对位置,必须使用`/`作为分隔符,如`lang/zh_cn.json`。
diff --git a/config/fontmap.txt b/config/fontmap.txt
deleted file mode 100644
index a761b5bc471a..000000000000
--- a/config/fontmap.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-[[钅卢]]>\ue900
-[[钅杜]]>\ue901
-[[钅喜]]>\ue902
-[[钅波]]>\ue903
-[[钅黑]]>\ue904
-[[钅麦]]>\u9fcf
-[[钅达]]>\ue906
-[[钅仑]]>\ue907
-[[钅哥]]>\u9fd4
-[[钅尔]]>\u9fed
-[[钅夫]]>\ue90a
-[[钅立]]>\ue90c
-[[石田]]>\u9fec
-[[奥气]]>\u9feb
-[[气奥]]>\u9feb
diff --git a/config/packer.json b/config/packer.json
deleted file mode 100644
index 33bc1cf6f49d..000000000000
--- a/config/packer.json
+++ /dev/null
@@ -1,126 +0,0 @@
-[
- {
- "targetVersion": "1.12.2",
- "targetLanguage": [
- "zh_cn"
- ],
- "additionalContent": [
- "LICENSE",
- "pack.mcmeta",
- "pack.png",
- "README.txt"
- ],
- "modNameBlackList": [
- "gregtechce"
- ],
- "domainBlackList": [],
- "noProcessNamespace": [
- "font",
- "textures"
- ]
- },
- {
- "targetVersion": "1.16",
- "targetLanguage": [
- "zh_cn"
- ],
- "additionalContent": [
- "LICENSE",
- "pack.mcmeta",
- "pack.png",
- "README.txt",
- "assets/alexs-mobs/alexsmobs/lang/en_us.json"
- ],
- "modNameBlackList": [],
- "domainBlackList": [],
- "noProcessNamespace": [
- "font",
- "textures"
- ]
- },
- {
- "targetVersion": "1.16-fabric",
- "targetLanguage": [
- "zh_cn"
- ],
- "additionalContent": [
- "LICENSE",
- "pack.mcmeta",
- "pack.png",
- "README.txt"
- ],
- "modNameBlackList": [],
- "domainBlackList": [],
- "noProcessNamespace": [
- "font",
- "textures"
- ]
- },
- {
- "targetVersion": "1.18",
- "targetLanguage": [
- "zh_cn"
- ],
- "additionalContent": [
- "LICENSE",
- "pack.mcmeta",
- "pack.png",
- "README.txt"
- ],
- "modNameBlackList": [
- "create"
- ],
- "domainBlackList": [
- "urushi",
- "thermal"
- ],
- "noProcessNamespace": [
- "font",
- "textures"
- ]
- },
- {
- "targetVersion": "1.18-fabric",
- "targetLanguage": [
- "zh_cn"
- ],
- "additionalContent": [
- "LICENSE",
- "pack.mcmeta",
- "pack.png",
- "README.txt"
- ],
- "modNameBlackList": [],
- "domainBlackList": [
- "litematica"
- ],
- "noProcessNamespace": [
- "font",
- "textures"
- ]
- },
- {
- "targetVersion": "1.19",
- "targetLanguage": [
- "zh_cn"
- ],
- "additionalContent": [
- "LICENSE",
- "pack.mcmeta",
- "pack.png",
- "README.txt"
- ],
- "modNameBlackList": [
- "0-example-nop",
- "0-example-patch",
- "0-example-simple-clone",
- "0-example-mixed-clone",
- "0-example-port"
- ],
- "domainBlackList": [],
- "noProcessNamespace": [
- "font",
- "textures"
- ]
- }
-]
diff --git a/config/packer/1.12.2.json b/config/packer/1.12.2.json
new file mode 100644
index 000000000000..66d0a541814f
--- /dev/null
+++ b/config/packer/1.12.2.json
@@ -0,0 +1,43 @@
+{
+ "base": {
+ "version": "1.12.2",
+ "targetLanguages": [
+ "zh_cn"
+ ],
+ "exclusionMods": [
+ "gregtechce"
+ ],
+ "exclusionNamespaces": []
+ },
+ "floating": {
+ "inclusionDomains": [
+ "font",
+ "textures"
+ ],
+ "exclusionDomains": [],
+ "exclusionPaths": [
+ "packer-policy.json",
+ "local-config.json"
+ ],
+ "inclusionPaths": [],
+ "characterReplacement": {
+ "[[钅卢]]": "\ue900",
+ "[[钅杜]]": "\ue901",
+ "[[钅喜]]": "\ue902",
+ "[[钅波]]": "\ue903",
+ "[[钅黑]]": "\ue904",
+ "[[钅麦]]": "\u9fcf",
+ "[[钅达]]": "\ue906",
+ "[[钅仑]]": "\ue907",
+ "[[钅哥]]": "\u9fd4",
+ "[[钅尔]]": "\u9fed",
+ "[[钅夫]]": "\ue90a",
+ "[[钅立]]": "\ue90c",
+ "[[石田]]": "\u9fec",
+ "[[奥气]]": "\u9feb",
+ "[[气奥]]": "\u9feb",
+ "……": "\u22ef\u22ef"
+ },
+ "destinationReplacement": {}
+ }
+}
\ No newline at end of file
diff --git a/config/packer/1.16-fabric.json b/config/packer/1.16-fabric.json
new file mode 100644
index 000000000000..366a34c4e619
--- /dev/null
+++ b/config/packer/1.16-fabric.json
@@ -0,0 +1,42 @@
+{
+ "base": {
+ "version": "1.16-fabric",
+ "targetLanguages": [
+ "zh_cn"
+ ],
+ "exclusionMods": [],
+ "exclusionNamespaces": []
+ },
+ "floating": {
+ "inclusionDomains": [
+ "font",
+ "textures"
+ ],
+ "exclusionDomains": [],
+ "exclusionPaths": [
+ "packer-policy.json",
+ "local-config.json"
+ ],
+ "inclusionPaths": [],
+ "characterReplacement": {
+ "[[钅卢]]": "\ud872\udf3b",
+ "[[钅杜]]": "\ud872\udf4a",
+ "[[钅喜]]": "\ud872\udf73",
+ "[[钅波]]": "\ud872\udf5b",
+ "[[钅黑]]": "\ud872\udf76",
+ "[[钅麦]]": "\u9fcf",
+ "[[钅达]]": "\ud86d\udffc",
+ "[[钅仑]]": "\ud872\udf2d",
+ "[[钅哥]]": "\u9fd4",
+ "[[钅尔]]": "\u9fed",
+ "[[钅夫]]": "\ud86d\udce7",
+ "[[钅立]]": "\ud86d\udff7",
+ "[[石田]]": "\u9fec",
+ "[[奥气]]": "\u9feb",
+ "[[气奥]]": "\u9feb",
+ "……": "\u22ef\u22ef",
+ "——": "\u2e3f"
+ },
+ "destinationReplacement": {}
+ }
+}
\ No newline at end of file
diff --git a/config/packer/1.16.json b/config/packer/1.16.json
new file mode 100644
index 000000000000..3d2413cc2849
--- /dev/null
+++ b/config/packer/1.16.json
@@ -0,0 +1,42 @@
+{
+ "base": {
+ "version": "1.16",
+ "targetLanguages": [
+ "zh_cn"
+ ],
+ "exclusionMods": [],
+ "exclusionNamespaces": []
+ },
+ "floating": {
+ "inclusionDomains": [
+ "font",
+ "textures"
+ ],
+ "exclusionDomains": [],
+ "exclusionPaths": [
+ "packer-policy.json",
+ "local-config.json"
+ ],
+ "inclusionPaths": [],
+ "characterReplacement": {
+ "[[钅卢]]": "\ud872\udf3b",
+ "[[钅杜]]": "\ud872\udf4a",
+ "[[钅喜]]": "\ud872\udf73",
+ "[[钅波]]": "\ud872\udf5b",
+ "[[钅黑]]": "\ud872\udf76",
+ "[[钅麦]]": "\u9fcf",
+ "[[钅达]]": "\ud86d\udffc",
+ "[[钅仑]]": "\ud872\udf2d",
+ "[[钅哥]]": "\u9fd4",
+ "[[钅尔]]": "\u9fed",
+ "[[钅夫]]": "\ud86d\udce7",
+ "[[钅立]]": "\ud86d\udff7",
+ "[[石田]]": "\u9fec",
+ "[[奥气]]": "\u9feb",
+ "[[气奥]]": "\u9feb",
+ "……": "\u22ef\u22ef",
+ "——": "\u2e3f"
+ },
+ "destinationReplacement": {}
+ }
+}
\ No newline at end of file
diff --git a/config/packer/1.18-fabric.json b/config/packer/1.18-fabric.json
new file mode 100644
index 000000000000..e8ed4e63ad12
--- /dev/null
+++ b/config/packer/1.18-fabric.json
@@ -0,0 +1,44 @@
+{
+ "base": {
+ "version": "1.18-fabric",
+ "targetLanguages": [
+ "zh_cn"
+ ],
+ "exclusionMods": [],
+ "exclusionNamespaces": [
+ "litematica"
+ ]
+ },
+ "floating": {
+ "inclusionDomains": [
+ "font",
+ "textures"
+ ],
+ "exclusionDomains": [],
+ "exclusionPaths": [
+ "packer-policy.json",
+ "local-config.json"
+ ],
+ "inclusionPaths": [],
+ "characterReplacement": {
+ "[[钅卢]]": "\ud872\udf3b",
+ "[[钅杜]]": "\ud872\udf4a",
+ "[[钅喜]]": "\ud872\udf73",
+ "[[钅波]]": "\ud872\udf5b",
+ "[[钅黑]]": "\ud872\udf76",
+ "[[钅麦]]": "\u9fcf",
+ "[[钅达]]": "\ud86d\udffc",
+ "[[钅仑]]": "\ud872\udf2d",
+ "[[钅哥]]": "\u9fd4",
+ "[[钅尔]]": "\u9fed",
+ "[[钅夫]]": "\ud86d\udce7",
+ "[[钅立]]": "\ud86d\udff7",
+ "[[石田]]": "\u9fec",
+ "[[奥气]]": "\u9feb",
+ "[[气奥]]": "\u9feb",
+ "……": "\u22ef\u22ef",
+ "——": "\u2e3f"
+ },
+ "destinationReplacement": {}
+ }
+}
\ No newline at end of file
diff --git a/config/packer/1.18.json b/config/packer/1.18.json
new file mode 100644
index 000000000000..c64eb1651dd3
--- /dev/null
+++ b/config/packer/1.18.json
@@ -0,0 +1,47 @@
+{
+ "base": {
+ "version": "1.18",
+ "targetLanguages": [
+ "zh_cn"
+ ],
+ "exclusionMods": [
+ "create"
+ ],
+ "exclusionNamespaces": [
+ "urushi",
+ "thermal"
+ ]
+ },
+ "floating": {
+ "inclusionDomains": [
+ "font",
+ "textures"
+ ],
+ "exclusionDomains": [],
+ "exclusionPaths": [
+ "packer-policy.json",
+ "local-config.json"
+ ],
+ "inclusionPaths": [],
+ "characterReplacement": {
+ "[[钅卢]]": "\ud872\udf3b",
+ "[[钅杜]]": "\ud872\udf4a",
+ "[[钅喜]]": "\ud872\udf73",
+ "[[钅波]]": "\ud872\udf5b",
+ "[[钅黑]]": "\ud872\udf76",
+ "[[钅麦]]": "\u9fcf",
+ "[[钅达]]": "\ud86d\udffc",
+ "[[钅仑]]": "\ud872\udf2d",
+ "[[钅哥]]": "\u9fd4",
+ "[[钅尔]]": "\u9fed",
+ "[[钅夫]]": "\ud86d\udce7",
+ "[[钅立]]": "\ud86d\udff7",
+ "[[石田]]": "\u9fec",
+ "[[奥气]]": "\u9feb",
+ "[[气奥]]": "\u9feb",
+ "……": "\u22ef\u22ef",
+ "——": "\u2e3f"
+ },
+ "destinationReplacement": {}
+ }
+}
\ No newline at end of file
diff --git a/config/packer/1.19.json b/config/packer/1.19.json
new file mode 100644
index 000000000000..7c7eb46ead6f
--- /dev/null
+++ b/config/packer/1.19.json
@@ -0,0 +1,42 @@
+{
+ "base": {
+ "version": "1.19",
+ "targetLanguages": [
+ "zh_cn"
+ ],
+ "exclusionMods": [],
+ "exclusionNamespaces": []
+ },
+ "floating": {
+ "inclusionDomains": [
+ "font",
+ "textures"
+ ],
+ "exclusionDomains": [],
+ "exclusionPaths": [
+ "packer-policy.json",
+ "local-config.json"
+ ],
+ "inclusionPaths": [],
+ "characterReplacement": {
+ "[[钅卢]]": "\ud872\udf3b",
+ "[[钅杜]]": "\ud872\udf4a",
+ "[[钅喜]]": "\ud872\udf73",
+ "[[钅波]]": "\ud872\udf5b",
+ "[[钅黑]]": "\ud872\udf76",
+ "[[钅麦]]": "\u9fcf",
+ "[[钅达]]": "\ud86d\udffc",
+ "[[钅仑]]": "\ud872\udf2d",
+ "[[钅哥]]": "\u9fd4",
+ "[[钅尔]]": "\u9fed",
+ "[[钅夫]]": "\ud86d\udce7",
+ "[[钅立]]": "\ud86d\udff7",
+ "[[石田]]": "\u9fec",
+ "[[奥气]]": "\u9feb",
+ "[[气奥]]": "\u9feb",
+ "……": "\u22ef\u22ef",
+ "——": "\u2e3f"
+ },
+ "destinationReplacement": {}
+ }
+}
\ No newline at end of file
diff --git a/config/packer/1.20-fabric.json b/config/packer/1.20-fabric.json
new file mode 100644
index 000000000000..072d278bdaa2
--- /dev/null
+++ b/config/packer/1.20-fabric.json
@@ -0,0 +1,42 @@
+{
+ "base": {
+ "version": "1.20-fabric",
+ "targetLanguages": [
+ "zh_cn"
+ ],
+ "exclusionMods": [],
+ "exclusionNamespaces": []
+ },
+ "floating": {
+ "inclusionDomains": [
+ "font",
+ "textures"
+ ],
+ "exclusionDomains": [],
+ "exclusionPaths": [
+ "packer-policy.json",
+ "local-config.json"
+ ],
+ "inclusionPaths": [],
+ "characterReplacement": {
+ "[[钅卢]]": "\ud872\udf3b",
+ "[[钅杜]]": "\ud872\udf4a",
+ "[[钅喜]]": "\ud872\udf73",
+ "[[钅波]]": "\ud872\udf5b",
+ "[[钅黑]]": "\ud872\udf76",
+ "[[钅麦]]": "\u9fcf",
+ "[[钅达]]": "\ud86d\udffc",
+ "[[钅仑]]": "\ud872\udf2d",
+ "[[钅哥]]": "\u9fd4",
+ "[[钅尔]]": "\u9fed",
+ "[[钅夫]]": "\ud86d\udce7",
+ "[[钅立]]": "\ud86d\udff7",
+ "[[石田]]": "\u9fec",
+ "[[奥气]]": "\u9feb",
+ "[[气奥]]": "\u9feb",
+ "……": "\u22ef\u22ef",
+ "——": "\u2e3f"
+ },
+ "destinationReplacement": {}
+ }
+}
\ No newline at end of file
diff --git a/config/packer/1.20.json b/config/packer/1.20.json
new file mode 100644
index 000000000000..8cf5760bd3f0
--- /dev/null
+++ b/config/packer/1.20.json
@@ -0,0 +1,42 @@
+{
+ "base": {
+ "version": "1.20",
+ "targetLanguages": [
+ "zh_cn"
+ ],
+ "exclusionMods": [],
+ "exclusionNamespaces": []
+ },
+ "floating": {
+ "inclusionDomains": [
+ "font",
+ "textures"
+ ],
+ "exclusionDomains": [],
+ "exclusionPaths": [
+ "packer-policy.json",
+ "local-config.json"
+ ],
+ "inclusionPaths": [],
+ "characterReplacement": {
+ "[[钅卢]]": "\ud872\udf3b",
+ "[[钅杜]]": "\ud872\udf4a",
+ "[[钅喜]]": "\ud872\udf73",
+ "[[钅波]]": "\ud872\udf5b",
+ "[[钅黑]]": "\ud872\udf76",
+ "[[钅麦]]": "\u9fcf",
+ "[[钅达]]": "\ud86d\udffc",
+ "[[钅仑]]": "\ud872\udf2d",
+ "[[钅哥]]": "\u9fd4",
+ "[[钅尔]]": "\u9fed",
+ "[[钅夫]]": "\ud86d\udce7",
+ "[[钅立]]": "\ud86d\udff7",
+ "[[石田]]": "\u9fec",
+ "[[奥气]]": "\u9feb",
+ "[[气奥]]": "\u9feb",
+ "……": "\u22ef\u22ef",
+ "——": "\u2e3f"
+ },
+ "destinationReplacement": {}
+ }
+}
\ No newline at end of file
diff --git a/config/packer/packer-example.json b/config/packer/packer-example.json
new file mode 100644
index 000000000000..47b31e97788e
--- /dev/null
+++ b/config/packer/packer-example.json
@@ -0,0 +1,30 @@
+{
+ "base": {
+ "version": "packer-example",
+ "targetLanguages": [
+ "zh_cn"
+ ],
+ "exclusionMods": [],
+ "exclusionNamespaces": []
+ },
+ "floating": {
+ "inclusionDomains": [
+ "font",
+ "textures"
+ ],
+ "exclusionDomains": [],
+ "exclusionPaths": [
+ "packer-policy.json",
+ "local-config.json"
+ ],
+ "inclusionPaths": [
+ "other.txt"
+ ],
+ "characterReplacement": {
+ "[[TEMPLATE]]": "[[REPALCEMENT]]"
+ },
+ "destinationReplacement": {
+ "[[TEMPLATE]]": "[[REPALCEMENT]]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/projects/1.12.2/README.md b/projects/1.12.2/README.md
deleted file mode 100644
index d6d3f02347e5..000000000000
--- a/projects/1.12.2/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-本资源包是适用于 Minecraft 1.12.2 的简体中文资源包,提供数千个模组的简体中文本地化。
-
-署名:CFPA团队(完整贡献者名单:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package/graphs/contributors)
-
-项目主页:https://cfpa.site/
-参与贡献:https://cfpa.site/joinus.html
-反馈问题:https://support.qq.com/product/382723
-GitHub 仓库:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package
-
-
-如果你是手动下载的本资源包,欢迎使用 I18n Update Mod 模组自动下载安装资源包。
-
-I18n Update Mod 下载方式有
-MC百科:https://www.mcmod.cn/download/1188.html
-CurseForge:https://www.curseforge.com/minecraft/mc-mods/i18nupdatemod
-Modrinth:https://modrinth.com/mod/i18nupdatemod
\ No newline at end of file
diff --git a/projects/1.12.2/assets/1UNKNOWN/minecraft/font/glyph_sizes.bin b/projects/1.12.2/assets/1UNKNOWN/minecraft/font/glyph_sizes.bin
index 0503133d08ef..a78037b8f4ac 100644
Binary files a/projects/1.12.2/assets/1UNKNOWN/minecraft/font/glyph_sizes.bin and b/projects/1.12.2/assets/1UNKNOWN/minecraft/font/glyph_sizes.bin differ
diff --git a/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_00.png b/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_00.png
new file mode 100644
index 000000000000..5cc454be2ec4
Binary files /dev/null and b/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_00.png differ
diff --git a/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_22.png b/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_22.png
new file mode 100644
index 000000000000..45b5333e27a7
Binary files /dev/null and b/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_22.png differ
diff --git a/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_30.png b/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_30.png
new file mode 100644
index 000000000000..fcd2cfe18b7e
Binary files /dev/null and b/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_30.png differ
diff --git a/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_ff.png b/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_ff.png
new file mode 100644
index 000000000000..92dbbed68114
Binary files /dev/null and b/projects/1.12.2/assets/1UNKNOWN/minecraft/textures/font/unicode_page_ff.png differ
diff --git a/projects/1.12.2/assets/angry-pixel-the-betweenlands-mod/thebetweenlands/lang/zh_cn.lang b/projects/1.12.2/assets/angry-pixel-the-betweenlands-mod/thebetweenlands/lang/zh_cn.lang
index 754a3f5d9972..a6af23a9c2af 100644
--- a/projects/1.12.2/assets/angry-pixel-the-betweenlands-mod/thebetweenlands/lang/zh_cn.lang
+++ b/projects/1.12.2/assets/angry-pixel-the-betweenlands-mod/thebetweenlands/lang/zh_cn.lang
@@ -2231,7 +2231,6 @@ blsubtitles.misc.gears=齿轮:旋转
blsubtitles.misc.wall_slide=墙:滑移
blsubtitles.misc.wall_slam=墙:撞击
blsubtitles.misc.poop_jet=污泥:被喷射
-blsubtitles.misc.pit_fall
blsubtitles.misc.zap=电击
blsubtitles.misc.chirobarb_erupter=翼手钩发射器:发射
blsubtitles.misc.simulacrum_break=远处传来神秘的噪声
diff --git a/projects/1.12.2/assets/chinjufumod/chinjufumod/lang/zh_cn.lang b/projects/1.12.2/assets/chinjufumod/chinjufumod/lang/zh_cn.lang
index 835907abf1b6..d0f2cb8cc9f3 100644
--- a/projects/1.12.2/assets/chinjufumod/chinjufumod/lang/zh_cn.lang
+++ b/projects/1.12.2/assets/chinjufumod/chinjufumod/lang/zh_cn.lang
@@ -1,4 +1,3 @@
-{
#簡体=Localization by Roseyasa
#Tab
itemGroup.tab_chinjufumod=ChinjufuMod
@@ -2745,4 +2744,3 @@ item.block_food_pizza_t1.name=烤鸡肉披萨
item.item_food_pizzac.name=芝士披萨片
item.item_food_pizzas.name=鱿鱼披萨片
item.item_food_pizzat.name=鸡肉披萨片
-}
diff --git a/projects/1.12.2/assets/rftools/rftools/lang/zh_cn.lang b/projects/1.12.2/assets/rftools/rftools/lang/zh_cn.lang
index 62da0521f3b6..30ec563b22fc 100644
--- a/projects/1.12.2/assets/rftools/rftools/lang/zh_cn.lang
+++ b/projects/1.12.2/assets/rftools/rftools/lang/zh_cn.lang
@@ -260,7 +260,7 @@ message.rftools.remote_scanner=\
@f该方块可以扫描一个区域,\n\
@f并将其与塑形卡片链接。\n\
@f所链接的塑形卡片也\n\
- @f可以在构建器中使用
+ @f可以在构建器中使用\
@6该版本的机器可以通过\n\
@6放置在其上方的\n\
@6已建立链接的物质传输器来扫描
diff --git a/projects/1.12.2/assets/tinkers-compendium/tinkerscompendium/lang/zh_cn.lang b/projects/1.12.2/assets/tinkers-compendium/tinkerscompendium/lang/zh_cn.lang
index c5e84a0af239..86ddebea4c93 100644
--- a/projects/1.12.2/assets/tinkers-compendium/tinkerscompendium/lang/zh_cn.lang
+++ b/projects/1.12.2/assets/tinkers-compendium/tinkerscompendium/lang/zh_cn.lang
@@ -986,7 +986,7 @@ modifier.td_novel.desc=武器和护甲都讲述了一个故事,其中一些比
#Sponge
modifier.td_soggy.name=浸水
modifier.td_soggy.desc=保留水有其好处。
-/Firewood
+#Firewood
modifier.td_hothead.name=急躁
modifier.td_hothead.desc=被击中让我生气!
modifier.td_toasty.name=烤面包
@@ -1010,7 +1010,7 @@ modifier.td_hog.name=肥猪
modifier.td_hog.desc=我的护甲吃掉了我的作业……
modifier.td_sounder.name=肥猪
modifier.td_trotters.name=猪蹄
-/Knightslime
+#Knightslime
modifier.td_shifting.name=移位
modifier.td_shifting.desc=它喜欢自己行动。
modifier.td_gumshoe2.name=口香糖鞋 II
diff --git a/projects/1.12.2/pack.mcmeta b/projects/1.12.2/pack.mcmeta
index 311c7c618a1e..ec41c11f1d23 100644
--- a/projects/1.12.2/pack.mcmeta
+++ b/projects/1.12.2/pack.mcmeta
@@ -1,6 +1,6 @@
-{
- "pack": {
+{{
+ "pack": {{
"pack_format": 3,
- "description": "汉化万用包,仅限1.12.2"
- }
-}
+ "description": "汉化万用包,仅限1.12.2/n打包时间:{0:yyyy-MM-ddTHH:mm:ssZ}"
+ }}
+}}
diff --git a/projects/1.16-fabric/README.md b/projects/1.16-fabric/README.md
deleted file mode 100644
index ddf4296dedbc..000000000000
--- a/projects/1.16-fabric/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-本资源包是适用于 Minecraft 1.16.5 Fabric 的简体中文资源包,提供数千个模组的简体中文本地化。
-
-署名:CFPA团队(完整贡献者名单:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package/graphs/contributors)
-
-项目主页:https://cfpa.site/
-参与贡献:https://cfpa.site/joinus.html
-反馈问题:https://support.qq.com/product/382723
-GitHub 仓库:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package
-
-
-如果你是手动下载的本资源包,欢迎使用 I18n Update Mod 模组自动下载安装资源包。
-
-I18n Update Mod 下载方式有
-MC百科:https://www.mcmod.cn/download/1188.html
-CurseForge:https://www.curseforge.com/minecraft/mc-mods/i18nupdatemod
-Modrinth:https://modrinth.com/mod/i18nupdatemod
\ No newline at end of file
diff --git a/projects/1.16-fabric/assets/cloth-config/cloth-config2/packer-policy.json b/projects/1.16-fabric/assets/cloth-config/cloth-config2/packer-policy.json
index 34b2ca268dbc..fea12dde5c74 100644
--- a/projects/1.16-fabric/assets/cloth-config/cloth-config2/packer-policy.json
+++ b/projects/1.16-fabric/assets/cloth-config/cloth-config2/packer-policy.json
@@ -1 +1,6 @@
-{"type":"plainclone","source":"./projects/1.16/assets/cloth-config/cloth-config2"}
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.16/assets/cloth-config/cloth-config2"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.16-fabric/assets/iron-furnaces/ironfurnaces/packer-policy.json b/projects/1.16-fabric/assets/iron-furnaces/ironfurnaces/packer-policy.json
index 2c9196ef57d8..d81675a62bcc 100644
--- a/projects/1.16-fabric/assets/iron-furnaces/ironfurnaces/packer-policy.json
+++ b/projects/1.16-fabric/assets/iron-furnaces/ironfurnaces/packer-policy.json
@@ -1,4 +1,6 @@
-{
- "type": "plainclone",
- "source": "./projects/1.16/assets/iron-furnaces/ironfurnaces"
-}
\ No newline at end of file
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.16/assets/iron-furnaces/ironfurnaces"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.16-fabric/assets/minecraft/minecraft/font/glyph_sizes.bin b/projects/1.16-fabric/assets/minecraft/minecraft/font/glyph_sizes.bin
deleted file mode 100644
index 0503133d08ef..000000000000
Binary files a/projects/1.16-fabric/assets/minecraft/minecraft/font/glyph_sizes.bin and /dev/null differ
diff --git a/projects/1.16-fabric/assets/minecraft/minecraft/packer-policy.json b/projects/1.16-fabric/assets/minecraft/minecraft/packer-policy.json
new file mode 100644
index 000000000000..ca88d9799a3c
--- /dev/null
+++ b/projects/1.16-fabric/assets/minecraft/minecraft/packer-policy.json
@@ -0,0 +1,6 @@
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.16/assets/minecraft/minecraft"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.16-fabric/assets/minecraft/minecraft/readme.md b/projects/1.16-fabric/assets/minecraft/minecraft/readme.md
deleted file mode 100644
index 1efc216f6cdc..000000000000
--- a/projects/1.16-fabric/assets/minecraft/minecraft/readme.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# 全角字符修正与特殊字符补充
-- 本包添加了几个特殊字符字体,用于补充部分新确定的元素名称
-- 本包修正了中文全角字符的宽度和位置问题。
-
-## 本包建议翻译文本采用左侧书写,在打包阶段使用脚本转换成右侧字符
-
-| 翻译文本 | 转换字符 | 备注 |
-| :------: | :------------: | :----------------------------------: |
-| [[钅卢]] | `\ue900` | |
-| [[钅杜]] | `\ue901` | |
-| [[钅喜]] | `\ue902` | |
-| [[钅波]] | `\ue903` | |
-| [[钅黑]] | `\ue904` | |
-| [[钅麦]] | `\u9fcf` | |
-| [[钅达]] | `\ue906` | |
-| [[钅仑]] | `\ue907` | |
-| [[钅哥]] | `\u9fd4` | |
-| [[钅尔]] | `\u9fed` | |
-| [[钅夫]] | `\ue90a` | |
-| 镆 | `\u9546` | 此元素名已存在对应字符,无需修改替换 |
-| [[钅立]] | `\ue90c` | |
-| [[石田]] | `\u9fec` | |
-| [[奥气]] | `\u9feb` | |
-| …… | `\ue908\ue909` | 全角省略号的修改 |
\ No newline at end of file
diff --git a/projects/1.16-fabric/assets/minecraft/minecraft/textures/font/unicode_page_20.png b/projects/1.16-fabric/assets/minecraft/minecraft/textures/font/unicode_page_20.png
deleted file mode 100644
index 8fc8524241c7..000000000000
Binary files a/projects/1.16-fabric/assets/minecraft/minecraft/textures/font/unicode_page_20.png and /dev/null differ
diff --git a/projects/1.16-fabric/assets/minecraft/minecraft/textures/font/unicode_page_9f.png b/projects/1.16-fabric/assets/minecraft/minecraft/textures/font/unicode_page_9f.png
deleted file mode 100644
index cee8a138a6d5..000000000000
Binary files a/projects/1.16-fabric/assets/minecraft/minecraft/textures/font/unicode_page_9f.png and /dev/null differ
diff --git a/projects/1.16-fabric/assets/minecraft/minecraft/textures/font/unicode_page_e9.png b/projects/1.16-fabric/assets/minecraft/minecraft/textures/font/unicode_page_e9.png
deleted file mode 100644
index e0762efe5e75..000000000000
Binary files a/projects/1.16-fabric/assets/minecraft/minecraft/textures/font/unicode_page_e9.png and /dev/null differ
diff --git a/projects/1.16-fabric/pack.mcmeta b/projects/1.16-fabric/pack.mcmeta
index 0a695a8a1c65..81488da86f03 100644
--- a/projects/1.16-fabric/pack.mcmeta
+++ b/projects/1.16-fabric/pack.mcmeta
@@ -1,6 +1,6 @@
-{
- "pack": {
+{{
+ "pack": {{
"pack_format": 6,
- "description": "汉化万用包,仅供1.16Fabric使用(目前版本1.16.5)"
- }
-}
\ No newline at end of file
+ "description": "汉化万用包,仅供1.16Fabric使用(目前版本1.16.5)/n打包时间:{0:yyyy-MM-ddTHH:mm:ssZ}"
+ }}
+}}
\ No newline at end of file
diff --git a/projects/1.16/README.md b/projects/1.16/README.md
deleted file mode 100644
index 9e8e2fc4f88c..000000000000
--- a/projects/1.16/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-本资源包是适用于 Minecraft 1.16.5 的简体中文资源包,提供数千个模组的简体中文本地化。
-
-署名:CFPA团队(完整贡献者名单:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package/graphs/contributors)
-
-项目主页:https://cfpa.site/
-参与贡献:https://cfpa.site/joinus.html
-反馈问题:https://support.qq.com/product/382723
-GitHub 仓库:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package
-
-
-如果你是手动下载的本资源包,欢迎使用 I18n Update Mod 模组自动下载安装资源包。
-
-I18n Update Mod 下载方式有
-MC百科:https://www.mcmod.cn/download/1188.html
-CurseForge:https://www.curseforge.com/minecraft/mc-mods/i18nupdatemod
-Modrinth:https://modrinth.com/mod/i18nupdatemod
\ No newline at end of file
diff --git a/projects/1.16/assets/alexs-mobs/alexsmobs/local-config.json b/projects/1.16/assets/alexs-mobs/alexsmobs/local-config.json
new file mode 100644
index 000000000000..d21a6af44a1c
--- /dev/null
+++ b/projects/1.16/assets/alexs-mobs/alexsmobs/local-config.json
@@ -0,0 +1,10 @@
+{
+ "inclusionDomains": [],
+ "exclusionDomains": [],
+ "exclusionPaths": [],
+ "inclusionPaths": [
+ "lang/en_us.json"
+ ],
+ "characterReplacement": {},
+ "destinationReplacement": {}
+}
\ No newline at end of file
diff --git a/projects/1.16/assets/corail-recycler/corail_recycler/packer-policy.json b/projects/1.16/assets/corail-recycler/corail_recycler/packer-policy.json
index 3c186f16b573..cc120ff0d580 100644
--- a/projects/1.16/assets/corail-recycler/corail_recycler/packer-policy.json
+++ b/projects/1.16/assets/corail-recycler/corail_recycler/packer-policy.json
@@ -1 +1,6 @@
-{"type":"plainclone","source":"./projects/1.18/assets/corail-recycler/corail_recycler"}
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.18/assets/corail-recycler/corail_recycler"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.16/assets/crafting-tweaks/craftingtweaks/packer-policy.json b/projects/1.16/assets/crafting-tweaks/craftingtweaks/packer-policy.json
index 4569f083c88f..86c8bed1dd41 100644
--- a/projects/1.16/assets/crafting-tweaks/craftingtweaks/packer-policy.json
+++ b/projects/1.16/assets/crafting-tweaks/craftingtweaks/packer-policy.json
@@ -1 +1,10 @@
-{"type":"backport","source":"./projects/1.19/assets/crafting-tweaks/craftingtweaks"}
+[
+ {
+ "type": "direct"
+ },
+ {
+ "type": "indirect",
+ "source": "./projects/1.19/assets/crafting-tweaks/craftingtweaks",
+ "overrides": true
+ }
+]
\ No newline at end of file
diff --git a/projects/1.16/assets/laser-mod/lasermod/lang/zh_cn.json b/projects/1.16/assets/laser-mod/lasermod/lang/zh_cn.json
index 5fc5d317aeb6..bd89d85c75d7 100644
--- a/projects/1.16/assets/laser-mod/lasermod/lang/zh_cn.json
+++ b/projects/1.16/assets/laser-mod/lasermod/lang/zh_cn.json
@@ -45,7 +45,7 @@
"container.lasermod.laser.button.options": "选项",
"container.lasermod.laser.red": "红(R)",
"container.lasermod.laser.green": "绿(G)",
- "container.lasermod.laser.blue": "蓝(B)"
+ "container.lasermod.laser.blue": "蓝(B)" ,
"death.block.laser": "%s被激光烧毁了",
diff --git a/projects/1.16/assets/minecraft/minecraft/font/default.json b/projects/1.16/assets/minecraft/minecraft/font/default.json
new file mode 100644
index 000000000000..afdb490092cd
--- /dev/null
+++ b/projects/1.16/assets/minecraft/minecraft/font/default.json
@@ -0,0 +1,221 @@
+{
+ "providers": [
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/element_ideographs.png",
+ "ascent": 7,
+ "chars": [
+ "\ud872\udf3b\ud872\udf4a\ud872\udf73\ud872\udf5b\ud872\udf76\u9fcf\ud86d\udffc",
+ "\ud872\udf2d\u9fd4\ud86d\udce7\ud86d\udff7\u9feb\u9fec\u9fed"
+ ]
+ },
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/cjk_punctuations.png",
+ "ascent": 7,
+ "chars": [
+ "\u3001\u3002\u300a\u300b\u3010\u3011\u2018\u2019\u201c\uff5e\u3008\u3009\u0000\u0000\u0000\u0000",
+ "\u201d\uff01\uff08\uff09\uff0c\uff1a\uff1b\uff1f\u2014\u00b7\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/ellipsis.png",
+ "ascent": 7,
+ "chars": [
+ "\u22ef"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/2em_dash.png",
+ "ascent": 7,
+ "chars": [
+ "\u2e3a"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/nonlatin_european.png",
+ "ascent": 7,
+ "chars": [
+ "\u00a1\u2030\u00ad\u00b7\u20b4\u2260\u00bf\u00d7\u00d8\u00de\u04bb\u00f0\u00f8\u00fe\u0391\u0392",
+ "\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3",
+ "\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba",
+ "\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u0402",
+ "\u0405\u0406\u0408\u0409\u040a\u040b\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u041a",
+ "\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a",
+ "\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u043a\u043b",
+ "\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b",
+ "\u044c\u044d\u044e\u044f\u0454\u0455\u0456\u0458\u0459\u045a\u2013\u2014\u2018\u2019\u201c\u201d",
+ "\u201e\u2026\u204a\u2190\u2191\u2192\u2193\u21c4\uff0b\u018f\u0259\u025b\u026a\u04ae\u04af\u04e8",
+ "\u04e9\u02bb\u02cc\u037e\u0138\u1e9e\u00df\u20bd\u20ac\u0462\u0463\u0474\u0475\u04c0\u0472\u0473",
+ "\u2070\u00b9\u00b3\u2074\u2075\u2076\u2077\u2078\u2079\u207a\u207b\u207c\u207d\u207e\u2071\u2122",
+ "\u0294\u0295\u29c8\u2694\u2620\u049a\u049b\u0492\u0493\u04b0\u04b1\u04d8\u04d9\u0496\u0497\u04a2",
+ "\u04a3\u04ba\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05db\u05dc\u05de\u05dd",
+ "\u05e0\u05df\u05e1\u05e2\u05e4\u05e3\u05e6\u05e5\u05e7\u05e8\u00a2\u00a4\u00a5\u00a9\u00ae\u00b5",
+ "\u00b6\u00bc\u00bd\u00be\u0387\u2010\u201a\u2020\u2021\u2022\u2031\u2032\u2033\u2034\u2035\u2036",
+ "\u2037\u2039\u203a\u203b\u203c\u203d\u2042\u2048\u2049\u204b\u204e\u204f\u2051\u2052\u2057\u2117",
+ "\u2212\u2213\u221e\u2600\u2601\u2608\u0404\u2632\u2635\u263d\u2640\u2642\u26a5\u2660\u2663\u2665",
+ "\u2666\u2669\u266a\u266b\u266c\u266d\u266e\u266f\u2680\u2681\u2682\u2683\u2684\u2685\u02ac\u26a1",
+ "\u26cf\u2714\u2744\u274c\u2764\u2b50\u2e18\u2e2e\u2e35\u2e38\u2e41\u2e4b\u295d\u1614\u0190\u07c8",
+ "\u03db\u3125\u2c6f\u15fa\u0186\u15e1\u018e\u2132\u2141\ua7b0\ua780\u0500\ua779\u1d1a\u27d8\u2229",
+ "\u0245\u2144\u0250\u0254\u01dd\u025f\u1d77\u0265\u1d09\u027e\u029e\ua781\u026f\u0279\u0287\u028c",
+ "\u028d\u028e\u0531\u0532\u0533\u0534\u0536\u0537\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540",
+ "\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054b\u054c\u054d\u054e\u054f\u0550\u0551",
+ "\u0552\u0553\u0554\u0555\u0556\u0559\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a",
+ "\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a",
+ "\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u05e9\u05ea\u0538",
+ "\u055a\u055b\u055c\u055d\u055e\u055f\u0560\u0588\u058f\u00af\u017f\u01b7\u0292\u01f7\u01bf\u021c",
+ "\u021d\u0224\u0225\u02d9\ua75a\ua75b\u2011\u214b\u23cf\u23e9\u23ea\u23ed\u23ee\u23ef\u23f4\u23f5",
+ "\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u2b58\u25b2\u25b6\u25bc\u25c0\u25cf\u25e6\u25d8",
+ "\u2693\u26e8\u0132\u0133\u01c9\ua728\ua729\ua739\ua73b\ufb00\ufb01\ufb02\ufb03\ufb05\ufffd\u0535",
+ "\u054a\u16a0\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af",
+ "\u16b0\u16b1\u16b2\u16b3\u16b4\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0",
+ "\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0",
+ "\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0",
+ "\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u16eb\u16ec\u16ed\u16ee\u16ef\u16f0",
+ "\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u263a\u263b\u00a6\u2639\u05da\u05f3\u05f4\u05f0",
+ "\u05f1\u05f2\u05be\u05c3\u05c6\u00b4\u00a8\u1d00\u0299\u1d04\u1d05\u1d07\ua730\u0262\u029c\u1d0a",
+ "\u1d0b\u029f\u1d0d\u0274\u1d0f\u1d18\ua7af\u0280\ua731\u1d1b\u1d1c\u1d20\u1d21\u028f\u1d22\u00a7",
+ "\u0271\u0273\u0272\u0288\u0256\u0261\u02a1\u0255\u0291\u0278\u029d\u02a2\u027b\u0281\u0266\u028b",
+ "\u0270\u026c\u026e\u0298\u01c0\u01c3\u01c2\u01c1\u0253\u0257\u1d91\u0284\u0260\u029b\u0267\u026b",
+ "\u0268\u0289\u028a\u0258\u0275\u0264\u025c\u025e\u0251\u0252\u025a\u025d\u0181\u0189\u0191\u01a9",
+ "\u01b2\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae",
+ "\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be",
+ "\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u10c7\u10cd\u10d0\u10d1\u10d2\u10d3\u10d4\u10d5\u10d6",
+ "\u10d7\u10d8\u10d9\u10da\u10db\u10dc\u10dd\u10de\u10df\u10e0\u10e1\u10e2\u10e3\u10e4\u10e5\u10e6",
+ "\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef\u10f0\u10f1\u10f2\u10f3\u10f4\u10f5\u10f6",
+ "\u10f7\u10f8\u10f9\u10fa\u10fb\u10fc\u10fd\u10fe\u10ff\ufb4a\ufb2b\ufb4e\ufb44\ufb3b\ufb1f\ufb1d",
+ "\ufb4b\ufb35\ufb4c\ufb31\ua727\ua726\u027a\u2c71\u02a0\u0297\u0296\u026d\u0277\u027f\u0285\u0286",
+ "\u0293\u029a\u20aa\u20be\u058a\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d21\u2d07\u2d08\u2d09",
+ "\u2d0a\u2d0b\u2d0c\u2d22\u2d0d\u2d0e\u2d0f\u2d10\u2d11\u2d12\u2d23\u2d13\u2d14\u2d15\u2d16\u2d17",
+ "\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d24\u2d1f\u2d20\u2d25\u215b\u215c\u215d\u215e\u2153",
+ "\u2154\u2709\u2602\u2614\u2604\u26c4\u2603\u231b\u231a\u2690\u270e\u2763\u2664\u2667\u2661\u2662",
+ "\u26c8\u2630\u2631\u2633\u2634\u2636\u2637\u2194\u21d2\u21cf\u21d4\u21f5\u2200\u2203\u2204\u2209",
+ "\u220b\u220c\u2282\u2283\u2284\u2285\u2227\u2228\u22bb\u22bc\u22bd\u2225\u2262\u22c6\u2211\u22a4",
+ "\u22a5\u22a2\u22a8\u2254\u2201\u2234\u2235\u221b\u221c\u2202\u22c3\u2286\u2287\u25a1\u25b3\u25b7",
+ "\u25bd\u25c1\u25c6\u25c7\u25cb\u25ce\u2606\u2605\u2718\u2080\u2081\u2082\u2083\u2084\u2085\u2086",
+ "\u2087\u2088\u2089\u208a\u208b\u208c\u208d\u208e\u222b\u222e\u221d\u2300\u2302\u2318\u3012\u027c",
+ "\u0184\u0185\u1e9f\u023d\u019a\u019b\u0220\u019e\u019f\u01a7\u01a8\u01aa\u01b8\u01b9\u01bb\u01bc",
+ "\u01bd\u01be\u0221\u0234\u0235\u0236\u023a\u2c65\u023b\u023c\u0246\u0247\u023e\u2c66\u0241\u0242",
+ "\u0243\u0244\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u1e9c\u1e9d\u1efc\u1efd\u1efe\u1eff",
+ "\ua7a8\ua7a9\ud800\udf30\ud800\udf31\ud800\udf32\ud800\udf33\ud800\udf34\ud800\udf35\ud800\udf36\ud800\udf37\ud800\udf38\ud800\udf39\ud800\udf3a\ud800\udf3b\ud800\udf3c\ud800\udf3d",
+ "\ud800\udf3e\ud800\udf3f\ud800\udf40\ud800\udf41\ud800\udf42\ud800\udf43\ud800\udf44\ud800\udf45\ud800\udf46\ud800\udf47\ud800\udf48\ud800\udf49\ud800\udf4a\ud83c\udf27\ud83d\udd25\ud83c\udf0a",
+ "\u2150\u2151\u2155\u2156\u2157\u2159\u215a\u215f\u2189\ud83d\udde1\ud83c\udff9\ud83e\ude93\ud83d\udd31\ud83c\udfa3\ud83e\uddea\u2697",
+ "\u2bea\u2beb\u2c6d\ud83d\udee1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/accented.png",
+ "height": 12,
+ "ascent": 10,
+ "chars": [
+ "\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf",
+ "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d9\u00da\u00db\u00dc\u00dd\u00e0\u00e1\u00e2\u00e3",
+ "\u00e4\u00e5\u00e6\u00e7\u00ec\u00ed\u00ee\u00ef\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f9\u00fa",
+ "\u00fb\u00fc\u00fd\u00ff\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b",
+ "\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0115\u0116\u0117\u0118\u0119\u011a\u011b",
+ "\u011c\u011d\u1e20\u1e21\u011e\u011f\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129",
+ "\u012a\u012b\u012c\u012d\u012e\u012f\u0130\u0131\u0134\u0135\u0136\u0137\u0139\u013a\u013b\u013c",
+ "\u013d\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u014a\u014b\u014c\u014d",
+ "\u014e\u014f\u0150\u0151\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b\u015c\u015d",
+ "\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d",
+ "\u016e\u016f\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179\u017a\u017b\u017c\u017d",
+ "\u017e\u01fc\u01fd\u01fe\u01ff\u0218\u0219\u021a\u021b\u0386\u0388\u0389\u038a\u038c\u038e\u038f",
+ "\u0390\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03ca\u03cb\u03cc\u03cd\u03ce\u0400\u0401\u0403",
+ "\u0407\u040c\u040d\u040e\u0419\u0439\u0450\u0451\u0452\u0453\u0457\u045b\u045c\u045d\u045e\u045f",
+ "\u0490\u0491\u1e02\u1e03\u1e0a\u1e0b\u1e1e\u1e1f\u1e22\u1e23\u1e30\u1e31\u1e40\u1e41\u1e56\u1e57",
+ "\u1e60\u1e61\u1e6a\u1e6b\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1ef2\u1ef3\u00e8\u00e9\u00ea\u00eb",
+ "\u0149\u01e7\u01eb\u040f\u1e0d\u1e25\u1e5b\u1e6d\u1e92\u1eca\u1ecb\u1ecc\u1ecd\u1ee4\u1ee5\u2116",
+ "\u0207\u0194\u0263\u0283\u2047\u01f1\u01f2\u01f3\u01c4\u01c5\u01c6\u01c7\u01c8\u01ca\u01cb\u01cc",
+ "\u2139\u1d6b\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua73a\ua73c\ua73d\ua74e\ua74f\ua760\ua761",
+ "\ufb04\ufb06\u16a1\u16b5\u01a0\u01a1\u01af\u01b0\u1eae\u1eaf\u1ea4\u1ea5\u1ebe\u1ebf\u1ed1\u1eda",
+ "\u1edb\u1ee8\u1ee9\u1eb0\u1eb1\u1ea6\u1ea7\u1ec0\u1ec1\u1ed3\u1edc\u1edd\u1eea\u1eeb\u1ea2\u1ea3",
+ "\u1eb2\u1eb3\u1ea8\u1ea9\u1eba\u1ebb\u1ed5\u1ede\u1ec2\u1ec3\u1ec8\u1ec9\u1ece\u1ecf\u1ed4\u1edf",
+ "\u1ee6\u1ee7\u1eec\u1eed\u1ef6\u1ef7\u1ea0\u1ea1\u1eb6\u1eb7\u1eac\u1ead\u1eb8\u1eb9\u1ec6\u1ec7",
+ "\u1ed8\u1ed9\u1ee2\u1ee3\u1ef0\u1ef1\u1ef4\u1ef5\u1ed0\u0195\u1eaa\u1eab\u1ed6\u1ed7\u1eef\u261e",
+ "\u261c\u262e\u1eb4\u1eb5\u1ebc\u1ebd\u1ec4\u1ec5\u1ed2\u1ee0\u1ee1\u1eee\u1ef8\u1ef9\u0498\u0499",
+ "\u04a0\u04a1\u04aa\u04ab\u01f6\u26a0\u24ea\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468",
+ "\u2469\u246a\u246b\u246c\u246d\u246e\u246f\u2470\u2471\u2472\u2473\u24b6\u24b7\u24b8\u24b9\u24ba",
+ "\u24bb\u24bc\u24bd\u24be\u24bf\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca",
+ "\u24cb\u24cc\u24cd\u24ce\u24cf\u24d0\u24d1\u24d2\u24d3\u24d4\u24d5\u24d6\u24d7\u24d8\u24d9\u24da",
+ "\u24db\u24dc\u24dd\u24de\u24df\u24e0\u24e1\u24e2\u24e3\u24e4\u24e5\u24e6\u24e7\u24e8\u24e9\u0327",
+ "\u0282\u0290\u0276\u01cd\u01ce\u01de\u01df\u01fa\u01fb\u0202\u0203\u0226\u0227\u01e0\u01e1\u1e00",
+ "\u1e01\u0200\u0201\u1e06\u1e07\u1e04\u1e05\u1d6c\u1e08\u1e09\u1e10\u1e11\u1e12\u1e13\u1e0e\u1e0f",
+ "\u1e0c\u1d6d\u1e14\u1e15\u1e16\u1e17\u1e18\u1e19\u1e1c\u1e1d\u0228\u0229\u1e1a\u1e1b\u0204\u0205",
+ "\u0206\u1d6e\u01f4\u01f5\u01e6\u1e26\u1e27\u1e28\u1e29\u1e2a\u1e2b\u021e\u021f\u1e24\u1e96\u1e2e",
+ "\u1e2f\u020a\u020b\u01cf\u01d0\u0208\u0209\u1e2c\u1e2d\u01f0\u0237\u01e8\u01e9\u1e32\u1e33\u1e34",
+ "\u1e35\u1e3a\u1e3b\u1e3c\u1e3d\u1e36\u1e37\u1e38\u1e39\u2c62\u1e3e\u1e3f\u1e42\u1e43\u1d6f\u1e44",
+ "\u1e45\u1e46\u1e47\u1e4a\u1e4b\u01f8\u01f9\u1e48\u1e49\u1d70\u01ec\u01ed\u022c\u022d\u1e4c\u1e4d",
+ "\u1e4e\u1e4f\u1e50\u1e51\u1e52\u1e53\u020e\u020f\u022a\u022b\u01d1\u01d2\u022e\u022f\u0230\u0231",
+ "\u020c\u020d\u01ea\u1e54\u1e55\u1d71\u0212\u0213\u1e58\u1e59\u1e5c\u1e5d\u1e5e\u1e5f\u0210\u0211",
+ "\u1e5a\u1d73\u1d72\u1e64\u1e65\u1e66\u1e67\u1e62\u1e63\u1e68\u1e69\u1d74\u1e70\u1e71\u1e6e\u1e6f",
+ "\u1e6c\u1e97\u1d75\u1e72\u1e73\u1e76\u1e77\u1e78\u1e79\u1e7a\u1e7b\u01d3\u01d4\u01d5\u01d6\u01d7",
+ "\u01d8\u01d9\u01da\u01db\u01dc\u1e74\u1e75\u0214\u0215\u0216\u1e7e\u1e7f\u1e7c\u1e7d\u1e86\u1e87",
+ "\u1e88\u1e89\u1e98\u1e8c\u1e8d\u1e8a\u1e8b\u0232\u0233\u1e8e\u1e8f\u1e99\u1e94\u1e95\u1e90\u1e91",
+ "\u1e93\u1d76\u01ee\u01ef\u1e9b\ua73e\ua73f\u01e2\u01e3\u1d7a\u1efb\u1d02\u1d14\uab63\u0238\u02a3",
+ "\u02a5\u02a4\u02a9\u02aa\u02ab\u0239\u02a8\u02a6\u02a7\uab50\uab51\u20a7\u1efa\ufb2e\ufb2f\u0180",
+ "\u0182\u0183\u0187\u0188\u018a\u018b\u018c\u0193\u01e4\u01e5\u0197\u0196\u0269\u0198\u0199\u019d",
+ "\u01a4\u01a5\u027d\u01a6\u01ac\u01ad\u01ab\u01ae\u0217\u01b1\u019c\u01b3\u01b4\u01b5\u01b6\u01a2",
+ "\u01a3\u0222\u0223\u02ad\u02ae\u02af\ufb14\ufb15\ufb17\ufb16\ufb13\u04d0\u04d1\u04d2\u04d3\u04f6",
+ "\u04f7\u0494\u0495\u04d6\u04d7\u04bc\u04bd\u04be\u04bf\u04da\u04db\u04dc\u04dd\u04c1\u04c2\u04de",
+ "\u04df\u04e2\u04e3\u04e4\u04e5\u04e6\u04e7\u04ea\u04eb\u04f0\u04f1\u04ee\u04ef\u04f2\u04f3\u04f4",
+ "\u04f5\u04f8\u04f9\u04ec\u04ed\u0476\u0477\u04d4\u04fa\u0502\ua682\ua680\ua688\u052a\u052c\ua684",
+ "\u0504\u0510\u04e0\u0506\u048a\u04c3\u049e\u049c\u051e\u051a\u04c5\u052e\u0512\u0520\u0508\u0514",
+ "\u04cd\u04c9\u0528\u04c7\u04a4\u0522\u050a\u04a8\u0524\u04a6\u048e\u0516\u050c\ua690\u04ac\ua68a",
+ "\ua68c\u050e\u04b2\u04fc\u04fe\u0526\ua694\u04b4\ua68e\u04b6\u04cb\u04b8\ua692\ua696\ua686\u048c",
+ "\u0518\u051c\u04d5\u04fb\u0503\ua683\ua681\ua689\u052b\u052d\ua685\u0505\u0511\u04e1\u0507\u048b",
+ "\u04c4\u049f\u049d\u051f\u051b\u04c6\u052f\u0513\u0521\u0509\u0515\u04ce\u04ca\u0529\u04c8\u04a5",
+ "\u0523\u050b\u04a9\u0525\u04a7\u048f\u0517\u050d\ua691\u04ad\ua68b\ua68d\u050f\u04b3\u04fd\u04ff",
+ "\u0527\ua695\u04b5\ua68f\u04b7\u04cc\u04b9\ua693\ua697\ua687\u048d\u0519\u051d\u1f08\u1f00\u1f09",
+ "\u1f01\u1f0a\u1f02\u1f0b\u1f03\u1f0c\u1f04\u1f0d\u1f05\u1f0e\u1f06\u1f0f\u1f07\u1fba\u1f70\u1fb8",
+ "\u1fb0\u1fb9\u1fb1\u1fbb\u1f71\u1f88\u1f80\u1f89\u1f81\u1f8a\u1f82\u1f8b\u1f83\u1f8c\u1f84\u1f8d",
+ "\u1f85\u1f8e\u1f86\u1f8f\u1f87\u1fbc\u1fb4\u1fb6\u1fb7\u1fb2\u1fb3\u1f18\u1f10\u1f19\u1f11\u1f1a",
+ "\u1f12\u1f1b\u1f13\u1f1c\u1f14\u1f1d\u1f15\u1fc8\u1fc9\u1f72\u1f73\u1f28\u1f20\u1fca\u1f74\u1f29",
+ "\u1f21\u1f2a\u1f22\u1f2b\u1f23\u1f2c\u1f24\u1f2d\u1f25\u1f2e\u1f26\u1f2f\u1f27\u1f98\u1f90\u1f99",
+ "\u1f91\u1f9a\u1f92\u1f9b\u1f93\u1f9c\u1f94\u1f9d\u1f95\u1f9e\u1f96\u1f9f\u1f97\u1fcb\u1f75\u1fcc",
+ "\u1fc3\u1fc2\u1fc4\u1fc6\u1fc7\u1fda\u1f76\u1fdb\u1f77\u1f38\u1f30\u1f39\u1f31\u1f3a\u1f32\u1f3b",
+ "\u1f33\u1f3c\u1f34\u1f3d\u1f35\u1f3e\u1f36\u1f3f\u1f37\u1fd8\u1fd0\u1fd9\u1fd1\u1fd2\u1fd3\u1fd6",
+ "\u1fd7\u1ff8\u1f78\u1ff9\u1f79\u1f48\u1f40\u1f49\u1f41\u1f4a\u1f42\u1f4b\u1f43\u1f4c\u1f44\u1f4d",
+ "\u1f45\u1fec\u1fe4\u1fe5\u1fea\u1f7a\u1feb\u1f7b\u1f59\u1f51\u1f5b\u1f53\u1f5d\u1f55\u1f5f\u1f57",
+ "\u1fe8\u1fe0\u1fe9\u1fe1\u03d3\u03d4\u1fe2\u1fe3\u1fe7\u1f50\u1f52\u1f54\u1fe6\u1f56\u1ffa\u1f7c",
+ "\u1ffb\u1f7d\u1f68\u1f60\u1f69\u1f61\u1f6a\u1f62\u1f6b\u1f63\u1f6c\u1f64\u1f6d\u1f65\u1f6e\u1f66",
+ "\u1f6f\u1f67\u1fa8\u1fa0\u1fa9\u1fa1\u1faa\u1fa2\u1fab\u1fa3\u1fac\u1fa4\u1fad\u1fa5\u1fae\u1fa6",
+ "\u1faf\u1fa7\u1ffc\u1ff3\u1ff2\u1ff4\u1ff6\u1ff7\u262f\u2610\u2611\u2612\u018d\u01ba\u2c7e\u023f",
+ "\u2c7f\u0240\u1d80\ua7c4\ua794\u1d81\u1d82\u1d83\ua795\u1d84\u1d85\u1d86\u1d87\u1d88\u1d89\u1d8a",
+ "\u1d8b\u1d8c\u1d8d\ua7c6\u1d8e\u1d8f\u1d90\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a",
+ "\u1e9a\u2152\u2158\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/ascii.png",
+ "ascent": 7,
+ "chars": [
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
+ "\u0020\u0021\u0022\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002a\u002b\u002c\u002d\u002e\u002f",
+ "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003a\u003b\u003c\u003d\u003e\u003f",
+ "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f",
+ "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a\u005b\u005c\u005d\u005e\u005f",
+ "\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f",
+ "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u007b\u007c\u007d\u007e\u0000",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u00a3\u0000\u0000\u0192",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u00aa\u00ba\u0000\u0000\u00ac\u0000\u0000\u0000\u00ab\u00bb",
+ "\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510",
+ "\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567",
+ "\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u2205\u2208\u0000",
+ "\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000"
+ ]
+ },
+ {
+ "type": "legacy_unicode",
+ "sizes": "minecraft:font/glyph_sizes.bin",
+ "template": "minecraft:font/unicode_page_%s.png"
+ }
+ ]
+}
diff --git a/projects/1.16/assets/minecraft/minecraft/font/glyph_sizes.bin b/projects/1.16/assets/minecraft/minecraft/font/glyph_sizes.bin
deleted file mode 100644
index 0503133d08ef..000000000000
Binary files a/projects/1.16/assets/minecraft/minecraft/font/glyph_sizes.bin and /dev/null differ
diff --git a/projects/1.16/assets/minecraft/minecraft/font/uniform.json b/projects/1.16/assets/minecraft/minecraft/font/uniform.json
new file mode 100644
index 000000000000..6ec90af4a0fa
--- /dev/null
+++ b/projects/1.16/assets/minecraft/minecraft/font/uniform.json
@@ -0,0 +1,43 @@
+{
+ "providers": [
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/element_ideographs.png",
+ "ascent": 7,
+ "chars": [
+ "\ud872\udf3b\ud872\udf4a\ud872\udf73\ud872\udf5b\ud872\udf76\u9fcf\ud86d\udffc",
+ "\ud872\udf2d\u9fd4\ud86d\udce7\ud86d\udff7\u9feb\u9fec\u9fed"
+ ]
+ },
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/cjk_punctuations.png",
+ "ascent": 7,
+ "chars": [
+ "\u3001\u3002\u300a\u300b\u3010\u3011\u2018\u2019\u201c\uff5e\u3008\u3009\u0000\u0000\u0000\u0000",
+ "\u201d\uff01\uff08\uff09\uff0c\uff1a\uff1b\uff1f\u2014\u00b7\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/ellipsis.png",
+ "ascent": 7,
+ "chars": [
+ "\u22ef"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/2em_dash.png",
+ "ascent": 7,
+ "chars": [
+ "\u2e3a"
+ ]
+ },
+ {
+ "type": "legacy_unicode",
+ "sizes": "minecraft:font/glyph_sizes.bin",
+ "template": "minecraft:font/unicode_page_%s.png"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/projects/1.16/assets/minecraft/minecraft/textures/font/2em_dash.png b/projects/1.16/assets/minecraft/minecraft/textures/font/2em_dash.png
new file mode 100644
index 000000000000..edd2703063f0
Binary files /dev/null and b/projects/1.16/assets/minecraft/minecraft/textures/font/2em_dash.png differ
diff --git a/projects/1.16/assets/minecraft/minecraft/textures/font/cjk_punctuations.png b/projects/1.16/assets/minecraft/minecraft/textures/font/cjk_punctuations.png
new file mode 100644
index 000000000000..89b9d440da29
Binary files /dev/null and b/projects/1.16/assets/minecraft/minecraft/textures/font/cjk_punctuations.png differ
diff --git a/projects/1.16/assets/minecraft/minecraft/textures/font/element_ideographs.png b/projects/1.16/assets/minecraft/minecraft/textures/font/element_ideographs.png
new file mode 100644
index 000000000000..387588f10c50
Binary files /dev/null and b/projects/1.16/assets/minecraft/minecraft/textures/font/element_ideographs.png differ
diff --git a/projects/1.16/assets/minecraft/minecraft/textures/font/ellipsis.png b/projects/1.16/assets/minecraft/minecraft/textures/font/ellipsis.png
new file mode 100644
index 000000000000..07588a8478d4
Binary files /dev/null and b/projects/1.16/assets/minecraft/minecraft/textures/font/ellipsis.png differ
diff --git a/projects/1.16/assets/minecraft/minecraft/textures/font/unicode_page_20.png b/projects/1.16/assets/minecraft/minecraft/textures/font/unicode_page_20.png
deleted file mode 100644
index 8fc8524241c7..000000000000
Binary files a/projects/1.16/assets/minecraft/minecraft/textures/font/unicode_page_20.png and /dev/null differ
diff --git a/projects/1.16/assets/minecraft/minecraft/textures/font/unicode_page_9f.png b/projects/1.16/assets/minecraft/minecraft/textures/font/unicode_page_9f.png
deleted file mode 100644
index cee8a138a6d5..000000000000
Binary files a/projects/1.16/assets/minecraft/minecraft/textures/font/unicode_page_9f.png and /dev/null differ
diff --git a/projects/1.16/assets/minecraft/minecraft/textures/font/unicode_page_e9.png b/projects/1.16/assets/minecraft/minecraft/textures/font/unicode_page_e9.png
deleted file mode 100644
index e0762efe5e75..000000000000
Binary files a/projects/1.16/assets/minecraft/minecraft/textures/font/unicode_page_e9.png and /dev/null differ
diff --git a/projects/1.16/assets/tool-kit/toolkit/packer-policy.json b/projects/1.16/assets/tool-kit/toolkit/packer-policy.json
index 5397cfe7f514..91e6926ddcdf 100644
--- a/projects/1.16/assets/tool-kit/toolkit/packer-policy.json
+++ b/projects/1.16/assets/tool-kit/toolkit/packer-policy.json
@@ -1 +1,6 @@
-{"type":"plainclone","source":"./projects/1.18/assets/tool-kit/toolkit"}
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.18/assets/tool-kit/toolkit"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.16/pack.mcmeta b/projects/1.16/pack.mcmeta
index e64ec481812a..61658bebd3d2 100644
--- a/projects/1.16/pack.mcmeta
+++ b/projects/1.16/pack.mcmeta
@@ -1,6 +1,6 @@
-{
- "pack": {
+{{
+ "pack": {{
"pack_format": 6,
- "description": "汉化万用包,仅供1.16Forge使用(目前版本1.16.5)"
- }
-}
\ No newline at end of file
+ "description": "汉化万用包,仅供1.16Forge使用(目前版本1.16.5)/n打包时间:{0:yyyy-MM-ddTHH:mm:ssZ}"
+ }}
+}}
\ No newline at end of file
diff --git a/projects/1.18-fabric/README.md b/projects/1.18-fabric/README.md
deleted file mode 100644
index 25e18a8d1aba..000000000000
--- a/projects/1.18-fabric/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-本资源包是适用于 Minecraft 1.18.2 Fabric 的简体中文资源包,提供数千个模组的简体中文本地化。
-
-署名:CFPA团队(完整贡献者名单:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package/graphs/contributors)
-
-项目主页:https://cfpa.site/
-参与贡献:https://cfpa.site/joinus.html
-反馈问题:https://support.qq.com/product/382723
-GitHub 仓库:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package
-
-
-如果你是手动下载的本资源包,欢迎使用 I18n Update Mod 模组自动下载安装资源包。
-
-I18n Update Mod 下载方式有
-MC百科:https://www.mcmod.cn/download/1188.html
-CurseForge:https://www.curseforge.com/minecraft/mc-mods/i18nupdatemod
-Modrinth:https://modrinth.com/mod/i18nupdatemod
\ No newline at end of file
diff --git a/projects/1.18-fabric/assets/cloth-config/cloth-config2/packer-policy.json b/projects/1.18-fabric/assets/cloth-config/cloth-config2/packer-policy.json
index 0b78380bd704..f117285a0270 100644
--- a/projects/1.18-fabric/assets/cloth-config/cloth-config2/packer-policy.json
+++ b/projects/1.18-fabric/assets/cloth-config/cloth-config2/packer-policy.json
@@ -1 +1,6 @@
-{"type":"plainclone","source":"./projects/1.18/assets/cloth-config/cloth-config2"}
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.18/assets/cloth-config/cloth-config2"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.18-fabric/assets/copper-equipment/exlinecopperequipment/packer-policy.json b/projects/1.18-fabric/assets/copper-equipment/exlinecopperequipment/packer-policy.json
index 7ed58570b1b3..4394bfad29a3 100644
--- a/projects/1.18-fabric/assets/copper-equipment/exlinecopperequipment/packer-policy.json
+++ b/projects/1.18-fabric/assets/copper-equipment/exlinecopperequipment/packer-policy.json
@@ -1 +1,6 @@
-{"type":"plainclone","source":"./projects/1.18/assets/copper-equipment/exlinecopperequipment"}
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.18/assets/copper-equipment/exlinecopperequipment"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.18-fabric/assets/goodall/goodall/packer-policy.json b/projects/1.18-fabric/assets/goodall/goodall/packer-policy.json
index 602c22da5d7e..3a8ca12c508d 100644
--- a/projects/1.18-fabric/assets/goodall/goodall/packer-policy.json
+++ b/projects/1.18-fabric/assets/goodall/goodall/packer-policy.json
@@ -1 +1,4 @@
-{"type":"plainclone","source":"./projects/1.18/assets/goodall/goodall"}
+[{
+ "type": "indirect",
+ "source": "./projects/1.18/assets/goodall/goodall"
+}]
\ No newline at end of file
diff --git a/projects/1.18-fabric/assets/iron-furnaces/ironfurnaces/packer-policy.json b/projects/1.18-fabric/assets/iron-furnaces/ironfurnaces/packer-policy.json
index 29d95fbd0aae..8ee1a3a68b3a 100644
--- a/projects/1.18-fabric/assets/iron-furnaces/ironfurnaces/packer-policy.json
+++ b/projects/1.18-fabric/assets/iron-furnaces/ironfurnaces/packer-policy.json
@@ -1,4 +1,6 @@
-{
- "type": "plainclone",
- "source": "./projects/1.18/assets/iron-furnaces/ironfurnaces"
-}
\ No newline at end of file
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.18/assets/iron-furnaces/ironfurnaces"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.18-fabric/assets/jei/jei/packer-policy.json b/projects/1.18-fabric/assets/jei/jei/packer-policy.json
index 1e668dc475c8..1279d7723e5f 100644
--- a/projects/1.18-fabric/assets/jei/jei/packer-policy.json
+++ b/projects/1.18-fabric/assets/jei/jei/packer-policy.json
@@ -1,4 +1,6 @@
-{
- "type": "plainclone",
- "source": "./projects/1.19/assets/jei/jei"
-}
\ No newline at end of file
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.19/assets/jei/jei"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.18-fabric/assets/minecraft/minecraft/font/glyph_sizes.bin b/projects/1.18-fabric/assets/minecraft/minecraft/font/glyph_sizes.bin
deleted file mode 100644
index 0503133d08ef..000000000000
Binary files a/projects/1.18-fabric/assets/minecraft/minecraft/font/glyph_sizes.bin and /dev/null differ
diff --git a/projects/1.18-fabric/assets/minecraft/minecraft/packer-policy.json b/projects/1.18-fabric/assets/minecraft/minecraft/packer-policy.json
new file mode 100644
index 000000000000..a1ea3f19be38
--- /dev/null
+++ b/projects/1.18-fabric/assets/minecraft/minecraft/packer-policy.json
@@ -0,0 +1,6 @@
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.18/assets/minecraft/minecraft"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.18-fabric/assets/minecraft/minecraft/readme.md b/projects/1.18-fabric/assets/minecraft/minecraft/readme.md
deleted file mode 100644
index 1efc216f6cdc..000000000000
--- a/projects/1.18-fabric/assets/minecraft/minecraft/readme.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# 全角字符修正与特殊字符补充
-- 本包添加了几个特殊字符字体,用于补充部分新确定的元素名称
-- 本包修正了中文全角字符的宽度和位置问题。
-
-## 本包建议翻译文本采用左侧书写,在打包阶段使用脚本转换成右侧字符
-
-| 翻译文本 | 转换字符 | 备注 |
-| :------: | :------------: | :----------------------------------: |
-| [[钅卢]] | `\ue900` | |
-| [[钅杜]] | `\ue901` | |
-| [[钅喜]] | `\ue902` | |
-| [[钅波]] | `\ue903` | |
-| [[钅黑]] | `\ue904` | |
-| [[钅麦]] | `\u9fcf` | |
-| [[钅达]] | `\ue906` | |
-| [[钅仑]] | `\ue907` | |
-| [[钅哥]] | `\u9fd4` | |
-| [[钅尔]] | `\u9fed` | |
-| [[钅夫]] | `\ue90a` | |
-| 镆 | `\u9546` | 此元素名已存在对应字符,无需修改替换 |
-| [[钅立]] | `\ue90c` | |
-| [[石田]] | `\u9fec` | |
-| [[奥气]] | `\u9feb` | |
-| …… | `\ue908\ue909` | 全角省略号的修改 |
\ No newline at end of file
diff --git a/projects/1.18-fabric/assets/minecraft/minecraft/textures/font/unicode_page_20.png b/projects/1.18-fabric/assets/minecraft/minecraft/textures/font/unicode_page_20.png
deleted file mode 100644
index 8fc8524241c7..000000000000
Binary files a/projects/1.18-fabric/assets/minecraft/minecraft/textures/font/unicode_page_20.png and /dev/null differ
diff --git a/projects/1.18-fabric/assets/minecraft/minecraft/textures/font/unicode_page_9f.png b/projects/1.18-fabric/assets/minecraft/minecraft/textures/font/unicode_page_9f.png
deleted file mode 100644
index cee8a138a6d5..000000000000
Binary files a/projects/1.18-fabric/assets/minecraft/minecraft/textures/font/unicode_page_9f.png and /dev/null differ
diff --git a/projects/1.18-fabric/assets/minecraft/minecraft/textures/font/unicode_page_e9.png b/projects/1.18-fabric/assets/minecraft/minecraft/textures/font/unicode_page_e9.png
deleted file mode 100644
index e0762efe5e75..000000000000
Binary files a/projects/1.18-fabric/assets/minecraft/minecraft/textures/font/unicode_page_e9.png and /dev/null differ
diff --git a/projects/1.18-fabric/assets/opulence/opulence/packer-policy.json b/projects/1.18-fabric/assets/opulence/opulence/packer-policy.json
index f2703e04267e..92158592d167 100644
--- a/projects/1.18-fabric/assets/opulence/opulence/packer-policy.json
+++ b/projects/1.18-fabric/assets/opulence/opulence/packer-policy.json
@@ -1,4 +1,6 @@
-{
- "type": "plainclone",
- "source": "./projects/1.18/assets/opulence/opulence"
-}
\ No newline at end of file
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.18/assets/opulence/opulence"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.18-fabric/assets/rotten-creatures/rottencreatures/packer-policy.json b/projects/1.18-fabric/assets/rotten-creatures/rottencreatures/packer-policy.json
index 62326ec5325d..55fbf7e28f8a 100644
--- a/projects/1.18-fabric/assets/rotten-creatures/rottencreatures/packer-policy.json
+++ b/projects/1.18-fabric/assets/rotten-creatures/rottencreatures/packer-policy.json
@@ -1,4 +1,4 @@
-{
- "type": "plainclone",
+[{
+ "type": "indirect",
"source": "./projects/1.18/assets/rotten-creatures/rottencreatures"
-}
\ No newline at end of file
+}]
\ No newline at end of file
diff --git a/projects/1.18-fabric/pack.mcmeta b/projects/1.18-fabric/pack.mcmeta
index dc999fa90fb9..758d59f822d8 100644
--- a/projects/1.18-fabric/pack.mcmeta
+++ b/projects/1.18-fabric/pack.mcmeta
@@ -1,6 +1,6 @@
-{
- "pack": {
- "pack_format": 8,
- "description": "汉化万用包,仅供1.18Fabric使用"
- }
-}
\ No newline at end of file
+{{
+ "pack": {{
+ "pack_format": 8,
+ "description": "汉化万用包,仅供1.18Fabric使用/n打包时间:{0:yyyy-MM-ddTHH:mm:ssZ}"
+ }}
+}}
\ No newline at end of file
diff --git a/projects/1.18/README.md b/projects/1.18/README.md
deleted file mode 100644
index 8d48ef401e59..000000000000
--- a/projects/1.18/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-本资源包是适用于 Minecraft 1.18.2 的简体中文资源包,提供数千个模组的简体中文本地化。
-
-署名:CFPA团队(完整贡献者名单:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package/graphs/contributors)
-
-项目主页:https://cfpa.site/
-参与贡献:https://cfpa.site/joinus.html
-反馈问题:https://support.qq.com/product/382723
-GitHub 仓库:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package
-
-
-如果你是手动下载的本资源包,欢迎使用 I18n Update Mod 模组自动下载安装资源包。
-
-I18n Update Mod 下载方式有
-MC百科:https://www.mcmod.cn/download/1188.html
-CurseForge:https://www.curseforge.com/minecraft/mc-mods/i18nupdatemod
-Modrinth:https://modrinth.com/mod/i18nupdatemod
\ No newline at end of file
diff --git a/projects/1.18/assets/crafting-tweaks/craftingtweaks/packer-policy.json b/projects/1.18/assets/crafting-tweaks/craftingtweaks/packer-policy.json
index 4569f083c88f..86c8bed1dd41 100644
--- a/projects/1.18/assets/crafting-tweaks/craftingtweaks/packer-policy.json
+++ b/projects/1.18/assets/crafting-tweaks/craftingtweaks/packer-policy.json
@@ -1 +1,10 @@
-{"type":"backport","source":"./projects/1.19/assets/crafting-tweaks/craftingtweaks"}
+[
+ {
+ "type": "direct"
+ },
+ {
+ "type": "indirect",
+ "source": "./projects/1.19/assets/crafting-tweaks/craftingtweaks",
+ "overrides": true
+ }
+]
\ No newline at end of file
diff --git a/projects/1.18/assets/jei/jei/packer-policy.json b/projects/1.18/assets/jei/jei/packer-policy.json
index 1e668dc475c8..1279d7723e5f 100644
--- a/projects/1.18/assets/jei/jei/packer-policy.json
+++ b/projects/1.18/assets/jei/jei/packer-policy.json
@@ -1,4 +1,6 @@
-{
- "type": "plainclone",
- "source": "./projects/1.19/assets/jei/jei"
-}
\ No newline at end of file
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.19/assets/jei/jei"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.18/assets/minecraft/minecraft/font/default.json b/projects/1.18/assets/minecraft/minecraft/font/default.json
new file mode 100644
index 000000000000..7a2c19d0aece
--- /dev/null
+++ b/projects/1.18/assets/minecraft/minecraft/font/default.json
@@ -0,0 +1,223 @@
+{
+ "providers": [
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/element_ideographs.png",
+ "ascent": 7,
+ "chars": [
+ "\ud872\udf3b\ud872\udf4a\ud872\udf73\ud872\udf5b\ud872\udf76\u9fcf\ud86d\udffc",
+ "\ud872\udf2d\u9fd4\ud86d\udce7\ud86d\udff7\u9feb\u9fec\u9fed"
+ ]
+ },
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/cjk_punctuations.png",
+ "ascent": 7,
+ "chars": [
+ "\u3001\u3002\u300a\u300b\u3010\u3011\u2018\u2019\u201c\uff5e\u3008\u3009\u0000\u0000\u0000\u0000",
+ "\u201d\uff01\uff08\uff09\uff0c\uff1a\uff1b\uff1f\u2014\u00b7\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/ellipsis.png",
+ "ascent": 7,
+ "chars": [
+ "\u22ef"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/2em_dash.png",
+ "ascent": 7,
+ "chars": [
+ "\u2e3a"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/nonlatin_european.png",
+ "ascent": 7,
+ "chars": [
+ "\u00a1\u2030\u00ad\u00b7\u20b4\u2260\u00bf\u00d7\u00d8\u00de\u04bb\u00f0\u00f8\u00fe\u0391\u0392",
+ "\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3",
+ "\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba",
+ "\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u0402",
+ "\u0405\u0406\u0408\u0409\u040a\u040b\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u041a",
+ "\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a",
+ "\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u043a\u043b",
+ "\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b",
+ "\u044c\u044d\u044e\u044f\u0454\u0455\u0456\u0458\u0459\u045a\u2013\u2014\u2018\u2019\u201c\u201d",
+ "\u201e\u2026\u204a\u2190\u2191\u2192\u2193\u21c4\uff0b\u018f\u0259\u025b\u026a\u04ae\u04af\u04e8",
+ "\u04e9\u02bb\u02cc\u037e\u0138\u1e9e\u00df\u20bd\u20ac\u0462\u0463\u0474\u0475\u04c0\u0472\u0473",
+ "\u2070\u00b9\u00b3\u2074\u2075\u2076\u2077\u2078\u2079\u207a\u207b\u207c\u207d\u207e\u2071\u2122",
+ "\u0294\u0295\u29c8\u2694\u2620\u049a\u049b\u0492\u0493\u04b0\u04b1\u04d8\u04d9\u0496\u0497\u04a2",
+ "\u04a3\u04ba\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05db\u05dc\u05de\u05dd",
+ "\u05e0\u05df\u05e1\u05e2\u05e4\u05e3\u05e6\u05e5\u05e7\u05e8\u00a2\u00a4\u00a5\u00a9\u00ae\u00b5",
+ "\u00b6\u00bc\u00bd\u00be\u0387\u2010\u201a\u2020\u2021\u2022\u2031\u2032\u2033\u2034\u2035\u2036",
+ "\u2037\u2039\u203a\u203b\u203c\u203d\u2042\u2048\u2049\u204b\u204e\u204f\u2051\u2052\u2057\u2117",
+ "\u2212\u2213\u221e\u2600\u2601\u2608\u0404\u2632\u2635\u263d\u2640\u2642\u26a5\u2660\u2663\u2665",
+ "\u2666\u2669\u266a\u266b\u266c\u266d\u266e\u266f\u2680\u2681\u2682\u2683\u2684\u2685\u02ac\u26a1",
+ "\u26cf\u2714\u2744\u274c\u2764\u2b50\u2e18\u2e2e\u2e35\u2e38\u2e41\u2e4b\u295d\u1614\u0190\u07c8",
+ "\u03db\u3125\u2c6f\u15fa\u0186\u15e1\u018e\u2132\u2141\ua7b0\ua780\u0500\ua779\u1d1a\u27d8\u2229",
+ "\u0245\u2144\u0250\u0254\u01dd\u025f\u1d77\u0265\u1d09\u027e\u029e\ua781\u026f\u0279\u0287\u028c",
+ "\u028d\u028e\u0531\u0532\u0533\u0534\u0536\u0537\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540",
+ "\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054b\u054c\u054d\u054e\u054f\u0550\u0551",
+ "\u0552\u0553\u0554\u0555\u0556\u0559\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a",
+ "\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a",
+ "\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u05e9\u05ea\u0538",
+ "\u055a\u055b\u055c\u055d\u055e\u055f\u0560\u0588\u058f\u00af\u017f\u01b7\u0292\u01f7\u01bf\u021c",
+ "\u021d\u0224\u0225\u02d9\ua75a\ua75b\u2011\u214b\u23cf\u23e9\u23ea\u23ed\u23ee\u23ef\u23f4\u23f5",
+ "\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u2b58\u25b2\u25b6\u25bc\u25c0\u25cf\u25e6\u25d8",
+ "\u2693\u26e8\u0132\u0133\u01c9\ua728\ua729\ua739\ua73b\ufb00\ufb01\ufb02\ufb03\ufb05\ufffd\u0535",
+ "\u054a\u16a0\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af",
+ "\u16b0\u16b1\u16b2\u16b3\u16b4\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0",
+ "\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0",
+ "\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0",
+ "\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u16eb\u16ec\u16ed\u16ee\u16ef\u16f0",
+ "\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u263a\u263b\u00a6\u2639\u05da\u05f3\u05f4\u05f0",
+ "\u05f1\u05f2\u05be\u05c3\u05c6\u00b4\u00a8\u1d00\u0299\u1d04\u1d05\u1d07\ua730\u0262\u029c\u1d0a",
+ "\u1d0b\u029f\u1d0d\u0274\u1d0f\u1d18\ua7af\u0280\ua731\u1d1b\u1d1c\u1d20\u1d21\u028f\u1d22\u00a7",
+ "\u0271\u0273\u0272\u0288\u0256\u0261\u02a1\u0255\u0291\u0278\u029d\u02a2\u027b\u0281\u0266\u028b",
+ "\u0270\u026c\u026e\u0298\u01c0\u01c3\u01c2\u01c1\u0253\u0257\u1d91\u0284\u0260\u029b\u0267\u026b",
+ "\u0268\u0289\u028a\u0258\u0275\u0264\u025c\u025e\u0251\u0252\u025a\u025d\u0181\u0189\u0191\u01a9",
+ "\u01b2\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae",
+ "\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be",
+ "\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u10c7\u10cd\u10d0\u10d1\u10d2\u10d3\u10d4\u10d5\u10d6",
+ "\u10d7\u10d8\u10d9\u10da\u10db\u10dc\u10dd\u10de\u10df\u10e0\u10e1\u10e2\u10e3\u10e4\u10e5\u10e6",
+ "\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef\u10f0\u10f1\u10f2\u10f3\u10f4\u10f5\u10f6",
+ "\u10f7\u10f8\u10f9\u10fa\u10fb\u10fc\u10fd\u10fe\u10ff\ufb4a\ufb2b\ufb4e\ufb44\ufb3b\ufb1f\ufb1d",
+ "\ufb4b\ufb35\ufb4c\ufb31\ua727\ua726\u027a\u2c71\u02a0\u0297\u0296\u026d\u0277\u027f\u0285\u0286",
+ "\u0293\u029a\u20aa\u20be\u058a\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d21\u2d07\u2d08\u2d09",
+ "\u2d0a\u2d0b\u2d0c\u2d22\u2d0d\u2d0e\u2d0f\u2d10\u2d11\u2d12\u2d23\u2d13\u2d14\u2d15\u2d16\u2d17",
+ "\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d24\u2d1f\u2d20\u2d25\u215b\u215c\u215d\u215e\u2153",
+ "\u2154\u2709\u2602\u2614\u2604\u26c4\u2603\u231b\u231a\u2690\u270e\u2763\u2664\u2667\u2661\u2662",
+ "\u26c8\u2630\u2631\u2633\u2634\u2636\u2637\u2194\u21d2\u21cf\u21d4\u21f5\u2200\u2203\u2204\u2209",
+ "\u220b\u220c\u2282\u2283\u2284\u2285\u2227\u2228\u22bb\u22bc\u22bd\u2225\u2262\u22c6\u2211\u22a4",
+ "\u22a5\u22a2\u22a8\u2254\u2201\u2234\u2235\u221b\u221c\u2202\u22c3\u2286\u2287\u25a1\u25b3\u25b7",
+ "\u25bd\u25c1\u25c6\u25c7\u25cb\u25ce\u2606\u2605\u2718\u2080\u2081\u2082\u2083\u2084\u2085\u2086",
+ "\u2087\u2088\u2089\u208a\u208b\u208c\u208d\u208e\u222b\u222e\u221d\u2300\u2302\u2318\u3012\u027c",
+ "\u0184\u0185\u1e9f\u023d\u019a\u019b\u0220\u019e\u019f\u01a7\u01a8\u01aa\u01b8\u01b9\u01bb\u01bc",
+ "\u01bd\u01be\u0221\u0234\u0235\u0236\u023a\u2c65\u023b\u023c\u0246\u0247\u023e\u2c66\u0241\u0242",
+ "\u0243\u0244\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u1e9c\u1e9d\u1efc\u1efd\u1efe\u1eff",
+ "\ua7a8\ua7a9\ud800\udf30\ud800\udf31\ud800\udf32\ud800\udf33\ud800\udf34\ud800\udf35\ud800\udf36\ud800\udf37\ud800\udf38\ud800\udf39\ud800\udf3a\ud800\udf3b\ud800\udf3c\ud800\udf3d",
+ "\ud800\udf3e\ud800\udf3f\ud800\udf40\ud800\udf41\ud800\udf42\ud800\udf43\ud800\udf44\ud800\udf45\ud800\udf46\ud800\udf47\ud800\udf48\ud800\udf49\ud800\udf4a\ud83c\udf27\ud83d\udd25\ud83c\udf0a",
+ "\u2150\u2151\u2155\u2156\u2157\u2159\u215a\u215f\u2189\ud83d\udde1\ud83c\udff9\ud83e\ude93\ud83d\udd31\ud83c\udfa3\ud83e\uddea\u2697",
+ "\u2bea\u2beb\u2c6d\ud83d\udee1\u2702\ud83c\udf56\ud83e\udea3\ud83d\udd14\u23f3\u2691\u20a0\u20a1\u20a2\u20a3\u20a4\u20a5",
+ "\u20a6\u20a9\u20ab\u20ad\u20ae\u20b0\u20b1\u20b2\u20b3\u20b5\u20b6\u20b7\u20b8\u20b9\u20ba\u20bb",
+ "\u20bc\u20bf\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/accented.png",
+ "height": 12,
+ "ascent": 10,
+ "chars": [
+ "\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf",
+ "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d9\u00da\u00db\u00dc\u00dd\u00e0\u00e1\u00e2\u00e3",
+ "\u00e4\u00e5\u00e6\u00e7\u00ec\u00ed\u00ee\u00ef\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f9\u00fa",
+ "\u00fb\u00fc\u00fd\u00ff\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b",
+ "\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0115\u0116\u0117\u0118\u0119\u011a\u011b",
+ "\u011c\u011d\u1e20\u1e21\u011e\u011f\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129",
+ "\u012a\u012b\u012c\u012d\u012e\u012f\u0130\u0131\u0134\u0135\u0136\u0137\u0139\u013a\u013b\u013c",
+ "\u013d\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u014a\u014b\u014c\u014d",
+ "\u014e\u014f\u0150\u0151\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b\u015c\u015d",
+ "\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d",
+ "\u016e\u016f\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179\u017a\u017b\u017c\u017d",
+ "\u017e\u01fc\u01fd\u01fe\u01ff\u0218\u0219\u021a\u021b\u0386\u0388\u0389\u038a\u038c\u038e\u038f",
+ "\u0390\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03ca\u03cb\u03cc\u03cd\u03ce\u0400\u0401\u0403",
+ "\u0407\u040c\u040d\u040e\u0419\u0439\u0450\u0451\u0452\u0453\u0457\u045b\u045c\u045d\u045e\u045f",
+ "\u0490\u0491\u1e02\u1e03\u1e0a\u1e0b\u1e1e\u1e1f\u1e22\u1e23\u1e30\u1e31\u1e40\u1e41\u1e56\u1e57",
+ "\u1e60\u1e61\u1e6a\u1e6b\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1ef2\u1ef3\u00e8\u00e9\u00ea\u00eb",
+ "\u0149\u01e7\u01eb\u040f\u1e0d\u1e25\u1e5b\u1e6d\u1e92\u1eca\u1ecb\u1ecc\u1ecd\u1ee4\u1ee5\u2116",
+ "\u0207\u0194\u0263\u0283\u2047\u01f1\u01f2\u01f3\u01c4\u01c5\u01c6\u01c7\u01c8\u01ca\u01cb\u01cc",
+ "\u2139\u1d6b\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua73a\ua73c\ua73d\ua74e\ua74f\ua760\ua761",
+ "\ufb04\ufb06\u16a1\u16b5\u01a0\u01a1\u01af\u01b0\u1eae\u1eaf\u1ea4\u1ea5\u1ebe\u1ebf\u1ed1\u1eda",
+ "\u1edb\u1ee8\u1ee9\u1eb0\u1eb1\u1ea6\u1ea7\u1ec0\u1ec1\u1ed3\u1edc\u1edd\u1eea\u1eeb\u1ea2\u1ea3",
+ "\u1eb2\u1eb3\u1ea8\u1ea9\u1eba\u1ebb\u1ed5\u1ede\u1ec2\u1ec3\u1ec8\u1ec9\u1ece\u1ecf\u1ed4\u1edf",
+ "\u1ee6\u1ee7\u1eec\u1eed\u1ef6\u1ef7\u1ea0\u1ea1\u1eb6\u1eb7\u1eac\u1ead\u1eb8\u1eb9\u1ec6\u1ec7",
+ "\u1ed8\u1ed9\u1ee2\u1ee3\u1ef0\u1ef1\u1ef4\u1ef5\u1ed0\u0195\u1eaa\u1eab\u1ed6\u1ed7\u1eef\u261e",
+ "\u261c\u262e\u1eb4\u1eb5\u1ebc\u1ebd\u1ec4\u1ec5\u1ed2\u1ee0\u1ee1\u1eee\u1ef8\u1ef9\u0498\u0499",
+ "\u04a0\u04a1\u04aa\u04ab\u01f6\u26a0\u24ea\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468",
+ "\u2469\u246a\u246b\u246c\u246d\u246e\u246f\u2470\u2471\u2472\u2473\u24b6\u24b7\u24b8\u24b9\u24ba",
+ "\u24bb\u24bc\u24bd\u24be\u24bf\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca",
+ "\u24cb\u24cc\u24cd\u24ce\u24cf\u24d0\u24d1\u24d2\u24d3\u24d4\u24d5\u24d6\u24d7\u24d8\u24d9\u24da",
+ "\u24db\u24dc\u24dd\u24de\u24df\u24e0\u24e1\u24e2\u24e3\u24e4\u24e5\u24e6\u24e7\u24e8\u24e9\u0327",
+ "\u0282\u0290\u0276\u01cd\u01ce\u01de\u01df\u01fa\u01fb\u0202\u0203\u0226\u0227\u01e0\u01e1\u1e00",
+ "\u1e01\u0200\u0201\u1e06\u1e07\u1e04\u1e05\u1d6c\u1e08\u1e09\u1e10\u1e11\u1e12\u1e13\u1e0e\u1e0f",
+ "\u1e0c\u1d6d\u1e14\u1e15\u1e16\u1e17\u1e18\u1e19\u1e1c\u1e1d\u0228\u0229\u1e1a\u1e1b\u0204\u0205",
+ "\u0206\u1d6e\u01f4\u01f5\u01e6\u1e26\u1e27\u1e28\u1e29\u1e2a\u1e2b\u021e\u021f\u1e24\u1e96\u1e2e",
+ "\u1e2f\u020a\u020b\u01cf\u01d0\u0208\u0209\u1e2c\u1e2d\u01f0\u0237\u01e8\u01e9\u1e32\u1e33\u1e34",
+ "\u1e35\u1e3a\u1e3b\u1e3c\u1e3d\u1e36\u1e37\u1e38\u1e39\u2c62\u1e3e\u1e3f\u1e42\u1e43\u1d6f\u1e44",
+ "\u1e45\u1e46\u1e47\u1e4a\u1e4b\u01f8\u01f9\u1e48\u1e49\u1d70\u01ec\u01ed\u022c\u022d\u1e4c\u1e4d",
+ "\u1e4e\u1e4f\u1e50\u1e51\u1e52\u1e53\u020e\u020f\u022a\u022b\u01d1\u01d2\u022e\u022f\u0230\u0231",
+ "\u020c\u020d\u01ea\u1e54\u1e55\u1d71\u0212\u0213\u1e58\u1e59\u1e5c\u1e5d\u1e5e\u1e5f\u0210\u0211",
+ "\u1e5a\u1d73\u1d72\u1e64\u1e65\u1e66\u1e67\u1e62\u1e63\u1e68\u1e69\u1d74\u1e70\u1e71\u1e6e\u1e6f",
+ "\u1e6c\u1e97\u1d75\u1e72\u1e73\u1e76\u1e77\u1e78\u1e79\u1e7a\u1e7b\u01d3\u01d4\u01d5\u01d6\u01d7",
+ "\u01d8\u01d9\u01da\u01db\u01dc\u1e74\u1e75\u0214\u0215\u0216\u1e7e\u1e7f\u1e7c\u1e7d\u1e86\u1e87",
+ "\u1e88\u1e89\u1e98\u1e8c\u1e8d\u1e8a\u1e8b\u0232\u0233\u1e8e\u1e8f\u1e99\u1e94\u1e95\u1e90\u1e91",
+ "\u1e93\u1d76\u01ee\u01ef\u1e9b\ua73e\ua73f\u01e2\u01e3\u1d7a\u1efb\u1d02\u1d14\uab63\u0238\u02a3",
+ "\u02a5\u02a4\u02a9\u02aa\u02ab\u0239\u02a8\u02a6\u02a7\uab50\uab51\u20a7\u1efa\ufb2e\ufb2f\u0180",
+ "\u0182\u0183\u0187\u0188\u018a\u018b\u018c\u0193\u01e4\u01e5\u0197\u0196\u0269\u0198\u0199\u019d",
+ "\u01a4\u01a5\u027d\u01a6\u01ac\u01ad\u01ab\u01ae\u0217\u01b1\u019c\u01b3\u01b4\u01b5\u01b6\u01a2",
+ "\u01a3\u0222\u0223\u02ad\u02ae\u02af\ufb14\ufb15\ufb17\ufb16\ufb13\u04d0\u04d1\u04d2\u04d3\u04f6",
+ "\u04f7\u0494\u0495\u04d6\u04d7\u04bc\u04bd\u04be\u04bf\u04da\u04db\u04dc\u04dd\u04c1\u04c2\u04de",
+ "\u04df\u04e2\u04e3\u04e4\u04e5\u04e6\u04e7\u04ea\u04eb\u04f0\u04f1\u04ee\u04ef\u04f2\u04f3\u04f4",
+ "\u04f5\u04f8\u04f9\u04ec\u04ed\u0476\u0477\u04d4\u04fa\u0502\ua682\ua680\ua688\u052a\u052c\ua684",
+ "\u0504\u0510\u04e0\u0506\u048a\u04c3\u049e\u049c\u051e\u051a\u04c5\u052e\u0512\u0520\u0508\u0514",
+ "\u04cd\u04c9\u0528\u04c7\u04a4\u0522\u050a\u04a8\u0524\u04a6\u048e\u0516\u050c\ua690\u04ac\ua68a",
+ "\ua68c\u050e\u04b2\u04fc\u04fe\u0526\ua694\u04b4\ua68e\u04b6\u04cb\u04b8\ua692\ua696\ua686\u048c",
+ "\u0518\u051c\u04d5\u04fb\u0503\ua683\ua681\ua689\u052b\u052d\ua685\u0505\u0511\u04e1\u0507\u048b",
+ "\u04c4\u049f\u049d\u051f\u051b\u04c6\u052f\u0513\u0521\u0509\u0515\u04ce\u04ca\u0529\u04c8\u04a5",
+ "\u0523\u050b\u04a9\u0525\u04a7\u048f\u0517\u050d\ua691\u04ad\ua68b\ua68d\u050f\u04b3\u04fd\u04ff",
+ "\u0527\ua695\u04b5\ua68f\u04b7\u04cc\u04b9\ua693\ua697\ua687\u048d\u0519\u051d\u1f08\u1f00\u1f09",
+ "\u1f01\u1f0a\u1f02\u1f0b\u1f03\u1f0c\u1f04\u1f0d\u1f05\u1f0e\u1f06\u1f0f\u1f07\u1fba\u1f70\u1fb8",
+ "\u1fb0\u1fb9\u1fb1\u1fbb\u1f71\u1f88\u1f80\u1f89\u1f81\u1f8a\u1f82\u1f8b\u1f83\u1f8c\u1f84\u1f8d",
+ "\u1f85\u1f8e\u1f86\u1f8f\u1f87\u1fbc\u1fb4\u1fb6\u1fb7\u1fb2\u1fb3\u1f18\u1f10\u1f19\u1f11\u1f1a",
+ "\u1f12\u1f1b\u1f13\u1f1c\u1f14\u1f1d\u1f15\u1fc8\u1fc9\u1f72\u1f73\u1f28\u1f20\u1fca\u1f74\u1f29",
+ "\u1f21\u1f2a\u1f22\u1f2b\u1f23\u1f2c\u1f24\u1f2d\u1f25\u1f2e\u1f26\u1f2f\u1f27\u1f98\u1f90\u1f99",
+ "\u1f91\u1f9a\u1f92\u1f9b\u1f93\u1f9c\u1f94\u1f9d\u1f95\u1f9e\u1f96\u1f9f\u1f97\u1fcb\u1f75\u1fcc",
+ "\u1fc3\u1fc2\u1fc4\u1fc6\u1fc7\u1fda\u1f76\u1fdb\u1f77\u1f38\u1f30\u1f39\u1f31\u1f3a\u1f32\u1f3b",
+ "\u1f33\u1f3c\u1f34\u1f3d\u1f35\u1f3e\u1f36\u1f3f\u1f37\u1fd8\u1fd0\u1fd9\u1fd1\u1fd2\u1fd3\u1fd6",
+ "\u1fd7\u1ff8\u1f78\u1ff9\u1f79\u1f48\u1f40\u1f49\u1f41\u1f4a\u1f42\u1f4b\u1f43\u1f4c\u1f44\u1f4d",
+ "\u1f45\u1fec\u1fe4\u1fe5\u1fea\u1f7a\u1feb\u1f7b\u1f59\u1f51\u1f5b\u1f53\u1f5d\u1f55\u1f5f\u1f57",
+ "\u1fe8\u1fe0\u1fe9\u1fe1\u03d3\u03d4\u1fe2\u1fe3\u1fe7\u1f50\u1f52\u1f54\u1fe6\u1f56\u1ffa\u1f7c",
+ "\u1ffb\u1f7d\u1f68\u1f60\u1f69\u1f61\u1f6a\u1f62\u1f6b\u1f63\u1f6c\u1f64\u1f6d\u1f65\u1f6e\u1f66",
+ "\u1f6f\u1f67\u1fa8\u1fa0\u1fa9\u1fa1\u1faa\u1fa2\u1fab\u1fa3\u1fac\u1fa4\u1fad\u1fa5\u1fae\u1fa6",
+ "\u1faf\u1fa7\u1ffc\u1ff3\u1ff2\u1ff4\u1ff6\u1ff7\u262f\u2610\u2611\u2612\u018d\u01ba\u2c7e\u023f",
+ "\u2c7f\u0240\u1d80\ua7c4\ua794\u1d81\u1d82\u1d83\ua795\u1d84\u1d85\u1d86\u1d87\u1d88\u1d89\u1d8a",
+ "\u1d8b\u1d8c\u1d8d\ua7c6\u1d8e\u1d8f\u1d90\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a",
+ "\u1e9a\u2152\u2158\u20a8\u20af\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/ascii.png",
+ "ascent": 7,
+ "chars": [
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
+ "\u0020\u0021\u0022\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002a\u002b\u002c\u002d\u002e\u002f",
+ "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003a\u003b\u003c\u003d\u003e\u003f",
+ "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f",
+ "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a\u005b\u005c\u005d\u005e\u005f",
+ "\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f",
+ "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u007b\u007c\u007d\u007e\u0000",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u00a3\u0000\u0000\u0192",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u00aa\u00ba\u0000\u0000\u00ac\u0000\u0000\u0000\u00ab\u00bb",
+ "\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510",
+ "\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567",
+ "\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u2205\u2208\u0000",
+ "\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u0000\u221a\u207f\u00b2\u25a0\u0000"
+ ]
+ },
+ {
+ "type": "legacy_unicode",
+ "sizes": "minecraft:font/glyph_sizes.bin",
+ "template": "minecraft:font/unicode_page_%s.png"
+ }
+ ]
+}
diff --git a/projects/1.18/assets/minecraft/minecraft/font/glyph_sizes.bin b/projects/1.18/assets/minecraft/minecraft/font/glyph_sizes.bin
deleted file mode 100644
index 0503133d08ef..000000000000
Binary files a/projects/1.18/assets/minecraft/minecraft/font/glyph_sizes.bin and /dev/null differ
diff --git a/projects/1.18/assets/minecraft/minecraft/packer-policy.json b/projects/1.18/assets/minecraft/minecraft/packer-policy.json
new file mode 100644
index 000000000000..36653b700ae5
--- /dev/null
+++ b/projects/1.18/assets/minecraft/minecraft/packer-policy.json
@@ -0,0 +1,9 @@
+[
+ {
+ "type": "direct"
+ },
+ {
+ "type": "indirect",
+ "source": "./projects/1.16/assets/minecraft/minecraft"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.18/assets/minecraft/minecraft/readme.md b/projects/1.18/assets/minecraft/minecraft/readme.md
deleted file mode 100644
index 1efc216f6cdc..000000000000
--- a/projects/1.18/assets/minecraft/minecraft/readme.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# 全角字符修正与特殊字符补充
-- 本包添加了几个特殊字符字体,用于补充部分新确定的元素名称
-- 本包修正了中文全角字符的宽度和位置问题。
-
-## 本包建议翻译文本采用左侧书写,在打包阶段使用脚本转换成右侧字符
-
-| 翻译文本 | 转换字符 | 备注 |
-| :------: | :------------: | :----------------------------------: |
-| [[钅卢]] | `\ue900` | |
-| [[钅杜]] | `\ue901` | |
-| [[钅喜]] | `\ue902` | |
-| [[钅波]] | `\ue903` | |
-| [[钅黑]] | `\ue904` | |
-| [[钅麦]] | `\u9fcf` | |
-| [[钅达]] | `\ue906` | |
-| [[钅仑]] | `\ue907` | |
-| [[钅哥]] | `\u9fd4` | |
-| [[钅尔]] | `\u9fed` | |
-| [[钅夫]] | `\ue90a` | |
-| 镆 | `\u9546` | 此元素名已存在对应字符,无需修改替换 |
-| [[钅立]] | `\ue90c` | |
-| [[石田]] | `\u9fec` | |
-| [[奥气]] | `\u9feb` | |
-| …… | `\ue908\ue909` | 全角省略号的修改 |
\ No newline at end of file
diff --git a/projects/1.18/assets/minecraft/minecraft/textures/font/unicode_page_20.png b/projects/1.18/assets/minecraft/minecraft/textures/font/unicode_page_20.png
deleted file mode 100644
index 8fc8524241c7..000000000000
Binary files a/projects/1.18/assets/minecraft/minecraft/textures/font/unicode_page_20.png and /dev/null differ
diff --git a/projects/1.18/assets/minecraft/minecraft/textures/font/unicode_page_9f.png b/projects/1.18/assets/minecraft/minecraft/textures/font/unicode_page_9f.png
deleted file mode 100644
index cee8a138a6d5..000000000000
Binary files a/projects/1.18/assets/minecraft/minecraft/textures/font/unicode_page_9f.png and /dev/null differ
diff --git a/projects/1.18/assets/minecraft/minecraft/textures/font/unicode_page_e9.png b/projects/1.18/assets/minecraft/minecraft/textures/font/unicode_page_e9.png
deleted file mode 100644
index e0762efe5e75..000000000000
Binary files a/projects/1.18/assets/minecraft/minecraft/textures/font/unicode_page_e9.png and /dev/null differ
diff --git a/projects/1.18/pack.mcmeta b/projects/1.18/pack.mcmeta
index 732aec859891..d59e05b3e937 100644
--- a/projects/1.18/pack.mcmeta
+++ b/projects/1.18/pack.mcmeta
@@ -1,6 +1,6 @@
-{
- "pack": {
- "pack_format": 8,
- "description": "汉化万用包,仅供1.18Forge使用"
- }
-}
\ No newline at end of file
+{{
+ "pack": {{
+ "pack_format": 8,
+ "description": "汉化万用包,仅供1.18Forge使用/n打包时间:{0:yyyy-MM-ddTHH:mm:ssZ}"
+ }}
+}}
\ No newline at end of file
diff --git a/projects/1.19/README.md b/projects/1.19/README.md
deleted file mode 100644
index 6f79681c9b41..000000000000
--- a/projects/1.19/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-本资源包是适用于 Minecraft 1.19.4 的简体中文资源包,提供模组的简体中文本地化。
-
-署名:CFPA团队(完整贡献者名单:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package/graphs/contributors)
-
-项目主页:https://cfpa.site/
-参与贡献:https://cfpa.site/joinus.html
-反馈问题:https://support.qq.com/product/382723
-GitHub 仓库:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package
-
-
-如果你是手动下载的本资源包,欢迎使用 I18n Update Mod 模组自动下载安装资源包。
-
-I18n Update Mod 下载方式有
-MC百科:https://www.mcmod.cn/download/1188.html
-CurseForge:https://www.curseforge.com/minecraft/mc-mods/i18nupdatemod
-Modrinth:https://modrinth.com/mod/i18nupdatemod
\ No newline at end of file
diff --git a/projects/1.19/assets/0-example-mixed-clone/mixed-clone/extra/extra.txt b/projects/1.19/assets/0-example-mixed-clone/mixed-clone/extra/extra.txt
deleted file mode 100644
index ce7a60f9fb91..000000000000
--- a/projects/1.19/assets/0-example-mixed-clone/mixed-clone/extra/extra.txt
+++ /dev/null
@@ -1 +0,0 @@
-exexe exexe
\ No newline at end of file
diff --git a/projects/1.19/assets/0-example-mixed-clone/mixed-clone/lang/zh_cn.json b/projects/1.19/assets/0-example-mixed-clone/mixed-clone/lang/zh_cn.json
deleted file mode 100644
index e8b9d44b06fb..000000000000
--- a/projects/1.19/assets/0-example-mixed-clone/mixed-clone/lang/zh_cn.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "key1": "new value 1",
- "key3": "new value 3"
-}
\ No newline at end of file
diff --git a/projects/1.19/assets/0-example-mixed-clone/mixed-clone/packer-policy.json b/projects/1.19/assets/0-example-mixed-clone/mixed-clone/packer-policy.json
deleted file mode 100644
index dcfd344d8dfc..000000000000
--- a/projects/1.19/assets/0-example-mixed-clone/mixed-clone/packer-policy.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type": "clonemissing",
- "source": "./projects/1.19/assets/0-example-nop/nop"
-}
\ No newline at end of file
diff --git a/projects/1.19/assets/0-example-nop/nop/lang/zh_cn.json b/projects/1.19/assets/0-example-nop/nop/lang/zh_cn.json
deleted file mode 100644
index 9b24d09faca1..000000000000
--- a/projects/1.19/assets/0-example-nop/nop/lang/zh_cn.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "key1": "value1",
- "key2": "value2"
-}
\ No newline at end of file
diff --git a/projects/1.19/assets/0-example-nop/nop/packer-policy.json b/projects/1.19/assets/0-example-nop/nop/packer-policy.json
deleted file mode 100644
index e6043069e4d8..000000000000
--- a/projects/1.19/assets/0-example-nop/nop/packer-policy.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "type": "noaction"
-}
\ No newline at end of file
diff --git a/projects/1.19/assets/0-example-patch/patch/lang/zh_cn.json.patch b/projects/1.19/assets/0-example-patch/patch/lang/zh_cn.json.patch
deleted file mode 100644
index 831d1cf538b4..000000000000
--- a/projects/1.19/assets/0-example-patch/patch/lang/zh_cn.json.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-@@ -4,24 +4,28 @@
- %22key1%22: %22
-+new
- value1%22,%0A
-@@ -33,18 +33,22 @@
- %22key
--2
-+3
- %22: %22
-+new
- value
--2
-+3
- %22%0A%7D
\ No newline at end of file
diff --git a/projects/1.19/assets/0-example-patch/patch/packer-policy.json b/projects/1.19/assets/0-example-patch/patch/packer-policy.json
deleted file mode 100644
index a75e14b615d8..000000000000
--- a/projects/1.19/assets/0-example-patch/patch/packer-policy.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "type": "patch",
- "source": "./projects/1.19/assets/0-example-nop/nop",
- "patches": {
- "lang/zh_cn.json": "./projects/1.19/assets/0-example-patch/patch/lang/zh_cn.json.patch"
- }
-}
\ No newline at end of file
diff --git a/projects/1.19/assets/0-example-port/port/lang/zh_cn.json b/projects/1.19/assets/0-example-port/port/lang/zh_cn.json
deleted file mode 100644
index 245d5df201df..000000000000
--- a/projects/1.19/assets/0-example-port/port/lang/zh_cn.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "key1": "value1",
- "key4": "value4"
-}
\ No newline at end of file
diff --git a/projects/1.19/assets/0-example-port/port/packer-policy.json b/projects/1.19/assets/0-example-port/port/packer-policy.json
deleted file mode 100644
index 422748cc0dcf..000000000000
--- a/projects/1.19/assets/0-example-port/port/packer-policy.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type": "backport",
- "source": "./projects/1.19/assets/0-example-mixed-clone/mixed-clone"
-}
\ No newline at end of file
diff --git a/projects/1.19/assets/0-example-simple-clone/clone/packer-policy.json b/projects/1.19/assets/0-example-simple-clone/clone/packer-policy.json
deleted file mode 100644
index df2d5f484bc6..000000000000
--- a/projects/1.19/assets/0-example-simple-clone/clone/packer-policy.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type": "plainclone",
- "source": "./projects/1.19/assets/0-example-nop/nop"
-}
\ No newline at end of file
diff --git a/projects/1.19/assets/armor-poser/armorposer/packer-policy.json b/projects/1.19/assets/armor-poser/armorposer/packer-policy.json
index 29880705a989..9bd62a8b19c5 100644
--- a/projects/1.19/assets/armor-poser/armorposer/packer-policy.json
+++ b/projects/1.19/assets/armor-poser/armorposer/packer-policy.json
@@ -1,4 +1,6 @@
-{
- "type": "plainclone",
- "source": "./projects/1.18/assets/armor-poser/armorposer"
-}
\ No newline at end of file
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.18/assets/armor-poser/armorposer"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.19/assets/hourglass/hourglass/packer-policy.json b/projects/1.19/assets/hourglass/hourglass/packer-policy.json
index ef13d58a6b5f..76baceff7578 100644
--- a/projects/1.19/assets/hourglass/hourglass/packer-policy.json
+++ b/projects/1.19/assets/hourglass/hourglass/packer-policy.json
@@ -1,4 +1,6 @@
-{
- "type": "plainclone",
- "source": "./projects/1.18/assets/hourglass/hourglass"
-}
\ No newline at end of file
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.18/assets/hourglass/hourglass"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.19/assets/minecraft/minecraft/font/default.json b/projects/1.19/assets/minecraft/minecraft/font/default.json
new file mode 100644
index 000000000000..fad7c5d36609
--- /dev/null
+++ b/projects/1.19/assets/minecraft/minecraft/font/default.json
@@ -0,0 +1,230 @@
+{
+ "providers": [
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/element_ideographs.png",
+ "ascent": 7,
+ "chars": [
+ "\ud872\udf3b\ud872\udf4a\ud872\udf73\ud872\udf5b\ud872\udf76\u9fcf\ud86d\udffc",
+ "\ud872\udf2d\u9fd4\ud86d\udce7\ud86d\udff7\u9feb\u9fec\u9fed"
+ ]
+ },
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/cjk_punctuations.png",
+ "ascent": 7,
+ "chars": [
+ "\u3001\u3002\u300a\u300b\u3010\u3011\u2018\u2019\u201c\uff5e\u3008\u3009\u0000\u0000\u0000\u0000",
+ "\u201d\uff01\uff08\uff09\uff0c\uff1a\uff1b\uff1f\u2014\u00b7\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/ellipsis.png",
+ "ascent": 7,
+ "chars": [
+ "\u22ef"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/2em_dash.png",
+ "ascent": 7,
+ "chars": [
+ "\u2e3a"
+ ]
+ },
+ {
+ "type": "space",
+ "advances": {
+ " ": 4,
+ "\u200c": 0
+ }
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/nonlatin_european.png",
+ "ascent": 7,
+ "chars": [
+ "\u00a1\u2030\u00ad\u00b7\u20b4\u2260\u00bf\u00d7\u00d8\u00de\u04bb\u00f0\u00f8\u00fe\u0391\u0392",
+ "\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3",
+ "\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba",
+ "\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u0402",
+ "\u0405\u0406\u0408\u0409\u040a\u040b\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u041a",
+ "\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a",
+ "\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u043a\u043b",
+ "\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b",
+ "\u044c\u044d\u044e\u044f\u0454\u0455\u0456\u0458\u0459\u045a\u2013\u2014\u2018\u2019\u201c\u201d",
+ "\u201e\u2026\u204a\u2190\u2191\u2192\u2193\u21c4\uff0b\u018f\u0259\u025b\u026a\u04ae\u04af\u04e8",
+ "\u04e9\u02bb\u02cc\u037e\u0138\u1e9e\u00df\u20bd\u20ac\u0462\u0463\u0474\u0475\u04c0\u0472\u0473",
+ "\u2070\u00b9\u00b3\u2074\u2075\u2076\u2077\u2078\u2079\u207a\u207b\u207c\u207d\u207e\u2071\u2122",
+ "\u0294\u0295\u29c8\u2694\u2620\u049a\u049b\u0492\u0493\u04b0\u04b1\u04d8\u04d9\u0496\u0497\u04a2",
+ "\u04a3\u04ba\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05db\u05dc\u05de\u05dd",
+ "\u05e0\u05df\u05e1\u05e2\u05e4\u05e3\u05e6\u05e5\u05e7\u05e8\u00a2\u00a4\u00a5\u00a9\u00ae\u00b5",
+ "\u00b6\u00bc\u00bd\u00be\u0387\u2010\u201a\u2020\u2021\u2022\u2031\u2032\u2033\u2034\u2035\u2036",
+ "\u2037\u2039\u203a\u203b\u203c\u203d\u2042\u2048\u2049\u204b\u204e\u204f\u2051\u2052\u2057\u2117",
+ "\u2212\u2213\u221e\u2600\u2601\u2608\u0404\u2632\u2635\u263d\u2640\u2642\u26a5\u2660\u2663\u2665",
+ "\u2666\u2669\u266a\u266b\u266c\u266d\u266e\u266f\u2680\u2681\u2682\u2683\u2684\u2685\u02ac\u26a1",
+ "\u26cf\u2714\u2744\u274c\u2764\u2b50\u2e18\u2e2e\u2e35\u2e38\u2e41\u2e4b\u295d\u1614\u0190\u07c8",
+ "\u03db\u3125\u2c6f\u15fa\u0186\u15e1\u018e\u2132\u2141\ua7b0\ua780\u0500\ua779\u1d1a\u27d8\u2229",
+ "\u0245\u2144\u0250\u0254\u01dd\u025f\u1d77\u0265\u1d09\u027e\u029e\ua781\u026f\u0279\u0287\u028c",
+ "\u028d\u028e\u0531\u0532\u0533\u0534\u0536\u0537\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540",
+ "\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054b\u054c\u054d\u054e\u054f\u0550\u0551",
+ "\u0552\u0553\u0554\u0555\u0556\u0559\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a",
+ "\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a",
+ "\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u05e9\u05ea\u0538",
+ "\u055a\u055b\u055c\u055d\u055e\u055f\u0560\u0588\u058f\u00af\u017f\u01b7\u0292\u01f7\u01bf\u021c",
+ "\u021d\u0224\u0225\u02d9\ua75a\ua75b\u2011\u214b\u23cf\u23e9\u23ea\u23ed\u23ee\u23ef\u23f4\u23f5",
+ "\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u2b58\u25b2\u25b6\u25bc\u25c0\u25cf\u25e6\u25d8",
+ "\u2693\u26e8\u0132\u0133\u01c9\ua728\ua729\ua739\ua73b\ufb00\ufb01\ufb02\ufb03\ufb05\ufffd\u0535",
+ "\u054a\u16a0\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af",
+ "\u16b0\u16b1\u16b2\u16b3\u16b4\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0",
+ "\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0",
+ "\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0",
+ "\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u16eb\u16ec\u16ed\u16ee\u16ef\u16f0",
+ "\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u263a\u263b\u00a6\u2639\u05da\u05f3\u05f4\u05f0",
+ "\u05f1\u05f2\u05be\u05c3\u05c6\u00b4\u00a8\u1d00\u0299\u1d04\u1d05\u1d07\ua730\u0262\u029c\u1d0a",
+ "\u1d0b\u029f\u1d0d\u0274\u1d0f\u1d18\ua7af\u0280\ua731\u1d1b\u1d1c\u1d20\u1d21\u028f\u1d22\u00a7",
+ "\u0271\u0273\u0272\u0288\u0256\u0261\u02a1\u0255\u0291\u0278\u029d\u02a2\u027b\u0281\u0266\u028b",
+ "\u0270\u026c\u026e\u0298\u01c0\u01c3\u01c2\u01c1\u0253\u0257\u1d91\u0284\u0260\u029b\u0267\u026b",
+ "\u0268\u0289\u028a\u0258\u0275\u0264\u025c\u025e\u0251\u0252\u025a\u025d\u0181\u0189\u0191\u01a9",
+ "\u01b2\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae",
+ "\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be",
+ "\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u10c7\u10cd\u10d0\u10d1\u10d2\u10d3\u10d4\u10d5\u10d6",
+ "\u10d7\u10d8\u10d9\u10da\u10db\u10dc\u10dd\u10de\u10df\u10e0\u10e1\u10e2\u10e3\u10e4\u10e5\u10e6",
+ "\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef\u10f0\u10f1\u10f2\u10f3\u10f4\u10f5\u10f6",
+ "\u10f7\u10f8\u10f9\u10fa\u10fb\u10fc\u10fd\u10fe\u10ff\ufb4a\ufb2b\ufb4e\ufb44\ufb3b\ufb1f\ufb1d",
+ "\ufb4b\ufb35\ufb4c\ufb31\ua727\ua726\u027a\u2c71\u02a0\u0297\u0296\u026d\u0277\u027f\u0285\u0286",
+ "\u0293\u029a\u20aa\u20be\u058a\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d21\u2d07\u2d08\u2d09",
+ "\u2d0a\u2d0b\u2d0c\u2d22\u2d0d\u2d0e\u2d0f\u2d10\u2d11\u2d12\u2d23\u2d13\u2d14\u2d15\u2d16\u2d17",
+ "\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d24\u2d1f\u2d20\u2d25\u215b\u215c\u215d\u215e\u2153",
+ "\u2154\u2709\u2602\u2614\u2604\u26c4\u2603\u231b\u231a\u2690\u270e\u2763\u2664\u2667\u2661\u2662",
+ "\u26c8\u2630\u2631\u2633\u2634\u2636\u2637\u2194\u21d2\u21cf\u21d4\u21f5\u2200\u2203\u2204\u2209",
+ "\u220b\u220c\u2282\u2283\u2284\u2285\u2227\u2228\u22bb\u22bc\u22bd\u2225\u2262\u22c6\u2211\u22a4",
+ "\u22a5\u22a2\u22a8\u2254\u2201\u2234\u2235\u221b\u221c\u2202\u22c3\u2286\u2287\u25a1\u25b3\u25b7",
+ "\u25bd\u25c1\u25c6\u25c7\u25cb\u25ce\u2606\u2605\u2718\u2080\u2081\u2082\u2083\u2084\u2085\u2086",
+ "\u2087\u2088\u2089\u208a\u208b\u208c\u208d\u208e\u222b\u222e\u221d\u2300\u2302\u2318\u3012\u027c",
+ "\u0184\u0185\u1e9f\u023d\u019a\u019b\u0220\u019e\u019f\u01a7\u01a8\u01aa\u01b8\u01b9\u01bb\u01bc",
+ "\u01bd\u01be\u0221\u0234\u0235\u0236\u023a\u2c65\u023b\u023c\u0246\u0247\u023e\u2c66\u0241\u0242",
+ "\u0243\u0244\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u1e9c\u1e9d\u1efc\u1efd\u1efe\u1eff",
+ "\ua7a8\ua7a9\ud800\udf30\ud800\udf31\ud800\udf32\ud800\udf33\ud800\udf34\ud800\udf35\ud800\udf36\ud800\udf37\ud800\udf38\ud800\udf39\ud800\udf3a\ud800\udf3b\ud800\udf3c\ud800\udf3d",
+ "\ud800\udf3e\ud800\udf3f\ud800\udf40\ud800\udf41\ud800\udf42\ud800\udf43\ud800\udf44\ud800\udf45\ud800\udf46\ud800\udf47\ud800\udf48\ud800\udf49\ud800\udf4a\ud83c\udf27\ud83d\udd25\ud83c\udf0a",
+ "\u2150\u2151\u2155\u2156\u2157\u2159\u215a\u215f\u2189\ud83d\udde1\ud83c\udff9\ud83e\ude93\ud83d\udd31\ud83c\udfa3\ud83e\uddea\u2697",
+ "\u2bea\u2beb\u2c6d\ud83d\udee1\u2702\ud83c\udf56\ud83e\udea3\ud83d\udd14\u23f3\u2691\u20a0\u20a1\u20a2\u20a3\u20a4\u20a5",
+ "\u20a6\u20a9\u20ab\u20ad\u20ae\u20b0\u20b1\u20b2\u20b3\u20b5\u20b6\u20b7\u20b8\u20b9\u20ba\u20bb",
+ "\u20bc\u20bf\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/accented.png",
+ "height": 12,
+ "ascent": 10,
+ "chars": [
+ "\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf",
+ "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d9\u00da\u00db\u00dc\u00dd\u00e0\u00e1\u00e2\u00e3",
+ "\u00e4\u00e5\u00e6\u00e7\u00ec\u00ed\u00ee\u00ef\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f9\u00fa",
+ "\u00fb\u00fc\u00fd\u00ff\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b",
+ "\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0115\u0116\u0117\u0118\u0119\u011a\u011b",
+ "\u011c\u011d\u1e20\u1e21\u011e\u011f\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129",
+ "\u012a\u012b\u012c\u012d\u012e\u012f\u0130\u0131\u0134\u0135\u0136\u0137\u0139\u013a\u013b\u013c",
+ "\u013d\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u014a\u014b\u014c\u014d",
+ "\u014e\u014f\u0150\u0151\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b\u015c\u015d",
+ "\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d",
+ "\u016e\u016f\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179\u017a\u017b\u017c\u017d",
+ "\u017e\u01fc\u01fd\u01fe\u01ff\u0218\u0219\u021a\u021b\u0386\u0388\u0389\u038a\u038c\u038e\u038f",
+ "\u0390\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03ca\u03cb\u03cc\u03cd\u03ce\u0400\u0401\u0403",
+ "\u0407\u040c\u040d\u040e\u0419\u0439\u0450\u0451\u0452\u0453\u0457\u045b\u045c\u045d\u045e\u045f",
+ "\u0490\u0491\u1e02\u1e03\u1e0a\u1e0b\u1e1e\u1e1f\u1e22\u1e23\u1e30\u1e31\u1e40\u1e41\u1e56\u1e57",
+ "\u1e60\u1e61\u1e6a\u1e6b\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1ef2\u1ef3\u00e8\u00e9\u00ea\u00eb",
+ "\u0149\u01e7\u01eb\u040f\u1e0d\u1e25\u1e5b\u1e6d\u1e92\u1eca\u1ecb\u1ecc\u1ecd\u1ee4\u1ee5\u2116",
+ "\u0207\u0194\u0263\u0283\u2047\u01f1\u01f2\u01f3\u01c4\u01c5\u01c6\u01c7\u01c8\u01ca\u01cb\u01cc",
+ "\u2139\u1d6b\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua73a\ua73c\ua73d\ua74e\ua74f\ua760\ua761",
+ "\ufb04\ufb06\u16a1\u16b5\u01a0\u01a1\u01af\u01b0\u1eae\u1eaf\u1ea4\u1ea5\u1ebe\u1ebf\u1ed1\u1eda",
+ "\u1edb\u1ee8\u1ee9\u1eb0\u1eb1\u1ea6\u1ea7\u1ec0\u1ec1\u1ed3\u1edc\u1edd\u1eea\u1eeb\u1ea2\u1ea3",
+ "\u1eb2\u1eb3\u1ea8\u1ea9\u1eba\u1ebb\u1ed5\u1ede\u1ec2\u1ec3\u1ec8\u1ec9\u1ece\u1ecf\u1ed4\u1edf",
+ "\u1ee6\u1ee7\u1eec\u1eed\u1ef6\u1ef7\u1ea0\u1ea1\u1eb6\u1eb7\u1eac\u1ead\u1eb8\u1eb9\u1ec6\u1ec7",
+ "\u1ed8\u1ed9\u1ee2\u1ee3\u1ef0\u1ef1\u1ef4\u1ef5\u1ed0\u0195\u1eaa\u1eab\u1ed6\u1ed7\u1eef\u261e",
+ "\u261c\u262e\u1eb4\u1eb5\u1ebc\u1ebd\u1ec4\u1ec5\u1ed2\u1ee0\u1ee1\u1eee\u1ef8\u1ef9\u0498\u0499",
+ "\u04a0\u04a1\u04aa\u04ab\u01f6\u26a0\u24ea\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468",
+ "\u2469\u246a\u246b\u246c\u246d\u246e\u246f\u2470\u2471\u2472\u2473\u24b6\u24b7\u24b8\u24b9\u24ba",
+ "\u24bb\u24bc\u24bd\u24be\u24bf\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca",
+ "\u24cb\u24cc\u24cd\u24ce\u24cf\u24d0\u24d1\u24d2\u24d3\u24d4\u24d5\u24d6\u24d7\u24d8\u24d9\u24da",
+ "\u24db\u24dc\u24dd\u24de\u24df\u24e0\u24e1\u24e2\u24e3\u24e4\u24e5\u24e6\u24e7\u24e8\u24e9\u0327",
+ "\u0282\u0290\u0276\u01cd\u01ce\u01de\u01df\u01fa\u01fb\u0202\u0203\u0226\u0227\u01e0\u01e1\u1e00",
+ "\u1e01\u0200\u0201\u1e06\u1e07\u1e04\u1e05\u1d6c\u1e08\u1e09\u1e10\u1e11\u1e12\u1e13\u1e0e\u1e0f",
+ "\u1e0c\u1d6d\u1e14\u1e15\u1e16\u1e17\u1e18\u1e19\u1e1c\u1e1d\u0228\u0229\u1e1a\u1e1b\u0204\u0205",
+ "\u0206\u1d6e\u01f4\u01f5\u01e6\u1e26\u1e27\u1e28\u1e29\u1e2a\u1e2b\u021e\u021f\u1e24\u1e96\u1e2e",
+ "\u1e2f\u020a\u020b\u01cf\u01d0\u0208\u0209\u1e2c\u1e2d\u01f0\u0237\u01e8\u01e9\u1e32\u1e33\u1e34",
+ "\u1e35\u1e3a\u1e3b\u1e3c\u1e3d\u1e36\u1e37\u1e38\u1e39\u2c62\u1e3e\u1e3f\u1e42\u1e43\u1d6f\u1e44",
+ "\u1e45\u1e46\u1e47\u1e4a\u1e4b\u01f8\u01f9\u1e48\u1e49\u1d70\u01ec\u01ed\u022c\u022d\u1e4c\u1e4d",
+ "\u1e4e\u1e4f\u1e50\u1e51\u1e52\u1e53\u020e\u020f\u022a\u022b\u01d1\u01d2\u022e\u022f\u0230\u0231",
+ "\u020c\u020d\u01ea\u1e54\u1e55\u1d71\u0212\u0213\u1e58\u1e59\u1e5c\u1e5d\u1e5e\u1e5f\u0210\u0211",
+ "\u1e5a\u1d73\u1d72\u1e64\u1e65\u1e66\u1e67\u1e62\u1e63\u1e68\u1e69\u1d74\u1e70\u1e71\u1e6e\u1e6f",
+ "\u1e6c\u1e97\u1d75\u1e72\u1e73\u1e76\u1e77\u1e78\u1e79\u1e7a\u1e7b\u01d3\u01d4\u01d5\u01d6\u01d7",
+ "\u01d8\u01d9\u01da\u01db\u01dc\u1e74\u1e75\u0214\u0215\u0216\u1e7e\u1e7f\u1e7c\u1e7d\u1e86\u1e87",
+ "\u1e88\u1e89\u1e98\u1e8c\u1e8d\u1e8a\u1e8b\u0232\u0233\u1e8e\u1e8f\u1e99\u1e94\u1e95\u1e90\u1e91",
+ "\u1e93\u1d76\u01ee\u01ef\u1e9b\ua73e\ua73f\u01e2\u01e3\u1d7a\u1efb\u1d02\u1d14\uab63\u0238\u02a3",
+ "\u02a5\u02a4\u02a9\u02aa\u02ab\u0239\u02a8\u02a6\u02a7\uab50\uab51\u20a7\u1efa\ufb2e\ufb2f\u0180",
+ "\u0182\u0183\u0187\u0188\u018a\u018b\u018c\u0193\u01e4\u01e5\u0197\u0196\u0269\u0198\u0199\u019d",
+ "\u01a4\u01a5\u027d\u01a6\u01ac\u01ad\u01ab\u01ae\u0217\u01b1\u019c\u01b3\u01b4\u01b5\u01b6\u01a2",
+ "\u01a3\u0222\u0223\u02ad\u02ae\u02af\ufb14\ufb15\ufb17\ufb16\ufb13\u04d0\u04d1\u04d2\u04d3\u04f6",
+ "\u04f7\u0494\u0495\u04d6\u04d7\u04bc\u04bd\u04be\u04bf\u04da\u04db\u04dc\u04dd\u04c1\u04c2\u04de",
+ "\u04df\u04e2\u04e3\u04e4\u04e5\u04e6\u04e7\u04ea\u04eb\u04f0\u04f1\u04ee\u04ef\u04f2\u04f3\u04f4",
+ "\u04f5\u04f8\u04f9\u04ec\u04ed\u0476\u0477\u04d4\u04fa\u0502\ua682\ua680\ua688\u052a\u052c\ua684",
+ "\u0504\u0510\u04e0\u0506\u048a\u04c3\u049e\u049c\u051e\u051a\u04c5\u052e\u0512\u0520\u0508\u0514",
+ "\u04cd\u04c9\u0528\u04c7\u04a4\u0522\u050a\u04a8\u0524\u04a6\u048e\u0516\u050c\ua690\u04ac\ua68a",
+ "\ua68c\u050e\u04b2\u04fc\u04fe\u0526\ua694\u04b4\ua68e\u04b6\u04cb\u04b8\ua692\ua696\ua686\u048c",
+ "\u0518\u051c\u04d5\u04fb\u0503\ua683\ua681\ua689\u052b\u052d\ua685\u0505\u0511\u04e1\u0507\u048b",
+ "\u04c4\u049f\u049d\u051f\u051b\u04c6\u052f\u0513\u0521\u0509\u0515\u04ce\u04ca\u0529\u04c8\u04a5",
+ "\u0523\u050b\u04a9\u0525\u04a7\u048f\u0517\u050d\ua691\u04ad\ua68b\ua68d\u050f\u04b3\u04fd\u04ff",
+ "\u0527\ua695\u04b5\ua68f\u04b7\u04cc\u04b9\ua693\ua697\ua687\u048d\u0519\u051d\u1f08\u1f00\u1f09",
+ "\u1f01\u1f0a\u1f02\u1f0b\u1f03\u1f0c\u1f04\u1f0d\u1f05\u1f0e\u1f06\u1f0f\u1f07\u1fba\u1f70\u1fb8",
+ "\u1fb0\u1fb9\u1fb1\u1fbb\u1f71\u1f88\u1f80\u1f89\u1f81\u1f8a\u1f82\u1f8b\u1f83\u1f8c\u1f84\u1f8d",
+ "\u1f85\u1f8e\u1f86\u1f8f\u1f87\u1fbc\u1fb4\u1fb6\u1fb7\u1fb2\u1fb3\u1f18\u1f10\u1f19\u1f11\u1f1a",
+ "\u1f12\u1f1b\u1f13\u1f1c\u1f14\u1f1d\u1f15\u1fc8\u1fc9\u1f72\u1f73\u1f28\u1f20\u1fca\u1f74\u1f29",
+ "\u1f21\u1f2a\u1f22\u1f2b\u1f23\u1f2c\u1f24\u1f2d\u1f25\u1f2e\u1f26\u1f2f\u1f27\u1f98\u1f90\u1f99",
+ "\u1f91\u1f9a\u1f92\u1f9b\u1f93\u1f9c\u1f94\u1f9d\u1f95\u1f9e\u1f96\u1f9f\u1f97\u1fcb\u1f75\u1fcc",
+ "\u1fc3\u1fc2\u1fc4\u1fc6\u1fc7\u1fda\u1f76\u1fdb\u1f77\u1f38\u1f30\u1f39\u1f31\u1f3a\u1f32\u1f3b",
+ "\u1f33\u1f3c\u1f34\u1f3d\u1f35\u1f3e\u1f36\u1f3f\u1f37\u1fd8\u1fd0\u1fd9\u1fd1\u1fd2\u1fd3\u1fd6",
+ "\u1fd7\u1ff8\u1f78\u1ff9\u1f79\u1f48\u1f40\u1f49\u1f41\u1f4a\u1f42\u1f4b\u1f43\u1f4c\u1f44\u1f4d",
+ "\u1f45\u1fec\u1fe4\u1fe5\u1fea\u1f7a\u1feb\u1f7b\u1f59\u1f51\u1f5b\u1f53\u1f5d\u1f55\u1f5f\u1f57",
+ "\u1fe8\u1fe0\u1fe9\u1fe1\u03d3\u03d4\u1fe2\u1fe3\u1fe7\u1f50\u1f52\u1f54\u1fe6\u1f56\u1ffa\u1f7c",
+ "\u1ffb\u1f7d\u1f68\u1f60\u1f69\u1f61\u1f6a\u1f62\u1f6b\u1f63\u1f6c\u1f64\u1f6d\u1f65\u1f6e\u1f66",
+ "\u1f6f\u1f67\u1fa8\u1fa0\u1fa9\u1fa1\u1faa\u1fa2\u1fab\u1fa3\u1fac\u1fa4\u1fad\u1fa5\u1fae\u1fa6",
+ "\u1faf\u1fa7\u1ffc\u1ff3\u1ff2\u1ff4\u1ff6\u1ff7\u262f\u2610\u2611\u2612\u018d\u01ba\u2c7e\u023f",
+ "\u2c7f\u0240\u1d80\ua7c4\ua794\u1d81\u1d82\u1d83\ua795\u1d84\u1d85\u1d86\u1d87\u1d88\u1d89\u1d8a",
+ "\u1d8b\u1d8c\u1d8d\ua7c6\u1d8e\u1d8f\u1d90\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a",
+ "\u1e9a\u2152\u2158\u20a8\u20af\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/ascii.png",
+ "ascent": 7,
+ "chars": [
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
+ "\u0020\u0021\u0022\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002a\u002b\u002c\u002d\u002e\u002f",
+ "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003a\u003b\u003c\u003d\u003e\u003f",
+ "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f",
+ "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a\u005b\u005c\u005d\u005e\u005f",
+ "\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f",
+ "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u007b\u007c\u007d\u007e\u0000",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u00a3\u0000\u0000\u0192",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u00aa\u00ba\u0000\u0000\u00ac\u0000\u0000\u0000\u00ab\u00bb",
+ "\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510",
+ "\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567",
+ "\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580",
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u2205\u2208\u0000",
+ "\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u0000\u221a\u207f\u00b2\u25a0\u0000"
+ ]
+ },
+ {
+ "type": "legacy_unicode",
+ "sizes": "minecraft:font/glyph_sizes.bin",
+ "template": "minecraft:font/unicode_page_%s.png"
+ }
+ ]
+}
diff --git a/projects/1.19/assets/minecraft/minecraft/font/glyph_sizes.bin b/projects/1.19/assets/minecraft/minecraft/font/glyph_sizes.bin
deleted file mode 100644
index 0503133d08ef..000000000000
Binary files a/projects/1.19/assets/minecraft/minecraft/font/glyph_sizes.bin and /dev/null differ
diff --git a/projects/1.19/assets/minecraft/minecraft/font/uniform.json b/projects/1.19/assets/minecraft/minecraft/font/uniform.json
new file mode 100644
index 000000000000..ace87244aefc
--- /dev/null
+++ b/projects/1.19/assets/minecraft/minecraft/font/uniform.json
@@ -0,0 +1,50 @@
+{
+ "providers": [
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/element_ideographs.png",
+ "ascent": 7,
+ "chars": [
+ "\ud872\udf3b\ud872\udf4a\ud872\udf73\ud872\udf5b\ud872\udf76\u9fcf\ud86d\udffc",
+ "\ud872\udf2d\u9fd4\ud86d\udce7\ud86d\udff7\u9feb\u9fec\u9fed"
+ ]
+ },
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/cjk_punctuations.png",
+ "ascent": 7,
+ "chars": [
+ "\u3001\u3002\u300a\u300b\u3010\u3011\u2018\u2019\u201c\uff5e\u3008\u3009\u0000\u0000\u0000\u0000",
+ "\u201d\uff01\uff08\uff09\uff0c\uff1a\uff1b\uff1f\u2014\u00b7\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/ellipsis.png",
+ "ascent": 7,
+ "chars": [
+ "\u22ef"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/2em_dash.png",
+ "ascent": 7,
+ "chars": [
+ "\u2e3a"
+ ]
+ },
+ {
+ "type": "space",
+ "advances": {
+ " ": 4,
+ "\u200c": 0
+ }
+ },
+ {
+ "type": "legacy_unicode",
+ "sizes": "minecraft:font/glyph_sizes.bin",
+ "template": "minecraft:font/unicode_page_%s.png"
+ }
+ ]
+}
diff --git a/projects/1.19/assets/minecraft/minecraft/packer-policy.json b/projects/1.19/assets/minecraft/minecraft/packer-policy.json
new file mode 100644
index 000000000000..36653b700ae5
--- /dev/null
+++ b/projects/1.19/assets/minecraft/minecraft/packer-policy.json
@@ -0,0 +1,9 @@
+[
+ {
+ "type": "direct"
+ },
+ {
+ "type": "indirect",
+ "source": "./projects/1.16/assets/minecraft/minecraft"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.19/assets/minecraft/minecraft/textures/font/unicode_page_20.png b/projects/1.19/assets/minecraft/minecraft/textures/font/unicode_page_20.png
deleted file mode 100644
index 8fc8524241c7..000000000000
Binary files a/projects/1.19/assets/minecraft/minecraft/textures/font/unicode_page_20.png and /dev/null differ
diff --git a/projects/1.19/assets/minecraft/minecraft/textures/font/unicode_page_9f.png b/projects/1.19/assets/minecraft/minecraft/textures/font/unicode_page_9f.png
deleted file mode 100644
index cee8a138a6d5..000000000000
Binary files a/projects/1.19/assets/minecraft/minecraft/textures/font/unicode_page_9f.png and /dev/null differ
diff --git a/projects/1.19/assets/minecraft/minecraft/textures/font/unicode_page_e9.png b/projects/1.19/assets/minecraft/minecraft/textures/font/unicode_page_e9.png
deleted file mode 100644
index e0762efe5e75..000000000000
Binary files a/projects/1.19/assets/minecraft/minecraft/textures/font/unicode_page_e9.png and /dev/null differ
diff --git a/projects/1.19/assets/shield-expansion/shieldexp/packer-policy.json b/projects/1.19/assets/shield-expansion/shieldexp/packer-policy.json
index 85161b17e85f..eab3d9593b71 100644
--- a/projects/1.19/assets/shield-expansion/shieldexp/packer-policy.json
+++ b/projects/1.19/assets/shield-expansion/shieldexp/packer-policy.json
@@ -1,4 +1,6 @@
-{
- "type": "plainclone",
- "source": "./projects/1.18/assets/shield-expansion/shieldexp"
-}
\ No newline at end of file
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.18/assets/shield-expansion/shieldexp"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.19/pack.mcmeta b/projects/1.19/pack.mcmeta
index d31a3c5da190..6694effb280d 100644
--- a/projects/1.19/pack.mcmeta
+++ b/projects/1.19/pack.mcmeta
@@ -1,6 +1,6 @@
-{
- "pack": {
+{{
+ "pack": {{
"pack_format": 13,
- "description": "汉化万用包,仅供1.19.4使用"
- }
-}
\ No newline at end of file
+ "description": "汉化万用包,仅供1.19.4使用/n打包时间:{0:yyyy-MM-ddTHH:mm:ssZ}"
+ }}
+}}
\ No newline at end of file
diff --git a/projects/1.20-fabric/README.md b/projects/1.20-fabric/README.md
deleted file mode 100644
index fc311ad9360e..000000000000
--- a/projects/1.20-fabric/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-本资源包是适用于 Minecraft 1.20.1 Fabric 的简体中文资源包,提供模组的简体中文本地化。
-
-署名:CFPA团队(完整贡献者名单:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package/graphs/contributors)
-
-项目主页:https://cfpa.site/
-参与贡献:https://cfpa.site/joinus.html
-反馈问题:https://support.qq.com/product/382723
-GitHub 仓库:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package
-
-
-如果你是手动下载的本资源包,欢迎使用 I18n Update Mod 模组自动下载安装资源包。
-
-I18n Update Mod 下载方式有
-MC百科:https://www.mcmod.cn/download/1188.html
-CurseForge:https://www.curseforge.com/minecraft/mc-mods/i18nupdatemod
-Modrinth:https://modrinth.com/mod/i18nupdatemod
\ No newline at end of file
diff --git a/projects/1.20-fabric/assets/minecraft/minecraft/packer-policy.json b/projects/1.20-fabric/assets/minecraft/minecraft/packer-policy.json
new file mode 100644
index 000000000000..a0802b736d3f
--- /dev/null
+++ b/projects/1.20-fabric/assets/minecraft/minecraft/packer-policy.json
@@ -0,0 +1,6 @@
+[
+ {
+ "type": "indirect",
+ "source": "./projects/1.20/assets/minecraft/minecraft"
+ }
+]
\ No newline at end of file
diff --git a/projects/1.20-fabric/pack.mcmeta b/projects/1.20-fabric/pack.mcmeta
index e05fae8f9262..bce9c05f5259 100644
--- a/projects/1.20-fabric/pack.mcmeta
+++ b/projects/1.20-fabric/pack.mcmeta
@@ -1,6 +1,6 @@
-{
- "pack": {
+{{
+ "pack": {{
"pack_format": 15,
- "description": "汉化万用包,仅供1.20.1Fabric使用"
- }
-}
\ No newline at end of file
+ "description": "汉化万用包,仅供1.20.1Fabric使用/n打包时间:{0:yyyy-MM-ddTHH:mm:ssZ}"
+ }}
+}}
\ No newline at end of file
diff --git a/projects/1.20/README.md b/projects/1.20/README.md
deleted file mode 100644
index d1a2a8d27344..000000000000
--- a/projects/1.20/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-本资源包是适用于 Minecraft 1.20.1 Forge 的简体中文资源包,提供模组的简体中文本地化。
-
-署名:CFPA团队(完整贡献者名单:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package/graphs/contributors)
-
-项目主页:https://cfpa.site/
-参与贡献:https://cfpa.site/joinus.html
-反馈问题:https://support.qq.com/product/382723
-GitHub 仓库:https://github.com/CFPAOrg/Minecraft-Mod-Language-Package
-
-
-如果你是手动下载的本资源包,欢迎使用 I18n Update Mod 模组自动下载安装资源包。
-
-I18n Update Mod 下载方式有
-MC百科:https://www.mcmod.cn/download/1188.html
-CurseForge:https://www.curseforge.com/minecraft/mc-mods/i18nupdatemod
-Modrinth:https://modrinth.com/mod/i18nupdatemod
\ No newline at end of file
diff --git a/projects/1.20/assets/entangled/entangled/lang/en_us.json b/projects/1.20/assets/entangled/entangled/lang/en_us.json
index edcfb8aa7e37..f654fbd87da4 100644
--- a/projects/1.20/assets/entangled/entangled/lang/en_us.json
+++ b/projects/1.20/assets/entangled/entangled/lang/en_us.json
@@ -1,24 +1,24 @@
-{
- "entangled.block.block": "Entangled Block",
- "entangled.item.item": "Entangled Binder",
- "entangled.entangled_block.info.ranged_same_dimension": "Can be bound to blocks in the same dimension up to %d blocks away",
- "entangled.entangled_block.info.infinite_same_dimension": "Can be bound to blocks in the same dimension",
- "entangled.entangled_block.info.ranged_other_dimension": "Can be bound to blocks up to %d blocks away or to blocks in other dimensions",
- "entangled.entangled_block.info.infinite_other_dimension": "Can be bound to other blocks",
- "entangled.entangled_block.info.bound": "Bound to %1$s in the %5$s at (%2$d, %3$d, %4$d)",
- "entangled.entangled_block.unbind": "Block unbound!",
- "entangled.entangled_block.no_selection": "No block selected!",
- "entangled.entangled_block.self": "Can't bind a block to itself!",
- "entangled.entangled_block.bind": "Block bound!",
- "entangled.entangled_block.wrong_dimension": "The targeted block must be in the same dimension!",
- "entangled.entangled_block.too_far": "The targeted block is too far away!",
- "entangled.entangled_binder.info": "Can bind entangled blocks to other blocks",
- "entangled.entangled_binder.info.target.unknown": "Linked to a block in the %4$s at (%1$d, %2$d, %3$d)",
- "entangled.entangled_binder.info.target.known": "Linked to %1$s in the %5$s at (%2$d, %3$d, %4$d)",
- "entangled.entangled_binder.select": "Block selected!",
- "entangled.entangled_binder.clear": "Connection cleared!",
- "entangled.waila.bound_same_dimension": "Bound to %1$s at (%2$d, %3$d, %4$d)",
- "entangled.waila.bound_other_dimension": "Bound to %1$s in the %5$s at (%2$d, %3$d, %4$d)",
- "entangled.waila.unbound": "Unbound",
- "config.jade.plugin_entangled.entangled_block_component": "Entangled Block"
+{
+ "entangled.block.block": "Entangled Block",
+ "entangled.item.item": "Entangled Binder",
+ "entangled.entangled_block.info.ranged_same_dimension": "Can be bound to blocks in the same dimension up to %d blocks away",
+ "entangled.entangled_block.info.infinite_same_dimension": "Can be bound to blocks in the same dimension",
+ "entangled.entangled_block.info.ranged_other_dimension": "Can be bound to blocks up to %d blocks away or to blocks in other dimensions",
+ "entangled.entangled_block.info.infinite_other_dimension": "Can be bound to other blocks",
+ "entangled.entangled_block.info.bound": "Bound to %1$s in the %5$s at (%2$d, %3$d, %4$d)",
+ "entangled.entangled_block.unbind": "Block unbound!",
+ "entangled.entangled_block.no_selection": "No block selected!",
+ "entangled.entangled_block.self": "Can't bind a block to itself!",
+ "entangled.entangled_block.bind": "Block bound!",
+ "entangled.entangled_block.wrong_dimension": "The targeted block must be in the same dimension!",
+ "entangled.entangled_block.too_far": "The targeted block is too far away!",
+ "entangled.entangled_binder.info": "Can bind entangled blocks to other blocks",
+ "entangled.entangled_binder.info.target.unknown": "Linked to a block in the %4$s at (%1$d, %2$d, %3$d)",
+ "entangled.entangled_binder.info.target.known": "Linked to %1$s in the %5$s at (%2$d, %3$d, %4$d)",
+ "entangled.entangled_binder.select": "Block selected!",
+ "entangled.entangled_binder.clear": "Connection cleared!",
+ "entangled.waila.bound_same_dimension": "Bound to %1$s at (%2$d, %3$d, %4$d)",
+ "entangled.waila.bound_other_dimension": "Bound to %1$s in the %5$s at (%2$d, %3$d, %4$d)",
+ "entangled.waila.unbound": "Unbound",
+ "config.jade.plugin_entangled.entangled_block_component": "Entangled Block"
}
\ No newline at end of file
diff --git a/projects/1.20/assets/entangled/entangled/lang/zh_cn.json b/projects/1.20/assets/entangled/entangled/lang/zh_cn.json
index 6baf30baf905..823de88a6f78 100644
--- a/projects/1.20/assets/entangled/entangled/lang/zh_cn.json
+++ b/projects/1.20/assets/entangled/entangled/lang/zh_cn.json
@@ -1,24 +1,24 @@
-{
- "entangled.block.block": "纠缠方块",
- "entangled.item.item": "纠缠绑定器",
- "entangled.entangled_block.info.ranged_same_dimension": "可以绑定到同一维度内最远%d格的方块",
- "entangled.entangled_block.info.infinite_same_dimension": "可以绑定到同一维度内的方块",
- "entangled.entangled_block.info.ranged_other_dimension": "可以绑定到同一维度内最远%d格的方块或其他维度的方块",
- "entangled.entangled_block.info.infinite_other_dimension": "可以绑定到其他方块",
- "entangled.entangled_block.info.bound": "已绑定至位于%5$s维度(%2$d,%3$d,%4$d)的%1$s",
- "entangled.entangled_block.unbind": "方块已解绑!",
- "entangled.entangled_block.no_selection": "未选择方块!",
- "entangled.entangled_block.self": "不能绑定方块本身!",
- "entangled.entangled_block.bind": "方块已绑定!",
- "entangled.entangled_block.wrong_dimension": "目标方块必须在同一维度!",
- "entangled.entangled_block.too_far": "目标方块距离过远!",
- "entangled.entangled_binder.info": "可以将纠缠方块与其他方块绑定",
- "entangled.entangled_binder.info.target.unknown": "连接到位于%4$s维度(%1$d,%2$d,%3$d)的方块",
- "entangled.entangled_binder.info.target.known": "已连接到位于%5$s维度(%2$d,%3$d,%4$d)的%1$s",
- "entangled.entangled_binder.select": "方块已选择!",
- "entangled.entangled_binder.clear": "连接已清除!",
- "entangled.waila.bound_same_dimension": "已绑定至(%2$d,%3$d,%4$d)的%1$s",
- "entangled.waila.bound_other_dimension": "已绑定至位于%5$s维度(%2$d,%3$d,%4$d)的%1$s",
- "entangled.waila.unbound": "未绑定",
- "config.jade.plugin_entangled.entangled_block_component": "纠缠方块"
+{
+ "entangled.block.block": "纠缠方块",
+ "entangled.item.item": "纠缠绑定器",
+ "entangled.entangled_block.info.ranged_same_dimension": "可以绑定到同一维度内最远%d格的方块",
+ "entangled.entangled_block.info.infinite_same_dimension": "可以绑定到同一维度内的方块",
+ "entangled.entangled_block.info.ranged_other_dimension": "可以绑定到同一维度内最远%d格的方块或其他维度的方块",
+ "entangled.entangled_block.info.infinite_other_dimension": "可以绑定到其他方块",
+ "entangled.entangled_block.info.bound": "已绑定至位于%5$s维度(%2$d,%3$d,%4$d)的%1$s",
+ "entangled.entangled_block.unbind": "方块已解绑!",
+ "entangled.entangled_block.no_selection": "未选择方块!",
+ "entangled.entangled_block.self": "不能绑定方块本身!",
+ "entangled.entangled_block.bind": "方块已绑定!",
+ "entangled.entangled_block.wrong_dimension": "目标方块必须在同一维度!",
+ "entangled.entangled_block.too_far": "目标方块距离过远!",
+ "entangled.entangled_binder.info": "可以将纠缠方块与其他方块绑定",
+ "entangled.entangled_binder.info.target.unknown": "连接到位于%4$s维度(%1$d,%2$d,%3$d)的方块",
+ "entangled.entangled_binder.info.target.known": "已连接到位于%5$s维度(%2$d,%3$d,%4$d)的%1$s",
+ "entangled.entangled_binder.select": "方块已选择!",
+ "entangled.entangled_binder.clear": "连接已清除!",
+ "entangled.waila.bound_same_dimension": "已绑定至(%2$d,%3$d,%4$d)的%1$s",
+ "entangled.waila.bound_other_dimension": "已绑定至位于%5$s维度(%2$d,%3$d,%4$d)的%1$s",
+ "entangled.waila.unbound": "未绑定",
+ "config.jade.plugin_entangled.entangled_block_component": "纠缠方块"
}
\ No newline at end of file
diff --git a/projects/1.20/assets/minecraft/minecraft/font/default.json b/projects/1.20/assets/minecraft/minecraft/font/default.json
new file mode 100644
index 000000000000..475571c9e600
--- /dev/null
+++ b/projects/1.20/assets/minecraft/minecraft/font/default.json
@@ -0,0 +1,20 @@
+{
+ "providers": [
+ {
+ "type": "reference",
+ "id": "minecraft:include/cjk-punctuations"
+ },
+ {
+ "type": "reference",
+ "id": "minecraft:include/space"
+ },
+ {
+ "type": "reference",
+ "id": "minecraft:include/default"
+ },
+ {
+ "type": "reference",
+ "id": "minecraft:include/unifont"
+ }
+ ]
+}
diff --git a/projects/1.20/assets/minecraft/minecraft/font/include/cjk-punctuations.json b/projects/1.20/assets/minecraft/minecraft/font/include/cjk-punctuations.json
new file mode 100644
index 000000000000..8a18537c968a
--- /dev/null
+++ b/projects/1.20/assets/minecraft/minecraft/font/include/cjk-punctuations.json
@@ -0,0 +1,29 @@
+{
+ "providers":[
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/cjk_punctuations.png",
+ "ascent": 7,
+ "chars": [
+ "\u3001\u3002\u300a\u300b\u3010\u3011\u2018\u2019\u201c\u0000\u3008\u3009\u0000\u0000\u0000\u0000",
+ "\u201d\uff01\uff08\uff09\uff0c\uff1a\uff1b\uff1f\u2014\u00b7\u0000\u0000\u0000\u0000\u0000\u0000"
+ ]
+ },
+ {
+ "type":"bitmap",
+ "file": "minecraft:font/ellipsis.png",
+ "ascent": 7,
+ "chars": [
+ "\u22ef"
+ ]
+ },
+ {
+ "type": "bitmap",
+ "file": "minecraft:font/2em_dash.png",
+ "ascent": 7,
+ "chars": [
+ "\u2e3a"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/projects/1.20/assets/minecraft/minecraft/font/uniform.json b/projects/1.20/assets/minecraft/minecraft/font/uniform.json
new file mode 100644
index 000000000000..32d3ee8fdb14
--- /dev/null
+++ b/projects/1.20/assets/minecraft/minecraft/font/uniform.json
@@ -0,0 +1,16 @@
+{
+ "providers": [
+ {
+ "type": "reference",
+ "id": "minecraft:include/cjk-punctuations"
+ },
+ {
+ "type": "reference",
+ "id": "minecraft:include/space"
+ },
+ {
+ "type": "reference",
+ "id": "minecraft:include/unifont"
+ }
+ ]
+}
diff --git a/projects/1.19/assets/minecraft/minecraft/readme.md b/projects/1.20/assets/minecraft/minecraft/readme.md
similarity index 100%
rename from projects/1.19/assets/minecraft/minecraft/readme.md
rename to projects/1.20/assets/minecraft/minecraft/readme.md
diff --git a/projects/1.20/assets/minecraft/minecraft/textures/font/2em_dash.png b/projects/1.20/assets/minecraft/minecraft/textures/font/2em_dash.png
new file mode 100644
index 000000000000..e8291ce5ab02
Binary files /dev/null and b/projects/1.20/assets/minecraft/minecraft/textures/font/2em_dash.png differ
diff --git a/projects/1.20/assets/minecraft/minecraft/textures/font/cjk_punctuations.png b/projects/1.20/assets/minecraft/minecraft/textures/font/cjk_punctuations.png
new file mode 100644
index 000000000000..7ad25d1665c0
Binary files /dev/null and b/projects/1.20/assets/minecraft/minecraft/textures/font/cjk_punctuations.png differ
diff --git a/projects/1.20/assets/minecraft/minecraft/textures/font/ellipsis.png b/projects/1.20/assets/minecraft/minecraft/textures/font/ellipsis.png
new file mode 100644
index 000000000000..70a21a0b5e07
Binary files /dev/null and b/projects/1.20/assets/minecraft/minecraft/textures/font/ellipsis.png differ
diff --git a/projects/1.20/assets/more-crossbows-cj/morecrossbows/lang/zh_cn.json b/projects/1.20/assets/more-crossbows-cj/morecrossbows/lang/zh_cn.json
index 93ba3bcdc0c2..6496e10d5ff2 100644
--- a/projects/1.20/assets/more-crossbows-cj/morecrossbows/lang/zh_cn.json
+++ b/projects/1.20/assets/more-crossbows-cj/morecrossbows/lang/zh_cn.json
@@ -1,40 +1,40 @@
-{
- "item。morecrossbows.copper_crossbow": "铜弩",
- "item。morecrossbows.iron_crossbow": "铁弩",
- "item.morecrossbows.golden_crossbow": "金弩",
- "item.morecrossbows.emerald_crossbow": "绿宝石弩",
- "item.morecrossbows.diamond_crossbow": "钻石弩",
- "item.morecrossbows。netherite_crossbow": "下界合金弩",
- "item.morecrossbows.gilded_netherite_crossbow": "镀金下界合金弩",
- "item.morecrossbows.enderite_crossbow": "末影合金弩",
- "item.morecrossbows.gilded_enderite_crossbow": "镀金末影合金弩",
-
- "upgrade.morecrossbows.crossbow_upgrade": "弩升级",
- "item.morecrossbows.smithing_template.crossbow_upgrade.additions_slot_description": "把下一级材料的块放在这里。",
- "item.morecrossbows.smithing_template.crossbow_upgrade.applies_to": "弩",
- "item.morecrossbows.smithing_template.crossbow_upgrade.base_slot_description": "在这里放一把弩。",
- "item.morecrossbows.smithing_template.crossbow_upgrade.ingredients": "铜、铁、金、绿宝石、钻石块",
-
- "tooltip.morecrossbows.damage_tooltip": "弹射物伤害",
- "tooltip.morecrossbows.speed_multiplier": "速度倍率",
- "tooltip.morecrossbows.charge_time": "蓄力时间",
-
- "advancements.adventure.morecrossbows.obtain_copper_crossbow.title": "铜弩",
- "advancements.adventure.morecrossbows.obtain_copper_crossbow.description": "铜做的弩!",
- "advancements.adventure.morecrossbows.obtain_iron_crossbow.title": "铁弩",
- "advancements.adventure.morecrossbows.obtain_iron_crossbow.description": "铁做的弩!",
- "advancements.adventure.morecrossbows.obtain_golden_crossbow.title": "金弩",
- "advancements.adventure.morecrossbows.obtain_golden_crossbow.description": "金做的弩!",
- "advancements.adventure.morecrossbows.obtain_emerald_crossbow.title": "绿宝石弩",
- "advancements.adventure.morecrossbows.obtain_emerald_crossbow.description": "绿宝石做的弩!",
- "advancements.adventure.morecrossbows.obtain_diamond_crossbow.title": "钻石弩",
- "advancements.adventure.morecrossbows.obtain_diamond_crossbow.description": "钻石做的弩!",
- "advancements.adventure.morecrossbows.obtain_netherite_crossbow.title": "下界合金弩",
- "advancements.adventure.morecrossbows.obtain_netherite_crossbow.description": "下界合金做的弩!",
- "advancements.adventure.morecrossbows.obtain_gilded_netherite_crossbow.title": "镀金下界合金做的弩",
- "advancements.adventure.morecrossbows.obtain_gilded_netherite_crossbow.description": "镀金下界合金做的弩!",
- "advancements.adventure.morecrossbows.obtain_enderite_crossbow.title": "末影合金弩",
- "advancements.adventure.morecrossbows.obtain_enderite_crossbow.description": "末影合金做的弩!",
- "advancements.adventure.morecrossbows.obtain_gilded_enderite_crossbow.title": "镀金末影合金做的弩",
- "advancements.adventure.morecrossbows.obtain_gilded_enderite_crossbow.description": "镀金末影合金做的弩!"
+{
+ "item。morecrossbows.copper_crossbow": "铜弩",
+ "item。morecrossbows.iron_crossbow": "铁弩",
+ "item.morecrossbows.golden_crossbow": "金弩",
+ "item.morecrossbows.emerald_crossbow": "绿宝石弩",
+ "item.morecrossbows.diamond_crossbow": "钻石弩",
+ "item.morecrossbows。netherite_crossbow": "下界合金弩",
+ "item.morecrossbows.gilded_netherite_crossbow": "镀金下界合金弩",
+ "item.morecrossbows.enderite_crossbow": "末影合金弩",
+ "item.morecrossbows.gilded_enderite_crossbow": "镀金末影合金弩",
+
+ "upgrade.morecrossbows.crossbow_upgrade": "弩升级",
+ "item.morecrossbows.smithing_template.crossbow_upgrade.additions_slot_description": "把下一级材料的块放在这里。",
+ "item.morecrossbows.smithing_template.crossbow_upgrade.applies_to": "弩",
+ "item.morecrossbows.smithing_template.crossbow_upgrade.base_slot_description": "在这里放一把弩。",
+ "item.morecrossbows.smithing_template.crossbow_upgrade.ingredients": "铜、铁、金、绿宝石、钻石块",
+
+ "tooltip.morecrossbows.damage_tooltip": "弹射物伤害",
+ "tooltip.morecrossbows.speed_multiplier": "速度倍率",
+ "tooltip.morecrossbows.charge_time": "蓄力时间",
+
+ "advancements.adventure.morecrossbows.obtain_copper_crossbow.title": "铜弩",
+ "advancements.adventure.morecrossbows.obtain_copper_crossbow.description": "铜做的弩!",
+ "advancements.adventure.morecrossbows.obtain_iron_crossbow.title": "铁弩",
+ "advancements.adventure.morecrossbows.obtain_iron_crossbow.description": "铁做的弩!",
+ "advancements.adventure.morecrossbows.obtain_golden_crossbow.title": "金弩",
+ "advancements.adventure.morecrossbows.obtain_golden_crossbow.description": "金做的弩!",
+ "advancements.adventure.morecrossbows.obtain_emerald_crossbow.title": "绿宝石弩",
+ "advancements.adventure.morecrossbows.obtain_emerald_crossbow.description": "绿宝石做的弩!",
+ "advancements.adventure.morecrossbows.obtain_diamond_crossbow.title": "钻石弩",
+ "advancements.adventure.morecrossbows.obtain_diamond_crossbow.description": "钻石做的弩!",
+ "advancements.adventure.morecrossbows.obtain_netherite_crossbow.title": "下界合金弩",
+ "advancements.adventure.morecrossbows.obtain_netherite_crossbow.description": "下界合金做的弩!",
+ "advancements.adventure.morecrossbows.obtain_gilded_netherite_crossbow.title": "镀金下界合金做的弩",
+ "advancements.adventure.morecrossbows.obtain_gilded_netherite_crossbow.description": "镀金下界合金做的弩!",
+ "advancements.adventure.morecrossbows.obtain_enderite_crossbow.title": "末影合金弩",
+ "advancements.adventure.morecrossbows.obtain_enderite_crossbow.description": "末影合金做的弩!",
+ "advancements.adventure.morecrossbows.obtain_gilded_enderite_crossbow.title": "镀金末影合金做的弩",
+ "advancements.adventure.morecrossbows.obtain_gilded_enderite_crossbow.description": "镀金末影合金做的弩!"
}
\ No newline at end of file
diff --git a/projects/1.20/pack.mcmeta b/projects/1.20/pack.mcmeta
index 1f2b80f1586a..b50bb96ae784 100644
--- a/projects/1.20/pack.mcmeta
+++ b/projects/1.20/pack.mcmeta
@@ -1,6 +1,6 @@
-{
- "pack": {
+{{
+ "pack": {{
"pack_format": 15,
- "description": "汉化万用包,仅供1.20.1Forge使用"
- }
-}
\ No newline at end of file
+ "description": "汉化万用包,仅供1.20.1Forge使用/n打包时间:{0:yyyy-MM-ddTHH:mm:ssZ}"
+ }}
+}}
\ No newline at end of file
diff --git a/projects/packer-example/README.txt b/projects/packer-example/README.txt
new file mode 100644
index 000000000000..08e00ed29169
--- /dev/null
+++ b/projects/packer-example/README.txt
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\ No newline at end of file
diff --git a/projects/packer-example/assets/example-composite/composite/lang/zh_cn.json b/projects/packer-example/assets/example-composite/composite/lang/zh_cn.json
new file mode 100644
index 000000000000..1d7f3709db82
--- /dev/null
+++ b/projects/packer-example/assets/example-composite/composite/lang/zh_cn.json
@@ -0,0 +1,4 @@
+{
+ "key3": "new value3",
+ "key_7_9": "new value_7_9"
+}
\ No newline at end of file
diff --git a/projects/packer-example/assets/example-composite/composite/other.txt b/projects/packer-example/assets/example-composite/composite/other.txt
new file mode 100644
index 000000000000..38f438fea794
--- /dev/null
+++ b/projects/packer-example/assets/example-composite/composite/other.txt
@@ -0,0 +1 @@
+////////////REPLACEMENT////////////
\ No newline at end of file
diff --git a/projects/packer-example/assets/example-composite/composite/packer-policy.json b/projects/packer-example/assets/example-composite/composite/packer-policy.json
new file mode 100644
index 000000000000..b6c48de2a6df
--- /dev/null
+++ b/projects/packer-example/assets/example-composite/composite/packer-policy.json
@@ -0,0 +1,13 @@
+[
+ {
+ "type": "direct"
+ },
+ {
+ "type": "indirect",
+ "source": "projects/packer-example/assets/example-indirect/indirect"
+ },
+ {
+ "type": "indirect",
+ "source": "projects/packer-example/assets/example-composition/composition"
+ }
+]
\ No newline at end of file
diff --git a/projects/packer-example/assets/example-composition/composition/compositions/zh_cn.json-composition.json b/projects/packer-example/assets/example-composition/composition/compositions/zh_cn.json-composition.json
new file mode 100644
index 000000000000..fea771c361ea
--- /dev/null
+++ b/projects/packer-example/assets/example-composition/composition/compositions/zh_cn.json-composition.json
@@ -0,0 +1,37 @@
+{
+ "target": "assets/composition/lang/zh_cn.json",
+ "entries": [
+ {
+ "templates": {
+ "key_{0}_{1}": "value_{0}_{1}",
+ "altkey_{0}_{1}": "altvalue_{0}_{1}"
+ },
+ "parameters": [
+ {
+ "0": "0",
+ "1": "1",
+ "2": "2",
+ "3": "3",
+ "4": "4",
+ "5": "5",
+ "6": "6",
+ "7": "7",
+ "8": "8",
+ "9": "9"
+ },
+ {
+ "00": "00",
+ "01": "01",
+ "02": "02",
+ "03": "03",
+ "04": "04",
+ "05": "05",
+ "06": "06",
+ "07": "07",
+ "08": "08",
+ "09": "09"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/projects/packer-example/assets/example-composition/composition/local-config.json b/projects/packer-example/assets/example-composition/composition/local-config.json
new file mode 100644
index 000000000000..3670d611ac56
--- /dev/null
+++ b/projects/packer-example/assets/example-composition/composition/local-config.json
@@ -0,0 +1,10 @@
+{
+ "inclusionDomains": [],
+ "exclusionDomains": [
+ "compositions"
+ ],
+ "exclusionPaths": [],
+ "inclusionPaths": [],
+ "characterReplacement": {},
+ "destinationReplacement": {}
+}
\ No newline at end of file
diff --git a/projects/packer-example/assets/example-composition/composition/packer-policy.json b/projects/packer-example/assets/example-composition/composition/packer-policy.json
new file mode 100644
index 000000000000..3d360165b4c2
--- /dev/null
+++ b/projects/packer-example/assets/example-composition/composition/packer-policy.json
@@ -0,0 +1,7 @@
+[
+ {
+ "type": "composition",
+ "destType": "json",
+ "source": "projects/packer-example/assets/example-composition/composition/compositions/zh_cn.json-composition.json"
+ }
+]
\ No newline at end of file
diff --git a/projects/packer-example/assets/example-direct/direct/lang/en_us.json b/projects/packer-example/assets/example-direct/direct/lang/en_us.json
new file mode 100644
index 000000000000..6ccb0f975d80
--- /dev/null
+++ b/projects/packer-example/assets/example-direct/direct/lang/en_us.json
@@ -0,0 +1,20 @@
+{
+ "key1": "ununvalue1",
+ "key2": "ununvalue2",
+ "key3": "ununvalue3",
+ "key4": "ununvalue4",
+ "key5": "ununvalue5",
+ "key6": "ununvalue6",
+ "key7": "ununvalue7",
+ "key8": "ununvalue8",
+ "key9": "ununvalue9",
+ "key10": "ununvalue10",
+ "key11": "ununvalue11",
+ "key12": "ununvalue12",
+ "key13": "ununvalue13",
+ "key14": "ununvalue14",
+ "key15": "ununvalue15",
+ "key16": "ununvalue16",
+ "key17": "ununvalue17",
+ "key18": "ununvalue18"
+}
\ No newline at end of file
diff --git a/projects/packer-example/assets/example-direct/direct/lang/zh_cn.json b/projects/packer-example/assets/example-direct/direct/lang/zh_cn.json
new file mode 100644
index 000000000000..7942577b11d5
--- /dev/null
+++ b/projects/packer-example/assets/example-direct/direct/lang/zh_cn.json
@@ -0,0 +1,20 @@
+{
+ "key1": "value1",
+ "key2": "value2",
+ "key3": "value3",
+ "key4": "value4",
+ "key5": "value5",
+ "key6": "value6",
+ "key7": "value7",
+ "key8": "value8",
+ "key9": "value9",
+ "key10": "value10",
+ "key11": "value11",
+ "key12": "value12",
+ "key13": "value13",
+ "key14": "value14",
+ "key15": "value15",
+ "key16": "value16",
+ "key17": "value17",
+ "key18": "value18"
+}
\ No newline at end of file
diff --git a/projects/packer-example/assets/example-direct/direct/other.txt b/projects/packer-example/assets/example-direct/direct/other.txt
new file mode 100644
index 000000000000..08e00ed29169
--- /dev/null
+++ b/projects/packer-example/assets/example-direct/direct/other.txt
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\ No newline at end of file
diff --git a/projects/packer-example/assets/example-indirect/indirect/packer-policy.json b/projects/packer-example/assets/example-indirect/indirect/packer-policy.json
new file mode 100644
index 000000000000..164b89d90ca6
--- /dev/null
+++ b/projects/packer-example/assets/example-indirect/indirect/packer-policy.json
@@ -0,0 +1,6 @@
+[
+ {
+ "type": "indirect",
+ "source": "projects/packer-example/assets/example-direct/direct"
+ }
+]
\ No newline at end of file
diff --git a/projects/packer-example/pack.mcmeta b/projects/packer-example/pack.mcmeta
new file mode 100644
index 000000000000..c3b7467d5a17
--- /dev/null
+++ b/projects/packer-example/pack.mcmeta
@@ -0,0 +1,6 @@
+{{
+ "pack": {{
+ "pack_format": 2147483647,
+ "description": "演示用包,不用于分发/n打包时间:{0:yyyy-MM-ddTHH:mm:ssZ}"
+ }}
+}}
\ No newline at end of file
diff --git a/src/Packer/Extensions/ArchiveExtension.cs b/src/Packer/Extensions/ArchiveExtension.cs
index ead7c752da90..62121c98727b 100644
--- a/src/Packer/Extensions/ArchiveExtension.cs
+++ b/src/Packer/Extensions/ArchiveExtension.cs
@@ -1,98 +1,25 @@
-using Packer.Models;
-using Serilog;
-using System.Collections.Generic;
-using System.IO;
+using System;
using System.IO.Compression;
-using System.Linq;
-using System.Threading.Tasks;
namespace Packer.Extensions
{
///
- /// 用于创建压缩包的各种拓展方法
+ /// 用于压缩包的拓展方法
///
static public class ArchiveExtension
{
///
- /// 按要求写入语言文件,并加Log
+ /// 校验将要传入压缩包的的文件是否存在重名
///
- /// 压缩文件
- /// 目标路径
- /// 写入内容
- ///
- public static async Task CreateLangFile(this ZipArchive archive, string destination, string content)
+ /// 所查询的压缩包
+ /// 所查询的路径
+ /// 传入文件存在重名。
+ public static void ValidateEntryDistinctness(this ZipArchive archive, string entryName)
{
- destination = destination.NormalizePath();
- Log.Information("正在添加 {0}", destination);
- using var writer = new StreamWriter(
- archive.CreateEntry(destination)
- .Open());
- await writer.WriteAsync(content);
- writer.Flush(); // 确保一下
- }
-
- ///
- /// 初始化压缩包
- /// 包括压缩包的基础文件
- ///
- /// 压缩文件
- /// 所使用的配置
- public static void Initialize(this ZipArchive archive, Config config)
- {
- Log.Information("开始初始化压缩包");
- string commonPrefix = $"./projects/{config.Version}";
- config.FilesToInitialize.ForEach(path =>
- {
- var destination = path.StripModName() // 除掉一层文件夹(在 assets/ 里的各种 fix)
- .NormalizePath();
- Log.Information("初始化压缩包:添加 {0}", destination);
- archive.CreateEntryFromFile($"{commonPrefix}/{path}", destination);
- });
-
- Log.Information("初始化完成");
- }
-
- ///
- /// 写入选出的Asset们
- ///
- /// 压缩文件
- /// 选出的内容
- ///
- public static async Task WriteContent(this ZipArchive archive, IEnumerable content)
- {
- Log.Information("添加处理后的文件");
-
-
- var tasks = content.SelectMany(asset => asset.contents.Select(file => archive.CreateLangFile(Path.Combine("assets",
- asset.domainName,
- file.RelativePath),
- file.StringifiedContent)));
-
- await Task.WhenAll(content.SelectMany(
- asset => asset.contents.Select(
- file => archive.CreateLangFile(destination: Path.Combine("assets",
- asset.domainName,
- file.RelativePath),
- content: file.StringifiedContent))));
- Log.Information("添加完毕");
- }
-
- ///
- /// 写入非文本处理的文件
- ///
- /// 压缩文件
- /// 非文本处理的文件
- public static void WriteBypassed(this ZipArchive archive, Dictionary bypassed)
- {
- Log.Information("添加未经处理的文件");
- foreach (var pair in bypassed)
+ if (archive.GetEntry(entryName) != null)
{
- var normalizedEntryName = pair.Value.NormalizePath();
- Log.Information("正在添加 {0}", normalizedEntryName);
- archive.CreateEntryFromFile(sourceFileName: pair.Key,
- entryName: normalizedEntryName);
+ throw new InvalidOperationException($"An entry named {entryName} already exists.");
}
- Log.Information("添加完毕");
}
}
}
diff --git a/src/Packer/Extensions/ContentExtension.cs b/src/Packer/Extensions/ContentExtension.cs
index 7273453dacd0..811ff75f676a 100644
--- a/src/Packer/Extensions/ContentExtension.cs
+++ b/src/Packer/Extensions/ContentExtension.cs
@@ -1,5 +1,4 @@
-using Packer.Models;
-using Serilog;
+using Serilog;
using System;
using System.IO;
using System.Linq;
@@ -11,95 +10,82 @@ namespace Packer.Extensions
///
/// 对字符串的一些拓展方法
///
- public static class ContentExtension
+ public static partial class ContentExtension
{
///
/// 将文件的目标路径正规化,以免各种加载出错
///
/// 目标路径
- ///
+ /// 正规化后的文件路径
public static string NormalizePath(this string path)
- => path.Replace('\\', '/') // 修正正反斜杠导致的压缩文件读取问题
- //.ToLower() // 确保大小写 * 由于语言类型需要大小写而禁用该条
- ;
+ => path.Replace('\\', '/'); // 修正正反斜杠导致的压缩文件读取问题
+
+
+ [GeneratedRegex(@"^[a-z0-9_.-]+$", RegexOptions.Singleline)]
+ internal static partial Regex ValidNamespaceRegex();
///
- /// 移除模组名一级,在基础文件处理处用到
+ /// 检查命名空间名称是否合法
///
- /// 目标文件在库中assets\1\2\...>式位置
- ///
- public static string StripModName(this string path)
+ ///
+ /// 合法的命名空间名称只包括小写字母、数字、_、.、-
+ ///
+ /// 待校验的命名空间名称
+ /// 若合法,返回
+ /// 校验的命名空间不合法
+ public static bool ValidateNamespace(this string namespaceName)
{
- var _ = path.Split('/').ToList();
-
- if (_.Count >= 2) _.RemoveAt(1); // 认为模组名在第一处 / 的后面
- return Path.Join(_.ToArray());
+ // 强行丢异常...行吧
+ if (!ValidNamespaceRegex().IsMatch(namespaceName))
+ throw new ArgumentOutOfRangeException(nameof(namespaceName), namespaceName, "Invalid namespace name.");
+ return true;
}
+
///
- /// 文本预处理
- /// 目前仅有特殊符号更换,但还是预留了空间
+ /// 判断domain是否强制包含
///
- /// 待处理的文本
- /// 文本种类,用于判断是否转义
+ /// 文件所在的位置
/// 所使用的配置
- ///
- public static string Preprocess(this string content, FileCategory category, Config config)
- {
- // 特殊符号替换
- foreach (var mapping in config.CharatcerReplacement)
- {
- if (content.Contains(mapping.Key))
- {
- Log.Information("正在进行特殊符号替换:{0} -> {1}", mapping.Key, mapping.Value);
- }
- if ((category & FileCategory.JsonAlike) == FileCategory.JsonAlike)
- { // 替换为 unicode 转义码
- content = content.Replace(mapping.Key, mapping.Value);
- }
- else
- { // 替换为 unicode 字符
- content = content.Replace(mapping.Key, Regex.Unescape(mapping.Value));
- }
+ public static bool IsDomainForceIncluded(this string location, Config config)
+ => config.Floating.InclusionDomains.Any(_ => location.StartsWith(_ + '/'));
- }
- return content;
- }
+ ///
+ /// 判断domain是否强制排除
+ ///
+ /// 文件所在的位置
+ /// 所使用的配置
+ public static bool IsDomainForceExcluded(this string location, Config config)
+ => config.Floating.ExclusionDomains.Any(_ => location.StartsWith(_ + '/'));
///
- /// 判断文件是否需要跳过预处理
- /// 一般而言,图片类文件需要跳过;这一点可以在config\packer.json里控制
+ /// 判断文件是否属于目标语言
///
/// 文件所在的位置
/// 所使用的配置
- ///
- public static bool NeedBypass(this string location, Config config)
- {
- foreach (var @namespace in config.BypassedNamespace)
- {
- if (location.StartsWith(@namespace + "/")) return true;
- }
- return false;
- }
+ public static bool IsInTargetLanguage(this string location, Config config)
+ => config.Base.TargetLanguages.Any(_ => location.Contains(_, StringComparison.OrdinalIgnoreCase));
///
- /// 判断文件是否属于应跳过的语言
+ /// 判断文件路径是否强制排除
///
/// 文件所在的位置
/// 所使用的配置
- ///
- public static bool IsTargetLang(this string location, Config config)
- {
- foreach (var lang in config.TargetLanguages)
- {
- if (location.Contains(lang, StringComparison.OrdinalIgnoreCase)) return true;
- }
- return false;
- }
+ public static bool IsPathForceExcluded(this string location, Config config)
+ => config.Floating.ExclusionPaths.Contains(location);
+
+ ///
+ /// 判断文件路径是否强制包含
+ ///
+ /// 文件所在的位置
+ /// 所使用的配置
+ public static bool IsPathForceIncluded(this string location, Config config)
+ => config.Floating.InclusionPaths.Contains(location);
+
// 临时方法
///
- /// 计算给定流中全体内容的MD5值。
+ /// 计算给定流中全体内容的MD5值
///
/// 被计算的流
///
diff --git a/src/Packer/Extensions/DirectoryExtension.cs b/src/Packer/Extensions/DirectoryExtension.cs
index 007069dea181..822a7875f1df 100644
--- a/src/Packer/Extensions/DirectoryExtension.cs
+++ b/src/Packer/Extensions/DirectoryExtension.cs
@@ -1,5 +1,8 @@
-using Packer.Models;
+using Packer.Helpers;
+using Packer.Models;
+using Packer.Models.Providers;
using Serilog;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -7,168 +10,143 @@
namespace Packer.Extensions
{
+ using EvaluatorReturnType = IEnumerable<(IResourceFileProvider provider, bool overrides)>;
+ using ParameterType = Dictionary;
+
+
///
/// 用于处理\[namespace]层级的不同加载策略的拓展方法,以及一些辅助方法
///
- public static class DirectoryExtension
+ public static partial class DirectoryExtension
{
-
+ ///
+ /// 加载策略所使用的标准方法代理
+ ///
+ /// 加载的基准位置
+ /// 采用的全局配置
+ /// 局部打包配置的附加参数
+ /// 一个,第一参数为提供器的目标
+ public delegate EvaluatorReturnType
+ ProviderEvaluator(DirectoryInfo namespaceDirectory,
+ Config config,
+ ParameterType? parameters);
///
- /// 从[namespace]生成所需的Asset对象,采用本目录下放置的配置文件
+ /// 从到加载方法的查询表
///
- /// 目标路径
- /// 采用的配置
- /// 未经处理的文件
- ///
- public static IEnumerable AggregateAssetFiles(this DirectoryInfo assetPath,
- Config config,
- ref Dictionary bypassed)
+ internal static Dictionary functionTable = new()
{
- // 读取局域配置文件;若为空,配置为“无操作”(直接处理文件)
- var policy = Utils.RetrieveStrategy(assetPath.GetFiles("packer-policy.json").FirstOrDefault());
-
- if (policy.Type != PackerStrategyType.NoAction)
- Log.Information("对asset-domain {2} 采用非标准检索策略:{0} w/ {1}",
- policy.Type,
- policy.Parameters,
- assetPath.Name);
-
- // Delegate是个好东西 要不然这参数不知道要多长
- // 不过,什么时候能支持集合字面量......这样子就甚至不用写类型了
- // 目前支持的打包策略
- Dictionary functionTable = new()
- {
- { PackerStrategyType.NoAction, FromImmediateDirectory },
- { PackerStrategyType.PlainClone, FromIndirectDirectory },
- { PackerStrategyType.CloneMissing, FromMixedDirectory },
- { PackerStrategyType.BackPort, FromBackPort },
- { PackerStrategyType.Patch, FromPatches }
- };
- return functionTable[policy.Type](assetPath, config, ref bypassed, policy.Parameters);
- }
+ { PackerPolicyType.Direct, FromCurrentDirectory }, // 现场生成
+ { PackerPolicyType.Indirect, FromSpecifiedDirectory }, // 给定目录
+ { PackerPolicyType.Composition, FromComposition } // 组合生成
+ };
///
- /// 加载策略所使用的标准方法代理
+ /// 从给定的命名空间,基于当地的packer-policy.json
+ /// 与packer-config-fixup.json,遍历
///
- /// 目标路径
- /// 采用的全局配置
- /// 不经处理的文件
- /// 局部打包配置的附加参数
+ /// 命名空间所在目录
+ /// 所使用的全局配置
///
- public delegate IEnumerable Del(DirectoryInfo assetDirectory,
- Config config,
- ref Dictionary unprocessed,
- Dictionary parameters);
+ public static IEnumerable EnumerateProviders
+ (this DirectoryInfo namespaceDirectory, Config config)
+ => from enumeratedPair in namespaceDirectory.EnumerateRawProviders(config)
+ group enumeratedPair by enumeratedPair.provider.Destination into providerGroup
+ select providerGroup.Aggregate(
+ seed: null as IResourceFileProvider,
+ (accumulate, next)
+ => next.provider.ApplyTo(accumulate, next.overrides));
- static IEnumerable FromMixedDirectory(DirectoryInfo assetDirectory,
- Config config,
- ref Dictionary unprocessed,
- Dictionary parameters)
- => Utils.MergeFiles(FromImmediateDirectory(assetDirectory, config, ref unprocessed, parameters),
- FromIndirectDirectory(assetDirectory, config, ref unprocessed, parameters));
+ ///
+ /// 遍历未经合并的文件,用于递归调用
+ ///
+ internal static EvaluatorReturnType EnumerateRawProviders(this DirectoryInfo namespaceDirectory, Config config)
+ => from policy in ConfigHelpers.RetrieveStrategy(namespaceDirectory)
+ from enumeratedPair in functionTable[policy.Type].Invoke(
+ namespaceDirectory, config, policy.Parameters)
+ select enumeratedPair;
- static IEnumerable FromBackPort(DirectoryInfo assetDirectory,
- Config config,
- ref Dictionary unprocessed,
- Dictionary parameters)
- => Utils.PortFiles(FromImmediateDirectory(assetDirectory, config, ref unprocessed, parameters),
- FromIndirectDirectory(assetDirectory, config, ref unprocessed, parameters));
- static IEnumerable FromIndirectDirectory(DirectoryInfo assetDirectory,
+ internal static EvaluatorReturnType FromCurrentDirectory(DirectoryInfo namespaceDirectory,
Config config,
- ref Dictionary unprocessed,
- Dictionary parameters)
- => FromImmediateDirectory(new DirectoryInfo(parameters["source"].GetString()), config, ref unprocessed, parameters);
-
- static IEnumerable FromPatches(DirectoryInfo assetDirectory,
- Config config,
- ref Dictionary unprocessed,
- Dictionary parameters)
+ ParameterType? parameters)
{
- var reference = FromIndirectDirectory(assetDirectory, config, ref unprocessed, parameters)
- .ToDictionary(_ => _.RelativePath);
- var patchList = JsonSerializer.Deserialize>(parameters["patches"]);
- foreach (var patch in patchList)
- {
- //Log.Information("{0}", reference.Keys);
- Log.Information("对文件 {0} 应用 {1} 处的 patch。", patch.Key, patch.Value);
- reference.Remove(patch.Key, out var target);
- var patchText = string.Join('\n', File.ReadAllLines(patch.Value)); // 不要问我为什么D-M-P默认换行是LF
- reference.Add(patch.Key, target.ApplyPatch(patchText));
- }
- return reference.Values;
+ var floatingConfig = ConfigHelpers.RetrieveLocalConfig(namespaceDirectory);
+ var localConfig = config.Modify(floatingConfig);
+
+ return from candidate in namespaceDirectory.EnumerateFiles("*", SearchOption.AllDirectories)
+ let relativePath = Path.GetRelativePath(namespaceDirectory.FullName,
+ candidate.FullName)
+ .NormalizePath()
+ let fullPath = Path.GetRelativePath(".", candidate.FullName)
+ let destination = Path.Combine(
+ "assets", namespaceDirectory.Name, relativePath)
+ .NormalizePath()
+ where !relativePath.IsPathForceExcluded(localConfig) // [1] 排除路径 -- packer-policy等
+ where (relativePath.IsPathForceIncluded(localConfig) // [2] 包含路径 [单列]
+ || relativePath.IsDomainForceIncluded(localConfig) // [3] 包含domain -- font/ textures/
+ || (destination.IsInTargetLanguage(localConfig) // [4] 语言标记 -- 含zh_cn的
+ && !relativePath.IsDomainForceExcluded(localConfig))) // [5] 排除domain [暂无]
+ let provider = CreateProviderFromFile(candidate, destination, localConfig)
+ select (provider, DoesOverride(parameters));
}
- // 目前所有策略的终点方法
- static IEnumerable FromImmediateDirectory(DirectoryInfo assetDirectory,
- Config config,
- ref Dictionary unprocessed,
- Dictionary parameters)
+ internal static EvaluatorReturnType FromSpecifiedDirectory(DirectoryInfo namespaceDirectory,
+ Config config,
+ ParameterType? parameters)
{
- var bypassed = unprocessed;
- var result = assetDirectory.EnumerateFiles("*", SearchOption.AllDirectories) // / 的下级文件
- .Select(file =>
- { // 这里开始真正的检索。被跳过的文本用 null 代替
- var prefixLength = assetDirectory.FullName.Length;
- var relativePath = file.FullName[(prefixLength + 1)..]
- .Replace('\\', '/'); // 在asset-domain下的位置,规范为用正斜杠分割
-
- // 处理被跳过的文本。处理顺序:policy -> [bypass](font, textures) -> !zh_cn
- // 顺序乱了会导致字体文件被丢弃,因为没有带zh_cn
+ var redirect = parameters!["source"].GetString();
+ var namespaceName = namespaceDirectory.Name;
+ var redirectDirectory = new DirectoryInfo(redirect!);
+
+ return from candidate in redirectDirectory.EnumerateRawProviders(config)
+ let provider = candidate.provider
+ .ReplaceDestination(@"(?<=^assets/)[^/]*(?=/)",
+ namespaceName)
+ select (provider, DoesOverride(parameters));
+ }
- // 跳过检索策略文件
- if (relativePath == "packer-policy.json")
- {
- return null;
- }
+ internal static EvaluatorReturnType FromComposition(DirectoryInfo namespaceDirectory,
+ Config config,
+ ParameterType? parameters)
+ {
+ var compositionPath = parameters!["source"].GetString();
+ var type = parameters["destType"].GetString();
+ var compositionFile = new FileInfo(compositionPath!);
+ IResourceFileProvider provider = type switch // 类型推断不出要用接口
+ {
+ "lang" => LangMappingHelper.CreateFromComposition(compositionFile),
+ "json" => JsonMappingHelper.CreateFromComposition(compositionFile),
+ _ => throw new InvalidOperationException($"Unexpected Type parameter at {namespaceDirectory.FullName}.")
+ };
+ yield return (provider, DoesOverride(parameters));
+ }
- // 选出不经过处理路径的文件
- if (relativePath.NeedBypass(config))
- {
- var target = Path.Combine("assets",
- assetDirectory.Name,
- relativePath);
- Log.Information("跳过了标记为直接加入的命名空间:{0} -> {1}",
- relativePath.Split('/')[0],
- target);
- bypassed.Add(file.FullName, target);
- return null;
- }
- // 跳过非中文文件
- if (!relativePath.IsTargetLang(config))
- {
- return null;
- }
+ internal static IResourceFileProvider CreateProviderFromFile(FileInfo file, string destination, Config config)
+ {
+ var extension = file.Extension;
+ if (file.Directory!.Name == "lang")
+ {
+ switch(extension)
+ {
+ case ".json": return JsonMappingHelper.CreateFromFile(file, destination);
+ case ".lang": return LangMappingHelper.CreateFromFile(file, destination);
+ };
+ }
+ return extension switch
+ {
+ // 已知的文本文件类型
+ ".txt" or ".json" or ".md" => TextFile.Create(file, destination),
+ _ => new RawFile(file, destination)
+ };
+ }
- // 处理正常的语言文件
- // TODO:Json5支持
- var parsingCategory = file.Extension switch
- {
- ".json" => FileCategory.JsonAlike,
- _ => FileCategory.LangAlike
- };
- if (relativePath.StartsWith("lang/"))
- {
- return new LangFile(file.OpenRead(),
- parsingCategory | FileCategory.LanguageFile,
- config)
- {
- RelativePath = relativePath
- };
- }
- else
- {
- return new TranslatedFile(file.OpenRead(),
- parsingCategory | FileCategory.OtherFiles,
- config)
- {
- RelativePath = relativePath
- };
- }
- }).Where(_ => _ is not null); // 排除掉跳过的文件
- return result;
+ internal static bool DoesOverride(ParameterType? parameters)
+ {
+ if (parameters is null) return false;
+ var hasKey = parameters.TryGetValue("overrides", out var element);
+ return hasKey ? element.GetBoolean() : false;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Packer/Extensions/SerializingExtension.cs b/src/Packer/Extensions/SerializingExtension.cs
deleted file mode 100644
index 812d675c5a61..000000000000
--- a/src/Packer/Extensions/SerializingExtension.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-using Packer.Models;
-using Serilog;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.Json;
-
-namespace Packer.Extensions
-{
- static class SerializingExtension
- {
- public static string SerializeAsset(this Dictionary assetMap, FileCategory category)
- => category switch
- {
- // Json 文件,直接写出
- FileCategory.JsonTranslationFormat => JsonSerializer.Serialize(assetMap,
- new JsonSerializerOptions
- {
- WriteIndented = true
- }),
- // Lang文件
- FileCategory.LangTranslationFormat => SerializeFromLang(assetMap),
- _ => null // 其实不应该执行到这个地方
- };
-
- static string SerializeFromLang(Dictionary assetMap)
- {
- // lang格式化还好说
- var sb = new StringBuilder();
- foreach (var pair in assetMap)
- {
- sb.AppendJoin('=', pair.Key, pair.Value);
- sb.Append(Environment.NewLine);
- }
- return sb.ToString();
- }
-
- public static Dictionary DeserializeAsset(this string content, FileCategory category)
- => category switch
- {
- FileCategory.JsonTranslationFormat
- => JsonSerializer.Deserialize>(content,
- new JsonSerializerOptions
- {
- ReadCommentHandling = JsonCommentHandling.Skip // 打包过程应当兼容注释,但不需要写入注释
- }), // 直接有的算法
- FileCategory.LangTranslationFormat => DeserializeFromLang(content),
- _ => null // 其实不应该执行到这个地方
- };
-
- static Dictionary DeserializeFromLang(string content)
- {
- // 甚至不是自动机...所以不敢多用,否则会炸
-
- // 下面的 Verbose 仅供调试,不会在 log 里出现
- // .lang的格式真的乱...
- Log.Verbose("开始反序列化 .lang 文件");
- // #PARSE_ESCAPE就算了吧
- var result = new Dictionary();
- var isInComment = false; // 处理多行注释
- new List(content.Split(Environment.NewLine,
- StringSplitOptions.RemoveEmptyEntries))
- .ForEach(line =>
- {
- var isSingleLineComment = false;
- new List { "//", "#" }
- .ForEach(_ => { isSingleLineComment |= line.StartsWith(_); });
- if (isSingleLineComment)
- {
- Log.Verbose("跳过了单行注释:{0}", line);
- }
- else if (isInComment) // 多行注释内
- {
- Log.Verbose("{0}", line);
- if (line.Trim()
- .EndsWith("*/"))
- {
- isInComment = false; // 跳出注释
- }
- }
- else if (line.StartsWith("/*")) // 开始多行注释
- {
- Log.Verbose("跳过了多行注释:{0}", line);
- }
- else // 真正的条目
- {
- Log.Verbose("添加对应映射:{0}", line);
- var spiltPosition = line.IndexOf('=');
- try
- {
- result.Add(line[..spiltPosition], line[(spiltPosition + 1)..]);
- }
- catch (Exception e)
- {
- Log.Warning(e.ToString());
- }
- }
- }
- );
- Log.Verbose("反序列化完成");
- return result;
- }
- }
-}
diff --git a/src/Packer/Helpers/ConfigHelpers.cs b/src/Packer/Helpers/ConfigHelpers.cs
new file mode 100644
index 000000000000..173513314bf4
--- /dev/null
+++ b/src/Packer/Helpers/ConfigHelpers.cs
@@ -0,0 +1,83 @@
+using Packer.Models;
+using Serilog;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+
+
+namespace Packer.Helpers
+{
+ ///
+ /// 配置相关的工具类
+ ///
+ public static class ConfigHelpers
+ {
+ ///
+ /// 从给定的命名空间获取局域配置
+ ///
+ /// 命名空间目录
+ /// 若文件存在,返回;否则,返回
+ public static FloatingConfig? RetrieveLocalConfig(DirectoryInfo directory)
+ {
+ var configFile = directory.GetFiles("local-config.json").FirstOrDefault();
+
+ if (configFile is null) return null;
+
+ using var reader = configFile.OpenText();
+ return JsonSerializer.Deserialize(
+ reader.ReadToEnd(),
+ new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
+ }
+
+ ///
+ /// 从仓库根目录获取全局配置
+ ///
+ /// 配置路径模板
+ /// 打包版本,用于定位全局配置
+ public static async Task RetrieveConfig(string configTemplate, string version)
+ {
+ Log.Information("正在获取配置。目标版本:{0}", version);
+
+ var configPath = string.Format(configTemplate, version);
+
+ Log.Information("配置位置:{0}", configPath);
+
+ var content = await File.ReadAllBytesAsync(configPath);
+ return JsonSerializer.Deserialize(
+ content,
+ new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })!;
+ }
+
+ ///
+ /// 从给定的命名空间获取策略内容
+ ///
+ /// 命名空间目录
+ /// 若文件存在,返回对应的内容;否则,返回Direct
+ /// 策略文件非法
+ public static List RetrieveStrategy(DirectoryInfo directory)
+ {
+ var file = directory.GetFiles("packer-policy.json").FirstOrDefault();
+
+ if (file is null)
+ return new List
+ {
+ new PackerPolicy { Type = PackerPolicyType.Direct }
+ };
+
+ using var reader = file.OpenText();
+ var result = JsonSerializer.Deserialize>(
+ reader.ReadToEnd(),
+ new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
+ });
+ if (result is null)
+ throw new InvalidDataException($"The policy file {file.FullName} cannot have null values.");
+ return result;
+ }
+ }
+}
diff --git a/src/Packer/Helpers/GitHelpers.cs b/src/Packer/Helpers/GitHelpers.cs
new file mode 100644
index 000000000000..d6d2cba9f8c2
--- /dev/null
+++ b/src/Packer/Helpers/GitHelpers.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using LibGit2Sharp;
+using Serilog;
+
+namespace Packer.Helpers
+{
+ ///
+ /// Git操作相关的工具类
+ ///
+ public static class GitHelpers
+ {
+ ///
+ /// 枚举相对与主分支main,给定版本下有更改的模组。
+ ///
+ public static IEnumerable EnumerateChangedMods(string version)
+ {
+ Log.Information("对版本 {0} 加载更改模组", version);
+ using var repo = new Repository(".");
+ var headTree = repo.Head.Tip.Tree;
+ var baseTree = repo.Branches["refs/remotes/origin/master"].Tip.Tree;
+ var changedFiles = repo.Diff.Compare(baseTree, headTree);
+ var query = from change in changedFiles
+ from path in new List { change.Path, change.OldPath }
+ where path.IsInTargetVersion(version)
+ select path.ExtractModIdentifier(version);
+ var result = query.Distinct();
+ Log.Information("更改模组列表:{0}", result);
+ return result;
+ }
+
+ internal static bool IsInTargetVersion(this string location, string version)
+ => location.StartsWith($"projects/{version}/assets");
+
+ internal static string ExtractModIdentifier(this string location, string version)
+ => Path.GetRelativePath($"projects/{version}/assets", location)
+ .Split(Path.DirectorySeparatorChar)[0];
+
+ }
+}
diff --git a/src/Packer/Lib.cs b/src/Packer/Lib.cs
deleted file mode 100644
index 1513f6be9bd7..000000000000
--- a/src/Packer/Lib.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-using Packer.Extensions;
-using Packer.Models;
-using Serilog;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text.RegularExpressions;
-
-namespace Packer
-{
- static class Lib
- {
- ///
- /// 从主库中选出所需文本。
- ///
- /// 所使用的配置
- /// 传出非文本处理的文件
- ///
- public static IEnumerable RetrieveContent(Config config, out Dictionary unprocessed)
- {
- // 警告:下面的代码中,有部分变量的名称并不规范!远期可能会调整这一部分。
-
- // 注:仓库的文件结构如下:(仅考虑主要翻译文件)
- // projects//assets////path/to/the/file
- // 其中, 与 config.Version 一致
- // 是唯一的,但 和 都不是唯一的
- // 目标文件层级:
- // assets///path/to/the/file
- //
- // 在目标层级以外的文件不支持处理,需要作为基础文件加入
-
- // 预备工作
- Log.Information("开始生成待打包的文件");
- var bypassed = new Dictionary(); // 文件完整路径 -> 压缩包中的完整路径
- var result = new Dictionary(); // domain -> 该domain对应的Asset对象
- var existingDomains = new Dictionary(); // domain -> 模组名
-
- // 下面开始检索模组
- // 以后可能会用更好看的linq语法写,但是现在就这样了
- var mods = new DirectoryInfo($"./projects/{config.Version}/assets")
- .EnumerateDirectories() // assets/ 的下级文件夹
- .Select(modDirectory => new Mod()
- {
- modName = modDirectory.Name,
- assets = modDirectory
- .EnumerateDirectories() // / 的下级文件夹
- .Select(assetDirectory => new Asset()
- {
- domainName = assetDirectory.Name,
- contents = assetDirectory.AggregateAssetFiles(config, ref bypassed)
- })
- });
- foreach (var mod in mods)
- {
- var name = mod.modName;
- if (!mod.assets.Any()) continue; // 没有 asset 的情况
-
- foreach (var asset in mod.assets)
- {
- var domain = asset.domainName;
- if (config.ModBlackList.Contains(name))
- {
- Log.Information("跳过了黑名单中的 mod:{0}(asset-domain:{1})", name, domain);
- continue;
- }
- if (config.DomainBlackList.Contains(domain))
- {
- Log.Information("跳过了黑名单中的 asset-domain:{0}(对应 mod:{1})", domain, name);
- continue;
- }
- Log.Information("正在处理 {0}(asset-domain:{1})", name, domain);
-
- // 强制中止打包,防止异常文件流出
- if(!Regex.IsMatch(domain, @"^[a-z0-9_\-.]+$", RegexOptions.Singleline))
- throw new ArgumentOutOfRangeException(paramName: nameof(domain),
- actualValue: domain,
- message: "非法的命名空间名称。强制中止");
-
- if (!existingDomains.ContainsKey(domain))
- {
- Log.Information("未检测到重合。直接加入");
- result.Add(domain, asset);
- Log.Information("向 asset-domain 映射表中加入:{0} -> {1}", domain, name);
- existingDomains.Add(domain, name);
- }
- else
- {
- Log.Warning("检测到 asset-domain 与 {0} 重合", existingDomains[domain]);
- result.Remove(domain, out var existing);
- result.Add(domain, existing.Combine(asset));
- }
- }
- }
- Log.Information("文件列表生成完毕");
- unprocessed = bypassed; // 正式传出非文本文件
- return result.Select(_ => _.Value);
- }
- }
-}
diff --git a/src/Packer/Models/Config.cs b/src/Packer/Models/Config.cs
index 1b1de808ca98..45aeb8bf2b31 100644
--- a/src/Packer/Models/Config.cs
+++ b/src/Packer/Models/Config.cs
@@ -1,59 +1,111 @@
using System.Collections.Generic;
-using System.Text.Json.Serialization;
+using System.Linq;
+
+// 要null就抛异常吧(逃)
+#nullable disable
namespace Packer
{
///
- /// 配置文件
- /// 主要从config/packer.json加载
+ /// 配置项
///
public class Config
+ {
+ ///
+ /// 基础配置,版本唯一
+ ///
+ public BaseConfig Base { get; set; }
+ ///
+ /// 浮动配置,可与命名空间下的文件合并
+ ///
+ public FloatingConfig Floating { get; set; }
+
+ ///
+ /// 从命名空间下的局域配置加载内容。
+ ///
+ public Config Modify(FloatingConfig? floatingConfig)
+ {
+ if (floatingConfig is null) return this;
+ return new()
+ {
+ Base = Base,
+ Floating = Floating.Merge(floatingConfig)
+ };
+ }
+ }
+
+ ///
+ /// 基础配置,版本唯一
+ ///
+ public class BaseConfig
{
///
/// 打包的目标版本
///
- [JsonPropertyName("targetVersion")]
public string Version { get; set; }
///
- /// 打包的目标语言
- /// 为1.20准备,因为据说语言文件名要改
+ /// 打包的目标语言
///
- [JsonPropertyName("targetLanguage")]
public string[] TargetLanguages { get; set; }
///
- /// 打包过程的基础文件(如在assets/以外的文件,或不宜通过打包流程的)
+ /// 不进行打包的mod(按[curseforge-]name)
///
- [JsonPropertyName("additionalContent")]
- public List FilesToInitialize { get; set; }
+ public IEnumerable ExclusionMods { get; set; }
///
- /// 不进行打包的mod(按[curseforge-]name处理)
- /// 有可能作为基础文件
+ /// 不进行打包的namespace
///
- [JsonPropertyName("modNameBlackList")]
- public List ModBlackList { get; set; }
+ public IEnumerable ExclusionNamespaces { get; set; }
+ }
+ ///
+ /// 浮动配置,可与命名空间下的文件合并
+ ///
+ public class FloatingConfig
+ {
+ ///
+ /// 强制包含的domain
+ ///
+ public IEnumerable InclusionDomains { get; set; }
+ ///
+ /// 强制排除的domain
+ ///
+ public IEnumerable ExclusionDomains { get; set; }
///
- /// 不进行打包的asset-domain
- /// 有可能作为基础文件
+ /// 强制包含的路径
///
- [JsonPropertyName("domainBlackList")]
- public List DomainBlackList { get; set; }
+ public IEnumerable ExclusionPaths { get; set; }
+ ///
+ /// 强制排除的路径
+ ///
+ public IEnumerable InclusionPaths { get; set; }
///
- /// (这不是基础文件!)
- /// 进入打包流程,但不按照语言文件格式化(也就不回避重复文件)
- /// 图片文件必须经过此流程!
- /// 按照namespace识别
+ /// 文本字符替换表
+ ///
+ public Dictionary CharacterReplacement { get; set; }
+ ///
+ /// 内容替换表
///
- [JsonPropertyName("noProcessNamespace")]
- public List BypassedNamespace { get; set; }
+ public Dictionary DestinationReplacement { get; set; }
///
- /// 字符替换表,从外源加载
+ /// 从另一对象合并配置
///
- public Dictionary CharatcerReplacement { get; set; } // 该项似乎无法通过 json 初始化
+ public FloatingConfig Merge(FloatingConfig other) => new()
+ {
+ ExclusionPaths = ExclusionPaths.Concat(other.ExclusionPaths).Distinct(),
+ ExclusionDomains = ExclusionDomains.Concat(other.ExclusionDomains).Distinct(),
+ InclusionDomains = InclusionDomains.Concat(other.InclusionDomains).Distinct(),
+ InclusionPaths = InclusionPaths.Concat(other.InclusionPaths).Distinct(),
+ CharacterReplacement = CharacterReplacement.Concat(other.CharacterReplacement).DistinctBy(_ => _.Key)
+ .ToDictionary(_ => _.Key, _ => _.Value),
+ DestinationReplacement = DestinationReplacement.Concat(other.DestinationReplacement).DistinctBy(_ => _.Key)
+ .ToDictionary(_ => _.Key, _ => _.Value)
+ };
+
+
}
}
diff --git a/src/Packer/Models/IResourceFileProvider.cs b/src/Packer/Models/IResourceFileProvider.cs
new file mode 100644
index 000000000000..76255e044182
--- /dev/null
+++ b/src/Packer/Models/IResourceFileProvider.cs
@@ -0,0 +1,59 @@
+using System;
+using System.IO.Compression;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace Packer.Models
+{
+ ///
+ /// 表示可以被添加到资源包中的内容
+ /// 可以有很多实现:文本文件、非文本文件、组合表......
+ ///
+ ///
+ /// 这是IMMUTABLE!
+ ///
+ public interface IResourceFileProvider
+ {
+ ///
+ /// 在本提供器的基础上,尝试添加新提供器中的内容
+ ///
+ /// 需要添加的新
+ /// 冲突解决方案。若为,保留本文件的内容;否则,保留新文件的内容
+ /// 合并得到的新
+ public IResourceFileProvider ApplyTo(IResourceFileProvider? incoming, bool overrideExisting = false)
+ // 默认实现
+ => !overrideExisting
+ ? (incoming ?? this) // 如果来源是null,无论冲突配置如何,都不应返回null
+ : this;
+
+ ///
+ /// 在该提供器的内容中执行替换
+ ///
+ /// 替换的匹配模式,使用
+ /// 替换文本
+ /// 替换得到的新
+ public IResourceFileProvider ReplaceContent(string searchPattern, string replacement)
+ // 默认实现
+ => this;
+
+ ///
+ /// 在该提供器的目标地址中执行替换
+ ///
+ /// 替换的匹配模式,使用
+ /// 替换文本
+ /// 替换得到的新
+ public IResourceFileProvider ReplaceDestination(string searchPattern, string replacement);
+
+ ///
+ /// 将该提供器的内容写入到资源包的正确位置
+ ///
+ ///
+ /// 资源包中已有同名文件
+ public Task WriteToArchive(ZipArchive archive);
+
+ ///
+ /// 目标在资源包中的相对位置,从根目录算起
+ ///
+ public string Destination { get; }
+ }
+}
diff --git a/src/Packer/Models/McMeta.cs b/src/Packer/Models/McMeta.cs
deleted file mode 100644
index 28f69882df73..000000000000
--- a/src/Packer/Models/McMeta.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace Packer.Models
-{
- ///
- /// MCMETA格式
- /// 唯一用处是写修改日期
- ///
- public class McMeta
- {
- ///
- ///
- ///
- [JsonPropertyName("pack")] public McMetaPack Pack { get; set; }
- }
-
- ///
- ///
- public class McMetaPack
- {
- ///
- ///
- [JsonPropertyName("pack_format")] public int Format { get; set; }
- ///
- ///
- [JsonPropertyName("description")] public string Description { get; set; }
- }
-}
\ No newline at end of file
diff --git a/src/Packer/Models/Mod.cs b/src/Packer/Models/Mod.cs
deleted file mode 100644
index 78f618397855..000000000000
--- a/src/Packer/Models/Mod.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Collections.Generic;
-
-namespace Packer.Models
-{
- ///
- /// 模组译文的抽象表示
- ///
- public class Mod
- {
- ///
- /// 模组名
- ///
- public string modName;
- ///
- /// 采用的asset,按asset-domain分
- ///
- public IEnumerable assets;
- }
-
- ///
- /// asset的抽象表示
- ///
- public class Asset
- {
- ///
- /// asset-domain名
- ///
- public string domainName;
- ///
- /// 该asset-domain下的文件
- ///
- public IEnumerable contents;
- ///
- /// domain合并,并解决重复文件问题
- ///
- /// 要合并的对象
- ///
- public Asset Combine(Asset other)
- {
-
- return new Asset()
- {
- domainName = this.domainName,
- contents = Utils.MergeFiles(this.contents, other.contents)
- };
- }
- }
-}
diff --git a/src/Packer/Models/PackerPolicy.cs b/src/Packer/Models/PackerPolicy.cs
new file mode 100644
index 000000000000..fb86d121b3ef
--- /dev/null
+++ b/src/Packer/Models/PackerPolicy.cs
@@ -0,0 +1,43 @@
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Packer.Models
+{
+ ///
+ /// Packer 的局域打包策略类型
+ ///
+ public enum PackerPolicyType
+ {
+ ///
+ /// 从当前位置加载文件。
+ ///
+ Direct,
+ ///
+ /// 从指定位置加载文件。
+ ///
+ Indirect,
+ ///
+ /// 从组合文件创建指定语言文件。
+ ///
+ Composition
+ }
+
+ ///
+ /// Packer的局域打包策略。包含策略类型,以及不同策略使用的额外参数
+ ///
+ public class PackerPolicy
+ {
+ ///
+ /// 打包策略的类型参数
+ ///
+ public PackerPolicyType Type { get; set; }
+
+ ///
+ /// 打包策略的额外参数
+ ///
+ [JsonExtensionData]
+ public Dictionary? Parameters { get; set; }
+ // JsonExtensionData要求以此种格式传入
+ }
+}
diff --git a/src/Packer/Models/PackerStrategy.cs b/src/Packer/Models/PackerStrategy.cs
deleted file mode 100644
index 835f51ddb073..000000000000
--- a/src/Packer/Models/PackerStrategy.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System.Collections.Generic;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Packer.Models
-{
- ///
- /// Packer 的局域打包策略类型
- ///
- public enum PackerStrategyType
- {
- ///
- /// 不进行额外操作,直接按照本处的文件结构打包
- /// 附加参数:无
- ///
- NoAction,
- ///
- /// 仅从某处复制文件,忽略当前目录的内容
- /// 附加参数:
- /// "source": string 复制源地址
- ///
- PlainClone,
- ///
- /// 使用本处的文件结构,随后从源地址补充文件
- /// 全体文件以本处优先
- /// 附加参数:
- /// "source": string 复制源地址
- ///
- CloneMissing,
- ///
- /// 使用此处的文件结构,仅对此处存在的条目从源地址更新
- /// 附加参数:
- /// "source": string 复制源地址
- ///
- BackPort,
- ///
- /// 从某处复制文件,然后应用由Google Diff-Match-Patch算法生成的修改项
- /// 附加参数:
- /// "source": string 复制源地址
- /// "patches": Dictionary<string, string> patch列表: patch目标 -> patch文本
- ///
- Patch
- }
-
- ///
- /// Packer的局域打包策略。包含策略类型,以及不同策略使用的额外参数
- ///
- public class PackerStrategy
- {
- ///
- /// 打包策略的类型参数
- ///
- [JsonPropertyName("type")]
- public PackerStrategyType Type { get; set; }
-
- ///
- /// 打包策略的额外参数
- ///
- [JsonExtensionData]
- public Dictionary Parameters { get; set; }
- // JsonExtensionData要求以此种格式传入
- }
-}
diff --git a/src/Packer/Models/Providers/CompositionHelper.cs b/src/Packer/Models/Providers/CompositionHelper.cs
new file mode 100644
index 000000000000..704bf65fd1a3
--- /dev/null
+++ b/src/Packer/Models/Providers/CompositionHelper.cs
@@ -0,0 +1,99 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+
+namespace Packer.Models.Providers
+{
+ using LangMappingProvider = TermMappingProvider;
+ using JsonMappingProvider = TermMappingProvider;
+
+ public static partial class LangMappingHelper
+ {
+ ///
+ /// 从组合文件创建
+ ///
+ /// 组合文件
+ public static LangMappingProvider CreateFromComposition(FileInfo file)
+ {
+ using var reader = file.OpenText();
+ var data = JsonSerializer.Deserialize(
+ reader.ReadToEnd(),
+ new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
+ return new LangMappingProvider(
+ new LangDictionaryWrapper(CompositionHelper.CreateRawDictionary(data)),
+ data.Target);
+ }
+ }
+
+ public static partial class JsonMappingHelper
+ {
+ ///
+ /// 从组合文件创建
+ ///
+ /// 组合文件
+ public static JsonMappingProvider CreateFromComposition(FileInfo file)
+ {
+ using var reader = file.OpenText();
+ var data = JsonSerializer.Deserialize(
+ reader.ReadToEnd(),
+ new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
+ return new JsonMappingProvider(
+ JsonDictionaryWrapper.Create(CompositionHelper.CreateRawDictionary(data)),
+ data.Target);
+ }
+ }
+
+ internal static class CompositionHelper
+ {
+ internal static Dictionary CreateRawDictionary(CompositionData data)
+ {
+ var query = from entry in data.Entries
+ let templates = entry.Templates
+ let parameters = entry.Parameters.CrossMap()
+ from parameter in parameters
+ from template in templates
+ let formattedKey = string.Format(template.Key, parameter.Key.ToArray())
+ let formattedValue = string.Format(template.Value, parameter.Value.ToArray())
+ select (formattedKey, formattedValue);
+ return query.ToDictionary(_ => _.formattedKey, _ => _.formattedValue);
+ }
+
+ internal static IEnumerable, IEnumerable>>
+ CrossMap(this IEnumerable>> origin)
+ => origin.Aggregate(
+ seed: new[] { KeyValuePair.Create(Enumerable.Empty(), Enumerable.Empty()) }
+ as IEnumerable, IEnumerable>>, // 这都需要手写...
+ (accumulate, next) => from incomingPair in next
+ join existingGroup in accumulate on true equals true
+ select KeyValuePair.Create(
+ existingGroup.Key.Append(incomingPair.Key),
+ existingGroup.Value.Append(incomingPair.Value)));
+ }
+
+
+ // composition format:
+ // - root
+ // * object
+ // - string target => 本文件产生组合的目标地址。
+ // * list
+ // * object
+ // * object templates => 组合所用的模板。所有内容采用C#格式化模式填写。
+ // - =>
+ // * list params => 参数表。参数按照列举顺序排列。不支持嵌套,必须字面量。
+ // * object
+ // - =>
+
+ internal struct CompositionData
+ {
+ public string Target { get; set; }
+ public List Entries { get; set; }
+ }
+
+ internal struct CompositionEntry
+ {
+ public Dictionary Templates { get; set; }
+ public List> Parameters { get; set; }
+ }
+}
diff --git a/src/Packer/Models/Providers/McMetaProvider.cs b/src/Packer/Models/Providers/McMetaProvider.cs
new file mode 100644
index 000000000000..a0616ebec22d
--- /dev/null
+++ b/src/Packer/Models/Providers/McMetaProvider.cs
@@ -0,0 +1,56 @@
+using Packer.Extensions;
+using Serilog;
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Packer.Models.Providers
+{
+ ///
+ /// 用于表示pack.mcmeta的提供器。写入时将会附加打包时间
+ ///
+ public class McMetaProvider : TextFile
+ {
+ internal McMetaProvider(string content, string destination) : base(content, destination) { }
+
+ ///
+ /// 从给定的构造提供器。
+ ///
+ /// 读取源
+ /// 目标地址
+ public static new McMetaProvider Create(FileInfo file, string destination)
+ {
+ using var stream = file.OpenRead();
+ using var reader = new StreamReader(stream, Encoding.UTF8);
+ var content = reader.ReadToEnd();
+ return new McMetaProvider(content, destination);
+ }
+
+ ///
+ public override string Destination => "pack.mcmeta";
+ ///
+ public override IResourceFileProvider ReplaceContent(string searchPattern, string replacement)
+ => this;
+ ///
+ public override IResourceFileProvider ReplaceDestination(string searchPattern, string replacement)
+ => this;
+ ///
+ public override async Task WriteToArchive(ZipArchive archive)
+ {
+ var destination = Destination.NormalizePath();
+ Log.Information("正在添加 {0}", destination);
+
+ var content = string.Format(Content, DateTime.UtcNow.AddHours(8) /* UTC +8:00 */);
+
+ archive.ValidateEntryDistinctness(destination);
+
+ using var writer = new StreamWriter(
+ archive.CreateEntry(destination)
+ .Open(),
+ new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
+ await writer.WriteAsync(content);
+ }
+ }
+}
diff --git a/src/Packer/Models/Providers/RawFile.cs b/src/Packer/Models/Providers/RawFile.cs
new file mode 100644
index 000000000000..101fcf8bfac5
--- /dev/null
+++ b/src/Packer/Models/Providers/RawFile.cs
@@ -0,0 +1,61 @@
+using Packer.Extensions;
+using Serilog;
+using System.IO;
+using System.IO.Compression;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace Packer.Models.Providers
+{
+ ///
+ /// 一般文件的提供器。不提供合并、替换支持
+ ///
+ ///
+ /// 对于非文本文件,使用该类
+ ///
+ public class RawFile : IResourceFileProvider
+ {
+ ///
+ /// 文件的源地址
+ ///
+ public FileInfo SourceFile { get; }
+
+ ///
+ public string Destination { get; }
+
+ ///
+ /// 从给定的构造提供器
+ ///
+ /// 源文件的引用
+ /// 目标地址
+ public RawFile(FileInfo sourceFile, string destination)
+ {
+ SourceFile = sourceFile;
+ Destination = destination;
+ }
+
+
+ ///
+ public IResourceFileProvider ReplaceDestination(string searchPattern, string replacement)
+ => new RawFile(SourceFile,
+ Regex.Replace(Destination,
+ searchPattern,
+ replacement,
+ RegexOptions.Singleline));
+
+ ///
+ public async Task WriteToArchive(ZipArchive archive)
+ {
+ var destination = Destination.NormalizePath();
+ Log.Information("正在添加 {0}", destination);
+
+ archive.ValidateEntryDistinctness(destination);
+
+ // 为什么这ZipArchive.CreateEntryFromFile没有Async变种...只有手动实现了
+ using var source = SourceFile.OpenRead();
+ using var entry = archive.CreateEntry(destination)
+ .Open();
+ await source.CopyToAsync(entry);
+ }
+ }
+}
diff --git a/src/Packer/Models/Providers/TermMappingProvider.cs b/src/Packer/Models/Providers/TermMappingProvider.cs
new file mode 100644
index 000000000000..755d25620d52
--- /dev/null
+++ b/src/Packer/Models/Providers/TermMappingProvider.cs
@@ -0,0 +1,315 @@
+using Packer.Extensions;
+using Serilog;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace Packer.Models.Providers
+{
+ using JsonMappingProvider = TermMappingProvider;
+ using LangMappingProvider = TermMappingProvider;
+
+ #region termDictionary
+ ///
+ /// 可以作为语言文件使用的键值对包装
+ ///
+ /// 内部使用的值类型
+ public interface ITermDictionary : IDictionary
+ {
+ ///
+ /// 从内部的键值对表示生成正确的语言文件文本
+ ///
+ /// 生成文本
+ public string ProvideStringContent();
+ ///
+ /// 在内部键值对的值中,对于文本部分,执行给定的替换。
+ ///
+ /// 替换的匹配模式,使用
+ /// 替换文本
+ /// 替换得到的新
+ public ITermDictionary ReplaceContent(string searchPattern, string replacement);
+ }
+
+ internal class LangDictionaryWrapper : Dictionary, ITermDictionary
+ {
+ public LangDictionaryWrapper(IDictionary dictionary) : base(dictionary) { }
+
+ public string ProvideStringContent()
+ {
+ var builder = new StringBuilder();
+ foreach (var (key, value) in this)
+ {
+ builder.AppendJoin('=', key, value);
+ builder.Append(Environment.NewLine);
+ }
+ return builder.ToString();
+ }
+
+ public ITermDictionary ReplaceContent(string searchPattern, string replacement)
+ {
+ var result = new Dictionary();
+ foreach (var (key, value) in this)
+ {
+ var replaced = Regex.Replace(value,
+ searchPattern,
+ replacement,
+ RegexOptions.Singleline);
+ result.Add(key, replaced);
+ }
+ return new LangDictionaryWrapper(result);
+ }
+ }
+
+ internal class JsonDictionaryWrapper : Dictionary, ITermDictionary
+ {
+ public JsonDictionaryWrapper(IDictionary dictionary) : base(dictionary) { }
+
+ public string ProvideStringContent()
+ => JsonSerializer.Serialize(this, new JsonSerializerOptions()
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+ });
+
+ public ITermDictionary ReplaceContent(string searchPattern, string replacement)
+ {
+ var result = new Dictionary();
+ foreach (var (key, value) in this)
+ {
+ if (value.GetType() == typeof(JsonValue)
+ && value.AsValue().TryGetValue(out var stringValue))
+ {
+ var replaced = Regex.Replace(stringValue,
+ searchPattern,
+ replacement,
+ RegexOptions.Singleline);
+ result.Add(key, JsonValue.Create(replaced)!); // 我猜不会null罢
+ continue;
+ }
+ result.Add(key, value);
+ }
+ return new JsonDictionaryWrapper(result);
+ }
+
+ public static ITermDictionary Create(IDictionary nominalMapping)
+ {
+ var query = from pair in nominalMapping
+ let key = pair.Key
+ let value = pair.Value
+ let node = JsonValue.Create(value)!
+ select (key, node);
+ var transformed = query.ToDictionary(_ => _.key, _ => _.node as JsonNode);
+ return new JsonDictionaryWrapper(transformed);
+ }
+ }
+
+ #endregion
+
+ #region Provider
+ ///
+ /// 语言文件的提供器。支持内容替换、文件合并
+ ///
+ ///
+ /// 对于在lang/下的文件,使用该类型
+ ///
+ /// 内部使用的值类型
+ public class TermMappingProvider : IResourceFileProvider
+ {
+ ///
+ /// 语言文件所表示的映射表
+ ///
+ public ITermDictionary Map { get; }
+
+ ///
+ public string Destination { get; }
+
+ ///
+ /// 从给定的映射表构造提供器
+ ///
+ /// 映射表
+ /// 目标地址
+ public TermMappingProvider(ITermDictionary map, string destination)
+ {
+ Map = map;
+ Destination = destination;
+ }
+
+ ///
+ public IResourceFileProvider ApplyTo(IResourceFileProvider? incoming, bool overrideExisting = false)
+ {
+ if (incoming is null) return this;
+ if (incoming is not TermMappingProvider inProvider)
+ throw new ArgumentException($"Argument not an instance of {typeof(TermMappingProvider)}.",
+ nameof(incoming));
+ //var inProvider = incoming as TermMappingProvider;
+
+ //if (inProvider is null) throw new ArgumentNullException(nameof(incoming));
+
+ var (baseMap, inMap) = overrideExisting
+ ? (Map, inProvider.Map)
+ : (inProvider.Map, Map); // 交换顺序
+
+ foreach (var pair in inMap)
+ {
+ baseMap.TryAdd(pair.Key, pair.Value);
+ }
+
+ return new TermMappingProvider(baseMap, Destination);
+ }
+
+ ///
+ public IResourceFileProvider ReplaceContent(string searchPattern, string replacement)
+ => new TermMappingProvider(
+ Map.ReplaceContent(searchPattern, replacement),
+ Destination);
+
+ ///
+ public IResourceFileProvider ReplaceDestination(string searchPattern, string replacement)
+ => new TermMappingProvider(
+ Map,
+ Regex.Replace(Destination,
+ searchPattern,
+ replacement,
+ RegexOptions.Singleline));
+
+ ///
+ public async Task WriteToArchive(ZipArchive archive)
+ {
+ var destination = Destination.NormalizePath();
+ Log.Information("正在添加 {0}", destination);
+
+ archive.ValidateEntryDistinctness(destination);
+
+ using var writer = new StreamWriter(
+ archive.CreateEntry(destination)
+ .Open(),
+ new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
+ await writer.WriteAsync(Map.ProvideStringContent());
+ }
+ }
+ #endregion
+
+ #region Helper
+ ///
+ /// 用于生成的辅助类
+ ///
+ public static partial class LangMappingHelper
+ {
+ ///
+ /// 从给定的语言文件创建
+ ///
+ /// 读取源
+ /// 目标地址
+ public static LangMappingProvider CreateFromFile(FileInfo file, string destination)
+ {
+ using var stream = file.OpenRead();
+ using var reader = new StreamReader(stream, Encoding.UTF8);
+ var content = reader.ReadToEnd();
+ return new(new LangDictionaryWrapper(DeserializeFromLang(content)), destination);
+ }
+
+ internal static Dictionary DeserializeFromLang(string content)
+ {
+ var result = new Dictionary();
+ var isInComment = false; // 处理多行注释
+ var isParseEscape = false;
+ var isLineContinuation = false;
+ var pendingKey = "";
+ var pendingValue = "";
+
+ foreach (var line in content.AsSpan().EnumerateLines())
+ {
+ if (isLineContinuation) // 行尾转义符,用于换行
+ {
+ if (line.EndsWith("\\"))
+ {
+ pendingValue += line[..^1].ToString();
+ }
+ else
+ {
+ pendingValue += ("\\n" + line.ToString());
+ result.TryAdd(pendingKey, pendingValue);
+ isLineContinuation = false;
+ }
+ continue;
+ }
+
+ if (!isParseEscape && line.Equals("#PARSE_ESCAPES", StringComparison.Ordinal))
+ {
+ isParseEscape = true;
+ continue;
+ }
+
+ if (isInComment)
+ { // 多行注释内
+ if (line.Trim()
+ .EndsWith("*/"))
+ isInComment = false; // 跳出注释
+ continue;
+ }
+
+ if (line.StartsWith("//") | line.StartsWith("#") | line.StartsWith("<"))
+ continue;
+
+ if (line.StartsWith("/*"))
+ { // 开始多行注释
+ isInComment = true;
+ continue;
+ }
+
+ if (line.IsEmpty || line.IsWhiteSpace()) // 空行需去
+ continue;
+
+ // 基础条目
+ var splitPosition = line.IndexOf('=');
+
+ var key = line[..splitPosition];
+ var value = splitPosition + 1 < line.Length
+ ? line[(splitPosition + 1)..]
+ : "";
+ if (isParseEscape && value.EndsWith("\\"))
+ {
+ isLineContinuation = true;
+ pendingKey = key.ToString();
+ pendingValue = value[..^1].ToString();
+ }
+ else
+ {
+ result.TryAdd(key.ToString(), value.ToString());
+ }
+ }
+
+ return result;
+ }
+ }
+
+ ///
+ /// 用于生成的辅助类
+ ///
+ public static partial class JsonMappingHelper
+ {
+ ///
+ /// 从给定的语言文件创建
+ ///
+ /// 读取源
+ /// 目标地址
+ public static JsonMappingProvider CreateFromFile(FileInfo file, string destination)
+ {
+ using var stream = file.OpenRead();
+ return new(
+ new JsonDictionaryWrapper(
+ JsonSerializer.Deserialize>(
+ stream,
+ new JsonSerializerOptions { ReadCommentHandling = JsonCommentHandling.Skip })!),
+ destination);
+ }
+ }
+ #endregion
+}
diff --git a/src/Packer/Models/Providers/TextFile.cs b/src/Packer/Models/Providers/TextFile.cs
new file mode 100644
index 000000000000..c6207b5a5323
--- /dev/null
+++ b/src/Packer/Models/Providers/TextFile.cs
@@ -0,0 +1,80 @@
+using Packer.Extensions;
+using Serilog;
+using System.IO;
+using System.IO.Compression;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace Packer.Models.Providers
+{
+ ///
+ /// 文本文件的提供器,支持内容替换,但不支持文件合并
+ ///
+ ///
+ /// 对于普通的文本文件,使用该类
+ ///
+ public class TextFile : IResourceFileProvider
+ {
+ ///
+ /// 提供器所携带的文本内容
+ ///
+ public virtual string Content { get; }
+
+ ///
+ public virtual string Destination { get; }
+
+ ///
+ /// 从给定的构造提供器
+ ///
+ /// 读取源
+ /// 目标地址
+ public static TextFile Create(FileInfo file, string destination)
+ {
+ using var stream = file.OpenRead();
+ using var reader = new StreamReader(stream, Encoding.UTF8);
+ var content = reader.ReadToEnd();
+ return new TextFile(content, destination);
+ }
+
+ ///
+ /// 从给定的文本内容构造提供器
+ ///
+ /// 来源文本
+ /// 目标地址
+ public TextFile(string content, string destination)
+ {
+ Content = content;
+ Destination = destination;
+ }
+
+ ///
+ public virtual IResourceFileProvider ReplaceContent(string searchPattern, string replacement)
+ => new TextFile(Regex.Replace(Content,
+ searchPattern,
+ replacement,
+ RegexOptions.Singleline),
+ Destination);
+ ///
+ public virtual IResourceFileProvider ReplaceDestination(string searchPattern, string replacement)
+ => new TextFile(Content,
+ Regex.Replace(Destination,
+ searchPattern,
+ replacement,
+ RegexOptions.Singleline));
+ ///
+ public virtual async Task WriteToArchive(ZipArchive archive)
+ {
+ var destination = Destination.NormalizePath();
+ Log.Information("正在添加 {0}", destination);
+
+ archive.ValidateEntryDistinctness(destination);
+
+ using var writer = new StreamWriter(
+ archive.CreateEntry(destination)
+ .Open(),
+ new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
+ await writer.WriteAsync(Content);
+ }
+ }
+}
diff --git a/src/Packer/Models/TranslatedFile.cs b/src/Packer/Models/TranslatedFile.cs
deleted file mode 100644
index 2c3afc390a33..000000000000
--- a/src/Packer/Models/TranslatedFile.cs
+++ /dev/null
@@ -1,229 +0,0 @@
-using DiffMatchPatch;
-using Packer.Extensions;
-using Serilog;
-using System;
-using System.Collections.Generic;
-using System.IO;
-
-namespace Packer.Models
-{
- ///
- /// 标志文件类型的枚举。目前而言有[是否按.json]和[是否按\lang\]两类
- ///
- [Flags]
- public enum FileCategory
- {
- ///
- /// 仅用于初始化为默认态
- ///
- None = 0,
- ///
- /// 类 .json,标识需要转义与 json 式序列化
- ///
- JsonAlike = 1,
- ///
- /// 类 .lang,标识无需转义与 lang 式序列化
- ///
- LangAlike = 2,
- ///
- /// 位于 /lang/ 中的文件,进行合并等
- ///
- LanguageFile = 4,
- ///
- /// 位于其余位置的文件,不进行合并
- ///
- OtherFiles = 8,
- ///
- /// */lang/*.json
- ///
- JsonTranslationFormat = JsonAlike | LanguageFile,
- ///
- /// */lang/*.lang
- ///
- LangTranslationFormat = LangAlike | LanguageFile,
- ///
- /// */[not-lang]/*.json
- ///
- JsonOthers = JsonAlike | OtherFiles,
- ///
- /// */[not-lang]/*.lang
- ///
- LangOthers = LangAlike | OtherFiles
- }
-
- ///
- /// 语言文本的抽象。这是基本类
- /// 基本上是immutable的
- ///
- public class TranslatedFile
- {
- ///
- /// asset-domain下的位置
- ///
- public string RelativePath { get; init; }
-
- ///
- /// 该文件的文本,用字符串表示
- /// 因此,不能存储非文本文件!
- ///
- public string StringifiedContent { get; }
-
- ///
- /// 文件类型
- ///
- public FileCategory Category { get; }
-
- ///
- /// 从文件流构造内容
- ///
- public TranslatedFile(Stream stream, FileCategory category, Config config)
- { // 注:文件流在此处被关闭
- using var reader = new StreamReader(stream);
- StringifiedContent = reader.ReadToEnd().Preprocess(category, config);
- this.Category = category;
- }
-
- ///
- /// 从文本构造内容
- ///
- public TranslatedFile(FileCategory category, string content)
- {
- this.Category = category;
- StringifiedContent = content;
- }
-
- ///
- /// 伪合并文件
- ///
- virtual public TranslatedFile Combine(TranslatedFile file)
- {
- Log.Information("文件不支持合并。取消合并");
- return this;
- }
-
- ///
- /// 伪适配文件
- ///
- virtual public TranslatedFile Port(TranslatedFile file)
- {
- Log.Information("文件不支持适配。直接覆盖");
- return file;
- }
-
- ///
- /// 对该文件的内容进行Google Diff-Match-Patch算法
- ///
- public TranslatedFile ApplyPatch(string patch)
- {
- // 对应的Patch可以自行生成,或者也可以做一个小工具,虽然不在这里
- // 应用Patch时,需要先根据Patch文本生成Patch列表,再应用Patch
- //
- // patch_apply 返回object[] [0]=string [1]=bool[]
- var dmp = new diff_match_patch();
- var patchList = dmp.patch_fromText(patch);
- return new TranslatedFile(Category,
- (string)dmp.patch_apply(patchList, StringifiedContent)[0]);
- }
- }
-
- ///
- /// 可以按照/lang/文件夹下解析的文件。这是衍生类
- /// 基本上是immutable的
- ///
- public class LangFile : TranslatedFile
- {
- private bool deserialized = false; // 非必要不解析,免得残废的lang解析炸掉
- private Dictionary _deserializedContent;
-
- ///
- /// 按照基础语言文件的格式解析而成的词条列表。
- /// 在访问时才会解析。
- ///
- public Dictionary DeserializedContent
- {
- get
- {
- if (!deserialized)
- {
- deserialized = true;
- _deserializedContent = StringifiedContent.DeserializeAsset(Category);
- }
- return _deserializedContent;
- }
- }
-
- ///
- /// 从文件流构造内容
- ///
- public LangFile(Stream stream, FileCategory category, Config config)
- : base(stream, category, config)
- {
- _deserializedContent = null;
- }
- ///
- /// 从映射表构造内容
- ///
- public LangFile(FileCategory category, Dictionary content)
- : base(category, content.SerializeAsset(category))
- {
- _deserializedContent = content;
- deserialized = true;
- }
- ///
- /// 真合并文件
- ///
- public override LangFile Combine(TranslatedFile file)
- {
- Log.Information("合并文件:{0}", this.RelativePath);
-
- var castedFile = (LangFile)file;
- if (castedFile is null)
- {
- Log.Information("检测到不支持合并的文件。取消合并");
- return this;
- }
-
- var resultMap = new Dictionary(DeserializedContent);
- foreach (var pair in castedFile.DeserializedContent)
- {
- if (!resultMap.TryAdd(pair.Key, pair.Value))
- {
- Log.Warning("检测到相同 key 的条目:{0} -> {1} | {2},选取 {1}",
- pair.Key, resultMap[pair.Key], pair.Value);
- }
- }
-
- return new LangFile(this.Category, resultMap)
- {
- RelativePath = this.RelativePath
- };
- }
-
- ///
- /// 真适配文件
- ///
- public override TranslatedFile Port(TranslatedFile file)
- {
- var castedFile = (LangFile)file;
- if (castedFile is null)
- {
- Log.Information("检测到不支持合并的文件。取消合并");
- return file;
- }
-
- var resultMap = DeserializedContent;
- foreach(var key in resultMap.Keys)
- {
- if(castedFile.DeserializedContent.TryGetValue(key, out var value))
- {
- Log.Information("正在替换适配项:<{0}> {1} => {2}", key, resultMap[key], value);
- resultMap[key] = value;
- }
- }
- return new LangFile(this.Category, resultMap)
- {
- RelativePath = this.RelativePath
- };
- }
- }
-}
diff --git a/src/Packer/Packer.csproj b/src/Packer/Packer.csproj
index db77fb7f587b..718ec94626ad 100644
--- a/src/Packer/Packer.csproj
+++ b/src/Packer/Packer.csproj
@@ -3,10 +3,12 @@
Exe
net7.0
+ enable
+ none
-
+
diff --git a/src/Packer/Program.cs b/src/Packer/Program.cs
index e6b53d3e90ac..e135e8646f5e 100644
--- a/src/Packer/Program.cs
+++ b/src/Packer/Program.cs
@@ -1,16 +1,21 @@
using Packer.Extensions;
+using Packer.Helpers;
+using Packer.Models;
+using Packer.Models.Providers;
using Serilog;
using System;
+using System.Collections;
using System.IO;
using System.IO.Compression;
+using System.Linq;
using System.Threading.Tasks;
namespace Packer
{
class Program
{
- // 由于某些魔法,这里可以直接加参数
- public static async Task Main(string version = null)
+ // System.CommandLine.DragonFruit支持
+ public static async Task Main(string version, bool increment = false)
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
@@ -18,39 +23,68 @@ public static async Task Main(string version = null)
.MinimumLevel.Information() // 以便 debug 时修改这一等级
.CreateLogger();
- if (version is null)
- {
- throw new ArgumentNullException(nameof(version));
- }
-
- var config = await Utils.RetrieveConfig(configPath: "./config/packer.json",
- mappingPath: "./config/fontmap.txt",
+ var config = await ConfigHelpers.RetrieveConfig(configTemplate: "./config/packer/{0}.json",
version: version);
+ Log.Information("开始对版本 {0} 的打包", config.Base.Version);
- if (config is null)
- {
- throw new ArgumentException("无效的版本参数", nameof(version));
- }
+ var targetModIdentifiers = increment ? GitHelpers.EnumerateChangedMods(config.Base.Version)
+ : Enumerable.Empty();
- // Packer输出的文件名,可以随时更改
- string packName = $"./Minecraft-Mod-Language-Package-{config.Version}.zip";
+ var query = // 这就是查询表达式吗(
+ from modDirectory in new DirectoryInfo($"./projects/{config.Base.Version}/assets")
+ .EnumerateDirectories()
+ let modIdentifier = modDirectory.Name
+ where targetModIdentifiers.Count() == 0 // 未提供列表,全部打包
+ || targetModIdentifiers.Contains(modIdentifier) // 有列表,仅打包列表中的项
+ where !config.Base.ExclusionMods.Contains(modIdentifier) // 没有被明确排除
+ from namespaceDirectory in modDirectory.EnumerateDirectories()
+ let namespaceName = namespaceDirectory.Name
+ where !config.Base.ExclusionNamespaces.Contains(namespaceName) // 没有被明确排除
+ where namespaceName.ValidateNamespace() // 不是非法名称
+ from provider in namespaceDirectory.EnumerateProviders(config)
+ group provider by provider.Destination into destinationGroup
+ select destinationGroup
+ .Aggregate(seed: null as IResourceFileProvider, // 合并文件
+ (accumulate, next)
+ => next.ApplyTo(
+ accumulate,
+ overrideExisting: false)) into provider
+ select config.Floating.CharacterReplacement // 内容的字符替换
+ .Aggregate(seed: provider,
+ (accumulate, replacement)
+ => accumulate.ReplaceContent(
+ replacement.Key,
+ replacement.Value)) into provider
+ select config.Floating.DestinationReplacement // 全局路径替换:预留
+ .Aggregate(seed: provider,
+ (accumulate, replacement)
+ => accumulate.ReplaceDestination(
+ replacement.Key,
+ replacement.Value));
- Log.Information("开始对版本 {0} 的打包", config.Version);
+ var initialsQuery = from file in new DirectoryInfo($"./projects/{config.Base.Version}")
+ .EnumerateFiles()
+ select (file.Name == "pack.mcmeta")
+ ? McMetaProvider.Create(file, file.Name) // 类型推断不出要用接口
+ : new RawFile(file, file.Name) as IResourceFileProvider;
- Utils.CreateTimeStamp(config.Version);
+ string packName = $"./Minecraft-Mod-Language-Package-{config.Base.Version}.zip";
await using var stream = File.Create(packName);
- var archive = new ZipArchive(stream, ZipArchiveMode.Update, leaveOpen: true);
- archive.Initialize(config);
- await archive.WriteContent(Lib.RetrieveContent(config, out var bypassed));
- archive.WriteBypassed(bypassed); // 将跳过的文件一并加入
+ using (var archive = new ZipArchive(stream, ZipArchiveMode.Update, leaveOpen: true))
+ {
+ await Task.WhenAll(from provider in query.Concat(initialsQuery)
+ select provider.WriteToArchive(archive));
+ }
- Log.Information("对版本 {0} 的打包结束", config.Version);
- archive.Dispose(); // 关闭压缩文档,不关闭流
+ Log.Information("对版本 {0} 的打包结束。共写入了 {1} + {2} 个文件",
+ config.Base.Version,
+ initialsQuery.Count(),
+ query.Count());
+ var md5 = stream.ComputeMD5();
- // 临时操作
- File.WriteAllText($"./{config.Version}.md5", stream.ComputeMD5());
- stream.Dispose();
+ Log.Information("打包文件的 MD5 值:{0}", md5);
+ File.WriteAllText($"./{config.Base.Version}.md5", md5);
}
}
}
diff --git a/src/Packer/Utils.cs b/src/Packer/Utils.cs
deleted file mode 100644
index dd928d9f4d00..000000000000
--- a/src/Packer/Utils.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-using Packer.Models;
-using Serilog;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text.Encodings.Web;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using System.Threading.Tasks;
-
-
-namespace Packer
-{
- static class Utils
- {
- public static async Task RetrieveConfig(string configPath, string mappingPath, string version)
- {
- Log.Information("正在获取配置");
- var reader = await File.ReadAllBytesAsync(configPath);
- var configs = JsonSerializer.Deserialize>(reader);
- var replacement = await ReadReplaceFontMap(mappingPath);
- configs.ForEach(_ => _.CharatcerReplacement = replacement);
- return configs.Where(_ => _.Version == version).FirstOrDefault(); // 仅选取指定版本,忽略重复
- }
-
- public static PackerStrategy RetrieveStrategy(FileInfo file)
- => file is null
- ? new PackerStrategy { Type = PackerStrategyType.NoAction } // 默认类型
- : JsonSerializer.Deserialize(file.OpenText().ReadToEnd(), new JsonSerializerOptions
- {
- Converters = { new JsonStringEnumConverter() }
- });
-
- ///
- /// 将两个带有TranslatedFile的列表合并,对冲突项按照target优先进行合并。
- ///
- /// 合并对象,优先选择
- /// 合并对象,非优先
- ///
- public static IEnumerable MergeFiles(IEnumerable baseFile, IEnumerable incoming)
- {
- var mapping = new Dictionary(); // asset-domain下的目标位置 -> 文件
- if (!baseFile.Any()) return incoming;
- if (!incoming.Any()) return baseFile;
- foreach (var file in baseFile)
- {
- mapping.Add(file.RelativePath, file);
- }
- foreach (var file in incoming)
- {
- if (!mapping.TryAdd(file.RelativePath, file))
- {
- mapping.Remove(file.RelativePath, out var existing);
- mapping.Add(existing.RelativePath, existing.Combine(file));
- }
- }
- return mapping.Values;
- }
-
- ///
- /// 基于原位置的文件key,用incoming的词条进行替换。未被替换的保持原文。
- ///
- /// 原位置的基准文件
- /// 更新文件
- ///
- public static IEnumerable PortFiles(IEnumerable baseFile, IEnumerable incoming)
- {
- var mapping = new Dictionary(); // asset-domain下的目标位置 -> 文件
- if (!incoming.Any()) return baseFile;
- foreach (var file in baseFile)
- {
- mapping.Add(file.RelativePath, file);
- }
- foreach (var file in incoming)
- {
- if (!mapping.TryAdd(file.RelativePath, file))
- {
- mapping.Remove(file.RelativePath, out var existing);
- mapping.Add(existing.RelativePath, existing.Port(file));
- }
- }
- return mapping.Values;
- }
-
- public static async Task> ReadReplaceFontMap(string path) // 从隔壁弄过来改了一下,就放这里了
- {
- var mapping = new Dictionary();
- foreach (string str in await File.ReadAllLinesAsync(path))
- {
- var kv = str.Split('>', StringSplitOptions.TrimEntries);
- var key = kv[0];
- var value = kv[1];
- Log.Verbose("添加了映射:{0} -> {1}", key, value);
- mapping.Add(key, value);
- }
- return mapping;
- }
-
- // 下面的这些...其实都不是我写的...
-
- public static void CreateTimeStamp(string version)
- {
- var mcmeta = $"./projects/{version}/pack.mcmeta";
- var meta = JsonSerializer.Deserialize(File.ReadAllText(mcmeta));
- var time = DateTime.UtcNow.AddHours(8);
- meta.Pack.Description += $"\n打包时间:{time:yyyy-MM-ddTHH:mm:ssZ}";
- var result = JsonSerializer.Serialize(meta, new JsonSerializerOptions()
- {
- Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
- WriteIndented = true
- });
- File.WriteAllText(mcmeta, result);
- }
- }
-}
diff --git a/src/Uploader/Program.cs b/src/Uploader/Program.cs
index 8a03ccbea28f..745b0c7f8914 100644
--- a/src/Uploader/Program.cs
+++ b/src/Uploader/Program.cs
@@ -33,9 +33,9 @@ static int Main(string host, string name, string password)
// 获取可用的资源包,准备上传
- var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
- var packList = currentDirectory
- .EnumerateFiles("Minecraft-Mod-Language-Package-*.zip");
+ var artifactDirectory = new DirectoryInfo(Path.Join(Directory.GetCurrentDirectory(), "artifacts"));
+ var packList = artifactDirectory
+ .EnumerateFiles("Minecraft-Mod-Language-Package-*.zip", SearchOption.AllDirectories);
Log.Information("检测到的资源包数目:{0}", packList.Count());
@@ -70,8 +70,8 @@ static int Main(string host, string name, string password)
});
// 临时操作 在使用旧md5校验的程序弃用以后需要删除
- var md5List = currentDirectory
- .EnumerateFiles("*.md5");
+ var md5List = artifactDirectory
+ .EnumerateFiles("*.md5", SearchOption.AllDirectories);
md5List.ToList()
.ForEach(_ =>
{