diff --git a/src/main/java/me/geyserextensionists/geyserdisplayentity/GeyserDisplayEntity.java b/src/main/java/me/geyserextensionists/geyserdisplayentity/GeyserDisplayEntity.java index b79063b..d2e6fe0 100644 --- a/src/main/java/me/geyserextensionists/geyserdisplayentity/GeyserDisplayEntity.java +++ b/src/main/java/me/geyserextensionists/geyserdisplayentity/GeyserDisplayEntity.java @@ -38,6 +38,7 @@ public class GeyserDisplayEntity implements Extension { public static final Integer MAX_VALUE = 1000000; public static final Integer MIN_VALUE = -1000000; + private static final float DEFAULT_DISPLAY_HEIGHT = 1.975f; @Subscribe public void onLoad(GeyserPreInitializeEvent event) { @@ -48,6 +49,8 @@ public void onLoad(GeyserPreInitializeEvent event) { @Subscribe public void onEntityPropertiesEvent(GeyserDefineEntityPropertiesEvent event) { try { + float displayHeight = getDisplayHeight(); + EntityDefinition entityBase = EntityDefinition.builder(Entity::new) .addTranslator(MetadataTypes.BYTE, Entity::setFlags) .addTranslator(MetadataTypes.INT, Entity::setAir) // Air/bubbles @@ -93,7 +96,7 @@ public void onEntityPropertiesEvent(GeyserDefineEntityPropertiesEvent event) { BLOCK_DISPLAY = EntityDefinition.inherited(BlockDisplayEntity::new, slotDisplayBase) .type(EntityType.BLOCK_DISPLAY) - .height(configManager.getConfig().getInt("general.height")).width(0.2f) + .height(displayHeight).width(0.2f) .propertiesBuilder(displayPropBuilder) .identifier("geyser:block_display") .addTranslator(MetadataTypes.BLOCK_STATE, BlockDisplayEntity::setDisplayedBlockState) @@ -101,7 +104,7 @@ public void onEntityPropertiesEvent(GeyserDefineEntityPropertiesEvent event) { ITEM_DISPLAY = EntityDefinition.inherited(ItemDisplayEntity::new, slotDisplayBase) .type(EntityType.ITEM_DISPLAY) - .height(configManager.getConfig().getInt("general.height")).width(0.2f) + .height(displayHeight).width(0.2f) .propertiesBuilder(displayPropBuilder) .identifier("geyser:item_display") .addTranslator(MetadataTypes.ITEM_STACK, ItemDisplayEntity::setDisplayedItem) @@ -153,6 +156,15 @@ private void loadManagers() { this.configManager = new ConfigManager(); } + private float getDisplayHeight() { + double configuredHeight = configManager.getConfig().getDouble("general.height"); + if (configuredHeight <= 0) { + return DEFAULT_DISPLAY_HEIGHT; + } + + return (float) configuredHeight; + } + public static GeyserDisplayEntity getExtension() { return extension; } diff --git a/src/main/java/me/geyserextensionists/geyserdisplayentity/entity/ItemDisplayEntity.java b/src/main/java/me/geyserextensionists/geyserdisplayentity/entity/ItemDisplayEntity.java index 4999a66..7156515 100644 --- a/src/main/java/me/geyserextensionists/geyserdisplayentity/entity/ItemDisplayEntity.java +++ b/src/main/java/me/geyserextensionists/geyserdisplayentity/entity/ItemDisplayEntity.java @@ -52,6 +52,9 @@ public void setDisplayedItem(EntityMetadata entityMetadata) { return; } + // Reset config to general defaults so unmapped items always have valid display options + config = GeyserDisplayEntity.getExtension().getConfigManager().getConfig().getConfigurationSection("general"); + ItemData item = ItemTranslator.translateToBedrock(session, stack); this.item = item; @@ -67,22 +70,33 @@ public void setDisplayedItem(EntityMetadata entityMetadata) { String type = session.getItemMappings().getMapping(stack.getId()).getJavaItem().javaIdentifier(); + boolean mappingApplied = false; + for (FileConfiguration mappingsConfig : GeyserDisplayEntity.getExtension().getConfigManager().getConfigMappingsCache().values()) { + if (mappingsConfig == null) continue; + for (Object mappingKey : mappingsConfig.getRootNode().childrenMap().keySet()) { String mappingString = mappingKey.toString(); FileConfiguration mappingConfig = mappingsConfig.getConfigurationSection(mappingString); if (mappingConfig == null) continue; - if (!mappingConfig.getString("type").equals(type)) continue; + String mappingType = mappingConfig.getString("type"); + if (mappingType == null || !mappingType.trim().equals(type)) continue; + + boolean applied; + if (shouldUseLegacyMapping(mappingConfig)) { + applied = applyLegacyModelData(stack, mappingConfig); + } else { + applied = applyModernItemModels(item, mappingConfig); + } - if (GeyserDisplayEntity.getExtension().getConfigManager().getConfig().getBoolean("general.use-legacy-models")) { - applyLegacyModelData(stack, mappingConfig); + if (applied) { + mappingApplied = true; break; } - - applyModernItemModels(item, mappingConfig); - break; } + + if (mappingApplied) break; } if (!item.getDefinition().getIdentifier().startsWith("minecraft:")) { @@ -96,53 +110,113 @@ public void setDisplayedItem(EntityMetadata entityMetadata) { // HIDE_TYPES check only if stack is present (it is) String javaID = session.getItemMappings().getMapping(stack).getJavaItem().javaIdentifier(); + boolean wasHiddenByType = needHide; - if (GeyserDisplayEntity.getExtension().getConfigManager().getConfig().getStringList("hide-types").contains(javaID)) { + // Keep hide-types behavior for vanilla items, but allow custom translated items + // (e.g. custom model data on minecraft:bone) to remain visible unless explicitly forced. + FileConfiguration rootConfig = GeyserDisplayEntity.getExtension().getConfigManager().getConfig(); + boolean hiddenByType = rootConfig.getStringList("hide-types").contains(javaID); + boolean forceHiddenCustomType = rootConfig.getStringList("hide-custom-types").contains(javaID); + + if ((hiddenByType && !custom) || forceHiddenCustomType) { setInvisible(true); needHide = true; this.dirtyMetadata.put(EntityDataTypes.SCALE, 0f); } else { needHide = false; + if (wasHiddenByType) { + setInvisible(false); + + if (config != null && config.getBoolean("vanilla-scale")) { + applyScale(); + } else { + this.dirtyMetadata.put(EntityDataTypes.SCALE, 1f); + } + } } updateMainHand(session); } - private void applyLegacyModelData(ItemStack item, FileConfiguration mappingConfig) { + private boolean shouldUseLegacyMapping(FileConfiguration mappingConfig) { + // Type + options only is a wildcard mapping; treat as modern-style default. + if (!mappingConfig.contains("item-identifier") && !mappingConfig.contains("model-data")) { + return false; + } + + if (mappingConfig.contains("item-identifier")) return false; + if (mappingConfig.contains("model-data")) return true; + + FileConfiguration rootConfig = GeyserDisplayEntity.getExtension().getConfigManager().getConfig(); + if (rootConfig.isBoolean("general.use-legacy-models")) { + return rootConfig.getBoolean("general.use-legacy-models"); + } + + return false; + } + + private boolean applyLegacyModelData(ItemStack item, FileConfiguration mappingConfig) { CustomModelData modelData = null; DataComponents components = item.getDataComponentsPatch(); if (components != null) modelData = components.get(DataComponentTypes.CUSTOM_MODEL_DATA); + if (!mappingConfig.contains("model-data")) { + return entityApplyDisplayConfig(mappingConfig); + } + if (mappingConfig.getInt("model-data") == -1) { - entityApplyDisplayConfig(mappingConfig); - return; + return entityApplyDisplayConfig(mappingConfig); } - if (modelData == null) return; + if (modelData == null) return false; + + List modelDataFloats = modelData.floats(); + if (modelDataFloats == null || modelDataFloats.isEmpty()) return false; - if (Math.abs(mappingConfig.getInt("model-data") - modelData.floats().get(0)) < 0.5) { - entityApplyDisplayConfig(mappingConfig); + if (Math.abs(mappingConfig.getInt("model-data") - modelDataFloats.get(0)) < 0.5) { + return entityApplyDisplayConfig(mappingConfig); } + + return false; } - private void applyModernItemModels(ItemData itemData, FileConfiguration mappingConfig) { + private boolean applyModernItemModels(ItemData itemData, FileConfiguration mappingConfig) { String itemBedrockIdentifier = itemData.getDefinition().getIdentifier().replace("geyser_custom:", ""); + String mappingIdentifier = mappingConfig.getString("item-identifier"); - if (mappingConfig.getString("item-identifier").equals("none")) { - entityApplyDisplayConfig(mappingConfig); - return; + if (mappingIdentifier == null) { + return entityApplyDisplayConfig(mappingConfig); } - if (mappingConfig.getString("item-identifier").equalsIgnoreCase(itemBedrockIdentifier)) { - entityApplyDisplayConfig(mappingConfig); + String normalizedIdentifier = mappingIdentifier.trim(); + if (normalizedIdentifier.isEmpty() || normalizedIdentifier.equalsIgnoreCase("none")) { + return entityApplyDisplayConfig(mappingConfig); } + + if (normalizedIdentifier.equalsIgnoreCase(itemBedrockIdentifier)) { + return entityApplyDisplayConfig(mappingConfig); + } + + return false; } - private void entityApplyDisplayConfig(FileConfiguration mappingConfig) { - config = mappingConfig.getConfigurationSection("displayentityoptions"); + private boolean entityApplyDisplayConfig(FileConfiguration mappingConfig) { + FileConfiguration mappingOptions = mappingConfig.getConfigurationSection("displayentityoptions"); + if (mappingOptions == null) { + // Legacy compatibility for pre-migration config.yml format + mappingOptions = mappingConfig.getConfigurationSection("options"); + } + + if (mappingOptions != null) { + config = mappingOptions; + } else if (config == null) { + return false; + } + setOffset(config.getDouble("y-offset")); if (config.getBoolean("vanilla-scale")) applyScale(); + return true; } @Override @@ -161,7 +235,7 @@ public void updateMainHand(GeyserSession session) { ItemData helmet = ItemData.AIR; // TODO ItemData chest = item; - if (custom && !config.getBoolean("hand")) { + if (custom && (config == null || !config.getBoolean("hand"))) { MobArmorEquipmentPacket armorEquipmentPacket = new MobArmorEquipmentPacket(); armorEquipmentPacket.setRuntimeEntityId(geyserId); armorEquipmentPacket.setHelmet(helmet); @@ -231,7 +305,7 @@ private static int getColor(int argb) { } public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { - double yOffset = config.getDouble("y-offset"); + double yOffset = (config != null) ? config.getDouble("y-offset") : 0; position = position.clone().add(0, yOffset, 0); diff --git a/src/main/java/me/geyserextensionists/geyserdisplayentity/entity/SlotDisplayEntity.java b/src/main/java/me/geyserextensionists/geyserdisplayentity/entity/SlotDisplayEntity.java index 6797e44..4e0290a 100644 --- a/src/main/java/me/geyserextensionists/geyserdisplayentity/entity/SlotDisplayEntity.java +++ b/src/main/java/me/geyserextensionists/geyserdisplayentity/entity/SlotDisplayEntity.java @@ -44,11 +44,15 @@ public void updateMainHand(GeyserSession session) { } + protected FileConfiguration getGeneralConfig() { + return GeyserDisplayEntity.getExtension().getConfigManager().getConfig().getConfigurationSection("general"); + } + @Override public void initializeMetadata() { super.initializeMetadata(); - config = GeyserDisplayEntity.getExtension().getConfigManager().getConfig().getConfigurationSection("general"); + config = getGeneralConfig(); item = ItemData.AIR; translation = Vector3f.from(0, 0, 0); @@ -66,7 +70,7 @@ public void initializeMetadata() { propertyManager.addProperty(new FloatProperty(Identifier.of("geyser:s_y"), MAX_VALUE, MIN_VALUE, 0F), scale.getY()); propertyManager.addProperty(new FloatProperty(Identifier.of("geyser:s_z"), MAX_VALUE, MIN_VALUE, 0F), scale.getZ()); - if (config.getBoolean("vanilla-scale")) applyScale(); + if (config != null && config.getBoolean("vanilla-scale")) applyScale(); propertyManager.addProperty(new FloatProperty(Identifier.of("geyser:r_x"), 180F, -180F, 0F), MathUtils.wrapDegrees(rotation.getX())); propertyManager.addProperty(new FloatProperty(Identifier.of("geyser:r_y"), 180F, -180F, 0F), MathUtils.wrapDegrees(-rotation.getY())); @@ -105,7 +109,7 @@ public void setTranslation(EntityMetadata entityMetadata) { public void setScale(EntityMetadata entityMetadata) { this.scale = entityMetadata.getValue(); - if (config.getBoolean("vanilla-scale")) applyScale(); + if (config != null && config.getBoolean("vanilla-scale")) applyScale(); propertyManager.addProperty(new FloatProperty(Identifier.of("geyser:s_x"), MAX_VALUE, MIN_VALUE, 0F), scale.getX()); propertyManager.addProperty(new FloatProperty(Identifier.of("geyser:s_y"), MAX_VALUE, MIN_VALUE, 0F), scale.getY()); @@ -115,7 +119,12 @@ public void setScale(EntityMetadata entityMetadata) { protected void applyScale() { Vector3f vector3f = this.scale; float scale = (vector3f.getX() + vector3f.getY() + vector3f.getZ()) / 3; - if (config.getBoolean("vanilla-scale")) scale *= (float) config.getDouble("vanilla-scale-multiplier"); + if (config != null && config.getBoolean("vanilla-scale")) { + float multiplier = config.contains("vanilla-scale-multiplier") + ? (float) config.getDouble("vanilla-scale-multiplier") + : 1.0f; + scale *= multiplier; + } this.dirtyMetadata.put(EntityDataTypes.SCALE, scale); } @@ -221,4 +230,4 @@ protected void hackRotation(float x, float y, float z) { updateBedrockEntityProperties(); } -} \ No newline at end of file +} diff --git a/src/main/java/me/geyserextensionists/geyserdisplayentity/managers/ConfigManager.java b/src/main/java/me/geyserextensionists/geyserdisplayentity/managers/ConfigManager.java index 12f0823..172e400 100644 --- a/src/main/java/me/geyserextensionists/geyserdisplayentity/managers/ConfigManager.java +++ b/src/main/java/me/geyserextensionists/geyserdisplayentity/managers/ConfigManager.java @@ -6,17 +6,19 @@ import java.io.File; import java.nio.file.Files; -import java.util.HashMap; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; public class ConfigManager { private FileConfiguration config, lang; - private HashMap configMappingsCache; + private LinkedHashMap configMappingsCache; public ConfigManager() { load(); - loadConfigMappings(); } public void load() { @@ -29,15 +31,25 @@ public void load() { } private void loadConfigMappings() { - HashMap tempConfigMappingsCache = new HashMap<>(); + LinkedHashMap tempConfigMappingsCache = new LinkedHashMap<>(); + List mappingFiles = new ArrayList<>(FileUtils.getAllFiles(GeyserDisplayEntity.getExtension().dataFolder().resolve("Mappings").toFile(), ".yml")); + mappingFiles.sort(Comparator.comparing(File::getAbsolutePath, String.CASE_INSENSITIVE_ORDER)); - for (File file : FileUtils.getAllFiles(GeyserDisplayEntity.getExtension().dataFolder().resolve("Mappings").toFile(), ".yml")) { + // Priority: explicit mapping files are evaluated first in deterministic order. + for (File file : mappingFiles) { FileConfiguration mappingsConfigFile = new FileConfiguration("Mappings/" + file.getName()); FileConfiguration mappingsConfig = mappingsConfigFile.getConfigurationSection("mappings"); + if (mappingsConfig == null) continue; tempConfigMappingsCache.put(file.getName().replace(".yml", ""), mappingsConfig); } + // Fallback priority: legacy config.yml mappings are evaluated last. + FileConfiguration legacyMappings = config.getConfigurationSection("mappings"); + if (legacyMappings != null) { + tempConfigMappingsCache.put("legacy-config", legacyMappings); + } + this.configMappingsCache = tempConfigMappingsCache; } @@ -49,7 +61,7 @@ public FileConfiguration getLang() { return lang; } - public HashMap getConfigMappingsCache() { + public LinkedHashMap getConfigMappingsCache() { return configMappingsCache; } } diff --git a/src/main/java/me/geyserextensionists/geyserdisplayentity/util/FileUtils.java b/src/main/java/me/geyserextensionists/geyserdisplayentity/util/FileUtils.java index fd42258..0a79ab6 100644 --- a/src/main/java/me/geyserextensionists/geyserdisplayentity/util/FileUtils.java +++ b/src/main/java/me/geyserextensionists/geyserdisplayentity/util/FileUtils.java @@ -38,8 +38,10 @@ public static void createFiles(GeyserDisplayEntity extension, String fileName) { public static List getAllFiles(File folder, String fileType) { List files = new ArrayList<>(); if (folder == null || !folder.exists()) return files; + File[] listedFiles = folder.listFiles(); + if (listedFiles == null) return files; - for (File file : folder.listFiles()) { + for (File file : listedFiles) { if (file.isDirectory()) { files.addAll(getAllFiles(file, fileType)); } else if (file.getName().endsWith(fileType)) { diff --git a/src/main/resources/Extension/config.yml b/src/main/resources/Extension/config.yml index 357692a..7e2edda 100644 --- a/src/main/resources/Extension/config.yml +++ b/src/main/resources/Extension/config.yml @@ -15,4 +15,8 @@ general: # Invisible item types hide-types: - "minecraft:leather_horse_armor" - - "minecraft:bone" \ No newline at end of file + - "minecraft:bone" + +# Always hide item types, even when translated to custom Bedrock item definitions +hide-custom-types: + - "minecraft:leather_horse_armor"