More changes - remove getPickItemComponents in WorldManager, separate additional and base component

This commit is contained in:
onebeastchris 2024-12-02 02:23:02 +08:00
parent feecc47092
commit 48ae28432e
21 changed files with 176 additions and 276 deletions

View file

@ -28,37 +28,24 @@ package org.geysermc.geyser.platform.mod.world;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minecraft.SharedConstants;
import net.minecraft.core.BlockPos;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BannerBlockEntity;
import net.minecraft.world.level.block.entity.BannerPatternLayers;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.DecoratedPotBlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunkSection;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3i;
import org.geysermc.geyser.level.GeyserWorldManager;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public class GeyserModWorldManager extends GeyserWorldManager {
@ -117,49 +104,6 @@ public class GeyserModWorldManager extends GeyserWorldManager {
return GameMode.byId(server.getDefaultGameType().getId());
}
@NonNull
@Override
public CompletableFuture<org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
CompletableFuture<org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents> future = new CompletableFuture<>();
server.execute(() -> {
ServerPlayer player = getPlayer(session);
if (player == null) {
future.complete(null);
return;
}
BlockPos pos = new BlockPos(x, y, z);
// Don't create a new block entity if invalid
//noinspection resource - level() is just a getter
BlockEntity blockEntity = player.level().getChunkAt(pos).getBlockEntity(pos);
if (blockEntity instanceof BannerBlockEntity banner) {
// Potentially exposes other NBT data? But we need to get the NBT data for the banner patterns *and*
// the banner might have a custom name, both of which a Java client knows and caches
ItemStack itemStack = banner.getItem();
org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents components =
new org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents(new HashMap<>());
components.put(DataComponentType.DAMAGE, itemStack.getDamageValue());
Component customName = itemStack.getComponents().get(DataComponents.CUSTOM_NAME);
if (customName != null) {
components.put(DataComponentType.CUSTOM_NAME, toKyoriComponent(customName));
}
BannerPatternLayers pattern = itemStack.get(DataComponents.BANNER_PATTERNS);
if (pattern != null) {
components.put(DataComponentType.BANNER_PATTERNS, toPatternList(pattern));
}
future.complete(components);
return;
}
future.complete(null);
});
return future;
}
@Override
public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer<List<String>> apply) {
server.execute(() -> {
@ -184,20 +128,4 @@ public class GeyserModWorldManager extends GeyserWorldManager {
private ServerPlayer getPlayer(GeyserSession session) {
return server.getPlayerList().getPlayer(session.getPlayerEntity().getUuid());
}
private static net.kyori.adventure.text.Component toKyoriComponent(Component component) {
String json = Component.Serializer.toJson(component, RegistryAccess.EMPTY);
return GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty());
}
private static List<BannerPatternLayer> toPatternList(BannerPatternLayers patternLayers) {
return patternLayers.layers().stream()
.map(layer -> {
BannerPatternLayer.BannerPattern pattern = new BannerPatternLayer.BannerPattern(
MinecraftKey.key(layer.pattern().value().assetId().toString()), layer.pattern().value().translationKey()
);
return new BannerPatternLayer(Holder.ofCustom(pattern), layer.color().getId());
})
.toList();
}
}

View file

@ -25,18 +25,14 @@
package org.geysermc.geyser.platform.spigot.world.manager;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.DecoratedPot;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i;
import org.geysermc.erosion.bukkit.BukkitUtils;
import org.geysermc.erosion.bukkit.PickBlockUtils;
import org.geysermc.erosion.bukkit.SchedulerUtils;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.level.GameRule;
@ -44,7 +40,6 @@ import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import java.util.List;
import java.util.Objects;
@ -128,20 +123,6 @@ public class GeyserSpigotWorldManager extends WorldManager {
return GameMode.byId(Bukkit.getDefaultGameMode().ordinal());
}
@Override
public @NonNull CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
Player bukkitPlayer;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
return CompletableFuture.completedFuture(null);
}
CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
// Paper 1.19.3 complains about async access otherwise.
// java.lang.IllegalStateException: Tile is null, asynchronous access?
SchedulerUtils.runTask(this.plugin, () -> future.complete(PickBlockUtils.pickBlock(block)), block);
return future.thenApply(RAW_TRANSFORMER);
}
public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer<List<String>> apply) {
Player bukkitPlayer;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {

View file

@ -38,6 +38,8 @@ import org.geysermc.geyser.item.enchantment.EnchantmentComponent;
import org.geysermc.geyser.item.type.DyeItem;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.geyser.session.cache.tags.GeyserHolderSet;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
@ -51,6 +53,8 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.Object
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
import java.util.Collections;
import java.util.Locale;
@ -58,7 +62,7 @@ import java.util.UUID;
public class WolfEntity extends TameableEntity {
private byte collarColor = 14; // Red - default
private GeyserHolderSet<Item> repairableItems = null;
private boolean isCurseOfBinding = false;
public WolfEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
@ -123,9 +127,11 @@ public class WolfEntity extends TameableEntity {
}
@Override
public void setChestplate(ItemStack stack) {
super.setChestplate(stack);
isCurseOfBinding = ItemUtils.hasEffect(session, stack, EnchantmentComponent.PREVENT_ARMOR_CHANGE); // TODO test
public void setBody(ItemStack stack) {
super.setBody(stack);
isCurseOfBinding = ItemUtils.hasEffect(session, stack, EnchantmentComponent.PREVENT_ARMOR_CHANGE);
HolderSet set = GeyserItemStack.from(stack).getComponent(DataComponentType.REPAIRABLE);
repairableItems = GeyserHolderSet.convertHolderSet(JavaRegistries.ITEM, set);
}
@Override
@ -152,16 +158,17 @@ public class WolfEntity extends TameableEntity {
return super.testMobInteraction(hand, itemInHand);
}
}
if (itemInHand.asItem() == Items.WOLF_ARMOR && !this.chestplate.isValid() && !getFlag(EntityFlag.BABY)) {
if (itemInHand.asItem() == Items.WOLF_ARMOR && !this.body.isValid() && !getFlag(EntityFlag.BABY)) {
return InteractiveTag.EQUIP_WOLF_ARMOR;
}
if (itemInHand.asItem() == Items.SHEARS && this.chestplate.isValid()
if (itemInHand.asItem() == Items.SHEARS && this.body.isValid()
&& (!isCurseOfBinding || session.getGameMode().equals(GameMode.CREATIVE))) {
return InteractiveTag.REMOVE_WOLF_ARMOR;
}
if (Items.WOLF_ARMOR.isValidRepairItem(itemInHand.asItem()) && getFlag(EntityFlag.SITTING) &&
this.chestplate.isValid() && this.chestplate.getTag() != null &&
this.chestplate.getTag().getInt("Damage") > 0) {
if (getFlag(EntityFlag.SITTING) &&
session.getTagCache().is(repairableItems, itemInHand.asItem()) &&
this.body.isValid() && this.body.getTag() != null &&
this.body.getTag().getInt("Damage") > 0) {
return InteractiveTag.REPAIR_WOLF_ARMOR;
}
// Tamed and owned by player - can sit/stand

View file

@ -25,15 +25,15 @@
package org.geysermc.geyser.inventory;
import net.kyori.adventure.text.Component;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
/**
* Used to determine if rename packets should be sent and stores
@ -73,7 +73,7 @@ public class AnvilContainer extends Container {
String correctRename;
newName = rename;
Component originalName = ItemUtils.getCustomName(getInput().getComponents());
Component originalName = getInput().getComponent(DataComponentType.CUSTOM_NAME);
String plainOriginalName = MessageTranslator.convertToPlainText(originalName, session.locale());
String plainNewName = MessageTranslator.convertToPlainText(rename);

View file

@ -25,7 +25,11 @@
package org.geysermc.geyser.inventory;
import lombok.*;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
@ -104,10 +108,27 @@ public class GeyserItemStack {
return isEmpty() ? 0 : amount;
}
/**
* Returns all components of this item - base and additional components sent over the network.
* These are NOT modifiable! To add components, use {@link #getOrCreateComponents()}.
*
* @return the item's base data components and the "additional" ones that may exist.
*/
public @Nullable DataComponents getAllComponents() {
return isEmpty() ? null : asItem().gatherComponents(components);
}
/**
* @return the {@link DataComponents} that aren't the base/default components.
*/
public @Nullable DataComponents getComponents() {
return isEmpty() ? null : components;
}
public boolean hasNonBaseComponents() {
return components != null;
}
@NonNull
public DataComponents getOrCreateComponents() {
if (components == null) {
@ -119,33 +140,20 @@ public class GeyserItemStack {
@Nullable
public <T> T getComponent(@NonNull DataComponentType<T> type) {
if (components == null) {
return null;
return asItem().getComponent(type);
}
return components.get(type);
T value = components.get(type);
if (value == null) {
return asItem().getComponent(type);
}
return value;
}
public <T extends Boolean> boolean getComponent(@NonNull DataComponentType<T> type, boolean def) {
if (components == null) {
return def;
}
Boolean result = components.get(type);
if (result != null) {
return result;
}
return def;
}
public <T extends Integer> int getComponent(@NonNull DataComponentType<T> type, int def) {
if (components == null) {
return def;
}
Integer result = components.get(type);
if (result != null) {
return result;
}
return def;
public <T> T getComponentOrFallback(@NonNull DataComponentType<T> type, T def) {
T value = getComponent(type);
return value == null ? def : value;
}
public int getNetId() {

View file

@ -40,12 +40,15 @@ import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.item.BedrockEnchantment;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.enchantment.Enchantment;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.geyser.session.cache.tags.GeyserHolderSet;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
@ -63,7 +66,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
super.updateInventory(translator, session, inventory);
AnvilContainer anvilContainer = (AnvilContainer) inventory;
updateInventoryState(session, anvilContainer);
int targetSlot = getTargetSlot(anvilContainer);
int targetSlot = getTargetSlot(anvilContainer, session);
for (int i = 0; i < translator.size; i++) {
final int bedrockSlot = translator.javaSlotToBedrock(i);
if (bedrockSlot == 50)
@ -88,7 +91,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
updateInventoryState(session, anvilContainer);
int lastTargetSlot = anvilContainer.getLastTargetSlot();
int targetSlot = getTargetSlot(anvilContainer);
int targetSlot = getTargetSlot(anvilContainer, session);
if (targetSlot != javaSlot) {
// Update the requested slot
InventorySlotPacket slotPacket = new InventorySlotPacket();
@ -117,7 +120,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
// Changing the item in the input slot resets the name field on Bedrock, but
// does not result in a FilterTextPacket
String originalName = MessageTranslator.convertToPlainText(ItemUtils.getCustomName(input.getComponents()), session.locale());
String originalName = MessageTranslator.convertToPlainText(input.getComponent(DataComponentType.CUSTOM_NAME), session.locale());
ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(originalName);
session.sendDownstreamGamePacket(renameItemPacket);
@ -135,12 +138,12 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
* @param anvilContainer the anvil inventory
* @return the slot to change the repair cost
*/
private int getTargetSlot(AnvilContainer anvilContainer) {
private int getTargetSlot(AnvilContainer anvilContainer, GeyserSession session) {
GeyserItemStack input = anvilContainer.getInput();
GeyserItemStack material = anvilContainer.getMaterial();
if (!material.isEmpty()) {
if (!input.isEmpty() && isRepairing(input, material)) {
if (!input.isEmpty() && isRepairing(input, material, session)) {
// Changing the repair cost on the material item makes it non-stackable
return 0;
}
@ -233,7 +236,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
// Can't repair or merge enchantments
return -1;
}
} else if (hasDurability(input) && isRepairing(input, material)) {
} else if (hasDurability(input) && isRepairing(input, material, session)) {
cost = calcRepairLevelCost(input, material);
if (cost == -1) {
// No damage to repair
@ -394,8 +397,14 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
return isEnchantedBook(material) || (input.getJavaId() == material.getJavaId() && hasDurability(input));
}
private boolean isRepairing(GeyserItemStack input, GeyserItemStack material) {
return input.asItem().isValidRepairItem(material.asItem());
private boolean isRepairing(GeyserItemStack input, GeyserItemStack material, GeyserSession session) {
HolderSet repairable = input.getComponent(DataComponentType.REPAIRABLE);
if (repairable == null) {
return false;
}
GeyserHolderSet<Item> set = GeyserHolderSet.convertHolderSet(JavaRegistries.ITEM, repairable);
return session.getTagCache().is(set, material.asItem());
}
private boolean isRenaming(GeyserSession session, AnvilContainer anvilContainer, boolean bedrock) {
@ -404,27 +413,27 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
}
// This should really check the name field in all cases, but that requires the localized name
// of the item which can change depending on NBT and Minecraft Edition
Component originalName = ItemUtils.getCustomName(anvilContainer.getInput().getComponents());
Component originalName = anvilContainer.getInput().getComponent(DataComponentType.CUSTOM_NAME);
if (bedrock && originalName != null && anvilContainer.getNewName() != null) {
// Check text and formatting
String legacyOriginalName = MessageTranslator.convertMessage(originalName, session.locale());
return !legacyOriginalName.equals(anvilContainer.getNewName());
}
return !Objects.equals(originalName, ItemUtils.getCustomName(anvilContainer.getResult().getComponents()));
return !Objects.equals(originalName, anvilContainer.getResult().getComponent(DataComponentType.CUSTOM_NAME));
}
private int getRepairCost(GeyserItemStack itemStack) {
return itemStack.getComponent(DataComponentType.REPAIR_COST, 0);
return itemStack.getComponentOrFallback(DataComponentType.REPAIR_COST, 0);
}
private boolean hasDurability(GeyserItemStack itemStack) {
if (itemStack.asItem().defaultMaxDamage() > 0) {
return itemStack.getComponent(DataComponentType.UNBREAKABLE, false);
return itemStack.getComponentOrFallback(DataComponentType.UNBREAKABLE, false);
}
return false;
}
private int getDamage(GeyserItemStack itemStack) {
return itemStack.getComponent(DataComponentType.DAMAGE, 0);
return itemStack.getComponentOrFallback(DataComponentType.DAMAGE, 0);
}
}

View file

@ -25,7 +25,6 @@
package org.geysermc.geyser.item;
import org.geysermc.geyser.item.components.ToolTier;
import org.geysermc.geyser.item.type.ArmorItem;
import org.geysermc.geyser.item.type.ArrowItem;
import org.geysermc.geyser.item.type.AxolotlBucketItem;
@ -45,16 +44,13 @@ import org.geysermc.geyser.item.type.FireworkStarItem;
import org.geysermc.geyser.item.type.FishingRodItem;
import org.geysermc.geyser.item.type.GoatHornItem;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.item.type.LightItem;
import org.geysermc.geyser.item.type.MaceItem;
import org.geysermc.geyser.item.type.MapItem;
import org.geysermc.geyser.item.type.OminousBottleItem;
import org.geysermc.geyser.item.type.PlayerHeadItem;
import org.geysermc.geyser.item.type.PotionItem;
import org.geysermc.geyser.item.type.ShieldItem;
import org.geysermc.geyser.item.type.ShulkerBoxItem;
import org.geysermc.geyser.item.type.SpawnEggItem;
import org.geysermc.geyser.item.type.TieredItem;
import org.geysermc.geyser.item.type.TippedArrowItem;
import org.geysermc.geyser.item.type.TropicalFishBucketItem;
import org.geysermc.geyser.item.type.WolfArmorItem;

View file

@ -25,6 +25,7 @@
package org.geysermc.geyser.item.type;
import com.google.common.collect.ImmutableMap;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
@ -45,7 +46,6 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.geyser.translator.item.ItemTranslator;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
@ -98,16 +98,23 @@ public class Item {
return Rarity.fromId(baseComponents.getOrDefault(DataComponentType.RARITY, 0));
}
/**
* Returns a modifiable DataComponents map. Should only be used when it must be modified.
* Otherwise, prefer using GeyserItemStack's getComponent
*/
@NonNull
public DataComponents gatherComponents(DataComponents others) {
if (others == null) return baseComponents.clone();
DataComponents components = baseComponents.clone();
if (others == null) {
return new DataComponents(ImmutableMap.copyOf(components.getDataComponents()));
}
components.getDataComponents().putAll(others.getDataComponents());
return components;
return new DataComponents(ImmutableMap.copyOf(components.getDataComponents()));
}
public boolean isValidRepairItem(Item other) {
return false;
@Nullable
public <T> T getComponent(@NonNull DataComponentType<T> type) {
return baseComponents.get(type);
}
public String translationKey() {
@ -121,14 +128,11 @@ public class Item {
// Return, essentially, air
return ItemData.builder();
}
ItemData.Builder builder = ItemData.builder()
return ItemData.builder()
.definition(mapping.getBedrockDefinition())
.damage(mapping.getBedrockData())
.count(count);
ItemTranslator.translateCustomItem(components, builder, mapping);
return builder;
}
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
@ -292,7 +296,7 @@ public class Item {
}
public static Builder builder() {
return new Builder().components(new DataComponents(new HashMap<>())); // TODO actually set components here
return new Builder().components(new DataComponents(ImmutableMap.of())); // TODO actually set components here
}
public static final class Builder {

View file

@ -25,20 +25,15 @@
package org.geysermc.geyser.level;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i;
import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPacket;
import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket;
import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket;
import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.geyser.erosion.ErosionCancellationException;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import java.util.concurrent.CompletableFuture;
@ -124,19 +119,4 @@ public class GeyserWorldManager extends WorldManager {
public GameMode getDefaultGameMode(GeyserSession session) {
return GameMode.SURVIVAL;
}
@NonNull
@Override
public CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) {
return super.getPickItemComponents(session, x, y, z, addNbtData);
} else if (session.isClosed()) {
return CompletableFuture.failedFuture(new ErosionCancellationException());
}
CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
erosionHandler.setPickBlockLookup(future);
erosionHandler.sendPacket(new BackendboundPickBlockPacket(Vector3i.from(x, y, z)));
return future.thenApply(RAW_TRANSFORMER);
}
}

View file

@ -192,16 +192,6 @@ public abstract class WorldManager {
return null;
}
/**
* Used for pick block, so we don't need to cache more data than necessary.
*
* @return expected NBT for this item.
*/
@NonNull
public CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addExtraData) {
return CompletableFuture.completedFuture(null);
}
/**
* Retrieves decorated pot sherds from the server. Used to ensure the data is not erased on animation sent
* through the BlockEntityDataPacket.

View file

@ -28,6 +28,7 @@ package org.geysermc.geyser.session.cache;
import it.unimi.dsi.fastutil.ints.IntArrays;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
@ -119,7 +120,10 @@ public final class TagCache {
/**
* @return true if the specified network ID is in the given holder set.
*/
public <T> boolean is(GeyserHolderSet<T> holderSet, T object) {
public <T> boolean is(@Nullable GeyserHolderSet<T> holderSet, @Nullable T object) {
if (holderSet == null || object == null) {
return false;
}
return contains(holderSet.resolveRaw(this), holderSet.getRegistry().toNetworkId(session, object));
}

View file

@ -25,10 +25,6 @@
package org.geysermc.geyser.session.cache.tags;
import java.util.List;
import java.util.Objects;
import java.util.function.ToIntFunction;
import it.unimi.dsi.fastutil.ints.IntArrays;
import lombok.Data;
import net.kyori.adventure.key.Key;
@ -37,6 +33,11 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.TagCache;
import org.geysermc.geyser.session.cache.registry.JavaRegistryKey;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
import java.util.List;
import java.util.Objects;
import java.util.function.ToIntFunction;
/**
* Similar to vanilla Minecraft's HolderSets, stores either a tag or a list of IDs (this list can also be represented as a single ID in vanilla HolderSets).
@ -87,6 +88,27 @@ public final class GeyserHolderSet<T> {
return tagCache.getRaw(Objects.requireNonNull(tag, "HolderSet must have a tag if it doesn't have a list of IDs"));
}
/**
* Reads a MCPL {@link HolderSet} and turns it into a GeyserHolderSet.
* @param registry the registry the HolderSet contains IDs from.
* @param holderSet the HolderSet as the MCPL HolderSet object
*/
public static <T> GeyserHolderSet<T> convertHolderSet(@NonNull JavaRegistryKey<T> registry, @Nullable HolderSet holderSet) {
if (holderSet == null) {
return new GeyserHolderSet<>(registry, IntArrays.EMPTY_ARRAY);
}
if (holderSet.getHolders() != null) {
return new GeyserHolderSet<>(registry, holderSet.getHolders());
}
if (holderSet.getLocation() != null) {
return new GeyserHolderSet<>(registry, new Tag<>(registry, holderSet.getLocation()));
}
throw new IllegalStateException("HolderSet must have a tag or a list of IDs! " + holderSet);
}
/**
* Reads a HolderSet from an object from NBT.
*

View file

@ -25,10 +25,6 @@
package org.geysermc.geyser.skin;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.auth.GameProfile.Texture;
import org.geysermc.mcprotocollib.auth.GameProfile.TextureModel;
import org.geysermc.mcprotocollib.auth.GameProfile.TextureType;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
@ -36,6 +32,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.skin.Cape;
import org.geysermc.geyser.api.skin.Skin;
@ -46,8 +43,10 @@ import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.skin.SkinManager.GameProfileData;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.auth.GameProfile.Texture;
import org.geysermc.mcprotocollib.auth.GameProfile.TextureModel;
import org.geysermc.mcprotocollib.auth.GameProfile.TextureType;
import java.awt.*;
import java.awt.image.BufferedImage;
@ -105,9 +104,7 @@ public class FakeHeadProvider {
}
});
public static void setHead(GeyserSession session, PlayerEntity entity, DataComponents components) {
GameProfile profile = components.get(DataComponentType.PROFILE);
public static void setHead(GeyserSession session, PlayerEntity entity, @Nullable GameProfile profile) {
if (profile == null) {
return;
}

View file

@ -76,7 +76,6 @@ import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
@ -252,8 +251,8 @@ public abstract class InventoryTranslator {
//only set the head if the destination is the head slot
GeyserItemStack javaItem = inventory.getItem(sourceSlot);
if (javaItem.asItem() == Items.PLAYER_HEAD
&& javaItem.getComponents() != null) {
FakeHeadProvider.setHead(session, session.getPlayerEntity(), javaItem.getComponents());
&& javaItem.hasNonBaseComponents()) {
FakeHeadProvider.setHead(session, session.getPlayerEntity(), javaItem.getComponent(DataComponentType.PROFILE));
}
} else if (sourceSlot == 5) {
//we are probably removing the head, so restore the original skin
@ -1020,12 +1019,9 @@ public abstract class InventoryTranslator {
// As of 1.16.210: Bedrock needs confirmation on what the current item durability is.
// If 0 is sent, then Bedrock thinks the item is not damaged
int durability = 0;
DataComponents components = itemStack.getComponents();
if (components != null) {
Integer damage = components.get(DataComponentType.DAMAGE);
if (damage != null) {
durability = ItemUtils.getCorrectBedrockDurability(itemStack.asItem(), damage);
}
Integer damage = itemStack.getComponent(DataComponentType.DAMAGE);
if (damage != null) {
durability = ItemUtils.getCorrectBedrockDurability(itemStack.asItem(), damage);
}
itemEntry = new ItemStackResponseSlot((byte) bedrockSlot, (byte) bedrockSlot, (byte) itemStack.getAmount(), itemStack.getNetId(), "", durability);

View file

@ -30,7 +30,6 @@ import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.Container;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.Inventory;
@ -158,13 +157,13 @@ public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator
session.getLastInteractionBlockPosition() : inventory.getHolderPosition();
NbtMap blockEntityTag;
if (book.getComponents() != null) {
if (book.hasNonBaseComponents()) {
int pages = 0;
WrittenBookContent writtenBookComponents = book.getComponents().get(DataComponentType.WRITTEN_BOOK_CONTENT);
WrittenBookContent writtenBookComponents = book.getComponent(DataComponentType.WRITTEN_BOOK_CONTENT);
if (writtenBookComponents != null) {
pages = writtenBookComponents.getPages().size();
} else {
WritableBookContent writableBookComponents = book.getComponents().get(DataComponentType.WRITABLE_BOOK_CONTENT);
WritableBookContent writableBookComponents = book.getComponent(DataComponentType.WRITABLE_BOOK_CONTENT);
if (writableBookComponents != null) {
pages = writableBookComponents.getPages().size();
}

View file

@ -49,11 +49,9 @@ import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerButtonClickPacket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator {
@ -156,16 +154,11 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator {
GeyserItemStack inputCopy = inventory.getItem(0).copy(1);
inputCopy.setNetId(session.getNextItemNetId());
// Add the pattern manually, for better item synchronization
if (inputCopy.getComponents() == null) {
inputCopy.setComponents(new DataComponents(new HashMap<>()));
}
BannerPatternLayer bannerPatternLayer = BannerItem.getJavaBannerPattern(session, pattern); // TODO
if (bannerPatternLayer != null) {
List<BannerPatternLayer> patternsList = inputCopy.getComponents().getOrDefault(DataComponentType.BANNER_PATTERNS, new ArrayList<>());
List<BannerPatternLayer> patternsList = inputCopy.getComponentOrFallback(DataComponentType.BANNER_PATTERNS, new ArrayList<>());
patternsList.add(bannerPatternLayer);
inputCopy.getComponents().put(DataComponentType.BANNER_PATTERNS, patternsList);
inputCopy.getOrCreateComponents().put(DataComponentType.BANNER_PATTERNS, patternsList);
}
// Set the new item as the output

View file

@ -58,6 +58,7 @@ import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundSetCreativeModeSlotPacket;
import java.util.Arrays;
@ -103,8 +104,8 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
contents[i - 5] = item.getItemData(session);
if (i == 5 &&
item.asItem() == Items.PLAYER_HEAD &&
item.getComponents() != null) {
FakeHeadProvider.setHead(session, session.getPlayerEntity(), item.getComponents());
item.hasNonBaseComponents()) {
FakeHeadProvider.setHead(session, session.getPlayerEntity(), item.getComponent(DataComponentType.PROFILE));
}
}
armorContentPacket.setContents(Arrays.asList(contents));
@ -147,8 +148,8 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
if (slot == 5) {
// Check for custom skull
if (javaItem.asItem() == Items.PLAYER_HEAD
&& javaItem.getComponents() != null) {
FakeHeadProvider.setHead(session, session.getPlayerEntity(), javaItem.getComponents());
&& javaItem.hasNonBaseComponents()) {
FakeHeadProvider.setHead(session, session.getPlayerEntity(), javaItem.getComponent(DataComponentType.PROFILE));
} else {
FakeHeadProvider.restoreOriginalSkin(session, session.getPlayerEntity());
}

View file

@ -112,7 +112,7 @@ public final class ItemTranslator {
NbtMap nbt = data.getTag();
if (nbt != null && !nbt.isEmpty()) {
// translateToJava may have added components
DataComponents components = itemStack.getComponents() == null ? new DataComponents(new HashMap<>()) : itemStack.getComponents();
DataComponents components = itemStack.getOrCreateComponents();
javaItem.translateNbtToJava(session, nbt, components, bedrockItem);
if (!components.getDataComponents().isEmpty()) {
itemStack.setComponents(components);
@ -193,7 +193,7 @@ public final class ItemTranslator {
}
if (bedrockItem.getJavaItem().equals(Items.PLAYER_HEAD)) {
translatePlayerHead(session, components, builder);
translatePlayerHead(session, components.get(DataComponentType.PROFILE), builder);
}
translateCustomItem(components, builder, bedrockItem);
@ -391,7 +391,7 @@ public final class ItemTranslator {
return ItemDefinition.AIR;
}
ItemMapping mapping = itemStack.asItem().toBedrockDefinition(itemStack.getComponents(), session.getItemMappings());
ItemMapping mapping = itemStack.asItem().toBedrockDefinition(itemStack.getAllComponents(), session.getItemMappings());
ItemDefinition itemDefinition = mapping.getBedrockDefinition();
CustomBlockData customBlockData = BlockRegistries.CUSTOM_BLOCK_ITEM_OVERRIDES.getOrDefault(
@ -401,7 +401,7 @@ public final class ItemTranslator {
}
if (mapping.getJavaItem().equals(Items.PLAYER_HEAD)) {
CustomSkull customSkull = getCustomSkull(itemStack.getComponents());
CustomSkull customSkull = getCustomSkull(itemStack.getComponent(DataComponentType.PROFILE));
if (customSkull != null) {
itemDefinition = session.getItemMappings().getCustomBlockItemDefinitions().get(customSkull.getCustomBlockData());
}
@ -466,39 +466,35 @@ public final class ItemTranslator {
builder.blockDefinition(blockDefinition);
}
private static @Nullable CustomSkull getCustomSkull(DataComponents components) {
if (components == null) {
private static @Nullable CustomSkull getCustomSkull(@Nullable GameProfile profile) {
if (profile == null) {
return null;
}
GameProfile profile = components.get(DataComponentType.PROFILE);
if (profile != null) {
Map<TextureType, Texture> textures;
try {
textures = profile.getTextures(false);
} catch (IllegalStateException e) {
GeyserImpl.getInstance().getLogger().debug("Could not decode player head from profile %s, got: %s".formatted(profile, e.getMessage()));
return null;
}
if (textures == null || textures.isEmpty()) {
return null;
}
Texture skinTexture = textures.get(TextureType.SKIN);
if (skinTexture == null) {
return null;
}
String skinHash = skinTexture.getURL().substring(skinTexture.getURL().lastIndexOf('/') + 1);
return BlockRegistries.CUSTOM_SKULLS.get(skinHash);
Map<TextureType, Texture> textures;
try {
textures = profile.getTextures(false);
} catch (IllegalStateException e) {
GeyserImpl.getInstance().getLogger().debug("Could not decode player head from profile %s, got: %s".formatted(profile, e.getMessage()));
return null;
}
return null;
if (textures == null || textures.isEmpty()) {
return null;
}
Texture skinTexture = textures.get(TextureType.SKIN);
if (skinTexture == null) {
return null;
}
String skinHash = skinTexture.getURL().substring(skinTexture.getURL().lastIndexOf('/') + 1);
return BlockRegistries.CUSTOM_SKULLS.get(skinHash);
}
private static void translatePlayerHead(GeyserSession session, DataComponents components, ItemData.Builder builder) {
CustomSkull customSkull = getCustomSkull(components);
private static void translatePlayerHead(GeyserSession session, GameProfile profile, ItemData.Builder builder) {
CustomSkull customSkull = getCustomSkull(profile);
if (customSkull != null) {
CustomBlockData customBlockData = customSkull.getCustomBlockData();
ItemDefinition itemDefinition = session.getItemMappings().getCustomBlockItemDefinitions().get(customBlockData);

View file

@ -53,7 +53,7 @@ public class BedrockBookEditTranslator extends PacketTranslator<BookEditPacket>
GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand();
if (itemStack != null) {
DataComponents components = itemStack.getComponents() != null ? itemStack.getComponents() : new DataComponents(new HashMap<>());
DataComponents components = itemStack.getOrCreateComponents();
ItemStack bookItem = new ItemStack(itemStack.getJavaId(), itemStack.getAmount(), components);
List<String> pages = new LinkedList<>();

View file

@ -131,6 +131,7 @@ public final class BlockUtils {
return 1.0 / speed;
}
// TODO 1.21.4 this changed probably; no more tiers
public static double getBreakTime(GeyserSession session, Block block, ItemMapping item, @Nullable DataComponents components, boolean isSessionPlayer) {
boolean isShearsEffective = session.getTagCache().is(BlockTag.LEAVES, block) || session.getTagCache().is(BlockTag.WOOL, block); //TODO called twice
boolean canHarvestWithHand = !block.requiresCorrectToolForDrops();
@ -160,7 +161,7 @@ public final class BlockUtils {
boolean waterInEyes = session.getCollisionManager().isWaterInEyes();
boolean insideOfWaterWithoutAquaAffinity = waterInEyes &&
ItemUtils.getEnchantmentLevel(session, session.getPlayerInventory().getItem(5).getComponents(), BedrockEnchantment.AQUA_AFFINITY) < 1;
ItemUtils.getEnchantmentLevel(session, session.getPlayerInventory().getItem(5).getAllComponents(), BedrockEnchantment.AQUA_AFFINITY) < 1;
return calculateBreakTime(block.destroyTime(), toolTier, canHarvestWithHand, correctTool, toolCanBreak, toolType, isShearsEffective,
toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, session.getPlayerEntity().isOnGround());
@ -173,7 +174,7 @@ public final class BlockUtils {
DataComponents components = null;
if (item != null) {
mapping = item.getMapping(session);
components = item.getComponents();
components = item.getAllComponents();
}
return getBreakTime(session, block, mapping, components, true);
}

View file

@ -25,7 +25,6 @@
package org.geysermc.geyser.util;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.inventory.item.BedrockEnchantment;
import org.geysermc.geyser.item.Items;
@ -102,17 +101,6 @@ public final class ItemUtils {
return original;
}
/**
* @param components the data components of the item
* @return the custom name of the item
*/
public static @Nullable Component getCustomName(DataComponents components) {
if (components == null) {
return null;
}
return components.get(DataComponentType.CUSTOM_NAME);
}
private ItemUtils() {
}
}