9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-29 20:09:13 +00:00

refactor items

This commit is contained in:
XiaoMoMi
2025-04-04 05:18:50 +08:00
parent 83af689268
commit f8fd3f7743
11 changed files with 196 additions and 133 deletions

View File

@@ -19,10 +19,13 @@ import net.momirealms.craftengine.core.block.*;
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.model.generation.ModelGeneration;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.WritableRegistry;
@@ -45,6 +48,7 @@ import java.util.*;
public class BukkitBlockManager extends AbstractBlockManager {
private static BukkitBlockManager instance;
private final BukkitCraftEngine plugin;
private final BlockParser blockParser;
// A temporary map used to detect whether the same block state corresponds to multiple models.
private final Map<Integer, Key> tempRegistryIdConflictMap = new HashMap<>();
@@ -57,7 +61,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
private int customBlockCount;
// CraftEngine objects
private final Map<Key, CustomBlock> id2CraftEngineBlocks = new HashMap<>();
private final Map<Key, CustomBlock> byId = new HashMap<>();
private final ImmutableBlockState[] stateId2ImmutableBlockStates;
// Minecraft objects
@@ -96,6 +100,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
public BukkitBlockManager(BukkitCraftEngine plugin) {
super(plugin);
this.plugin = plugin;
this.blockParser = new BlockParser();
this.initVanillaRegistry();
this.loadMappingsAndAdditionalBlocks();
if (plugin.hasMod() && plugin.requiresRestart()) {
@@ -150,7 +155,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
super.clearModelsToGenerate();
this.clearCache();
this.appearanceToRealState.clear();
this.id2CraftEngineBlocks.clear();
this.byId.clear();
this.cachedSuggestions.clear();
this.blockStateOverrides.clear();
this.modBlockStates.clear();
@@ -221,6 +226,11 @@ public class BukkitBlockManager extends AbstractBlockManager {
return Collections.unmodifiableMap(this.modBlockStates);
}
@Override
public ConfigSectionParser parser() {
return this.blockParser;
}
@Override
public Map<Key, Map<String, JsonElement>> blockOverrides() {
return Collections.unmodifiableMap(this.blockStateOverrides);
@@ -228,12 +238,12 @@ public class BukkitBlockManager extends AbstractBlockManager {
@Override
public Map<Key, CustomBlock> blocks() {
return Collections.unmodifiableMap(this.id2CraftEngineBlocks);
return Collections.unmodifiableMap(this.byId);
}
@Override
public Optional<CustomBlock> getBlock(Key key) {
return Optional.ofNullable(this.id2CraftEngineBlocks.get(key));
return Optional.ofNullable(this.byId.get(key));
}
@Override
@@ -246,7 +256,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
this.cachedSuggestions.clear();
this.namespacesInUse.clear();
Set<String> states = new HashSet<>();
for (CustomBlock block : this.id2CraftEngineBlocks.values()) {
for (CustomBlock block : this.byId.values()) {
states.add(block.id().toString());
this.namespacesInUse.add(block.id().namespace());
for (ImmutableBlockState state : block.variantProvider().states()) {
@@ -368,100 +378,134 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
// read block settings
BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.getOrDefault("settings", Map.of()), false));
// read loot table
LootTable<ItemStack> lootTable = LootTable.fromMap(MiscUtils.castToMap(section.getOrDefault("loot", Map.of()), false));
// read states
Map<String, Property<?>> properties;
Map<String, Integer> appearances;
Map<String, VariantState> variants;
Map<String, Object> stateSection = MiscUtils.castToMap(section.get("state"), true);
if (stateSection != null) {
properties = Map.of();
int internalId = MiscUtils.getAsInt(stateSection.getOrDefault("id", -1));
if (PreConditions.runIfTrue(internalId < 0, () -> plugin.logger().warn(path, "No state id configured for block " + id))) return;
Pair<Key, Integer> pair = parseAppearanceSection(path, stateSection, id);
if (pair == null) return;
appearances = Map.of("default", pair.right());
Key internalBlockId = Key.of(CraftEngine.NAMESPACE, pair.left().value() + "_" + internalId);
int internalBlockRegistryId = MiscUtils.getAsInt(this.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(this.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");
return;
}
variants = Map.of("", new VariantState("default", settings, internalBlockRegistryId));
} else {
Map<String, Object> statesSection = MiscUtils.castToMap(section.get("states"), true);
if (statesSection == null) {
plugin.logger().warn(path, "No states configured for block " + id);
return;
}
Map<String, Object> propertySection = MiscUtils.castToMap(statesSection.get("properties"), true);
if (PreConditions.isNull(propertySection, () -> plugin.logger().warn(path, "No properties configured for block " + id))) return;
properties = parseProperties(path, propertySection);
Map<String, Object> appearancesSection = MiscUtils.castToMap(statesSection.get("appearances"), true);
if (PreConditions.isNull(appearancesSection, () -> plugin.logger().warn(path, "No appearances configured for block " + id))) return;
appearances = new HashMap<>();
Map<String, Key> tempTypeMap = new HashMap<>();
for (Map.Entry<String, Object> appearanceEntry : appearancesSection.entrySet()) {
if (appearanceEntry.getValue() instanceof Map<?, ?> appearanceSection) {
Pair<Key, Integer> pair = parseAppearanceSection(path, MiscUtils.castToMap(appearanceSection, false), id);
if (pair == null) return;
appearances.put(appearanceEntry.getKey(), pair.right());
tempTypeMap.put(appearanceEntry.getKey(), pair.left());
}
}
Map<String, Object> variantsSection = MiscUtils.castToMap(statesSection.get("variants"), true);
if (PreConditions.isNull(variantsSection, () -> plugin.logger().warn(path, "No variants configured for block " + id))) return;
variants = new HashMap<>();
for (Map.Entry<String, Object> variantEntry : variantsSection.entrySet()) {
if (variantEntry.getValue() instanceof Map<?, ?> variantSection0) {
Map<String, Object> variantSection = MiscUtils.castToMap(variantSection0, false);
String variantName = variantEntry.getKey();
String appearance = (String) variantSection.get("appearance");
if (appearance == null) {
plugin.logger().warn(path, "No appearance configured for variant " + variantName);
return;
}
if (!appearances.containsKey(appearance)) {
plugin.logger().warn(path, appearance + " is not a valid appearance for block " + id);
return;
}
int internalId = MiscUtils.getAsInt(variantSection.getOrDefault("id", -1));
Key baseBlock = tempTypeMap.get(appearance);
Key internalBlockId = Key.of(CraftEngine.NAMESPACE, baseBlock.value() + "_" + internalId);
int internalBlockRegistryId = MiscUtils.getAsInt(this.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(this.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");
return;
}
Map<String, Object> anotherSetting = MiscUtils.castToMap(variantSection.get("settings"), true);
variants.put(variantName, new VariantState(appearance, anotherSetting == null ? settings : BlockSettings.ofFullCopy(settings, anotherSetting), internalBlockRegistryId));
}
}
public class BlockParser implements ConfigSectionParser {
public static final String[] CONFIG_SECTION_NAME = new String[] {"blocks", "block"};
@Override
public String[] sectionId() {
return CONFIG_SECTION_NAME;
}
// create or get block holder
Holder.Reference<CustomBlock> holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() ->
((WritableRegistry<CustomBlock>) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id)));
// create block
Map<String, Object> behaviorSection = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false);
BukkitCustomBlock block = new BukkitCustomBlock(id, holder, properties, appearances, variants, settings, behaviorSection, lootTable);
@Override
public int loadingSequence() {
return LoadingSequence.BLOCK;
}
// bind appearance
bindAppearance(block);
this.id2CraftEngineBlocks.put(id, block);
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
if (byId.containsKey(id)) {
TranslationManager.instance().log("warning.config.block.duplicated", path.toString(), id.toString());
return;
}
// generate mod assets
if (ConfigManager.generateModAssets()) {
for (ImmutableBlockState state : block.variantProvider().states()) {
Key realBlockId = BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState());
this.modBlockStates.put(realBlockId, this.tempVanillaBlockStateModels.get(state.vanillaBlockState().registryId()));
// read block settings
BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.getOrDefault("settings", Map.of()), false));
// read loot table
LootTable<ItemStack> lootTable = LootTable.fromMap(MiscUtils.castToMap(section.getOrDefault("loot", Map.of()), false));
// read states
Map<String, Property<?>> properties;
Map<String, Integer> appearances;
Map<String, VariantState> variants;
Map<String, Object> stateSection = MiscUtils.castToMap(section.get("state"), true);
if (stateSection != null) {
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());
return;
}
Pair<Key, Integer> 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");
return;
}
variants = Map.of("", new VariantState("default", settings, internalBlockRegistryId));
} else {
Map<String, Object> statesSection = MiscUtils.castToMap(section.get("states"), true);
if (statesSection == null) {
TranslationManager.instance().log("warning.config.block.lack_state", path.toString(), id.toString());
return;
}
Map<String, Object> 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);
Map<String, Object> 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<String, Key> tempTypeMap = new HashMap<>();
for (Map.Entry<String, Object> appearanceEntry : appearancesSection.entrySet()) {
if (appearanceEntry.getValue() instanceof Map<?, ?> appearanceSection) {
Pair<Key, Integer> pair = parseAppearanceSection(path, id, MiscUtils.castToMap(appearanceSection, false));
if (pair == null) return;
appearances.put(appearanceEntry.getKey(), pair.right());
tempTypeMap.put(appearanceEntry.getKey(), pair.left());
}
}
Map<String, Object> 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<String, Object> variantEntry : variantsSection.entrySet()) {
if (variantEntry.getValue() instanceof Map<?, ?> variantSection0) {
Map<String, Object> variantSection = MiscUtils.castToMap(variantSection0, false);
String variantName = variantEntry.getKey();
String appearance = (String) variantSection.get("appearance");
if (appearance == null) {
TranslationManager.instance().log("warning.config.block.state.variant.lack_appearance", path.toString(), id.toString(), variantName);
return;
}
if (!appearances.containsKey(appearance)) {
TranslationManager.instance().log("warning.config.block.state.variant.invalid_appearance", path.toString(), id.toString(), variantName, appearance);
return;
}
int internalId = MiscUtils.getAsInt(variantSection.getOrDefault("id", -1));
Key baseBlock = tempTypeMap.get(appearance);
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");
return;
}
Map<String, Object> anotherSetting = MiscUtils.castToMap(variantSection.get("settings"), true);
variants.put(variantName, new VariantState(appearance, anotherSetting == null ? settings : BlockSettings.ofFullCopy(settings, anotherSetting), internalBlockRegistryId));
}
}
}
// create or get block holder
Holder.Reference<CustomBlock> holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() ->
((WritableRegistry<CustomBlock>) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id)));
// create block
Map<String, Object> 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);
// generate mod assets
if (ConfigManager.generateModAssets()) {
for (ImmutableBlockState state : block.variantProvider().states()) {
Key realBlockId = BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState());
modBlockStates.put(realBlockId, tempVanillaBlockStateModels.get(state.vanillaBlockState().registryId()));
}
}
}
}
@@ -493,10 +537,13 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
@Nullable
private Pair<Key, Integer> parseAppearanceSection(Path path, Map<String, Object> section, Key id) {
private Pair<Key, Integer> parseAppearanceSection(Path path, Key id, Map<String, Object> section) {
String vanillaStateString = (String) section.get("state");
if (PreConditions.isNull(vanillaStateString,
() -> this.plugin.logger().warn(path, "No block state found for: " + id))) return null;
if (vanillaStateString == null) {
TranslationManager.instance().log("warning.config.block.state.lack_state", path.toString(), id.toString());
return null;
}
int vanillaStateRegistryId;
try {
vanillaStateRegistryId = parseVanillaStateRegistryId(vanillaStateString);

View File

@@ -176,7 +176,7 @@ public class BlockItemBehavior extends ItemBehavior {
throw new IllegalArgumentException("Missing required parameter 'block' for block_item behavior");
}
if (id instanceof Map<?, ?> map) {
BukkitBlockManager.instance().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
return new BlockItemBehavior(key);
} else {
return new BlockItemBehavior(Key.of(id.toString()));

View File

@@ -67,7 +67,7 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
}
int offset = MiscUtils.getAsInt(arguments.getOrDefault("y-offset", 1));
if (id instanceof Map<?, ?> map) {
BukkitBlockManager.instance().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
return new LiquidCollisionBlockItemBehavior(key, offset);
} else {
return new LiquidCollisionBlockItemBehavior(Key.of(id.toString()), offset);

View File

@@ -37,10 +37,7 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
plugin().scheduler().executeAsync(() -> {
try {
RELOAD_PACK_FLAG = true;
long time1 = System.currentTimeMillis();
plugin().reload();
long time2 = System.currentTimeMillis();
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_SUCCESS, Component.text(time2 - time1));
plugin().reload((a, b) -> handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_SUCCESS, Component.text(a + b), Component.text(a), Component.text(b)));
} catch (Exception e) {
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_FAILURE);
plugin().logger().warn("Failed to reload config", e);
@@ -60,16 +57,17 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
});
} else if (argument == ReloadArgument.ALL) {
plugin().scheduler().executeAsync(() -> {
long time1 = System.currentTimeMillis();
plugin().reload();
try {
plugin().packManager().generateResourcePack();
long time2 = System.currentTimeMillis();
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_SUCCESS, Component.text(time2 - time1));
} catch (Exception e) {
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_FAILURE);
plugin().logger().warn("Failed to generate resource pack", e);
}
plugin().reload((a, b) -> {
try {
long time1 = System.currentTimeMillis();
plugin().packManager().generateResourcePack();
long time2 = System.currentTimeMillis();
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_SUCCESS, Component.text(a + b + time2 - time1), Component.text(a), Component.text(b), Component.text(time2 - time1));
} catch (Exception e) {
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_FAILURE);
plugin().logger().warn("Failed to generate resource pack", e);
}
});
});
}
});