Rename plugin to MagicGem Reforged
This commit is contained in:
+117
-354
@@ -1,395 +1,178 @@
|
|||||||
# MagicGem 迁移指南
|
# MagicGem → MagicGem Reforged 升级指南
|
||||||
|
|
||||||
本文说明如何从原版 MagicGem 或之前的 CMagicGem 重实现迁移到当前版本。迁移范围包括插件 JAR、配置文件、宝石、技能、GUI、语言文件,以及玩家物品中的宝石数据。
|
本文只描述从原版 MagicGem 升级到 MagicGem Reforged 的流程。当前版本不扫描或导入 `plugins/CMagicGem`,也不执行多个重实现目录之间的合并。
|
||||||
|
|
||||||
当前版本面向 Minecraft/Paper 1.21.x(包括 1.21.11),使用 Java 21。插件保留原插件的非授权功能,不实现网络授权或机器绑定。
|
MagicGem Reforged 面向 Minecraft/Paper 1.21.x(包括 1.21.11),使用 Java 21。它保留原插件的非授权功能,不实现网络授权或机器绑定。
|
||||||
|
|
||||||
## 迁移前的目录和插件身份
|
## 升级前的状态
|
||||||
|
|
||||||
当前插件的身份仍然是 `MagicGem`:
|
原版服务器通常是:
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: MagicGem
|
|
||||||
main: pku.yim.magicgem.MagicGem
|
|
||||||
```
|
|
||||||
|
|
||||||
主命令是 `/mgem`,别名是 `/cmgem` 和 `/cmg`。新旧 JAR 不能同时运行,因为它们使用相同的插件名、数据目录和命令。
|
|
||||||
|
|
||||||
常见的数据目录有四种情况:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
# 原版 MagicGem
|
|
||||||
plugins/
|
plugins/
|
||||||
|
├─ MagicGem-旧版本.jar
|
||||||
└─ MagicGem/
|
└─ MagicGem/
|
||||||
|
├─ config.yml
|
||||||
# 之前的 CMagicGem 重实现
|
├─ gems/
|
||||||
plugins/
|
├─ skills/
|
||||||
└─ CMagicGem/
|
├─ gui/
|
||||||
|
└─ languages/
|
||||||
# 两个目录同时存在
|
|
||||||
plugins/
|
|
||||||
├─ MagicGem/
|
|
||||||
└─ CMagicGem/
|
|
||||||
|
|
||||||
# 全新安装
|
|
||||||
plugins/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
当前版本始终把 `plugins/MagicGem` 作为有效目录。`plugins/CMagicGem` 只作为旧数据来源导入。
|
升级后的结构是:
|
||||||
|
|
||||||
## 第一次迁移前的准备
|
```text
|
||||||
|
plugins/
|
||||||
|
├─ MagicGem-Reforged-0.1.0-SNAPSHOT.jar
|
||||||
|
├─ MagicGem/
|
||||||
|
└─ MagicGem-Reforged/
|
||||||
|
```
|
||||||
|
|
||||||
1. 停止服务器,不能在服务器运行时替换 JAR 或修改插件数据。
|
`MagicGem` 是迁移来源,`MagicGem-Reforged` 是当前有效数据目录。
|
||||||
2. 备份整个服务器,至少包括以下内容:
|
|
||||||
|
## 正式升级步骤
|
||||||
|
|
||||||
|
1. 停止服务器,不能在运行中替换插件 JAR 或修改插件数据。
|
||||||
|
2. 备份整个服务器,至少包括:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
plugins/MagicGem/
|
plugins/MagicGem/
|
||||||
plugins/CMagicGem/
|
|
||||||
world/
|
world/
|
||||||
world_nether/
|
world_nether/
|
||||||
world_the_end/
|
world_the_end/
|
||||||
```
|
```
|
||||||
|
|
||||||
玩家背包、末影箱、容器和世界区块中的物品数据不在插件目录中,只备份插件目录不足以完整回滚。
|
玩家背包、末影箱、容器和世界区块中的物品数据不在插件目录中,只备份 `plugins/MagicGem` 不足以完整回滚。
|
||||||
3. 移除旧版 MagicGem JAR 和旧版 CMagicGem JAR。
|
3. 移除旧版 MagicGem JAR。不要同时加载旧版和重制版。
|
||||||
4. 将当前版本 JAR 放入 `plugins/`。
|
4. 将 `MagicGem-Reforged` JAR 放入 `plugins/`。
|
||||||
5. 保留 `plugins/MagicGem` 和 `plugins/CMagicGem`,不要在第一次启动前删除旧目录。
|
5. 保留 `plugins/MagicGem`,不要提前重命名、删除或清空它。
|
||||||
6. 使用 Java 21 启动 Paper 1.21.x。
|
6. 使用 Java 21 启动 Paper 1.21.x。
|
||||||
|
7. 等待迁移完成后检查控制台日志,再执行游戏内验证。
|
||||||
|
|
||||||
## 启动时的执行顺序
|
## 插件身份和命令
|
||||||
|
|
||||||
当前插件启动时会按照以下顺序工作:
|
重制版的 `plugin.yml` 使用:
|
||||||
|
|
||||||
1. Paper 读取 `plugin.yml`,以 `MagicGem` 身份加载插件。
|
```yaml
|
||||||
2. `onLoad()` 预加载 NBT API。
|
name: MagicGem-Reforged
|
||||||
3. 执行旧目录导入器,检查 `plugins/CMagicGem`。
|
main: cn.infstar.magicgemreforged.MagicGemReforged
|
||||||
4. 如果缺少默认 `config.yml`,创建当前版本的默认配置。
|
```
|
||||||
5. 只为不存在的文件写入内置宝石、技能、GUI 和语言资源。
|
|
||||||
6. 读取语言配置。
|
|
||||||
7. 初始化宝石注册表、技能注册表、GUI、物品编解码器和软依赖集成。
|
|
||||||
8. 执行完整 reload,读取配置并加载宝石、技能和 GUI。
|
|
||||||
9. 注册 `/mgem` 命令和事件监听器。
|
|
||||||
10. 输出集成状态和已加载宝石数量。
|
|
||||||
|
|
||||||
迁移必须发生在宝石和技能注册表加载之前。物品里保存的是宝石名或技能名,完整定义仍然来自 `gems/*.yml` 和 `skills/*.yml`。
|
主命令仍然是 `/mgem`,别名仍然是 `/cmgem` 和 `/cmg`。命令兼容不代表旧 JAR 可以同时运行;服务器中只能安装当前 JAR。
|
||||||
|
|
||||||
## 情况一:只有原版 `plugins/MagicGem`
|
Java 主类已经是独立的新入口 `MagicGemReforged`。旧入口 `pku.yim.magicgem.MagicGem` 作为兼容父类保留,供依赖原版公开 API 的其他插件继续解析;它不会作为 Paper 的实际入口加载。
|
||||||
|
|
||||||
这种情况下不需要复制或重命名目录。当前版本会直接沿用:
|
## 第一次启动时的目录迁移
|
||||||
|
|
||||||
|
启动时,迁移器只检查:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
plugins/MagicGem/config.yml
|
plugins/MagicGem/
|
||||||
plugins/MagicGem/gems/*.yml
|
|
||||||
plugins/MagicGem/skills/*.yml
|
|
||||||
plugins/MagicGem/gui/*.yml
|
|
||||||
plugins/MagicGem/languages/*.yml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
迁移器发现当前数据目录就是 `plugins/MagicGem` 后会跳过目录导入。已有配置不会被默认配置覆盖,已有资源文件也不会被内置资源覆盖。
|
目标是当前插件的数据目录:
|
||||||
|
|
||||||
旧配置中的以下写法都可以读取:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
lang: zh_CN
|
|
||||||
```
|
|
||||||
|
|
||||||
或:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
language: zh_CN
|
|
||||||
```
|
|
||||||
|
|
||||||
操作流程就是停止服务器、替换 JAR、保留 `plugins/MagicGem`、启动服务器。
|
|
||||||
|
|
||||||
## 情况二:只有 `plugins/CMagicGem`
|
|
||||||
|
|
||||||
如果不存在 `plugins/MagicGem`,但存在 `plugins/CMagicGem`,当前版本会创建 `plugins/MagicGem`,并导入以下内容:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
CMagicGem/gems → MagicGem/gems
|
plugins/MagicGem-Reforged/
|
||||||
CMagicGem/skills → MagicGem/skills
|
|
||||||
CMagicGem/gui → MagicGem/gui
|
|
||||||
CMagicGem/languages → MagicGem/languages
|
|
||||||
CMagicGem/config.yml → MagicGem/config.yml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
旧的 `plugins/CMagicGem` 源目录不会被删除,也不会被修改。
|
会迁移以下内容:
|
||||||
|
|
||||||
如果旧目录没有 `config.yml`,插件会在后续启动阶段创建当前版本默认配置。如果旧目录有配置而目标目录没有配置,插件会先创建默认配置,再用旧配置中的值覆盖对应默认项,同时保留新版本新增的配置项。
|
|
||||||
|
|
||||||
## 情况三:两个目录同时存在
|
|
||||||
|
|
||||||
当两个目录都存在时:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
plugins/MagicGem/ # 有效目录,优先级高
|
MagicGem/config.yml → MagicGem-Reforged/config.yml
|
||||||
plugins/CMagicGem/ # 旧目录,作为补充来源
|
MagicGem/gems/ → MagicGem-Reforged/gems/
|
||||||
|
MagicGem/skills/ → MagicGem-Reforged/skills/
|
||||||
|
MagicGem/gui/ → MagicGem-Reforged/gui/
|
||||||
|
MagicGem/languages/ → MagicGem-Reforged/languages/
|
||||||
```
|
```
|
||||||
|
|
||||||
迁移器只从 `CMagicGem` 补充目标目录中缺少的文件和配置项。`MagicGem` 中已经存在的内容优先保留。
|
迁移器只复制目标中不存在的文件,因此原版文件内容会完整保留。它不会删除、改写或移动 `plugins/MagicGem`。
|
||||||
|
|
||||||
例如旧文件是:
|
如果 `MagicGem-Reforged` 已经存在,例如上一次迁移中断后重新启动:
|
||||||
|
|
||||||
```yaml
|
- 目标中不存在的文件继续复制;
|
||||||
Shared:
|
- 目标中内容相同的文件跳过;
|
||||||
Name: Old Name
|
- 目标中内容不同的文件保留目标版本;
|
||||||
Material: DIAMOND
|
- 冲突的原版文件会保存到 `migration-backup/MagicGem/`;
|
||||||
OldOnly: retained
|
- 原版源目录始终保留。
|
||||||
```
|
|
||||||
|
|
||||||
当前目录中的文件是:
|
迁移器只处理 `config.yml`、`gems/`、`skills/`、`gui/` 和 `languages/`。其他原版目录不会自动复制,遇到自定义数据时应在备份后手动检查。
|
||||||
|
|
||||||
```yaml
|
|
||||||
Shared:
|
|
||||||
Name: Current Name
|
|
||||||
Material: DIAMOND
|
|
||||||
CurrentOnly: retained
|
|
||||||
```
|
|
||||||
|
|
||||||
合并后的目标文件是:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
Shared:
|
|
||||||
Name: Current Name
|
|
||||||
Material: DIAMOND
|
|
||||||
OldOnly: retained
|
|
||||||
CurrentOnly: retained
|
|
||||||
```
|
|
||||||
|
|
||||||
也就是说,目标目录中的 `Name` 优先,旧目录独有的 `OldOnly` 会补入,目标目录独有的 `CurrentOnly` 也会保留。
|
|
||||||
|
|
||||||
## 文件合并规则
|
|
||||||
|
|
||||||
迁移器处理以下目录:
|
|
||||||
|
|
||||||
```text
|
|
||||||
gems/
|
|
||||||
skills/
|
|
||||||
gui/
|
|
||||||
languages/
|
|
||||||
```
|
|
||||||
|
|
||||||
每个文件按照以下规则处理:
|
|
||||||
|
|
||||||
| 情况 | 行为 |
|
|
||||||
| --- | --- |
|
|
||||||
| 目标文件不存在 | 从 `CMagicGem` 复制到 `MagicGem` |
|
|
||||||
| YAML 文件同名且内容不同 | 递归合并,目标值优先 |
|
|
||||||
| YAML 中只有旧目录存在的配置项 | 补入目标文件 |
|
|
||||||
| YAML 中双方值不同 | 保留 `MagicGem` 值,记录冲突 |
|
|
||||||
| 非 YAML 文件内容相同 | 跳过 |
|
|
||||||
| 非 YAML 文件内容不同 | 保留目标文件,记录冲突 |
|
|
||||||
| 目标文件已存在 | 不直接覆盖 |
|
|
||||||
|
|
||||||
YAML 合并可以处理同一个文件中不同的宝石、技能或语言段落,而不是简单地按文件覆盖。
|
|
||||||
|
|
||||||
## `config.yml` 合并规则
|
|
||||||
|
|
||||||
如果目标目录中没有 `config.yml`:
|
|
||||||
|
|
||||||
1. 先生成当前版本默认配置。
|
|
||||||
2. 导入旧配置中的字段。
|
|
||||||
3. 旧配置存在的值覆盖默认值。
|
|
||||||
4. 旧配置不存在的新字段继续保留默认值。
|
|
||||||
|
|
||||||
例如当前默认配置是:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
language: zh_CN
|
|
||||||
workbench: true
|
|
||||||
backpack: false
|
|
||||||
```
|
|
||||||
|
|
||||||
旧配置是:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
lang: en_US
|
|
||||||
backpack: true
|
|
||||||
```
|
|
||||||
|
|
||||||
最终结果会保留旧设置和新默认项,效果类似:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
language: en_US
|
|
||||||
lang: en_US
|
|
||||||
workbench: true
|
|
||||||
backpack: true
|
|
||||||
```
|
|
||||||
|
|
||||||
如果目标配置已经存在,则目标配置优先,旧配置中目标没有的字段会补入:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# MagicGem/config.yml
|
|
||||||
language: zh_CN
|
|
||||||
backpack: false
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# CMagicGem/config.yml
|
|
||||||
language: en_US
|
|
||||||
backpack: true
|
|
||||||
old-option: kept
|
|
||||||
```
|
|
||||||
|
|
||||||
最终目标配置会保持:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
language: zh_CN
|
|
||||||
backpack: false
|
|
||||||
old-option: kept
|
|
||||||
```
|
|
||||||
|
|
||||||
## 冲突备份
|
|
||||||
|
|
||||||
发生冲突时,旧源文件会保存到:
|
|
||||||
|
|
||||||
```text
|
|
||||||
plugins/MagicGem/migration-backup/CMagicGem/
|
|
||||||
```
|
|
||||||
|
|
||||||
例如:
|
|
||||||
|
|
||||||
```text
|
|
||||||
plugins/CMagicGem/gems/Shared.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
会备份为:
|
|
||||||
|
|
||||||
```text
|
|
||||||
plugins/MagicGem/migration-backup/CMagicGem/gems/Shared.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
配置文件冲突则会备份为:
|
|
||||||
|
|
||||||
```text
|
|
||||||
plugins/MagicGem/migration-backup/CMagicGem/config.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
控制台会列出冲突字段,并说明 `MagicGem` 的值被保留。旧源目录本身保持不变。
|
|
||||||
|
|
||||||
如果源文件和目标文件内容完全相同,不会被当作冲突,也不会产生额外备份。
|
|
||||||
|
|
||||||
## 迁移完成标记
|
## 迁移完成标记
|
||||||
|
|
||||||
完整迁移成功后会创建:
|
成功完成一次迁移后,目标目录会创建:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
plugins/MagicGem/.migration-cmagicgem-v1.complete
|
plugins/MagicGem-Reforged/.migration-magicgem-reforged-v1.complete
|
||||||
```
|
```
|
||||||
|
|
||||||
该标记用于防止每次服务器启动都重复执行相同迁移。
|
再次启动时发现该标记,迁移器会跳过重复复制。
|
||||||
|
|
||||||
如果第一次迁移过程中发生 IO 错误或 YAML 格式错误:
|
如果迁移过程中出现文件权限或 IO 错误:
|
||||||
|
|
||||||
- 不会写入完成标记;
|
- 不会删除原版目录;
|
||||||
- 不会删除旧源目录;
|
- 会记录错误日志;
|
||||||
- 控制台会输出错误日志;
|
- 不会把本次启动标记为已完成;
|
||||||
- 下次启动会再次尝试迁移。
|
- 下一次启动会再次尝试。
|
||||||
|
|
||||||
如果迁移完成后又修改了 `plugins/CMagicGem`,这些新修改不会自动再次导入,因为完成标记已经存在。需要重新导入时,应先停止服务器、备份当前 `plugins/MagicGem`、删除完成标记,再重新启动。
|
如果确实需要重新扫描原版目录,应停止服务器,先备份当前 `MagicGem-Reforged`,再删除完成标记并重新启动。不要在没有备份的情况下删除标记。
|
||||||
|
|
||||||
## 玩家物品的迁移方式
|
## 配置和资源加载顺序
|
||||||
|
|
||||||
插件目录迁移和玩家物品迁移是两个独立过程。
|
迁移发生在插件默认资源写入和注册表加载之前:
|
||||||
|
|
||||||
当前版本不会在启动时遍历所有玩家背包并一次性重写全部物品,而是采用兼容读取和延迟迁移。物品在被识别、镶嵌、拆卸、强化或执行技能相关操作时,才会逐步写入当前格式。
|
1. 复制原版文件到 `MagicGem-Reforged`;
|
||||||
|
2. 只为缺失文件写入当前版本默认资源;
|
||||||
|
3. 读取 `MagicGem-Reforged/config.yml`;
|
||||||
|
4. 加载 `gems/`、`skills/`、`gui/` 和 `languages/`;
|
||||||
|
5. 注册命令与事件。
|
||||||
|
|
||||||
物品可能包含以下数据格式:
|
因此原版自定义配置会优先存在,当前版本新增的默认文件也会补齐。原版配置中的 `lang` 和当前配置中的 `language` 都可以识别。
|
||||||
|
|
||||||
```text
|
## 玩家物品兼容
|
||||||
当前 MagicGem PDC
|
|
||||||
旧 CMagicGem PDC
|
|
||||||
原版 MagicGem NBT
|
|
||||||
Lore 标识
|
|
||||||
```
|
|
||||||
|
|
||||||
当前版本会依次尝试读取:
|
目录迁移不会遍历所有玩家物品。物品采用兼容读取和按需写回,玩家拿到物品、使用宝石、镶嵌、拆卸或触发技能时才会逐步写入当前格式。
|
||||||
|
|
||||||
```text
|
### 宝石身份
|
||||||
magicgem:gem_name
|
|
||||||
cmagicgem:gem_name
|
|
||||||
MAGICGEM_NAME
|
|
||||||
```
|
|
||||||
|
|
||||||
如果允许使用 Lore 识别,还会把宝石 Lore 作为备用依据。
|
原版宝石物品中的:
|
||||||
|
|
||||||
### 宝石名称
|
|
||||||
|
|
||||||
原版物品通常包含:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
MAGICGEM_NAME
|
MAGICGEM_NAME
|
||||||
```
|
```
|
||||||
|
|
||||||
当前版本会使用这个名称去查找已经加载的宝石定义。识别成功后会写入当前 PDC:
|
仍然可以被识别。识别成功后,重制版会写入自己的当前数据,同时保留原版名称字段。
|
||||||
|
|
||||||
```text
|
|
||||||
magicgem:gem_name
|
|
||||||
```
|
|
||||||
|
|
||||||
原版 `MAGICGEM_NAME` 作为兼容字段继续保留。新创建的宝石也会同时写入当前标识和原版名称字段。
|
|
||||||
|
|
||||||
如果物品中的宝石名称存在,但 `gems/*.yml` 没有迁移或加载失败,插件无法恢复完整宝石定义。
|
|
||||||
|
|
||||||
### 镶嵌内容
|
### 镶嵌内容
|
||||||
|
|
||||||
原版镶嵌数据通常位于:
|
原版镶嵌数据:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
MAGICGEM_EMBED
|
MAGICGEM_EMBED
|
||||||
```
|
```
|
||||||
|
|
||||||
它是一个包含宝石名称和数量的复合 NBT,例如:
|
仍然可以读取。执行镶嵌、拆卸或相关物品操作时,重制版会更新当前数据并继续写入兼容的原版 NBT 字段。
|
||||||
|
|
||||||
```text
|
|
||||||
{
|
|
||||||
"AttributeGem": 2,
|
|
||||||
"EnchantGem": 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
当前版本会按以下顺序读取:
|
|
||||||
|
|
||||||
1. `magicgem:embedded_gems`;
|
|
||||||
2. `cmagicgem:embedded_gems`;
|
|
||||||
3. `MAGICGEM_EMBED`;
|
|
||||||
4. 兼容旧格式的嵌套 NBT。
|
|
||||||
|
|
||||||
读取旧 NBT 后,物品在后续使用时会写入当前 PDC,同时继续写入兼容的 `MAGICGEM_EMBED`。执行镶嵌或拆卸时,这个受插件管理的字段会被重新整理;其他无关物品数据不会被主动删除。
|
|
||||||
|
|
||||||
### 技能列表
|
### 技能列表
|
||||||
|
|
||||||
原版技能数据通常位于:
|
原版技能列表:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
MAGICGEM_SKILL
|
MAGICGEM_SKILL
|
||||||
```
|
```
|
||||||
|
|
||||||
当前版本会按以下顺序读取:
|
仍然可以读取。技能能否执行取决于对应的 `skills/*.yml` 是否成功迁移并加载。
|
||||||
|
|
||||||
|
## 启动后验证
|
||||||
|
|
||||||
|
应在控制台确认:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
magicgem:skills
|
Imported original plugins/MagicGem into MagicGem-Reforged: X files copied, Y conflicts.
|
||||||
cmagicgem:skills
|
MagicGem Reforged 已启用,命令 /mgem,加载 N 颗宝石。
|
||||||
MAGICGEM_SKILL
|
|
||||||
```
|
```
|
||||||
|
|
||||||
读取成功后,技能名称会按当前格式保存,并继续写入兼容的 `MAGICGEM_SKILL`。技能是否能实际执行,还取决于对应的 `skills/*.yml` 是否已经加载。
|
然后执行:
|
||||||
|
|
||||||
## 启动后的检查
|
|
||||||
|
|
||||||
迁移成功时,控制台应出现类似日志:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Imported plugins/CMagicGem into plugins/MagicGem: X files copied, Y files merged, Z files with conflicts.
|
|
||||||
```
|
|
||||||
|
|
||||||
插件正常启用时,应出现类似日志:
|
|
||||||
|
|
||||||
```text
|
|
||||||
MagicGem 已启用,命令 /mgem,加载 N 颗宝石。
|
|
||||||
```
|
|
||||||
|
|
||||||
建议执行以下检查:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
/mgem view
|
/mgem view
|
||||||
@@ -398,61 +181,41 @@ MagicGem 已启用,命令 /mgem,加载 N 颗宝石。
|
|||||||
/mgem reload
|
/mgem reload
|
||||||
```
|
```
|
||||||
|
|
||||||
还应实际测试:
|
至少验证:
|
||||||
|
|
||||||
- 旧宝石是否可以识别;
|
- 旧宝石可以识别和使用;
|
||||||
- 旧镶嵌装备是否显示正确;
|
- 已镶嵌装备的宝石数量正确;
|
||||||
- 拆卸和重新镶嵌是否正常;
|
- 拆卸和重新镶嵌正常;
|
||||||
- 技能是否能够触发;
|
- 原有技能可以触发;
|
||||||
- GUI 是否可以打开;
|
- GUI 和语言设置正确;
|
||||||
- 中文或英文语言是否正确;
|
- Vault、PlayerPoints、PlaceholderAPI、MythicMobs 等软依赖状态正常;
|
||||||
- Vault、PlayerPoints、PlaceholderAPI、MythicMobs 等软依赖是否正常;
|
- 重启服务器后数据仍然存在。
|
||||||
- 服务器重启后数据是否仍然存在。
|
|
||||||
|
|
||||||
如果日志显示宝石数量明显少于旧版本,应优先检查 `plugins/MagicGem/gems/` 中的 YAML 是否完整,以及控制台是否有宝石解析错误。
|
如果重制版加载的宝石数量异常,先检查 `plugins/MagicGem-Reforged/gems/` 是否完整,再查看 YAML 解析错误。
|
||||||
|
|
||||||
## 回滚方式
|
## 回滚
|
||||||
|
|
||||||
最安全的回滚方式是停止服务器并恢复迁移前的完整备份:
|
发生问题时:
|
||||||
|
|
||||||
1. 停止服务器。
|
1. 停止服务器。
|
||||||
2. 备份当前运行后的日志和数据,方便排查问题。
|
2. 保存当前日志和 `plugins/MagicGem-Reforged`,用于排查。
|
||||||
3. 恢复旧版 MagicGem JAR。
|
3. 移除重制版 JAR。
|
||||||
4. 恢复原来的 `plugins/MagicGem`。
|
4. 恢复原版 MagicGem JAR。
|
||||||
5. 必要时恢复玩家数据和世界备份。
|
5. 使用备份恢复 `plugins/MagicGem`、世界和玩家数据。
|
||||||
6. 启动服务器验证。
|
6. 启动旧版服务器验证。
|
||||||
|
|
||||||
如果只有 `plugins/CMagicGem`,迁移器不会删除它,因此它可以继续作为旧配置来源。但是如果新版本运行期间已经改变了玩家物品或世界数据,仍然建议恢复完整服务器备份,而不是只恢复插件 JAR。
|
迁移器不会删除 `plugins/MagicGem`,所以仅目录迁移失败时原版数据仍然存在。但如果重制版运行期间已经改变了玩家物品或世界数据,建议恢复完整服务器备份。
|
||||||
|
|
||||||
## 当前实现的注意事项
|
## 升级检查清单
|
||||||
|
|
||||||
配置文件包含:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
legacy-migration: true
|
|
||||||
```
|
|
||||||
|
|
||||||
但当前启动代码在读取配置前就调用目录导入器,因此这个开关目前不会阻止 `CMagicGem` 目录迁移。正常升级不需要修改它;如果以后需要支持关闭迁移,应将导入调用调整到配置加载之后,并显式检查该设置。
|
|
||||||
|
|
||||||
另外,目录识别使用的旧目录名是准确的:
|
|
||||||
|
|
||||||
```text
|
|
||||||
plugins/CMagicGem
|
|
||||||
```
|
|
||||||
|
|
||||||
其他自定义目录名不会被自动扫描。需要使用其他目录名时,应在停服并备份后手动改名为 `CMagicGem`,或者手动合并文件。
|
|
||||||
|
|
||||||
## 迁移完成检查清单
|
|
||||||
|
|
||||||
- [ ] 已停止服务器并备份插件、世界和玩家数据。
|
- [ ] 已停止服务器并备份插件、世界和玩家数据。
|
||||||
- [ ] 已移除旧版 MagicGem/CMagicGem JAR。
|
- [ ] 已移除旧版 MagicGem JAR。
|
||||||
- [ ] 新 JAR 已放入 `plugins/`。
|
- [ ] 已放入 MagicGem-Reforged JAR。
|
||||||
- [ ] `plugins/MagicGem` 已保留。
|
- [ ] `plugins/MagicGem` 原版目录仍然保留。
|
||||||
- [ ] `plugins/CMagicGem` 在确认迁移成功前已保留。
|
- [ ] `plugins/MagicGem-Reforged` 中已生成迁移后的配置和资源。
|
||||||
- [ ] 控制台显示迁移结果或明确显示没有旧目录。
|
- [ ] 控制台没有迁移或 YAML 解析错误。
|
||||||
- [ ] 没有未处理的 YAML 解析错误。
|
- [ ] 宝石数量与原版预期一致。
|
||||||
- [ ] 宝石数量与预期一致。
|
|
||||||
- [ ] 旧宝石、镶嵌装备和技能已实际测试。
|
- [ ] 旧宝石、镶嵌装备和技能已实际测试。
|
||||||
- [ ] GUI、语言和软依赖功能已测试。
|
- [ ] GUI、语言和软依赖已测试。
|
||||||
- [ ] 已保存冲突备份和迁移前备份。
|
- [ ] 已保留冲突备份和原始服务器备份。
|
||||||
- [ ] 已重启服务器确认数据仍然正常。
|
- [ ] 已重启服务器验证持久化结果。
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
# MagicGem
|
# MagicGem Reforged
|
||||||
|
|
||||||
MagicGem 是面向 Minecraft/Paper 1.21.x(包括 1.21.11)的插件重实现,使用 Java 21 和 Paper API。它保留原插件的非授权功能,不实现网络授权或机器绑定。
|
MagicGem Reforged 是 MagicGem 的高版本重制版,面向 Minecraft/Paper 1.21.x(包括 1.21.11),使用 Java 21 和 Paper API。它保留原插件的非授权功能,不实现网络授权或机器绑定。
|
||||||
|
|
||||||
## 安装与迁移
|
## 安装与升级
|
||||||
|
|
||||||
1. 停止服务器并备份整个服务器目录,尤其是 `plugins/MagicGem`、`plugins/CMagicGem` 和玩家数据。
|
当前插件身份是 `MagicGem-Reforged`,数据目录是 `plugins/MagicGem-Reforged`。升级只支持这一条路径:
|
||||||
2. 使用 Java 21 启动 Paper 1.21.x,将新 JAR 放入 `plugins`,移除旧 MagicGem JAR。不要同时加载新旧 JAR。
|
|
||||||
3. 如果有 `plugins/MagicGem`,新版本会沿用它作为主数据目录,宝石、技能、GUI、语言、配置和已有物品按原路径继续读取。
|
|
||||||
4. 如果还保留先前重实现的 `plugins/CMagicGem`,新版本会将其中的 `gems`、`skills`、`gui`、`languages` 和 `config.yml` 导入 `plugins/MagicGem`。缺失文件和 YAML 配置项会自动补入。
|
|
||||||
5. 两个目录有同名冲突时,`plugins/MagicGem` 的值优先;冲突的 `CMagicGem` 原文件副本会写入 `plugins/MagicGem/migration-backup/CMagicGem`。源 `plugins/CMagicGem` 保持不变,迁移完成后会写入 `.migration-cmagicgem-v1.complete` 标记,避免每次启动重复导入。
|
|
||||||
6. 启动后检查控制台的导入数量、冲突提示和配置加载结果。确认一切正常前,请保留备份和旧数据目录。
|
|
||||||
|
|
||||||
原版 `MagicGem` 数据已经在 `plugins/MagicGem` 时无需复制或重命名目录。物品读取兼容原版 `MAGICGEM_*` NBT 和先前 `CMagicGem` 的 PDC 数据,并继续写入原版 NBT 字段以兼容升级过程。
|
```text
|
||||||
|
原版 MagicGem → MagicGem Reforged
|
||||||
|
plugins/MagicGem → plugins/MagicGem-Reforged
|
||||||
|
```
|
||||||
|
|
||||||
完整迁移步骤、目录合并规则、冲突备份、物品兼容和回滚流程见 [MIGRATION.md](MIGRATION.md)。
|
升级前停止服务器并备份整个服务器,尤其是 `plugins/MagicGem`、世界目录和玩家数据。移除旧 MagicGem JAR,放入新 JAR,保留 `plugins/MagicGem`,然后启动服务器。
|
||||||
|
|
||||||
主命令是 `/mgem`,并提供 `/cmgem` 和 `/cmg` 别名。
|
首次启动时,插件会把原版目录中的 `config.yml`、`gems/`、`skills/`、`gui/` 和 `languages/` 复制到 `plugins/MagicGem-Reforged`。原版目录不会删除。如果目标文件已经存在,目标文件优先,发生冲突时旧文件会保存到 `plugins/MagicGem-Reforged/migration-backup/MagicGem/`。
|
||||||
|
|
||||||
|
完整的升级顺序、文件处理、物品数据兼容和回滚流程见 [MIGRATION.md](MIGRATION.md)。
|
||||||
|
|
||||||
|
主命令是 `/mgem`,并提供 `/cmgem` 和 `/cmg` 别名。原版物品中的 `MAGICGEM_NAME`、`MAGICGEM_EMBED` 和 `MAGICGEM_SKILL` 会继续兼容。
|
||||||
|
|
||||||
## 命令
|
## 命令
|
||||||
|
|
||||||
@@ -37,4 +39,4 @@ MagicGem 是面向 Minecraft/Paper 1.21.x(包括 1.21.11)的插件重实现
|
|||||||
mvn clean package
|
mvn clean package
|
||||||
```
|
```
|
||||||
|
|
||||||
产物位于 `target/cmagicgem-0.1.0-SNAPSHOT.jar`。
|
产物位于 `target/MagicGem-Reforged-0.1.0-SNAPSHOT.jar`。
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>cn.infstar</groupId>
|
<groupId>cn.infstar</groupId>
|
||||||
<artifactId>cmagicgem</artifactId>
|
<artifactId>magicgem-reforged</artifactId>
|
||||||
<version>0.1.0-SNAPSHOT</version>
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>cmagicgem</name>
|
<name>MagicGem-Reforged</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>21</java.version>
|
<java.version>21</java.version>
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
<build>
|
<build>
|
||||||
<defaultGoal>clean package</defaultGoal>
|
<defaultGoal>clean package</defaultGoal>
|
||||||
|
<finalName>MagicGem-Reforged-${project.version}</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public class CMagicGem extends JavaPlugin {
|
|||||||
+ ", PlayerPoints=" + points.status()
|
+ ", PlayerPoints=" + points.status()
|
||||||
+ ", PlaceholderAPI=" + placeholders.status()
|
+ ", PlaceholderAPI=" + placeholders.status()
|
||||||
+ ", MythicMobs=" + mythic.status());
|
+ ", MythicMobs=" + mythic.status());
|
||||||
getLogger().info("MagicGem 已启用,命令 /mgem,加载 " + gemRegistry.all().size() + " 颗宝石。");
|
getLogger().info("MagicGem Reforged 已启用,命令 /mgem,加载 " + gemRegistry.all().size() + " 颗宝石。");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reloadAll() {
|
public void reloadAll() {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public final class CMagicGemCommand implements CommandExecutor, TabCompleter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean help(CommandSender sender) {
|
private boolean help(CommandSender sender) {
|
||||||
sender.sendMessage(itemCodec.color("&6MagicGem &7- &f/mgem"));
|
sender.sendMessage(itemCodec.color("&6MagicGem Reforged &7- &f/mgem"));
|
||||||
List<String> help = languages.section("command.help");
|
List<String> help = languages.section("command.help");
|
||||||
if (help.isEmpty()) help = List.of("&e/mgem give <玩家> <宝石> [数量]",
|
if (help.isEmpty()) help = List.of("&e/mgem give <玩家> <宝石> [数量]",
|
||||||
"&e/mgem apply <玩家> <宝石> [槽位]", "&e/mgem remove <玩家> <宝石> [槽位]",
|
"&e/mgem apply <玩家> <宝石> [槽位]", "&e/mgem remove <玩家> <宝石> [槽位]",
|
||||||
@@ -155,7 +155,7 @@ public final class CMagicGemCommand implements CommandExecutor, TabCompleter {
|
|||||||
String fallback = switch (scope) {
|
String fallback = switch (scope) {
|
||||||
case "gem" -> "&a宝石配置已重载,共加载 {amount} 颗宝石。";
|
case "gem" -> "&a宝石配置已重载,共加载 {amount} 颗宝石。";
|
||||||
case "skill" -> "&a技能配置已重载。";
|
case "skill" -> "&a技能配置已重载。";
|
||||||
default -> "&aMagicGem 已安全重载,共加载 {amount} 颗宝石。";
|
default -> "&aMagicGem Reforged 已安全重载,共加载 {amount} 颗宝石。";
|
||||||
};
|
};
|
||||||
sender.sendMessage(itemCodec.color(languages.text("cmagic.command.reload_" + scope, fallback,
|
sender.sendMessage(itemCodec.color(languages.text("cmagic.command.reload_" + scope, fallback,
|
||||||
"amount", registry.all().size())));
|
"amount", registry.all().size())));
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ package cn.infstar.cmagicgem.config;
|
|||||||
import org.bukkit.configuration.file.FileConfiguration;
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
|
||||||
public record PluginSettings(boolean workbench, boolean backpack, boolean gemLore, boolean gemCraft,
|
public record PluginSettings(boolean workbench, boolean backpack, boolean gemLore, boolean gemCraft,
|
||||||
boolean skillEngine, boolean skillEngineLoreColor, boolean legacyMigration) {
|
boolean skillEngine, boolean skillEngineLoreColor) {
|
||||||
public static PluginSettings load(FileConfiguration config) {
|
public static PluginSettings load(FileConfiguration config) {
|
||||||
return new PluginSettings(config.getBoolean("workbench", true), config.getBoolean("backpack", false),
|
return new PluginSettings(config.getBoolean("workbench", true), config.getBoolean("backpack", false),
|
||||||
config.getBoolean("gem-lore", false), config.getBoolean("gem-craft", false),
|
config.getBoolean("gem-lore", false), config.getBoolean("gem-craft", false),
|
||||||
config.getBoolean("skill-engine", true), config.getBoolean("skill-engine-lore-color", false),
|
config.getBoolean("skill-engine", true), config.getBoolean("skill-engine-lore-color", false));
|
||||||
config.getBoolean("legacy-migration", true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,9 +41,6 @@ public final class ItemCodec {
|
|||||||
private final NamespacedKey dataVersionKey;
|
private final NamespacedKey dataVersionKey;
|
||||||
private final NamespacedKey embeddedKey;
|
private final NamespacedKey embeddedKey;
|
||||||
private final NamespacedKey skillsKey;
|
private final NamespacedKey skillsKey;
|
||||||
private final NamespacedKey cMagicGemNameKey;
|
|
||||||
private final NamespacedKey cMagicGemEmbeddedKey;
|
|
||||||
private final NamespacedKey cMagicGemSkillsKey;
|
|
||||||
private final LegacyItemMigrator legacyMigrator;
|
private final LegacyItemMigrator legacyMigrator;
|
||||||
|
|
||||||
public ItemCodec(JavaPlugin plugin, GemRegistry registry) {
|
public ItemCodec(JavaPlugin plugin, GemRegistry registry) {
|
||||||
@@ -52,9 +49,6 @@ public final class ItemCodec {
|
|||||||
this.dataVersionKey = new NamespacedKey(plugin, "data_version");
|
this.dataVersionKey = new NamespacedKey(plugin, "data_version");
|
||||||
this.embeddedKey = new NamespacedKey(plugin, "embedded_gems");
|
this.embeddedKey = new NamespacedKey(plugin, "embedded_gems");
|
||||||
this.skillsKey = new NamespacedKey(plugin, "skills");
|
this.skillsKey = new NamespacedKey(plugin, "skills");
|
||||||
this.cMagicGemNameKey = new NamespacedKey("cmagicgem", "gem_name");
|
|
||||||
this.cMagicGemEmbeddedKey = new NamespacedKey("cmagicgem", "embedded_gems");
|
|
||||||
this.cMagicGemSkillsKey = new NamespacedKey("cmagicgem", "skills");
|
|
||||||
this.legacyMigrator = new LegacyItemMigrator(registry);
|
this.legacyMigrator = new LegacyItemMigrator(registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +135,6 @@ public final class ItemCodec {
|
|||||||
if (item == null || item.getType().isAir() || !item.hasItemMeta()) return Optional.empty();
|
if (item == null || item.getType().isAir() || !item.hasItemMeta()) return Optional.empty();
|
||||||
ItemMeta meta = item.getItemMeta();
|
ItemMeta meta = item.getItemMeta();
|
||||||
String name = meta.getPersistentDataContainer().get(gemNameKey, PersistentDataType.STRING);
|
String name = meta.getPersistentDataContainer().get(gemNameKey, PersistentDataType.STRING);
|
||||||
if (name == null) name = meta.getPersistentDataContainer().get(cMagicGemNameKey, PersistentDataType.STRING);
|
|
||||||
Optional<GemDefinition> direct = registry.find(name);
|
Optional<GemDefinition> direct = registry.find(name);
|
||||||
if (direct.isPresent()) return direct;
|
if (direct.isPresent()) return direct;
|
||||||
LegacyItemMigrator.LegacyData legacy = legacyMigrator.inspect(item);
|
LegacyItemMigrator.LegacyData legacy = legacyMigrator.inspect(item);
|
||||||
@@ -169,7 +162,6 @@ public final class ItemCodec {
|
|||||||
if (item == null || !item.hasItemMeta()) return Map.of();
|
if (item == null || !item.hasItemMeta()) return Map.of();
|
||||||
PersistentDataContainer data = item.getItemMeta().getPersistentDataContainer();
|
PersistentDataContainer data = item.getItemMeta().getPersistentDataContainer();
|
||||||
String encoded = data.get(embeddedKey, PersistentDataType.STRING);
|
String encoded = data.get(embeddedKey, PersistentDataType.STRING);
|
||||||
if (encoded == null) encoded = data.get(cMagicGemEmbeddedKey, PersistentDataType.STRING);
|
|
||||||
Map<String, Integer> result = new LinkedHashMap<>();
|
Map<String, Integer> result = new LinkedHashMap<>();
|
||||||
if (encoded == null || encoded.isBlank()) {
|
if (encoded == null || encoded.isBlank()) {
|
||||||
LegacyItemMigrator.LegacyData legacy = legacyMigrator.inspect(item);
|
LegacyItemMigrator.LegacyData legacy = legacyMigrator.inspect(item);
|
||||||
@@ -224,7 +216,6 @@ public final class ItemCodec {
|
|||||||
if (item == null || !item.hasItemMeta()) return new ArrayList<>();
|
if (item == null || !item.hasItemMeta()) return new ArrayList<>();
|
||||||
PersistentDataContainer data = item.getItemMeta().getPersistentDataContainer();
|
PersistentDataContainer data = item.getItemMeta().getPersistentDataContainer();
|
||||||
String value = data.get(skillsKey, PersistentDataType.STRING);
|
String value = data.get(skillsKey, PersistentDataType.STRING);
|
||||||
if (value == null) value = data.get(cMagicGemSkillsKey, PersistentDataType.STRING);
|
|
||||||
if (value == null || value.isBlank()) {
|
if (value == null || value.isBlank()) {
|
||||||
List<String> legacy = new ArrayList<>(legacyMigrator.inspect(item).skills());
|
List<String> legacy = new ArrayList<>(legacyMigrator.inspect(item).skills());
|
||||||
if (!legacy.isEmpty()) writeSkills(item, legacy);
|
if (!legacy.isEmpty()) writeSkills(item, legacy);
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package cn.infstar.cmagicgem.migration;
|
package cn.infstar.cmagicgem.migration;
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
|
||||||
import org.bukkit.configuration.InvalidConfigurationException;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -15,234 +12,124 @@ import java.util.Objects;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/** Imports files left by the previous CMagicGem reimplementation without touching the source directory. */
|
/** Imports the original MagicGem data directory into MagicGem Reforged once. */
|
||||||
public final class LegacyDirectoryImporter {
|
public final class LegacyDirectoryImporter {
|
||||||
private static final String LEGACY_PLUGIN_DIRECTORY = "CMagicGem";
|
private static final String LEGACY_PLUGIN_DIRECTORY = "MagicGem";
|
||||||
|
private static final String TARGET_BACKUP_DIRECTORY = "MagicGem";
|
||||||
private static final List<String> DATA_DIRECTORIES = List.of("gems", "skills", "gui", "languages");
|
private static final List<String> DATA_DIRECTORIES = List.of("gems", "skills", "gui", "languages");
|
||||||
private static final String MARKER = ".migration-cmagicgem-v1.complete";
|
private static final String MARKER = ".migration-magicgem-reforged-v1.complete";
|
||||||
|
|
||||||
private final Path pluginsDirectory;
|
private final Path pluginsDirectory;
|
||||||
private final Path targetDirectory;
|
private final Path targetDirectory;
|
||||||
private final Runnable saveDefaultConfig;
|
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
|
|
||||||
public LegacyDirectoryImporter(JavaPlugin plugin) {
|
public LegacyDirectoryImporter(JavaPlugin plugin) {
|
||||||
this(plugin.getDataFolder().toPath().toAbsolutePath().normalize().getParent(),
|
this(plugin.getDataFolder().toPath().toAbsolutePath().normalize().getParent(),
|
||||||
plugin.getDataFolder().toPath().toAbsolutePath().normalize(),
|
plugin.getDataFolder().toPath().toAbsolutePath().normalize(), plugin.getLogger());
|
||||||
plugin::saveDefaultConfig, plugin.getLogger());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LegacyDirectoryImporter(Path pluginsDirectory, Path targetDirectory,
|
LegacyDirectoryImporter(Path pluginsDirectory, Path targetDirectory, Logger logger) {
|
||||||
Runnable saveDefaultConfig, Logger logger) {
|
|
||||||
this.pluginsDirectory = Objects.requireNonNull(pluginsDirectory).toAbsolutePath().normalize();
|
this.pluginsDirectory = Objects.requireNonNull(pluginsDirectory).toAbsolutePath().normalize();
|
||||||
this.targetDirectory = Objects.requireNonNull(targetDirectory).toAbsolutePath().normalize();
|
this.targetDirectory = Objects.requireNonNull(targetDirectory).toAbsolutePath().normalize();
|
||||||
this.saveDefaultConfig = Objects.requireNonNull(saveDefaultConfig);
|
|
||||||
this.logger = Objects.requireNonNull(logger);
|
this.logger = Objects.requireNonNull(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImportResult importIfPresent() {
|
public ImportResult importIfPresent() {
|
||||||
if (targetDirectory.equals(pluginsDirectory.resolve(LEGACY_PLUGIN_DIRECTORY).normalize())) {
|
Path sourceDirectory = pluginsDirectory.resolve(LEGACY_PLUGIN_DIRECTORY).normalize();
|
||||||
|
if (targetDirectory.equals(sourceDirectory) || !Files.isDirectory(sourceDirectory)) {
|
||||||
return ImportResult.none();
|
return ImportResult.none();
|
||||||
}
|
}
|
||||||
|
|
||||||
Path sourceDirectory = pluginsDirectory.resolve(LEGACY_PLUGIN_DIRECTORY).normalize();
|
|
||||||
if (!Files.isDirectory(sourceDirectory)) return ImportResult.none();
|
|
||||||
Path marker = targetDirectory.resolve(MARKER);
|
Path marker = targetDirectory.resolve(MARKER);
|
||||||
if (Files.exists(marker)) return new ImportResult(0, 0, 0, false, true);
|
if (Files.exists(marker)) return new ImportResult(0, 0, true);
|
||||||
|
|
||||||
int copiedFiles = 0;
|
int copiedFiles = 0;
|
||||||
int mergedFiles = 0;
|
|
||||||
int conflictFiles = 0;
|
int conflictFiles = 0;
|
||||||
boolean configImported = false;
|
|
||||||
List<Conflict> conflicts = new ArrayList<>();
|
List<Conflict> conflicts = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(targetDirectory);
|
Files.createDirectories(targetDirectory);
|
||||||
for (String directory : DATA_DIRECTORIES) {
|
for (String directory : DATA_DIRECTORIES) {
|
||||||
ImportCounts counts = importDirectory(sourceDirectory.resolve(directory), targetDirectory.resolve(directory),
|
ImportCounts counts = importDirectory(sourceDirectory.resolve(directory),
|
||||||
Path.of(directory), conflicts);
|
targetDirectory.resolve(directory), Path.of(directory), conflicts);
|
||||||
copiedFiles += counts.copiedFiles;
|
copiedFiles += counts.copiedFiles;
|
||||||
mergedFiles += counts.mergedFiles;
|
|
||||||
conflictFiles += counts.conflictFiles;
|
conflictFiles += counts.conflictFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
Path sourceConfig = sourceDirectory.resolve("config.yml");
|
Path sourceConfig = sourceDirectory.resolve("config.yml");
|
||||||
Path targetConfig = targetDirectory.resolve("config.yml");
|
|
||||||
if (Files.isRegularFile(sourceConfig)) {
|
if (Files.isRegularFile(sourceConfig)) {
|
||||||
boolean targetConfigExisted = Files.isRegularFile(targetConfig);
|
ImportCounts counts = importFile(sourceConfig, targetDirectory.resolve("config.yml"),
|
||||||
if (!targetConfigExisted) saveDefaultConfig.run();
|
Path.of("config.yml"), conflicts);
|
||||||
MergeResult result = mergeYaml(sourceConfig, targetConfig, Path.of("config.yml"),
|
copiedFiles += counts.copiedFiles;
|
||||||
!targetConfigExisted, conflicts);
|
conflictFiles += counts.conflictFiles;
|
||||||
configImported = result.changed;
|
|
||||||
if (result.changed) mergedFiles++;
|
|
||||||
if (result.conflicted) conflictFiles++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writeConflictBackups(sourceDirectory, conflicts);
|
writeConflictBackups(sourceDirectory, conflicts);
|
||||||
Files.writeString(marker, "Imported from plugins/CMagicGem. Source directory remains unchanged.\n");
|
Files.writeString(marker, "Imported from plugins/MagicGem. Source directory remains unchanged.\n");
|
||||||
if (!conflicts.isEmpty()) {
|
for (Conflict conflict : conflicts) {
|
||||||
for (Conflict conflict : conflicts) {
|
logger.warning("MagicGem Reforged kept the target file for " + conflict.relativePath()
|
||||||
logger.warning("CMagicGem migration kept the MagicGem value for "
|
+ "; the original file was saved under migration-backup/MagicGem.");
|
||||||
+ conflict.sourceRelative() + " (" + conflict.key() + ")"
|
|
||||||
+ "; the original source file was saved under migration-backup/CMagicGem.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
logger.info("Imported plugins/CMagicGem into plugins/MagicGem: " + copiedFiles + " files copied, "
|
logger.info("Imported original plugins/MagicGem into " + targetDirectory.getFileName()
|
||||||
+ mergedFiles + " files merged, " + conflictFiles + " files with conflicts. "
|
+ ": " + copiedFiles + " files copied, " + conflictFiles + " conflicts. "
|
||||||
+ "The source directory was left untouched.");
|
+ "The original directory was left untouched.");
|
||||||
return new ImportResult(copiedFiles, mergedFiles, conflictFiles, configImported, false);
|
return new ImportResult(copiedFiles, conflictFiles, false);
|
||||||
} catch (IOException | InvalidConfigurationException exception) {
|
} catch (IOException exception) {
|
||||||
logger.log(Level.SEVERE, "Could not finish importing plugins/CMagicGem. No source files were deleted; "
|
logger.log(Level.SEVERE, "Could not finish importing original plugins/MagicGem. "
|
||||||
+ "the migration will be retried on the next startup.", exception);
|
+ "The original directory was not deleted; migration will be retried on the next startup.", exception);
|
||||||
return new ImportResult(copiedFiles, mergedFiles, conflictFiles, configImported, false);
|
return new ImportResult(copiedFiles, conflictFiles, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImportCounts importDirectory(Path source, Path target, Path sourcePrefix, List<Conflict> conflicts)
|
private ImportCounts importDirectory(Path source, Path target, Path prefix, List<Conflict> conflicts)
|
||||||
throws IOException, InvalidConfigurationException {
|
throws IOException {
|
||||||
if (!Files.isDirectory(source)) return new ImportCounts(0, 0, 0);
|
if (!Files.isDirectory(source)) return new ImportCounts(0, 0);
|
||||||
int copied = 0;
|
int copied = 0;
|
||||||
int merged = 0;
|
int conflictCount = 0;
|
||||||
int conflicted = 0;
|
|
||||||
Path normalizedTarget = target.toAbsolutePath().normalize();
|
Path normalizedTarget = target.toAbsolutePath().normalize();
|
||||||
try (var paths = Files.walk(source)) {
|
try (var paths = Files.walk(source)) {
|
||||||
for (Path path : paths.toList()) {
|
for (Path path : paths.toList()) {
|
||||||
Path relative = source.relativize(path);
|
if (Files.isDirectory(path) || !Files.isRegularFile(path)) continue;
|
||||||
Path destination = normalizedTarget.resolve(relative).normalize();
|
Path relative = prefix.resolve(source.relativize(path));
|
||||||
if (!destination.startsWith(normalizedTarget)) continue;
|
ImportCounts counts = importFile(path, normalizedTarget.resolve(source.relativize(path)).normalize(),
|
||||||
if (Files.isDirectory(path)) {
|
relative, conflicts);
|
||||||
Files.createDirectories(destination);
|
copied += counts.copiedFiles;
|
||||||
continue;
|
conflictCount += counts.conflictFiles;
|
||||||
}
|
|
||||||
if (!Files.isRegularFile(path)) continue;
|
|
||||||
if (!Files.exists(destination)) {
|
|
||||||
Files.createDirectories(destination.getParent());
|
|
||||||
Files.copy(path, destination, StandardCopyOption.COPY_ATTRIBUTES);
|
|
||||||
copied++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isYaml(path)) {
|
|
||||||
MergeResult result = mergeYaml(path, destination,
|
|
||||||
sourcePrefix.resolve(source.relativize(path)), false, conflicts);
|
|
||||||
if (result.changed) merged++;
|
|
||||||
if (result.conflicted) conflicted++;
|
|
||||||
} else if (Files.mismatch(path, destination) != -1) {
|
|
||||||
conflicts.add(new Conflict(sourcePrefix.resolve(source.relativize(path)), "<binary content>"));
|
|
||||||
conflicted++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ImportCounts(copied, merged, conflicted);
|
return new ImportCounts(copied, conflictCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MergeResult mergeYaml(Path source, Path destination, Path sourceRelative,
|
private ImportCounts importFile(Path source, Path target, Path relative, List<Conflict> conflicts)
|
||||||
boolean preferSource, List<Conflict> conflicts)
|
throws IOException {
|
||||||
throws IOException, InvalidConfigurationException {
|
if (!target.normalize().startsWith(targetDirectory)) return new ImportCounts(0, 0);
|
||||||
if (!Files.exists(destination)) {
|
if (!Files.exists(target)) {
|
||||||
Files.createDirectories(destination.getParent());
|
Files.createDirectories(target.getParent());
|
||||||
Files.copy(source, destination, StandardCopyOption.COPY_ATTRIBUTES);
|
Files.copy(source, target, StandardCopyOption.COPY_ATTRIBUTES);
|
||||||
return new MergeResult(true, false);
|
return new ImportCounts(1, 0);
|
||||||
}
|
}
|
||||||
|
if (Files.mismatch(source, target) != -1) {
|
||||||
YamlConfiguration sourceYaml = new YamlConfiguration();
|
conflicts.add(new Conflict(relative));
|
||||||
sourceYaml.load(source.toFile());
|
return new ImportCounts(0, 1);
|
||||||
YamlConfiguration destinationYaml = new YamlConfiguration();
|
|
||||||
destinationYaml.load(destination.toFile());
|
|
||||||
List<String> fileConflicts = new ArrayList<>();
|
|
||||||
boolean changed = mergeSections(sourceYaml, destinationYaml, "", preferSource, fileConflicts);
|
|
||||||
if (source.getFileName().toString().equalsIgnoreCase("config.yml")) {
|
|
||||||
String legacyLanguage = sourceYaml.getString("language", sourceYaml.getString("lang"));
|
|
||||||
if (legacyLanguage != null && (preferSource || !destinationYaml.contains("language"))) {
|
|
||||||
if (!Objects.equals(destinationYaml.getString("language"), legacyLanguage)) {
|
|
||||||
destinationYaml.set("language", legacyLanguage);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (changed) destinationYaml.save(destination.toFile());
|
return new ImportCounts(0, 0);
|
||||||
for (String conflict : fileConflicts) conflicts.add(new Conflict(sourceRelative, conflict));
|
|
||||||
return new MergeResult(changed, !fileConflicts.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean mergeSections(ConfigurationSection source, ConfigurationSection destination,
|
|
||||||
String prefix, boolean preferSource, List<String> conflicts) {
|
|
||||||
boolean changed = false;
|
|
||||||
for (String key : source.getKeys(false)) {
|
|
||||||
String path = prefix.isEmpty() ? key : prefix + "." + key;
|
|
||||||
ConfigurationSection sourceSection = source.getConfigurationSection(key);
|
|
||||||
ConfigurationSection destinationSection = destination.getConfigurationSection(key);
|
|
||||||
if (sourceSection != null && destinationSection == null) {
|
|
||||||
if (!destination.contains(key)) {
|
|
||||||
destination.createSection(key);
|
|
||||||
changed = true;
|
|
||||||
} else {
|
|
||||||
if (preferSource) {
|
|
||||||
destination.set(key, null);
|
|
||||||
destination.createSection(key);
|
|
||||||
changed = true;
|
|
||||||
} else {
|
|
||||||
conflicts.add(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
destinationSection = destination.getConfigurationSection(key);
|
|
||||||
if (destinationSection != null) {
|
|
||||||
changed |= mergeSections(sourceSection, destinationSection, path, preferSource, conflicts);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (sourceSection == null && destinationSection != null) {
|
|
||||||
if (preferSource) {
|
|
||||||
destination.set(key, source.get(key));
|
|
||||||
changed = true;
|
|
||||||
} else {
|
|
||||||
conflicts.add(path);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (sourceSection != null) {
|
|
||||||
changed |= mergeSections(sourceSection, destinationSection, path, preferSource, conflicts);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object sourceValue = source.get(key);
|
|
||||||
if (!destination.contains(key)) {
|
|
||||||
destination.set(key, sourceValue);
|
|
||||||
changed = true;
|
|
||||||
} else if (!Objects.equals(sourceValue, destination.get(key))) {
|
|
||||||
if (preferSource) {
|
|
||||||
destination.set(key, sourceValue);
|
|
||||||
changed = true;
|
|
||||||
} else {
|
|
||||||
conflicts.add(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return changed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeConflictBackups(Path sourceDirectory, List<Conflict> conflicts) throws IOException {
|
private void writeConflictBackups(Path sourceDirectory, List<Conflict> conflicts) throws IOException {
|
||||||
for (Path relative : conflicts.stream().map(Conflict::sourceRelative).distinct().toList()) {
|
for (Path relative : conflicts.stream().map(Conflict::relativePath).distinct().toList()) {
|
||||||
Path sourceFile = sourceDirectory.resolve(relative).normalize();
|
Path source = sourceDirectory.resolve(relative).normalize();
|
||||||
if (!sourceFile.startsWith(sourceDirectory) || !Files.isRegularFile(sourceFile)) continue;
|
if (!source.startsWith(sourceDirectory) || !Files.isRegularFile(source)) continue;
|
||||||
Path backup = targetDirectory.resolve("migration-backup").resolve(LEGACY_PLUGIN_DIRECTORY).resolve(relative);
|
Path backup = targetDirectory.resolve("migration-backup").resolve(TARGET_BACKUP_DIRECTORY).resolve(relative);
|
||||||
Files.createDirectories(backup.getParent());
|
Files.createDirectories(backup.getParent());
|
||||||
Files.copy(sourceFile, backup, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
Files.copy(source, backup, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isYaml(Path path) {
|
public record ImportResult(int copiedFiles, int conflictFiles, boolean alreadyMigrated) {
|
||||||
String name = path.getFileName().toString().toLowerCase(java.util.Locale.ROOT);
|
|
||||||
return name.endsWith(".yml") || name.endsWith(".yaml");
|
|
||||||
}
|
|
||||||
|
|
||||||
public record ImportResult(int copiedFiles, int mergedFiles, int conflictFiles,
|
|
||||||
boolean configImported, boolean alreadyMigrated) {
|
|
||||||
private static ImportResult none() {
|
private static ImportResult none() {
|
||||||
return new ImportResult(0, 0, 0, false, false);
|
return new ImportResult(0, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private record ImportCounts(int copiedFiles, int mergedFiles, int conflictFiles) { }
|
private record ImportCounts(int copiedFiles, int conflictFiles) { }
|
||||||
private record MergeResult(boolean changed, boolean conflicted) { }
|
private record Conflict(Path relativePath) { }
|
||||||
private record Conflict(Path sourceRelative, String key) { }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package cn.infstar.magicgemreforged;
|
||||||
|
|
||||||
|
import pku.yim.magicgem.MagicGem;
|
||||||
|
|
||||||
|
/** Paper entry point for MagicGem Reforged. The superclass keeps the original public API available. */
|
||||||
|
public final class MagicGemReforged extends MagicGem {
|
||||||
|
public static MagicGemReforged instance;
|
||||||
|
|
||||||
|
public static MagicGemReforged getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
instance = this;
|
||||||
|
super.onEnable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
try {
|
||||||
|
super.onDisable();
|
||||||
|
} finally {
|
||||||
|
if (instance == this) instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,8 +5,8 @@ import cn.infstar.cmagicgem.config.PluginSettings;
|
|||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
/** Legacy entry point retained for plugins that resolve MagicGem by class name. */
|
/** Compatibility base class retained for plugins compiled against the original MagicGem API. */
|
||||||
public final class MagicGem extends CMagicGem {
|
public class MagicGem extends CMagicGem {
|
||||||
public static MagicGem instance;
|
public static MagicGem instance;
|
||||||
public static boolean placeholderapi;
|
public static boolean placeholderapi;
|
||||||
public static boolean MMCorMMA;
|
public static boolean MMCorMMA;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ gem-lore: false
|
|||||||
gem-craft: false
|
gem-craft: false
|
||||||
skill-engine: true
|
skill-engine: true
|
||||||
skill-engine-lore-color: false
|
skill-engine-lore-color: false
|
||||||
legacy-migration: true
|
|
||||||
potion-global-limit: {}
|
potion-global-limit: {}
|
||||||
integrations:
|
integrations:
|
||||||
mythicmobs:
|
mythicmobs:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
Material: EMERALD
|
Material: EMERALD
|
||||||
Display: "&a示例生命宝石"
|
Display: "&a示例生命宝石"
|
||||||
Tips:
|
Tips:
|
||||||
- "&7用于验证 CMagicGem 基础功能"
|
- "&7用于验证 MagicGem Reforged 基础功能"
|
||||||
Require:
|
Require:
|
||||||
- ARMOR
|
- ARMOR
|
||||||
Success: 100
|
Success: 100
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
default:
|
default:
|
||||||
Title: "&8CMagicGem 镶嵌台 &7%page%"
|
Title: "&8MagicGem Reforged 镶嵌台 &7%page%"
|
||||||
Page: 1
|
Page: 1
|
||||||
Locations: []
|
Locations: []
|
||||||
Slots:
|
Slots:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
version: 1.10
|
version: 1.10
|
||||||
plugin:
|
plugin:
|
||||||
prefix: "&9&l[&e&lMagicGem&9&l]&r"
|
prefix: "&9&l[&e&lMagicGem Reforged&9&l]&r"
|
||||||
read_file: "&eReading "
|
read_file: "&eReading "
|
||||||
dupe_gem: "&cDuplicated gems find! Name: "
|
dupe_gem: "&cDuplicated gems find! Name: "
|
||||||
extract: "&eExtracting resource files"
|
extract: "&eExtracting resource files"
|
||||||
@@ -13,7 +13,7 @@ plugin:
|
|||||||
accept: "&cPlugin licensed. Thank you for purchasing!"
|
accept: "&cPlugin licensed. Thank you for purchasing!"
|
||||||
busy: "&cThe license server is busy. Will try again later"
|
busy: "&cThe license server is busy. Will try again later"
|
||||||
wrong_info: "&cLicense request failed, plugin file may be corrupted"
|
wrong_info: "&cLicense request failed, plugin file may be corrupted"
|
||||||
wrong_user: "&cPlease specify correct account in MagicGem/config.yml (For example, user: Bob), and then use /mgem reload."
|
wrong_user: "&cPlease specify correct account in MagicGem-Reforged/config.yml (For example, user: Bob), and then use /mgem reload."
|
||||||
help: "&aFor support, please contact QQ: 386029724 or email liyi20129@gmail.com"
|
help: "&aFor support, please contact QQ: 386029724 or email liyi20129@gmail.com"
|
||||||
gem:
|
gem:
|
||||||
tip:
|
tip:
|
||||||
@@ -187,8 +187,8 @@ reward:
|
|||||||
type: "Potion Effect"
|
type: "Potion Effect"
|
||||||
duration: "Duration"
|
duration: "Duration"
|
||||||
skill:
|
skill:
|
||||||
add: "Add MagicGem Skill"
|
add: "Add MagicGem Reforged Skill"
|
||||||
change: "Change MagicGem Skill"
|
change: "Change MagicGem Reforged Skill"
|
||||||
remove: "Remove MaticGem Skill"
|
remove: "Remove MaticGem Skill"
|
||||||
unbreakable:
|
unbreakable:
|
||||||
type: "Unbreakable"
|
type: "Unbreakable"
|
||||||
@@ -276,7 +276,7 @@ cmagic:
|
|||||||
gem_not_found: "&cUnknown gem: {gem}"
|
gem_not_found: "&cUnknown gem: {gem}"
|
||||||
not_embedded: "&cThat gem is not embedded in this item"
|
not_embedded: "&cThat gem is not embedded in this item"
|
||||||
command:
|
command:
|
||||||
reload: "&aMagicGem reloaded safely; {amount} gems loaded."
|
reload: "&aMagicGem Reforged reloaded safely; {amount} gems loaded."
|
||||||
give: "&aGave {player} {amount} {gem}"
|
give: "&aGave {player} {amount} {gem}"
|
||||||
list: "&6Loaded gems (&e{amount}&6):"
|
list: "&6Loaded gems (&e{amount}&6):"
|
||||||
signal_all: "&aSent the skill signal to every online player."
|
signal_all: "&aSent the skill signal to every online player."
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ cmagic:
|
|||||||
gem_not_found: "&c未知宝石: {gem}"
|
gem_not_found: "&c未知宝石: {gem}"
|
||||||
not_embedded: "&c装备未镶嵌该宝石"
|
not_embedded: "&c装备未镶嵌该宝石"
|
||||||
command:
|
command:
|
||||||
reload: "&aMagicGem 已安全重载,共加载 {amount} 颗宝石。"
|
reload: "&aMagicGem Reforged 已安全重载,共加载 {amount} 颗宝石。"
|
||||||
give: "&a已给予 {player} {amount} 个 {gem}"
|
give: "&a已给予 {player} {amount} 个 {gem}"
|
||||||
list: "&6已加载宝石 (&e{amount}&6):"
|
list: "&6已加载宝石 (&e{amount}&6):"
|
||||||
signal_all: "&a已向所有在线玩家发送技能信号。"
|
signal_all: "&a已向所有在线玩家发送技能信号。"
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
name: MagicGem
|
name: MagicGem-Reforged
|
||||||
version: '${version}'
|
version: '${version}'
|
||||||
description: Paper 1.21.x compatible MagicGem reimplementation
|
description: MagicGem Reforged for Paper 1.21.x
|
||||||
main: pku.yim.magicgem.MagicGem
|
main: cn.infstar.magicgemreforged.MagicGemReforged
|
||||||
api-version: '1.21'
|
api-version: '1.21'
|
||||||
authors: [ Coldsmile_7 ]
|
authors: [ Coldsmile_7 ]
|
||||||
softdepend: [PlaceholderAPI, Vault, PlayerPoints, MythicMobs, MMOItems, MythicArtifacts, MythicCrucible, Residence, PvPManager, EcoEnchants, GoldenEnchants]
|
softdepend: [PlaceholderAPI, Vault, PlayerPoints, MythicMobs, MMOItems, MythicArtifacts, MythicCrucible, Residence, PvPManager, EcoEnchants, GoldenEnchants]
|
||||||
commands:
|
commands:
|
||||||
mgem:
|
mgem:
|
||||||
description: MagicGem main command
|
description: MagicGem Reforged main command
|
||||||
usage: /mgem help
|
usage: /mgem help
|
||||||
aliases: [cmgem, cmg]
|
aliases: [cmgem, cmg]
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package cn.infstar.cmagicgem.migration;
|
package cn.infstar.cmagicgem.migration;
|
||||||
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -18,81 +16,71 @@ class LegacyDirectoryImporterTest {
|
|||||||
Path temporaryDirectory;
|
Path temporaryDirectory;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void mergesOldDirectoryWithoutOverwritingTargetAndKeepsConflictBackup() throws Exception {
|
void importsOriginalMagicGemDirectoryIntoReforgedTarget() throws Exception {
|
||||||
Path plugins = Files.createDirectory(temporaryDirectory.resolve("plugins"));
|
Path plugins = Files.createDirectory(temporaryDirectory.resolve("plugins"));
|
||||||
Path source = Files.createDirectories(plugins.resolve("CMagicGem/gems"));
|
Path source = Files.createDirectories(plugins.resolve("MagicGem/gems"));
|
||||||
Path target = Files.createDirectories(plugins.resolve("MagicGem/gems"));
|
Path target = plugins.resolve("MagicGem-Reforged");
|
||||||
Path oldGem = source.resolve("Shared.yml");
|
|
||||||
Path currentGem = target.resolve("Shared.yml");
|
|
||||||
Files.writeString(oldGem, "Shared:\n Name: Old Name\n Material: DIAMOND\n OldOnly: retained\n");
|
|
||||||
Files.writeString(currentGem, "Shared:\n Name: Current Name\n Material: DIAMOND\n CurrentOnly: retained\n");
|
|
||||||
Files.writeString(source.resolve("Custom.yml"), "Custom:\n Name: Imported\n");
|
Files.writeString(source.resolve("Custom.yml"), "Custom:\n Name: Imported\n");
|
||||||
Files.writeString(source.getParent().resolve("config.yml"), "language: en_US\nbackpack: true\nold-option: kept\n");
|
Files.writeString(source.getParent().resolve("config.yml"), "language: en_US\nbackpack: true\n");
|
||||||
Files.writeString(target.getParent().resolve("config.yml"), "language: zh_CN\nbackpack: false\n");
|
|
||||||
|
|
||||||
LegacyDirectoryImporter importer = importer(plugins, plugins.resolve("MagicGem"), () -> { });
|
LegacyDirectoryImporter.ImportResult result = importer(plugins, target).importIfPresent();
|
||||||
LegacyDirectoryImporter.ImportResult result = importer.importIfPresent();
|
|
||||||
|
|
||||||
YamlConfiguration mergedGem = load(currentGem);
|
assertTrue(Files.isRegularFile(target.resolve("gems/Custom.yml")));
|
||||||
YamlConfiguration mergedConfig = load(target.getParent().resolve("config.yml"));
|
assertTrue(Files.isRegularFile(target.resolve("config.yml")));
|
||||||
assertEquals("Current Name", mergedGem.getString("Shared.Name"));
|
assertEquals("Custom:\n Name: Imported\n", Files.readString(target.resolve("gems/Custom.yml")));
|
||||||
assertEquals("retained", mergedGem.getString("Shared.OldOnly"));
|
assertEquals("language: en_US\nbackpack: true\n", Files.readString(target.resolve("config.yml")));
|
||||||
assertEquals("retained", mergedGem.getString("Shared.CurrentOnly"));
|
assertTrue(Files.isDirectory(source.getParent()));
|
||||||
assertTrue(Files.isRegularFile(target.resolve("Custom.yml")));
|
assertTrue(result.copiedFiles() >= 2);
|
||||||
assertEquals("zh_CN", mergedConfig.getString("language"));
|
assertEquals(0, result.conflictFiles());
|
||||||
assertEquals(false, mergedConfig.getBoolean("backpack"));
|
assertFalse(result.alreadyMigrated());
|
||||||
assertEquals("kept", mergedConfig.getString("old-option"));
|
}
|
||||||
assertTrue(Files.isRegularFile(plugins.resolve("MagicGem/migration-backup/CMagicGem/gems/Shared.yml")));
|
|
||||||
assertEquals("Old Name", load(oldGem).getString("Shared.Name"));
|
|
||||||
assertTrue(result.copiedFiles() >= 1);
|
|
||||||
assertTrue(result.mergedFiles() >= 2);
|
|
||||||
assertTrue(result.conflictFiles() >= 1);
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void preservesTargetFilesAndBacksUpConflicts() throws Exception {
|
||||||
|
Path plugins = Files.createDirectory(temporaryDirectory.resolve("plugins"));
|
||||||
|
Path source = Files.createDirectories(plugins.resolve("MagicGem/gems"));
|
||||||
|
Path target = Files.createDirectories(plugins.resolve("MagicGem-Reforged/gems"));
|
||||||
|
Files.writeString(source.resolve("Shared.yml"), "Shared:\n Name: Original\n");
|
||||||
|
Files.writeString(target.resolve("Shared.yml"), "Shared:\n Name: Reforged\n");
|
||||||
|
|
||||||
|
LegacyDirectoryImporter.ImportResult result = importer(plugins, target.getParent()).importIfPresent();
|
||||||
|
|
||||||
|
assertEquals("Shared:\n Name: Reforged\n", Files.readString(target.resolve("Shared.yml")));
|
||||||
|
assertEquals("Shared:\n Name: Original\n",
|
||||||
|
Files.readString(target.getParent().resolve("migration-backup/MagicGem/gems/Shared.yml")));
|
||||||
|
assertEquals(1, result.conflictFiles());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void markerMakesMigrationIdempotent() throws Exception {
|
||||||
|
Path plugins = Files.createDirectory(temporaryDirectory.resolve("plugins"));
|
||||||
|
Path source = Files.createDirectories(plugins.resolve("MagicGem"));
|
||||||
|
Path target = plugins.resolve("MagicGem-Reforged");
|
||||||
|
Files.writeString(source.resolve("config.yml"), "language: zh_CN\n");
|
||||||
|
|
||||||
|
LegacyDirectoryImporter importer = importer(plugins, target);
|
||||||
|
importer.importIfPresent();
|
||||||
LegacyDirectoryImporter.ImportResult repeated = importer.importIfPresent();
|
LegacyDirectoryImporter.ImportResult repeated = importer.importIfPresent();
|
||||||
|
|
||||||
assertTrue(repeated.alreadyMigrated());
|
assertTrue(repeated.alreadyMigrated());
|
||||||
assertEquals(0, repeated.copiedFiles());
|
assertEquals(0, repeated.copiedFiles());
|
||||||
|
assertEquals(0, repeated.conflictFiles());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void oldConfigurationOverridesBundledDefaultsWhenTargetHasNoConfig() throws Exception {
|
void missingOriginalDirectoryDoesNothing() throws Exception {
|
||||||
Path plugins = Files.createDirectory(temporaryDirectory.resolve("plugins"));
|
Path plugins = Files.createDirectory(temporaryDirectory.resolve("plugins"));
|
||||||
Path source = Files.createDirectories(plugins.resolve("CMagicGem"));
|
Path target = plugins.resolve("MagicGem-Reforged");
|
||||||
Path target = plugins.resolve("MagicGem");
|
|
||||||
Files.writeString(source.resolve("config.yml"), "lang: en_US\nbackpack: true\n");
|
|
||||||
Runnable createDefaults = () -> {
|
|
||||||
try {
|
|
||||||
Files.createDirectories(target);
|
|
||||||
Files.writeString(target.resolve("config.yml"), "language: zh_CN\nbackpack: false\nworkbench: true\n");
|
|
||||||
} catch (IOException exception) {
|
|
||||||
throw new IllegalStateException(exception);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
LegacyDirectoryImporter.ImportResult result = importer(plugins, target, createDefaults).importIfPresent();
|
LegacyDirectoryImporter.ImportResult result = importer(plugins, target).importIfPresent();
|
||||||
|
|
||||||
YamlConfiguration config = load(target.resolve("config.yml"));
|
|
||||||
assertEquals("en_US", config.getString("language"));
|
|
||||||
assertEquals(true, config.getBoolean("backpack"));
|
|
||||||
assertEquals(true, config.getBoolean("workbench"));
|
|
||||||
assertTrue(result.configImported());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void originalMagicGemDirectoryIsTreatedAsTheActiveTarget() throws Exception {
|
|
||||||
Path plugins = Files.createDirectory(temporaryDirectory.resolve("plugins"));
|
|
||||||
Path target = Files.createDirectory(plugins.resolve("MagicGem"));
|
|
||||||
|
|
||||||
LegacyDirectoryImporter.ImportResult result = importer(plugins, target, () -> { }).importIfPresent();
|
|
||||||
|
|
||||||
|
assertEquals(0, result.copiedFiles());
|
||||||
|
assertEquals(0, result.conflictFiles());
|
||||||
assertFalse(result.alreadyMigrated());
|
assertFalse(result.alreadyMigrated());
|
||||||
assertFalse(Files.exists(target.resolve(".migration-cmagicgem-v1.complete")));
|
assertFalse(Files.exists(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
private LegacyDirectoryImporter importer(Path plugins, Path target, Runnable saveDefaults) {
|
private LegacyDirectoryImporter importer(Path plugins, Path target) {
|
||||||
return new LegacyDirectoryImporter(plugins, target, saveDefaults, Logger.getLogger("migration-test"));
|
return new LegacyDirectoryImporter(plugins, target, Logger.getLogger("migration-test"));
|
||||||
}
|
|
||||||
|
|
||||||
private YamlConfiguration load(Path path) {
|
|
||||||
return YamlConfiguration.loadConfiguration(path.toFile());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ class BundledConfigurationTest {
|
|||||||
private final DirectiveParser parser = new DirectiveParser();
|
private final DirectiveParser parser = new DirectiveParser();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void pluginDescriptorKeepsLegacyCommandAndPermissionCompatibility() {
|
void pluginDescriptorUsesReforgedEntryPointAndKeepsCompatibility() {
|
||||||
YamlConfiguration descriptor = YamlConfiguration.loadConfiguration(new File("src/main/resources/plugin.yml"));
|
YamlConfiguration descriptor = YamlConfiguration.loadConfiguration(new File("src/main/resources/plugin.yml"));
|
||||||
|
|
||||||
assertTrue(descriptor.getString("name").equalsIgnoreCase("MagicGem"));
|
assertTrue(descriptor.getString("name").equalsIgnoreCase("MagicGem-Reforged"));
|
||||||
assertTrue(descriptor.getString("main").equals("pku.yim.magicgem.MagicGem"));
|
assertTrue(descriptor.getString("main").equals("cn.infstar.magicgemreforged.MagicGemReforged"));
|
||||||
assertNotNull(descriptor.getConfigurationSection("commands.mgem"));
|
assertNotNull(descriptor.getConfigurationSection("commands.mgem"));
|
||||||
assertTrue(descriptor.getStringList("commands.mgem.aliases").contains("cmg"));
|
assertTrue(descriptor.getStringList("commands.mgem.aliases").contains("cmg"));
|
||||||
assertTrue(descriptor.getStringList("commands.mgem.aliases").contains("cmgem"));
|
assertTrue(descriptor.getStringList("commands.mgem.aliases").contains("cmgem"));
|
||||||
|
|||||||
Reference in New Issue
Block a user