diff --git a/bukkit/loader/src/main/resources/translations/en.yml b/bukkit/loader/src/main/resources/translations/en.yml index ef65e98c7..760faf424 100644 --- a/bukkit/loader/src/main/resources/translations/en.yml +++ b/bukkit/loader/src/main/resources/translations/en.yml @@ -82,10 +82,19 @@ warning.config.item.custom_model_data_conflict: "Issue found in file Issue found in file - The item '' is missing the required 'custom-model-data' or 'item-model' argument." warning.config.block.duplicated: "Issue found in file - Duplicated block ''." warning.config.block.lack_state: "Issue found in file - The block '' is missing the required 'state' argument." -warning.config.block.lack_real_id: "Issue found in file - The block '' is missing the required 'id' argument for 'state'." +warning.config.block.state.lack_real_id: "Issue found in file - The block '' is missing the required 'id' argument for 'state'." warning.config.block.state.lack_state: "Issue found in file - The block '' is missing the required 'state' argument for 'state'." warning.config.block.state.lack_properties: "Issue found in file - The block '' is missing the required 'properties' section for 'states'." warning.config.block.state.lack_appearances: "Issue found in file - The block '' is missing the required 'appearances' section for 'states'." warning.config.block.state.lack_variants: "Issue found in file - The block '' is missing the required 'variants' section for 'states'." warning.config.block.state.variant.lack_appearance: "Issue found in file - The block '' is missing the required 'appearance' argument for variant ''." -warning.config.block.state.variant.invalid_appearance: "Issue found in file - The block '' has an error that the variant '' is using a non-existing appearance ''." \ No newline at end of file +warning.config.block.state.variant.invalid_appearance: "Issue found in file - The block '' has an error that the variant '' is using a non-existing appearance ''." +warning.config.block.state.invalid_state: "Issue found in file - The block '' is using an invalid vanilla block state ''." +warning.config.block.state.unavailable_state: "Issue found in file - The block '' is using an unavailable vanilla block state ''." +warning.config.block.state.invalid_vanilla_state_id: "Issue found in file - The block '' is using a vanilla block state '' that exceeds the available slot range '0~'." +warning.config.block.state.conflict: "Issue found in file - The block '' is using a vanilla block state '' that has been occupied by ''." +warning.config.block.bind_real_state: "Issue found in file - The block '' failed to bind real block state for '' as the state has been occupied by ''." +warning.config.block.state.invalid_property_structure: "Issue found in file - The block '' has an invalid property structure ''." +warning.config.block.state.invalid_property: "Issue found in file - Failed to create property '' for block '': ." +warning.config.block.state.no_model_set: "Issue found in file - The block '' is missing the required 'model' or 'models' argument." +warning.config.block.state.invalid_real_state_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." \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/translations/zh_cn.yml b/bukkit/loader/src/main/resources/translations/zh_cn.yml index 9e458de6c..0cab6ac5d 100644 --- a/bukkit/loader/src/main/resources/translations/zh_cn.yml +++ b/bukkit/loader/src/main/resources/translations/zh_cn.yml @@ -82,7 +82,7 @@ warning.config.item.custom_model_data_conflict: "在文件 中 warning.config.item.lack_model_id: "在文件 中发现问题 - 物品 '' 缺少必要的 'custom-model-data' 或 'item-model' 模型标识参数" warning.config.block.duplicated: "在文件 中发现问题 - 方块 '' 重复定义" warning.config.block.lack_state: "在文件 中发现问题 - 方块 '' 缺少必要的 'state' 状态参数" -warning.config.block.lack_real_id: "在文件 中发现问题 - 方块 '' 的 'state' 配置缺少必要的 'id' 标识参数" +warning.config.block.state.lack_real_id: "在文件 中发现问题 - 方块 '' 的 'state' 配置缺少必要的 'id' 标识参数" warning.config.block.state.lack_state: "在文件 中发现问题 - 方块 '' 的 'state' 配置缺少必要的 'state' 状态参数" warning.config.block.state.lack_properties: "在文件 中发现问题 - 方块 '' 的 'states' 配置缺少必要的 'properties' 属性配置" warning.config.block.state.lack_appearances: "在文件 中发现问题 - 方块 '' 的 'states' 配置缺少必要的 'appearances' 外观配置" diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java index 61d50a512..b7bbe8e5a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java @@ -37,7 +37,7 @@ public final class CraftEngineBlocks { */ @Nullable public static CustomBlock byId(@NotNull Key id) { - return BukkitBlockManager.instance().getBlock(id).orElse(null); + return BukkitBlockManager.instance().blockById(id).orElse(null); } /** 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 95bfdcf33..c62766b00 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 @@ -36,7 +36,6 @@ import org.bukkit.NamespacedKey; import org.bukkit.block.data.BlockData; import org.bukkit.event.HandlerList; import org.bukkit.inventory.ItemStack; -import org.incendo.cloud.suggestion.Suggestion; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; @@ -59,11 +58,7 @@ public class BukkitBlockManager extends AbstractBlockManager { // The total amount of blocks registered private int customBlockCount; - - // CraftEngine objects - private final Map byId = new HashMap<>(); - private final ImmutableBlockState[] stateId2ImmutableBlockStates; - + protected final ImmutableBlockState[] stateId2ImmutableBlockStates; // Minecraft objects // Cached new blocks $ holders private ImmutableMap internalId2StateId; @@ -87,14 +82,9 @@ public class BukkitBlockManager extends AbstractBlockManager { private final Map> blockStateOverrides = new HashMap<>(); // for mod, real block id -> state models private final Map modBlockStates = new HashMap<>(); - // Cached command suggestions - private final List cachedSuggestions = new ArrayList<>(); - // Cached Namespace - private final Set namespacesInUse = new HashSet<>(); // Event listeners private final BlockEventListener blockEventListener; private final FallingBlockRemoveListener fallingBlockRemoveListener; - private WorldEditCommandHelper weCommandHelper; public BukkitBlockManager(BukkitCraftEngine plugin) { @@ -106,7 +96,7 @@ public class BukkitBlockManager extends AbstractBlockManager { if (plugin.hasMod() && plugin.requiresRestart()) { blockEventListener = null; fallingBlockRemoveListener = null; - stateId2ImmutableBlockStates = null; + stateId2ImmutableBlockStates = new ImmutableBlockState[]{}; return; } this.registerBlocks(); @@ -152,11 +142,9 @@ public class BukkitBlockManager extends AbstractBlockManager { @Override public void unload() { - super.clearModelsToGenerate(); + super.unload(); this.clearCache(); this.appearanceToRealState.clear(); - this.byId.clear(); - this.cachedSuggestions.clear(); this.blockStateOverrides.clear(); this.modBlockStates.clear(); if (EmptyBlock.INSTANCE != null) @@ -236,41 +224,6 @@ public class BukkitBlockManager extends AbstractBlockManager { return Collections.unmodifiableMap(this.blockStateOverrides); } - @Override - public Map blocks() { - return Collections.unmodifiableMap(this.byId); - } - - @Override - public Optional getBlock(Key key) { - return Optional.ofNullable(this.byId.get(key)); - } - - @Override - public Collection cachedSuggestions() { - return Collections.unmodifiableCollection(this.cachedSuggestions); - } - - private void initSuggestions() { - this.cachedSuggestions.clear(); - this.namespacesInUse.clear(); - Set states = new HashSet<>(); - for (CustomBlock block : this.byId.values()) { - states.add(block.id().toString()); - this.namespacesInUse.add(block.id().namespace()); - for (ImmutableBlockState state : block.variantProvider().states()) { - states.add(state.toString()); - } - } - for (String state : states) { - this.cachedSuggestions.add(Suggestion.suggestion(state)); - } - } - - public Set namespacesInUse() { - return Collections.unmodifiableSet(this.namespacesInUse); - } - public ImmutableMap> blockAppearanceArranger() { return blockAppearanceArranger; } @@ -392,11 +345,11 @@ public class BukkitBlockManager extends AbstractBlockManager { @Override public void parseSection(Pack pack, Path path, Key id, Map section) { + // check duplicated config if (byId.containsKey(id)) { TranslationManager.instance().log("warning.config.block.duplicated", path.toString(), id.toString()); return; } - // read block settings BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.getOrDefault("settings", Map.of()), false)); // read loot table @@ -410,38 +363,46 @@ public class BukkitBlockManager extends AbstractBlockManager { properties = Map.of(); int internalId = MiscUtils.getAsInt(stateSection.getOrDefault("id", -1)); if (internalId < 0) { - TranslationManager.instance().log("warning.config.block.lack_real_id", path.toString(), id.toString()); + TranslationManager.instance().log("warning.config.block.state.lack_real_id", path.toString(), id.toString()); return; } + Pair pair = parseAppearanceSection(path, id, stateSection); if (pair == null) return; + appearances = Map.of("default", pair.right()); Key internalBlockId = Key.of(CraftEngine.NAMESPACE, pair.left().value() + "_" + internalId); int internalBlockRegistryId = MiscUtils.getAsInt(internalId2StateId.getOrDefault(internalBlockId, -1)); if (internalBlockRegistryId == -1) { - plugin.logger().warn(path, "Failed to register " + id + " because id " + internalId + " is not a value between 0~" + (MiscUtils.getAsInt(registeredRealBlockSlots.get(pair.left()))-1) + - ". Consider editing additional-real-blocks.yml if the number of real block IDs is insufficient while there are still available appearances"); + TranslationManager.instance().log("warning.config.block.state.invalid_real_state_id", + path.toString(), + id.toString(), + pair.left().value() + "_" + internalId, + String.valueOf(MiscUtils.getAsInt(registeredRealBlockSlots.get(pair.left()))-1) + ); return; } variants = Map.of("", new VariantState("default", settings, internalBlockRegistryId)); } else { + // states Map statesSection = MiscUtils.castToMap(section.get("states"), true); if (statesSection == null) { TranslationManager.instance().log("warning.config.block.lack_state", path.toString(), id.toString()); return; } + // properties Map propertySection = MiscUtils.castToMap(statesSection.get("properties"), true); if (propertySection == null) { TranslationManager.instance().log("warning.config.block.state.lack_properties", path.toString(), id.toString()); return; } - properties = parseProperties(path, propertySection); + properties = parseProperties(path, id, propertySection); + // appearance Map appearancesSection = MiscUtils.castToMap(statesSection.get("appearances"), true); if (appearancesSection == null) { TranslationManager.instance().log("warning.config.block.state.lack_appearances", path.toString(), id.toString()); return; } - appearances = new HashMap<>(); Map tempTypeMap = new HashMap<>(); for (Map.Entry appearanceEntry : appearancesSection.entrySet()) { @@ -452,13 +413,12 @@ public class BukkitBlockManager extends AbstractBlockManager { tempTypeMap.put(appearanceEntry.getKey(), pair.left()); } } - + // variants Map variantsSection = MiscUtils.castToMap(statesSection.get("variants"), true); if (variantsSection == null) { TranslationManager.instance().log("warning.config.block.state.lack_variants", path.toString(), id.toString()); return; } - variants = new HashMap<>(); for (Map.Entry variantEntry : variantsSection.entrySet()) { if (variantEntry.getValue() instanceof Map variantSection0) { @@ -478,8 +438,12 @@ public class BukkitBlockManager extends AbstractBlockManager { Key internalBlockId = Key.of(CraftEngine.NAMESPACE, baseBlock.value() + "_" + internalId); int internalBlockRegistryId = MiscUtils.getAsInt(internalId2StateId.getOrDefault(internalBlockId, -1)); if (internalBlockRegistryId == -1) { - plugin.logger().warn(path, "Failed to register " + id + " because id " + internalId + " is not a value between 0~" + (MiscUtils.getAsInt(registeredRealBlockSlots.getOrDefault(baseBlock, 1))-1) + - ". Consider editing additional-real-blocks.yml if the number of real block IDs is insufficient while there are still available appearances"); + TranslationManager.instance().log("warning.config.block.state.invalid_real_state_id", + path.toString(), + id.toString(), + internalBlockId.toString(), + String.valueOf(MiscUtils.getAsInt(registeredRealBlockSlots.getOrDefault(baseBlock, 1)) - 1) + ); return; } Map anotherSetting = MiscUtils.castToMap(variantSection.get("settings"), true); @@ -487,18 +451,27 @@ public class BukkitBlockManager extends AbstractBlockManager { } } } + // create or get block holder Holder.Reference holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() -> ((WritableRegistry) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id))); // create block Map behaviorSection = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false); - BukkitCustomBlock block = new BukkitCustomBlock(id, holder, properties, appearances, variants, settings, behaviorSection, lootTable); - // bind appearance - bindAppearance(block); - byId.put(id, block); + // bind appearance and real state + for (ImmutableBlockState state : block.variantProvider().states()) { + ImmutableBlockState previous = stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()]; + if (previous != null && !previous.isEmpty()) { + TranslationManager.instance().log("warning.config.block.bind_real_state", path.toString(), id.toString(), state.toString(), previous.toString()); + continue; + } + stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()] = state; + tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId()); + appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new ArrayList<>()).add(state.customBlockState().registryId()); + } + byId.put(id, block); // generate mod assets if (Config.generateModAssets()) { for (ImmutableBlockState state : block.variantProvider().states()) { @@ -509,27 +482,18 @@ public class BukkitBlockManager extends AbstractBlockManager { } } - private void bindAppearance(CustomBlock block) { - for (ImmutableBlockState state : block.variantProvider().states()) { - ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()]; - if (previous != null && !previous.isEmpty()) { - this.plugin.logger().severe("[Fatal] Failed to bind real block state for " + state + ": the state is already occupied by " + previous); - continue; - } - this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()] = state; - this.tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId()); - this.appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new ArrayList<>()).add(state.customBlockState().registryId()); - } - } - - private Map> parseProperties(Path path, Map propertiesSection) { + private Map> parseProperties(Path path, Key id, Map propertiesSection) { Map> properties = new HashMap<>(); for (Map.Entry entry : propertiesSection.entrySet()) { if (entry.getValue() instanceof Map params) { - Property property = Properties.fromMap(entry.getKey(), MiscUtils.castToMap(params, false)); - properties.put(entry.getKey(), property); + try { + Property property = Properties.fromMap(entry.getKey(), MiscUtils.castToMap(params, false)); + properties.put(entry.getKey(), property); + } catch (Exception e) { + TranslationManager.instance().log("warning.config.block.state.invalid_property", path.toString(), id.toString(), entry.getKey(), e.getMessage()); + } } else { - this.plugin.logger().warn(path, "Invalid property format: " + entry.getKey()); + TranslationManager.instance().log("warning.config.block.state.invalid_property_structure", path.toString(), id.toString(), entry.getKey()); } } return properties; @@ -537,25 +501,41 @@ public class BukkitBlockManager extends AbstractBlockManager { @Nullable private Pair parseAppearanceSection(Path path, Key id, Map section) { + // require state non null String vanillaStateString = (String) section.get("state"); if (vanillaStateString == null) { TranslationManager.instance().log("warning.config.block.state.lack_state", path.toString(), id.toString()); return null; } + // get its registry id int vanillaStateRegistryId; - try { - vanillaStateRegistryId = parseVanillaStateRegistryId(vanillaStateString); - } catch (BlockStateArrangeException e) { - this.plugin.logger().warn(path, "Failed to load " + id + " - " + e.getMessage(), e); + VanillaStateParseResult parseResult = parseVanillaStateRegistryId(vanillaStateString); + if (parseResult.success()) { + vanillaStateRegistryId = parseResult.result; + } else { + String[] args = new String[parseResult.args.length + 2]; + args[0] = path.toString(); + args[1] = id.toString(); + System.arraycopy(parseResult.args, 0, args, 2, parseResult.args.length); + TranslationManager.instance().log(parseResult.reason(), args); return null; } + + // check conflict Key ifAny = this.tempRegistryIdConflictMap.get(vanillaStateRegistryId); if (ifAny != null && !ifAny.equals(id)) { - this.plugin.logger().warn(path, "Can't use " + BlockStateUtils.idToBlockState(vanillaStateRegistryId) + " as the base block for " + id + " because the state has already been used by " + ifAny); + TranslationManager.instance().log("warning.config.block.state.conflict", path.toString(), id.toString(), BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(vanillaStateRegistryId)).getAsString(), ifAny.toString()); return null; } + + // require models not to be null Object models = section.getOrDefault("models", section.get("model")); + if (models == null) { + TranslationManager.instance().log("warning.config.block.state.no_model_set", path.toString(), id.toString()); + return null; + } + List variants = new ArrayList<>(); if (models instanceof Map singleModelSection) { loadVariantModel(variants, MiscUtils.castToMap(singleModelSection, false)); @@ -565,11 +545,9 @@ public class BukkitBlockManager extends AbstractBlockManager { loadVariantModel(variants, MiscUtils.castToMap(singleModelMap, false)); } } - } else { - this.plugin.logger().warn(path, "No model set for " + id); - return null; } if (variants.isEmpty()) return null; + this.tempRegistryIdConflictMap.put(vanillaStateRegistryId, id); String blockState = BlockStateUtils.idToBlockState(vanillaStateRegistryId).toString(); Key block = Key.of(blockState.substring(blockState.indexOf('{') + 1, blockState.lastIndexOf('}'))); @@ -604,46 +582,50 @@ public class BukkitBlockManager extends AbstractBlockManager { variants.add(json); } - private int parseVanillaStateRegistryId(String blockState) throws BlockStateArrangeException { + private VanillaStateParseResult parseVanillaStateRegistryId(String blockState) { String[] split = blockState.split(":", 3); - PreConditions.runIfTrue(split.length >= 4, () -> { - throw new BlockStateArrangeException("Invalid vanilla block state: " + blockState); - }); + if (split.length >= 4) { + return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState}); + } int registryId; - // minecraft:xxx:0 - // xxx:0 String stateOrId = split[split.length - 1]; boolean isId = !stateOrId.contains("[") && !stateOrId.contains("]"); if (isId) { - if (split.length == 1) { - throw new BlockStateArrangeException("Invalid vanilla block state: " + blockState); - } + if (split.length == 1) return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{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]); - PreConditions.runIfTrue(id < 0, () -> { - throw new BlockStateArrangeException("Invalid block state: " + blockState); - }); + if (id < 0) return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState}); List arranger = this.blockAppearanceArranger.get(block); - if (arranger == null) { - throw new BlockStateArrangeException("No freed block state is available for block " + block); - } - if (id >= arranger.size()) { - throw new BlockStateArrangeException(blockState + " is not a valid block state because " + id + " is outside of the range (0~" + (arranger.size() - 1) + ")"); - } + if (arranger == null) return VanillaStateParseResult.failure("warning.config.block.state.unavailable_state", new String[]{blockState}); + if (id >= arranger.size()) return VanillaStateParseResult.failure("warning.config.block.state.invalid_vanilla_state_id", new String[]{blockState, String.valueOf(arranger.size() - 1)}); registryId = arranger.get(id); } catch (NumberFormatException e) { - throw new BlockStateArrangeException("Invalid block state: " + blockState); + return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState}); } } else { try { BlockData blockData = Bukkit.createBlockData(blockState); registryId = BlockStateUtils.blockDataToId(blockData); + if (!this.blockAppearanceMapper.containsKey(registryId)) { + return VanillaStateParseResult.failure("warning.config.block.state.unavailable_state", new String[]{blockState}); + } } catch (IllegalArgumentException e) { - throw new BlockStateArrangeException("Invalid block state: " + blockState); + return VanillaStateParseResult.failure("warning.config.block.state.invalid_state", new String[]{blockState}); } } - return registryId; + return VanillaStateParseResult.success(registryId); + } + + public record VanillaStateParseResult(boolean success, int result, String reason, String[] args) { + + public static VanillaStateParseResult success(int result) { + return new VanillaStateParseResult(true, result, null, null); + } + + public static VanillaStateParseResult failure(String reason, String[] args) { + return new VanillaStateParseResult(false, -1, reason, args); + } } private void loadMappingsAndAdditionalBlocks() { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java index 2f79abc20..451c60f5e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java @@ -48,7 +48,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior { if (this.defaultBlockState != null) { return this.defaultBlockState; } - Optional optionalCustomBlock = BukkitBlockManager.instance().getBlock(this.targetBlock); + Optional optionalCustomBlock = BukkitBlockManager.instance().blockById(this.targetBlock); if (optionalCustomBlock.isPresent()) { CustomBlock customBlock = optionalCustomBlock.get(); this.defaultBlockState = customBlock.defaultState().customBlockState().handle(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java index 01ee866e1..c19b855ed 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java @@ -60,7 +60,7 @@ public class AxeItemBehavior extends ItemBehavior { return InteractionResult.PASS; } - Optional optionalNewCustomBlock = BukkitBlockManager.instance().getBlock(blockBehavior.stripped()); + Optional optionalNewCustomBlock = BukkitBlockManager.instance().blockById(blockBehavior.stripped()); if (optionalNewCustomBlock.isEmpty()) { CraftEngine.instance().logger().warn("stripped block " + blockBehavior.stripped() + " does not exist"); return InteractionResult.FAIL; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java index 40b96fe9d..563a4cfb7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java @@ -61,7 +61,7 @@ public class BlockItemBehavior extends ItemBehavior { if (!context.canPlace()) { return InteractionResult.FAIL; } - Optional optionalBlock = BukkitBlockManager.instance().getBlock(this.blockId); + Optional optionalBlock = BukkitBlockManager.instance().blockById(this.blockId); if (optionalBlock.isEmpty()) { CraftEngine.instance().logger().warn("Failed to place unknown block " + this.blockId); return InteractionResult.FAIL; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java index 52b298618..f50b12081 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java @@ -39,7 +39,7 @@ public class BlockStateUtils { } else { String blockTypeString = blockState.substring(0, index); Key block = Key.of(blockTypeString); - Optional optionalCustomBlock = BukkitBlockManager.instance().getBlock(block); + Optional optionalCustomBlock = BukkitBlockManager.instance().blockById(block); if (optionalCustomBlock.isPresent()) { ImmutableBlockState state = BlockStateParser.deserialize(blockState); if (state == null) { @@ -55,7 +55,7 @@ public class BlockStateUtils { } public static List getAllBlockStates(Key block) { - Optional optionalCustomBlock = BukkitBlockManager.instance().getBlock(block); + Optional optionalCustomBlock = BukkitBlockManager.instance().blockById(block); return optionalCustomBlock.map(customBlock -> customBlock.variantProvider().states().stream().map(it -> it.customBlockState().handle()).toList()) .orElseGet(() -> getAllVanillaBlockStates(block)); } @@ -80,7 +80,7 @@ public class BlockStateUtils { } public static BlockData fromBlockData(Object blockState) { - return (BlockData) FastNMS.INSTANCE.method$CraftBlockData$fromData(blockState); + return FastNMS.INSTANCE.method$CraftBlockData$fromData(blockState); } public static int blockDataToId(BlockData blockData) { 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 a1046b6e7..ba3e03d8f 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 @@ -2,10 +2,62 @@ package net.momirealms.craftengine.core.block; import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.Key; +import org.incendo.cloud.suggestion.Suggestion; + +import java.util.*; public abstract class AbstractBlockManager extends AbstractModelGenerator implements BlockManager { + // CraftEngine objects + protected final Map byId = new HashMap<>(); + // Cached command suggestions + protected final List cachedSuggestions = new ArrayList<>(); + // Cached Namespace + protected final Set namespacesInUse = new HashSet<>(); public AbstractBlockManager(CraftEngine plugin) { super(plugin); } + + @Override + public Map blocks() { + return Collections.unmodifiableMap(this.byId); + } + + @Override + public Optional blockById(Key id) { + return Optional.ofNullable(this.byId.get(id)); + } + + @Override + public void unload() { + super.clearModelsToGenerate(); + this.cachedSuggestions.clear(); + this.byId.clear(); + } + + @Override + public Collection cachedSuggestions() { + return Collections.unmodifiableCollection(this.cachedSuggestions); + } + + public Set namespacesInUse() { + return Collections.unmodifiableSet(this.namespacesInUse); + } + + protected void initSuggestions() { + this.cachedSuggestions.clear(); + this.namespacesInUse.clear(); + Set states = new HashSet<>(); + for (CustomBlock block : this.byId.values()) { + states.add(block.id().toString()); + this.namespacesInUse.add(block.id().namespace()); + for (ImmutableBlockState state : block.variantProvider().states()) { + states.add(state.toString()); + } + } + for (String state : states) { + this.cachedSuggestions.add(Suggestion.suggestion(state)); + } + } } 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 d21392cea..125f42018 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 @@ -24,7 +24,7 @@ public interface BlockManager extends Manageable, ModelGenerator { Map blocks(); - Optional getBlock(Key key); + Optional blockById(Key key); Collection cachedSuggestions(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java index ad31c10fb..ac84cf0d5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java @@ -36,7 +36,7 @@ public class Properties { public static Property fromMap(String name, Map map) { String type = (String) map.getOrDefault("type", "empty"); if (type == null) { - throw new NullPointerException("behavior type cannot be null"); + throw new NullPointerException("Property type cannot be null"); } Key key = Key.withDefaultNamespace(type, "craftengine"); PropertyFactory factory = BuiltInRegistries.PROPERTY_FACTORY.getValue(key); 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 4c2a241a7..73a442ee8 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 @@ -444,10 +444,8 @@ public abstract class AbstractPackManager implements PackManager { int hashIndex = key.indexOf('#'); String configType = hashIndex != -1 ? key.substring(0, hashIndex) : key; Optional.ofNullable(this.sectionParsers.get(configType)) - .ifPresent(parser -> { - this.cachedConfigs.computeIfAbsent(parser, k -> new ArrayList<>()) - .add(new CachedConfig(castToMap(typeSections0, false), path, pack)); - }); + .ifPresent(parser -> this.cachedConfigs.computeIfAbsent(parser, k -> new ArrayList<>()) + .add(new CachedConfig(castToMap(typeSections0, false), path, pack))); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java b/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java index 5885168b9..471e5ead5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java @@ -19,7 +19,7 @@ public class ResourceLocation { return character == '_' || character == '-' || character >= 'a' && character <= 'z' || character >= '0' && character <= '9' || character == '.'; } - private static boolean isValidNamespace(String namespace) { + public static boolean isValidNamespace(String namespace) { for(int i = 0; i < namespace.length(); ++i) { if (!validNamespaceChar(namespace.charAt(i))) { return false; @@ -28,7 +28,7 @@ public class ResourceLocation { return true; } - private static boolean isValidPath(String path) { + public static boolean isValidPath(String path) { for(int i = 0; i < path.length(); ++i) { if (!validPathChar(path.charAt(i))) { return false; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java index e2702a7da..7184da0b1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java @@ -22,7 +22,7 @@ public class I18NData { return List.of("block." + stateToRealBlockId(parsed)); } else { Key blockId = Key.of(id); - Optional blockOptional = CraftEngine.instance().blockManager().getBlock(blockId); + Optional blockOptional = CraftEngine.instance().blockManager().blockById(blockId); if (blockOptional.isPresent()) { List states = blockOptional.get().variantProvider().states(); if (states.size() == 1) {