From 9b3c2e767d2d997e2382e5b76faff7d4dbcbe6e6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 20 Aug 2025 21:38:18 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E9=85=8D=E7=BD=AE=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=EF=BC=8C=E5=85=81=E8=AE=B8=E7=8A=B6=E6=80=81=E5=A4=9A?= =?UTF-8?q?=E7=BB=91=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/BukkitBlockManager.java | 275 ++---------------- .../plugin/command/feature/TestCommand.java | 13 - .../src/main/resources/translations/en.yml | 1 + .../src/main/resources/translations/zh_cn.yml | 1 + .../core/block/AbstractBlockManager.java | 75 ++++- .../core/block/AbstractCustomBlock.java | 15 +- .../craftengine/core/block/BlockManager.java | 2 - .../core/item/modifier/PDCModifier.java | 3 - .../core/util/ResourceConfigUtils.java | 11 + .../core/world/chunk/PalettedContainer.java | 5 +- 10 files changed, 119 insertions(+), 282 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java index 97d99df9c..d3c7709fd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java @@ -3,8 +3,6 @@ package net.momirealms.craftengine.bukkit.block; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; @@ -26,16 +24,7 @@ import net.momirealms.craftengine.bukkit.util.TagUtils; import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.block.behavior.EmptyBlockBehavior; import net.momirealms.craftengine.core.block.parser.BlockStateParser; -import net.momirealms.craftengine.core.block.properties.Properties; -import net.momirealms.craftengine.core.block.properties.Property; -import net.momirealms.craftengine.core.loot.LootTable; -import net.momirealms.craftengine.core.pack.LoadingSequence; -import net.momirealms.craftengine.core.pack.Pack; -import net.momirealms.craftengine.core.pack.ResourceLocation; -import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; -import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor; -import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -47,7 +36,6 @@ import net.momirealms.craftengine.core.world.chunk.PalettedContainer; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.Registry; import org.bukkit.block.data.BlockData; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; @@ -69,14 +57,12 @@ import java.util.function.Predicate; public final class BukkitBlockManager extends AbstractBlockManager { private static BukkitBlockManager instance; private final BukkitCraftEngine plugin; - private final BlockParser blockParser; + // The total amount of blocks registered private int customBlockCount; private ImmutableBlockState[] stateId2ImmutableBlockStates; // Minecraft objects // Cached new blocks $ holders - private Map internalId2StateId; - private Map registeredBlocks; private Map stateId2BlockHolder; // This map is used to change the block states that are not necessarily needed into a certain block state private Map blockAppearanceMapper; @@ -100,7 +86,6 @@ public final class BukkitBlockManager extends AbstractBlockManager { super(plugin); instance = this; this.plugin = plugin; - this.blockParser = new BlockParser(); this.initVanillaRegistry(); this.loadMappingsAndAdditionalBlocks(); this.registerBlocks(); @@ -230,12 +215,7 @@ public final class BukkitBlockManager extends AbstractBlockManager { } @Override - public ConfigParser parser() { - return this.blockParser; - } - - @Override - public void addBlock(Key id, CustomBlock customBlock) { + public void addBlockInternal(Key id, CustomBlock customBlock) { // bind appearance and real state for (ImmutableBlockState state : customBlock.variantProvider().states()) { ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()]; @@ -246,7 +226,7 @@ public final class BukkitBlockManager extends AbstractBlockManager { this.tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId()); this.appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new IntArrayList()).add(state.customBlockState().registryId()); } - super.addBlock(id, customBlock); + super.addBlockInternal(id, customBlock); } @Override @@ -254,6 +234,11 @@ public final class BukkitBlockManager extends AbstractBlockManager { return BlockStateUtils.getBlockOwnerIdFromState(state.handle()); } + @Override + protected Key getBlockOwnerId(int id) { + return BlockStateUtils.getBlockOwnerIdFromState(BlockStateUtils.idToBlockState(id)); + } + @Override public int availableAppearances(Key blockType) { return Optional.ofNullable(this.registeredRealBlockSlots.get(blockType)).orElse(0); @@ -300,6 +285,11 @@ public final class BukkitBlockManager extends AbstractBlockManager { BlockStateUtils.init(vanillaStateCount); } + @Override + protected CustomBlock.Builder platformBuilder(Key id) { + return BukkitCustomBlock.builder(id); + } + @SuppressWarnings("unchecked") private void registerBlocks() { this.plugin.logger().info("Registering blocks. Please wait..."); @@ -406,225 +396,6 @@ public final class BukkitBlockManager extends AbstractBlockManager { return cachedUpdateTagsPacket; } - public class BlockParser implements ConfigParser { - public static final String[] CONFIG_SECTION_NAME = new String[]{"blocks", "block"}; - - @Override - public String[] sectionId() { - return CONFIG_SECTION_NAME; - } - - @Override - public int loadingSequence() { - return LoadingSequence.BLOCK; - } - - @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { - if (id.namespace().equals("minecraft") && Registry.MATERIAL.get(KeyUtils.toNamespacedKey(id)) != null) { - parseVanillaBlock(pack, path, id, section); - } else { - // check duplicated config - if (BukkitBlockManager.this.byId.containsKey(id)) { - throw new LocalizedResourceConfigException("warning.config.block.duplicate"); - } - parseCustomBlock(pack, path, id, section); - } - } - - private void parseCustomBlock(Pack pack, Path path, Key id, Map section) { - // read block settings - BlockSettings settings = BlockSettings.fromMap(id, MiscUtils.castToMap(section.get("settings"), true)); - // read states - Map> properties; - Map appearances; - Map variants; - - Map stateSection = MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "state", "states"), "warning.config.block.missing_state"), true); - boolean singleState = !stateSection.containsKey("properties"); - // single state - if (singleState) { - properties = Map.of(); - int internalId = ResourceConfigUtils.getAsInt(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("id"), "warning.config.block.state.missing_real_id"), "id"); - VanillaBlockState vanillaBlock = getVanillaBlock(id, stateSection); - appearances = Map.of("", vanillaBlock.registryId()); - Key internalBlockId = Key.of(Key.DEFAULT_NAMESPACE, vanillaBlock.type().value() + "_" + internalId); - int internalBlockRegistryId = Optional.ofNullable(internalId2StateId.get(internalBlockId)).orElse(-1); - if (internalBlockRegistryId == -1) { - throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", internalBlockId.toString(), String.valueOf(availableAppearances(vanillaBlock.type()) - 1)); - } - variants = Map.of("", new BlockStateVariant("", settings, internalBlockRegistryId)); - } else { - // properties - properties = getProperties(MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("properties"), "warning.config.block.state.missing_properties"), true)); - // appearance - appearances = new HashMap<>(); - Map appearance2BlockType = new HashMap<>(); - for (Map.Entry appearanceEntry : MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("appearances"), "warning.config.block.state.missing_appearances"), false).entrySet()) { - if (appearanceEntry.getValue() instanceof Map) { - VanillaBlockState vanillaBlock = getVanillaBlock(id, MiscUtils.castToMap(appearanceEntry.getValue(), false)); - appearances.put(appearanceEntry.getKey(), vanillaBlock.registryId()); - appearance2BlockType.put(appearanceEntry.getKey(), vanillaBlock.type()); - } - } - // variants - variants = new HashMap<>(); - for (Map.Entry variantEntry : MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("variants"), "warning.config.block.state.missing_variants"), false).entrySet()) { - if (variantEntry.getValue() instanceof Map) { - Map variantSection = MiscUtils.castToMap(variantEntry.getValue(), false); - String variantNBT = variantEntry.getKey(); - String appearance = ResourceConfigUtils.requireNonEmptyStringOrThrow(variantSection.get("appearance"), "warning.config.block.state.variant.missing_appearance"); - if (!appearances.containsKey(appearance)) { - throw new LocalizedResourceConfigException("warning.config.block.state.variant.invalid_appearance", variantNBT, appearance); - } - int internalId = ResourceConfigUtils.getAsInt(ResourceConfigUtils.requireNonNullOrThrow(variantSection.get("id"), "warning.config.block.state.missing_real_id"), "id"); - Key baseBlock = appearance2BlockType.get(appearance); - Key internalBlockId = Key.of(Key.DEFAULT_NAMESPACE, baseBlock.value() + "_" + internalId); - int internalBlockRegistryId = Optional.ofNullable(internalId2StateId.get(internalBlockId)).orElse(-1); - if (internalBlockRegistryId == -1) { - throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", internalBlockId.toString(), String.valueOf(availableAppearances(baseBlock) - 1)); - } - Map anotherSetting = MiscUtils.castToMap(variantSection.get("settings"), true); - variants.put(variantNBT, new BlockStateVariant(appearance, anotherSetting == null ? settings : BlockSettings.ofFullCopy(settings, anotherSetting), internalBlockRegistryId)); - } - } - } - - CustomBlock block = BukkitCustomBlock.builder(id) - .appearances(appearances) - .variantMapper(variants) - .properties(properties) - .settings(settings) - .lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true))) - .behavior(MiscUtils.getAsMapList(ResourceConfigUtils.get(section, "behavior", "behaviors"))) - .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) - .build(); - - addBlock(id, block); - } - - private void parseVanillaBlock(Pack pack, Path path, Key id, Map section) { - Map settings = MiscUtils.castToMap(section.get("settings"), true); - if (settings != null) { - Object clientBoundTags = settings.get("client-bound-tags"); - if (clientBoundTags instanceof List list) { - List clientSideTags = MiscUtils.getAsStringList(list).stream().filter(ResourceLocation::isValid).toList(); - Object nmsBlock = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(id)); - FastNMS.INSTANCE.method$IdMap$getId(MBuiltInRegistries.BLOCK, nmsBlock).ifPresent(i -> - BukkitBlockManager.this.clientBoundTags.put(i, clientSideTags)); - } - } - } - } - - @NotNull - private Map> getProperties(Map propertiesSection) { - Map> properties = new HashMap<>(); - for (Map.Entry entry : propertiesSection.entrySet()) { - Property property = Properties.fromMap(entry.getKey(), MiscUtils.castToMap(entry.getValue(), false)); - properties.put(entry.getKey(), property); - } - return properties; - } - - @NotNull - private VanillaBlockState getVanillaBlock(Key id, Map section) { - // require state non null - String vanillaBlockStateTag = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("state"), "warning.config.block.state.missing_state"); - // get its registry id - int vanillaBlockStateRegistryId = getVanillaBlockStateRegistryId(vanillaBlockStateTag); - // check if another block has occupied the appearance - // TODO blocks share the same look - Key ifAny = this.tempRegistryIdConflictMap.get(vanillaBlockStateRegistryId); - if (ifAny != null && !ifAny.equals(id)) { - throw new LocalizedResourceConfigException("warning.config.block.state.conflict", BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(vanillaBlockStateRegistryId)).getAsString(), ifAny.toString()); - } - // require models not to be null - Object models = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "models", "model"), "warning.config.block.state.missing_model"); - List variants = ResourceConfigUtils.parseConfigAsList(models, this::getVariantModel); - if (variants.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.block.state.missing_model"); - } - // TODO blocks share the same look - this.tempRegistryIdConflictMap.put(vanillaBlockStateRegistryId, id); - // gets the full block state - String blockState = BlockStateUtils.idToBlockState(vanillaBlockStateRegistryId).toString(); - Key blockId = Key.of(blockState.substring(blockState.indexOf('{') + 1, blockState.lastIndexOf('}'))); - String propertyNBT = blockState.substring(blockState.indexOf('[') + 1, blockState.lastIndexOf(']')); - // for generating assets - JsonElement combinedVariant = GsonHelper.combine(variants); - this.blockStateOverrides.computeIfAbsent(blockId, k -> new HashMap<>()).put(propertyNBT, combinedVariant); - this.tempVanillaBlockStateModels.put(vanillaBlockStateRegistryId, combinedVariant); - return new VanillaBlockState(blockId, propertyNBT, vanillaBlockStateRegistryId); - } - - public record VanillaBlockState(Key type, String properties, int registryId) { - } - - private JsonObject getVariantModel(Map singleModelMap) { - JsonObject json = new JsonObject(); - String modelPath = ResourceConfigUtils.requireNonEmptyStringOrThrow(singleModelMap.get("path"), "warning.config.block.state.model.missing_path"); - if (!ResourceLocation.isValid(modelPath)) { - throw new LocalizedResourceConfigException("warning.config.block.state.model.invalid_path", modelPath); - } - json.addProperty("model", modelPath); - if (singleModelMap.containsKey("x")) - json.addProperty("x", ResourceConfigUtils.getAsInt(singleModelMap.get("x"), "x")); - if (singleModelMap.containsKey("y")) - json.addProperty("y", ResourceConfigUtils.getAsInt(singleModelMap.get("y"), "y")); - if (singleModelMap.containsKey("uvlock")) json.addProperty("uvlock", ResourceConfigUtils.getAsBoolean(singleModelMap.get("uvlock"), "uvlock")); - if (singleModelMap.containsKey("weight")) - json.addProperty("weight", ResourceConfigUtils.getAsInt(singleModelMap.get("weight"), "weight")); - Map generationMap = MiscUtils.castToMap(singleModelMap.get("generation"), true); - if (generationMap != null) { - prepareModelGeneration(ModelGeneration.of(Key.of(modelPath), generationMap)); - } - return json; - } - - private int getVanillaBlockStateRegistryId(String blockState) { - String[] split = blockState.split(":", 3); - if (split.length >= 4) { - throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); - } - int registryId; - String stateOrId = split[split.length - 1]; - boolean isId = !stateOrId.contains("[") && !stateOrId.contains("]"); - if (isId) { - if (split.length == 1) { - throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); - } - Key block = split.length == 2 ? Key.of(split[0]) : Key.of(split[0], split[1]); - try { - int id = split.length == 2 ? Integer.parseInt(split[1]) : Integer.parseInt(split[2]); - if (id < 0) { - throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); - } - List arranger = this.blockAppearanceArranger.get(block); - if (arranger == null) { - throw new LocalizedResourceConfigException("warning.config.block.state.unavailable_vanilla", blockState); - } - if (id >= arranger.size()) { - throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla_id", blockState, String.valueOf(arranger.size() - 1)); - } - registryId = arranger.get(id); - } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", e, blockState); - } - } else { - try { - BlockData blockData = Bukkit.createBlockData(blockState); - registryId = BlockStateUtils.blockDataToId(blockData); - if (!this.blockAppearanceMapper.containsKey(registryId)) { - throw new LocalizedResourceConfigException("warning.config.block.state.unavailable_vanilla", blockState); - } - } catch (IllegalArgumentException e) { - throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", e, blockState); - } - } - return registryId; - } - private void loadMappingsAndAdditionalBlocks() { this.plugin.logger().info("Loading mappings.yml."); Path mappingsFile = this.plugin.dataFolderPath().resolve("mappings.yml"); @@ -762,13 +533,19 @@ public final class BukkitBlockManager extends AbstractBlockManager { } private LinkedHashMap buildRegisteredRealBlockSlots(Map counter, Map additionalYaml) { - LinkedHashMap map = new LinkedHashMap<>(); - for (Map.Entry entry : counter.entrySet()) { - String id = entry.getKey().toString(); - int additionalStates = (int) additionalYaml.getOrDefault(id, 0); - int internalIds = entry.getValue() + additionalStates; - plugin.logger().info("Loaded " + id + " with " + entry.getValue() + " appearances and " + internalIds + " real block states"); - map.put(entry.getKey(), internalIds); + LinkedHashMap map = new LinkedHashMap<>(counter); + for (Map.Entry entry : additionalYaml.entrySet()) { + Key blockType = Key.of(entry.getKey()); + if (entry.getValue() instanceof Integer i) { + int previous = map.getOrDefault(blockType, 0); + if (previous == 0) { + map.put(blockType, i); + this.plugin.logger().info("Loaded " + blockType + " with " + i + " real block states"); + } else { + map.put(blockType, i + previous); + this.plugin.logger().info("Loaded " + blockType + " with " + previous + " appearances and " + (i + previous) + " real block states"); + } + } } return map; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java index bb3ef7aaf..e5f42e169 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java @@ -2,27 +2,14 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; -import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.block.parser.BlockStateParser; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import org.bukkit.Location; import org.bukkit.block.data.BlockData; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Minecart; import org.bukkit.entity.Player; -import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; -import org.incendo.cloud.bukkit.parser.location.LocationParser; -import org.incendo.cloud.context.CommandContext; -import org.incendo.cloud.context.CommandInput; import org.incendo.cloud.parser.standard.IntegerParser; -import org.incendo.cloud.parser.standard.StringParser; -import org.incendo.cloud.suggestion.Suggestion; -import org.incendo.cloud.suggestion.SuggestionProvider; - -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; public class TestCommand extends BukkitCommandFeature { diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 213fa69b8..c15db1c61 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -267,6 +267,7 @@ warning.config.block.state.bind_failed: "Issue found in file - T warning.config.block.state.invalid_real_id: "Issue found in file - The block '' is using a real block state '' that exceeds the available slot range '0~'. Consider adding more real states in 'additional-real-blocks.yml' if the slots are used up." warning.config.block.state.model.missing_path: "Issue found in file - The block '' is missing the required 'path' option for 'model'." warning.config.block.state.model.invalid_path: "Issue found in file - The block '' has a 'path' argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.block.state.model.conflict: "Issue found in file - The block '' is trying to bind model '' to block state '' which has already been bound to model ''" warning.config.block.settings.unknown: "Issue found in file - The block '' is using an unknown setting type ''." warning.config.block.behavior.missing_type: "Issue found in file - The block '' is missing the required 'type' argument for its block behavior." warning.config.block.behavior.invalid_type: "Issue found in file - The block '' is using an invalid block behavior type ''." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index f67146759..348ab6b22 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -267,6 +267,7 @@ warning.config.block.state.bind_failed: "在文件 发现问题 warning.config.block.state.invalid_real_id: "在文件 发现问题 - 方块 '' 使用的真实方块状态 '' 超出可用槽位范围 '0~' 如果槽位已用尽 请在 additional-real-blocks.yml 中添加更多真实状态" warning.config.block.state.model.missing_path: "在文件 发现问题 - 方块 '' 的 'model' 缺少必需的 'path' 选项" warning.config.block.state.model.invalid_path: "在文件 发现问题 - 方块 '' 的 'path' 参数 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" +warning.config.block.state.model.conflict: "在文件 发现问题 - 方块 '' 正尝试将模型 '' 绑定到方块状态 '' 上, 但是此状态已绑定了另一个模型 ''" warning.config.block.settings.unknown: "在文件 发现问题 - 方块 '' 使用了未知的设置类型 ''" warning.config.block.behavior.missing_type: "在文件 发现问题 - 方块 '' 的行为配置缺少必需的 'type' 参数" warning.config.block.behavior.invalid_type: "在文件 发现问题 - 方块 '' 使用了无效的行为类型 ''" diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java index 70dfad312..8b1f9e59d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java @@ -6,6 +6,7 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.momirealms.craftengine.core.block.properties.Properties; import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.ResourceLocation; @@ -14,6 +15,7 @@ import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; @@ -24,8 +26,10 @@ import org.jetbrains.annotations.NotNull; import java.nio.file.Path; import java.util.*; +import java.util.function.Function; public abstract class AbstractBlockManager extends AbstractModelGenerator implements BlockManager { + protected final BlockParser blockParser; // CraftEngine objects protected final Map byId = new HashMap<>(); // Cached command suggestions @@ -44,15 +48,19 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem protected final Map> blockStateOverrides = new HashMap<>(); // a reverted mapper protected final Map> appearanceToRealState = new Int2ObjectOpenHashMap<>(); + // client side block tags protected Map> clientBoundTags = Map.of(); protected Map> previousClientBoundTags = Map.of(); // Used to automatically arrange block states for strings such as minecraft:note_block:0 protected Map> blockAppearanceArranger; protected Map> realBlockArranger; + protected Map internalId2StateId; + protected Map registeredBlocks; protected AbstractBlockManager(CraftEngine plugin) { super(plugin); + this.blockParser = new BlockParser(); } @Override @@ -85,8 +93,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem return Optional.ofNullable(this.byId.get(id)); } - @Override - public void addBlock(Key id, CustomBlock customBlock) { + protected void addBlockInternal(Key id, CustomBlock customBlock) { this.byId.put(id, customBlock); // generate mod assets if (Config.generateModAssets()) { @@ -96,6 +103,11 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } } + @Override + public ConfigParser parser() { + return this.blockParser; + } + @Override public Map modBlockStates() { return Collections.unmodifiableMap(this.modBlockStates); @@ -148,7 +160,11 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem protected abstract int getBlockRegistryId(Key id); - public abstract String stateRegistryIdToStateSNBT(int id); + protected abstract String stateRegistryIdToStateSNBT(int id); + + protected abstract Key getBlockOwnerId(int id); + + protected abstract CustomBlock.Builder platformBuilder(Key id); public class BlockParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[]{"blocks", "block"}; @@ -210,14 +226,56 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem // 设置参数 properties = Map.of(); appearances = Map.of("", appearanceId); - variants = Map.of("", new BlockStateVariant("", settings, internalId)); + variants = Map.of("", new BlockStateVariant("", settings, getInternalBlockId(internalId, appearanceId))); } // 多方块状态 else { properties = parseBlockProperties(ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("properties"), "warning.config.block.state.missing_properties"), "properties")); appearances = parseBlockAppearances(ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("appearances"), "warning.config.block.state.missing_appearances"), "appearances")); - + variants = parseBlockVariants( + ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("variants"), "warning.config.block.state.missing_variants"), "variants"), + it -> appearances.getOrDefault(it, -1), settings + ); } + + addBlockInternal(id, platformBuilder(id) + .appearances(appearances) + .variantMapper(variants) + .properties(properties) + .settings(settings) + .lootTable(LootTable.fromMap(ResourceConfigUtils.getAsMapOrNull(section.get("loot"), "loot"))) + .behavior(MiscUtils.getAsMapList(ResourceConfigUtils.get(section, "behavior", "behaviors"))) + .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) + .build()); + } + + private Map parseBlockVariants(Map variantsSection, + Function appearanceValidator, + BlockSettings parentSettings) { + Map variants = new HashMap<>(); + for (Map.Entry entry : variantsSection.entrySet()) { + Map variantSection = ResourceConfigUtils.getAsMap(entry.getValue(), entry.getKey()); + String variantNBT = entry.getKey(); + String appearance = ResourceConfigUtils.requireNonEmptyStringOrThrow(variantSection.get("appearance"), "warning.config.block.state.variant.missing_appearance"); + int appearanceId = appearanceValidator.apply(appearance); + if (appearanceId == -1) { + throw new LocalizedResourceConfigException("warning.config.block.state.variant.invalid_appearance", variantNBT, appearance); + } + int internalId = getInternalBlockId(ResourceConfigUtils.getAsInt(ResourceConfigUtils.requireNonNullOrThrow(variantSection.get("id"), "warning.config.block.state.missing_real_id"), "id"), appearanceId); + Map anotherSetting = ResourceConfigUtils.getAsMapOrNull(variantSection.get("settings"), "settings"); + variants.put(variantNBT, new BlockStateVariant(appearance, anotherSetting == null ? parentSettings : BlockSettings.ofFullCopy(parentSettings, anotherSetting), internalId)); + } + return variants; + } + + private int getInternalBlockId(int internalId, int appearanceId) { + Key baseBlock = getBlockOwnerId(appearanceId); + Key internalBlockId = Key.of(Key.DEFAULT_NAMESPACE, baseBlock.value() + "_" + internalId); + int internalBlockRegistryId = Optional.ofNullable(AbstractBlockManager.this.internalId2StateId.get(internalBlockId)).orElse(-1); + if (internalBlockRegistryId == -1) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", internalBlockId.toString(), String.valueOf(availableAppearances(baseBlock) - 1)); + } + return internalBlockRegistryId; } private Map parseBlockAppearances(Map appearancesSection) { @@ -265,11 +323,12 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem String propertyNBT = blockState.substring(blockState.indexOf('[') + 1, blockState.lastIndexOf(']')); // 结合variants JsonElement combinedVariant = GsonHelper.combine(variants); - JsonElement previous = AbstractBlockManager.this.blockStateOverrides.computeIfAbsent(blockId, k -> new HashMap<>()).put(propertyNBT, combinedVariant); + Map overrideMap = AbstractBlockManager.this.blockStateOverrides.computeIfAbsent(blockId, k -> new HashMap<>()); + JsonElement previous = overrideMap.get(propertyNBT); if (previous != null && !previous.equals(combinedVariant)) { - // todo 播报可能的冲突 - plugin.logger().warn("warning 1"); + throw new LocalizedResourceConfigException("warning.config.block.state.model.conflict", GsonHelper.get().toJson(combinedVariant), blockState, GsonHelper.get().toJson(previous)); } + overrideMap.put(propertyNBT, combinedVariant); } private JsonObject parseAppearanceModelSectionAsJson(Map section) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java index cc2c8b02e..e524b86c9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java @@ -64,6 +64,10 @@ public abstract class AbstractCustomBlock implements CustomBlock { if (tag == null) { throw new LocalizedResourceConfigException("warning.config.block.state.property.invalid_format", nbtString); } + List possibleStates = this.getPossibleStates(tag); + if (possibleStates.size() != 1) { + throw new LocalizedResourceConfigException("warning.config.block.state.property.invalid_format", nbtString); + } BlockStateVariant blockStateVariant = entry.getValue(); int vanillaStateRegistryId = appearances.getOrDefault(blockStateVariant.appearance(), -1); // This should never happen @@ -71,15 +75,14 @@ public abstract class AbstractCustomBlock implements CustomBlock { vanillaStateRegistryId = appearances.values().iterator().next(); } // Late init states - for (ImmutableBlockState state : this.getPossibleStates(tag)) { - state.setBehavior(this.behavior); - state.setSettings(blockStateVariant.settings()); - state.setVanillaBlockState((BlockStateWrapper.VanillaBlockState) BlockRegistryMirror.stateByRegistryId(vanillaStateRegistryId)); - state.setCustomBlockState((BlockStateWrapper.CustomBlockState) BlockRegistryMirror.stateByRegistryId(blockStateVariant.internalRegistryId())); - } + ImmutableBlockState state = possibleStates.getFirst(); + state.setSettings(blockStateVariant.settings()); + state.setVanillaBlockState((BlockStateWrapper.VanillaBlockState) BlockRegistryMirror.stateByRegistryId(vanillaStateRegistryId)); + state.setCustomBlockState((BlockStateWrapper.CustomBlockState) BlockRegistryMirror.stateByRegistryId(blockStateVariant.internalRegistryId())); } // double check if there's any invalid state for (ImmutableBlockState state : this.variantProvider().states()) { + state.setBehavior(this.behavior); if (state.settings() == null) { state.setSettings(settings); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java index 0319ecfd9..6b69f04ee 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java @@ -28,8 +28,6 @@ public interface BlockManager extends Manageable, ModelGenerator { Optional blockById(Key key); - void addBlock(Key id, CustomBlock customBlock); - Collection cachedSuggestions(); Map soundMapper(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/PDCModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/PDCModifier.java index b6a52e91d..d5cfdd402 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/PDCModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/PDCModifier.java @@ -6,13 +6,10 @@ import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; -import java.util.List; import java.util.Map; import java.util.Optional; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java index d0d69ebc7..9f240f7dd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java @@ -212,4 +212,15 @@ public final class ResourceConfigUtils { } throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option); } + + @SuppressWarnings("unchecked") + public static Map getAsMapOrNull(Object obj, String option) { + if (obj == null) { + return null; + } + if (obj instanceof Map map) { + return (Map) map; + } + throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java index e5f704471..211addb11 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java @@ -16,7 +16,10 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.IntUnaryOperator; +import java.util.function.Predicate; import java.util.stream.LongStream; public class PalettedContainer implements PaletteResizeListener, ReadableContainer {