All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.nukkit.item.Item Maven / Gradle / Ivy

There is a newer version: 1.20.40-r1
Show newest version
package cn.nukkit.item;

import cn.nukkit.Player;
import cn.nukkit.Server;
import cn.nukkit.api.*;
import cn.nukkit.block.Block;
import cn.nukkit.block.BlockID;
import cn.nukkit.block.BlockUnknown;
import cn.nukkit.blockproperty.UnknownRuntimeIdException;
import cn.nukkit.blockproperty.exception.BlockPropertyNotFoundException;
import cn.nukkit.blockproperty.exception.InvalidBlockPropertyMetaException;
import cn.nukkit.blockstate.BlockState;
import cn.nukkit.blockstate.BlockStateRegistry;
import cn.nukkit.blockstate.exception.InvalidBlockStateException;
import cn.nukkit.entity.Entity;
import cn.nukkit.inventory.Fuel;
import cn.nukkit.inventory.ItemTag;
import cn.nukkit.item.customitem.CustomItem;
import cn.nukkit.item.customitem.CustomItemDefinition;
import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.item.randomitem.ItemEchoShard;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.Vector3;
import cn.nukkit.nbt.NBTIO;
import cn.nukkit.nbt.tag.*;
import cn.nukkit.utils.*;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import io.netty.util.internal.EmptyArrays;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.nio.ByteOrder;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author MagicDroidX (Nukkit Project)
 */
@Log4j2
public class Item implements Cloneable, BlockID, ItemID {
    @PowerNukkitXOnly
    @Since("1.19.70-r1")
    public static final Item AIR_ITEM = new Item(0);

    @PowerNukkitOnly
    @Since("1.4.0.0-PN")
    public static final Item[] EMPTY_ARRAY = new Item[0];

    /**
     * Groups:
     * 
    *
  1. namespace (optional)
  2. *
  3. item name (choice)
  4. *
  5. damage (optional, for item name)
  6. *
  7. numeric id (choice)
  8. *
  9. damage (optional, for numeric id)
  10. *
*/ private static final Pattern ITEM_STRING_PATTERN = Pattern.compile( // 1:namespace 2:name 3:damage 4:num-id 5:damage "^(?:(?:([a-z_]\\w*):)?([a-z._]\\w*)(?::(-?\\d+))?|(-?\\d+)(?::(-?\\d+))?)$"); public static String UNKNOWN_STR = "Unknown"; public static Class[] list = null; private static Map itemIds = Arrays.stream(ItemID.class.getDeclaredFields()) .filter(field -> field.getModifiers() == (Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL)) .filter(field -> field.getType().equals(int.class)) .collect(Collectors.toMap( field -> field.getName().toLowerCase(), field -> { try { return field.getInt(null); } catch (IllegalAccessException e) { throw new InternalError(e); } }, (e1, e2) -> e1, LinkedHashMap::new )); private static Map blockIds = Arrays.stream(BlockID.class.getDeclaredFields()) .filter(field -> field.getModifiers() == (Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL)) .filter(field -> field.getType().equals(int.class)) .collect(Collectors.toMap( field -> field.getName().toLowerCase(), field -> { try { int blockId = field.getInt(null); if (blockId > 255) { return 255 - blockId; } return blockId; } catch (IllegalAccessException e) { throw new InternalError(e); } }, (e1, e2) -> e1, LinkedHashMap::new )); @PowerNukkitXOnly @Since("1.6.0.0-PNX") private static final HashMap> CUSTOM_ITEMS = new HashMap<>(); @PowerNukkitXOnly @Since("1.19.31-r1") private static final HashMap CUSTOM_ITEM_DEFINITIONS = new HashMap<>(); protected Block block = null; protected final int id; protected int meta; protected boolean hasMeta = true; private byte[] tags = EmptyArrays.EMPTY_BYTES; private transient CompoundTag cachedNBT = null; public int count; @Deprecated @DeprecationDetails(since = "1.4.0.0-PN", by = "PowerNukkit", reason = "Unused", replaceWith = "meta or getDamage()") protected int durability = 0; protected String name; public Item(int id) { this(id, 0, 1, UNKNOWN_STR); } public Item(int id, Integer meta) { this(id, meta, 1, UNKNOWN_STR); } public Item(int id, Integer meta, int count) { this(id, meta, count, UNKNOWN_STR); } public Item(int id, Integer meta, int count, String name) { //this.id = id & 0xffff; this.id = id; if (meta != null && meta >= 0) { this.meta = meta & 0xffff; } else { this.hasMeta = false; } this.count = count; this.name = name != null ? name.intern() : null; /*f (this.block != null && this.id <= 0xff && Block.list[id] != null) { //probably useless this.block = Block.get(this.id, this.meta); this.name = this.block.getName(); }*/ } public boolean hasMeta() { return hasMeta; } public boolean canBeActivated() { return false; } public static void init() { if (list == null) { list = new Class[65535]; list[IRON_SHOVEL] = ItemShovelIron.class; //256 list[IRON_PICKAXE] = ItemPickaxeIron.class; //257 list[IRON_AXE] = ItemAxeIron.class; //258 list[FLINT_AND_STEEL] = ItemFlintSteel.class; //259 list[APPLE] = ItemApple.class; //260 list[BOW] = ItemBow.class; //261 list[ARROW] = ItemArrow.class; //262 list[COAL] = ItemCoal.class; //263 list[DIAMOND] = ItemDiamond.class; //264 list[IRON_INGOT] = ItemIngotIron.class; //265 list[GOLD_INGOT] = ItemIngotGold.class; //266 list[IRON_SWORD] = ItemSwordIron.class; //267 list[WOODEN_SWORD] = ItemSwordWood.class; //268 list[WOODEN_SHOVEL] = ItemShovelWood.class; //269 list[WOODEN_PICKAXE] = ItemPickaxeWood.class; //270 list[WOODEN_AXE] = ItemAxeWood.class; //271 list[STONE_SWORD] = ItemSwordStone.class; //272 list[STONE_SHOVEL] = ItemShovelStone.class; //273 list[STONE_PICKAXE] = ItemPickaxeStone.class; //274 list[STONE_AXE] = ItemAxeStone.class; //275 list[DIAMOND_SWORD] = ItemSwordDiamond.class; //276 list[DIAMOND_SHOVEL] = ItemShovelDiamond.class; //277 list[DIAMOND_PICKAXE] = ItemPickaxeDiamond.class; //278 list[DIAMOND_AXE] = ItemAxeDiamond.class; //279 list[STICK] = ItemStick.class; //280 list[BOWL] = ItemBowl.class; //281 list[MUSHROOM_STEW] = ItemMushroomStew.class; //282 list[GOLD_SWORD] = ItemSwordGold.class; //283 list[GOLD_SHOVEL] = ItemShovelGold.class; //284 list[GOLD_PICKAXE] = ItemPickaxeGold.class; //285 list[GOLD_AXE] = ItemAxeGold.class; //286 list[STRING] = ItemString.class; //287 list[FEATHER] = ItemFeather.class; //288 list[GUNPOWDER] = ItemGunpowder.class; //289 list[WOODEN_HOE] = ItemHoeWood.class; //290 list[STONE_HOE] = ItemHoeStone.class; //291 list[IRON_HOE] = ItemHoeIron.class; //292 list[DIAMOND_HOE] = ItemHoeDiamond.class; //293 list[GOLD_HOE] = ItemHoeGold.class; //294 list[WHEAT_SEEDS] = ItemSeedsWheat.class; //295 list[WHEAT] = ItemWheat.class; //296 list[BREAD] = ItemBread.class; //297 list[LEATHER_CAP] = ItemHelmetLeather.class; //298 list[LEATHER_TUNIC] = ItemChestplateLeather.class; //299 list[LEATHER_PANTS] = ItemLeggingsLeather.class; //300 list[LEATHER_BOOTS] = ItemBootsLeather.class; //301 list[CHAIN_HELMET] = ItemHelmetChain.class; //302 list[CHAIN_CHESTPLATE] = ItemChestplateChain.class; //303 list[CHAIN_LEGGINGS] = ItemLeggingsChain.class; //304 list[CHAIN_BOOTS] = ItemBootsChain.class; //305 list[IRON_HELMET] = ItemHelmetIron.class; //306 list[IRON_CHESTPLATE] = ItemChestplateIron.class; //307 list[IRON_LEGGINGS] = ItemLeggingsIron.class; //308 list[IRON_BOOTS] = ItemBootsIron.class; //309 list[DIAMOND_HELMET] = ItemHelmetDiamond.class; //310 list[DIAMOND_CHESTPLATE] = ItemChestplateDiamond.class; //311 list[DIAMOND_LEGGINGS] = ItemLeggingsDiamond.class; //312 list[DIAMOND_BOOTS] = ItemBootsDiamond.class; //313 list[GOLD_HELMET] = ItemHelmetGold.class; //314 list[GOLD_CHESTPLATE] = ItemChestplateGold.class; //315 list[GOLD_LEGGINGS] = ItemLeggingsGold.class; //316 list[GOLD_BOOTS] = ItemBootsGold.class; //317 list[FLINT] = ItemFlint.class; //318 list[RAW_PORKCHOP] = ItemPorkchopRaw.class; //319 list[COOKED_PORKCHOP] = ItemPorkchopCooked.class; //320 list[PAINTING] = ItemPainting.class; //321 list[GOLDEN_APPLE] = ItemAppleGold.class; //322 list[SIGN] = ItemSign.class; //323 list[WOODEN_DOOR] = ItemDoorWood.class; //324 list[BUCKET] = ItemBucket.class; //325 list[MINECART] = ItemMinecart.class; //328 list[SADDLE] = ItemSaddle.class; //329 list[IRON_DOOR] = ItemDoorIron.class; //330 list[REDSTONE] = ItemRedstone.class; //331 list[SNOWBALL] = ItemSnowball.class; //332 list[BOAT] = ItemBoat.class; //333 list[LEATHER] = ItemLeather.class; //334 list[KELP] = ItemKelp.class; //335 list[BRICK] = ItemBrick.class; //336 list[CLAY] = ItemClay.class; //337 list[SUGARCANE] = ItemSugarcane.class; //338 list[PAPER] = ItemPaper.class; //339 list[BOOK] = ItemBook.class; //340 list[SLIMEBALL] = ItemSlimeball.class; //341 list[CHEST_MINECART] = ItemMinecartChest.class; //342 list[EGG] = ItemEgg.class; //344 list[COMPASS] = ItemCompass.class; //345 list[FISHING_ROD] = ItemFishingRod.class; //346 list[CLOCK] = ItemClock.class; //347 list[GLOWSTONE_DUST] = ItemGlowstoneDust.class; //348 list[RAW_FISH] = ItemFish.class; //349 list[COOKED_FISH] = ItemFishCooked.class; //350 list[DYE] = ItemDye.class; //351 list[BONE] = ItemBone.class; //352 list[SUGAR] = ItemSugar.class; //353 list[CAKE] = ItemCake.class; //354 list[BED] = ItemBed.class; //355 list[REPEATER] = ItemRedstoneRepeater.class; //356 list[COOKIE] = ItemCookie.class; //357 list[MAP] = ItemMap.class; //358 list[SHEARS] = ItemShears.class; //359 list[MELON] = ItemMelon.class; //360 list[PUMPKIN_SEEDS] = ItemSeedsPumpkin.class; //361 list[MELON_SEEDS] = ItemSeedsMelon.class; //362 list[RAW_BEEF] = ItemBeefRaw.class; //363 list[STEAK] = ItemSteak.class; //364 list[RAW_CHICKEN] = ItemChickenRaw.class; //365 list[COOKED_CHICKEN] = ItemChickenCooked.class; //366 list[ROTTEN_FLESH] = ItemRottenFlesh.class; //367 list[ENDER_PEARL] = ItemEnderPearl.class; //368 list[BLAZE_ROD] = ItemBlazeRod.class; //369 list[GHAST_TEAR] = ItemGhastTear.class; //370 list[GOLD_NUGGET] = ItemNuggetGold.class; //371 list[NETHER_WART] = ItemNetherWart.class; //372 list[POTION] = ItemPotion.class; //373 list[GLASS_BOTTLE] = ItemGlassBottle.class; //374 list[SPIDER_EYE] = ItemSpiderEye.class; //375 list[FERMENTED_SPIDER_EYE] = ItemSpiderEyeFermented.class; //376 list[BLAZE_POWDER] = ItemBlazePowder.class; //377 list[MAGMA_CREAM] = ItemMagmaCream.class; //378 list[BREWING_STAND] = ItemBrewingStand.class; //379 list[CAULDRON] = ItemCauldron.class; //380 list[ENDER_EYE] = ItemEnderEye.class; //381 list[GLISTERING_MELON] = ItemMelonGlistering.class; //382 list[SPAWN_EGG] = ItemSpawnEgg.class; //383 list[EXPERIENCE_BOTTLE] = ItemExpBottle.class; //384 list[FIRE_CHARGE] = ItemFireCharge.class; //385 list[BOOK_AND_QUILL] = ItemBookAndQuill.class; //386 list[WRITTEN_BOOK] = ItemBookWritten.class; //387 list[EMERALD] = ItemEmerald.class; //388 list[ITEM_FRAME] = ItemItemFrame.class; //389 list[FLOWER_POT] = ItemFlowerPot.class; //390 list[CARROT] = ItemCarrot.class; //391 list[POTATO] = ItemPotato.class; //392 list[BAKED_POTATO] = ItemPotatoBaked.class; //393 list[POISONOUS_POTATO] = ItemPotatoPoisonous.class; //394 list[EMPTY_MAP] = ItemEmptyMap.class; //395 list[GOLDEN_CARROT] = ItemCarrotGolden.class; //396 list[SKULL] = ItemSkull.class; //397 list[CARROT_ON_A_STICK] = ItemCarrotOnAStick.class; //398 list[NETHER_STAR] = ItemNetherStar.class; //399 list[PUMPKIN_PIE] = ItemPumpkinPie.class; //400 list[FIREWORKS] = ItemFirework.class; //401 list[FIREWORKSCHARGE] = ItemFireworkStar.class; //402 list[ENCHANTED_BOOK] = ItemBookEnchanted.class; //403 list[COMPARATOR] = ItemRedstoneComparator.class; //404 list[NETHER_BRICK] = ItemNetherBrick.class; //405 list[QUARTZ] = ItemQuartz.class; //406 list[TNT_MINECART] = ItemMinecartTNT.class; //407 list[HOPPER_MINECART] = ItemMinecartHopper.class; //408 list[PRISMARINE_SHARD] = ItemPrismarineShard.class; //409 list[HOPPER] = ItemHopper.class; list[RAW_RABBIT] = ItemRabbitRaw.class; //411 list[COOKED_RABBIT] = ItemRabbitCooked.class; //412 list[RABBIT_STEW] = ItemRabbitStew.class; //413 list[RABBIT_FOOT] = ItemRabbitFoot.class; //414 list[RABBIT_HIDE] = ItemRabbitHide.class; //415 list[LEATHER_HORSE_ARMOR] = ItemHorseArmorLeather.class; //416 list[IRON_HORSE_ARMOR] = ItemHorseArmorIron.class; //417 list[GOLD_HORSE_ARMOR] = ItemHorseArmorGold.class; //418 list[DIAMOND_HORSE_ARMOR] = ItemHorseArmorDiamond.class; //419 list[LEAD] = ItemLead.class; //420 list[NAME_TAG] = ItemNameTag.class; //421 list[PRISMARINE_CRYSTALS] = ItemPrismarineCrystals.class; //422 list[RAW_MUTTON] = ItemMuttonRaw.class; //423 list[COOKED_MUTTON] = ItemMuttonCooked.class; //424 list[ARMOR_STAND] = ItemArmorStand.class; //425 list[END_CRYSTAL] = ItemEndCrystal.class; //426 list[SPRUCE_DOOR] = ItemDoorSpruce.class; //427 list[BIRCH_DOOR] = ItemDoorBirch.class; //428 list[JUNGLE_DOOR] = ItemDoorJungle.class; //429 list[ACACIA_DOOR] = ItemDoorAcacia.class; //430 list[DARK_OAK_DOOR] = ItemDoorDarkOak.class; //431 list[CHORUS_FRUIT] = ItemChorusFruit.class; //432 list[POPPED_CHORUS_FRUIT] = ItemChorusFruitPopped.class; //433 list[BANNER_PATTERN] = ItemBannerPattern.class; //434 list[DRAGON_BREATH] = ItemDragonBreath.class; //437 list[SPLASH_POTION] = ItemPotionSplash.class; //438 list[LINGERING_POTION] = ItemPotionLingering.class; //441 list[ELYTRA] = ItemElytra.class; //444 list[SHULKER_SHELL] = ItemShulkerShell.class; //445 list[BANNER] = ItemBanner.class; //446 list[TOTEM] = ItemTotem.class; //450 list[IRON_NUGGET] = ItemNuggetIron.class; //452 list[TRIDENT] = ItemTrident.class; //455 list[BEETROOT] = ItemBeetroot.class; //457 list[BEETROOT_SEEDS] = ItemSeedsBeetroot.class; //458 list[BEETROOT_SOUP] = ItemBeetrootSoup.class; //459 list[RAW_SALMON] = ItemSalmon.class; //460 list[CLOWNFISH] = ItemClownfish.class; //461 list[PUFFERFISH] = ItemPufferfish.class; //462 list[COOKED_SALMON] = ItemSalmonCooked.class; //463 list[DRIED_KELP] = ItemDriedKelp.class; //464 list[NAUTILUS_SHELL] = ItemNautilusShell.class; //465 list[GOLDEN_APPLE_ENCHANTED] = ItemAppleGoldEnchanted.class; //466 list[HEART_OF_THE_SEA] = ItemHeartOfTheSea.class; //467 list[SCUTE] = ItemScute.class; //468 list[TURTLE_SHELL] = ItemTurtleShell.class; //469 list[PHANTOM_MEMBRANE] = ItemPhantomMembrane.class; //470 list[CROSSBOW] = ItemCrossbow.class; //471 list[SPRUCE_SIGN] = ItemSpruceSign.class; //472 list[BIRCH_SIGN] = ItemBirchSign.class; //473 list[JUNGLE_SIGN] = ItemJungleSign.class; //474 list[ACACIA_SIGN] = ItemAcaciaSign.class; //475 list[DARKOAK_SIGN] = ItemDarkOakSign.class; //476 list[SWEET_BERRIES] = ItemSweetBerries.class; //477 list[RECORD_13] = ItemRecord13.class; //500 list[RECORD_CAT] = ItemRecordCat.class; //501 list[RECORD_BLOCKS] = ItemRecordBlocks.class; //502 list[RECORD_CHIRP] = ItemRecordChirp.class; //503 list[RECORD_FAR] = ItemRecordFar.class; //504 list[RECORD_MALL] = ItemRecordMall.class; //505 list[RECORD_MELLOHI] = ItemRecordMellohi.class; //506 list[RECORD_STAL] = ItemRecordStal.class; //507 list[RECORD_STRAD] = ItemRecordStrad.class; //508 list[RECORD_WARD] = ItemRecordWard.class; //509 list[RECORD_11] = ItemRecord11.class; //510 list[RECORD_WAIT] = ItemRecordWait.class; //511 list[SHIELD] = ItemShield.class; //513 list[GLOW_ITEM_FRAME] = ItemItemFrameGlow.class; //623 list[RECORD_OTHERSIDE] = ItemRecordOtherside.class; //626 list[ITEM_MANGROVE_DOOR] = ItemDoorMangrove.class;//633 list[MANGROVE_SIGN] = ItemMangroveSign.class;//634 list[RECORD_5] = ItemRecord5.class;//636 list[DISC_FRAGMENT_5] = ItemDiscFragment5.class;//637 list[OAK_CHEST_BOAT] = ItemChestBoatOak.class; //638 list[BIRCH_CHEST_BOAT] = ItemChestBoatBirch.class; //639 list[JUNGLE_CHEST_BOAT] = ItemChestBoatJungle.class; //640 list[SPRUCE_CHEST_BOAT] = ItemChestBoatSpruce.class; //641 list[ACACIA_CHEST_BOAT] = ItemChestBoatAcacia.class; //642 list[DARK_OAK_CHEST_BOAT] = ItemChestBoatDarkOak.class; //643 list[MANGROVE_CHEST_BOAT] = ItemChestBoatMangrove.class; //644 list[ECHO_SHARD] = ItemEchoShard.class; //647 list[GLOW_BERRIES] = ItemGlowBerries.class; //654 list[CAMPFIRE] = ItemCampfire.class; //720 list[SUSPICIOUS_STEW] = ItemSuspiciousStew.class; //734 list[HONEYCOMB] = ItemHoneycomb.class; //736 list[HONEY_BOTTLE] = ItemHoneyBottle.class; //737 list[LODESTONECOMPASS] = ItemCompassLodestone.class; //741; list[NETHERITE_INGOT] = ItemIngotNetherite.class; //742 list[NETHERITE_SWORD] = ItemSwordNetherite.class; //743 list[NETHERITE_SHOVEL] = ItemShovelNetherite.class; //744 list[NETHERITE_PICKAXE] = ItemPickaxeNetherite.class; //745 list[NETHERITE_AXE] = ItemAxeNetherite.class; //746 list[NETHERITE_HOE] = ItemHoeNetherite.class; //747 list[NETHERITE_HELMET] = ItemHelmetNetherite.class; //748 list[NETHERITE_CHESTPLATE] = ItemChestplateNetherite.class; //749 list[NETHERITE_LEGGINGS] = ItemLeggingsNetherite.class; //750 list[NETHERITE_BOOTS] = ItemBootsNetherite.class; //751 list[NETHERITE_SCRAP] = ItemScrapNetherite.class; //752 list[CRIMSON_SIGN] = ItemCrimsonSign.class; //753 list[WARPED_SIGN] = ItemWarpedSign.class; //754 list[CRIMSON_DOOR] = ItemDoorCrimson.class; //755 list[WARPED_DOOR] = ItemDoorWarped.class; //756 list[WARPED_FUNGUS_ON_A_STICK] = ItemWarpedFungusOnAStick.class; //757 list[CHAIN] = ItemChain.class; //758 list[RECORD_PIGSTEP] = ItemRecordPigstep.class; //759 list[NETHER_SPROUTS] = ItemNetherSprouts.class; //760 list[AMETHYST_SHARD] = ItemAmethystShard.class; //771 list[SPYGLASS] = ItemSpyglass.class; //772 list[SOUL_CAMPFIRE] = ItemCampfireSoul.class; //801 for (int i = 0; i < 256; ++i) { if (Block.list[i] != null) { list[i] = Block.list[i]; } } RuntimeItemMapping runtimeMapping = RuntimeItems.getRuntimeMapping(); for (@SuppressWarnings("unchecked") Class aClass : list) { if (!Item.class.equals(aClass)) { continue; } try { Constructor constructor = aClass.getConstructor(); Item item = constructor.newInstance(); runtimeMapping.registerNamespacedIdItem(item.getNamespaceId(), constructor); } catch (Exception e) { log.warn("Failed to cache the namespaced id resolution of the item {}", aClass, e); } } registerInternalStringItem(runtimeMapping); } initCreativeItems(); } private static void registerInternalStringItem(RuntimeItemMapping runtimeMapping) { runtimeMapping.registerNamespacedIdItem(ItemRawIron.class); runtimeMapping.registerNamespacedIdItem(ItemRawGold.class); runtimeMapping.registerNamespacedIdItem(ItemRawCopper.class); runtimeMapping.registerNamespacedIdItem(ItemGlowInkSac.class); runtimeMapping.registerNamespacedIdItem(ItemIngotCopper.class); runtimeMapping.registerNamespacedIdItem(ItemGoatHorn.class); } private static List itemList; /** * 重构项目物品列表 *

* rebuild ItemList */ @PowerNukkitOnly @Since("1.4.0.0-PN") public static List rebuildItemList() { return itemList = Collections.unmodifiableList(Stream.of( BlockStateRegistry.getPersistenceNames().stream() .map(name -> name.substring(name.indexOf(':') + 1)), itemIds.keySet().stream() ).flatMap(Function.identity()).distinct().collect(Collectors.toList())); } /** * 获取项目物品列表也可以获取重构物品列表 *

* Get the list of item items and also get the list of reconstructed items */ @PowerNukkitOnly @Since("1.4.0.0-PN") public static List getItemList() { List itemList = Item.itemList; if (itemList == null) { return rebuildItemList(); } return itemList; } private static final ArrayList creative = new ArrayList<>(); @SneakyThrows(IOException.class) @SuppressWarnings("unchecked") private static void initCreativeItems() { clearCreativeItems(); Gson gson = new GsonBuilder().create(); List> list; try (InputStream resourceAsStream = Server.class.getModule().getResourceAsStream("creativeitems.json")) { list = gson.fromJson(new InputStreamReader(resourceAsStream), List.class); } for (Map map : list) { try { Item item = loadCreativeItemEntry(map); if (!item.isNull()) { addCreativeItem(item); } } catch (Exception e) { log.error("Error while registering a creative item {}", map, e); } } } private static Item loadCreativeItemEntry(Map data) { String name = data.get("name").toString(); String nbt = (String) data.get("nbt"); byte[] nbtBytes = nbt != null ? Base64.getDecoder().decode(nbt) : EmptyArrays.EMPTY_BYTES; if (data.containsKey("block_states")) { StringBuilder strState = new StringBuilder(name); String block_states = (String) data.get("block_states"); CompoundTag states; try { states = NBTIO.read(Base64.getDecoder().decode(block_states), ByteOrder.LITTLE_ENDIAN); } catch (IOException e) { throw new RuntimeException(e); } states.getTags().forEach((k, v) -> strState.append(';').append(k).append('=').append(v.parseValue())); String blockStateId = strState.toString(); try { Integer blockId = BlockStateRegistry.getBlockId(name); if (blockId != null && blockId > Block.MAX_BLOCK_ID) { return Item.getBlock(BlockID.AIR); } BlockState state = BlockState.of(blockStateId); Item item = state.asItemBlock(); item.setCompoundTag(nbtBytes); return item; } catch (BlockPropertyNotFoundException | UnknownRuntimeIdException e) { int runtimeId = BlockStateRegistry.getKnownRuntimeIdByBlockStateId(blockStateId); if (runtimeId == -1) { log.debug("Unsupported block found in creativeitems.json: {}", blockStateId); return Item.AIR_ITEM; } int blockId = BlockStateRegistry.getBlockIdByRuntimeId(runtimeId); BlockState defaultBlockState = BlockState.of(blockId); if (defaultBlockState.getProperties().equals(BlockUnknown.PROPERTIES)) { log.debug("Unsupported block found in creativeitems.json: {}", blockStateId); return Item.AIR_ITEM; } log.error("Failed to load the creative item with {}", blockStateId, e); return Item.AIR_ITEM; } catch (NoSuchElementException e) { log.debug("No Such Element in creativeitems.json: {}", blockStateId, e); } catch (Exception e) { log.error("Failed to load the creative item {}", blockStateId, e); return Item.AIR_ITEM; } } Item item = null; if (data.containsKey("meta")) { int meta = Utils.toInt(data.get("meta")); item = fromString(name + ":" + meta); } if (item == null) { item = fromString(name); } item.setCompoundTag(nbtBytes); return item; } /** * 注册自定义物品 *

* Register custom item * * @param c 传入自定义物品类的实例 *

* Import in an instance of a custom item class */ @PowerNukkitXOnly @Since("1.6.0.0-PNX") public static OK registerCustomItem(Class c) { return registerCustomItem(List.of(c)); } /** * 注册自定义物品 *

* Register custom item * * @param itemClassList 传入自定义物品class List
Import custom items class List */ @PowerNukkitXOnly @Since("1.6.0.0-PNX") public static OK registerCustomItem(@NotNull List> itemClassList) { if (!Server.getInstance().isEnableExperimentMode() || Server.getInstance().getConfig("settings.waterdogpe", false)) { return new OK<>(false, "The server does not have the custom item feature enabled. Unable to register the customItemList!"); } for (var clazz : itemClassList) { CustomItem customItem; Supplier supplier; try { var method = clazz.getDeclaredConstructor(); method.setAccessible(true); customItem = method.newInstance(); supplier = () -> { try { return (Item) method.newInstance(); } catch (ReflectiveOperationException e) { throw new UnsupportedOperationException(e); } }; } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { return new OK<>(false, e); } try { Identifier.assertValid(customItem.getNamespaceId()); } catch (InvalidIdentifierException e) { return new OK<>(false, e); } if (CUSTOM_ITEMS.containsKey(customItem.getNamespaceId())) continue; CUSTOM_ITEMS.put(customItem.getNamespaceId(), supplier); var customDef = customItem.getDefinition(); CUSTOM_ITEM_DEFINITIONS.put(customItem.getNamespaceId(), customDef); // 在服务端注册自定义物品的tag if (customDef.nbt().get("components") instanceof CompoundTag componentTag) { var tagList = componentTag.getList("item_tags", StringTag.class); if (tagList.size() != 0) { ItemTag.registerItemTag(customItem.getNamespaceId(), tagList.getAll().stream().map(tag -> tag.data).collect(Collectors.toSet())); } } RuntimeItems.getRuntimeMapping().registerCustomItem(customItem, supplier); addCreativeItem((Item) customItem); } return new OK(true); } /** * 卸载自定义物品 *

* Remove custom items * * @param namespaceId 传入自定义物品的namespaceId */ @PowerNukkitXOnly @Since("1.6.0.0-PNX") public static void deleteCustomItem(String namespaceId) { if (CUSTOM_ITEMS.containsKey(namespaceId)) { Item customItem = fromString(namespaceId); removeCreativeItem(customItem); CUSTOM_ITEMS.remove(namespaceId); CUSTOM_ITEM_DEFINITIONS.remove(namespaceId); RuntimeItems.getRuntimeMapping().deleteCustomItem((CustomItem) customItem); } } /** * 卸载全部的自定义物品 *

* Remove all custom items */ @PowerNukkitXOnly @Since("1.6.0.0-PNX") public static void deleteAllCustomItem() { for (String name : CUSTOM_ITEMS.keySet()) { Item customItem = fromString(name); removeCreativeItem(customItem); CUSTOM_ITEMS.remove(name); CUSTOM_ITEM_DEFINITIONS.remove(name); RuntimeItems.getRuntimeMapping().deleteCustomItem((CustomItem) customItem); } } /** * 用于获取发送给客户端的自定义物品数据 *

* Used to get the custom item data sent to the client */ @PowerNukkitXOnly @Since("1.6.0.0-PNX") public static HashMap> getCustomItems() { return new HashMap<>(CUSTOM_ITEMS); } @PowerNukkitXOnly @Since("1.19.31-r1") public static HashMap getCustomItemDefinition() { return new HashMap<>(CUSTOM_ITEM_DEFINITIONS); } /** * 取消创造模式下创造背包中的物品 *

* Cancel the Creative of items in the backpack in Creative mode */ public static void clearCreativeItems() { Item.creative.clear(); } /** * 获取{@link Item#creative} *

* Get the {@link Item#creative} */ public static ArrayList getCreativeItems() { return new ArrayList<>(Item.creative); } /** * 添加一个物品到{@link Item#creative} *

* Add a item to {@link Item#creative} */ public static void addCreativeItem(Item item) { Item.creative.add(item.clone()); } /** * 移除一个指定的创造物品 *

* Remove a specified created item */ public static void removeCreativeItem(Item item) { int index = getCreativeItemIndex(item); if (index != -1) { Item.creative.remove(index); } } /** * 检测这个物品是否存在于创造背包 *

* Detect if the item exists in the Creative backpack * * @param item * @return */ public static boolean isCreativeItem(Item item) { for (Item aCreative : Item.creative) { if (item.equals(aCreative, !item.isTool())) { return true; } } return false; } /** * @param index * @return */ public static Item getCreativeItem(int index) { return (index >= 0 && index < Item.creative.size()) ? Item.creative.get(index) : null; } /** * 获取指定物品在{@link Item#creative}中的索引 *

* Get the index of the specified item in {@link Item#creative} * * @param item 指定物品
specified item */ public static int getCreativeItemIndex(Item item) { for (int i = 0; i < Item.creative.size(); i++) { if (item.equals(Item.creative.get(i), !item.isTool())) { return i; } } return -1; } @PowerNukkitOnly public static Item getBlock(int id) { return getBlock(id, 0); } @PowerNukkitOnly public static Item getBlock(int id, Integer meta) { return getBlock(id, meta, 1); } @PowerNukkitOnly public static Item getBlock(int id, Integer meta, int count) { return getBlock(id, meta, count, EmptyArrays.EMPTY_BYTES); } @PowerNukkitOnly public static Item getBlock(int id, Integer meta, int count, byte[] tags) { var result = Block.get(id, meta).toItem(); result.setCount(count); result.setCompoundTag(tags); return result; } public static Item get(int id) { return get(id, 0); } public static Item get(int id, Integer meta) { return get(id, meta, 1); } public static Item get(int id, Integer meta, int count) { return get(id, meta, count, EmptyArrays.EMPTY_BYTES); } @PowerNukkitDifference( info = "Prevents players from getting invalid items by limiting the return to the maximum damage defined in Block.getMaxItemDamage()", since = "1.4.0.0-PN") public static Item get(int id, Integer meta, int count, byte[] tags) { try { Class c = null; if (id <= 255 - Block.MAX_BLOCK_ID) { var customBlockItem = Block.get(255 - id).toItem(); customBlockItem.setCount(count); customBlockItem.setDamage(meta); customBlockItem.setCompoundTag(tags); return customBlockItem; } else if (id < 0) { int blockId = 255 - id; c = Block.list[blockId]; } else { c = list[id]; } Item item; if (id < 256) { int blockId = id < 0 ? 255 - id : id; if (meta == 0) { item = new ItemBlock(Block.get(blockId), 0, count); } else if (meta == -1) { // Special case for item instances used in fuzzy recipes item = new ItemBlock(Block.get(blockId), -1); } else { BlockState state = BlockState.of(blockId, meta); try { state.validate(); item = state.asItemBlock(count); } catch (InvalidBlockPropertyMetaException | InvalidBlockStateException e) { log.warn("Attempted to get an ItemBlock with invalid block state in memory: {}, trying to repair the block state...", state); log.catching(org.apache.logging.log4j.Level.DEBUG, e); Block repaired = state.getBlockRepairing(null, 0, 0, 0); item = repaired.asItemBlock(count); log.error("Attempted to get an illegal item block {}:{} ({}), the meta was changed to {}", id, meta, blockId, item.getDamage(), e); } catch (UnknownRuntimeIdException e) { log.warn("Attempted to get an illegal item block {}:{} ({}), the runtime id was unknown and the meta was changed to 0", id, meta, blockId, e); item = BlockState.of(blockId).asItemBlock(count); } } } else if (c == null) { item = new Item(id, meta, count); } else { if (meta == -1) { item = ((Item) c.getConstructor(Integer.class, int.class).newInstance(0, count)).createFuzzyCraftingRecipe(); } else { item = ((Item) c.getConstructor(Integer.class, int.class).newInstance(meta, count)); } } if (tags.length != 0) { item.setCompoundTag(tags); } return item; } catch (Exception e) { log.error("Error getting the item {}:{}{}! Returning an unsafe item stack!", id, meta, id < 0 ? " (" + (255 - id) + ")" : "", e); return new Item(id, meta, count).setCompoundTag(tags); } } @PowerNukkitDifference(since = "1.4.0.0-PN", info = "Improve namespaced name handling and allows to get custom blocks by name") @NotNull public static Item fromString(String str) { String normalized = str.trim().replace(' ', '_').toLowerCase(); Matcher matcher = ITEM_STRING_PATTERN.matcher(normalized); if (!matcher.matches()) { return Item.AIR_ITEM; } String name = matcher.group(2); OptionalInt meta = OptionalInt.empty(); String metaGroup; if (name != null) { metaGroup = matcher.group(3); } else { metaGroup = matcher.group(5); } if (metaGroup != null) { meta = OptionalInt.of(Short.parseShort(metaGroup)); } String numericIdGroup = matcher.group(4); if (name != null) { String namespaceGroup = matcher.group(1); String namespacedId; if (namespaceGroup != null) { namespacedId = namespaceGroup + ":" + name; } else { namespacedId = "minecraft:" + name; } if (namespacedId.equals("minecraft:air")) { return Item.AIR_ITEM; } //custom item if (CUSTOM_ITEMS.containsKey(namespacedId)) { var item = RuntimeItems.getRuntimeMapping().getItemByNamespaceId(namespacedId, 1); Item customItem; /* * 因为getDefinition中如果需要使用Item.fromString()获取自定义物品,此时RuntimeItems中还没注册自定义物品,留一个备用构造。 * 主要用于getDefinition中addRepairItems */ if (item.getName() != null && item.getName().equals(Item.UNKNOWN_STR)) { customItem = CUSTOM_ITEMS.get(namespacedId).get(); } else customItem = item; if (meta.isPresent()) { int damage = meta.getAsInt(); if (damage < 0) { customItem = customItem.createFuzzyCraftingRecipe(); } else { customItem.setDamage(damage); } } return customItem; //custom block } else if (Block.CUSTOM_BLOCK_ID_MAP.containsKey(namespacedId)) { ItemBlock customItemBlock = (ItemBlock) RuntimeItems.getRuntimeMapping().getItemByNamespaceId(namespacedId, 1); if (meta.isPresent()) { int damage = meta.getAsInt(); if (damage < 0) { customItemBlock = (ItemBlock) customItemBlock.createFuzzyCraftingRecipe(); } else { customItemBlock.setDamage(damage); } } return customItemBlock; } //common item MinecraftItemID minecraftItemId = MinecraftItemID.getByNamespaceId(namespacedId); if (minecraftItemId != null) { //todo edu item if (minecraftItemId.isEducationEdition()) { return Item.AIR_ITEM; } Item item = minecraftItemId.get(1); if (meta.isPresent()) { int damage = meta.getAsInt(); if (damage < 0) { item = item.createFuzzyCraftingRecipe(); } else { item.setDamage(damage); } } return item; } else if (namespaceGroup != null && !namespaceGroup.equals("minecraft:")) { return Item.AIR_ITEM; } } else if (numericIdGroup != null) { int id = Integer.parseInt(numericIdGroup); return get(id, meta.orElse(0)); } if (name == null) { return Item.AIR_ITEM; } int id = 0; try { id = ItemID.class.getField(name.toUpperCase()).getInt(null); } catch (Exception ignore1) { try { id = BlockID.class.getField(name.toUpperCase()).getInt(null); return getBlock(id, meta.orElse(0)); } catch (Exception ignore2) { } } return get(id, meta.orElse(0)); } public static Item fromJson(Map data) { return fromJson(data, false); } private static Item fromJson(Map data, boolean ignoreNegativeItemId) { String nbt = (String) data.get("nbt_b64"); byte[] nbtBytes; if (nbt != null) { nbtBytes = Base64.getDecoder().decode(nbt); } else { // Support old format for backwards compat nbt = (String) data.getOrDefault("nbt_hex", null); if (nbt == null) { nbtBytes = EmptyArrays.EMPTY_BYTES; } else { nbtBytes = Utils.parseHexBinary(nbt); } } int id = Utils.toInt(data.get("id")); if (ignoreNegativeItemId && id < 0) return null; return get(id, Utils.toInt(data.getOrDefault("damage", 0)), Utils.toInt(data.getOrDefault("count", 1)), nbtBytes); } @PowerNukkitOnly @Since("1.4.0.0-PN") public static Item fromJsonNetworkId(Map data) { String nbt = (String) data.get("nbt_b64"); byte[] nbtBytes; if (nbt != null) { nbtBytes = Base64.getDecoder().decode(nbt); } else { // Support old format for backwards compat nbt = (String) data.getOrDefault("nbt_hex", null); if (nbt == null) { nbtBytes = EmptyArrays.EMPTY_BYTES; } else { nbtBytes = Utils.parseHexBinary(nbt); } } int networkId = Utils.toInt(data.get("id")); RuntimeItemMapping mapping = RuntimeItems.getRuntimeMapping(); int legacyFullId = mapping.getLegacyFullId(networkId); int id = RuntimeItems.getId(legacyFullId); OptionalInt meta = RuntimeItems.hasData(legacyFullId) ? OptionalInt.of(RuntimeItems.getData(legacyFullId)) : OptionalInt.empty(); if (data.containsKey("damage")) { int jsonMeta = Utils.toInt(data.get("damage")); if (jsonMeta != Short.MAX_VALUE) { if (meta.isPresent() && jsonMeta != meta.getAsInt()) { throw new IllegalArgumentException( "Conflicting damage value for " + mapping.getNamespacedIdByNetworkId(networkId) + ". " + "From json: " + jsonMeta + ", from mapping: " + meta.getAsInt() ); } meta = OptionalInt.of(jsonMeta); } else if (!meta.isPresent()) { meta = OptionalInt.of(-1); } } return get(id, meta.orElse(0), Utils.toInt(data.getOrDefault("count", 1)), nbtBytes); } public static Item[] fromStringMultiple(String str) { String[] b = str.split(","); Item[] items = new Item[b.length - 1]; for (int i = 0; i < b.length; i++) { items[i] = fromString(b[i]); } return items; } public Item setCompoundTag(CompoundTag tag) { this.setNamedTag(tag); return this; } public Item setCompoundTag(byte[] tags) { this.tags = tags; this.cachedNBT = null; return this; } public byte[] getCompoundTag() { return tags; } public boolean hasCompoundTag() { return this.tags != null && this.tags.length > 0; } public boolean hasCustomBlockData() { if (!this.hasCompoundTag()) { return false; } CompoundTag tag = this.getNamedTag(); return tag.contains("BlockEntityTag") && tag.get("BlockEntityTag") instanceof CompoundTag; } public Item clearCustomBlockData() { if (!this.hasCompoundTag()) { return this; } CompoundTag tag = this.getNamedTag(); if (tag.contains("BlockEntityTag") && tag.get("BlockEntityTag") instanceof CompoundTag) { tag.remove("BlockEntityTag"); this.setNamedTag(tag); } return this; } public Item setCustomBlockData(CompoundTag compoundTag) { CompoundTag tags = compoundTag.copy(); tags.setName("BlockEntityTag"); CompoundTag tag; if (!this.hasCompoundTag()) { tag = new CompoundTag(); } else { tag = this.getNamedTag(); } tag.putCompound("BlockEntityTag", tags); this.setNamedTag(tag); return this; } public CompoundTag getCustomBlockData() { if (!this.hasCompoundTag()) { return null; } CompoundTag tag = this.getNamedTag(); if (tag.contains("BlockEntityTag")) { Tag bet = tag.get("BlockEntityTag"); if (bet instanceof CompoundTag) { return (CompoundTag) bet; } } return null; } /** * 该物品是否可以应用附魔效果 */ @PowerNukkitXOnly @Since("1.6.0.0-PNX") public boolean applyEnchantments() { return true; } public boolean hasEnchantments() { if (!this.hasCompoundTag()) { return false; } CompoundTag tag = this.getNamedTag(); if (tag.contains("ench")) { Tag enchTag = tag.get("ench"); return enchTag instanceof ListTag; } else if (tag.contains("custom_ench")) { Tag enchTag = tag.get("custom_ench"); return enchTag instanceof ListTag; } return false; } /** * 通过附魔id来查找对应附魔的等级 *

* Find the enchantment level by the enchantment id. * * @param id The enchantment ID from {@link Enchantment} constants. * @return {@code 0} if the item don't have that enchantment or the current level of the given enchantment. */ @PowerNukkitOnly @Since("1.4.0.0-PN") public int getEnchantmentLevel(int id) { if (!this.hasEnchantments()) { return 0; } for (CompoundTag entry : this.getNamedTag().getList("ench", CompoundTag.class).getAll()) { if (entry.getShort("id") == id) { return entry.getShort("lvl"); } } return 0; } /** * 通过附魔id来查找对应附魔的等级 *

* Find the enchantment level by the enchantment id. * * @param id 要查询的附魔标识符 * @return {@code 0} if the item don't have that enchantment or the current level of the given enchantment. */ @PowerNukkitXOnly @Since("1.19.60-r1") public int getCustomEnchantmentLevel(String id) { if (!this.hasEnchantments()) { return 0; } for (CompoundTag entry : this.getNamedTag().getList("custom_ench", CompoundTag.class).getAll()) { if (entry.getString("id").equals(id)) { return entry.getShort("lvl"); } } return 0; } /** * @param id 要查询的附魔标识符 */ @PowerNukkitXOnly @Since("1.19.60-r1") public Enchantment getCustomEnchantment(String id) { if (!this.hasEnchantments()) { return null; } for (CompoundTag entry : this.getNamedTag().getList("custom_ench", CompoundTag.class).getAll()) { if (entry.getString("id").equals(id)) { Enchantment e = Enchantment.getEnchantment(entry.getString("id")); if (e != null) { e.setLevel(entry.getShort("lvl"), false); return e; } } } return null; } /** * 检测该物品是否有该附魔 *

* Detect if the item has the enchantment * * @param id 要查询的附魔标识符 */ @PowerNukkitXOnly @Since("1.19.60-r1") public boolean hasCustomEnchantment(String id) { return this.getCustomEnchantmentLevel(id) > 0; } /** * @param id 要查询的附魔标识符 */ @PowerNukkitXOnly @Since("1.19.80-r3") public int getCustomEnchantmentLevel(@NotNull Identifier id) { return getCustomEnchantmentLevel(id.toString()); } /** * @param id 要查询的附魔标识符 */ @PowerNukkitXOnly @Since("1.19.80-r3") public boolean hasCustomEnchantment(@NotNull Identifier id) { return hasCustomEnchantment(id.toString()); } /** * @param id 要查询的附魔标识符 */ @PowerNukkitXOnly @Since("1.19.80-r3") public Enchantment getCustomEnchantment(@NotNull Identifier id) { return getCustomEnchantment(id.toString()); } /** * 从给定的附魔id查找该物品是否存在对应的附魔效果,如果查找不到返回null *

* Get the id of the enchantment */ public Enchantment getEnchantment(int id) { return getEnchantment((short) (id & 0xffff)); } public Enchantment getEnchantment(short id) { if (!this.hasEnchantments()) { return null; } for (CompoundTag entry : this.getNamedTag().getList("ench", CompoundTag.class).getAll()) { if (entry.getShort("id") == id) { Enchantment e = Enchantment.getEnchantment(entry.getShort("id")); if (e != null) { e.setLevel(entry.getShort("lvl"), false); return e; } } } return null; } public void addEnchantment(Enchantment... enchantments) { CompoundTag tag; if (!this.hasCompoundTag()) { tag = new CompoundTag(); } else { tag = this.getNamedTag(); } ListTag ench; if (!tag.contains("ench")) { ench = new ListTag<>("ench"); tag.putList(ench); } else { ench = tag.getList("ench", CompoundTag.class); } ListTag custom_ench; if (!tag.contains("custom_ench")) { custom_ench = new ListTag<>("custom_ench"); tag.putList(custom_ench); } else { custom_ench = tag.getList("custom_ench", CompoundTag.class); } for (Enchantment enchantment : enchantments) { boolean found = false; if (enchantment.getIdentifier() == null) { for (int k = 0; k < ench.size(); k++) { CompoundTag entry = ench.get(k); if (entry.getShort("id") == enchantment.getId()) { ench.add(k, new CompoundTag() .putShort("id", enchantment.getId()) .putShort("lvl", enchantment.getLevel()) ); found = true; break; } } if (!found) { ench.add(new CompoundTag() .putShort("id", enchantment.getId()) .putShort("lvl", enchantment.getLevel()) ); } } else { for (int k = 0; k < custom_ench.size(); k++) { CompoundTag entry = custom_ench.get(k); if (entry.getString("id").equals(enchantment.getIdentifier().toString())) { custom_ench.add(k, new CompoundTag() .putString("id", enchantment.getIdentifier().toString()) .putShort("lvl", enchantment.getLevel()) ); found = true; break; } } if (!found) { custom_ench.add(new CompoundTag() .putString("id", enchantment.getIdentifier().toString()) .putShort("lvl", enchantment.getLevel()) ); } } } if (custom_ench.size() != 0) { var customName = setCustomEnchantDisplay(custom_ench); if (tag.contains("display") && tag.get("display") instanceof CompoundTag) { tag.getCompound("display").putString("Name", customName); } else { tag.putCompound("display", new CompoundTag("display") .putString("Name", customName) ); } } this.setNamedTag(tag); } private String setCustomEnchantDisplay(ListTag custom_ench) { StringJoiner joiner = new StringJoiner("\n", "" + TextFormat.RESET + TextFormat.AQUA + this.name + "\n", ""); for (var ench : custom_ench.getAll()) { var enchantment = Enchantment.getEnchantment(ench.getString("id")); joiner.add(enchantment.getLore()); } return joiner.toString(); } /** * 获取该物品所带有的全部附魔 *

* Get all the enchantments that the item comes with * * @return 如果没有附魔效果返回Enchantment.EMPTY_ARRAY
If there is no enchanting effect return Enchantment.EMPTY_ARRAY */ public Enchantment[] getEnchantments() { if (!this.hasEnchantments()) { return Enchantment.EMPTY_ARRAY; } List enchantments = new ArrayList<>(); ListTag ench = this.getNamedTag().getList("ench", CompoundTag.class); for (CompoundTag entry : ench.getAll()) { Enchantment e = Enchantment.getEnchantment(entry.getShort("id")); if (e != null) { e.setLevel(entry.getShort("lvl"), false); enchantments.add(e); } } //custom ench ListTag custom_ench = this.getNamedTag().getList("custom_ench", CompoundTag.class); for (CompoundTag entry : custom_ench.getAll()) { Enchantment e = Enchantment.getEnchantment(entry.getString("id")); if (e != null) { e.setLevel(entry.getShort("lvl"), false); enchantments.add(e); } } return enchantments.toArray(Enchantment.EMPTY_ARRAY); } /** * 检测该物品是否有该附魔 *

* Detect if the item has the enchantment * * @param id The enchantment ID from {@link Enchantment} constants. */ @Since("1.4.0.0-PN") public boolean hasEnchantment(int id) { return this.getEnchantmentLevel(id) > 0; } @Since("1.4.0.0-PN") public int getRepairCost() { if (this.hasCompoundTag()) { CompoundTag tag = this.getNamedTag(); if (tag.contains("RepairCost")) { Tag repairCost = tag.get("RepairCost"); if (repairCost instanceof IntTag) { return ((IntTag) repairCost).data; } } } return 0; } @Since("1.4.0.0-PN") public Item setRepairCost(int cost) { if (cost <= 0 && this.hasCompoundTag()) { return this.setNamedTag(this.getNamedTag().remove("RepairCost")); } CompoundTag tag; if (!this.hasCompoundTag()) { tag = new CompoundTag(); } else { tag = this.getNamedTag(); } return this.setNamedTag(tag.putInt("RepairCost", cost)); } public boolean hasCustomName() { if (!this.hasCompoundTag()) { return false; } CompoundTag tag = this.getNamedTag(); if (tag.contains("display")) { Tag tag1 = tag.get("display"); return tag1 instanceof CompoundTag && ((CompoundTag) tag1).contains("Name") && ((CompoundTag) tag1).get("Name") instanceof StringTag; } return false; } public String getCustomName() { if (!this.hasCompoundTag()) { return ""; } CompoundTag tag = this.getNamedTag(); if (tag.contains("display")) { Tag tag1 = tag.get("display"); if (tag1 instanceof CompoundTag && ((CompoundTag) tag1).contains("Name") && ((CompoundTag) tag1).get("Name") instanceof StringTag) { return ((CompoundTag) tag1).getString("Name"); } } return ""; } /** * 设置物品的自定义名字 *

* Set custom names for items * * @param name * @return */ public Item setCustomName(String name) { if (name == null || name.equals("")) { this.clearCustomName(); } CompoundTag tag; if (!this.hasCompoundTag()) { tag = new CompoundTag(); } else { tag = this.getNamedTag(); } if (tag.contains("display") && tag.get("display") instanceof CompoundTag) { tag.getCompound("display").putString("Name", name); } else { tag.putCompound("display", new CompoundTag("display") .putString("Name", name) ); } this.setNamedTag(tag); return this; } /** * 清除物品的自定义名称 *

* Clear custom name for item * * @return */ public Item clearCustomName() { if (!this.hasCompoundTag()) { return this; } CompoundTag tag = this.getNamedTag(); if (tag.contains("display") && tag.get("display") instanceof CompoundTag) { tag.getCompound("display").remove("Name"); if (tag.getCompound("display").isEmpty()) { tag.remove("display"); } this.setNamedTag(tag); } return this; } /** * 定义物品的Lore信息 *

* Get the Lore information of the item * * @return */ public String[] getLore() { Tag tag = this.getNamedTagEntry("display"); ArrayList lines = new ArrayList<>(); if (tag instanceof CompoundTag) { CompoundTag nbt = (CompoundTag) tag; ListTag lore = nbt.getList("Lore", StringTag.class); if (lore.size() > 0) { for (StringTag stringTag : lore.getAll()) { lines.add(stringTag.data); } } } return lines.toArray(EmptyArrays.EMPTY_STRINGS); } /** * 设置物品的Lore信息 *

* Set the Lore information of the item * * @param lines the lines * @return the lore */ public Item setLore(String... lines) { CompoundTag tag; if (!this.hasCompoundTag()) { tag = new CompoundTag(); } else { tag = this.getNamedTag(); } ListTag lore = new ListTag<>("Lore"); for (String line : lines) { lore.add(new StringTag("", line)); } if (!tag.contains("display")) { tag.putCompound("display", new CompoundTag("display").putList(lore)); } else { tag.getCompound("display").putList(lore); } this.setNamedTag(tag); return this; } public Tag getNamedTagEntry(String name) { CompoundTag tag = this.getNamedTag(); if (tag != null) { return tag.contains(name) ? tag.get(name) : null; } return null; } public CompoundTag getNamedTag() { if (!this.hasCompoundTag()) { return null; } if (this.cachedNBT == null) { this.cachedNBT = parseCompoundTag(this.tags); } if (this.cachedNBT != null) { this.cachedNBT.setName(""); } return this.cachedNBT; } public CompoundTag getOrCreateNamedTag() { if (!hasCompoundTag()) { return new CompoundTag(); } return getNamedTag(); } public Item setNamedTag(CompoundTag tag) { if (tag.isEmpty()) { return this.clearNamedTag(); } tag.setName(null); this.cachedNBT = tag; this.tags = writeCompoundTag(tag); return this; } public Item clearNamedTag() { return this.setCompoundTag(EmptyArrays.EMPTY_BYTES); } public static CompoundTag parseCompoundTag(byte[] tag) { try { return NBTIO.read(tag, ByteOrder.LITTLE_ENDIAN); } catch (IOException e) { throw new UncheckedIOException(e); } } public byte[] writeCompoundTag(CompoundTag tag) { try { tag.setName(""); return NBTIO.write(tag, ByteOrder.LITTLE_ENDIAN); } catch (IOException e) { throw new UncheckedIOException(e); } } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public boolean isNull() { return this.count <= 0 || this.id == AIR || this.id == STRING_IDENTIFIED_ITEM && !(this instanceof StringItem); } final public String getName() { return this.hasCustomName() ? this.getCustomName() : this.name; } final public boolean canBePlaced() { return ((this.block != null) && this.block.canBePlaced()); } public Block getBlock() { if (this.block != null) { return this.block.clone(); } else { return Block.get(BlockID.AIR); } } @Since("1.4.0.0-PN") @API(definition = API.Definition.INTERNAL, usage = API.Usage.INCUBATING) public Block getBlockUnsafe() { return this.block; } public int getId() { return id; } @PowerNukkitOnly @Since("1.4.0.0-PN") public final int getNetworkId() throws UnknownNetworkIdException { try { return RuntimeItems.getRuntimeMapping().getNetworkId(this); } catch (IllegalArgumentException e) { throw new UnknownNetworkIdException(this, e); } } @PowerNukkitOnly @Since("1.4.0.0-PN") public String getNamespaceId() { RuntimeItemMapping runtimeMapping = RuntimeItems.getRuntimeMapping(); return runtimeMapping.getNamespacedIdByNetworkId(this.getNetworkId()); } @PowerNukkitOnly @Since("1.4.0.0-PN") public int getBlockId() { if (block != null) { return block.getId(); } else { return -1; } } public int getDamage() { return meta; } public void setDamage(Integer meta) { if (meta != null) { this.meta = meta & 0xffff; } else { this.hasMeta = false; } } /** * 创建一个通配配方物品,即该物品可以不限制数据值应用到配方中 *

* Create a wildcard recipe item,the item can be applied to a recipe without restriction on data(damage/meta) values */ @PowerNukkitOnly @Since("1.4.0.0-PN") public Item createFuzzyCraftingRecipe() { Item item = clone(); item.hasMeta = false; return item; } /** * 定义物品堆叠的最大数量 *

* Define the maximum number of items to be stacked */ public int getMaxStackSize() { return block == null ? 64 : block.getItemMaxStackSize(); } /** * 获取一个可燃烧物品的燃烧时间 *

* Get the burn time of a burnable item */ final public Short getFuelTime() { if (!Fuel.isFuel(this)) { return null; } if (this.id != BUCKET || this.meta == 10) { return Fuel.getFuelDuration(this); } return null; } public boolean useOn(Entity entity) { return false; } public boolean useOn(Block block) { return false; } /** * 定义物品是否为工具 *

* Define if this item is a tool */ public boolean isTool() { return false; } /** * 定义物品最大耐久值 *

* Define the maximum durability value of the item */ public int getMaxDurability() { return -1; } /** * 定义物品的挖掘等级 *

* Define the item Tier level */ public int getTier() { return 0; } /** * 定义物品是否为镐子 *

* Define if the item is a Pickaxe */ public boolean isPickaxe() { return false; } /** * 定义物品是否为斧子 *

* Define if the item is a Axe */ public boolean isAxe() { return false; } /** * 定义物品是否为剑 *

* Define if the item is a Sword */ public boolean isSword() { return false; } /** * 定义物品是否为铲子 *

* Define if the item is a Shovel */ public boolean isShovel() { return false; } /** * 定义物品是否为锄头 *

* Define if the item is a Hoe */ public boolean isHoe() { return false; } /** * 定义物品是否为剪刀 *

* Define if the item is a Shears */ public boolean isShears() { return false; } /** * 定义物品是否为盔甲 *

* Define if the item is a Armor */ public boolean isArmor() { return false; } /** * 定义物品是否为头盔 *

* Define if the item is a Helmet */ public boolean isHelmet() { return false; } /** * 定义物品是否为胸甲 *

* Define if the item is a Chestplate */ public boolean isChestplate() { return false; } /** * 定义物品是否为护腿 *

* Define if the item is a Leggings */ public boolean isLeggings() { return false; } /** * 定义物品是否为靴子 *

* Define if the item is a Boots */ public boolean isBoots() { return false; } /** * 定义物品的附魔 *

* Define the enchantment of an item */ public int getEnchantAbility() { return 0; } /** * 定义物品的攻击伤害 *

* Define the attackdamage of an item */ public int getAttackDamage() { return 1; } /** * 定义物品的护甲值 *

* Define the Armour value of an item */ public int getArmorPoints() { return 0; } /** * 定义物品的盔甲韧性 *

* Define the Armour Toughness of an item */ public int getToughness() { return 0; } /** * 定义物品是否不可损坏 *

* Define if the item is Unbreakable */ public boolean isUnbreakable() { return false; } /** * 物品是否抵抗熔岩和火,并且可以像在水上一样漂浮在熔岩上。 *

* If the item is resistant to lava and fire and can float on lava like if it was on water. * * @since 1.4.0.0-PN */ @PowerNukkitOnly @Since("1.4.0.0-PN") public boolean isLavaResistant() { return false; } /** * 定义物品是否可以打破盾牌 *

* Define if the item can break the shield */ @PowerNukkitXOnly @Since("1.19.21-r4") public boolean canBreakShield() { return false; } /** * 在{@link #onClickAir}执行成功后才会调用 * * @param player the player * @param ticksUsed 物品被使用了多久(右键持续时间)
How long the item has been used (right-click duration) * @return the boolean */ public boolean onUse(Player player, int ticksUsed) { return false; } /** * 当玩家在长时间右键物品后释放物品时,该函数被调用。 *

* Allows the item to execute code when the player releases the item after long clicking it. * * @param player The player who released the click button
松开按钮的玩家 * @param ticksUsed How many ticks the item was held.
这个物品被使用多少ticks时间 * @return If an inventory contents update should be sent to the player
是否要向玩家发送库存内容的更新信息 */ public boolean onRelease(Player player, int ticksUsed) { return false; } @Override final public String toString() { return "Item " + this.name + " (" + (this instanceof StringItem ? this.getNamespaceId() : this.id) + ":" + (!this.hasMeta ? "?" : this.meta) + ")x" + this.count + (this.hasCompoundTag() ? " tags:0x" + Binary.bytesToHexString(this.getCompoundTag()) : ""); } public int getDestroySpeed(Block block, Player player) { return 1; } /** * 玩家使用一个物品交互时会调用这个方法 *

* This method is called when the player interacts with an item * * @param level 玩家所在地图
Player location level * @param player 玩家实例对象
Player instance object * @param block the block * @param target 交互的目标方块
Interacting target block * @param face 交互的方向
Direction of Interaction * @param fx the fx * @param fy the fy * @param fz the fz * @return boolean */ public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { return false; } @PowerNukkitOnly @Since("1.4.0.0-PN") public final Item decrement(int amount) { return increment(-amount); } @PowerNukkitOnly @Since("1.4.0.0-PN") public final Item increment(int amount) { if (count + amount <= 0) { return getBlock(BlockID.AIR); } Item cloned = clone(); cloned.count += amount; return cloned; } /** * 如果为true,这个物品可以如骨粉一样减少作物成长时间 *

* When true, this item can be used to reduce growing times like a bone meal. * * @return {@code true} if it can act like a bone meal */ @Since("1.4.0.0-PN") @PowerNukkitOnly public boolean isFertilizer() { return false; } /** * 当玩家对着空中使用物品时调用,例如投掷物品。返回物品是否已更改,例如数量减少或耐久度更改。 *

* Called when a player uses the item on air, for example throwing a projectile. * Returns whether the item was changed, for example count decrease or durability change. * * @param player player * @param directionVector 点击的方向向量
The direction vector of the click * @return item changed */ public boolean onClickAir(Player player, Vector3 directionVector) { return false; } @Override public final boolean equals(Object item) { return item instanceof Item && this.equals((Item) item, true); } public final boolean equals(Item item, boolean checkDamage) { return equals(item, checkDamage, true); } /** * 判断两个物品是否相等 * * @param item 要比较的物品 * @param checkDamage 是否检查数据值 * @param checkCompound 是否检查NBT * @return the boolean */ public final boolean equals(Item item, boolean checkDamage, boolean checkCompound) { if (this.getId() == 255 && item.getId() == 255) { if (!this.getNamespaceId().equals(item.getNamespaceId())) return false; } else if (this.getId() != item.getId()) return false; if (!checkDamage || this.getDamage() == item.getDamage()) { if (checkCompound) { if (Arrays.equals(this.getCompoundTag(), item.getCompoundTag())) { return true; } else if (this.hasCompoundTag() && item.hasCompoundTag()) { return this.getNamedTag().equals(item.getNamedTag()); } } else { return true; } } return false; } /** * 返回物品堆叠是否与指定的物品堆叠有相同的ID,伤害,NBT和数量 *

* Returns whether the specified item stack has the same ID, damage, NBT and count as this item stack. * * @param other item * @return equal */ public final boolean equalsExact(Item other) { return this.equals(other, true, true) && this.count == other.count; } /** * Same as {@link #equals(Item, boolean)} but the enchantment order of the items does not affect the result. * * @since 1.2.1.0-PN */ @PowerNukkitOnly @Since("1.2.1.0-PN") public final boolean equalsIgnoringEnchantmentOrder(Item item, boolean checkDamage) { if (!this.equals(item, checkDamage, false)) { return false; } if (Arrays.equals(this.getCompoundTag(), item.getCompoundTag())) { return true; } if (!this.hasCompoundTag() || !item.hasCompoundTag()) { return false; } CompoundTag thisTags = this.getNamedTag(); CompoundTag otherTags = item.getNamedTag(); if (thisTags.equals(otherTags)) { return true; } if (!thisTags.contains("ench") || !otherTags.contains("ench") || !(thisTags.get("ench") instanceof ListTag) || !(otherTags.get("ench") instanceof ListTag) || thisTags.getList("ench").size() != otherTags.getList("ench").size()) { return false; } ListTag thisEnchantmentTags = thisTags.getList("ench", CompoundTag.class); ListTag otherEnchantmentTags = otherTags.getList("ench", CompoundTag.class); int size = thisEnchantmentTags.size(); Int2IntMap enchantments = new Int2IntArrayMap(size); enchantments.defaultReturnValue(Integer.MIN_VALUE); for (int i = 0; i < size; i++) { CompoundTag tag = thisEnchantmentTags.get(i); enchantments.put(tag.getShort("id"), tag.getShort("lvl")); } for (int i = 0; i < size; i++) { CompoundTag tag = otherEnchantmentTags.get(i); if (enchantments.get(tag.getShort("id")) != tag.getShort("lvl")) { return false; } } return true; } @Deprecated public final boolean deepEquals(Item item) { return equals(item, true); } @Deprecated public final boolean deepEquals(Item item, boolean checkDamage) { return equals(item, checkDamage, true); } @Deprecated public final boolean deepEquals(Item item, boolean checkDamage, boolean checkCompound) { return equals(item, checkDamage, checkCompound); } @Override public Item clone() { try { Item item = (Item) super.clone(); item.tags = this.tags.clone(); item.cachedNBT = null; return item; } catch (CloneNotSupportedException e) { return null; } } /** * 控制此方块(在冒险模式下)可以使用/放置在其上的方块类型。 *

* Controls what block types this block may be placed on. */ @PowerNukkitXOnly @Since("1.6.0.0-PNX") public void addCanPlaceOn(Block block) { CompoundTag tag = getOrCreateNamedTag(); ListTag canPlaceOn = tag.getList("CanPlaceOn", StringTag.class); tag.putList(canPlaceOn.add(new StringTag("", block.toItem().getNamespaceId()))); this.setCompoundTag(tag); } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public void addCanPlaceOn(Block[] blocks) { for (Block block : blocks) { addCanPlaceOn(block); } } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public void setCanPlaceOn(Block[] blocks) { CompoundTag tag = getOrCreateNamedTag(); ListTag canPlaceOn = new ListTag<>("CanPlaceOn"); for (Block block : blocks) { canPlaceOn.add(new StringTag("", block.toItem().getNamespaceId())); } tag.putList(canPlaceOn); this.setCompoundTag(tag); } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public ListTag getCanPlaceOn() { CompoundTag tag = getOrCreateNamedTag(); return tag.getList("CanPlaceOn", StringTag.class); } /** * 控制此方块(在冒险模式下)可以破坏的方块类型。此效果不会改变原本的破坏速度和破坏后掉落物。 *

* Controls what block types can destroy */ @PowerNukkitXOnly @Since("1.6.0.0-PNX") public void addCanDestroy(Block block) { CompoundTag tag = getOrCreateNamedTag(); ListTag canDestroy = tag.getList("CanDestroy", StringTag.class); tag.putList(canDestroy.add(new StringTag("", block.toItem().getNamespaceId()))); this.setCompoundTag(tag); } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public void addCanDestroy(Block[] blocks) { for (Block block : blocks) { addCanDestroy(block); } } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public void setCanDestroy(Block[] blocks) { CompoundTag tag = getOrCreateNamedTag(); ListTag canDestroy = new ListTag<>("CanDestroy"); for (Block block : blocks) { canDestroy.add(new StringTag("", block.toItem().getNamespaceId())); } tag.putList(canDestroy); this.setCompoundTag(tag); } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public ListTag getCanDestroy() { CompoundTag tag = getOrCreateNamedTag(); return tag.getList("CanDestroy", StringTag.class); } /** * 物品锁定在玩家的物品栏 * LOCK_IN_SLOT 阻止该物品被从玩家物品栏的该槽位移动、移除、丢弃或用于合成 * LOCK_IN_INVENTORY 阻止该物品被从玩家的物品栏移除、丢弃或用于合成 *

* Locks the item in the player's inventory * LOCK_IN_SLOT Prevents the item from being removed from the player's inventory, dropped, or crafted with. * LOCK_IN_INVENTORY Prevents the item from being moved or removed from its slot in the player's inventory, dropped, or crafted with */ @PowerNukkitXOnly @Since("1.6.0.0-PNX") public enum ItemLockMode { NONE,//only used in server LOCK_IN_SLOT, LOCK_IN_INVENTORY } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public void setItemLockMode(ItemLockMode mode) { CompoundTag tag = getOrCreateNamedTag(); if (mode == ItemLockMode.NONE) { tag.remove("minecraft:item_lock"); } else { tag.putByte("minecraft:item_lock", mode.ordinal()); } this.setCompoundTag(tag); } /** * 获取物品锁定在玩家的物品栏的模式 *

* Get items locked mode in the player's item inventory * * @return */ @PowerNukkitXOnly @Since("1.6.0.0-PNX") public ItemLockMode getItemLockMode() { CompoundTag tag = getOrCreateNamedTag(); if (tag.contains("minecraft:item_lock")) { return ItemLockMode.values()[tag.getByte("minecraft:item_lock")]; } return ItemLockMode.NONE; } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public void setKeepOnDeath(boolean keepOnDeath) { CompoundTag tag = getOrCreateNamedTag(); if (keepOnDeath) { tag.putByte("minecraft:keep_on_death", 1); } else { tag.remove("minecraft:keep_on_death"); } this.setCompoundTag(tag); } /** * 该物品是否死亡不掉落 *

* Define if the item does not drop on death * * @return */ @PowerNukkitXOnly @Since("1.6.0.0-PNX") public boolean keepOnDeath() { CompoundTag tag = getOrCreateNamedTag(); return tag.contains("minecraft:keep_on_death"); } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public static class ItemJsonComponents { private static Gson gson = new Gson(); public static class CanPlaceOn { public String[] blocks; } public static class CanDestory { public String[] blocks; } public static class ItemLock { public static final String LOCK_IN_INVENTORY = "lock_in_inventory"; public static final String LOCK_IN_SLOT = "lock_in_slot"; String mode; } public static ItemJsonComponents fromJson(String json) { return gson.fromJson(json, ItemJsonComponents.class); } public static class KeepOnDeath { } private ItemJsonComponents() { } @SerializedName(value = "minecraft:can_place_on", alternate = {"can_place_on"}) public CanPlaceOn canPlaceOn; @SerializedName(value = "minecraft:can_destroy", alternate = {"can_destroy"}) public CanDestory canDestroy; @SerializedName(value = "minecraft:item_lock", alternate = {"item_lock"}) public ItemLock itemLock; @SerializedName(value = "minecraft:keep_on_death", alternate = {"keep_on_death"}) public KeepOnDeath keepOnDeath; } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public void readItemJsonComponents(ItemJsonComponents components) { if (components.canPlaceOn != null) this.setCanPlaceOn(Arrays.stream(components.canPlaceOn.blocks).map(str -> Block.get(BlockStateRegistry.getBlockId(str.startsWith("minecraft:") ? str : "minecraft:" + str))).collect(Collectors.toList()).toArray(new Block[0])); if (components.canDestroy != null) this.setCanDestroy(Arrays.stream(components.canDestroy.blocks).map(str -> Block.get(BlockStateRegistry.getBlockId(str.startsWith("minecraft:") ? str : "minecraft:" + str))).collect(Collectors.toList()).toArray(new Block[0])); if (components.itemLock != null) this.setItemLockMode(switch (components.itemLock.mode) { case ItemJsonComponents.ItemLock.LOCK_IN_SLOT -> Item.ItemLockMode.LOCK_IN_SLOT; case ItemJsonComponents.ItemLock.LOCK_IN_INVENTORY -> Item.ItemLockMode.LOCK_IN_INVENTORY; default -> Item.ItemLockMode.NONE; }); if (components.keepOnDeath != null) this.setKeepOnDeath(components.keepOnDeath != null); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy