diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java index ad53f6d70..3d2491f0b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java @@ -2,12 +2,14 @@ package net.momirealms.craftengine.bukkit.sound; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.sound.AbstractSoundManager; import net.momirealms.craftengine.core.sound.JukeboxSong; @@ -15,6 +17,7 @@ import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.Location; import java.io.IOException; import java.nio.file.Files; diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 2d2a2967c..bed42747b 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -94,17 +94,9 @@ resource-pack: resolution: type: merge_font # Validate if there is any error in the resource pack, such as missing textures or models. - # Validation may not always be accurate due to the presence of resource pack overlays. # If your resource pack is compliant with the standard, you can disable validation to improve the resource pack generation speed. validation: enable: true - # Determines on which versions the resource pack validation will be performed. - # Allowed values: - # - 1.20.1, 1.21, 1.21.8, etc. - # - latest: the latest client version - # - server: the current server version - test-versions: - - server # Fix textures that are not within the atlas. It is unreasonable to always rely on plugins to fix your mistakes. # CraftEngine will only fix the atlas of resource pack under the first version specified in the test-versions. # You should strive to make your resource pack more standardized after gaining some experience with resource packs. diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 9bc16b149..a524baa1a 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -40,14 +40,16 @@ argument.parse.failure.either: "Could not resolve or from ' argument.parse.failure.namedtextcolor: "'' is not a named text color" info.pack.load: "Loaded pack: . Default namespace: " info.resource.load: "Loaded in ms ()" -info.resource_pack.start: "Generating resource pack..." -info.resource_pack.generate: "Generated resource pack in ms" -info.resource_pack.validate: "Validated resource pack in ms" -info.resource_pack.optimize: "Optimized resource pack in ms" +info.resource_pack.generate.start: "Generating resource pack..." +info.resource_pack.generate.finish: "Generated resource pack in ms" +info.resource_pack.validate.start: "Validating resource pack... (Progress: / | Pack Format: ~ | Overlays: )" +info.resource_pack.validate.finish: "Validated resource pack in ms" info.resource_pack.optimize.json: "> Optimizing json files..." info.resource_pack.optimize.texture: "> Optimizing textures..." info.resource_pack.optimize.result: "□ Before/After/Ratio: //%" -info.resource_pack.create: "Created resource pack zip in ms" +info.resource_pack.optimize.finish: "Optimized resource pack in ms" +info.resource_pack.create.start: "Creating the resource pack zip..." +info.resource_pack.create.finish: "Created resource pack zip in ms" info.resource_pack.upload: "Completed uploading resource pack" info.host.self.netty_server: "Netty HTTP server started on port: " info.host.cache.load: "[] Loaded cached resource pack metadata" diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index babcafa21..cbe3894bc 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -40,14 +40,16 @@ argument.parse.failure.either: "无法从 '' 解析 '' 不是颜色代码" info.pack.load: "已加载包: . 默认命名空间: " info.resource.load: "加载 耗时 ms ()" -info.resource_pack.start: "正在开始生成资源包..." -info.resource_pack.generate: "生成资源包耗时 ms" -info.resource_pack.validate: "验证资源包耗时 ms" -info.resource_pack.optimize: "优化资源包耗时 ms" +info.resource_pack.generate.start: "正在开始生成资源包..." +info.resource_pack.generate.finish: "生成资源包耗时 ms" +info.resource_pack.validate.start: "正在验证资源包... (进度: / | 资源包格式: ~ | 叠加资源包: )" +info.resource_pack.validate.finish: "验证资源包耗时 ms" info.resource_pack.optimize.json: "> 正在优化json文件..." info.resource_pack.optimize.texture: "> 正在优化贴图文件..." info.resource_pack.optimize.result: "□ 优化前/优化后/比例: //%" -info.resource_pack.create: "创建资源包文件耗时 ms" +info.resource_pack.optimize.finish: "优化资源包耗时 ms" +info.resource_pack.create.start: "正在创建资源包压缩文件..." +info.resource_pack.create.finish: "创建资源包文件耗时 ms" info.resource_pack.upload: "资源包上传完成" info.host.self.netty_server: "Netty HTTP 服务已在端口 开启" info.host.cache.load: "[] 已加载缓存的资源包元数据" diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index a1dbf343a..9921fb300 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java @@ -406,19 +406,19 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } private boolean isModernFormatRequired() { - return Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_4); + return Config.packMaxVersion().isAtOrAbove(MinecraftVersion.V1_21_4); } private boolean needsLegacyCompatibility() { - return Config.packMinVersion().isBelow(MinecraftVersions.V1_21_4); + return Config.packMinVersion().isBelow(MinecraftVersion.V1_21_4); } private boolean needsCustomModelDataCompatibility() { - return Config.packMinVersion().isBelow(MinecraftVersions.V1_21_2); + return Config.packMinVersion().isBelow(MinecraftVersion.V1_21_2); } private boolean needsItemModelCompatibility() { - return Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2) && VersionHelper.isOrAbove1_21_2(); //todo 能否通过客户端包解决问题 + return Config.packMaxVersion().isAtOrAbove(MinecraftVersion.V1_21_2) && VersionHelper.isOrAbove1_21_2(); //todo 能否通过客户端包解决问题 } public Map idAllocators() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index 01d973b46..95b195f93 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -20,6 +20,8 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.pack.host.impl.NoneHost; import net.momirealms.craftengine.core.pack.mcmeta.Overlay; import net.momirealms.craftengine.core.pack.mcmeta.PackMcMeta; +import net.momirealms.craftengine.core.pack.mcmeta.PackVersion; +import net.momirealms.craftengine.core.pack.mcmeta.overlay.OverlayCombination; import net.momirealms.craftengine.core.pack.model.ItemModel; import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel; import net.momirealms.craftengine.core.pack.model.ModernItemModel; @@ -742,8 +744,8 @@ public abstract class AbstractPackManager implements PackManager { } @Override - public void generateResourcePack() throws Exception { - this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.start")); + public void generateResourcePack() { + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.generate.start")); long time1 = System.currentTimeMillis(); // Create cache data @@ -791,19 +793,22 @@ public abstract class AbstractPackManager implements PackManager { this.removeAllShaders(generatedPackPath); } long time2 = System.currentTimeMillis(); - this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.generate", String.valueOf(time2 - time1))); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.generate.finish", String.valueOf(time2 - time1))); if (Config.validateResourcePack()) { - for (MinecraftVersion version : Config.validationTestVersions()) { - this.validateResourcePack(generatedPackPath, version); - } + this.validateResourcePack(generatedPackPath); } long time3 = System.currentTimeMillis(); - this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.validate", String.valueOf(time3 - time2))); + if (Config.validateResourcePack()) { + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.validate.finish", String.valueOf(time3 - time2))); + } if (Config.optimizeResourcePack()) { this.optimizeResourcePack(generatedPackPath); } long time4 = System.currentTimeMillis(); - this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize", String.valueOf(time4 - time3))); + if (Config.optimizeResourcePack()) { + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize.finish", String.valueOf(time4 - time3))); + } + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.create.start")); Path finalPath = resourcePackPath(); Files.createDirectories(finalPath.getParent()); try { @@ -812,8 +817,10 @@ public abstract class AbstractPackManager implements PackManager { this.plugin.logger().severe("Error zipping resource pack", e); } long time5 = System.currentTimeMillis(); - this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.create", String.valueOf(time5 - time4))); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.create.finish", String.valueOf(time5 - time4))); this.generationEventDispatcher.accept(generatedPackPath, finalPath); + } catch (IOException e) { + this.plugin.logger().severe("Error generating resource pack", e); } } @@ -1177,8 +1184,7 @@ public abstract class AbstractPackManager implements PackManager { } } - @SuppressWarnings("DuplicatedCode") - private void validateResourcePack(Path path, MinecraftVersion version) { + private void validateResourcePack(Path path) { Path packMcMetaPath = path.resolve("pack.mcmeta"); PackMcMeta packMeta; try { @@ -1188,27 +1194,150 @@ public abstract class AbstractPackManager implements PackManager { return; } - // 获取当前版本生效的overlays - List overlayDirectories = new ArrayList<>(); - for (Overlay overlay : packMeta.overlays()) { - if (overlay.test(version)) { - overlayDirectories.add(overlay.directory()); + List segments = new ArrayList<>(); + // 完全小于1.21.11或完全大于1.21.11 + if (Config.packMaxVersion().isBelow(MinecraftVersion.V1_21_11) || Config.packMinVersion().isAtOrAbove(MinecraftVersion.V1_21_11)) { + OverlayCombination combination = new OverlayCombination(packMeta.overlays(), Config.packMinVersion().packFormat().major(), Config.packMaxVersion().packFormat().major()); + while (combination.hasNext()) { + OverlayCombination.Segment segment = combination.nextSegment(); + if (segment != null) { + segments.add(segment); + } else { + break; + } } } - overlayDirectories = overlayDirectories.stream().distinct().toList(); - - List rootPathList = new ArrayList<>(); - rootPathList.add(path); - for (String directory : overlayDirectories) { - Path resolve = path.resolve(directory); - if (Files.isDirectory(resolve)) { - rootPathList.add(resolve); + // 混合版本 + else { + OverlayCombination combinationLegacy = new OverlayCombination(packMeta.overlays(), Config.packMinVersion().packFormat().major(), MinecraftVersion.V1_21_11.packFormat().major() - 1); + while (combinationLegacy.hasNext()) { + OverlayCombination.Segment segment = combinationLegacy.nextSegment(); + if (segment != null) { + segments.add(segment); + } else { + break; + } + } + OverlayCombination combinationModern = new OverlayCombination(packMeta.overlays(), MinecraftVersion.V1_21_11.packFormat().major(), Config.packMaxVersion().packFormat().major()); + while (combinationModern.hasNext()) { + OverlayCombination.Segment segment = combinationModern.nextSegment(); + if (segment != null) { + segments.add(segment); + } else { + break; + } } } - // 收集全部overlay,按照正确的顺序加载 - Path[] rootPaths = rootPathList.toArray(new Path[0]); + AtlasFixer itemFixer = new AtlasFixer(); + AtlasFixer blockFixer = new AtlasFixer(); + boolean hasNonOverlaySupport = false; + if (!segments.isEmpty()) { + // 第一个segment一定是最小的 + hasNonOverlaySupport = segments.getFirst().min() <= MinecraftVersion.V1_20_1.packFormat().major(); + } + + for (int i = 0, size = segments.size(); i < size; i++) { + OverlayCombination.Segment segment = segments.get(i); + List rootPathList = new ArrayList<>(); + rootPathList.add(path); + List overlayInOrder = new ArrayList<>(segment.overlays().size()); + for (Overlay overlay : packMeta.overlays()) { + if (segment.overlays().contains(overlay)) { + Path resolve = path.resolve(overlay.directory()); + if (Files.isDirectory(resolve)) { + overlayInOrder.add(overlay); + rootPathList.add(resolve); + } + } + } + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.validate.start", + String.valueOf(i + 1), String.valueOf(size), String.valueOf(segment.min()), String.valueOf(segment.max()), overlayInOrder.stream().map(Overlay::directory).toList().toString())); + ValidationResult result = validateOverlayedResourcePack(rootPathList.toArray(new Path[0]), segment.max() >= MinecraftVersion.V1_21_11.packFormat().major()); + if (Config.fixTextureAtlas()) { + // 有修复物品 + if (result.fixedItemAtlas != null) { + itemFixer.addEntry(segment.min(), segment.max(), result.fixedItemAtlas); + } + // 有修复方块 + if (result.fixedBlockAtlas != null) { + blockFixer.addEntry(segment.min(), segment.max(), result.fixedBlockAtlas); + } else if (hasNonOverlaySupport) { + // 如果有低版本的支持,那么要通过overlay复原atlas + blockFixer.addEntry(segment.min(), segment.max(), Objects.requireNonNullElseGet(result.originalBlockAtlas, JsonObject::new)); + } + } + } + + // 尝试修复atlas + if (Config.fixTextureAtlas()) { + Map atlasToAdd = new LinkedHashMap<>(); + // 物品 + for (AtlasFixer.Entry entry : itemFixer.entries()) { + int min = entry.min(); + int max = entry.max(); + MinecraftVersion minV = MinecraftVersion.byMajorPackFormat(min).getFirst(); + MinecraftVersion maxV = MinecraftVersion.byMajorPackFormat(max).getLast(); + String directoryName = Config.createOverlayFolderName(minV.version().replace("\\.", "_") + "-" + maxV.version().replace("\\.", "_")); + Path atlasPath = path.resolve(directoryName) + .resolve("assets") + .resolve("minecraft") + .resolve("atlases") + .resolve("items.json"); + try { + Files.createDirectories(atlasPath.getParent()); + GsonHelper.writeJsonFile(entry.atlas(), atlasPath); + if (!atlasToAdd.containsKey(directoryName)) { + Overlay overlay = new Overlay(new PackVersion(min), new PackVersion(max), directoryName); + atlasToAdd.put(directoryName, overlay.getAsOverlayEntry()); + } + } catch (IOException e) { + this.plugin.logger().warn("Failed to write atlas " + atlasPath.toAbsolutePath(), e); + } + } + // 方块 + for (AtlasFixer.Entry entry : itemFixer.entries()) { + int min = entry.min(); + int max = entry.max(); + MinecraftVersion minV = MinecraftVersion.byMajorPackFormat(min).getFirst(); + MinecraftVersion maxV = MinecraftVersion.byMajorPackFormat(max).getLast(); + String directoryName = Config.createOverlayFolderName(minV.version().replace("\\.", "_") + "-" + maxV.version().replace("\\.", "_")); + // 这个版本不认可overlay,得把atlas直接写进主包内 + if (min <= MinecraftVersion.V1_20_1.packFormat().major()) { + Path atlasPath = path.resolve("assets") + .resolve("minecraft") + .resolve("atlases") + .resolve("blocks.json"); + try { + Files.createDirectories(atlasPath.getParent()); + GsonHelper.writeJsonFile(entry.atlas(), atlasPath); + } catch (IOException e) { + this.plugin.logger().warn("Failed to write atlas " + atlasPath.toAbsolutePath(), e); + } + } else { + Path atlasPath = path.resolve(directoryName) + .resolve("assets") + .resolve("minecraft") + .resolve("atlases") + .resolve("blocks.json"); + try { + Files.createDirectories(atlasPath.getParent()); + GsonHelper.writeJsonFile(entry.atlas(), atlasPath); + if (!atlasToAdd.containsKey(directoryName)) { + Overlay overlay = new Overlay(new PackVersion(min), new PackVersion(max), directoryName); + atlasToAdd.put(directoryName, overlay.getAsOverlayEntry()); + } + } catch (IOException e) { + this.plugin.logger().warn("Failed to write atlas " + atlasPath.toAbsolutePath(), e); + } + } + } + } + } + + @SuppressWarnings("DuplicatedCode") + private ValidationResult validateOverlayedResourcePack(Path[] rootPaths, boolean above1_21_11) { Multimap glyphToFonts = HashMultimap.create(128, 32); // 图片到字体的映射 Multimap modelToItemDefinitions = HashMultimap.create(128, 4); // 模型到物品的映射 Multimap modelToBlockStates = HashMultimap.create(128, 32); // 模型到方块的映射 @@ -1216,9 +1345,6 @@ public abstract class AbstractPackManager implements PackManager { Multimap textureToEquipments = HashMultimap.create(128, 8); // 纹理到盔甲的映射 Multimap oggToSoundEvents = HashMultimap.create(128, 4); // 音频到声音的映射 - Map blockAtlasJsons = new HashMap<>(); - Map itemAtlasJsons = new HashMap<>(); - JsonObject lastBlocksAtlas = null; JsonObject lastItemAtlas = null; @@ -1231,39 +1357,27 @@ public abstract class AbstractPackManager implements PackManager { .resolve("blocks.json"); if (Files.exists(blockAtlasFile)) { try { - JsonObject atlasJsonObject = GsonHelper.readJsonFile(blockAtlasFile).getAsJsonObject(); - blockAtlasJsons.put(blockAtlasFile, atlasJsonObject); - lastBlocksAtlas = atlasJsonObject; + lastBlocksAtlas = GsonHelper.readJsonFile(blockAtlasFile).getAsJsonObject(); } catch (IOException | JsonParseException e) { TranslationManager.instance().log("warning.config.resource_pack.generation.malformatted_json", blockAtlasFile.toAbsolutePath().toString()); } } - Path itemAtlasFile = rootPath - .resolve("assets") - .resolve("minecraft") - .resolve("atlases") - .resolve("items.json"); - if (Files.exists(itemAtlasFile)) { - try { - JsonObject atlasJsonObject = GsonHelper.readJsonFile(itemAtlasFile).getAsJsonObject(); - itemAtlasJsons.put(itemAtlasFile, atlasJsonObject); - lastItemAtlas = atlasJsonObject; - } catch (IOException | JsonParseException e) { - TranslationManager.instance().log("warning.config.resource_pack.generation.malformatted_json", itemAtlasFile.toAbsolutePath().toString()); + if (above1_21_11) { + Path itemAtlasFile = rootPath + .resolve("assets") + .resolve("minecraft") + .resolve("atlases") + .resolve("items.json"); + if (Files.exists(itemAtlasFile)) { + try { + lastItemAtlas = GsonHelper.readJsonFile(itemAtlasFile).getAsJsonObject(); + } catch (IOException | JsonParseException e) { + TranslationManager.instance().log("warning.config.resource_pack.generation.malformatted_json", itemAtlasFile.toAbsolutePath().toString()); + } } } } - // 添加必要的基础atlas路径,方便后续修复 - Path defaultBlockAtlas = path.resolve("assets").resolve("minecraft").resolve("atlases").resolve("blocks.json"); - if (!blockAtlasJsons.containsKey(defaultBlockAtlas)) { - blockAtlasJsons.put(defaultBlockAtlas, new JsonObject()); - } - Path defaultItemAtlas = path.resolve("assets").resolve("minecraft").resolve("atlases").resolve("items.json"); - if (!itemAtlasJsons.containsKey(defaultItemAtlas)) { - itemAtlasJsons.put(defaultItemAtlas, new JsonObject()); - } - /* @@ -1274,7 +1388,7 @@ public abstract class AbstractPackManager implements PackManager { */ Atlas blockAtlas; Atlas itemAtlas; - if (version.isAtOrAbove(MinecraftVersions.V1_21_11)) { + if (above1_21_11) { blockAtlas = new Atlas(ListUtils.newNonNullList(lastBlocksAtlas, this.vanillaBlockAtlas)); itemAtlas = new Atlas(ListUtils.newNonNullList(lastItemAtlas, this.vanillaItemAtlas)); } else { @@ -1292,7 +1406,7 @@ public abstract class AbstractPackManager implements PackManager { namespaces = FileUtils.collectNamespaces(assetsPath); } catch (IOException e) { this.plugin.logger().warn("Failed to collect namespaces for " + assetsPath.toAbsolutePath(), e); - return; + continue; } for (Path namespacePath : namespaces) { @@ -1697,59 +1811,71 @@ public abstract class AbstractPackManager implements PackManager { // 尝试修复 if (Config.fixTextureAtlas()) { - // 只用了方块图集,那么修复的东西丢方块图集 - if (blockAtlasInUse) { + if (above1_21_11) { + // 只用了方块图集,那么修复的东西丢方块图集 + if (blockAtlasInUse) { + for (Key key : tempTextureToFix) { + blockAtlasesToFix.put(key, entry.getKey()); + } + } + // 只用了物品图集,那么修复的东西丢物品图集 + if (itemAtlasInUse) { + for (Key key : tempTextureToFix) { + itemAtlasesToFix.put(key, entry.getKey()); + } + } + // 如果都没有,先暂定 + if (!itemAtlasInUse && !blockAtlasInUse) { + for (Key key : tempTextureToFix) { + anyAtlasesToFix.put(key, entry.getKey()); + } + } + } else { + // 对于较低的版本,全部塞blocks里 for (Key key : tempTextureToFix) { blockAtlasesToFix.put(key, entry.getKey()); } } - // 只用了物品图集,那么修复的东西丢物品图集 - if (itemAtlasInUse) { - for (Key key : tempTextureToFix) { - itemAtlasesToFix.put(key, entry.getKey()); - } - } - // 如果都没有,先暂定 - if (!itemAtlasInUse && !blockAtlasInUse) { - for (Key key : tempTextureToFix) { - anyAtlasesToFix.put(key, entry.getKey()); - } - } } } - if (Config.fixTextureAtlas() && !Config.enableObfuscation()) { - // 获取两个 Multimap 的所有 key - // 找出相同的 key - Set commonKeys = new LinkedHashSet<>(blockAtlasesToFix.keySet()); - commonKeys.retainAll(itemAtlasesToFix.keySet()); + ValidationResult result = null; - // 都是有问题的模型 - if (!commonKeys.isEmpty()) { - for (Key sprite : commonKeys) { - List duplicated = new ArrayList<>(); - duplicated.addAll(blockAtlasesToFix.get(sprite)); - duplicated.addAll(itemAtlasesToFix.get(sprite)); - TranslationManager.instance().log("warning.config.resource_pack.generation.duplicated_sprite", duplicated.toString(), sprite.asString(), "minecraft:textures/atlas/blocks.png", "minecraft:textures/atlas/items.png"); - for (Key duplicatedModel : duplicated) { - // model已经塌房了,就不进入后续遍历了 - blockModels.remove(duplicatedModel); - itemModels.remove(duplicatedModel); + if (Config.fixTextureAtlas() && !Config.enableObfuscation()) { + + if (above1_21_11) { + // 获取两个 Multimap 的所有 key + // 找出相同的 key + Set commonKeys = new LinkedHashSet<>(blockAtlasesToFix.keySet()); + commonKeys.retainAll(itemAtlasesToFix.keySet()); + + // 都是有问题的模型 + if (!commonKeys.isEmpty()) { + for (Key sprite : commonKeys) { + List duplicated = new ArrayList<>(); + duplicated.addAll(blockAtlasesToFix.get(sprite)); + duplicated.addAll(itemAtlasesToFix.get(sprite)); + TranslationManager.instance().log("warning.config.resource_pack.generation.duplicated_sprite", duplicated.toString(), sprite.asString(), "minecraft:textures/atlas/blocks.png", "minecraft:textures/atlas/items.png"); + for (Key duplicatedModel : duplicated) { + // model已经塌房了,就不进入后续遍历了 + blockModels.remove(duplicatedModel); + itemModels.remove(duplicatedModel); + } } } - } - // 将任意atlas的优先分配到items上,非必要不要到blocks上 - for (Map.Entry> entry : anyAtlasesToFix.asMap().entrySet()) { - if (blockAtlasesToFix.containsKey(entry.getKey())) { - blockAtlasesToFix.putAll(entry.getKey(), entry.getValue()); - } else { - itemAtlasesToFix.putAll(entry.getKey(), entry.getValue()); + // 将任意atlas的优先分配到items上,非必要不要到blocks上 + for (Map.Entry> entry : anyAtlasesToFix.asMap().entrySet()) { + if (blockAtlasesToFix.containsKey(entry.getKey())) { + blockAtlasesToFix.putAll(entry.getKey(), entry.getValue()); + } else { + itemAtlasesToFix.putAll(entry.getKey(), entry.getValue()); + } } - } - // 清空任意atlas分配,因为已经重分配了 - anyAtlasesToFix.clear(); + // 清空任意atlas分配,因为已经重分配了 + anyAtlasesToFix.clear(); + } /* @@ -1757,13 +1883,7 @@ public abstract class AbstractPackManager implements PackManager { 后续我们先对剩余的正常模型进行贴图路径验证(此阶段不验证那些存在atlas问题的模型,理论已被全部移除) */ - - // 如果最低支持版本太低了,那么全部塞blocks.json里 - if (!Config.packMinVersion().isAtOrAbove(MinecraftVersions.V1_21_11)) { - blockAtlasesToFix.putAll(itemAtlasesToFix); - itemAtlasesToFix.clear(); - } - + JsonObject fixedItemAtlas = null; if (!itemAtlasesToFix.isEmpty() && itemAtlas != null) { List sourcesToAdd = new ArrayList<>(itemAtlasesToFix.size()); for (Key itemTexture : itemAtlasesToFix.keySet()) { @@ -1773,25 +1893,18 @@ public abstract class AbstractPackManager implements PackManager { source.addProperty("resource", itemTexture.asString()); sourcesToAdd.add(source); } - for (Map.Entry atlas : itemAtlasJsons.entrySet()) { - JsonObject right = atlas.getValue(); - JsonArray sources = right.getAsJsonArray("sources"); - if (sources == null) { - sources = new JsonArray(); - right.add("sources", sources); - } - for (JsonObject source : sourcesToAdd) { - sources.add(source); - } - try { - Files.createDirectories(atlas.getKey().getParent()); - GsonHelper.writeJsonFile(right, atlas.getKey()); - } catch (IOException e) { - this.plugin.logger().warn("Failed to write atlas to json file", e); - } + fixedItemAtlas = lastItemAtlas == null ? new JsonObject() : lastItemAtlas; + JsonArray sources = fixedItemAtlas.getAsJsonArray("sources"); + if (sources == null) { + sources = new JsonArray(); + fixedItemAtlas.add("sources", sources); + } + for (JsonObject source : sourcesToAdd) { + sources.add(source); } } + JsonObject fixedBlockAtlas = null; if (!blockAtlasesToFix.isEmpty()) { List sourcesToAdd = new ArrayList<>(blockAtlasesToFix.size()); for (Key blockTexture : blockAtlasesToFix.keySet()) { @@ -1801,23 +1914,20 @@ public abstract class AbstractPackManager implements PackManager { source.addProperty("resource", blockTexture.asString()); sourcesToAdd.add(source); } - for (Map.Entry atlas : blockAtlasJsons.entrySet()) { - JsonObject right = atlas.getValue(); - JsonArray sources = right.getAsJsonArray("sources"); - if (sources == null) { - sources = new JsonArray(); - right.add("sources", sources); - } - for (JsonObject source : sourcesToAdd) { - sources.add(source); - } - try { - Files.createDirectories(atlas.getKey().getParent()); - GsonHelper.writeJsonFile(right, atlas.getKey()); - } catch (IOException e) { - this.plugin.logger().warn("Failed to write atlas to json file", e); - } + fixedBlockAtlas = lastBlocksAtlas == null ? new JsonObject() : lastBlocksAtlas; + JsonArray sources = fixedBlockAtlas.getAsJsonArray("sources"); + if (sources == null) { + sources = new JsonArray(); + fixedBlockAtlas.add("sources", sources); } + for (JsonObject source : sourcesToAdd) { + sources.add(source); + } + } + + // 至少有一个修复 + if (fixedBlockAtlas != null || fixedItemAtlas != null) { + result = new ValidationResult(fixedItemAtlas, lastItemAtlas, fixedBlockAtlas, lastBlocksAtlas); } } @@ -1861,12 +1971,23 @@ public abstract class AbstractPackManager implements PackManager { break outer; } } - TranslationManager.instance().log("warning.config.resource_pack.generation.missing_model_texture", entry.getValue().stream().distinct().toList().toString(), texturePath); + TranslationManager.instance().log("warning.config.resource_pack.generation.missing_model_texture", entry.getValue().toString(), texturePath); } } } + if (result == null) { + result = new ValidationResult(null, lastItemAtlas, null, lastBlocksAtlas); + } + // todo 验证 unstitch 和 paletted permutations + return result; + } + + protected record ValidationResult(JsonObject fixedItemAtlas, + JsonObject originalItemAtlas, + JsonObject fixedBlockAtlas, + JsonObject originalBlockAtlas) { } // 经过这一步拿到的模型为包含全部父贴图的模型 @@ -1997,7 +2118,7 @@ public abstract class AbstractPackManager implements PackManager { private void generateParticle(Path generatedPackPath) { if (!Config.removeTintedLeavesParticle()) return; - if (Config.packMaxVersion().isBelow(MinecraftVersions.V1_21_5)) return; + if (Config.packMaxVersion().isBelow(MinecraftVersion.V1_21_5)) return; JsonObject particleJson = new JsonObject(); JsonArray textures = new JsonArray(); textures.add("empty"); @@ -2033,8 +2154,8 @@ public abstract class AbstractPackManager implements PackManager { List> collectedTrims = new ArrayList<>(); // 为trim类型提供的两个兼容性值 - boolean needLegacyCompatibility = Config.packMinVersion().isBelow(MinecraftVersions.V1_21_2); - boolean needModernCompatibility = Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2); + boolean needLegacyCompatibility = Config.packMinVersion().isBelow(MinecraftVersion.V1_21_2); + boolean needModernCompatibility = Config.packMaxVersion().isAtOrAbove(MinecraftVersion.V1_21_2); for (Equipment equipment : this.plugin.itemManager().equipments().values()) { if (equipment instanceof ComponentBasedEquipment componentBasedEquipment) { @@ -2214,7 +2335,7 @@ public abstract class AbstractPackManager implements PackManager { return; } - if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_4)) { + if (Config.packMaxVersion().isAtOrAbove(MinecraftVersion.V1_21_4)) { Path equipmentPath = generatedPackPath .resolve("assets") .resolve(assetId.namespace()) @@ -2247,7 +2368,7 @@ public abstract class AbstractPackManager implements PackManager { this.plugin.logger().severe("Error writing equipment file", e); } } - if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2) && Config.packMinVersion().isBelow(MinecraftVersions.V1_21_4)) { + if (Config.packMaxVersion().isAtOrAbove(MinecraftVersion.V1_21_2) && Config.packMinVersion().isBelow(MinecraftVersion.V1_21_4)) { Path equipmentPath = generatedPackPath .resolve("assets") .resolve(assetId.namespace()) @@ -2303,7 +2424,7 @@ public abstract class AbstractPackManager implements PackManager { return null; } boolean shouldPreserve = false; - if (Config.packMinVersion().isBelow(MinecraftVersions.V1_21_2)) { + if (Config.packMinVersion().isBelow(MinecraftVersion.V1_21_2)) { Path legacyTarget = generatedPackPath .resolve("assets") .resolve(assetId.namespace()) @@ -2323,7 +2444,7 @@ public abstract class AbstractPackManager implements PackManager { shouldPreserve = true; } } - if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2)) { + if (Config.packMaxVersion().isAtOrAbove(MinecraftVersion.V1_21_2)) { Path modernTarget = generatedPackPath .resolve("assets") .resolve(assetId.namespace()) @@ -2362,7 +2483,7 @@ public abstract class AbstractPackManager implements PackManager { return null; } boolean shouldPreserve = false; - if (Config.packMinVersion().isBelow(MinecraftVersions.V1_21_2)) { + if (Config.packMinVersion().isBelow(MinecraftVersion.V1_21_2)) { Path legacyTarget = generatedPackPath .resolve("assets") .resolve(assetId.namespace()) @@ -2382,7 +2503,7 @@ public abstract class AbstractPackManager implements PackManager { shouldPreserve = true; } } - if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2)) { + if (Config.packMaxVersion().isAtOrAbove(MinecraftVersion.V1_21_2)) { Path modernTarget = generatedPackPath .resolve("assets") .resolve(assetId.namespace()) @@ -2665,8 +2786,8 @@ public abstract class AbstractPackManager implements PackManager { } private void generateModernItemModels1_21_2(Path generatedPackPath) { - if (Config.packMaxVersion().isBelow(MinecraftVersions.V1_21_2)) return; - if (Config.packMinVersion().isAtOrAbove(MinecraftVersions.V1_21_4)) return; + if (Config.packMaxVersion().isBelow(MinecraftVersion.V1_21_2)) return; + if (Config.packMinVersion().isAtOrAbove(MinecraftVersion.V1_21_4)) return; // 此段代码生成1.21.2专用的item model文件,情况非常复杂! for (Map.Entry> entry : this.plugin.itemManager().modernItemModels1_21_2().entrySet()) { @@ -2748,7 +2869,7 @@ public abstract class AbstractPackManager implements PackManager { } private void generateModernItemModels1_21_4(Path generatedPackPath, Consumer callback) { - if (Config.packMaxVersion().isBelow(MinecraftVersions.V1_21_4)) return; + if (Config.packMaxVersion().isBelow(MinecraftVersion.V1_21_4)) return; for (Map.Entry entry : this.plugin.itemManager().modernItemModels1_21_4().entrySet()) { Key key = entry.getKey(); Path itemPath = generatedPackPath @@ -2802,7 +2923,7 @@ public abstract class AbstractPackManager implements PackManager { } private void generateModernItemOverrides(Path generatedPackPath, Consumer callback) { - if (Config.packMaxVersion().isBelow(MinecraftVersions.V1_21_4)) return; + if (Config.packMaxVersion().isBelow(MinecraftVersion.V1_21_4)) return; for (Map.Entry> entry : this.plugin.itemManager().modernItemOverrides().entrySet()) { Key vanillaItemModel = entry.getKey(); Path overridedItemPath = generatedPackPath @@ -2893,7 +3014,7 @@ public abstract class AbstractPackManager implements PackManager { } private void generateLegacyItemOverrides(Path generatedPackPath) { - if (Config.packMinVersion().isAtOrAbove(MinecraftVersions.V1_21_4)) return; + if (Config.packMinVersion().isAtOrAbove(MinecraftVersion.V1_21_4)) return; for (Map.Entry> entry : this.plugin.itemManager().legacyItemOverrides().entrySet()) { Key vanillaLegacyModel = entry.getKey(); Path overridedItemPath = generatedPackPath diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AtlasFixer.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AtlasFixer.java new file mode 100644 index 000000000..6a60e66ef --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AtlasFixer.java @@ -0,0 +1,66 @@ +package net.momirealms.craftengine.core.pack; + +import com.google.gson.JsonObject; + +import java.util.ArrayList; +import java.util.List; + +class AtlasFixer { + private final List entries; + + public AtlasFixer() { + this.entries = new ArrayList<>(); + } + + // 理论是从小到大加的 + public void addEntry(int min, int max, JsonObject atlas) { + if (this.entries.isEmpty()) { + this.entries.add(new Entry(min, max, atlas)); + } else { + Entry last = this.entries.getLast(); + // 相同则扩大区间 + if (last.atlas.equals(atlas)) { + last.setMax(max); + } + // 不同则另加元素 + else { + this.entries.add(new Entry(min, max, atlas)); + } + } + } + + public List entries() { + return entries; + } + + protected static class Entry { + private int min, max; + private final JsonObject atlas; + + public Entry(int min, int max, JsonObject atlas) { + this.min = min; + this.max = max; + this.atlas = atlas; + } + + public int min() { + return min; + } + + public int max() { + return max; + } + + public JsonObject atlas() { + return atlas; + } + + public void setMin(int min) { + this.min = min; + } + + public void setMax(int max) { + this.max = max; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/Overlay.java b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/Overlay.java index 6b4a9d470..72440de6b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/Overlay.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/Overlay.java @@ -32,6 +32,33 @@ public class Overlay { this.maxVersion = supportedVersions.right(); } + @Override + public String toString() { + return "Overlay{" + + "minVersion=" + minVersion + + ", maxVersion=" + maxVersion + + ", directory='" + directory + '\'' + + '}'; + } + + public JsonObject getAsOverlayEntry() { + JsonObject entry = new JsonObject(); + entry.addProperty("directory", this.directory); + JsonArray minFormat = new JsonArray(); + minFormat.add(new JsonPrimitive(this.minVersion.major())); + minFormat.add(new JsonPrimitive(this.minVersion.minor())); + entry.add("min_format", minFormat); + JsonArray maxFormat = new JsonArray(); + maxFormat.add(new JsonPrimitive(this.maxVersion.major())); + maxFormat.add(new JsonPrimitive(this.maxVersion.minor())); + entry.add("max_format", maxFormat); + JsonArray formats = new JsonArray(); + formats.add(new JsonPrimitive(this.minVersion.major())); + formats.add(new JsonPrimitive(this.maxVersion.major())); + entry.add("formats", formats); + return entry; + } + public PackVersion minVersion() { return minVersion; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/PackVersion.java b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/PackVersion.java index 609823392..076b6e5cf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/PackVersion.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/PackVersion.java @@ -8,6 +8,10 @@ public record PackVersion(int major, int minor) implements Comparable versionBasedEvents; + private final List currentOverlays; + private int cursor; + private int version; + + public OverlayCombination(List overlays, int minVersion, int maxVersion) { + this.versionBasedEvents = new ArrayList<>(); + this.currentOverlays = new ArrayList<>(); + this.version = minVersion; + this.cursor = 0; + + Map> eventsByVersion = new TreeMap<>(); + eventsByVersion.computeIfAbsent(minVersion, k -> new ArrayList<>()); + eventsByVersion.computeIfAbsent(maxVersion + 1, k -> new ArrayList<>()); + for (Overlay overlay : overlays) { + if (overlay.minVersion().major() > maxVersion) { + continue; + } + if (overlay.maxVersion().major() < minVersion) { + continue; + } + + // 取最小中的较大值 + int join = Math.max(overlay.minVersion().major(), minVersion); + // 去最大中的较小值 + int leave = Math.min(overlay.maxVersion().major(), maxVersion); + List joinEvents = eventsByVersion.computeIfAbsent(join, k -> new ArrayList<>()); + joinEvents.add(new Event(overlay, Operation.JOIN)); + List leaveEvents = eventsByVersion.computeIfAbsent(leave + 1, k -> new ArrayList<>()); + leaveEvents.add(new Event(overlay, Operation.LEAVE)); + } + + for (Map.Entry> entry : eventsByVersion.entrySet()) { + this.versionBasedEvents.add(new VersionBasedEvent(entry.getKey(), entry.getValue())); + } + } + + public boolean hasNext() { + return this.cursor < this.versionBasedEvents.size(); + } + + @Nullable + public OverlayCombination.Segment nextSegment() { + Segment next = next(); + if (next == null) { + return null; + } + // 第一次100%有问题 + if (next.min > next.max) { + return next(); + } + return next; + } + + @Nullable + private OverlayCombination.Segment next() { + // 已经没有事件里 + if (this.cursor >= this.versionBasedEvents.size()) { + return null; + } + // 获取事件 + VersionBasedEvent events = this.versionBasedEvents.get(this.cursor); + // 将上一个版本和上次记录的版本打为一个overlay返回 + Segment segment = new Segment(this.version, events.version - 1, Set.copyOf(this.currentOverlays)); + this.version = events.version; + // 变更当前成员 + for (Event event : events.events) { + if (event.operation() == Operation.LEAVE) { + this.currentOverlays.remove(event.overlay); + } else if (event.operation() == Operation.JOIN) { + this.currentOverlays.add(event.overlay); + } + } + this.cursor++; + return segment; + } + + public record Segment(int min, int max, Set overlays) { + + @Override + public @NotNull String toString() { + return "OverlaySegment{" + + "min=" + min + + ", max=" + max + + ", overlays=" + overlays + + '}'; + } + } + + record Event(Overlay overlay, Operation operation) { + } + + record VersionBasedEvent(int version, List events) { + } + + enum Operation { + JOIN, LEAVE + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ModernItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ModernItemModel.java index a094e4be2..3e1512dea 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ModernItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ModernItemModel.java @@ -4,7 +4,6 @@ import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.MinecraftVersion; -import net.momirealms.craftengine.core.util.MinecraftVersions; import java.util.List; @@ -33,13 +32,13 @@ public class ModernItemModel { public JsonObject toJson(MinecraftVersion version) { JsonObject json = new JsonObject(); - if (this.oversizedInGui && version.isAtOrAbove(MinecraftVersions.V1_21_6)) { + if (this.oversizedInGui && version.isAtOrAbove(MinecraftVersion.V1_21_6)) { json.addProperty("oversized_in_gui", true); } if (!this.handAnimationOnSwap) { json.addProperty("hand_animation_on_swap", false); } - if (this.swapAnimationScale != 1.0f && version.isAtOrAbove(MinecraftVersions.V1_21_11)) { + if (this.swapAnimationScale != 1.0f && version.isAtOrAbove(MinecraftVersion.V1_21_11)) { json.addProperty("swap_animation_scale", this.swapAnimationScale); } json.add("model", this.itemModel.apply(version)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/DisplayContextSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/DisplayContextSelectProperty.java index 49299c8f8..8b2e953a7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/DisplayContextSelectProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/DisplayContextSelectProperty.java @@ -7,7 +7,6 @@ import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.pack.revision.Revisions; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MinecraftVersion; -import net.momirealms.craftengine.core.util.MinecraftVersions; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -38,7 +37,7 @@ public class DisplayContextSelectProperty implements SelectProperty { @Override public @Nullable JsonElement remap(JsonElement element, MinecraftVersion version) { - if (version.isBelow(MinecraftVersions.V1_21_9) && element instanceof JsonPrimitive primitive && primitive.isString()) { + if (version.isBelow(MinecraftVersion.V1_21_9) && element instanceof JsonPrimitive primitive && primitive.isString()) { if (primitive.getAsString().equals("on_shelf")) { return null; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/PlayerHeadSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/PlayerHeadSpecialModel.java index a106a009a..a36998761 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/PlayerHeadSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/PlayerHeadSpecialModel.java @@ -5,7 +5,6 @@ import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.pack.revision.Revisions; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MinecraftVersion; -import net.momirealms.craftengine.core.util.MinecraftVersions; import java.util.List; import java.util.Map; @@ -31,7 +30,7 @@ public class PlayerHeadSpecialModel implements SpecialModel { @Override public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); - if (version.isAtOrAbove(MinecraftVersions.V1_21_6)) { + if (version.isAtOrAbove(MinecraftVersion.V1_21_6)) { json.addProperty("type", type().toString()); } else { json.addProperty("type", SpecialModels.HEAD.toString()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java b/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java index 949174f6f..adcc1e885 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.pack.revision; import net.momirealms.craftengine.core.util.MinecraftVersion; -import net.momirealms.craftengine.core.util.MinecraftVersions; import java.util.Objects; @@ -37,7 +36,7 @@ public interface Revision { @Override public MinecraftVersion maxVersion() { - return MinecraftVersions.FUTURE; + return MinecraftVersion.FUTURE; } @Override @@ -61,7 +60,7 @@ public interface Revision { @Override public int maxPackVersion() { // todo 重构revision系统 - return MinecraftVersions.FUTURE.packFormat().major(); + return MinecraftVersion.FUTURE.packFormat().major(); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revisions.java b/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revisions.java index d14f3090f..4c08ff8eb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revisions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revisions.java @@ -1,11 +1,11 @@ package net.momirealms.craftengine.core.pack.revision; -import net.momirealms.craftengine.core.util.MinecraftVersions; +import net.momirealms.craftengine.core.util.MinecraftVersion; public final class Revisions { private Revisions() {} - public static final Revision SINCE_1_21_6 = Revision.since(MinecraftVersions.V1_21_6); - public static final Revision SINCE_1_21_2 = Revision.since(MinecraftVersions.V1_21_2); - public static final Revision SINCE_1_21_9 = Revision.since(MinecraftVersions.V1_21_9); + public static final Revision SINCE_1_21_6 = Revision.since(MinecraftVersion.V1_21_6); + public static final Revision SINCE_1_21_2 = Revision.since(MinecraftVersion.V1_21_2); + public static final Revision SINCE_1_21_9 = Revision.since(MinecraftVersion.V1_21_9); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index 298a48d7d..3182d6ecf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -611,12 +611,12 @@ public class Config { private static MinecraftVersion getVersion(String version) { if (version.equalsIgnoreCase("latest")) { - return new MinecraftVersion(PluginProperties.getValue("latest-version")); + return MinecraftVersion.byName(PluginProperties.getValue("latest-version")); } if (version.equalsIgnoreCase("server")) { return VersionHelper.MINECRAFT_VERSION; } - return MinecraftVersion.parse(version); + return MinecraftVersion.byName(version); } public static Locale forcedLocale() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java b/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java index 34c00fa46..aa35ddc78 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java @@ -1,13 +1,25 @@ package net.momirealms.craftengine.core.util; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; import net.momirealms.craftengine.core.pack.mcmeta.PackVersion; -import java.util.HashMap; -import java.util.Map; +import java.util.*; + +public class MinecraftVersion implements Comparable { + private static final Map PACK_FORMATS = new HashMap<>(); -public final class MinecraftVersion implements Comparable { - public static final Map PACK_FORMATS = new HashMap<>(); static { + PACK_FORMATS.put(1_17_00, new PackVersion(7, 0)); + PACK_FORMATS.put(1_17_01, new PackVersion(7, 0)); + PACK_FORMATS.put(1_18_00, new PackVersion(8, 0)); + PACK_FORMATS.put(1_18_01, new PackVersion(8, 0)); + PACK_FORMATS.put(1_18_02, new PackVersion(8, 0)); + PACK_FORMATS.put(1_19_00, new PackVersion(9, 0)); + PACK_FORMATS.put(1_19_01, new PackVersion(9, 0)); + PACK_FORMATS.put(1_19_02, new PackVersion(9, 0)); + PACK_FORMATS.put(1_19_03, new PackVersion(12, 0)); + PACK_FORMATS.put(1_19_04, new PackVersion(13, 0)); PACK_FORMATS.put(1_20_00, new PackVersion(15, 0)); PACK_FORMATS.put(1_20_01, new PackVersion(15, 0)); PACK_FORMATS.put(1_20_02, new PackVersion(18, 0)); @@ -27,15 +39,62 @@ public final class MinecraftVersion implements Comparable { PACK_FORMATS.put(1_21_09, new PackVersion(69, 0)); PACK_FORMATS.put(1_21_10, new PackVersion(69, 0)); PACK_FORMATS.put(1_21_11, new PackVersion(75, 0)); - PACK_FORMATS.put(99_99_99, new PackVersion(1000, 0)); + PACK_FORMATS.put(1_26_01, new PackVersion(76, 0)); + PACK_FORMATS.put(1_99_99, new PackVersion(1000, 0)); } + private static final Map BY_NAME = new LinkedHashMap<>(); + private static final Multimap BY_PACK_FORMAT = ArrayListMultimap.create(); + public static final MinecraftVersion V1_17 = new MinecraftVersion("1.17"); + public static final MinecraftVersion V1_17_1 = new MinecraftVersion("1.17.1"); + public static final MinecraftVersion V1_18 = new MinecraftVersion("1.18"); + public static final MinecraftVersion V1_18_1 = new MinecraftVersion("1.18.1"); + public static final MinecraftVersion V1_18_2 = new MinecraftVersion("1.18.2"); + public static final MinecraftVersion V1_19 = new MinecraftVersion("1.19"); + public static final MinecraftVersion V1_19_1 = new MinecraftVersion("1.19.1"); + public static final MinecraftVersion V1_19_2 = new MinecraftVersion("1.19.2"); + public static final MinecraftVersion V1_19_3 = new MinecraftVersion("1.19.3"); + public static final MinecraftVersion V1_19_4 = new MinecraftVersion("1.19.4"); + public static final MinecraftVersion V1_20 = new MinecraftVersion("1.20"); + public static final MinecraftVersion V1_20_1 = new MinecraftVersion("1.20.1"); + public static final MinecraftVersion V1_20_2 = new MinecraftVersion("1.20.2"); + public static final MinecraftVersion V1_20_3 = new MinecraftVersion("1.20.3"); + public static final MinecraftVersion V1_20_4 = new MinecraftVersion("1.20.4"); + public static final MinecraftVersion V1_20_5 = new MinecraftVersion("1.20.5"); + public static final MinecraftVersion V1_20_6 = new MinecraftVersion("1.20.6"); + public static final MinecraftVersion V1_21 = new MinecraftVersion("1.21"); + public static final MinecraftVersion V1_21_1 = new MinecraftVersion("1.21.1"); + public static final MinecraftVersion V1_21_2 = new MinecraftVersion("1.21.2"); + public static final MinecraftVersion V1_21_3 = new MinecraftVersion("1.21.3"); + public static final MinecraftVersion V1_21_4 = new MinecraftVersion("1.21.4"); + public static final MinecraftVersion V1_21_5 = new MinecraftVersion("1.21.5"); + public static final MinecraftVersion V1_21_6 = new MinecraftVersion("1.21.6"); + public static final MinecraftVersion V1_21_7 = new MinecraftVersion("1.21.7"); + public static final MinecraftVersion V1_21_8 = new MinecraftVersion("1.21.8"); + public static final MinecraftVersion V1_21_9 = new MinecraftVersion("1.21.9"); + public static final MinecraftVersion V1_21_10 = new MinecraftVersion("1.21.10"); + public static final MinecraftVersion V1_21_11 = new MinecraftVersion("1.21.11"); + public static final MinecraftVersion V26_1 = new MinecraftVersion("26.1"); + public static final MinecraftVersion FUTURE = new MinecraftVersion("99.99"); + private final int version; private final String versionString; private final PackVersion packFormat; - public static MinecraftVersion parse(final String version) { - return new MinecraftVersion(version); + public static MinecraftVersion byName(final String version) { + MinecraftVersion mcV = BY_NAME.get(version); + if (mcV == null) { + throw new IllegalArgumentException("Unsupported version: " + version); + } + return mcV; + } + + public static List byMajorPackFormat(final int packFormat) { + List minecraftVersions = (List) BY_PACK_FORMAT.get(packFormat); + if (minecraftVersions.isEmpty()) { + throw new IllegalArgumentException("Unsupported pack format: " + packFormat); + } + return minecraftVersions; } public String version() { @@ -46,10 +105,12 @@ public final class MinecraftVersion implements Comparable { return packFormat; } - public MinecraftVersion(String version) { + private MinecraftVersion(String version) { this.version = VersionHelper.parseVersionToInteger(version); this.versionString = version; - this.packFormat = PACK_FORMATS.get(this.version); + this.packFormat = Objects.requireNonNull(PACK_FORMATS.get(this.version)); + BY_NAME.put(this.versionString, this); + BY_PACK_FORMAT.put(this.packFormat.major(), this); } public boolean isAtOrAbove(MinecraftVersion other) { @@ -60,7 +121,7 @@ public final class MinecraftVersion implements Comparable { return version <= other.version; } - public boolean isAt(MinecraftVersion other) { + public boolean is(MinecraftVersion other) { return version == other.version; } @@ -80,7 +141,7 @@ public final class MinecraftVersion implements Comparable { @Override public int hashCode() { - return version; + return this.version; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java b/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java deleted file mode 100644 index 0c51a9af1..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.momirealms.craftengine.core.util; - -public final class MinecraftVersions { - private MinecraftVersions() {} - - public static final MinecraftVersion V1_20 = new MinecraftVersion("1.20"); - public static final MinecraftVersion V1_20_1 = new MinecraftVersion("1.20.1"); - public static final MinecraftVersion V1_20_2 = new MinecraftVersion("1.20.2"); - public static final MinecraftVersion V1_20_3 = new MinecraftVersion("1.20.3"); - public static final MinecraftVersion V1_20_4 = new MinecraftVersion("1.20.4"); - public static final MinecraftVersion V1_20_5 = new MinecraftVersion("1.20.5"); - public static final MinecraftVersion V1_20_6 = new MinecraftVersion("1.20.6"); - public static final MinecraftVersion V1_21 = new MinecraftVersion("1.21"); - public static final MinecraftVersion V1_21_1 = new MinecraftVersion("1.21.1"); - public static final MinecraftVersion V1_21_2 = new MinecraftVersion("1.21.2"); - public static final MinecraftVersion V1_21_3 = new MinecraftVersion("1.21.3"); - public static final MinecraftVersion V1_21_4 = new MinecraftVersion("1.21.4"); - public static final MinecraftVersion V1_21_5 = new MinecraftVersion("1.21.5"); - public static final MinecraftVersion V1_21_6 = new MinecraftVersion("1.21.6"); - public static final MinecraftVersion V1_21_7 = new MinecraftVersion("1.21.7"); - public static final MinecraftVersion V1_21_8 = new MinecraftVersion("1.21.8"); - public static final MinecraftVersion V1_21_9 = new MinecraftVersion("1.21.9"); - public static final MinecraftVersion V1_21_10 = new MinecraftVersion("1.21.10"); - public static final MinecraftVersion V1_21_11 = new MinecraftVersion("1.21.11"); - public static final MinecraftVersion FUTURE = new MinecraftVersion("1.99.99"); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java index 18c1d97da..c37a5eea2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java @@ -38,6 +38,7 @@ public class VersionHelper { private static final boolean v1_21_9; private static final boolean v1_21_10; private static final boolean v1_21_11; + private static final boolean v26_1; private static final Class UNOBFUSCATED_CLAZZ = Objects.requireNonNull(ReflectionUtils.getClazz( "net.minecraft.obfuscate.DontObfuscate", // 因为无混淆版本没有这个类所以说多写几个防止找不到了 "net.minecraft.data.Main", @@ -57,7 +58,7 @@ public class VersionHelper { .split("-", 2)[0] // 1.21.10-rc1 -> 1.21.10 .split("_", 2)[0]; // 1.21.11_unobfuscated -> 1.21.11 - MINECRAFT_VERSION = new MinecraftVersion(versionString); + MINECRAFT_VERSION = MinecraftVersion.byName(versionString); String[] split = versionString.split("\\."); int major = Integer.parseInt(split[1]); @@ -86,6 +87,7 @@ public class VersionHelper { v1_21_9 = version >= 12109; v1_21_10 = version >= 12110; v1_21_11 = version >= 12111; + v26_1 = version >= 12601; majorVersion = major; minorVersion = minor; @@ -102,8 +104,9 @@ public class VersionHelper { } public static int parseVersionToInteger(String versionString) { - int major = 0; - int minor = 0; + int v1 = 0; + int v2 = 0; + int v3 = 0; int currentNumber = 0; int part = 0; for (int i = 0; i < versionString.length(); i++) { @@ -111,8 +114,11 @@ public class VersionHelper { if (c >= '0' && c <= '9') { currentNumber = currentNumber * 10 + (c - '0'); } else if (c == '.') { + if (part == 0) { + v1 = currentNumber; + } if (part == 1) { - major = currentNumber; + v2 = currentNumber; } part++; currentNumber = 0; @@ -121,12 +127,23 @@ public class VersionHelper { } } } - if (part == 1) { - major = currentNumber; - } else if (part == 2) { - minor = currentNumber; + // 处理最后一个数字部分 + if (part == 0) { // 没有点号:如 "26" + v1 = currentNumber; + } else if (part == 1) { // 一个点号:如 "26.1" + v2 = currentNumber; + } else if (part == 2) { // 两个点号:如 "1.2.3" + v3 = currentNumber; + } + + // 新版命名法 + if (v1 >= 26) { + return 10000 + v1 * 100 + v2; + } + // 旧版命名法 + else { + return 10000 + v2 * 100 + v3; } - return 10000 + major * 100 + minor; } public static int majorVersion() { @@ -269,4 +286,8 @@ public class VersionHelper { public static boolean isOrAbove1_21_11() { return v1_21_11; } + + public static boolean isOrAbove26_1() { + return v26_1; + } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index baa9346aa..0c853f64d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx4G # Project settings project_version=0.0.66.5 config_version=63 -lang_version=45 +lang_version=46 project_group=net.momirealms latest_supported_version=1.21.11 @@ -38,7 +38,7 @@ zstd_version=1.5.7-6 commons_io_version=2.21.0 commons_lang3_version=3.20.0 sparrow_nbt_version=0.10.9 -sparrow_util_version=0.75 +sparrow_util_version=0.76 fastutil_version=8.5.18 netty_version=4.1.128.Final joml_version=1.10.8