Better nullability annotations/handling for ItemStacks

This commit is contained in:
Konicai 2023-11-30 01:58:35 -05:00
parent f0e983977a
commit c7da8fe163
13 changed files with 29 additions and 19 deletions

View file

@ -34,6 +34,7 @@ import lombok.Getter;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMapping;
@ -42,7 +43,7 @@ import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
@Data @Data
public class GeyserItemStack { public class GeyserItemStack {
public static final GeyserItemStack EMPTY = new GeyserItemStack(0, 0, null); public static final GeyserItemStack EMPTY = new GeyserItemStack(Items.AIR_ID, 0, null);
private final int javaId; private final int javaId;
private int amount; private int amount;
@ -64,7 +65,7 @@ public class GeyserItemStack {
this.netId = netId; this.netId = netId;
} }
public static @NonNull GeyserItemStack from(ItemStack itemStack) { public static @NonNull GeyserItemStack from(@Nullable ItemStack itemStack) {
return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getNbt()); return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getNbt());
} }
@ -122,7 +123,7 @@ public class GeyserItemStack {
} }
public boolean isEmpty() { public boolean isEmpty() {
return amount <= 0 || javaId == 0; return amount <= 0 || javaId == Items.AIR_ID;
} }
public GeyserItemStack copy() { public GeyserItemStack copy() {

View file

@ -28,8 +28,9 @@ package org.geysermc.geyser.inventory.recipe;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient;
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData;
import org.checkerframework.checker.nullness.qual.Nullable;
public record GeyserShapedRecipe(int width, int height, Ingredient[] ingredients, ItemStack result) implements GeyserRecipe { public record GeyserShapedRecipe(int width, int height, Ingredient[] ingredients, @Nullable ItemStack result) implements GeyserRecipe {
public GeyserShapedRecipe(ShapedRecipeData data) { public GeyserShapedRecipe(ShapedRecipeData data) {
this(data.getWidth(), data.getHeight(), data.getIngredients(), data.getResult()); this(data.getWidth(), data.getHeight(), data.getIngredients(), data.getResult());

View file

@ -28,8 +28,9 @@ package org.geysermc.geyser.inventory.recipe;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient;
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData;
import org.checkerframework.checker.nullness.qual.Nullable;
public record GeyserShapelessRecipe(Ingredient[] ingredients, ItemStack result) implements GeyserRecipe { public record GeyserShapelessRecipe(Ingredient[] ingredients, @Nullable ItemStack result) implements GeyserRecipe {
public GeyserShapelessRecipe(ShapelessRecipeData data) { public GeyserShapelessRecipe(ShapelessRecipeData data) {
this(data.getIngredients(), data.getResult()); this(data.getIngredients(), data.getResult());

View file

@ -26,10 +26,11 @@
package org.geysermc.geyser.inventory.recipe; package org.geysermc.geyser.inventory.recipe;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @param buttonId the button that needs to be pressed for Java Edition to accept this item. * @param buttonId the button that needs to be pressed for Java Edition to accept this item.
* @param output the expected output of this item when cut. * @param output the expected output of this item when cut.
*/ */
public record GeyserStonecutterData(int buttonId, ItemStack output) { public record GeyserStonecutterData(int buttonId, @Nullable ItemStack output) {
} }

View file

@ -1351,6 +1351,8 @@ public final class Items {
public static final Item TRIAL_SPAWNER = register(new BlockItem("trial_spawner", builder())); public static final Item TRIAL_SPAWNER = register(new BlockItem("trial_spawner", builder()));
public static final Item TRIAL_KEY = register(new Item("trial_key", builder())); public static final Item TRIAL_KEY = register(new Item("trial_key", builder()));
public static final int AIR_ID = AIR.javaId();
private static <T extends Item> T register(T item) { private static <T extends Item> T register(T item) {
return register(item, Registries.JAVA_ITEMS.get().size()); return register(item, Registries.JAVA_ITEMS.get().size());
} }

View file

@ -40,6 +40,7 @@ import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.InventoryUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -82,7 +83,7 @@ public class Item {
/* Translation methods to Bedrock and back */ /* Translation methods to Bedrock and back */
public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) {
if (itemStack == null) { if (InventoryUtils.isEmpty(itemStack)) {
// Return, essentially, air // Return, essentially, air
return ItemData.builder(); return ItemData.builder();
} }

View file

@ -83,7 +83,7 @@ public class ItemMappings implements DefinitionRegistry<ItemDefinition> {
* @return an item entry from the given java edition identifier * @return an item entry from the given java edition identifier
*/ */
@NonNull @NonNull
public ItemMapping getMapping(ItemStack itemStack) { public ItemMapping getMapping(@NonNull ItemStack itemStack) {
return this.getMapping(itemStack.getId()); return this.getMapping(itemStack.getId());
} }

View file

@ -67,6 +67,7 @@ import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.InventoryUtils;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.*; import java.util.*;
@ -89,7 +90,7 @@ public final class ItemTranslator {
*/ */
public static ItemStack translateToJava(ItemData data, ItemMappings mappings) { public static ItemStack translateToJava(ItemData data, ItemMappings mappings) {
if (data == null) { if (data == null) {
return new ItemStack(0); return new ItemStack(Items.AIR_ID);
} }
ItemMapping bedrockItem = mappings.getMapping(data); ItemMapping bedrockItem = mappings.getMapping(data);
@ -118,7 +119,7 @@ public final class ItemTranslator {
@NonNull @NonNull
public static ItemData translateToBedrock(GeyserSession session, ItemStack stack) { public static ItemData translateToBedrock(GeyserSession session, ItemStack stack) {
if (stack == null) { if (InventoryUtils.isEmpty(stack)) {
return ItemData.AIR; return ItemData.AIR;
} }

View file

@ -56,7 +56,7 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPic
ItemFrameEntity entity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition()); ItemFrameEntity entity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
if (entity != null) { if (entity != null) {
// Check to see if the item frame has an item in it first // Check to see if the item frame has an item in it first
if (entity.getHeldItem() != null && entity.getHeldItem().getId() != 0) { if (!InventoryUtils.isEmpty(entity.getHeldItem())) {
// Grab the item in the frame // Grab the item in the frame
InventoryUtils.findOrCreateItem(session, entity.getHeldItem()); InventoryUtils.findOrCreateItem(session, entity.getHeldItem());
} else { } else {

View file

@ -118,7 +118,7 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
return; return;
} }
if (item == null || item.getId() == 0) { if (InventoryUtils.isEmpty(item)) {
return; return;
} }

View file

@ -44,6 +44,7 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.geyser.util.MathUtils; import org.geysermc.geyser.util.MathUtils;
import java.util.ArrayList; import java.util.ArrayList;
@ -158,14 +159,14 @@ public class JavaMerchantOffersTranslator extends PacketTranslator<ClientboundMe
} }
private static NbtMap getItemTag(GeyserSession session, ItemStack stack) { private static NbtMap getItemTag(GeyserSession session, ItemStack stack) {
if (stack == null || stack.getAmount() <= 0) { // Negative item counts appear as air on Java if (InventoryUtils.isEmpty(stack)) { // Negative item counts appear as air on Java
return NbtMap.EMPTY; return NbtMap.EMPTY;
} }
return getItemTag(session, stack, session.getItemMappings().getMapping(stack), stack.getAmount()); return getItemTag(session, stack, session.getItemMappings().getMapping(stack), stack.getAmount());
} }
private static NbtMap getItemTag(GeyserSession session, ItemStack stack, int specialPrice, int demand, float priceMultiplier) { private static NbtMap getItemTag(GeyserSession session, ItemStack stack, int specialPrice, int demand, float priceMultiplier) {
if (stack == null || stack.getAmount() <= 0) { // Negative item counts appear as air on Java if (InventoryUtils.isEmpty(stack)) { // Negative item counts appear as air on Java
return NbtMap.EMPTY; return NbtMap.EMPTY;
} }
ItemMapping mapping = session.getItemMappings().getMapping(stack); ItemMapping mapping = session.getItemMappings().getMapping(stack);

View file

@ -61,6 +61,7 @@ import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator; import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
import org.geysermc.geyser.translator.inventory.chest.DoubleChestInventoryTranslator; import org.geysermc.geyser.translator.inventory.chest.DoubleChestInventoryTranslator;
import org.jetbrains.annotations.Contract;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -181,8 +182,9 @@ public class InventoryUtils {
/** /**
* Checks to see if an item stack represents air or has no count. * Checks to see if an item stack represents air or has no count.
*/ */
@Contract("null -> true")
public static boolean isEmpty(@Nullable ItemStack itemStack) { public static boolean isEmpty(@Nullable ItemStack itemStack) {
return itemStack == null || itemStack.getId() == Items.AIR.javaId() || itemStack.getAmount() <= 0; return itemStack == null || itemStack.getId() == Items.AIR_ID || itemStack.getAmount() <= 0;
} }
/** /**
@ -233,11 +235,10 @@ public class InventoryUtils {
* @param itemStack the item to try to find a match for. NBT will also be accounted for. * @param itemStack the item to try to find a match for. NBT will also be accounted for.
*/ */
public static void findOrCreateItem(GeyserSession session, ItemStack itemStack) { public static void findOrCreateItem(GeyserSession session, ItemStack itemStack) {
PlayerInventory inventory = session.getPlayerInventory(); if (isEmpty(itemStack)) {
if (itemStack == null || itemStack.getId() == 0) {
return; return;
} }
PlayerInventory inventory = session.getPlayerInventory();
// Check hotbar for item // Check hotbar for item
for (int i = 36; i < 45; i++) { for (int i = 36; i < 45; i++) {

View file

@ -14,7 +14,7 @@ protocol-connection = "3.0.0.Beta1-20231107.190703-112"
raknet = "1.0.0.CR1-20230703.195238-9" raknet = "1.0.0.CR1-20230703.195238-9"
blockstateupdater="1.20.50-20231106.161340-1" blockstateupdater="1.20.50-20231106.161340-1"
mcauthlib = "d9d773e" mcauthlib = "d9d773e"
mcprotocollib = "00bce42" mcprotocollib = "c6ba2fa"
adventure = "4.14.0" adventure = "4.14.0"
adventure-platform = "4.3.0" adventure-platform = "4.3.0"
junit = "5.9.2" junit = "5.9.2"