9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-31 12:56:28 +00:00

重构方块部分

This commit is contained in:
XiaoMoMi
2025-05-20 04:10:35 +08:00
parent e60a818331
commit eaf8d3663b
27 changed files with 598 additions and 484 deletions

View File

@@ -3,12 +3,10 @@ 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.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import dev.dejvokep.boostedyaml.YamlDocument;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
@@ -28,12 +26,8 @@ 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.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
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,7 +39,6 @@ import org.bukkit.Registry;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
@@ -58,50 +51,34 @@ 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 Int2ObjectOpenHashMap<>();
// A temporary map that converts the custom block registered on the server to the vanilla block ID.
private final Map<Integer, Integer> tempBlockAppearanceConvertor = new Int2IntOpenHashMap();
// A temporary map that stores the model path of a certain vanilla block state
private final Map<Integer, JsonElement> tempVanillaBlockStateModels = new Int2ObjectOpenHashMap<>();
// The total amount of blocks registered
private int customBlockCount;
protected final ImmutableBlockState[] stateId2ImmutableBlockStates;
// Minecraft objects
// Cached new blocks $ holders
private ImmutableMap<Key, Integer> internalId2StateId;
private ImmutableMap<Integer, Object> stateId2BlockHolder;
private Map<Key, Integer> internalId2StateId;
private Map<Integer, Object> stateId2BlockHolder;
// This map is used to change the block states that are not necessarily needed into a certain block state
private ImmutableMap<Integer, Integer> blockAppearanceMapper;
private Map<Integer, Integer> blockAppearanceMapper;
// Used to automatically arrange block states for strings such as minecraft:note_block:0
private ImmutableMap<Key, List<Integer>> blockAppearanceArranger;
private ImmutableMap<Key, List<Integer>> realBlockArranger;
private Map<Key, List<Integer>> blockAppearanceArranger;
private Map<Key, List<Integer>> realBlockArranger;
// Record the amount of real blocks by block type
private LinkedHashMap<Key, Integer> registeredRealBlockSlots;
private Map<Key, Integer> registeredRealBlockSlots;
// A set of blocks that sounds have been removed
private ImmutableSet<Object> affectedSoundBlocks;
private ImmutableMap<Key, Key> soundMapper;
private Set<Object> affectedSoundBlocks;
private Map<Key, Key> soundMapper;
// A list to record the order of registration
private List<Key> blockRegisterOrder = new ObjectArrayList<>();
// a reverted mapper
private final Map<Integer, List<Integer>> appearanceToRealState = new Int2ObjectOpenHashMap<>();
// Used to store override information of json files
private final Map<Key, Map<String, JsonElement>> blockStateOverrides = new HashMap<>();
// for mod, real block id -> state models
private final Map<Key, JsonElement> modBlockStates = new HashMap<>();
// Event listeners
private final BlockEventListener blockEventListener;
private final FallingBlockRemoveListener fallingBlockRemoveListener;
private Map<Integer, List<String>> clientBoundTags = Map.of();
private Map<Integer, List<String>> previousTags = Map.of();
// cached tag packet
protected Object cachedUpdateTagsPacket;
public BukkitBlockManager(BukkitCraftEngine plugin) {
super(plugin);
instance = this;
this.plugin = plugin;
this.blockParser = new BlockParser();
this.initVanillaRegistry();
@@ -120,42 +97,33 @@ public class BukkitBlockManager extends AbstractBlockManager {
if (enableNoteBlocks) {
this.recordVanillaNoteBlocks();
}
if (VersionHelper.isOrAbove1_20_3()) {
this.fallingBlockRemoveListener = new FallingBlockRemoveListener();
} else this.fallingBlockRemoveListener = null;
this.stateId2ImmutableBlockStates = new ImmutableBlockState[customBlockCount];
this.fallingBlockRemoveListener = VersionHelper.isOrAbove1_20_3() ? new FallingBlockRemoveListener() : null;
this.stateId2ImmutableBlockStates = new ImmutableBlockState[this.customBlockCount];
Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.INSTANCE.defaultState());
instance = this;
this.resetPacketConsumers();
}
public List<Key> blockRegisterOrder() {
return Collections.unmodifiableList(this.blockRegisterOrder);
}
public static BukkitBlockManager instance() {
return instance;
}
public List<Key> blockRegisterOrder() {
return Collections.unmodifiableList(this.blockRegisterOrder);
}
@Override
public void delayedInit() {
Bukkit.getPluginManager().registerEvents(this.blockEventListener, plugin.bootstrap());
Bukkit.getPluginManager().registerEvents(this.blockEventListener, this.plugin.bootstrap());
if (this.fallingBlockRemoveListener != null) {
Bukkit.getPluginManager().registerEvents(this.fallingBlockRemoveListener, plugin.bootstrap());
Bukkit.getPluginManager().registerEvents(this.fallingBlockRemoveListener, this.plugin.bootstrap());
}
}
@Override
public void unload() {
super.unload();
this.clearCache();
this.appearanceToRealState.clear();
this.blockStateOverrides.clear();
this.modBlockStates.clear();
if (EmptyBlock.STATE != null)
Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.STATE);
this.previousTags = this.clientBoundTags;
this.clientBoundTags = new HashMap<>();
}
@Override
@@ -172,15 +140,14 @@ public class BukkitBlockManager extends AbstractBlockManager {
@Override
public void delayedLoad() {
initSuggestions();
resetPacketConsumers();
clearCache();
resendTags();
this.resetPacketConsumers();
super.delayedLoad();
}
private void resendTags() {
@Override
protected void resendTags() {
// if there's no change
if (this.clientBoundTags.equals(this.previousTags)) return;
if (this.clientBoundTags.equals(this.previousClientBoundTags)) return;
List<TagUtils.TagEntry> list = new ArrayList<>();
for (Map.Entry<Integer, List<String>> entry : this.clientBoundTags.entrySet()) {
list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue()));
@@ -197,23 +164,19 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
private void clearCache() {
this.tempRegistryIdConflictMap.clear();
this.tempBlockAppearanceConvertor.clear();
this.tempVanillaBlockStateModels.clear();
}
@Nullable
public Object getMinecraftBlockHolder(int stateId) {
return stateId2BlockHolder.get(stateId);
}
@NotNull
@Override
public ImmutableBlockState getImmutableBlockStateUnsafe(int stateId) {
return this.stateId2ImmutableBlockStates[stateId - BlockStateUtils.vanillaStateSize()];
}
@Nullable
@Override
public ImmutableBlockState getImmutableBlockState(int stateId) {
if (!BlockStateUtils.isVanillaBlock(stateId)) {
return this.stateId2ImmutableBlockStates[stateId - BlockStateUtils.vanillaStateSize()];
@@ -221,34 +184,46 @@ public class BukkitBlockManager extends AbstractBlockManager {
return null;
}
@Override
public Map<Key, JsonElement> modBlockStates() {
return Collections.unmodifiableMap(this.modBlockStates);
}
@Override
public ConfigParser parser() {
return this.blockParser;
}
@Override
public Map<Key, Map<String, JsonElement>> blockOverrides() {
return Collections.unmodifiableMap(this.blockStateOverrides);
public void addBlock(Key id, CustomBlock customBlock) {
// bind appearance and real state
for (ImmutableBlockState state : customBlock.variantProvider().states()) {
ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()];
if (previous != null && !previous.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.block.state.bind_failed", state.toString(), previous.toString());
}
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 IntArrayList()).add(state.customBlockState().registryId());
}
super.addBlock(id, customBlock);
}
public ImmutableMap<Key, List<Integer>> blockAppearanceArranger() {
@Override
public Key getBlockOwnerId(PackedBlockState state) {
return BlockStateUtils.getBlockOwnerIdFromState(state.handle());
}
@Override
public int availableAppearances(Key blockType) {
return Optional.ofNullable(this.registeredRealBlockSlots.get(blockType)).orElse(0);
}
@NotNull
public Map<Key, List<Integer>> blockAppearanceArranger() {
return this.blockAppearanceArranger;
}
public ImmutableMap<Key, List<Integer>> realBlockArranger() {
@NotNull
public Map<Key, List<Integer>> realBlockArranger() {
return this.realBlockArranger;
}
@Nullable
public List<Integer> appearanceToRealStates(int appearanceStateId) {
return this.appearanceToRealState.get(appearanceStateId);
}
private void initMirrorRegistry() {
int size = RegistryUtils.currentBlockRegistrySize();
PackedBlockState[] states = new PackedBlockState[size];
@@ -276,25 +251,25 @@ public class BukkitBlockManager extends AbstractBlockManager {
private void initVanillaRegistry() {
int vanillaStateCount;
if (plugin.hasMod()) {
if (this.plugin.hasMod()) {
try {
Class<?> modClass = ReflectionUtils.getClazz(CraftEngine.MOD_CLASS);
Field amountField = ReflectionUtils.getDeclaredField(modClass, "vanillaRegistrySize");
vanillaStateCount = amountField.getInt(null);
} catch (Exception e) {
vanillaStateCount = RegistryUtils.currentBlockRegistrySize();
plugin.logger().severe("Fatal error", e);
this.plugin.logger().severe("Fatal error", e);
}
} else {
vanillaStateCount = RegistryUtils.currentBlockRegistrySize();
}
plugin.logger().info("Vanilla block count: " + vanillaStateCount);
this.plugin.logger().info("Vanilla block count: " + vanillaStateCount);
BlockStateUtils.init(vanillaStateCount);
}
@SuppressWarnings("unchecked")
private void registerBlocks() {
plugin.logger().info("Registering blocks. Please wait...");
this.plugin.logger().info("Registering blocks. Please wait...");
try {
ImmutableMap.Builder<Key, Integer> builder1 = ImmutableMap.builder();
ImmutableMap.Builder<Integer, Object> builder2 = ImmutableMap.builder();
@@ -367,7 +342,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
parseVanillaBlock(pack, path, id, section);
} else {
// check duplicated config
if (byId.containsKey(id)) {
if (BukkitBlockManager.this.byId.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.block.duplicate");
}
parseCustomBlock(pack, path, id, section);
@@ -376,128 +351,73 @@ public class BukkitBlockManager extends AbstractBlockManager {
private void parseCustomBlock(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));
BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.get("settings"), true));
// read states
Map<String, Property<?>> properties;
Map<String, Integer> appearances;
Map<String, VariantState> variants;
Object stateObj = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "state", "states"), "warning.config.block.missing_state");
Map<String, Object> stateSection = MiscUtils.castToMap(stateObj, true);
Map<String, Object> stateSection = MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "state", "states"), "warning.config.block.missing_state"), true);
boolean singleState = !stateSection.containsKey("properties");
// single state
if (!stateSection.containsKey("properties")) {
if (singleState) {
properties = Map.of();
int internalId = ResourceConfigUtils.getAsInt(stateSection.getOrDefault("id", -1), "id");
if (internalId < 0) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_real_id");
}
Pair<Key, Integer> pair = parseAppearanceSection(id, stateSection);
if (pair == null) return;
appearances = Map.of("default", pair.right());
String internalBlock = pair.left().value() + "_" + internalId;
Key internalBlockId = Key.of(Key.DEFAULT_NAMESPACE, internalBlock);
int internalId = ResourceConfigUtils.getAsInt(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("id"), "warning.config.block.state.missing_real_id"), "id");
VanillaBlock 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",
internalBlock,
String.valueOf(registeredRealBlockSlots.get(pair.left()) - 1));
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", internalBlockId.toString(), String.valueOf(availableAppearances(vanillaBlock.type()) - 1));
}
variants = Map.of("", new VariantState("default", settings, internalBlockRegistryId));
variants = Map.of("", new VariantState("", settings, internalBlockRegistryId));
} else {
// properties
Map<String, Object> propertySection = MiscUtils.castToMap(stateSection.get("properties"), true);
if (propertySection == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_properties");
}
properties = parseProperties(propertySection);
properties = getProperties(MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("properties"), "warning.config.block.state.missing_properties"), true));
// appearance
Map<String, Object> appearancesSection = MiscUtils.castToMap(stateSection.get("appearances"), true);
if (appearancesSection == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_appearances");
}
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(id, MiscUtils.castToMap(appearanceSection, false));
if (pair == null) return;
appearances.put(appearanceEntry.getKey(), pair.right());
tempTypeMap.put(appearanceEntry.getKey(), pair.left());
Map<String, Key> appearance2BlockType = new HashMap<>();
for (Map.Entry<String, Object> appearanceEntry : MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("appearances"), "warning.config.block.state.missing_appearances"), false).entrySet()) {
if (appearanceEntry.getValue() instanceof Map<?, ?>) {
VanillaBlock vanillaBlock = getVanillaBlock(id, MiscUtils.castToMap(appearanceEntry.getValue(), false));
appearances.put(appearanceEntry.getKey(), vanillaBlock.registryId());
appearance2BlockType.put(appearanceEntry.getKey(), vanillaBlock.type());
}
}
// variants
Map<String, Object> variantsSection = MiscUtils.castToMap(stateSection.get("variants"), true);
if (variantsSection == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_variants");
}
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) {
throw new LocalizedResourceConfigException("warning.config.block.state.variant.missing_appearance", variantName);
}
for (Map.Entry<String, Object> variantEntry : MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("variants"), "warning.config.block.state.missing_variants"), false).entrySet()) {
if (variantEntry.getValue() instanceof Map<?, ?>) {
Map<String, Object> 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", variantName, appearance);
throw new LocalizedResourceConfigException("warning.config.block.state.variant.invalid_appearance", variantNBT, appearance);
}
int internalId = ResourceConfigUtils.getAsInt(variantSection.getOrDefault("id", -1), "id");
Key baseBlock = tempTypeMap.get(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(registeredRealBlockSlots.getOrDefault(baseBlock, 1) - 1));
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", internalBlockId.toString(), String.valueOf(availableAppearances(baseBlock) - 1));
}
Map<String, Object> anotherSetting = MiscUtils.castToMap(variantSection.get("settings"), true);
variants.put(variantName, new VariantState(appearance, anotherSetting == null ? settings : BlockSettings.ofFullCopy(settings, anotherSetting), internalBlockRegistryId));
variants.put(variantNBT, new VariantState(appearance, anotherSetting == null ? settings : BlockSettings.ofFullCopy(settings, anotherSetting), internalBlockRegistryId));
}
}
}
Object eventsObj = ResourceConfigUtils.get(section, "events", "event");
EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events = EventFunctions.parseEvents(eventsObj);
Map<String, Object> behaviors = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false);
CustomBlock block = BukkitCustomBlock.builder(id)
.appearances(appearances)
.variantMapper(variants)
.lootTable(lootTable)
.properties(properties)
.settings(settings)
.behavior(behaviors)
.events(events)
.lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true)))
.behavior(MiscUtils.castToMap(section.get("behavior"), true))
.events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")))
.build();
// 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.state.bind_failed", 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 IntArrayList()).add(state.customBlockState().registryId());
}
BukkitBlockManager.this.byId.put(id, block);
// generate mod assets
if (Config.generateModAssets()) {
for (ImmutableBlockState state : block.variantProvider().states()) {
Key realBlockId = BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState());
modBlockStates.put(realBlockId, tempVanillaBlockStateModels.get(state.vanillaBlockState().registryId()));
}
}
addBlock(id, block);
}
private void parseVanillaBlock(Pack pack, Path path, Key id, Map<String, Object> section) {
@@ -518,7 +438,8 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
private Map<String, Property<?>> parseProperties(Map<String, Object> propertiesSection) {
@NotNull
private Map<String, Property<?>> getProperties(Map<String, Object> propertiesSection) {
Map<String, Property<?>> properties = new HashMap<>();
for (Map.Entry<String, Object> entry : propertiesSection.entrySet()) {
Property<?> property = Properties.fromMap(entry.getKey(), MiscUtils.castToMap(entry.getValue(), false));
@@ -527,69 +448,40 @@ public class BukkitBlockManager extends AbstractBlockManager {
return properties;
}
@Nullable
private Pair<Key, Integer> parseAppearanceSection(Key id, Map<String, Object> section) {
@NotNull
private VanillaBlock getVanillaBlock(Key id, Map<String, Object> section) {
// require state non null
Object vanillaStateString = section.get("state");
if (vanillaStateString == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_state");
}
String vanillaBlockStateTag = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("state"), "warning.config.block.state.missing_state");
// get its registry id
int vanillaStateRegistryId = parseVanillaStateRegistryId(vanillaStateString.toString());
// check conflict
Key ifAny = this.tempRegistryIdConflictMap.get(vanillaStateRegistryId);
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(vanillaStateRegistryId)).getAsString(), ifAny.toString());
throw new LocalizedResourceConfigException("warning.config.block.state.conflict", BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(vanillaBlockStateRegistryId)).getAsString(), ifAny.toString());
}
// require models not to be null
Object models = section.get("models");
if (models == null) {
models = section.get("model");
}
if (models == null) {
Object models = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "models", "model"), "warning.config.block.state.missing_model");
List<JsonObject> variants = ResourceConfigUtils.parseConfigAsList(models, this::getVariantModel);
if (variants.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_model");
}
List<JsonObject> variants = new ArrayList<>();
if (models instanceof Map<?, ?> singleModelSection) {
loadVariantModel(variants, MiscUtils.castToMap(singleModelSection, false));
} else if (models instanceof List<?> modelList) {
for (Object model : modelList) {
if (model instanceof Map<?,?> singleModelMap) {
loadVariantModel(variants, MiscUtils.castToMap(singleModelMap, false));
}
}
}
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('}')));
String propertyData = blockState.substring(blockState.indexOf('[') + 1, blockState.lastIndexOf(']'));
Map<String, JsonElement> paths = this.blockStateOverrides.computeIfAbsent(block, k -> new HashMap<>());
if (variants.size() == 1) {
paths.put(propertyData, variants.get(0));
this.tempVanillaBlockStateModels.put(vanillaStateRegistryId, variants.get(0));
} else {
JsonArray array = new JsonArray();
for (JsonObject object : variants) {
array.add(object);
}
paths.put(propertyData, array);
this.tempVanillaBlockStateModels.put(vanillaStateRegistryId, array);
}
return Pair.of(block, vanillaStateRegistryId);
// 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 VanillaBlock(blockId, propertyNBT, vanillaBlockStateRegistryId);
}
private void loadVariantModel(List<JsonObject> variants, Map<String, Object> singleModelMap) {
private JsonObject getVariantModel(Map<String, Object> singleModelMap) {
JsonObject json = new JsonObject();
String modelPath = (String) singleModelMap.get("path");
if (modelPath == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.model.missing_path");
}
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);
}
@@ -602,10 +494,10 @@ public class BukkitBlockManager extends AbstractBlockManager {
if (generationMap != null) {
prepareModelGeneration(ModelGeneration.of(Key.of(modelPath), generationMap));
}
variants.add(json);
return json;
}
private int parseVanillaStateRegistryId(String blockState) {
private int getVanillaBlockStateRegistryId(String blockState) {
String[] split = blockState.split(":", 3);
if (split.length >= 4) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState);

View File

@@ -28,7 +28,7 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.*;
public class BukkitCustomBlock extends CustomBlock {
public class BukkitCustomBlock extends AbstractCustomBlock {
protected BukkitCustomBlock(
Key id,
@@ -147,17 +147,67 @@ public class BukkitCustomBlock extends CustomBlock {
}
public static Builder builder(Key id) {
return new Builder(id);
return new BuilderImpl(id);
}
public static class Builder extends CustomBlock.Builder {
public static class BuilderImpl implements Builder {
protected final Key id;
protected Map<String, Property<?>> properties;
protected Map<String, Integer> appearances;
protected Map<String, VariantState> variantMapper;
protected BlockSettings settings;
protected Map<String, Object> behavior;
protected LootTable<?> lootTable;
protected EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events;
protected Builder(Key id) {
super(id);
public BuilderImpl(Key id) {
this.id = id;
}
@Override
public CustomBlock build() {
public Builder events(EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
this.events = events;
return this;
}
@Override
public Builder appearances(Map<String, Integer> appearances) {
this.appearances = appearances;
return this;
}
@Override
public Builder behavior(Map<String, Object> behavior) {
this.behavior = behavior;
return this;
}
@Override
public Builder lootTable(LootTable<?> lootTable) {
this.lootTable = lootTable;
return this;
}
@Override
public Builder properties(Map<String, Property<?>> properties) {
this.properties = properties;
return this;
}
@Override
public Builder settings(BlockSettings settings) {
this.settings = settings;
return this;
}
@Override
public Builder variantMapper(Map<String, VariantState> variantMapper) {
this.variantMapper = variantMapper;
return this;
}
@Override
public @NotNull CustomBlock build() {
// 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)));

View File

@@ -13,7 +13,9 @@ import net.momirealms.craftengine.core.util.Key;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import java.util.*;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
private final Material material;

View File

@@ -36,7 +36,6 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

View File

@@ -51,7 +51,7 @@ public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature<Comma
for (int appearance : appearances) {
Component text = Component.text("|");
List<Integer> reals = blockManager.appearanceToRealStates(appearance);
if (reals == null) {
if (reals == null || reals.isEmpty()) {
Component hover = Component.text(baseBlockId.value() + ":" + i).color(NamedTextColor.GREEN);
hover = hover.append(Component.newline()).append(Component.text(BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(appearance)).getAsString()).color(NamedTextColor.GREEN));
text = text.color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover));

View File

@@ -3,16 +3,17 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.bukkit.util.MaterialUtils;
import net.momirealms.craftengine.bukkit.util.PlayerUtils;
import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.ItemKeys;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import net.momirealms.craftengine.core.plugin.command.FlagKeys;
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -26,7 +27,6 @@ import org.incendo.cloud.bukkit.parser.selector.MultiplePlayerSelectorParser;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.context.CommandInput;
import org.incendo.cloud.parser.flag.CommandFlag;
import org.incendo.cloud.parser.standard.StringParser;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.SuggestionProvider;

View File

@@ -101,10 +101,10 @@ public class BlockStateUtils {
}
public static Key getBlockOwnerId(Block block) {
return getBlockOwnerId(block.getBlockData());
return getBlockOwnerIdFromData(block.getBlockData());
}
public static Key getBlockOwnerId(BlockData block) {
public static Key getBlockOwnerIdFromData(BlockData block) {
Object blockState = blockDataToBlockState(block);
return getBlockOwnerIdFromState(blockState);
}

View File

@@ -274,7 +274,7 @@ public class InteractUtils {
}
public static boolean isInteractable(Player player, BlockData state, BlockHitResult hit, Item<ItemStack> item) {
Key blockType = BlockStateUtils.getBlockOwnerId(state);
Key blockType = BlockStateUtils.getBlockOwnerIdFromData(state);
if (INTERACTIONS.containsKey(blockType)) {
return INTERACTIONS.get(blockType).apply(player, item, state, hit);
} else {