From d20ef9f4fc6762278a6a100b6c5d088e8df7ef0a Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 17 Dec 2025 17:10:08 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0invisible=E9=80=89?= =?UTF-8?q?=E9=A1=B9=E6=8E=A7=E5=88=B6=E6=BD=9C=E5=BD=B1=E8=B4=9D=E7=A2=B0?= =?UTF-8?q?=E6=92=9E=E7=AE=B1=E6=98=AF=E5=90=A6=E5=8F=AF=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../furniture/hitbox/ShulkerFurnitureHitboxConfig.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index 6756948bb..bb6d2c067 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -50,6 +50,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< byte peek, boolean interactive, boolean interactionEntity, + boolean invisible, Direction direction) { super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); this.scale = scale; @@ -66,7 +67,9 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // Invisible List cachedInteractionValues = new ArrayList<>(); - InteractionEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedInteractionValues); + if (invisible) { + InteractionEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedInteractionValues); + } float shulkerHeight = (getPhysicalPeek(peek * 0.01F) + 1) * scale; if (direction == Direction.UP) { InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues); @@ -309,11 +312,12 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on"); boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile"); boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); + boolean invisible = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("invisible", false), "invisible"); return new ShulkerFurnitureHitboxConfig( SeatConfig.fromObj(arguments.get("seats")), position, canUseItemOn, blocksBuilding, canBeHitByProjectile, - scale, peek, interactive, interactionEntity, directionEnum + scale, peek, interactive, interactionEntity, invisible, directionEnum ); } } From bc16e248e7c6ef90cf71ca35ec26111da941edc9 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 17 Dec 2025 17:21:20 +0800 Subject: [PATCH 02/13] =?UTF-8?q?=E6=9B=B4=E6=99=BA=E8=83=BD=E7=9A=84atlas?= =?UTF-8?q?=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resolution/ResolutionMergePackMcMeta.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java index 4d0ac2180..959d233ce 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java @@ -58,13 +58,23 @@ public class ResolutionMergePackMcMeta implements Resolution { collectOverlays(mcmeta1.getAsJsonObject("overlays"), overlays::add); collectOverlays(mcmeta2.getAsJsonObject("overlays"), overlays::add); if (!overlays.isEmpty()) { - JsonObject mergedOverlay = new JsonObject(); - JsonArray entries = new JsonArray(); - for (JsonObject entry : overlays) { - entries.add(entry); + Map overlayMap = new LinkedHashMap<>(); + for (JsonObject overlay : overlays) { + JsonPrimitive directory = overlay.getAsJsonPrimitive("directory"); + if (directory != null) { + // 名字相同的大概率内部版本也一致,不进一步处理了 + overlayMap.put(directory.getAsString(), overlay); + } + } + if (!overlayMap.isEmpty()) { + JsonObject mergedOverlay = new JsonObject(); + JsonArray entries = new JsonArray(); + for (JsonObject entry : overlayMap.values()) { + entries.add(entry); + } + mergedOverlay.add("entries", entries); + merged.add("overlays", mergedOverlay); } - mergedOverlay.add("entries", entries); - merged.add("overlays", mergedOverlay); } // 第四步,合并filter List filters = new ArrayList<>(); From f2042dc9c8b219870093dc4c3aa557c18c5f761e Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 17 Dec 2025 19:26:42 +0800 Subject: [PATCH 03/13] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E9=80=BB=E8=BE=911?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/config.yml | 10 +- .../core/pack/AbstractPackManager.java | 83 ++++++++----- .../resolution/ResolutionMergePackMcMeta.java | 110 ++++-------------- .../craftengine/core/pack/mcmeta/Overlay.java | 106 +++++++++++++++++ .../core/pack/mcmeta/PackMcMeta.java | 36 ++++++ .../core/pack/mcmeta/PackVersion.java | 87 ++++++++++++++ .../pack/overlay/ResourcePackOverlay.java | 32 ----- .../core/pack/revision/Revision.java | 10 +- .../core/plugin/config/Config.java | 6 + .../craftengine/core/util/FileUtils.java | 15 ++- .../craftengine/core/util/ListUtils.java | 11 ++ .../core/util/MinecraftVersion.java | 48 ++++---- gradle.properties | 6 +- 13 files changed, 383 insertions(+), 177 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/Overlay.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/PackMcMeta.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/PackVersion.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/overlay/ResourcePackOverlay.java diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 34d59ee8b..2d2a2967c 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -98,9 +98,17 @@ resource-pack: # 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. - # When a model file mixes textures from both the blocks atlas and the items atlas, you must manually fix the issue. + # The fix-atlas feature is not all-powerful since 1.21.11. In some cases, CraftEngine cannot fix it for you, and you will need to fix your model yourself. fix-atlas: true # Optimize your resource pack by reducing its size without any quality loss. optimization: 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 e45b16ae8..01d973b46 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 @@ -18,6 +18,8 @@ import net.momirealms.craftengine.core.pack.conflict.resolution.ResolutionCondit import net.momirealms.craftengine.core.pack.host.ResourcePackHost; 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.model.ItemModel; import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel; import net.momirealms.craftengine.core.pack.model.ModernItemModel; @@ -791,7 +793,9 @@ public abstract class AbstractPackManager implements PackManager { long time2 = System.currentTimeMillis(); this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.generate", String.valueOf(time2 - time1))); if (Config.validateResourcePack()) { - this.validateResourcePack(generatedPackPath); + for (MinecraftVersion version : Config.validationTestVersions()) { + this.validateResourcePack(generatedPackPath, version); + } } long time3 = System.currentTimeMillis(); this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.validate", String.valueOf(time3 - time2))); @@ -825,10 +829,10 @@ public abstract class AbstractPackManager implements PackManager { if (!rawMeta.has("pack")) { JsonObject pack = new JsonObject(); rawMeta.add("pack", pack); - pack.addProperty("pack_format", Config.packMinVersion().packFormat()); + pack.addProperty("pack_format", Config.packMinVersion().packFormat().major()); JsonObject supportedFormats = new JsonObject(); - supportedFormats.addProperty("min_inclusive", Config.packMinVersion().packFormat()); - supportedFormats.addProperty("max_inclusive", Config.packMaxVersion().packFormat()); + supportedFormats.addProperty("min_inclusive", Config.packMinVersion().packFormat().major()); + supportedFormats.addProperty("max_inclusive", Config.packMaxVersion().packFormat().major()); pack.add("supported_formats", supportedFormats); changed = true; } @@ -869,7 +873,7 @@ public abstract class AbstractPackManager implements PackManager { private void removeAllShaders(Path path) { List rootPaths; try { - rootPaths = FileUtils.collectOverlays(path); + rootPaths = MiscUtils.init(FileUtils.collectOverlays(path), a -> a.addFirst(path)); } catch (IOException e) { plugin.logger().warn("Failed to collect overlays for " + path.toAbsolutePath(), e); return; @@ -889,7 +893,7 @@ public abstract class AbstractPackManager implements PackManager { // 收集全部overlay Path[] rootPaths; try { - rootPaths = FileUtils.collectOverlays(path).toArray(new Path[0]); + rootPaths = MiscUtils.init(FileUtils.collectOverlays(path), a -> a.addFirst(path)).toArray(new Path[0]); } catch (IOException e) { this.plugin.logger().warn("Failed to collect overlays for " + path.toAbsolutePath(), e); return; @@ -1174,16 +1178,37 @@ public abstract class AbstractPackManager implements PackManager { } @SuppressWarnings("DuplicatedCode") - private void validateResourcePack(Path path) { - // 收集全部overlay - Path[] rootPaths; + private void validateResourcePack(Path path, MinecraftVersion version) { + Path packMcMetaPath = path.resolve("pack.mcmeta"); + PackMcMeta packMeta; try { - rootPaths = FileUtils.collectOverlays(path).toArray(new Path[0]); + packMeta = new PackMcMeta(GsonHelper.readJsonFile(packMcMetaPath).getAsJsonObject()); } catch (IOException e) { - this.plugin.logger().warn("Failed to collect overlays for " + path.toAbsolutePath(), e); + this.plugin.logger().warn("Failed to read pack.mcmeta " + packMcMetaPath.toAbsolutePath(), e); return; } + // 获取当前版本生效的overlays + List overlayDirectories = new ArrayList<>(); + for (Overlay overlay : packMeta.overlays()) { + if (overlay.test(version)) { + overlayDirectories.add(overlay.directory()); + } + } + 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); + } + } + + // 收集全部overlay,按照正确的顺序加载 + Path[] rootPaths = rootPathList.toArray(new Path[0]); + Multimap glyphToFonts = HashMultimap.create(128, 32); // 图片到字体的映射 Multimap modelToItemDefinitions = HashMultimap.create(128, 4); // 模型到物品的映射 Multimap modelToBlockStates = HashMultimap.create(128, 32); // 模型到方块的映射 @@ -1191,8 +1216,11 @@ public abstract class AbstractPackManager implements PackManager { Multimap textureToEquipments = HashMultimap.create(128, 8); // 纹理到盔甲的映射 Multimap oggToSoundEvents = HashMultimap.create(128, 4); // 音频到声音的映射 - Map blockAtlasJsons = new LinkedHashMap<>(); - Map itemAtlasJsons = new LinkedHashMap<>(); + Map blockAtlasJsons = new HashMap<>(); + Map itemAtlasJsons = new HashMap<>(); + + JsonObject lastBlocksAtlas = null; + JsonObject lastItemAtlas = null; // 如果需要验证资源包,则需要先读取所有atlas for (Path rootPath : rootPaths) { @@ -1205,6 +1233,7 @@ public abstract class AbstractPackManager implements PackManager { try { JsonObject atlasJsonObject = GsonHelper.readJsonFile(blockAtlasFile).getAsJsonObject(); blockAtlasJsons.put(blockAtlasFile, atlasJsonObject); + lastBlocksAtlas = atlasJsonObject; } catch (IOException | JsonParseException e) { TranslationManager.instance().log("warning.config.resource_pack.generation.malformatted_json", blockAtlasFile.toAbsolutePath().toString()); } @@ -1218,6 +1247,7 @@ public abstract class AbstractPackManager implements PackManager { 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()); } @@ -1242,16 +1272,15 @@ public abstract class AbstractPackManager implements PackManager { */ - Atlas blockAtlas = new Atlas(MiscUtils.make(new ArrayList<>(4), k -> { - k.add(blockAtlasJsons.get(defaultBlockAtlas)); - k.add(this.vanillaBlockAtlas); - return k; - })); - Atlas itemAtlas = new Atlas(MiscUtils.make(new ArrayList<>(4), k -> { - k.add(itemAtlasJsons.get(defaultItemAtlas)); - k.add(this.vanillaItemAtlas); - return k; - })); + Atlas blockAtlas; + Atlas itemAtlas; + if (version.isAtOrAbove(MinecraftVersions.V1_21_11)) { + blockAtlas = new Atlas(ListUtils.newNonNullList(lastBlocksAtlas, this.vanillaBlockAtlas)); + itemAtlas = new Atlas(ListUtils.newNonNullList(lastItemAtlas, this.vanillaItemAtlas)); + } else { + blockAtlas = new Atlas(ListUtils.newNonNullList(lastBlocksAtlas, this.vanillaBlockAtlas, this.vanillaItemAtlas)); + itemAtlas = null; + } for (Path rootPath : rootPaths) { Path assetsPath = rootPath.resolve("assets"); @@ -1564,7 +1593,7 @@ public abstract class AbstractPackManager implements PackManager { Key spritePath = texture.getValue(); // 方块纹理不应该在item图集内,这样必然出问题 - boolean definedInItemAtlas = itemAtlas.isDefined(spritePath); + boolean definedInItemAtlas = itemAtlas != null && itemAtlas.isDefined(spritePath); if (definedInItemAtlas) { TranslationManager.instance().log("warning.config.resource_pack.generation.multiple_atlases", entry.getKey().asString(), "minecraft:textures/atlas/blocks.png", @@ -1614,7 +1643,7 @@ public abstract class AbstractPackManager implements PackManager { for (Map.Entry texture : textures.entrySet()) { Key spritePath = texture.getValue(); boolean definedInBlockAtlas = blockAtlas.isDefined(spritePath); - boolean definedInItemAtlas = itemAtlas.isDefined(spritePath); + boolean definedInItemAtlas = itemAtlas != null && itemAtlas.isDefined(spritePath); // 双倍定义 if (definedInItemAtlas && definedInBlockAtlas) { @@ -1735,7 +1764,7 @@ public abstract class AbstractPackManager implements PackManager { itemAtlasesToFix.clear(); } - if (!itemAtlasesToFix.isEmpty()) { + if (!itemAtlasesToFix.isEmpty() && itemAtlas != null) { List sourcesToAdd = new ArrayList<>(itemAtlasesToFix.size()); for (Key itemTexture : itemAtlasesToFix.keySet()) { itemAtlas.addSingle(itemTexture); @@ -1809,7 +1838,7 @@ public abstract class AbstractPackManager implements PackManager { Map textures = entry.getValue().textures; for (Map.Entry texture : textures.entrySet()) { Key spritePath = texture.getValue(); - Key sourceTexturePath = itemAtlas.getSourceTexturePath(spritePath); + Key sourceTexturePath = itemAtlas == null ? null : itemAtlas.getSourceTexturePath(spritePath); if (sourceTexturePath != null) { textureToModels.put(sourceTexturePath, entry.getKey()); } else { diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java index 959d233ce..c524ecebe 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java @@ -6,23 +6,25 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import net.momirealms.craftengine.core.pack.conflict.PathContext; +import net.momirealms.craftengine.core.pack.mcmeta.PackVersion; import net.momirealms.craftengine.core.plugin.CraftEngine; 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.Pair; -import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.nio.file.Path; import java.util.*; import java.util.function.Consumer; +import static net.momirealms.craftengine.core.pack.mcmeta.PackVersion.MAX_PACK_VERSION; +import static net.momirealms.craftengine.core.pack.mcmeta.PackVersion.MIN_PACK_VERSION; + public class ResolutionMergePackMcMeta implements Resolution { public static final Factory FACTORY = new Factory(); public static final Set STANDARD_PACK_KEYS = ImmutableSet.of("pack", "features", "filter", "overlays", "language"); - public static final PackVersion MIN_PACK_VERSION = new PackVersion(15, 0); // 1.20 - public static final PackVersion MAX_PACK_VERSION = new PackVersion(1000, 0); // future + private final String description; public ResolutionMergePackMcMeta(String description) { @@ -164,87 +166,23 @@ public class ResolutionMergePackMcMeta implements Resolution { PackVersion max = supportedVersions.right(); // 旧版格式支持 JsonObject supportedFormats = new JsonObject(); - supportedFormats.addProperty("min_inclusive", min.major); - supportedFormats.addProperty("max_inclusive", max.major); + supportedFormats.addProperty("min_inclusive", min.major()); + supportedFormats.addProperty("max_inclusive", max.major()); entryJson.add("formats", supportedFormats); // 新版格式支持 JsonArray minFormat = new JsonArray(); - minFormat.add(min.major); - minFormat.add(min.minor); + minFormat.add(min.major()); + minFormat.add(min.minor()); entryJson.add("min_format", minFormat); JsonArray maxFormat = new JsonArray(); - maxFormat.add(max.major); - maxFormat.add(max.minor); + maxFormat.add(max.major()); + maxFormat.add(max.minor()); entryJson.add("max_format", maxFormat); overlayCollector.accept(entryJson); } } } - public record PackVersion(int major, int minor) implements Comparable { - - @Override - public int compareTo(@NotNull ResolutionMergePackMcMeta.PackVersion o) { - // 首先比较 major 版本 - int majorCompare = Integer.compare(this.major, o.major); - if (majorCompare != 0) { - return majorCompare; - } - // 如果 major 相同,则比较 minor 版本 - return Integer.compare(this.minor, o.minor); - } - - /** - * 返回两个版本中较小的那个(版本较低的) - */ - public static PackVersion getLower(PackVersion v1, PackVersion v2) { - if (v1 == null) return v2; - if (v2 == null) return v1; - return v1.compareTo(v2) <= 0 ? v1 : v2; - } - - /** - * 返回两个版本中较大的那个(版本较高的) - */ - public static PackVersion getHigher(PackVersion v1, PackVersion v2) { - if (v1 == null) return v2; - if (v2 == null) return v1; - return v1.compareTo(v2) >= 0 ? v1 : v2; - } - - public static PackVersion getLowest(List versions) { - if (versions == null || versions.isEmpty()) { - return MIN_PACK_VERSION; - } - - PackVersion lowest = versions.getFirst(); - for (int i = 1; i < versions.size(); i++) { - lowest = getLower(lowest, versions.get(i)); - } - return lowest; - } - - public static PackVersion getHighest(List versions) { - if (versions == null || versions.isEmpty()) { - return MAX_PACK_VERSION; - } - - PackVersion highest = versions.getFirst(); - for (int i = 1; i < versions.size(); i++) { - highest = getHigher(highest, versions.get(i)); - } - return highest; - } - - public static PackVersion parse(float num) { - String str = String.valueOf(num); - String[] parts = str.split("\\."); - int integerPart = Integer.parseInt(parts[0]); - int decimalPart = parts.length > 1 ? Integer.parseInt(parts[1]) : 0; - return new PackVersion(integerPart, decimalPart); - } - } - public static void mergePack(JsonObject merged, JsonObject pack1, JsonObject pack2) { Pair pack1Version = getSupportedVersions(pack1); Pair pack2Version = getSupportedVersions(pack2); @@ -252,18 +190,18 @@ public class ResolutionMergePackMcMeta implements Resolution { PackVersion max = PackVersion.getHigher(pack1Version.right(), pack2Version.right()); // 旧版格式支持 JsonObject supportedFormats = new JsonObject(); - supportedFormats.addProperty("min_inclusive", min.major); - supportedFormats.addProperty("max_inclusive", max.major); + supportedFormats.addProperty("min_inclusive", min.major()); + supportedFormats.addProperty("max_inclusive", max.major()); merged.add("supported_formats", supportedFormats); - merged.addProperty("pack_format", min.major); + merged.addProperty("pack_format", min.major()); // 新版格式支持 JsonArray minFormat = new JsonArray(); - minFormat.add(min.major); - minFormat.add(min.minor); + minFormat.add(min.major()); + minFormat.add(min.minor()); merged.add("min_format", minFormat); JsonArray maxFormat = new JsonArray(); - maxFormat.add(max.major); - maxFormat.add(max.minor); + maxFormat.add(max.major()); + maxFormat.add(max.minor()); merged.add("max_format", maxFormat); } @@ -307,15 +245,15 @@ public class ResolutionMergePackMcMeta implements Resolution { case JsonArray array -> { if (array.isEmpty()) return Pair.of(MIN_PACK_VERSION, MAX_PACK_VERSION); if (array.size() == 1) { - return new Pair<>(new PackVersion(GsonHelper.getAsInt(array.get(0), MIN_PACK_VERSION.major), 0), MAX_PACK_VERSION); + return new Pair<>(new PackVersion(GsonHelper.getAsInt(array.get(0), MIN_PACK_VERSION.major()), 0), MAX_PACK_VERSION); } if (array.size() == 2) { - return new Pair<>(new PackVersion(GsonHelper.getAsInt(array.get(0), MIN_PACK_VERSION.major), 0), new PackVersion(GsonHelper.getAsInt(array.get(1), MAX_PACK_VERSION.major), 0)); + return new Pair<>(new PackVersion(GsonHelper.getAsInt(array.get(0), MIN_PACK_VERSION.major()), 0), new PackVersion(GsonHelper.getAsInt(array.get(1), MAX_PACK_VERSION.major()), 0)); } } case JsonObject object -> { - int min = GsonHelper.getAsInt(object.get("min_inclusive"), MIN_PACK_VERSION.major); - int max = GsonHelper.getAsInt(object.get("max_inclusive"), MAX_PACK_VERSION.major); + int min = GsonHelper.getAsInt(object.get("min_inclusive"), MIN_PACK_VERSION.major()); + int max = GsonHelper.getAsInt(object.get("max_inclusive"), MAX_PACK_VERSION.major()); return new Pair<>(new PackVersion(min, 0), new PackVersion(max, 0)); } default -> { @@ -328,10 +266,10 @@ public class ResolutionMergePackMcMeta implements Resolution { if (format instanceof JsonArray array) { if (array.isEmpty()) return defaultVersion; if (array.size() == 1) { - return new PackVersion(GsonHelper.getAsInt(array.get(0), defaultVersion.major), 0); + return new PackVersion(GsonHelper.getAsInt(array.get(0), defaultVersion.major()), 0); } if (array.size() == 2) { - return new PackVersion(GsonHelper.getAsInt(array.get(0), defaultVersion.major), GsonHelper.getAsInt(array.get(1), defaultVersion.minor)); + return new PackVersion(GsonHelper.getAsInt(array.get(0), defaultVersion.major()), GsonHelper.getAsInt(array.get(1), defaultVersion.minor())); } } else if (format instanceof JsonPrimitive jsonPrimitive) { float version = jsonPrimitive.getAsFloat(); 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 new file mode 100644 index 000000000..6b4a9d470 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/Overlay.java @@ -0,0 +1,106 @@ +package net.momirealms.craftengine.core.pack.mcmeta; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.momirealms.craftengine.core.util.GsonHelper; +import net.momirealms.craftengine.core.util.MinecraftVersion; +import net.momirealms.craftengine.core.util.Pair; + +import java.util.ArrayList; +import java.util.List; + +public class Overlay { + private final PackVersion minVersion; + private final PackVersion maxVersion; + private final String directory; + + public Overlay(PackVersion minVersion, + PackVersion maxVersion, + String directory + ) { + this.minVersion = minVersion; + this.maxVersion = maxVersion; + this.directory = directory; + } + + public Overlay(JsonObject overlay) { + this.directory = overlay.get("directory").getAsString(); + Pair supportedVersions = getSupportedVersions(overlay); + this.minVersion = supportedVersions.left(); + this.maxVersion = supportedVersions.right(); + } + + public PackVersion minVersion() { + return minVersion; + } + + public PackVersion maxVersion() { + return maxVersion; + } + + public String directory() { + return directory; + } + + public boolean test(MinecraftVersion version) { + return version.packFormat().isAtOrAbove(this.minVersion) && version.packFormat().isAtOrBelow(this.maxVersion); + } + + private static Pair getSupportedVersions(JsonObject pack) { + List minVersions = new ArrayList<>(); + List maxVersions = new ArrayList<>(); + if (pack.has("min_format")) { + minVersions.add(getFormatVersion(pack.get("min_format"))); + } + if (pack.has("max_format")) { + maxVersions.add(getFormatVersion(pack.get("max_format"))); + } + if (pack.has("formats")) { + Pair supportedFormats = parseSupportedFormats(pack.get("formats")); + minVersions.add(supportedFormats.left()); + maxVersions.add(supportedFormats.right()); + } + return Pair.of( + PackVersion.getLowest(minVersions), + PackVersion.getHighest(maxVersions) + ); + } + + private static PackVersion getFormatVersion(JsonElement format) { + if (format instanceof JsonArray array) { + if (array.size() == 1) { + return new PackVersion(GsonHelper.getAsInt(array.get(0), 15), 0); + } + if (array.size() == 2) { + return new PackVersion(GsonHelper.getAsInt(array.get(0), 15), GsonHelper.getAsInt(array.get(1), 1000)); + } + } else if (format instanceof JsonPrimitive jsonPrimitive) { + float version = jsonPrimitive.getAsFloat(); + return PackVersion.parse(version); + } + throw new IllegalArgumentException("Unknown overlay version format: " + format); + } + + private static Pair parseSupportedFormats(JsonElement formats) { + switch (formats) { + case JsonPrimitive jsonPrimitive -> { + return new Pair<>(new PackVersion(jsonPrimitive.getAsInt(), 0), new PackVersion(jsonPrimitive.getAsInt(), 0)); + } + case JsonArray array -> { + if (array.size() == 2) { + return new Pair<>(new PackVersion(GsonHelper.getAsInt(array.get(0), 15), 0), new PackVersion(GsonHelper.getAsInt(array.get(1), 1000), 0)); + } + } + case JsonObject object -> { + int min = GsonHelper.getAsInt(object.get("min_inclusive"), 15); + int max = GsonHelper.getAsInt(object.get("max_inclusive"), 1000); + return new Pair<>(new PackVersion(min, 0), new PackVersion(max, 0)); + } + default -> { + } + } + throw new IllegalArgumentException("Unsupported overlay version format: " + formats); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/PackMcMeta.java b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/PackMcMeta.java new file mode 100644 index 000000000..506b150fa --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/PackMcMeta.java @@ -0,0 +1,36 @@ +package net.momirealms.craftengine.core.pack.mcmeta; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.util.ArrayList; +import java.util.List; + +public class PackMcMeta { + private final List overlays; + + public PackMcMeta(JsonObject mcmeta) { + this.overlays = getOverlays(mcmeta); + } + + private List getOverlays(JsonObject mcmeta) { + List overlays = new ArrayList<>(); + JsonObject overlaysJson = mcmeta.getAsJsonObject("overlays"); + if (overlaysJson != null) { + JsonArray entries = overlaysJson.getAsJsonArray("entries"); + if (entries != null) { + for (JsonElement overlayJson : entries) { + if (overlayJson instanceof JsonObject overlayJsonObj) { + overlays.add(new Overlay(overlayJsonObj)); + } + } + } + } + return overlays; + } + + public List overlays() { + return this.overlays; + } +} 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 new file mode 100644 index 000000000..609823392 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/PackVersion.java @@ -0,0 +1,87 @@ +package net.momirealms.craftengine.core.pack.mcmeta; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public record PackVersion(int major, int minor) implements Comparable { + public static final PackVersion MIN_PACK_VERSION = new PackVersion(15, 0); // 1.20 + public static final PackVersion MAX_PACK_VERSION = new PackVersion(1000, 0); // future + + @Override + public int compareTo(@NotNull PackVersion o) { + // 首先比较 major 版本 + int majorCompare = Integer.compare(this.major, o.major); + if (majorCompare != 0) { + return majorCompare; + } + // 如果 major 相同,则比较 minor 版本 + return Integer.compare(this.minor, o.minor); + } + + public boolean isAtOrAbove(PackVersion other) { + return this.compareTo(other) >= 0; + } + + public boolean isAbove(PackVersion other) { + return this.compareTo(other) > 0; + } + + public boolean isAtOrBelow(PackVersion other) { + return this.compareTo(other) <= 0; + } + + public boolean isBelow(PackVersion other) { + return this.compareTo(other) < 0; + } + + /** + * 返回两个版本中较小的那个(版本较低的) + */ + public static PackVersion getLower(PackVersion v1, PackVersion v2) { + if (v1 == null) return v2; + if (v2 == null) return v1; + return v1.compareTo(v2) <= 0 ? v1 : v2; + } + + /** + * 返回两个版本中较大的那个(版本较高的) + */ + public static PackVersion getHigher(PackVersion v1, PackVersion v2) { + if (v1 == null) return v2; + if (v2 == null) return v1; + return v1.compareTo(v2) >= 0 ? v1 : v2; + } + + public static PackVersion getLowest(List versions) { + if (versions == null || versions.isEmpty()) { + return MIN_PACK_VERSION; + } + + PackVersion lowest = versions.getFirst(); + for (int i = 1; i < versions.size(); i++) { + lowest = getLower(lowest, versions.get(i)); + } + return lowest; + } + + public static PackVersion getHighest(List versions) { + if (versions == null || versions.isEmpty()) { + return MAX_PACK_VERSION; + } + + PackVersion highest = versions.getFirst(); + for (int i = 1; i < versions.size(); i++) { + highest = getHigher(highest, versions.get(i)); + } + return highest; + } + + public static PackVersion parse(float num) { + String str = String.valueOf(num); + String[] parts = str.split("\\."); + int integerPart = Integer.parseInt(parts[0]); + int decimalPart = parts.length > 1 ? Integer.parseInt(parts[1]) : 0; + return new PackVersion(integerPart, decimalPart); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/overlay/ResourcePackOverlay.java b/core/src/main/java/net/momirealms/craftengine/core/pack/overlay/ResourcePackOverlay.java deleted file mode 100644 index 960c7da75..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/overlay/ResourcePackOverlay.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.momirealms.craftengine.core.pack.overlay; - -import net.momirealms.craftengine.core.util.MinecraftVersion; - -import java.nio.file.Path; - -public class ResourcePackOverlay { - private final MinecraftVersion minVersion; - private final MinecraftVersion maxVersion; - private final Path folder; - - public ResourcePackOverlay(MinecraftVersion minVersion, - MinecraftVersion maxVersion, - Path folder - ) { - this.minVersion = minVersion; - this.maxVersion = maxVersion; - this.folder = folder; - } - - public MinecraftVersion minVersion() { - return minVersion; - } - - public MinecraftVersion maxVersion() { - return maxVersion; - } - - public Path folder() { - return folder; - } -} 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 731d3f77e..949174f6f 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 @@ -60,12 +60,14 @@ public interface Revision { @Override public int maxPackVersion() { - return MinecraftVersions.FUTURE.packFormat(); + // todo 重构revision系统 + return MinecraftVersions.FUTURE.packFormat().major(); } @Override public int minPackVersion() { - return this.minVersion.packFormat(); + // todo 重构revision系统 + return this.minVersion.packFormat().major(); } @Override @@ -112,12 +114,12 @@ public interface Revision { @Override public int minPackVersion() { - return this.minVersion.packFormat(); + return this.minVersion.packFormat().major(); } @Override public int maxPackVersion() { - return this.maxVersion.packFormat(); + return this.maxVersion.packFormat().major(); } @Override 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 6bd5d3ad9..298a48d7d 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 @@ -79,6 +79,7 @@ public class Config { protected boolean resource_pack$validation$enable; protected boolean resource_pack$validation$fix_atlas; + protected List resource_pack$validation$test_versions; protected boolean resource_pack$exclude_core_shaders; protected boolean resource_pack$protection$obfuscation$enable; @@ -391,6 +392,7 @@ public class Config { }).collect(Collectors.toSet()); resource_pack$validation$enable = config.getBoolean("resource-pack.validation.enable", true); resource_pack$validation$fix_atlas = config.getBoolean("resource-pack.validation.fix-atlas", true); + resource_pack$validation$test_versions = config.getStringList("resource-pack.validation.test-versions", List.of("server")).stream().map(Config::getVersion).distinct().toList(); resource_pack$exclude_core_shaders = config.getBoolean("resource-pack.exclude-core-shaders", false); resource_pack$overlay_format = config.getString("resource-pack.overlay-format", "overlay_{version}"); if (!resource_pack$overlay_format.contains("{version}")) { @@ -1110,6 +1112,10 @@ public class Config { return instance.resource_pack$validation$fix_atlas; } + public static List validationTestVersions() { + return instance.resource_pack$validation$test_versions; + } + public static boolean excludeShaders() { return instance.resource_pack$exclude_core_shaders; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/FileUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/FileUtils.java index e98fb261c..8ae7c6b3c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/FileUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/FileUtils.java @@ -10,6 +10,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.Comparator; import java.util.EnumSet; import java.util.List; +import java.util.function.Predicate; import java.util.stream.Stream; public class FileUtils { @@ -67,9 +68,21 @@ public class FileUtils { } } + public static List collectOverlays(Path resourcePackFolder, Predicate directoryPredicate) throws IOException { + List folders = new ObjectArrayList<>(); + try (Stream paths = Files.list(resourcePackFolder)) { + folders.addAll(paths + .filter(Files::isDirectory) + .filter(path -> directoryPredicate.test(path.getFileName().toString())) + .filter(path -> !path.getFileName().toString().equals("assets")) + .filter(path -> Files.exists(path.resolve("assets"))) + .toList()); + } + return folders; + } + public static List collectOverlays(Path resourcePackFolder) throws IOException { List folders = new ObjectArrayList<>(); - folders.add(resourcePackFolder); try (Stream paths = Files.list(resourcePackFolder)) { folders.addAll(paths .filter(Files::isDirectory) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ListUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ListUtils.java index 7c8ef0ae0..16982d59a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ListUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ListUtils.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.core.util; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -13,4 +15,13 @@ public final class ListUtils { if (list.size() == 2) return List.of(list.get(0), list.get(1)); return list; } + + @SafeVarargs + public static List newNonNullList(T... elements) { + List list = new ArrayList<>(elements.length); + for (T element : elements) { + if (element != null) list.add(element); + } + return list; + } } 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 5489f3002..34c00fa46 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,36 +1,38 @@ package net.momirealms.craftengine.core.util; +import net.momirealms.craftengine.core.pack.mcmeta.PackVersion; + import java.util.HashMap; import java.util.Map; public final class MinecraftVersion implements Comparable { - public static final Map PACK_FORMATS = new HashMap<>(); + public static final Map PACK_FORMATS = new HashMap<>(); static { - PACK_FORMATS.put(1_20_00, 15); - PACK_FORMATS.put(1_20_01, 15); - PACK_FORMATS.put(1_20_02, 18); - PACK_FORMATS.put(1_20_03, 22); - PACK_FORMATS.put(1_20_04, 22); - PACK_FORMATS.put(1_20_05, 32); - PACK_FORMATS.put(1_20_06, 32); - PACK_FORMATS.put(1_21_00, 34); - PACK_FORMATS.put(1_21_01, 34); - PACK_FORMATS.put(1_21_02, 42); - PACK_FORMATS.put(1_21_03, 42); - PACK_FORMATS.put(1_21_04, 46); - PACK_FORMATS.put(1_21_05, 55); - PACK_FORMATS.put(1_21_06, 63); - PACK_FORMATS.put(1_21_07, 64); - PACK_FORMATS.put(1_21_08, 64); - PACK_FORMATS.put(1_21_09, 69); - PACK_FORMATS.put(1_21_10, 69); - PACK_FORMATS.put(1_21_11, 75); - PACK_FORMATS.put(1_99_99, 1000); + 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)); + PACK_FORMATS.put(1_20_03, new PackVersion(22, 0)); + PACK_FORMATS.put(1_20_04, new PackVersion(22, 0)); + PACK_FORMATS.put(1_20_05, new PackVersion(32, 0)); + PACK_FORMATS.put(1_20_06, new PackVersion(32, 0)); + PACK_FORMATS.put(1_21_00, new PackVersion(34, 0)); + PACK_FORMATS.put(1_21_01, new PackVersion(34, 0)); + PACK_FORMATS.put(1_21_02, new PackVersion(42, 0)); + PACK_FORMATS.put(1_21_03, new PackVersion(42, 0)); + PACK_FORMATS.put(1_21_04, new PackVersion(46, 0)); + PACK_FORMATS.put(1_21_05, new PackVersion(55, 0)); + PACK_FORMATS.put(1_21_06, new PackVersion(63, 0)); + PACK_FORMATS.put(1_21_07, new PackVersion(64, 0)); + PACK_FORMATS.put(1_21_08, new PackVersion(64, 0)); + 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)); } private final int version; private final String versionString; - private final int packFormat; + private final PackVersion packFormat; public static MinecraftVersion parse(final String version) { return new MinecraftVersion(version); @@ -40,7 +42,7 @@ public final class MinecraftVersion implements Comparable { return versionString; } - public int packFormat() { + public PackVersion packFormat() { return packFormat; } diff --git a/gradle.properties b/gradle.properties index 396afb318..baa9346aa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ org.gradle.jvmargs=-Xmx4G # Project settings -project_version=0.0.66.4 -config_version=62 +project_version=0.0.66.5 +config_version=63 lang_version=45 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.74 +sparrow_util_version=0.75 fastutil_version=8.5.18 netty_version=4.1.128.Final joml_version=1.10.8 From 2275f860909aa33765feac51196924fd45b0c7a7 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 18 Dec 2025 01:54:46 +0800 Subject: [PATCH 04/13] =?UTF-8?q?=E8=AE=B0=E5=BD=95=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/sound/BukkitSoundManager.java | 3 + common-files/src/main/resources/config.yml | 8 - .../src/main/resources/translations/en.yml | 12 +- .../src/main/resources/translations/zh_cn.yml | 12 +- .../core/item/AbstractItemManager.java | 8 +- .../core/pack/AbstractPackManager.java | 421 +++++++++++------- .../craftengine/core/pack/AtlasFixer.java | 66 +++ .../craftengine/core/pack/mcmeta/Overlay.java | 27 ++ .../core/pack/mcmeta/PackVersion.java | 4 + .../mcmeta/overlay/OverlayCombination.java | 108 +++++ .../core/pack/model/ModernItemModel.java | 5 +- .../select/DisplayContextSelectProperty.java | 3 +- .../model/special/PlayerHeadSpecialModel.java | 3 +- .../core/pack/revision/Revision.java | 5 +- .../core/pack/revision/Revisions.java | 8 +- .../core/plugin/config/Config.java | 4 +- .../core/util/MinecraftVersion.java | 83 +++- .../core/util/MinecraftVersions.java | 26 -- .../craftengine/core/util/VersionHelper.java | 39 +- gradle.properties | 4 +- 20 files changed, 613 insertions(+), 236 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/AtlasFixer.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/overlay/OverlayCombination.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java 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 From 4262015da5aa0b176f23599bd3f8c309708e1e71 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 18 Dec 2025 03:31:03 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E6=9C=80=E7=BB=88=E5=86=B3=E6=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/sound/BukkitSoundManager.java | 3 -- common-files/src/main/resources/config.yml | 1 - .../core/pack/AbstractPackManager.java | 29 +++++++++---------- .../craftengine/core/pack/PackManager.java | 1 - .../core/pack/mcmeta/PackVersion.java | 16 ++++++++++ .../mcmeta/overlay/OverlayCombination.java | 20 +++++++++++-- .../core/pack/revision/Revision.java | 27 +++++++++-------- .../core/plugin/config/Config.java | 6 ---- .../craftengine/core/util/ListUtils.java | 1 - .../core/util/MinecraftVersion.java | 6 +++- 10 files changed, 65 insertions(+), 45 deletions(-) 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 3d2491f0b..ad53f6d70 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,14 +2,12 @@ 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; @@ -17,7 +15,6 @@ 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 bed42747b..498128677 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -98,7 +98,6 @@ resource-pack: validation: enable: true # 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. # The fix-atlas feature is not all-powerful since 1.21.11. In some cases, CraftEngine cannot fix it for you, and you will need to fix your model yourself. fix-atlas: true 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 95b195f93..0112ecf87 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 @@ -836,10 +836,10 @@ public abstract class AbstractPackManager implements PackManager { if (!rawMeta.has("pack")) { JsonObject pack = new JsonObject(); rawMeta.add("pack", pack); - pack.addProperty("pack_format", Config.packMinVersion().packFormat().major()); + pack.addProperty("pack_format", Config.packMinVersion().majorPackFormat()); JsonObject supportedFormats = new JsonObject(); - supportedFormats.addProperty("min_inclusive", Config.packMinVersion().packFormat().major()); - supportedFormats.addProperty("max_inclusive", Config.packMaxVersion().packFormat().major()); + supportedFormats.addProperty("min_inclusive", Config.packMinVersion().majorPackFormat()); + supportedFormats.addProperty("max_inclusive", Config.packMaxVersion().majorPackFormat()); pack.add("supported_formats", supportedFormats); changed = true; } @@ -867,10 +867,11 @@ public abstract class AbstractPackManager implements PackManager { JsonObject entry = new JsonObject(); JsonObject formats = new JsonObject(); entry.add("formats", formats); - formats.addProperty("min_inclusive", revision.minPackVersion()); - formats.addProperty("max_inclusive", revision.maxPackVersion()); - entry.addProperty("min_format", revision.minPackVersion()); - entry.addProperty("max_format", revision.maxPackVersion()); + JsonArray formatsArray = new JsonArray(); + formatsArray.add(revision.minPackVersion().major()); + formatsArray.add(revision.maxPackVersion().major()); + entry.add("min_format", revision.minPackVersion().getAsJsonArray()); + entry.add("max_format", revision.maxPackVersion().getAsJsonArray()); entry.addProperty("directory", Config.createOverlayFolderName(revision.versionString())); entries.add(entry); } @@ -1197,7 +1198,7 @@ public abstract class AbstractPackManager implements PackManager { 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()); + OverlayCombination combination = new OverlayCombination(packMeta.overlays(), Config.packMinVersion().majorPackFormat(), Config.packMaxVersion().majorPackFormat()); while (combination.hasNext()) { OverlayCombination.Segment segment = combination.nextSegment(); if (segment != null) { @@ -1209,7 +1210,7 @@ public abstract class AbstractPackManager implements PackManager { } // 混合版本 else { - OverlayCombination combinationLegacy = new OverlayCombination(packMeta.overlays(), Config.packMinVersion().packFormat().major(), MinecraftVersion.V1_21_11.packFormat().major() - 1); + OverlayCombination combinationLegacy = new OverlayCombination(packMeta.overlays(), Config.packMinVersion().majorPackFormat(), 72 /* 25w44a */); while (combinationLegacy.hasNext()) { OverlayCombination.Segment segment = combinationLegacy.nextSegment(); if (segment != null) { @@ -1218,7 +1219,7 @@ public abstract class AbstractPackManager implements PackManager { break; } } - OverlayCombination combinationModern = new OverlayCombination(packMeta.overlays(), MinecraftVersion.V1_21_11.packFormat().major(), Config.packMaxVersion().packFormat().major()); + OverlayCombination combinationModern = new OverlayCombination(packMeta.overlays(), 73 /* 25w45a */, Config.packMaxVersion().majorPackFormat()); while (combinationModern.hasNext()) { OverlayCombination.Segment segment = combinationModern.nextSegment(); if (segment != null) { @@ -1277,9 +1278,7 @@ public abstract class AbstractPackManager implements PackManager { 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("\\.", "_")); + String directoryName = Config.createOverlayFolderName(min + "-" + max); Path atlasPath = path.resolve(directoryName) .resolve("assets") .resolve("minecraft") @@ -1300,9 +1299,7 @@ public abstract class AbstractPackManager implements PackManager { 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("\\.", "_")); + String directoryName = Config.createOverlayFolderName(min + "-" + max); // 这个版本不认可overlay,得把atlas直接写进主包内 if (min <= MinecraftVersion.V1_20_1.packFormat().major()) { Path atlasPath = path.resolve("assets") diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java index a66052fa6..dd864f93c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java @@ -6,7 +6,6 @@ import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigParser; import org.jetbrains.annotations.NotNull; -import java.io.IOException; import java.nio.file.Path; import java.util.Collection; import java.util.function.Predicate; 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 076b6e5cf..6e3bf1780 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 @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack.mcmeta; +import com.google.gson.JsonArray; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -39,6 +40,21 @@ public record PackVersion(int major, int minor) implements Comparable resource_pack$validation$test_versions; protected boolean resource_pack$exclude_core_shaders; protected boolean resource_pack$protection$obfuscation$enable; @@ -392,7 +391,6 @@ public class Config { }).collect(Collectors.toSet()); resource_pack$validation$enable = config.getBoolean("resource-pack.validation.enable", true); resource_pack$validation$fix_atlas = config.getBoolean("resource-pack.validation.fix-atlas", true); - resource_pack$validation$test_versions = config.getStringList("resource-pack.validation.test-versions", List.of("server")).stream().map(Config::getVersion).distinct().toList(); resource_pack$exclude_core_shaders = config.getBoolean("resource-pack.exclude-core-shaders", false); resource_pack$overlay_format = config.getString("resource-pack.overlay-format", "overlay_{version}"); if (!resource_pack$overlay_format.contains("{version}")) { @@ -1112,10 +1110,6 @@ public class Config { return instance.resource_pack$validation$fix_atlas; } - public static List validationTestVersions() { - return instance.resource_pack$validation$test_versions; - } - public static boolean excludeShaders() { return instance.resource_pack$exclude_core_shaders; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ListUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ListUtils.java index 16982d59a..02155b7d5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ListUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ListUtils.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.util; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; 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 aa35ddc78..4107010e2 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 @@ -102,7 +102,11 @@ public class MinecraftVersion implements Comparable { } public PackVersion packFormat() { - return packFormat; + return this.packFormat; + } + + public int majorPackFormat() { + return packFormat().major(); } private MinecraftVersion(String version) { From 37b8b06a587d9c6e508e72062011a09d783a52d9 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 18 Dec 2025 04:41:32 +0800 Subject: [PATCH 06/13] =?UTF-8?q?=E6=89=93=E5=80=92=E4=BA=86atlas=E6=81=B6?= =?UTF-8?q?=E9=AD=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/paper-loader/build.gradle.kts | 17 ++-------- common-files/src/main/resources/config.yml | 3 +- .../core/pack/AbstractPackManager.java | 32 +++++++++++++++---- .../resolution/ResolutionMergePackMcMeta.java | 15 +++++---- .../core/pack/mcmeta/PackVersion.java | 1 + 5 files changed, 39 insertions(+), 29 deletions(-) diff --git a/bukkit/paper-loader/build.gradle.kts b/bukkit/paper-loader/build.gradle.kts index 44379691f..aea13160d 100644 --- a/bukkit/paper-loader/build.gradle.kts +++ b/bukkit/paper-loader/build.gradle.kts @@ -203,10 +203,9 @@ fun registerPaperTask( version: String, dirName: String = version, javaVersion : Int = 21, - serverJar: File? = null, - downloadPlugins: Action? = null + serverJar: File? = null ) { - listOf(version, "${version}-with-viaversion").forEach { taskName -> + listOf(version).forEach { taskName -> tasks.register(taskName, RunServer::class) { group = "run dev server" minecraftVersion(version) @@ -224,18 +223,6 @@ fun registerPaperTask( jvmArgs("-Ddisable.watchdog=true") jvmArgs("-Xlog:redefine+class*=info") jvmArgs("-XX:+AllowEnhancedClassRedefinition") - if (taskName.contains("viaversion")) { - downloadPlugins { - url("https://ci.viaversion.com/job/ViaVersion/lastBuild/artifact/build/libs/${getJenkinsArtifactFileName("https://ci.viaversion.com/job/ViaVersion/lastSuccessfulBuild/api/json?tree=artifacts[*]")}") - url("https://ci.viaversion.com/view/ViaBackwards/job/ViaBackwards/662/artifact/build/libs/${getJenkinsArtifactFileName("https://ci.viaversion.com/job/ViaBackwards/lastSuccessfulBuild/api/json?tree=artifacts[*]")}") - } - } } } -} - -fun getJenkinsArtifactFileName(url: String): String { - val response = URI.create(url).toURL().readText() - val regex = """"fileName":"([^"]+)"""".toRegex() - return regex.find(response)?.groupValues?.get(1) ?: throw Exception("fileName not found") } \ No newline at end of file diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 498128677..4e33699a4 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -16,8 +16,9 @@ resource-pack: # This option determines the location of the generated resource pack # You can use either an absolute path or a relative path here path: "./generated/resource_pack.zip" + # The minimum client version supported by CraftEngine is 1.20. If you need to provide support for lower client versions, please modify the resource pack yourself. # Allowed values: - # - 1.20.1, 1.21, 1.21.8, etc. + # - 1.20.2, 1.21, 1.21.8, etc. # - latest: the latest client version # - server: the current server version supported-version: 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 0112ecf87..1f69ce39f 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 @@ -865,9 +865,8 @@ public abstract class AbstractPackManager implements PackManager { } for (Revision revision : revisions) { JsonObject entry = new JsonObject(); - JsonObject formats = new JsonObject(); - entry.add("formats", formats); JsonArray formatsArray = new JsonArray(); + entry.add("formats", formatsArray); formatsArray.add(revision.minPackVersion().major()); formatsArray.add(revision.maxPackVersion().major()); entry.add("min_format", revision.minPackVersion().getAsJsonArray()); @@ -1188,8 +1187,10 @@ public abstract class AbstractPackManager implements PackManager { private void validateResourcePack(Path path) { Path packMcMetaPath = path.resolve("pack.mcmeta"); PackMcMeta packMeta; + JsonObject packMetaJson; try { - packMeta = new PackMcMeta(GsonHelper.readJsonFile(packMcMetaPath).getAsJsonObject()); + packMetaJson = GsonHelper.readJsonFile(packMcMetaPath).getAsJsonObject(); + packMeta = new PackMcMeta(packMetaJson); } catch (IOException e) { this.plugin.logger().warn("Failed to read pack.mcmeta " + packMcMetaPath.toAbsolutePath(), e); return; @@ -1256,7 +1257,7 @@ public abstract class AbstractPackManager implements PackManager { 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 (Config.fixTextureAtlas() && !Config.enableObfuscation()) { // 有修复物品 if (result.fixedItemAtlas != null) { itemFixer.addEntry(segment.min(), segment.max(), result.fixedItemAtlas); @@ -1272,7 +1273,7 @@ public abstract class AbstractPackManager implements PackManager { } // 尝试修复atlas - if (Config.fixTextureAtlas()) { + if (Config.fixTextureAtlas() && !Config.enableObfuscation()) { Map atlasToAdd = new LinkedHashMap<>(); // 物品 for (AtlasFixer.Entry entry : itemFixer.entries()) { @@ -1296,7 +1297,7 @@ public abstract class AbstractPackManager implements PackManager { } } // 方块 - for (AtlasFixer.Entry entry : itemFixer.entries()) { + for (AtlasFixer.Entry entry : blockFixer.entries()) { int min = entry.min(); int max = entry.max(); String directoryName = Config.createOverlayFolderName(min + "-" + max); @@ -1330,6 +1331,25 @@ public abstract class AbstractPackManager implements PackManager { } } } + + JsonObject overlaysJson = packMetaJson.getAsJsonObject("overlays"); + if (overlaysJson == null) { + overlaysJson = new JsonObject(); + packMetaJson.add("overlays", overlaysJson); + } + JsonArray overlayEntries = overlaysJson.getAsJsonArray("entries"); + if (overlayEntries == null) { + overlayEntries = new JsonArray(); + overlaysJson.add("entries", overlayEntries); + } + for (JsonElement entry : atlasToAdd.values()) { + overlayEntries.add(entry); + } + try { + GsonHelper.writeJsonFile(packMetaJson, packMcMetaPath); + } catch (IOException e) { + this.plugin.logger().warn("Failed to write pack.mcmeta", e); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java index c524ecebe..65e5ee5d2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java @@ -8,6 +8,7 @@ import com.google.gson.JsonPrimitive; import net.momirealms.craftengine.core.pack.conflict.PathContext; import net.momirealms.craftengine.core.pack.mcmeta.PackVersion; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; @@ -162,12 +163,12 @@ public class ResolutionMergePackMcMeta implements Resolution { JsonObject entryJson = entry.getAsJsonObject(); if (entryJson == null) continue; Pair supportedVersions = getSupportedVersions(entryJson); - PackVersion min = supportedVersions.left(); - PackVersion max = supportedVersions.right(); + PackVersion min = PackVersion.getHigher(supportedVersions.left(), PackVersion.MIN_OVERLAY_VERSION); + PackVersion max = PackVersion.getHigher(supportedVersions.right(), PackVersion.MIN_OVERLAY_VERSION); // 旧版格式支持 - JsonObject supportedFormats = new JsonObject(); - supportedFormats.addProperty("min_inclusive", min.major()); - supportedFormats.addProperty("max_inclusive", max.major()); + JsonArray supportedFormats = new JsonArray(); + supportedFormats.add(min.major()); + supportedFormats.add(max.major()); entryJson.add("formats", supportedFormats); // 新版格式支持 JsonArray minFormat = new JsonArray(); @@ -186,8 +187,8 @@ public class ResolutionMergePackMcMeta implements Resolution { public static void mergePack(JsonObject merged, JsonObject pack1, JsonObject pack2) { Pair pack1Version = getSupportedVersions(pack1); Pair pack2Version = getSupportedVersions(pack2); - PackVersion min = PackVersion.getLower(pack1Version.left(), pack2Version.left()); - PackVersion max = PackVersion.getHigher(pack1Version.right(), pack2Version.right()); + PackVersion min = Config.packMinVersion().packFormat(); + PackVersion max = PackVersion.getHigher(PackVersion.getHigher(pack1Version.right(), pack2Version.right()), Config.packMaxVersion().packFormat()); // 旧版格式支持 JsonObject supportedFormats = new JsonObject(); supportedFormats.addProperty("min_inclusive", min.major()); 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 6e3bf1780..2e321e26f 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 @@ -7,6 +7,7 @@ import java.util.List; public record PackVersion(int major, int minor) implements Comparable { public static final PackVersion MIN_PACK_VERSION = new PackVersion(15, 0); // 1.20 + public static final PackVersion MIN_OVERLAY_VERSION = new PackVersion(18, 0); // 1.20 public static final PackVersion MAX_PACK_VERSION = new PackVersion(1000, 0); // future public PackVersion(int major) { From 1d4335d53bea1c8e7c1670165f56c271054a1973 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 18 Dec 2025 05:04:26 +0800 Subject: [PATCH 07/13] =?UTF-8?q?=E4=BF=AE=E5=A4=8D65=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E9=99=B7=E9=98=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/core/pack/AbstractPackManager.java | 10 ++++++---- .../resolution/ResolutionMergePackMcMeta.java | 13 ++++++++----- .../craftengine/core/pack/mcmeta/Overlay.java | 10 ++++++---- 3 files changed, 20 insertions(+), 13 deletions(-) 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 1f69ce39f..f64c064f1 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 @@ -865,10 +865,12 @@ public abstract class AbstractPackManager implements PackManager { } for (Revision revision : revisions) { JsonObject entry = new JsonObject(); - JsonArray formatsArray = new JsonArray(); - entry.add("formats", formatsArray); - formatsArray.add(revision.minPackVersion().major()); - formatsArray.add(revision.maxPackVersion().major()); + if (revision.minPackVersion().major() < 65) { + JsonArray formatsArray = new JsonArray(); + entry.add("formats", formatsArray); + formatsArray.add(revision.minPackVersion().major()); + formatsArray.add(revision.maxPackVersion().major()); + } entry.add("min_format", revision.minPackVersion().getAsJsonArray()); entry.add("max_format", revision.maxPackVersion().getAsJsonArray()); entry.addProperty("directory", Config.createOverlayFolderName(revision.versionString())); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java index 65e5ee5d2..38063ac9e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java @@ -165,11 +165,14 @@ public class ResolutionMergePackMcMeta implements Resolution { Pair supportedVersions = getSupportedVersions(entryJson); PackVersion min = PackVersion.getHigher(supportedVersions.left(), PackVersion.MIN_OVERLAY_VERSION); PackVersion max = PackVersion.getHigher(supportedVersions.right(), PackVersion.MIN_OVERLAY_VERSION); - // 旧版格式支持 - JsonArray supportedFormats = new JsonArray(); - supportedFormats.add(min.major()); - supportedFormats.add(max.major()); - entryJson.add("formats", supportedFormats); + // https://minecraft.wiki/w/Java_Edition_25w31a + if (min.major() < 65) { + // 旧版格式支持 + JsonArray supportedFormats = new JsonArray(); + supportedFormats.add(min.major()); + supportedFormats.add(max.major()); + entryJson.add("formats", supportedFormats); + } // 新版格式支持 JsonArray minFormat = new JsonArray(); minFormat.add(min.major()); 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 72440de6b..b995fa8cd 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 @@ -52,10 +52,12 @@ public class Overlay { 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); + if (this.minVersion.major() < 65) { + JsonArray formats = new JsonArray(); + formats.add(new JsonPrimitive(this.minVersion.major())); + formats.add(new JsonPrimitive(this.maxVersion.major())); + entry.add("formats", formats); + } return entry; } From a0055cc356c9dc5189ef317a151a798da559636f Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 18 Dec 2025 05:50:06 +0800 Subject: [PATCH 08/13] Update Atlas.java --- .../java/net/momirealms/craftengine/core/pack/atlas/Atlas.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/atlas/Atlas.java b/core/src/main/java/net/momirealms/craftengine/core/pack/atlas/Atlas.java index 711c8e00d..561518efe 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/atlas/Atlas.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/atlas/Atlas.java @@ -154,6 +154,8 @@ public final class Atlas { if (this.filtered.test(texture)) return null; // single直接包含 if (this.single.contains(texture)) return texture; + // 被unstitch或者调色盘定义 + if (this.defined.contains(texture)) return null; String path = texture.value(); // 路径匹配 for (Map.Entry entry : this.directory.entrySet()) { From 37d045aabfde8fb9d22aee7ffce73190ba04ed15 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 18 Dec 2025 18:10:08 +0800 Subject: [PATCH 09/13] =?UTF-8?q?=E5=A2=9E=E5=BC=BApack.mcmeta=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/config.yml | 3 +- .../default/resourcepack/pack.mcmeta | 12 -- .../core/pack/AbstractPackManager.java | 123 +++++++++++------- .../resolution/ResolutionMergePackMcMeta.java | 43 +++--- .../craftengine/core/pack/mcmeta/Overlay.java | 4 +- .../core/pack/mcmeta/PackVersion.java | 1 + .../core/plugin/config/Config.java | 6 + gradle.properties | 4 +- 8 files changed, 103 insertions(+), 93 deletions(-) delete mode 100644 common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 4e33699a4..7947a3357 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -24,6 +24,8 @@ resource-pack: supported-version: min: server max: latest + # The description of your resource pack + description: "CraftEngine ResourcePack" # Remove 1.21.5+ tinted_leaves particles remove-tinted-leaves-particle: true # Define the name of the overlay folders @@ -65,7 +67,6 @@ resource-pack: path: "pack.mcmeta" resolution: type: merge_pack_mcmeta - description: "CraftEngine ResourcePack" - term: type: exact path: "pack.png" diff --git a/common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta b/common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta deleted file mode 100644 index 4d5e8390e..000000000 --- a/common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta +++ /dev/null @@ -1,12 +0,0 @@ -{ - "pack": { - "pack_format": 15, - "description": "CraftEngine", - "supported_formats": { - "min_inclusive": 15, - "max_inclusive": 1000 - }, - "min_format": 15, - "max_format": 1000 - } -} \ No newline at end of file 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 f64c064f1..140421668 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 @@ -788,19 +788,28 @@ public abstract class AbstractPackManager implements PackManager { this.generateClientLang(generatedPackPath); this.generateEquipments(generatedPackPath, revisions::add); this.generateParticle(generatedPackPath); - this.generatePackMetadata(generatedPackPath.resolve("pack.mcmeta"), revisions); + + Path packMcMetaPath = generatedPackPath.resolve("pack.mcmeta"); + JsonObject packMcMeta = Files.isRegularFile(packMcMetaPath) ? GsonHelper.readJsonFile(packMcMetaPath).getAsJsonObject() : new JsonObject(); + // 生成revision overlay + this.generateRevisionOverlays(packMcMeta, revisions); + if (Config.excludeShaders()) { this.removeAllShaders(generatedPackPath); } long time2 = System.currentTimeMillis(); this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.generate.finish", String.valueOf(time2 - time1))); + // 校验资源包 if (Config.validateResourcePack()) { - this.validateResourcePack(generatedPackPath); + this.validateResourcePack(generatedPackPath, packMcMeta); } long time3 = System.currentTimeMillis(); if (Config.validateResourcePack()) { this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.validate.finish", String.valueOf(time3 - time2))); } + // 验证完成后,应该重新校验pack.mcmeta并写入 + this.validatePackMetadata(generatedPackPath.resolve("pack.mcmeta"), packMcMeta); + // 优化资源包 if (Config.optimizeResourcePack()) { this.optimizeResourcePack(generatedPackPath); } @@ -824,29 +833,61 @@ public abstract class AbstractPackManager implements PackManager { } } - private void generatePackMetadata(Path path, Set revisions) throws IOException { - JsonObject rawMeta; - boolean changed = false; - if (!Files.exists(path)) { - rawMeta = new JsonObject(); - changed = true; - } else { - rawMeta = GsonHelper.readJsonFile(path).getAsJsonObject(); - } - if (!rawMeta.has("pack")) { - JsonObject pack = new JsonObject(); - rawMeta.add("pack", pack); - pack.addProperty("pack_format", Config.packMinVersion().majorPackFormat()); - JsonObject supportedFormats = new JsonObject(); - supportedFormats.addProperty("min_inclusive", Config.packMinVersion().majorPackFormat()); - supportedFormats.addProperty("max_inclusive", Config.packMaxVersion().majorPackFormat()); - pack.add("supported_formats", supportedFormats); - changed = true; - } - if (revisions.isEmpty()) { - if (changed) { - GsonHelper.writeJsonFile(rawMeta, path); + private void validatePackMetadata(Path path, JsonObject rawMeta) throws IOException { + // 获取设定的最大和最小值 + PackVersion minVersion = Config.packMinVersion().packFormat(); + PackVersion maxVersion = Config.packMaxVersion().packFormat(); + + // 设置pack + { + JsonObject packJson = new JsonObject(); + rawMeta.add("pack", packJson); + JsonElement description = AdventureHelper.componentToJsonElement(AdventureHelper.miniMessage().deserialize(Config.packDescription())); + packJson.add("description", description); + // 需要旧版本兼容性 + if (minVersion.isBelow(PackVersion.PACK_FORMAT_CHANGE_VERSION)) { + packJson.addProperty("pack_format", minVersion.major()); + JsonObject supportedVersions = new JsonObject(); + supportedVersions.addProperty("min_inclusive", minVersion.major()); + supportedVersions.addProperty("max_inclusive", maxVersion.major()); + packJson.add("supported_formats", supportedVersions); } + // 到达了1.21.9 + if (maxVersion.isAtOrAbove(PackVersion.PACK_FORMAT_CHANGE_VERSION)) { + // 同时要兼容低版本 + packJson.add("min_format", minVersion.getAsJsonArray()); + packJson.add("max_format", maxVersion.getAsJsonArray()); + } + } + + // 验证overlay + { + PackMcMeta mcMeta = new PackMcMeta(rawMeta); + List overlays = mcMeta.overlays(); + if (!overlays.isEmpty()) { + boolean legacySupported = false; + for (Overlay overlay : overlays) { + if (overlay.minVersion().isBelow(PackVersion.PACK_FORMAT_CHANGE_VERSION)) { + legacySupported = true; + break; + } + } + JsonArray newOverlayEntries = new JsonArray(); + for (Overlay overlay : overlays) { + newOverlayEntries.add(overlay.getAsOverlayEntry(legacySupported)); + } + JsonObject overlaysJson = new JsonObject(); + overlaysJson.add("entries", newOverlayEntries); + rawMeta.add("overlays", overlaysJson); + } + } + + GsonHelper.writeJsonFile(rawMeta, path); + } + + // 这里都是随便写写的,重点在之后的校验里 + private void generateRevisionOverlays(JsonObject rawMeta, Set revisions) throws IOException { + if (revisions.isEmpty()) { return; } JsonObject overlays; @@ -865,18 +906,15 @@ public abstract class AbstractPackManager implements PackManager { } for (Revision revision : revisions) { JsonObject entry = new JsonObject(); - if (revision.minPackVersion().major() < 65) { - JsonArray formatsArray = new JsonArray(); - entry.add("formats", formatsArray); - formatsArray.add(revision.minPackVersion().major()); - formatsArray.add(revision.maxPackVersion().major()); - } + JsonArray formatsArray = new JsonArray(); + entry.add("formats", formatsArray); + formatsArray.add(revision.minPackVersion().major()); + formatsArray.add(revision.maxPackVersion().major()); entry.add("min_format", revision.minPackVersion().getAsJsonArray()); entry.add("max_format", revision.maxPackVersion().getAsJsonArray()); entry.addProperty("directory", Config.createOverlayFolderName(revision.versionString())); entries.add(entry); } - GsonHelper.writeJsonFile(rawMeta, path); } private void removeAllShaders(Path path) { @@ -1186,18 +1224,8 @@ public abstract class AbstractPackManager implements PackManager { } } - private void validateResourcePack(Path path) { - Path packMcMetaPath = path.resolve("pack.mcmeta"); - PackMcMeta packMeta; - JsonObject packMetaJson; - try { - packMetaJson = GsonHelper.readJsonFile(packMcMetaPath).getAsJsonObject(); - packMeta = new PackMcMeta(packMetaJson); - } catch (IOException e) { - this.plugin.logger().warn("Failed to read pack.mcmeta " + packMcMetaPath.toAbsolutePath(), e); - return; - } - + private void validateResourcePack(Path path, JsonObject packMetaJson) { + PackMcMeta packMeta = new PackMcMeta(packMetaJson); 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)) { @@ -1292,7 +1320,7 @@ public abstract class AbstractPackManager implements PackManager { 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()); + atlasToAdd.put(directoryName, overlay.getAsOverlayEntry(true)); } } catch (IOException e) { this.plugin.logger().warn("Failed to write atlas " + atlasPath.toAbsolutePath(), e); @@ -1326,7 +1354,7 @@ public abstract class AbstractPackManager implements PackManager { 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()); + atlasToAdd.put(directoryName, overlay.getAsOverlayEntry(true)); } } catch (IOException e) { this.plugin.logger().warn("Failed to write atlas " + atlasPath.toAbsolutePath(), e); @@ -1347,11 +1375,6 @@ public abstract class AbstractPackManager implements PackManager { for (JsonElement entry : atlasToAdd.values()) { overlayEntries.add(entry); } - try { - GsonHelper.writeJsonFile(packMetaJson, packMcMetaPath); - } catch (IOException e) { - this.plugin.logger().warn("Failed to write pack.mcmeta", e); - } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java index 38063ac9e..ef7057ec0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java @@ -9,7 +9,6 @@ import net.momirealms.craftengine.core.pack.conflict.PathContext; import net.momirealms.craftengine.core.pack.mcmeta.PackVersion; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; -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.Pair; @@ -26,13 +25,7 @@ public class ResolutionMergePackMcMeta implements Resolution { public static final Factory FACTORY = new Factory(); public static final Set STANDARD_PACK_KEYS = ImmutableSet.of("pack", "features", "filter", "overlays", "language"); - private final String description; - - public ResolutionMergePackMcMeta(String description) { - this.description = description; - } - - public static void merge(Path file1, Path file2, JsonElement customDescription) throws IOException { + public static void merge(Path file1, Path file2) throws IOException { // 第一步,解析全部的mcmeta文件为json对象 JsonObject mcmeta1; try { @@ -49,13 +42,15 @@ public class ResolutionMergePackMcMeta implements Resolution { return; } JsonObject merged = new JsonObject(); - // 第三步,处理pack区域 - JsonObject pack1 = mcmeta1.getAsJsonObject("pack"); - JsonObject pack2 = mcmeta2.getAsJsonObject("pack"); - JsonObject mergedPack = new JsonObject(); - mergedPack.add("description", customDescription); - merged.add("pack", mergedPack); - mergePack(mergedPack, pack1, pack2); + +// 注释: 无需处理,由后续验证合并 +// //第二步,处理pack区域 +// JsonObject pack1 = mcmeta1.getAsJsonObject("pack"); +// JsonObject pack2 = mcmeta2.getAsJsonObject("pack"); +// JsonObject mergedPack = new JsonObject(); +// merged.add("pack", mergedPack); +// mergePack(mergedPack, pack1, pack2); + // 第三步,合并overlays List overlays = new ArrayList<>(); collectOverlays(mcmeta1.getAsJsonObject("overlays"), overlays::add); @@ -165,14 +160,11 @@ public class ResolutionMergePackMcMeta implements Resolution { Pair supportedVersions = getSupportedVersions(entryJson); PackVersion min = PackVersion.getHigher(supportedVersions.left(), PackVersion.MIN_OVERLAY_VERSION); PackVersion max = PackVersion.getHigher(supportedVersions.right(), PackVersion.MIN_OVERLAY_VERSION); - // https://minecraft.wiki/w/Java_Edition_25w31a - if (min.major() < 65) { - // 旧版格式支持 - JsonArray supportedFormats = new JsonArray(); - supportedFormats.add(min.major()); - supportedFormats.add(max.major()); - entryJson.add("formats", supportedFormats); - } + // 旧版格式支持 + JsonArray supportedFormats = new JsonArray(); + supportedFormats.add(min.major()); + supportedFormats.add(max.major()); + entryJson.add("formats", supportedFormats); // 新版格式支持 JsonArray minFormat = new JsonArray(); minFormat.add(min.major()); @@ -285,7 +277,7 @@ public class ResolutionMergePackMcMeta implements Resolution { @Override public void run(PathContext existing, PathContext conflict) { try { - merge(existing.path(), conflict.path(), AdventureHelper.componentToJsonElement(AdventureHelper.miniMessage().deserialize(this.description))); + merge(existing.path(), conflict.path()); } catch (Exception e) { CraftEngine.instance().logger().severe("Failed to merge pack.mcmeta when resolving file conflicts for '" + existing.path() + "' and '" + conflict.path() + "'", e); } @@ -299,8 +291,7 @@ public class ResolutionMergePackMcMeta implements Resolution { public static class Factory implements ResolutionFactory { @Override public Resolution create(Map arguments) { - String description = arguments.getOrDefault("description", "CraftEngine ResourcePack").toString(); - return new ResolutionMergePackMcMeta(description); + return new ResolutionMergePackMcMeta(); } } } 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 b995fa8cd..a40b02acc 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 @@ -41,7 +41,7 @@ public class Overlay { '}'; } - public JsonObject getAsOverlayEntry() { + public JsonObject getAsOverlayEntry(boolean legacy) { JsonObject entry = new JsonObject(); entry.addProperty("directory", this.directory); JsonArray minFormat = new JsonArray(); @@ -52,7 +52,7 @@ public class Overlay { maxFormat.add(new JsonPrimitive(this.maxVersion.major())); maxFormat.add(new JsonPrimitive(this.maxVersion.minor())); entry.add("max_format", maxFormat); - if (this.minVersion.major() < 65) { + if (legacy) { JsonArray formats = new JsonArray(); formats.add(new JsonPrimitive(this.minVersion.major())); formats.add(new JsonPrimitive(this.maxVersion.major())); 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 2e321e26f..4022e9bd5 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,7 @@ import java.util.List; public record PackVersion(int major, int minor) implements Comparable { public static final PackVersion MIN_PACK_VERSION = new PackVersion(15, 0); // 1.20 public static final PackVersion MIN_OVERLAY_VERSION = new PackVersion(18, 0); // 1.20 + public static final PackVersion PACK_FORMAT_CHANGE_VERSION = new PackVersion(65, 0); // 25w31a public static final PackVersion MAX_PACK_VERSION = new PackVersion(1000, 0); // future public PackVersion(int major) { 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 49fbf150b..1c1b0d093 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 @@ -66,6 +66,7 @@ public class Config { protected List resource_pack$merge_external_zips; protected Set resource_pack$exclude_file_extensions; protected Path resource_pack$path; + protected String resource_pack$description; protected boolean resource_pack$protection$crash_tools$method_1; protected boolean resource_pack$protection$crash_tools$method_2; @@ -329,6 +330,7 @@ public class Config { // resource pack resource_pack$path = resolvePath(config.getString("resource-pack.path", "./generated/resource_pack.zip")); + resource_pack$description = config.getString("resource-pack.description", "CraftEngine ResourcePack"); resource_pack$override_uniform_font = config.getBoolean("resource-pack.override-uniform-font", false); resource_pack$generate_mod_assets = config.getBoolean("resource-pack.generate-mod-assets", false); resource_pack$remove_tinted_leaves_particle = config.getBoolean("resource-pack.remove-tinted-leaves-particle", true); @@ -1130,6 +1132,10 @@ public class Config { return instance.equipment$sacrificed_vanilla_armor$humanoid_leggings; } + public static String packDescription() { + return instance.resource_pack$description; + } + public static String sacrificedVanillaArmorType() { return instance.equipment$sacrificed_vanilla_armor$type; } diff --git a/gradle.properties b/gradle.properties index 0c853f64d..27f3fbb86 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ org.gradle.jvmargs=-Xmx4G # Project settings -project_version=0.0.66.5 -config_version=63 +project_version=0.0.66.6 +config_version=64 lang_version=46 project_group=net.momirealms latest_supported_version=1.21.11 From 9606c17406e60222261dd0c6bd6be36ecfdd4e9b Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 18 Dec 2025 18:47:45 +0800 Subject: [PATCH 10/13] =?UTF-8?q?=E6=9C=80=E4=BD=8E=E6=94=AF=E6=8C=811.16?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/config.yml | 4 ++-- .../craftengine/core/pack/AbstractPackManager.java | 3 ++- .../core/pack/mcmeta/overlay/OverlayCombination.java | 1 + .../craftengine/core/util/MinecraftVersion.java | 12 ++++++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 7947a3357..72bb3b173 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -16,9 +16,9 @@ resource-pack: # This option determines the location of the generated resource pack # You can use either an absolute path or a relative path here path: "./generated/resource_pack.zip" - # The minimum client version supported by CraftEngine is 1.20. If you need to provide support for lower client versions, please modify the resource pack yourself. + # The minimum client version supported by CraftEngine is 1.16. If you need to provide support for lower client versions, please modify the resource pack yourself. # Allowed values: - # - 1.20.2, 1.21, 1.21.8, etc. + # - 1.20.1, 1.21, 1.21.8, 26.1, etc. # - latest: the latest client version # - server: the current server version supported-version: 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 140421668..cc8d102dc 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 @@ -845,6 +845,7 @@ public abstract class AbstractPackManager implements PackManager { JsonElement description = AdventureHelper.componentToJsonElement(AdventureHelper.miniMessage().deserialize(Config.packDescription())); packJson.add("description", description); // 需要旧版本兼容性 + // https://minecraft.wiki/w/Java_Edition_25w31a if (minVersion.isBelow(PackVersion.PACK_FORMAT_CHANGE_VERSION)) { packJson.addProperty("pack_format", minVersion.major()); JsonObject supportedVersions = new JsonObject(); @@ -865,7 +866,7 @@ public abstract class AbstractPackManager implements PackManager { PackMcMeta mcMeta = new PackMcMeta(rawMeta); List overlays = mcMeta.overlays(); if (!overlays.isEmpty()) { - boolean legacySupported = false; + boolean legacySupported = false; // https://minecraft.wiki/w/Java_Edition_25w31a for (Overlay overlay : overlays) { if (overlay.minVersion().isBelow(PackVersion.PACK_FORMAT_CHANGE_VERSION)) { legacySupported = true; diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/overlay/OverlayCombination.java b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/overlay/OverlayCombination.java index a15bbb88a..0c800cc74 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/overlay/OverlayCombination.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/overlay/OverlayCombination.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.pack.mcmeta.overlay; import net.momirealms.craftengine.core.pack.mcmeta.Overlay; +import net.momirealms.craftengine.core.util.MinecraftVersion; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; 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 4107010e2..d009c89dc 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 @@ -10,6 +10,12 @@ public class MinecraftVersion implements Comparable { private static final Map PACK_FORMATS = new HashMap<>(); static { + PACK_FORMATS.put(1_16_00, new PackVersion(5, 0)); + PACK_FORMATS.put(1_16_01, new PackVersion(5, 0)); + PACK_FORMATS.put(1_16_02, new PackVersion(6, 0)); + PACK_FORMATS.put(1_16_03, new PackVersion(6, 0)); + PACK_FORMATS.put(1_16_04, new PackVersion(6, 0)); + PACK_FORMATS.put(1_16_05, new PackVersion(6, 0)); 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)); @@ -45,6 +51,12 @@ public class MinecraftVersion implements Comparable { private static final Map BY_NAME = new LinkedHashMap<>(); private static final Multimap BY_PACK_FORMAT = ArrayListMultimap.create(); + public static final MinecraftVersion V1_16 = new MinecraftVersion("1.16"); + public static final MinecraftVersion V1_16_1 = new MinecraftVersion("1.16.1"); + public static final MinecraftVersion V1_16_2 = new MinecraftVersion("1.16.2"); + public static final MinecraftVersion V1_16_3 = new MinecraftVersion("1.16.3"); + public static final MinecraftVersion V1_16_4 = new MinecraftVersion("1.16.4"); + public static final MinecraftVersion V1_16_5 = new MinecraftVersion("1.16.5"); 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"); From 31c1dc59e395932e04c9ea9b05c01d7071136e69 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 18 Dec 2025 19:26:24 +0800 Subject: [PATCH 11/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E9=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/paper-loader/build.gradle.kts | 2 -- .../momirealms/craftengine/core/block/CustomBlock.java | 6 ++++++ .../core/entity/furniture/CustomFurniture.java | 5 +++++ .../momirealms/craftengine/core/item/CustomItem.java | 5 +++++ .../craftengine/core/pack/AbstractPackManager.java | 1 + .../core/pack/mcmeta/overlay/OverlayCombination.java | 1 - .../craftengine/core/plugin/locale/LangData.java | 10 +++++----- 7 files changed, 22 insertions(+), 8 deletions(-) diff --git a/bukkit/paper-loader/build.gradle.kts b/bukkit/paper-loader/build.gradle.kts index aea13160d..155a5e15b 100644 --- a/bukkit/paper-loader/build.gradle.kts +++ b/bukkit/paper-loader/build.gradle.kts @@ -1,7 +1,5 @@ import net.minecrell.pluginyml.paper.PaperPluginDescription import xyz.jpenilla.runpaper.task.RunServer -import xyz.jpenilla.runtask.pluginsapi.DownloadPluginsSpec -import java.net.URI plugins { id("com.gradleup.shadow") version "9.3.0" diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java index daa2ade2f..267f4a9c1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java @@ -20,6 +20,12 @@ public interface CustomBlock { @Nullable LootTable lootTable(); + @NotNull + default String translationKey() { + Key id = id(); + return "block." + id.namespace() + "." + id.value(); + } + void execute(Context context, EventTrigger trigger); @NotNull diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java index abfd362bf..3f3cf37a9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java @@ -22,6 +22,11 @@ public interface CustomFurniture { FurnitureSettings settings(); + default String translationKey() { + Key id = this.id(); + return "furniture." + id.namespace() + "." + id.value(); + } + @Nullable LootTable lootTable(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java index d367cf882..f5bbdb308 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java @@ -26,6 +26,11 @@ public interface CustomItem extends BuildableItem { UniqueKey uniqueId(); + default String translationKey() { + Key id = this.id(); + return "item." + id.namespace() + "." + id.value(); + } + Key material(); Key clientBoundMaterial(); 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 cc8d102dc..f7d6138dd 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 @@ -211,6 +211,7 @@ public abstract class AbstractPackManager implements PackManager { for (int i = 0; i < 256; i++) { VANILLA_TEXTURES.add(Key.of("minecraft", "font/unicode_page_" + String.format("%02x", i))); } + VANILLA_TEXTURES.add(Key.of("minecraft", "missingno")); loadInternalList("internal/textures/processed.json", VANILLA_TEXTURES::add); loadInternalList("internal/sounds/processed.json", VANILLA_SOUNDS::add); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/overlay/OverlayCombination.java b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/overlay/OverlayCombination.java index 0c800cc74..a15bbb88a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/overlay/OverlayCombination.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/mcmeta/overlay/OverlayCombination.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.pack.mcmeta.overlay; import net.momirealms.craftengine.core.pack.mcmeta.Overlay; -import net.momirealms.craftengine.core.util.MinecraftVersion; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java index fa6689ecd..5b41fa05b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java @@ -19,18 +19,18 @@ public class LangData { if (id.contains("[") && id.contains("]")) { ImmutableBlockState parsed = BlockStateParser.deserialize(id); if (parsed == null) return List.of(id); - return List.of("block." + stateToRealBlockId(parsed)); + return List.of(translationKey(parsed)); } else { Key blockId = Key.of(id); Optional blockOptional = CraftEngine.instance().blockManager().blockById(blockId); if (blockOptional.isPresent()) { List states = blockOptional.get().variantProvider().states(); if (states.size() == 1) { - return List.of("block." + stateToRealBlockId(states.getFirst())); + return List.of(translationKey(states.getFirst())); } else { ArrayList processed = new ArrayList<>(); for (ImmutableBlockState state : states) { - processed.add("block." + stateToRealBlockId(state)); + processed.add(translationKey(state)); } return processed; } @@ -91,7 +91,7 @@ public class LangData { }); } - private static String stateToRealBlockId(ImmutableBlockState state) { + public static String translationKey(ImmutableBlockState state) { String id = state.customBlockState().literalObject().toString(); int first = -1, last = -1; for (int i = 0; i < id.length(); i++) { @@ -113,6 +113,6 @@ public class LangData { chars[i] = '.'; } } - return new String(chars); + return "block." + new String(chars); } } From b56848bb4ce842d75f165f92068f7e492b528278 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 18 Dec 2025 20:29:47 +0800 Subject: [PATCH 12/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E7=89=A9=E5=93=81=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/item/AbstractItemManager.java | 28 +------- .../item/modifier/ConditionalModifier.java | 66 +++++++++++++++++++ .../core/item/modifier/ItemDataModifiers.java | 36 ++++++++++ .../OverwritableItemModelModifier.java | 5 +- .../item/recipe/result/PostProcessors.java | 6 +- .../item/updater/impl/ApplyDataOperation.java | 8 +-- .../core/loot/function/ApplyDataFunction.java | 6 +- 7 files changed, 116 insertions(+), 39 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/item/modifier/ConditionalModifier.java 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 9921fb300..81b865041 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 @@ -91,30 +91,6 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } } - @SuppressWarnings("unchecked") - protected void applyDataModifiers(Map dataSection, Consumer> callback) { - ExceptionCollector errorCollector = new ExceptionCollector<>(); - if (dataSection != null) { - for (Map.Entry dataEntry : dataSection.entrySet()) { - Object value = dataEntry.getValue(); - if (value == null) continue; - String key = dataEntry.getKey(); - int idIndex = key.indexOf('#'); - if (idIndex != -1) { - key = key.substring(0, idIndex); - } - Optional.ofNullable(BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY.getValue(Key.withDefaultNamespace(key, Key.DEFAULT_NAMESPACE))).ifPresent(factory -> { - try { - callback.accept((ItemDataModifier) factory.create(value)); - } catch (LocalizedResourceConfigException e) { - errorCollector.add(e); - } - }); - } - } - errorCollector.throwIfPresent(); - } - @Override public ConfigParser[] parsers() { return new ConfigParser[]{this.itemParser, this.equipmentParser}; @@ -628,7 +604,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl // 应用物品数据 try { - applyDataModifiers(MiscUtils.castToMap(section.get("data"), true), itemBuilder::dataModifier); + ItemDataModifiers.applyDataModifiers(MiscUtils.castToMap(section.get("data"), true), itemBuilder::dataModifier); } catch (LocalizedResourceConfigException e) { collector.add(e); } @@ -636,7 +612,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl // 应用客户端侧数据 try { if (VersionHelper.PREMIUM) { - applyDataModifiers(MiscUtils.castToMap(section.get("client-bound-data"), true), itemBuilder::clientBoundDataModifier); + ItemDataModifiers.applyDataModifiers(MiscUtils.castToMap(section.get("client-bound-data"), true), itemBuilder::clientBoundDataModifier); } } catch (LocalizedResourceConfigException e) { collector.add(e); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ConditionalModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ConditionalModifier.java new file mode 100644 index 000000000..0929c753f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ConditionalModifier.java @@ -0,0 +1,66 @@ +package net.momirealms.craftengine.core.item.modifier; + +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.event.EventConditions; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.sparrow.nbt.CompoundTag; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +public class ConditionalModifier implements ItemDataModifier { + public static final Factory FACTORY = new Factory<>(); + private final Predicate condition; + private final ItemDataModifier[] modifiers; + + public ConditionalModifier(Predicate condition, ItemDataModifier[] modifiers) { + this.modifiers = modifiers; + this.condition = condition; + } + + @Override + public Key type() { + return ItemDataModifiers.CONDITIONAL; + } + + @Override + public Item apply(Item item, ItemBuildContext context) { + if (this.condition.test(context)) { + for (ItemDataModifier m : this.modifiers) { + item = item.apply(m, context); + } + } + return item; + } + + @Override + public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { + if (this.condition.test(context)) { + for (ItemDataModifier m : this.modifiers) { + item = m.prepareNetworkItem(item, context, networkData); + } + } + return item; + } + + public static class Factory implements ItemDataModifierFactory { + + @SuppressWarnings("unchecked") + @Override + public ItemDataModifier create(Object arg) { + Map conditionalData = ResourceConfigUtils.getAsMap(arg, "conditional"); + List> conditions = ResourceConfigUtils.parseConfigAsList(conditionalData.get("conditions"), EventConditions::fromMap); + List> modifiers = new ArrayList<>(); + ItemDataModifiers.applyDataModifiers(ResourceConfigUtils.getAsMap(conditionalData.get("data"), "conditional.data"), m -> modifiers.add((ItemDataModifier) m)); + return new ConditionalModifier<>(MiscUtils.allOf(conditions), modifiers.toArray(new ItemDataModifier[0])); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java index b0064ce8e..1360ebdd0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java @@ -4,13 +4,19 @@ import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.modifier.lore.DynamicLoreModifier; import net.momirealms.craftengine.core.item.modifier.lore.LoreModifier; import net.momirealms.craftengine.core.item.modifier.lore.OverwritableLoreModifier; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.ExceptionCollector; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; import net.momirealms.craftengine.core.util.VersionHelper; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; + public final class ItemDataModifiers { private ItemDataModifiers() {} @@ -51,6 +57,8 @@ public final class ItemDataModifiers { public static final Key OVERWRITABLE_LORE = Key.of("craftengine:overwritable-lore"); public static final Key MAX_DAMAGE = Key.of("craftengine:max-damage"); public static final Key BLOCK_STATE = Key.of("craftengine:block-state"); + public static final Key CONDITIONAL = Key.of("craftengine:conditional"); + public static final Key CONDITION = Key.of("craftengine:condition"); public static void register(Key key, ItemDataModifierFactory factory) { ((WritableRegistry>) BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY) @@ -61,6 +69,30 @@ public final class ItemDataModifiers { } } + @SuppressWarnings("unchecked") + public static void applyDataModifiers(Map dataSection, Consumer> callback) { + ExceptionCollector errorCollector = new ExceptionCollector<>(); + if (dataSection != null) { + for (Map.Entry dataEntry : dataSection.entrySet()) { + Object value = dataEntry.getValue(); + if (value == null) continue; + String key = dataEntry.getKey(); + int idIndex = key.indexOf('#'); + if (idIndex != -1) { + key = key.substring(0, idIndex); + } + Optional.ofNullable(BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY.getValue(Key.withDefaultNamespace(key, Key.DEFAULT_NAMESPACE))).ifPresent(factory -> { + try { + callback.accept((ItemDataModifier) factory.create(value)); + } catch (LocalizedResourceConfigException e) { + errorCollector.add(e); + } + }); + } + } + errorCollector.throwIfPresent(); + } + public static void init() {} static { @@ -106,5 +138,9 @@ public final class ItemDataModifiers { register(ITEM_MODEL, ItemModelModifier.FACTORY); register(EQUIPPABLE, EquippableModifier.FACTORY); } + if (VersionHelper.PREMIUM) { + register(CONDITIONAL, ConditionalModifier.FACTORY); + register(CONDITION, ConditionalModifier.FACTORY); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/OverwritableItemModelModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/OverwritableItemModelModifier.java index 9e54d05bc..a5d1d0d6d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/OverwritableItemModelModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/OverwritableItemModelModifier.java @@ -7,6 +7,9 @@ import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; + public class OverwritableItemModelModifier implements SimpleNetworkItemDataModifier { public static final Factory FACTORY = new Factory<>(); private final Key data; @@ -21,7 +24,7 @@ public class OverwritableItemModelModifier implements SimpleNetworkItemDataMo @Override public Key type() { - return ItemDataModifiers.OVERWRITABLE_CUSTOM_MODEL_DATA; + return ItemDataModifiers.OVERWRITABLE_ITEM_MODEL; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/PostProcessors.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/PostProcessors.java index b091523f5..ef46fabea 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/PostProcessors.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/PostProcessors.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.item.recipe.result; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; +import net.momirealms.craftengine.core.item.modifier.ItemDataModifiers; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Registries; @@ -22,10 +23,7 @@ public class PostProcessors { registerPostProcessorType(APPLY_DATA, args -> { List> modifiers = new ArrayList<>(); Map data = ResourceConfigUtils.getAsMap(args.get("data"), "data"); - for (Map.Entry entry : data.entrySet()) { - Optional.ofNullable(BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY.getValue(Key.withDefaultNamespace(entry.getKey(), Key.DEFAULT_NAMESPACE))) - .ifPresent(factory -> modifiers.add(factory.create(entry.getValue()))); - } + ItemDataModifiers.applyDataModifiers(data, modifiers::add); return new ApplyItemDataPostProcessor<>(modifiers.toArray(new ItemDataModifier[0])); }); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/updater/impl/ApplyDataOperation.java b/core/src/main/java/net/momirealms/craftengine/core/item/updater/impl/ApplyDataOperation.java index 53d06471d..137e793c9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/updater/impl/ApplyDataOperation.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/updater/impl/ApplyDataOperation.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.item.updater.impl; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; +import net.momirealms.craftengine.core.item.modifier.ItemDataModifiers; import net.momirealms.craftengine.core.item.updater.ItemUpdater; import net.momirealms.craftengine.core.item.updater.ItemUpdaterType; import net.momirealms.craftengine.core.registry.BuiltInRegistries; @@ -39,10 +40,9 @@ public class ApplyDataOperation implements ItemUpdater { public ItemUpdater create(Key item, Map args) { List> modifiers = new ArrayList<>(); Map data = ResourceConfigUtils.getAsMap(args.get("data"), "data"); - for (Map.Entry entry : data.entrySet()) { - Optional.ofNullable(BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY.getValue(Key.withDefaultNamespace(entry.getKey(), Key.DEFAULT_NAMESPACE))) - .ifPresent(factory -> modifiers.add((ItemDataModifier) factory.create(entry.getValue()))); - } + ItemDataModifiers.applyDataModifiers(data, m -> { + modifiers.add((ItemDataModifier) m); + }); return new ApplyDataOperation<>(modifiers); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyDataFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyDataFunction.java index 2d870d95f..d53e2f5bb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyDataFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyDataFunction.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.loot.function; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; +import net.momirealms.craftengine.core.item.modifier.ItemDataModifiers; import net.momirealms.craftengine.core.loot.LootConditions; import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.plugin.context.Condition; @@ -42,10 +43,7 @@ public class ApplyDataFunction extends AbstractLootConditionalFunction { public LootFunction create(Map arguments) { List> modifiers = new ArrayList<>(); Map data = ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("data"), "warning.config.loot_table.function.apply_data.missing_data"), "data"); - for (Map.Entry entry : data.entrySet()) { - Optional.ofNullable(BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY.getValue(Key.withDefaultNamespace(entry.getKey(), Key.DEFAULT_NAMESPACE))) - .ifPresent(factory -> modifiers.add(factory.create(entry.getValue()))); - } + ItemDataModifiers.applyDataModifiers(data, modifiers::add); List> conditions = Optional.ofNullable(arguments.get("conditions")) .map(it -> LootConditions.fromMapList((List>) it)) .orElse(Collections.emptyList()); From d840e669d72d7eb4b9dedfdc5d93abf874e93453 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 18 Dec 2025 20:32:12 +0800 Subject: [PATCH 13/13] =?UTF-8?q?=E6=9D=91=E6=B0=91=E4=BA=A4=E6=98=93?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=A7=A3=E6=9E=90=E7=8E=A9=E5=AE=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../function/MerchantTradeFunction.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MerchantTradeFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MerchantTradeFunction.java index 43ac9d0a6..48de8a308 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MerchantTradeFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MerchantTradeFunction.java @@ -25,9 +25,9 @@ import java.util.Optional; public class MerchantTradeFunction extends AbstractConditionalFunction { private final String title; private final PlayerSelector selector; - private final LazyReference>> offers; + private final java.util.function.Function>> offers; - public MerchantTradeFunction(List> predicates, @Nullable PlayerSelector selector, String title, LazyReference>> offers) { + public MerchantTradeFunction(List> predicates, @Nullable PlayerSelector selector, String title, java.util.function.Function>> offers) { super(predicates); this.title = title; this.selector = selector; @@ -38,12 +38,12 @@ public class MerchantTradeFunction extends AbstractConditio public void runInternal(CTX ctx) { if (this.selector == null) { ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> { - CraftEngine.instance().guiManager().openMerchant(it, this.title == null ? null : AdventureHelper.miniMessage().deserialize(this.title, ctx.tagResolvers()), this.offers.get()); + CraftEngine.instance().guiManager().openMerchant(it, this.title == null ? null : AdventureHelper.miniMessage().deserialize(this.title, ctx.tagResolvers()), this.offers.apply(it)); }); } else { for (Player viewer : this.selector.get(ctx)) { RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer)); - CraftEngine.instance().guiManager().openMerchant(viewer, this.title == null ? null : AdventureHelper.miniMessage().deserialize(this.title, relationalContext.tagResolvers()), this.offers.get()); + CraftEngine.instance().guiManager().openMerchant(viewer, this.title == null ? null : AdventureHelper.miniMessage().deserialize(this.title, relationalContext.tagResolvers()), this.offers.apply(viewer)); } } } @@ -71,27 +71,27 @@ public class MerchantTradeFunction extends AbstractConditio return new TempOffer(cost1, cost2, result, exp); }); return new MerchantTradeFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), title, - LazyReference.lazyReference(() -> { + (player) -> { List> offers = new ArrayList<>(merchantOffers.size()); for (TempOffer offer : merchantOffers) { - Item cost1 = parseIngredient(offer.cost1); - Optional cost2 = Optional.ofNullable(parseIngredient(offer.cost2)); - Item result = parseIngredient(offer.result); + Item cost1 = parseIngredient(offer.cost1, player); + Optional cost2 = Optional.ofNullable(parseIngredient(offer.cost2, player)); + Item result = parseIngredient(offer.result, player); offers.add(new MerchantOffer<>(cost1, cost2, result, false, 0, Integer.MAX_VALUE, offer.exp, 0, 0, 0)); } return offers; - })); + }); } public record TempOffer(Object cost1, Object cost2, Object result, int exp) { } - private Item parseIngredient(Object arguments) { + private Item parseIngredient(Object arguments, Player player) { if (arguments == null) return null; if (arguments instanceof Map map) { Map args = MiscUtils.castToMap(map, false); String itemName = args.getOrDefault("item", "minecraft:stone").toString(); - Item item = createSafeItem(itemName); + Item item = createSafeItem(itemName, player); if (args.containsKey("count")) { item.count(ResourceConfigUtils.getAsInt(args.get("count"), "count")); } @@ -104,15 +104,15 @@ public class MerchantTradeFunction extends AbstractConditio return item; } else { String itemName = arguments.toString(); - return createSafeItem(itemName); + return createSafeItem(itemName, player); } } - private Item createSafeItem(String itemName) { + private Item createSafeItem(String itemName, Player player) { Key itemId = Key.of(itemName); - Item item = CraftEngine.instance().itemManager().createWrappedItem(itemId, null); + Item item = CraftEngine.instance().itemManager().createWrappedItem(itemId, player); if (item == null) { - item = CraftEngine.instance().itemManager().createWrappedItem(ItemKeys.STONE, null); + item = CraftEngine.instance().itemManager().createWrappedItem(ItemKeys.STONE, player); assert item != null; item.itemNameComponent(Component.text(itemName).color(NamedTextColor.RED)); }