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

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

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

import cn.nukkit.api.API;
import cn.nukkit.api.PowerNukkitOnly;
import cn.nukkit.api.PowerNukkitXOnly;
import cn.nukkit.api.Since;
import cn.nukkit.block.customblock.CustomBlock;
import cn.nukkit.item.customitem.CustomItemDefinition;
import cn.nukkit.item.customitem.ItemCustom;
import cn.nukkit.utils.BinaryStream;
import com.google.common.base.Preconditions;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static com.google.common.base.Verify.verify;

/**
 * Responsible for mapping item full ids, item network ids and item namespaced ids between each other.
 * 
    *
  • A full id is a combination of item id and item damage. * The way they are combined may change in future, so you should not combine them by yourself and neither store them * permanently. It's mainly used to preserve backward compatibility with plugins that don't support namespaced ids. *
  • A network id is an id that is used to communicated with the client, it may change between executions of the * same server version depending on how the plugins are setup. *
  • A namespaced id is the new way Mojang saves the ids, a string like minecraft:stone. It may change * in Minecraft updates but tends to be permanent, unless Mojang decides to change them for some random reasons... */ @Log4j2 @Since("1.4.0.0-PN") public class RuntimeItemMapping { @PowerNukkitXOnly @Since("1.6.0.0-PNX") private final Collection entries; private final Int2IntMap legacyNetworkMap; private final Int2IntMap networkLegacyMap; private byte[] itemDataPalette; private final Map namespaceNetworkMap; private final Int2ObjectMap networkNamespaceMap; private final Map> namespacedIdItem = new LinkedHashMap<>(); @PowerNukkitXOnly @Since("1.6.0.0-PNX") private final HashMap customItemEntries = new HashMap<>(); @PowerNukkitXOnly @Since("1.6.0.0-PNX") private final HashMap customBlockEntries = new HashMap<>(); @Since("1.4.0.0-PN") @PowerNukkitOnly @Deprecated public RuntimeItemMapping(byte[] itemDataPalette, Int2IntMap legacyNetworkMap, Int2IntMap networkLegacyMap) { this.entries = null; this.itemDataPalette = itemDataPalette; this.legacyNetworkMap = legacyNetworkMap; this.networkLegacyMap = networkLegacyMap; this.legacyNetworkMap.defaultReturnValue(-1); this.networkLegacyMap.defaultReturnValue(-1); this.namespaceNetworkMap = new LinkedHashMap<>(); this.networkNamespaceMap = new Int2ObjectOpenHashMap<>(); } @PowerNukkitOnly @Since("1.4.0.0-PN") @API(definition = API.Definition.INTERNAL, usage = API.Usage.BLEEDING) @Deprecated public RuntimeItemMapping( byte[] itemDataPalette, Int2IntMap legacyNetworkMap, Int2IntMap networkLegacyMap, Map namespaceNetworkMap, Int2ObjectMap networkNamespaceMap) { this.entries = null; this.itemDataPalette = itemDataPalette; this.legacyNetworkMap = legacyNetworkMap; this.networkLegacyMap = networkLegacyMap; this.legacyNetworkMap.defaultReturnValue(-1); this.networkLegacyMap.defaultReturnValue(-1); this.networkNamespaceMap = networkNamespaceMap; this.namespaceNetworkMap = namespaceNetworkMap.entrySet().stream() .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), OptionalInt.of(e.getValue()))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public RuntimeItemMapping(Collection entries) { this.entries = entries; this.legacyNetworkMap = new Int2IntOpenHashMap(); this.networkLegacyMap = new Int2IntOpenHashMap(); LinkedHashMap namespaceNetworkMap = new LinkedHashMap<>(); this.networkNamespaceMap = new Int2ObjectOpenHashMap<>(); for (RuntimeItems.Entry entry : entries) { namespaceNetworkMap.put(entry.name, OptionalInt.of(entry.id)); networkNamespaceMap.put(entry.id, entry.name); if (entry.oldId != null) { boolean hasData = entry.oldData != null; int fullId = RuntimeItems.getFullId(entry.oldId, hasData ? entry.oldData : 0); if (entry.deprecated != Boolean.TRUE) { verify(legacyNetworkMap.put(fullId, (entry.id << 1) | (hasData ? 1 : 0)) == 0, "Conflict while registering an item runtime id!" ); } verify(networkLegacyMap.put(entry.id, fullId | (hasData ? 1 : 0)) == 0, "Conflict while registering an item runtime id!" ); } } this.namespaceNetworkMap = namespaceNetworkMap.entrySet().stream() .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), e.getValue())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); this.legacyNetworkMap.defaultReturnValue(-1); this.networkLegacyMap.defaultReturnValue(-1); this.generatePalette(); } @PowerNukkitXOnly @Since("1.6.0.0-PNX") private synchronized void generatePalette() { if (this.entries == null) { return; } BinaryStream paletteBuffer = new BinaryStream(); paletteBuffer.putUnsignedVarInt(this.entries.size()); for (RuntimeItems.Entry entry : this.entries) { paletteBuffer.putString(entry.name.replace("minecraft:", "")); paletteBuffer.putLShort(entry.id); paletteBuffer.putBoolean(entry.isComponentItem); // Component item } this.itemDataPalette = paletteBuffer.getBuffer(); } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public synchronized void registerCustomItem(ItemCustom itemCustom) { var runtimeId = CustomItemDefinition.getRuntimeId(itemCustom.getNamespaceId()); RuntimeItems.Entry entry = new RuntimeItems.Entry( itemCustom.getNamespaceId(), runtimeId, null, null, false, true ); this.customItemEntries.put(itemCustom.getNamespaceId(), entry); this.entries.add(entry); this.registerNamespacedIdItem(itemCustom); this.namespaceNetworkMap.put(itemCustom.getNamespaceId(), OptionalInt.of(runtimeId)); this.networkNamespaceMap.put(runtimeId, itemCustom.getNamespaceId()); this.generatePalette(); } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public synchronized void deleteCustomItem(ItemCustom itemCustom) { RuntimeItems.Entry entry = this.customItemEntries.remove(itemCustom.getNamespaceId()); if (entry != null) { this.entries.remove(entry); this.namespaceNetworkMap.remove(itemCustom.getNamespaceId()); this.networkNamespaceMap.remove(CustomItemDefinition.getRuntimeId(itemCustom.getNamespaceId())); this.generatePalette(); } } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public synchronized void registerCustomBlock(List blocks) { for (var block : blocks) { int id = 255 - block.getId();//方块物品id等于 255-方块id(即-750开始递减) RuntimeItems.Entry entry = new RuntimeItems.Entry( block.getNamespaceId(),//方块命名空间也是方块物品命名空间 id, id, null, null, false ); this.customBlockEntries.put(block.getNamespaceId(), entry); this.entries.add(entry); this.namespacedIdItem.put(block.getNamespaceId(), block::toItem); this.namespaceNetworkMap.put(block.getNamespaceId(), OptionalInt.of(id)); this.networkNamespaceMap.put(id, block.getNamespaceId()); int fullId = RuntimeItems.getFullId(id, 0); legacyNetworkMap.put(fullId, id << 1);//todo 实现多状态方块需要在这里加入数据值判断 networkLegacyMap.put(id, fullId); } this.generatePalette(); } @PowerNukkitXOnly @Since("1.6.0.0-PNX") public synchronized void deleteCustomBlock(List blocks) { for (var block : blocks) { RuntimeItems.Entry entry = this.customBlockEntries.remove(block.getNamespaceId()); if (entry != null) { this.entries.remove(entry); this.namespaceNetworkMap.remove(block.getNamespaceId()); this.networkNamespaceMap.remove(255 - block.getId()); } } this.generatePalette(); } /** * Returns the network id based on the full id of the given item. * * @param item Given item * @return The network id * @throws IllegalArgumentException If the mapping of the full id to the network id is unknown */ @PowerNukkitOnly @Since("1.4.0.0-PN") public int getNetworkFullId(Item item) { if (item instanceof StringItem) { return namespaceNetworkMap.getOrDefault(item.getNamespaceId(), OptionalInt.empty()) .orElseThrow(() -> new IllegalArgumentException("Unknown item mapping " + item)) << 1; } int fullId = RuntimeItems.getFullId(item.getId(), item.hasMeta() ? item.getDamage() : -1); int networkFullId = this.legacyNetworkMap.get(fullId); if (networkFullId == -1 && !item.hasMeta() && item.getDamage() != 0) { // Fuzzy crafting recipe of a remapped item, like charcoal networkFullId = this.legacyNetworkMap.get(RuntimeItems.getFullId(item.getId(), item.getDamage())); } if (networkFullId == -1) { networkFullId = this.legacyNetworkMap.get(RuntimeItems.getFullId(item.getId(), 0)); } if (networkFullId == -1) { throw new IllegalArgumentException("Unknown item mapping " + item); } return networkFullId; } /** * Returns the full id of a given network id. * * @param networkId The given network id * @return The full id * @throws IllegalArgumentException If the mapping of the full id to the network id is unknown */ @PowerNukkitOnly @Since("1.4.0.0-PN") public int getLegacyFullId(int networkId) { int fullId = networkLegacyMap.get(networkId); if (fullId == -1) { throw new IllegalArgumentException("Unknown network mapping: " + networkId); } return fullId; } @PowerNukkitOnly @Since("1.4.0.0-PN") public byte[] getItemDataPalette() { return this.itemDataPalette; } /** * Returns the namespaced id of a given network id. * * @param networkId The given network id * @return The namespace id or {@code null} if it is unknown */ @PowerNukkitOnly @Since("1.4.0.0-PN") @Nullable public String getNamespacedIdByNetworkId(int networkId) { return networkNamespaceMap.get(networkId); } /** * Returns the network id of a given namespaced id. * * @param namespaceId The given namespaced id * @return A network id wrapped in {@link OptionalInt} or an empty {@link OptionalInt} if it is unknown */ @PowerNukkitOnly @Since("1.4.0.0-PN") @Nonnull public OptionalInt getNetworkIdByNamespaceId(@Nonnull String namespaceId) { return namespaceNetworkMap.getOrDefault(namespaceId, OptionalInt.empty()); } /** * Creates a new instance of the respective {@link Item} by the namespaced id. * * @param namespaceId The namespaced id * @param amount How many items will be in the stack. * @return The correct {@link Item} instance with the write item id and item damage values. * @throws IllegalArgumentException If there are unknown mappings in the process. */ @PowerNukkitOnly @Since("1.4.0.0-PN") @Nonnull public Item getItemByNamespaceId(@Nonnull String namespaceId, int amount) { Supplier constructor = this.namespacedIdItem.get(namespaceId.toLowerCase(Locale.ENGLISH)); if (constructor != null) { try { Item item = constructor.get(); item.setCount(amount); return item; } catch (Exception e) { log.warn("Could not create a new instance of {} using the namespaced id {}", constructor, namespaceId, e); } } int legacyFullId; try { legacyFullId = getLegacyFullId( getNetworkIdByNamespaceId(namespaceId) .orElseThrow(() -> new IllegalArgumentException("The network id of \"" + namespaceId + "\" is unknown")) ); } catch (IllegalArgumentException e) { log.debug("Found an unknown item {}", namespaceId, e); Item item = new StringItem(namespaceId, Item.UNKNOWN_STR); item.setCount(amount); return item; } if (RuntimeItems.hasData(legacyFullId)) { return Item.get(RuntimeItems.getId(legacyFullId), RuntimeItems.getData(legacyFullId), amount); } else { Item item = Item.get(RuntimeItems.getId(legacyFullId)); item.setCount(amount); return item; } } @PowerNukkitOnly @Since("FUTURE") public void registerNamespacedIdItem(@Nonnull String namespacedId, @Nonnull Constructor constructor) { Preconditions.checkNotNull(namespacedId, "namespacedId is null"); Preconditions.checkNotNull(constructor, "constructor is null"); this.namespacedIdItem.put(namespacedId.toLowerCase(Locale.ENGLISH), itemSupplier(constructor)); } @SneakyThrows @PowerNukkitOnly @Since("FUTURE") public void registerNamespacedIdItem(@Nonnull StringItem item) { registerNamespacedIdItem(item.getNamespaceId(), item.getClass().getConstructor()); } @SneakyThrows @PowerNukkitOnly @Since("FUTURE") public void registerNamespacedIdItem(@Nonnull Class item) { registerNamespacedIdItem(item.getDeclaredConstructor().newInstance()); } @Nonnull private static Supplier itemSupplier(@Nonnull Constructor constructor) { return () -> { try { return constructor.newInstance(); } catch (ReflectiveOperationException e) { throw new UnsupportedOperationException(e); } }; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy