Skip to content

Commit b8fe886

Browse files
Update custom item translator for 1.21.4, implement custom model data
1 parent 0d32bbe commit b8fe886

File tree

2 files changed

+38
-76
lines changed

2 files changed

+38
-76
lines changed

core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator_v2.java

Lines changed: 3 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -42,23 +42,18 @@
4242
import org.geysermc.geyser.api.item.custom.v2.CustomItemDefinition;
4343
import org.geysermc.geyser.api.item.custom.v2.predicate.CustomItemPredicate;
4444
import org.geysermc.geyser.item.GeyserCustomMappingData;
45-
import org.geysermc.geyser.item.Items;
4645
import org.geysermc.geyser.item.components.WearableSlot;
47-
import org.geysermc.geyser.item.type.ArmorItem;
4846
import org.geysermc.geyser.item.type.Item;
4947
import org.geysermc.geyser.registry.mappings.MappingsConfigReader;
5048
import org.geysermc.geyser.registry.type.GeyserMappingItem;
5149
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Consumable;
52-
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ConsumeEffect;
5350
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
5451
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
5552
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable;
5653
import org.geysermc.mcprotocollib.protocol.data.game.item.component.FoodProperties;
5754
import org.geysermc.mcprotocollib.protocol.data.game.item.component.UseCooldown;
58-
import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound;
5955

6056
import java.util.ArrayList;
61-
import java.util.HashMap;
6257
import java.util.List;
6358
import java.util.Map;
6459
import java.util.Objects;
@@ -181,7 +176,7 @@ private static NbtMapBuilder createComponentNbt(CustomItemDefinition customItemD
181176

182177
boolean canDestroyInCreative = true;
183178
if (vanillaMapping.getToolType() != null) { // This is not using the isTool boolean because it is not just a render type here.
184-
canDestroyInCreative = computeToolProperties(vanillaMapping.getToolType(), itemProperties, componentBuilder, vanillaJavaItem.attackDamage());
179+
canDestroyInCreative = computeToolProperties(vanillaMapping.getToolType(), itemProperties, componentBuilder, vanillaJavaItem.defaultAttackDamage());
185180
}
186181
itemProperties.putBoolean("can_destroy_in_creative", canDestroyInCreative);
187182

@@ -498,60 +493,9 @@ private static NbtMap xyzToScaleList(float x, float y, float z) {
498493
return NbtMap.builder().putList("scale", NbtType.FLOAT, List.of(x, y, z)).build();
499494
}
500495

501-
// TODO this needs to be a simpler method once we just load default vanilla components from mappings or something
496+
// TODO is this right?
502497
private static DataComponents patchDataComponents(Item javaItem, CustomItemDefinition definition) {
503-
DataComponents components = new DataComponents(new HashMap<>()); // TODO faster map ?
504-
505-
components.put(DataComponentType.MAX_STACK_SIZE, javaItem.maxStackSize());
506-
components.put(DataComponentType.MAX_DAMAGE, javaItem.maxDamage());
507-
508-
Consumable consumable = getItemConsumable(javaItem);
509-
if (consumable != null) {
510-
components.put(DataComponentType.CONSUMABLE, consumable);
511-
}
512-
513-
if (canAlwaysEat(javaItem)) {
514-
components.put(DataComponentType.FOOD, new FoodProperties(0, 0, true));
515-
}
516-
517-
if (javaItem.glint()) {
518-
components.put(DataComponentType.ENCHANTMENT_GLINT_OVERRIDE, true);
519-
}
520-
521-
if (javaItem instanceof ArmorItem armor) { // TODO equippable
522-
}
523-
524-
components.put(DataComponentType.RARITY, javaItem.rarity().ordinal());
525-
526-
components.getDataComponents().putAll(definition.components().getDataComponents());
527-
return components;
528-
}
529-
530-
private static Consumable getItemConsumable(Item item) {
531-
if (item == Items.APPLE || item == Items.BAKED_POTATO || item == Items.BEETROOT || item == Items.BEETROOT_SOUP || item == Items.BREAD
532-
|| item == Items.CARROT || item == Items.CHORUS_FRUIT || item == Items.COOKED_CHICKEN || item == Items.COOKED_COD
533-
|| item == Items.COOKED_MUTTON || item == Items.COOKED_PORKCHOP || item == Items.COOKED_RABBIT || item == Items.COOKED_SALMON
534-
|| item == Items.COOKIE || item == Items.ENCHANTED_GOLDEN_APPLE || item == Items.GOLDEN_APPLE || item == Items.GLOW_BERRIES
535-
|| item == Items.GOLDEN_CARROT || item == Items.MELON_SLICE || item == Items.MUSHROOM_STEW || item == Items.POISONOUS_POTATO
536-
|| item == Items.POTATO || item == Items.PUFFERFISH || item == Items.PUMPKIN_PIE || item == Items.RABBIT_STEW
537-
|| item == Items.BEEF || item == Items.CHICKEN || item == Items.COD || item == Items.MUTTON || item == Items.PORKCHOP
538-
|| item == Items.RABBIT || item == Items.ROTTEN_FLESH || item == Items.SPIDER_EYE || item == Items.COOKED_BEEF
539-
|| item == Items.SUSPICIOUS_STEW || item == Items.SWEET_BERRIES || item == Items.TROPICAL_FISH) {
540-
return Consumables.DEFAULT_FOOD;
541-
} else if (item == Items.POTION) {
542-
return Consumables.DEFAULT_DRINK;
543-
} else if (item == Items.HONEY_BOTTLE) {
544-
return Consumables.HONEY_BOTTLE;
545-
} else if (item == Items.OMINOUS_BOTTLE) {
546-
return Consumables.OMINOUS_BOTTLE;
547-
} else if (item == Items.DRIED_KELP) {
548-
return Consumables.DRIED_KELP;
549-
}
550-
return null;
551-
}
552-
553-
private static boolean canAlwaysEat(Item item) {
554-
return item == Items.CHORUS_FRUIT || item == Items.ENCHANTED_GOLDEN_APPLE || item == Items.GOLDEN_APPLE || item == Items.HONEY_BOTTLE || item == Items.SUSPICIOUS_STEW;
498+
return javaItem.gatherComponents(definition.components());
555499
}
556500

557501
@SuppressWarnings("unchecked")
@@ -568,13 +512,4 @@ private static void addItemTag(NbtMapBuilder builder, String tag) {
568512
}
569513
}
570514
}
571-
572-
private static final class Consumables {
573-
private static final Consumable DEFAULT_FOOD = new Consumable(1.6F, Consumable.ItemUseAnimation.EAT, BuiltinSound.ENTITY_GENERIC_EAT, true, List.of());
574-
private static final Consumable DEFAULT_DRINK = new Consumable(1.6F, Consumable.ItemUseAnimation.DRINK, BuiltinSound.ENTITY_GENERIC_DRINK, false, List.of());
575-
private static final Consumable HONEY_BOTTLE = new Consumable(2.0F, Consumable.ItemUseAnimation.DRINK, BuiltinSound.ITEM_HONEY_BOTTLE_DRINK, false, List.of());
576-
private static final Consumable OMINOUS_BOTTLE = new Consumable(2.0F, Consumable.ItemUseAnimation.DRINK, BuiltinSound.ITEM_HONEY_BOTTLE_DRINK,
577-
false, List.of(new ConsumeEffect.PlaySound(BuiltinSound.ITEM_OMINOUS_BOTTLE_DISPOSE)));
578-
private static final Consumable DRIED_KELP = new Consumable(0.8F, Consumable.ItemUseAnimation.EAT, BuiltinSound.ENTITY_GENERIC_EAT, false, List.of());
579-
}
580515
}

core/src/main/java/org/geysermc/geyser/translator/item/CustomItemTranslator.java

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
package org.geysermc.geyser.translator.item;
2727

2828
import com.google.common.collect.Multimap;
29+
import lombok.extern.slf4j.Slf4j;
2930
import net.kyori.adventure.key.Key;
3031
import org.cloudburstmc.protocol.bedrock.data.TrimMaterial;
3132
import org.geysermc.geyser.api.item.custom.v2.CustomItemDefinition;
@@ -34,6 +35,7 @@
3435
import org.geysermc.geyser.api.item.custom.v2.predicate.RangeDispatchPredicate;
3536
import org.geysermc.geyser.api.item.custom.v2.predicate.match.ChargeType;
3637
import org.geysermc.geyser.api.item.custom.v2.predicate.MatchPredicate;
38+
import org.geysermc.geyser.api.item.custom.v2.predicate.match.CustomModelDataString;
3739
import org.geysermc.geyser.api.item.custom.v2.predicate.match.MatchPredicateProperty;
3840
import org.geysermc.geyser.item.Items;
3941
import org.geysermc.geyser.level.JavaDimension;
@@ -42,6 +44,7 @@
4244
import org.geysermc.geyser.util.MinecraftKey;
4345
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
4446
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ArmorTrim;
47+
import org.geysermc.mcprotocollib.protocol.data.game.item.component.CustomModelData;
4548
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
4649
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
4750
import it.unimi.dsi.fastutil.Pair;
@@ -51,10 +54,12 @@
5154

5255
import java.util.Collection;
5356
import java.util.List;
57+
import java.util.function.Function;
5458

5559
/**
5660
* This is only a separate class for testing purposes so we don't have to load in GeyserImpl in ItemTranslator.
5761
*/
62+
@Slf4j
5863
public final class CustomItemTranslator {
5964

6065
@Nullable
@@ -68,13 +73,13 @@ public static ItemDefinition getCustomItem(GeyserSession session, int stackSize,
6873
return null;
6974
}
7075

71-
Key itemModel = components.getOrDefault(DataComponentType.ITEM_MODEL, MinecraftKey.key("air")); // TODO fallback onto default item model (when thats done by chris)
76+
Key itemModel = components.getOrDefault(DataComponentType.ITEM_MODEL, MinecraftKey.key("air"));
77+
System.out.println(itemModel + " is the model!");
7278
Collection<Pair<CustomItemDefinition, ItemDefinition>> customItems = allCustomItems.get(itemModel);
7379
if (customItems.isEmpty()) {
7480
return null;
7581
}
7682

77-
// TODO check if definitions/predicates are in the correct order
7883
for (Pair<CustomItemDefinition, ItemDefinition> customModel : customItems) {
7984
if (customModel.first().model().equals(itemModel)) {
8085
boolean allMatch = true;
@@ -97,9 +102,9 @@ private static boolean predicateMatches(GeyserSession session, CustomItemPredica
97102
return switch (condition.property()) {
98103
case BROKEN -> nextDamageWillBreak(components);
99104
case DAMAGED -> isDamaged(components);
100-
case CUSTOM_MODEL_DATA -> false; // TODO 1.21.4
101-
};
102-
} else if (predicate instanceof MatchPredicate<?> match) { // TODO not much of a fun of the casts here, find a solution for the types?
105+
case CUSTOM_MODEL_DATA -> getCustomBoolean(components, condition.index());
106+
} == condition.expected();
107+
} else if (predicate instanceof MatchPredicate<?> match) { // TODO not much of a fan of the casts here, find a solution for the types?
103108
if (match.property() == MatchPredicateProperty.CHARGE_TYPE) {
104109
ChargeType expected = (ChargeType) match.data();
105110
List<ItemStack> charged = components.get(DataComponentType.CHARGED_PROJECTILES);
@@ -127,8 +132,8 @@ private static boolean predicateMatches(GeyserSession session, CustomItemPredica
127132
RegistryEntryData<JavaDimension> registered = session.getRegistryCache().dimensions().entryByValue(session.getDimensionType());
128133
return registered != null && dimension.equals(registered.key());
129134
} else if (match.property() == MatchPredicateProperty.CUSTOM_MODEL_DATA) {
130-
// TODO 1.21.4
131-
return false;
135+
CustomModelDataString expected = (CustomModelDataString) match.data();
136+
return expected.value().equals(getSafeCustomModelData(components, CustomModelData::strings, expected.index()));
132137
}
133138
} else if (predicate instanceof RangeDispatchPredicate rangeDispatch) {
134139
double propertyValue = switch (rangeDispatch.property()) {
@@ -145,14 +150,36 @@ private static boolean predicateMatches(GeyserSession session, CustomItemPredica
145150
}
146151
case DAMAGE -> tryNormalize(rangeDispatch, components.get(DataComponentType.DAMAGE), components.get(DataComponentType.MAX_DAMAGE));
147152
case COUNT -> tryNormalize(rangeDispatch, stackSize, components.get(DataComponentType.MAX_STACK_SIZE));
148-
case CUSTOM_MODEL_DATA -> 0.0; // TODO 1.21.4
153+
case CUSTOM_MODEL_DATA -> getCustomFloat(components, rangeDispatch.index());
149154
} * rangeDispatch.scale();
150155
return propertyValue >= rangeDispatch.threshold();
151156
}
152157

153158
throw new IllegalStateException("Unimplemented predicate type");
154159
}
155160

161+
private static boolean getCustomBoolean(DataComponents components, int index) {
162+
Boolean b = getSafeCustomModelData(components, CustomModelData::flags, index);
163+
return b != null && b;
164+
}
165+
166+
private static float getCustomFloat(DataComponents components, int index) {
167+
Float f = getSafeCustomModelData(components, CustomModelData::floats, index);
168+
return f == null ? 0.0F : f;
169+
}
170+
171+
private static <T> T getSafeCustomModelData(DataComponents components, Function<CustomModelData, List<T>> listGetter, int index) {
172+
CustomModelData modelData = components.get(DataComponentType.CUSTOM_MODEL_DATA);
173+
if (modelData == null || index < 0) {
174+
return null;
175+
}
176+
List<T> list = listGetter.apply(modelData);
177+
if (index < list.size()) {
178+
return list.get(index);
179+
}
180+
return null;
181+
}
182+
156183
private static double tryNormalize(RangeDispatchPredicate predicate, @Nullable Integer value, @Nullable Integer max) {
157184
if (value == null) {
158185
return 0.0;

0 commit comments

Comments
 (0)