diff --git a/MIGRATION.md b/MIGRATION.md index f83efd5..415d970 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -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 -# 原版 MagicGem plugins/ +├─ MagicGem-旧版本.jar └─ MagicGem/ - -# 之前的 CMagicGem 重实现 -plugins/ -└─ CMagicGem/ - -# 两个目录同时存在 -plugins/ -├─ MagicGem/ -└─ CMagicGem/ - -# 全新安装 -plugins/ + ├─ config.yml + ├─ gems/ + ├─ skills/ + ├─ gui/ + └─ languages/ ``` -当前版本始终把 `plugins/MagicGem` 作为有效目录。`plugins/CMagicGem` 只作为旧数据来源导入。 +升级后的结构是: -## 第一次迁移前的准备 +```text +plugins/ +├─ MagicGem-Reforged-0.1.0-SNAPSHOT.jar +├─ MagicGem/ +└─ MagicGem-Reforged/ +``` -1. 停止服务器,不能在服务器运行时替换 JAR 或修改插件数据。 -2. 备份整个服务器,至少包括以下内容: +`MagicGem` 是迁移来源,`MagicGem-Reforged` 是当前有效数据目录。 + +## 正式升级步骤 + +1. 停止服务器,不能在运行中替换插件 JAR 或修改插件数据。 +2. 备份整个服务器,至少包括: ```text plugins/MagicGem/ - plugins/CMagicGem/ world/ world_nether/ world_the_end/ ``` - 玩家背包、末影箱、容器和世界区块中的物品数据不在插件目录中,只备份插件目录不足以完整回滚。 -3. 移除旧版 MagicGem JAR 和旧版 CMagicGem JAR。 -4. 将当前版本 JAR 放入 `plugins/`。 -5. 保留 `plugins/MagicGem` 和 `plugins/CMagicGem`,不要在第一次启动前删除旧目录。 + 玩家背包、末影箱、容器和世界区块中的物品数据不在插件目录中,只备份 `plugins/MagicGem` 不足以完整回滚。 +3. 移除旧版 MagicGem JAR。不要同时加载旧版和重制版。 +4. 将 `MagicGem-Reforged` JAR 放入 `plugins/`。 +5. 保留 `plugins/MagicGem`,不要提前重命名、删除或清空它。 6. 使用 Java 21 启动 Paper 1.21.x。 +7. 等待迁移完成后检查控制台日志,再执行游戏内验证。 -## 启动时的执行顺序 +## 插件身份和命令 -当前插件启动时会按照以下顺序工作: +重制版的 `plugin.yml` 使用: -1. Paper 读取 `plugin.yml`,以 `MagicGem` 身份加载插件。 -2. `onLoad()` 预加载 NBT API。 -3. 执行旧目录导入器,检查 `plugins/CMagicGem`。 -4. 如果缺少默认 `config.yml`,创建当前版本的默认配置。 -5. 只为不存在的文件写入内置宝石、技能、GUI 和语言资源。 -6. 读取语言配置。 -7. 初始化宝石注册表、技能注册表、GUI、物品编解码器和软依赖集成。 -8. 执行完整 reload,读取配置并加载宝石、技能和 GUI。 -9. 注册 `/mgem` 命令和事件监听器。 -10. 输出集成状态和已加载宝石数量。 +```yaml +name: MagicGem-Reforged +main: cn.infstar.magicgemreforged.MagicGemReforged +``` -迁移必须发生在宝石和技能注册表加载之前。物品里保存的是宝石名或技能名,完整定义仍然来自 `gems/*.yml` 和 `skills/*.yml`。 +主命令仍然是 `/mgem`,别名仍然是 `/cmgem` 和 `/cmg`。命令兼容不代表旧 JAR 可以同时运行;服务器中只能安装当前 JAR。 -## 情况一:只有原版 `plugins/MagicGem` +Java 主类已经是独立的新入口 `MagicGemReforged`。旧入口 `pku.yim.magicgem.MagicGem` 作为兼容父类保留,供依赖原版公开 API 的其他插件继续解析;它不会作为 Paper 的实际入口加载。 -这种情况下不需要复制或重命名目录。当前版本会直接沿用: +## 第一次启动时的目录迁移 + +启动时,迁移器只检查: ```text -plugins/MagicGem/config.yml -plugins/MagicGem/gems/*.yml -plugins/MagicGem/skills/*.yml -plugins/MagicGem/gui/*.yml -plugins/MagicGem/languages/*.yml +plugins/MagicGem/ ``` -迁移器发现当前数据目录就是 `plugins/MagicGem` 后会跳过目录导入。已有配置不会被默认配置覆盖,已有资源文件也不会被内置资源覆盖。 - -旧配置中的以下写法都可以读取: - -```yaml -lang: zh_CN -``` - -或: - -```yaml -language: zh_CN -``` - -操作流程就是停止服务器、替换 JAR、保留 `plugins/MagicGem`、启动服务器。 - -## 情况二:只有 `plugins/CMagicGem` - -如果不存在 `plugins/MagicGem`,但存在 `plugins/CMagicGem`,当前版本会创建 `plugins/MagicGem`,并导入以下内容: +目标是当前插件的数据目录: ```text -CMagicGem/gems → MagicGem/gems -CMagicGem/skills → MagicGem/skills -CMagicGem/gui → MagicGem/gui -CMagicGem/languages → MagicGem/languages -CMagicGem/config.yml → MagicGem/config.yml +plugins/MagicGem-Reforged/ ``` -旧的 `plugins/CMagicGem` 源目录不会被删除,也不会被修改。 - -如果旧目录没有 `config.yml`,插件会在后续启动阶段创建当前版本默认配置。如果旧目录有配置而目标目录没有配置,插件会先创建默认配置,再用旧配置中的值覆盖对应默认项,同时保留新版本新增的配置项。 - -## 情况三:两个目录同时存在 - -当两个目录都存在时: +会迁移以下内容: ```text -plugins/MagicGem/ # 有效目录,优先级高 -plugins/CMagicGem/ # 旧目录,作为补充来源 +MagicGem/config.yml → MagicGem-Reforged/config.yml +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 - OldOnly: retained -``` +- 目标中不存在的文件继续复制; +- 目标中内容相同的文件跳过; +- 目标中内容不同的文件保留目标版本; +- 冲突的原版文件会保存到 `migration-backup/MagicGem/`; +- 原版源目录始终保留。 -当前目录中的文件是: - -```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` 的值被保留。旧源目录本身保持不变。 - -如果源文件和目标文件内容完全相同,不会被当作冲突,也不会产生额外备份。 +迁移器只处理 `config.yml`、`gems/`、`skills/`、`gui/` 和 `languages/`。其他原版目录不会自动复制,遇到自定义数据时应在备份后手动检查。 ## 迁移完成标记 -完整迁移成功后会创建: +成功完成一次迁移后,目标目录会创建: ```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 MAGICGEM_NAME ``` -当前版本会使用这个名称去查找已经加载的宝石定义。识别成功后会写入当前 PDC: - -```text -magicgem:gem_name -``` - -原版 `MAGICGEM_NAME` 作为兼容字段继续保留。新创建的宝石也会同时写入当前标识和原版名称字段。 - -如果物品中的宝石名称存在,但 `gems/*.yml` 没有迁移或加载失败,插件无法恢复完整宝石定义。 +仍然可以被识别。识别成功后,重制版会写入自己的当前数据,同时保留原版名称字段。 ### 镶嵌内容 -原版镶嵌数据通常位于: +原版镶嵌数据: ```text MAGICGEM_EMBED ``` -它是一个包含宝石名称和数量的复合 NBT,例如: - -```text -{ - "AttributeGem": 2, - "EnchantGem": 1 -} -``` - -当前版本会按以下顺序读取: - -1. `magicgem:embedded_gems`; -2. `cmagicgem:embedded_gems`; -3. `MAGICGEM_EMBED`; -4. 兼容旧格式的嵌套 NBT。 - -读取旧 NBT 后,物品在后续使用时会写入当前 PDC,同时继续写入兼容的 `MAGICGEM_EMBED`。执行镶嵌或拆卸时,这个受插件管理的字段会被重新整理;其他无关物品数据不会被主动删除。 +仍然可以读取。执行镶嵌、拆卸或相关物品操作时,重制版会更新当前数据并继续写入兼容的原版 NBT 字段。 ### 技能列表 -原版技能数据通常位于: +原版技能列表: ```text MAGICGEM_SKILL ``` -当前版本会按以下顺序读取: +仍然可以读取。技能能否执行取决于对应的 `skills/*.yml` 是否成功迁移并加载。 + +## 启动后验证 + +应在控制台确认: ```text -magicgem:skills -cmagicgem:skills -MAGICGEM_SKILL +Imported original plugins/MagicGem into MagicGem-Reforged: X files copied, Y conflicts. +MagicGem Reforged 已启用,命令 /mgem,加载 N 颗宝石。 ``` -读取成功后,技能名称会按当前格式保存,并继续写入兼容的 `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 /mgem view @@ -398,61 +181,41 @@ MagicGem 已启用,命令 /mgem,加载 N 颗宝石。 /mgem reload ``` -还应实际测试: +至少验证: -- 旧宝石是否可以识别; -- 旧镶嵌装备是否显示正确; -- 拆卸和重新镶嵌是否正常; -- 技能是否能够触发; -- GUI 是否可以打开; -- 中文或英文语言是否正确; -- Vault、PlayerPoints、PlaceholderAPI、MythicMobs 等软依赖是否正常; -- 服务器重启后数据是否仍然存在。 +- 旧宝石可以识别和使用; +- 已镶嵌装备的宝石数量正确; +- 拆卸和重新镶嵌正常; +- 原有技能可以触发; +- GUI 和语言设置正确; +- Vault、PlayerPoints、PlaceholderAPI、MythicMobs 等软依赖状态正常; +- 重启服务器后数据仍然存在。 -如果日志显示宝石数量明显少于旧版本,应优先检查 `plugins/MagicGem/gems/` 中的 YAML 是否完整,以及控制台是否有宝石解析错误。 +如果重制版加载的宝石数量异常,先检查 `plugins/MagicGem-Reforged/gems/` 是否完整,再查看 YAML 解析错误。 -## 回滚方式 +## 回滚 -最安全的回滚方式是停止服务器并恢复迁移前的完整备份: +发生问题时: 1. 停止服务器。 -2. 备份当前运行后的日志和数据,方便排查问题。 -3. 恢复旧版 MagicGem JAR。 -4. 恢复原来的 `plugins/MagicGem`。 -5. 必要时恢复玩家数据和世界备份。 -6. 启动服务器验证。 +2. 保存当前日志和 `plugins/MagicGem-Reforged`,用于排查。 +3. 移除重制版 JAR。 +4. 恢复原版 MagicGem JAR。 +5. 使用备份恢复 `plugins/MagicGem`、世界和玩家数据。 +6. 启动旧版服务器验证。 -如果只有 `plugins/CMagicGem`,迁移器不会删除它,因此它可以继续作为旧配置来源。但是如果新版本运行期间已经改变了玩家物品或世界数据,仍然建议恢复完整服务器备份,而不是只恢复插件 JAR。 +迁移器不会删除 `plugins/MagicGem`,所以仅目录迁移失败时原版数据仍然存在。但如果重制版运行期间已经改变了玩家物品或世界数据,建议恢复完整服务器备份。 -## 当前实现的注意事项 - -配置文件包含: - -```yaml -legacy-migration: true -``` - -但当前启动代码在读取配置前就调用目录导入器,因此这个开关目前不会阻止 `CMagicGem` 目录迁移。正常升级不需要修改它;如果以后需要支持关闭迁移,应将导入调用调整到配置加载之后,并显式检查该设置。 - -另外,目录识别使用的旧目录名是准确的: - -```text -plugins/CMagicGem -``` - -其他自定义目录名不会被自动扫描。需要使用其他目录名时,应在停服并备份后手动改名为 `CMagicGem`,或者手动合并文件。 - -## 迁移完成检查清单 +## 升级检查清单 - [ ] 已停止服务器并备份插件、世界和玩家数据。 -- [ ] 已移除旧版 MagicGem/CMagicGem JAR。 -- [ ] 新 JAR 已放入 `plugins/`。 -- [ ] `plugins/MagicGem` 已保留。 -- [ ] `plugins/CMagicGem` 在确认迁移成功前已保留。 -- [ ] 控制台显示迁移结果或明确显示没有旧目录。 -- [ ] 没有未处理的 YAML 解析错误。 -- [ ] 宝石数量与预期一致。 +- [ ] 已移除旧版 MagicGem JAR。 +- [ ] 已放入 MagicGem-Reforged JAR。 +- [ ] `plugins/MagicGem` 原版目录仍然保留。 +- [ ] `plugins/MagicGem-Reforged` 中已生成迁移后的配置和资源。 +- [ ] 控制台没有迁移或 YAML 解析错误。 +- [ ] 宝石数量与原版预期一致。 - [ ] 旧宝石、镶嵌装备和技能已实际测试。 -- [ ] GUI、语言和软依赖功能已测试。 -- [ ] 已保存冲突备份和迁移前备份。 -- [ ] 已重启服务器确认数据仍然正常。 +- [ ] GUI、语言和软依赖已测试。 +- [ ] 已保留冲突备份和原始服务器备份。 +- [ ] 已重启服务器验证持久化结果。 diff --git a/README.md b/README.md index e0ec65c..92e53f3 100644 --- a/README.md +++ b/README.md @@ -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` 和玩家数据。 -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-Reforged`,数据目录是 `plugins/MagicGem-Reforged`。升级只支持这一条路径: -原版 `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 ``` -产物位于 `target/cmagicgem-0.1.0-SNAPSHOT.jar`。 +产物位于 `target/MagicGem-Reforged-0.1.0-SNAPSHOT.jar`。 diff --git a/pom.xml b/pom.xml index e4152ba..19dcde6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,11 +5,11 @@ 4.0.0 cn.infstar - cmagicgem + magicgem-reforged 0.1.0-SNAPSHOT jar - cmagicgem + MagicGem-Reforged 21 @@ -21,6 +21,7 @@ clean package + MagicGem-Reforged-${project.version} org.apache.maven.plugins diff --git a/src/main/java/cn/infstar/cMagicGem/CMagicGem.java b/src/main/java/cn/infstar/cMagicGem/CMagicGem.java index e3b3c36..424e1fc 100644 --- a/src/main/java/cn/infstar/cMagicGem/CMagicGem.java +++ b/src/main/java/cn/infstar/cMagicGem/CMagicGem.java @@ -94,7 +94,7 @@ public class CMagicGem extends JavaPlugin { + ", PlayerPoints=" + points.status() + ", PlaceholderAPI=" + placeholders.status() + ", MythicMobs=" + mythic.status()); - getLogger().info("MagicGem 已启用,命令 /mgem,加载 " + gemRegistry.all().size() + " 颗宝石。"); + getLogger().info("MagicGem Reforged 已启用,命令 /mgem,加载 " + gemRegistry.all().size() + " 颗宝石。"); } public void reloadAll() { diff --git a/src/main/java/cn/infstar/cMagicGem/command/CMagicGemCommand.java b/src/main/java/cn/infstar/cMagicGem/command/CMagicGemCommand.java index 2654dea..04b3689 100644 --- a/src/main/java/cn/infstar/cMagicGem/command/CMagicGemCommand.java +++ b/src/main/java/cn/infstar/cMagicGem/command/CMagicGemCommand.java @@ -88,7 +88,7 @@ public final class CMagicGemCommand implements CommandExecutor, TabCompleter { } private boolean help(CommandSender sender) { - sender.sendMessage(itemCodec.color("&6MagicGem &7- &f/mgem")); + sender.sendMessage(itemCodec.color("&6MagicGem Reforged &7- &f/mgem")); List help = languages.section("command.help"); if (help.isEmpty()) help = List.of("&e/mgem give <玩家> <宝石> [数量]", "&e/mgem apply <玩家> <宝石> [槽位]", "&e/mgem remove <玩家> <宝石> [槽位]", @@ -155,7 +155,7 @@ public final class CMagicGemCommand implements CommandExecutor, TabCompleter { String fallback = switch (scope) { case "gem" -> "&a宝石配置已重载,共加载 {amount} 颗宝石。"; case "skill" -> "&a技能配置已重载。"; - default -> "&aMagicGem 已安全重载,共加载 {amount} 颗宝石。"; + default -> "&aMagicGem Reforged 已安全重载,共加载 {amount} 颗宝石。"; }; sender.sendMessage(itemCodec.color(languages.text("cmagic.command.reload_" + scope, fallback, "amount", registry.all().size()))); diff --git a/src/main/java/cn/infstar/cMagicGem/config/PluginSettings.java b/src/main/java/cn/infstar/cMagicGem/config/PluginSettings.java index 2104b96..37af5d1 100644 --- a/src/main/java/cn/infstar/cMagicGem/config/PluginSettings.java +++ b/src/main/java/cn/infstar/cMagicGem/config/PluginSettings.java @@ -3,11 +3,10 @@ package cn.infstar.cmagicgem.config; import org.bukkit.configuration.file.FileConfiguration; 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) { return new PluginSettings(config.getBoolean("workbench", true), config.getBoolean("backpack", 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("legacy-migration", true)); + config.getBoolean("skill-engine", true), config.getBoolean("skill-engine-lore-color", false)); } } diff --git a/src/main/java/cn/infstar/cMagicGem/item/ItemCodec.java b/src/main/java/cn/infstar/cMagicGem/item/ItemCodec.java index a83d17d..56b1660 100644 --- a/src/main/java/cn/infstar/cMagicGem/item/ItemCodec.java +++ b/src/main/java/cn/infstar/cMagicGem/item/ItemCodec.java @@ -41,9 +41,6 @@ public final class ItemCodec { private final NamespacedKey dataVersionKey; private final NamespacedKey embeddedKey; private final NamespacedKey skillsKey; - private final NamespacedKey cMagicGemNameKey; - private final NamespacedKey cMagicGemEmbeddedKey; - private final NamespacedKey cMagicGemSkillsKey; private final LegacyItemMigrator legacyMigrator; public ItemCodec(JavaPlugin plugin, GemRegistry registry) { @@ -52,9 +49,6 @@ public final class ItemCodec { this.dataVersionKey = new NamespacedKey(plugin, "data_version"); this.embeddedKey = new NamespacedKey(plugin, "embedded_gems"); 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); } @@ -141,7 +135,6 @@ public final class ItemCodec { if (item == null || item.getType().isAir() || !item.hasItemMeta()) return Optional.empty(); ItemMeta meta = item.getItemMeta(); String name = meta.getPersistentDataContainer().get(gemNameKey, PersistentDataType.STRING); - if (name == null) name = meta.getPersistentDataContainer().get(cMagicGemNameKey, PersistentDataType.STRING); Optional direct = registry.find(name); if (direct.isPresent()) return direct; LegacyItemMigrator.LegacyData legacy = legacyMigrator.inspect(item); @@ -169,7 +162,6 @@ public final class ItemCodec { if (item == null || !item.hasItemMeta()) return Map.of(); PersistentDataContainer data = item.getItemMeta().getPersistentDataContainer(); String encoded = data.get(embeddedKey, PersistentDataType.STRING); - if (encoded == null) encoded = data.get(cMagicGemEmbeddedKey, PersistentDataType.STRING); Map result = new LinkedHashMap<>(); if (encoded == null || encoded.isBlank()) { LegacyItemMigrator.LegacyData legacy = legacyMigrator.inspect(item); @@ -224,7 +216,6 @@ public final class ItemCodec { if (item == null || !item.hasItemMeta()) return new ArrayList<>(); PersistentDataContainer data = item.getItemMeta().getPersistentDataContainer(); String value = data.get(skillsKey, PersistentDataType.STRING); - if (value == null) value = data.get(cMagicGemSkillsKey, PersistentDataType.STRING); if (value == null || value.isBlank()) { List legacy = new ArrayList<>(legacyMigrator.inspect(item).skills()); if (!legacy.isEmpty()) writeSkills(item, legacy); diff --git a/src/main/java/cn/infstar/cMagicGem/migration/LegacyDirectoryImporter.java b/src/main/java/cn/infstar/cMagicGem/migration/LegacyDirectoryImporter.java index 077ffb9..f249ba6 100644 --- a/src/main/java/cn/infstar/cMagicGem/migration/LegacyDirectoryImporter.java +++ b/src/main/java/cn/infstar/cMagicGem/migration/LegacyDirectoryImporter.java @@ -1,8 +1,5 @@ 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 java.io.IOException; @@ -15,234 +12,124 @@ import java.util.Objects; import java.util.logging.Level; 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 { - 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 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 targetDirectory; - private final Runnable saveDefaultConfig; private final Logger logger; public LegacyDirectoryImporter(JavaPlugin plugin) { this(plugin.getDataFolder().toPath().toAbsolutePath().normalize().getParent(), - plugin.getDataFolder().toPath().toAbsolutePath().normalize(), - plugin::saveDefaultConfig, plugin.getLogger()); + plugin.getDataFolder().toPath().toAbsolutePath().normalize(), plugin.getLogger()); } - LegacyDirectoryImporter(Path pluginsDirectory, Path targetDirectory, - Runnable saveDefaultConfig, Logger logger) { + LegacyDirectoryImporter(Path pluginsDirectory, Path targetDirectory, Logger logger) { this.pluginsDirectory = Objects.requireNonNull(pluginsDirectory).toAbsolutePath().normalize(); this.targetDirectory = Objects.requireNonNull(targetDirectory).toAbsolutePath().normalize(); - this.saveDefaultConfig = Objects.requireNonNull(saveDefaultConfig); this.logger = Objects.requireNonNull(logger); } 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(); } - Path sourceDirectory = pluginsDirectory.resolve(LEGACY_PLUGIN_DIRECTORY).normalize(); - if (!Files.isDirectory(sourceDirectory)) return ImportResult.none(); 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 mergedFiles = 0; int conflictFiles = 0; - boolean configImported = false; List conflicts = new ArrayList<>(); try { Files.createDirectories(targetDirectory); for (String directory : DATA_DIRECTORIES) { - ImportCounts counts = importDirectory(sourceDirectory.resolve(directory), targetDirectory.resolve(directory), - Path.of(directory), conflicts); + ImportCounts counts = importDirectory(sourceDirectory.resolve(directory), + targetDirectory.resolve(directory), Path.of(directory), conflicts); copiedFiles += counts.copiedFiles; - mergedFiles += counts.mergedFiles; conflictFiles += counts.conflictFiles; } Path sourceConfig = sourceDirectory.resolve("config.yml"); - Path targetConfig = targetDirectory.resolve("config.yml"); if (Files.isRegularFile(sourceConfig)) { - boolean targetConfigExisted = Files.isRegularFile(targetConfig); - if (!targetConfigExisted) saveDefaultConfig.run(); - MergeResult result = mergeYaml(sourceConfig, targetConfig, Path.of("config.yml"), - !targetConfigExisted, conflicts); - configImported = result.changed; - if (result.changed) mergedFiles++; - if (result.conflicted) conflictFiles++; + ImportCounts counts = importFile(sourceConfig, targetDirectory.resolve("config.yml"), + Path.of("config.yml"), conflicts); + copiedFiles += counts.copiedFiles; + conflictFiles += counts.conflictFiles; } writeConflictBackups(sourceDirectory, conflicts); - Files.writeString(marker, "Imported from plugins/CMagicGem. Source directory remains unchanged.\n"); - if (!conflicts.isEmpty()) { - for (Conflict conflict : conflicts) { - logger.warning("CMagicGem migration kept the MagicGem value for " - + conflict.sourceRelative() + " (" + conflict.key() + ")" - + "; the original source file was saved under migration-backup/CMagicGem."); - } + Files.writeString(marker, "Imported from plugins/MagicGem. Source directory remains unchanged.\n"); + for (Conflict conflict : conflicts) { + logger.warning("MagicGem Reforged kept the target file for " + conflict.relativePath() + + "; the original file was saved under migration-backup/MagicGem."); } - logger.info("Imported plugins/CMagicGem into plugins/MagicGem: " + copiedFiles + " files copied, " - + mergedFiles + " files merged, " + conflictFiles + " files with conflicts. " - + "The source directory was left untouched."); - return new ImportResult(copiedFiles, mergedFiles, conflictFiles, configImported, false); - } catch (IOException | InvalidConfigurationException exception) { - logger.log(Level.SEVERE, "Could not finish importing plugins/CMagicGem. No source files were deleted; " - + "the migration will be retried on the next startup.", exception); - return new ImportResult(copiedFiles, mergedFiles, conflictFiles, configImported, false); + logger.info("Imported original plugins/MagicGem into " + targetDirectory.getFileName() + + ": " + copiedFiles + " files copied, " + conflictFiles + " conflicts. " + + "The original directory was left untouched."); + return new ImportResult(copiedFiles, conflictFiles, false); + } catch (IOException exception) { + logger.log(Level.SEVERE, "Could not finish importing original plugins/MagicGem. " + + "The original directory was not deleted; migration will be retried on the next startup.", exception); + return new ImportResult(copiedFiles, conflictFiles, false); } } - private ImportCounts importDirectory(Path source, Path target, Path sourcePrefix, List conflicts) - throws IOException, InvalidConfigurationException { - if (!Files.isDirectory(source)) return new ImportCounts(0, 0, 0); + private ImportCounts importDirectory(Path source, Path target, Path prefix, List conflicts) + throws IOException { + if (!Files.isDirectory(source)) return new ImportCounts(0, 0); int copied = 0; - int merged = 0; - int conflicted = 0; + int conflictCount = 0; Path normalizedTarget = target.toAbsolutePath().normalize(); try (var paths = Files.walk(source)) { for (Path path : paths.toList()) { - Path relative = source.relativize(path); - Path destination = normalizedTarget.resolve(relative).normalize(); - if (!destination.startsWith(normalizedTarget)) continue; - if (Files.isDirectory(path)) { - Files.createDirectories(destination); - continue; - } - 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)), "")); - conflicted++; - } + if (Files.isDirectory(path) || !Files.isRegularFile(path)) continue; + Path relative = prefix.resolve(source.relativize(path)); + ImportCounts counts = importFile(path, normalizedTarget.resolve(source.relativize(path)).normalize(), + relative, conflicts); + copied += counts.copiedFiles; + conflictCount += counts.conflictFiles; } } - return new ImportCounts(copied, merged, conflicted); + return new ImportCounts(copied, conflictCount); } - private MergeResult mergeYaml(Path source, Path destination, Path sourceRelative, - boolean preferSource, List conflicts) - throws IOException, InvalidConfigurationException { - if (!Files.exists(destination)) { - Files.createDirectories(destination.getParent()); - Files.copy(source, destination, StandardCopyOption.COPY_ATTRIBUTES); - return new MergeResult(true, false); + private ImportCounts importFile(Path source, Path target, Path relative, List conflicts) + throws IOException { + if (!target.normalize().startsWith(targetDirectory)) return new ImportCounts(0, 0); + if (!Files.exists(target)) { + Files.createDirectories(target.getParent()); + Files.copy(source, target, StandardCopyOption.COPY_ATTRIBUTES); + return new ImportCounts(1, 0); } - - YamlConfiguration sourceYaml = new YamlConfiguration(); - sourceYaml.load(source.toFile()); - YamlConfiguration destinationYaml = new YamlConfiguration(); - destinationYaml.load(destination.toFile()); - List 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 (Files.mismatch(source, target) != -1) { + conflicts.add(new Conflict(relative)); + return new ImportCounts(0, 1); } - if (changed) destinationYaml.save(destination.toFile()); - 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 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; + return new ImportCounts(0, 0); } private void writeConflictBackups(Path sourceDirectory, List conflicts) throws IOException { - for (Path relative : conflicts.stream().map(Conflict::sourceRelative).distinct().toList()) { - Path sourceFile = sourceDirectory.resolve(relative).normalize(); - if (!sourceFile.startsWith(sourceDirectory) || !Files.isRegularFile(sourceFile)) continue; - Path backup = targetDirectory.resolve("migration-backup").resolve(LEGACY_PLUGIN_DIRECTORY).resolve(relative); + for (Path relative : conflicts.stream().map(Conflict::relativePath).distinct().toList()) { + Path source = sourceDirectory.resolve(relative).normalize(); + if (!source.startsWith(sourceDirectory) || !Files.isRegularFile(source)) continue; + Path backup = targetDirectory.resolve("migration-backup").resolve(TARGET_BACKUP_DIRECTORY).resolve(relative); 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) { - 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) { + public record ImportResult(int copiedFiles, int conflictFiles, boolean alreadyMigrated) { 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 MergeResult(boolean changed, boolean conflicted) { } - private record Conflict(Path sourceRelative, String key) { } + private record ImportCounts(int copiedFiles, int conflictFiles) { } + private record Conflict(Path relativePath) { } } diff --git a/src/main/java/cn/infstar/magicgemreforged/MagicGemReforged.java b/src/main/java/cn/infstar/magicgemreforged/MagicGemReforged.java new file mode 100644 index 0000000..87f26c0 --- /dev/null +++ b/src/main/java/cn/infstar/magicgemreforged/MagicGemReforged.java @@ -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; + } + } +} diff --git a/src/main/java/pku/yim/magicgem/MagicGem.java b/src/main/java/pku/yim/magicgem/MagicGem.java index 55a77c8..620d65e 100644 --- a/src/main/java/pku/yim/magicgem/MagicGem.java +++ b/src/main/java/pku/yim/magicgem/MagicGem.java @@ -5,8 +5,8 @@ import cn.infstar.cmagicgem.config.PluginSettings; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; -/** Legacy entry point retained for plugins that resolve MagicGem by class name. */ -public final class MagicGem extends CMagicGem { +/** Compatibility base class retained for plugins compiled against the original MagicGem API. */ +public class MagicGem extends CMagicGem { public static MagicGem instance; public static boolean placeholderapi; public static boolean MMCorMMA; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 3ad0b97..484bbec 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -5,7 +5,6 @@ gem-lore: false gem-craft: false skill-engine: true skill-engine-lore-color: false -legacy-migration: true potion-global-limit: {} integrations: mythicmobs: diff --git a/src/main/resources/gems/ExampleGem.yml b/src/main/resources/gems/ExampleGem.yml index 9116b9f..0c2f960 100644 --- a/src/main/resources/gems/ExampleGem.yml +++ b/src/main/resources/gems/ExampleGem.yml @@ -4,7 +4,7 @@ Material: EMERALD Display: "&a示例生命宝石" Tips: - - "&7用于验证 CMagicGem 基础功能" + - "&7用于验证 MagicGem Reforged 基础功能" Require: - ARMOR Success: 100 diff --git a/src/main/resources/gui/Default.yml b/src/main/resources/gui/Default.yml index bf67b44..3af1693 100644 --- a/src/main/resources/gui/Default.yml +++ b/src/main/resources/gui/Default.yml @@ -1,5 +1,5 @@ default: - Title: "&8CMagicGem 镶嵌台 &7%page%" + Title: "&8MagicGem Reforged 镶嵌台 &7%page%" Page: 1 Locations: [] Slots: diff --git a/src/main/resources/languages/en_US.yml b/src/main/resources/languages/en_US.yml index 815fc9e..310fcec 100644 --- a/src/main/resources/languages/en_US.yml +++ b/src/main/resources/languages/en_US.yml @@ -1,6 +1,6 @@ version: 1.10 plugin: - prefix: "&9&l[&e&lMagicGem&9&l]&r" + prefix: "&9&l[&e&lMagicGem Reforged&9&l]&r" read_file: "&eReading " dupe_gem: "&cDuplicated gems find! Name: " extract: "&eExtracting resource files" @@ -13,7 +13,7 @@ plugin: accept: "&cPlugin licensed. Thank you for purchasing!" busy: "&cThe license server is busy. Will try again later" 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" gem: tip: @@ -187,8 +187,8 @@ reward: type: "Potion Effect" duration: "Duration" skill: - add: "Add MagicGem Skill" - change: "Change MagicGem Skill" + add: "Add MagicGem Reforged Skill" + change: "Change MagicGem Reforged Skill" remove: "Remove MaticGem Skill" unbreakable: type: "Unbreakable" @@ -276,7 +276,7 @@ cmagic: gem_not_found: "&cUnknown gem: {gem}" not_embedded: "&cThat gem is not embedded in this item" command: - reload: "&aMagicGem reloaded safely; {amount} gems loaded." + reload: "&aMagicGem Reforged reloaded safely; {amount} gems loaded." give: "&aGave {player} {amount} {gem}" list: "&6Loaded gems (&e{amount}&6):" signal_all: "&aSent the skill signal to every online player." diff --git a/src/main/resources/languages/zh_CN.yml b/src/main/resources/languages/zh_CN.yml index 09fecc3..2e44339 100644 --- a/src/main/resources/languages/zh_CN.yml +++ b/src/main/resources/languages/zh_CN.yml @@ -276,7 +276,7 @@ cmagic: gem_not_found: "&c未知宝石: {gem}" not_embedded: "&c装备未镶嵌该宝石" command: - reload: "&aMagicGem 已安全重载,共加载 {amount} 颗宝石。" + reload: "&aMagicGem Reforged 已安全重载,共加载 {amount} 颗宝石。" give: "&a已给予 {player} {amount} 个 {gem}" list: "&6已加载宝石 (&e{amount}&6):" signal_all: "&a已向所有在线玩家发送技能信号。" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a35f978..47be7ad 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,13 +1,13 @@ -name: MagicGem +name: MagicGem-Reforged version: '${version}' -description: Paper 1.21.x compatible MagicGem reimplementation -main: pku.yim.magicgem.MagicGem +description: MagicGem Reforged for Paper 1.21.x +main: cn.infstar.magicgemreforged.MagicGemReforged api-version: '1.21' authors: [ Coldsmile_7 ] softdepend: [PlaceholderAPI, Vault, PlayerPoints, MythicMobs, MMOItems, MythicArtifacts, MythicCrucible, Residence, PvPManager, EcoEnchants, GoldenEnchants] commands: mgem: - description: MagicGem main command + description: MagicGem Reforged main command usage: /mgem help aliases: [cmgem, cmg] permissions: diff --git a/src/test/java/cn/infstar/cmagicgem/migration/LegacyDirectoryImporterTest.java b/src/test/java/cn/infstar/cmagicgem/migration/LegacyDirectoryImporterTest.java index 54ef456..4ea52c6 100644 --- a/src/test/java/cn/infstar/cmagicgem/migration/LegacyDirectoryImporterTest.java +++ b/src/test/java/cn/infstar/cmagicgem/migration/LegacyDirectoryImporterTest.java @@ -1,10 +1,8 @@ package cn.infstar.cmagicgem.migration; -import org.bukkit.configuration.file.YamlConfiguration; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.logging.Logger; @@ -18,81 +16,71 @@ class LegacyDirectoryImporterTest { Path temporaryDirectory; @Test - void mergesOldDirectoryWithoutOverwritingTargetAndKeepsConflictBackup() throws Exception { + void importsOriginalMagicGemDirectoryIntoReforgedTarget() throws Exception { Path plugins = Files.createDirectory(temporaryDirectory.resolve("plugins")); - Path source = Files.createDirectories(plugins.resolve("CMagicGem/gems")); - Path target = Files.createDirectories(plugins.resolve("MagicGem/gems")); - 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"); + Path source = Files.createDirectories(plugins.resolve("MagicGem/gems")); + Path target = plugins.resolve("MagicGem-Reforged"); 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(target.getParent().resolve("config.yml"), "language: zh_CN\nbackpack: false\n"); + Files.writeString(source.getParent().resolve("config.yml"), "language: en_US\nbackpack: true\n"); - LegacyDirectoryImporter importer = importer(plugins, plugins.resolve("MagicGem"), () -> { }); - LegacyDirectoryImporter.ImportResult result = importer.importIfPresent(); + LegacyDirectoryImporter.ImportResult result = importer(plugins, target).importIfPresent(); - YamlConfiguration mergedGem = load(currentGem); - YamlConfiguration mergedConfig = load(target.getParent().resolve("config.yml")); - assertEquals("Current Name", mergedGem.getString("Shared.Name")); - assertEquals("retained", mergedGem.getString("Shared.OldOnly")); - assertEquals("retained", mergedGem.getString("Shared.CurrentOnly")); - assertTrue(Files.isRegularFile(target.resolve("Custom.yml"))); - assertEquals("zh_CN", mergedConfig.getString("language")); - assertEquals(false, mergedConfig.getBoolean("backpack")); - 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); + assertTrue(Files.isRegularFile(target.resolve("gems/Custom.yml"))); + assertTrue(Files.isRegularFile(target.resolve("config.yml"))); + assertEquals("Custom:\n Name: Imported\n", Files.readString(target.resolve("gems/Custom.yml"))); + assertEquals("language: en_US\nbackpack: true\n", Files.readString(target.resolve("config.yml"))); + assertTrue(Files.isDirectory(source.getParent())); + assertTrue(result.copiedFiles() >= 2); + assertEquals(0, result.conflictFiles()); + assertFalse(result.alreadyMigrated()); + } + @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(); + assertTrue(repeated.alreadyMigrated()); assertEquals(0, repeated.copiedFiles()); + assertEquals(0, repeated.conflictFiles()); } @Test - void oldConfigurationOverridesBundledDefaultsWhenTargetHasNoConfig() throws Exception { + void missingOriginalDirectoryDoesNothing() throws Exception { Path plugins = Files.createDirectory(temporaryDirectory.resolve("plugins")); - Path source = Files.createDirectories(plugins.resolve("CMagicGem")); - 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); - } - }; + Path target = plugins.resolve("MagicGem-Reforged"); - LegacyDirectoryImporter.ImportResult result = importer(plugins, target, createDefaults).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(); + LegacyDirectoryImporter.ImportResult result = importer(plugins, target).importIfPresent(); + assertEquals(0, result.copiedFiles()); + assertEquals(0, result.conflictFiles()); 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) { - return new LegacyDirectoryImporter(plugins, target, saveDefaults, Logger.getLogger("migration-test")); - } - - private YamlConfiguration load(Path path) { - return YamlConfiguration.loadConfiguration(path.toFile()); + private LegacyDirectoryImporter importer(Path plugins, Path target) { + return new LegacyDirectoryImporter(plugins, target, Logger.getLogger("migration-test")); } } diff --git a/src/test/java/cn/infstar/cmagicgem/skill/BundledConfigurationTest.java b/src/test/java/cn/infstar/cmagicgem/skill/BundledConfigurationTest.java index df2bebc..e705b17 100644 --- a/src/test/java/cn/infstar/cmagicgem/skill/BundledConfigurationTest.java +++ b/src/test/java/cn/infstar/cmagicgem/skill/BundledConfigurationTest.java @@ -23,11 +23,11 @@ class BundledConfigurationTest { private final DirectiveParser parser = new DirectiveParser(); @Test - void pluginDescriptorKeepsLegacyCommandAndPermissionCompatibility() { + void pluginDescriptorUsesReforgedEntryPointAndKeepsCompatibility() { YamlConfiguration descriptor = YamlConfiguration.loadConfiguration(new File("src/main/resources/plugin.yml")); - assertTrue(descriptor.getString("name").equalsIgnoreCase("MagicGem")); - assertTrue(descriptor.getString("main").equals("pku.yim.magicgem.MagicGem")); + assertTrue(descriptor.getString("name").equalsIgnoreCase("MagicGem-Reforged")); + assertTrue(descriptor.getString("main").equals("cn.infstar.magicgemreforged.MagicGemReforged")); assertNotNull(descriptor.getConfigurationSection("commands.mgem")); assertTrue(descriptor.getStringList("commands.mgem.aliases").contains("cmg")); assertTrue(descriptor.getStringList("commands.mgem.aliases").contains("cmgem"));