Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -48,6 +49,8 @@ public void onLoad(GeyserPreInitializeEvent event) {
@Subscribe
public void onEntityPropertiesEvent(GeyserDefineEntityPropertiesEvent event) {
try {
float displayHeight = getDisplayHeight();

EntityDefinition<Entity> entityBase = EntityDefinition.builder(Entity::new)
.addTranslator(MetadataTypes.BYTE, Entity::setFlags)
.addTranslator(MetadataTypes.INT, Entity::setAir) // Air/bubbles
Expand Down Expand Up @@ -93,15 +96,15 @@ 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)
.build();

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)
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public void setDisplayedItem(EntityMetadata<ItemStack, ?> 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;

Expand All @@ -67,22 +70,33 @@ public void setDisplayedItem(EntityMetadata<ItemStack, ?> 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:")) {
Expand All @@ -96,53 +110,113 @@ public void setDisplayedItem(EntityMetadata<ItemStack, ?> 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<Float> 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
Expand All @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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()));
Expand Down Expand Up @@ -105,7 +109,7 @@ public void setTranslation(EntityMetadata<Vector3f, ?> entityMetadata) {
public void setScale(EntityMetadata<Vector3f, ?> 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());
Expand All @@ -115,7 +119,12 @@ public void setScale(EntityMetadata<Vector3f, ?> 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);
}

Expand Down Expand Up @@ -221,4 +230,4 @@ protected void hackRotation(float x, float y, float z) {

updateBedrockEntityProperties();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, FileConfiguration> configMappingsCache;
private LinkedHashMap<String, FileConfiguration> configMappingsCache;

public ConfigManager() {
load();
loadConfigMappings();
}

public void load() {
Expand All @@ -29,15 +31,25 @@ public void load() {
}

private void loadConfigMappings() {
HashMap<String, FileConfiguration> tempConfigMappingsCache = new HashMap<>();
LinkedHashMap<String, FileConfiguration> tempConfigMappingsCache = new LinkedHashMap<>();
List<File> 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;
}

Expand All @@ -49,7 +61,7 @@ public FileConfiguration getLang() {
return lang;
}

public HashMap<String, FileConfiguration> getConfigMappingsCache() {
public LinkedHashMap<String, FileConfiguration> getConfigMappingsCache() {
return configMappingsCache;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ public static void createFiles(GeyserDisplayEntity extension, String fileName) {
public static List<File> getAllFiles(File folder, String fileType) {
List<File> 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)) {
Expand Down
6 changes: 5 additions & 1 deletion src/main/resources/Extension/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ general:
# Invisible item types
hide-types:
- "minecraft:leather_horse_armor"
- "minecraft:bone"
- "minecraft:bone"

# Always hide item types, even when translated to custom Bedrock item definitions
hide-custom-types:
- "minecraft:leather_horse_armor"