mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-07 08:58:20 +01:00
commit
270a466a81
@ -68,8 +68,9 @@ public class Demo {
|
||||
private static void createFrame(Instance instance, int id, int x, int y, int z) {
|
||||
EntityItemFrame itemFrame = new EntityItemFrame(new Position(x, y, z), EntityItemFrame.ItemFrameOrientation.NORTH);
|
||||
itemFrame.getPosition().setYaw(180f);
|
||||
ItemStack map = new ItemStack(Material.FILLED_MAP, (byte) 1);
|
||||
map.setItemMeta(new MapMeta(id));
|
||||
ItemStack map = ItemStack.builder(Material.FILLED_MAP)
|
||||
.meta(new MapMeta.Builder().mapId(id).build())
|
||||
.build();
|
||||
itemFrame.setItemStack(map);
|
||||
itemFrame.setInstance(instance);
|
||||
itemFrame.setCustomNameVisible(true);
|
||||
|
@ -60,7 +60,7 @@ public class Advancement {
|
||||
public Advancement(@NotNull JsonMessage title, @NotNull JsonMessage description,
|
||||
@NotNull Material icon, @NotNull FrameType frameType,
|
||||
float x, float y) {
|
||||
this(title, description, new ItemStack(icon, (byte) 1), frameType, x, y);
|
||||
this(title, description, ItemStack.of(icon), frameType, x, y);
|
||||
}
|
||||
|
||||
public Advancement(@NotNull Component title, Component description,
|
||||
@ -77,7 +77,7 @@ public class Advancement {
|
||||
public Advancement(@NotNull Component title, @NotNull Component description,
|
||||
@NotNull Material icon, @NotNull FrameType frameType,
|
||||
float x, float y) {
|
||||
this(title, description, new ItemStack(icon, (byte) 1), frameType, x, y);
|
||||
this(title, description, ItemStack.of(icon), frameType, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,7 @@ public class Notification {
|
||||
}
|
||||
|
||||
public Notification(@NotNull Component title, @NotNull FrameType frameType, @NotNull Material icon) {
|
||||
this(title, frameType, new ItemStack(icon, (byte) 1));
|
||||
this(title, frameType, ItemStack.of(icon));
|
||||
}
|
||||
|
||||
public Notification(@NotNull Component title, @NotNull FrameType frameType, @NotNull ItemStack icon) {
|
||||
@ -46,7 +46,6 @@ public class Notification {
|
||||
* Gets the title of the notification.
|
||||
*
|
||||
* @return the notification title
|
||||
*
|
||||
* @deprecated Use {@link #getTitle()}
|
||||
*/
|
||||
@NotNull
|
||||
|
@ -10,12 +10,12 @@ import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a hover event for a specific portion of the message.
|
||||
*
|
||||
* @deprecated Use {@link HoverEvent}
|
||||
*/
|
||||
@Deprecated
|
||||
@ -93,11 +93,8 @@ public class ChatHoverEvent {
|
||||
JsonObject obj = GsonComponentSerializer.gson().serializer().toJsonTree(Component.empty().hoverEvent(event)).getAsJsonObject();
|
||||
obj = obj.get("hoverEvent").getAsJsonObject().get("contents").getAsJsonObject();
|
||||
|
||||
if (itemStack.getItemMeta() != null) {
|
||||
NBTCompound compound = new NBTCompound();
|
||||
itemStack.getItemMeta().write(compound);
|
||||
obj.add("tag", new JsonPrimitive(compound.toSNBT()));
|
||||
}
|
||||
final String snbt = itemStack.getMeta().toSNBT();
|
||||
obj.add("tag", new JsonPrimitive(snbt));
|
||||
|
||||
return new ChatHoverEvent("show_item", obj);
|
||||
}
|
||||
|
@ -42,13 +42,11 @@ public class ArgumentItemStack extends Argument<ItemStack> {
|
||||
if (nbtIndex == -1) {
|
||||
// Only item name
|
||||
final Material material = Registries.getMaterial(input);
|
||||
return new ItemStack(material, (byte) 1);
|
||||
return ItemStack.of(material);
|
||||
} else {
|
||||
final String materialName = input.substring(0, nbtIndex);
|
||||
final Material material = Registries.getMaterial(materialName);
|
||||
|
||||
ItemStack itemStack = new ItemStack(material, (byte) 1);
|
||||
|
||||
final String sNBT = input.substring(nbtIndex).replace("\\\"", "\"");
|
||||
|
||||
NBTCompound compound;
|
||||
@ -58,9 +56,7 @@ public class ArgumentItemStack extends Argument<ItemStack> {
|
||||
throw new ArgumentSyntaxException("Item NBT is invalid", input, INVALID_NBT);
|
||||
}
|
||||
|
||||
NBTUtils.loadDataIntoItem(itemStack, compound);
|
||||
|
||||
return itemStack;
|
||||
return NBTUtils.loadItem(material, 1, compound);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.minestom.server.data.type;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.data.DataType;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.InventoryType;
|
||||
@ -15,7 +16,7 @@ public class InventoryData extends DataType<Inventory> {
|
||||
final int size = inventoryType.getSize();
|
||||
|
||||
// Inventory title & type
|
||||
writer.writeSizedString(value.getTitle());
|
||||
writer.writeComponent(value.getTitle());
|
||||
writer.writeSizedString(inventoryType.name());
|
||||
|
||||
// Write all item stacks
|
||||
@ -27,7 +28,7 @@ public class InventoryData extends DataType<Inventory> {
|
||||
@NotNull
|
||||
@Override
|
||||
public Inventory decode(@NotNull BinaryReader reader) {
|
||||
final String title = reader.readSizedString(Integer.MAX_VALUE);
|
||||
final Component title = reader.readComponent(Integer.MAX_VALUE);
|
||||
final InventoryType inventoryType = InventoryType.valueOf(reader.readSizedString(Integer.MAX_VALUE));
|
||||
final int size = inventoryType.getSize();
|
||||
|
||||
|
@ -109,7 +109,7 @@ public class ItemEntity extends ObjectEntity {
|
||||
if (!canApply)
|
||||
continue;
|
||||
|
||||
final ItemStack result = stackingRule.apply(itemStack.clone(), totalAmount);
|
||||
final ItemStack result = stackingRule.apply(itemStack, totalAmount);
|
||||
|
||||
EntityItemMergeEvent entityItemMergeEvent = new EntityItemMergeEvent(this, itemEntity, result);
|
||||
callCancellableEvent(EntityItemMergeEvent.class, entityItemMergeEvent, () -> {
|
||||
|
@ -37,7 +37,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
//TODO: Default attributes registration (and limitation ?)
|
||||
public class LivingEntity extends Entity implements EquipmentHandler {
|
||||
|
||||
// Item pickup
|
||||
// ItemStack pickup
|
||||
protected boolean canPickupItem;
|
||||
protected Cooldown itemPickupCooldown = new Cooldown(new UpdateOption(5, TimeUnit.TICK));
|
||||
|
||||
@ -111,13 +111,13 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
||||
}
|
||||
|
||||
private void initEquipments() {
|
||||
this.mainHandItem = ItemStack.getAirItem();
|
||||
this.offHandItem = ItemStack.getAirItem();
|
||||
this.mainHandItem = ItemStack.AIR;
|
||||
this.offHandItem = ItemStack.AIR;
|
||||
|
||||
this.helmet = ItemStack.getAirItem();
|
||||
this.chestplate = ItemStack.getAirItem();
|
||||
this.leggings = ItemStack.getAirItem();
|
||||
this.boots = ItemStack.getAirItem();
|
||||
this.helmet = ItemStack.AIR;
|
||||
this.chestplate = ItemStack.AIR;
|
||||
this.leggings = ItemStack.AIR;
|
||||
this.boots = ItemStack.AIR;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
@ -343,7 +343,7 @@ public class Metadata {
|
||||
case TYPE_OPTCHAT:
|
||||
return (Value<T>) OptChat((Component) null);
|
||||
case TYPE_SLOT:
|
||||
return (Value<T>) Slot(ItemStack.getAirItem());
|
||||
return (Value<T>) Slot(ItemStack.AIR);
|
||||
case TYPE_BOOLEAN:
|
||||
return (Value<T>) Boolean(false);
|
||||
case TYPE_ROTATION:
|
||||
|
@ -68,6 +68,7 @@ import net.minestom.server.utils.chunk.ChunkCallback;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import net.minestom.server.utils.entity.EntityUtils;
|
||||
import net.minestom.server.utils.instance.InstanceUtils;
|
||||
import net.minestom.server.utils.inventory.PlayerInventoryUtils;
|
||||
import net.minestom.server.utils.time.Cooldown;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import net.minestom.server.utils.time.UpdateOption;
|
||||
@ -589,14 +590,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
}
|
||||
}
|
||||
|
||||
// Item ownership cache
|
||||
{
|
||||
ItemStack[] itemStacks = inventory.getItemStacks();
|
||||
for (ItemStack itemStack : itemStacks) {
|
||||
ItemStack.DATA_OWNERSHIP.clearCache(itemStack.getIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all viewable entities
|
||||
this.viewableEntities.forEach(entity -> entity.removeViewer(this));
|
||||
// Clear all viewable chunks
|
||||
@ -1096,42 +1089,16 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
MinecraftServer.getBossBarManager().removeBossBar(this, bar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a book ui for the player with the given book metadata.
|
||||
*
|
||||
* @param bookMeta The metadata of the book to open
|
||||
* @deprecated Use {@link #openBook(Book)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void openBook(@NotNull WrittenBookMeta bookMeta) {
|
||||
// Set book in offhand
|
||||
final ItemStack writtenBook = new ItemStack(Material.WRITTEN_BOOK, (byte) 1);
|
||||
writtenBook.setItemMeta(bookMeta);
|
||||
final SetSlotPacket setSlotPacket = new SetSlotPacket();
|
||||
setSlotPacket.windowId = 0;
|
||||
setSlotPacket.slot = 45;
|
||||
setSlotPacket.itemStack = writtenBook;
|
||||
this.playerConnection.sendPacket(setSlotPacket);
|
||||
|
||||
// Open the book
|
||||
final OpenBookPacket openBookPacket = new OpenBookPacket();
|
||||
openBookPacket.hand = Hand.OFF;
|
||||
this.playerConnection.sendPacket(openBookPacket);
|
||||
|
||||
// Update inventory to remove book (which the actual inventory does not have)
|
||||
this.inventory.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openBook(@NotNull Book book) {
|
||||
// make the book
|
||||
ItemStack writtenBook = new ItemStack(Material.WRITTEN_BOOK, (byte) 1);
|
||||
writtenBook.setItemMeta(WrittenBookMeta.fromAdventure(book, this));
|
||||
ItemStack writtenBook = ItemStack.builder(Material.WRITTEN_BOOK)
|
||||
.meta(WrittenBookMeta.fromAdventure(book, this))
|
||||
.build();
|
||||
|
||||
// Set book in offhand
|
||||
SetSlotPacket setBookPacket = new SetSlotPacket();
|
||||
setBookPacket.windowId = 0;
|
||||
setBookPacket.slot = 45;
|
||||
setBookPacket.slot = PlayerInventoryUtils.OFFHAND_SLOT;
|
||||
setBookPacket.itemStack = writtenBook;
|
||||
playerConnection.sendPacket(setBookPacket);
|
||||
|
||||
@ -1143,7 +1110,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
// Restore the item in offhand
|
||||
SetSlotPacket restoreItemPacket = new SetSlotPacket();
|
||||
restoreItemPacket.windowId = 0;
|
||||
restoreItemPacket.slot = 45;
|
||||
restoreItemPacket.slot = PlayerInventoryUtils.OFFHAND_SLOT;
|
||||
restoreItemPacket.itemStack = getItemInOffHand();
|
||||
playerConnection.sendPacket(restoreItemPacket);
|
||||
}
|
||||
@ -1996,10 +1963,10 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
ItemStack cursorItem;
|
||||
if (openInventory == null) {
|
||||
cursorItem = getInventory().getCursorItem();
|
||||
getInventory().setCursorItem(ItemStack.getAirItem());
|
||||
getInventory().setCursorItem(ItemStack.AIR);
|
||||
} else {
|
||||
cursorItem = openInventory.getCursorItem(this);
|
||||
openInventory.setCursorItem(this, ItemStack.getAirItem());
|
||||
openInventory.setCursorItem(this, ItemStack.AIR);
|
||||
}
|
||||
if (!cursorItem.isAir()) {
|
||||
// Add item to inventory if he hasn't been able to drop it
|
||||
|
@ -4,7 +4,7 @@ import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.instance.block.BlockFace;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.InventoryModifier;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.PlayerInventory;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.network.packet.client.ClientPlayPacket;
|
||||
@ -48,12 +48,12 @@ public class FakePlayerController {
|
||||
*/
|
||||
public void clickWindow(boolean playerInventory, short slot, byte button, short action, int mode) {
|
||||
Inventory inventory = playerInventory ? null : fakePlayer.getOpenInventory();
|
||||
InventoryModifier inventoryModifier = inventory == null ? fakePlayer.getInventory() : inventory;
|
||||
playerInventory = inventoryModifier instanceof PlayerInventory;
|
||||
AbstractInventory abstractInventory = inventory == null ? fakePlayer.getInventory() : inventory;
|
||||
playerInventory = abstractInventory instanceof PlayerInventory;
|
||||
|
||||
slot = playerInventory ? (short) PlayerInventoryUtils.convertToPacketSlot(slot) : slot;
|
||||
|
||||
ItemStack itemStack = inventoryModifier.getItemStack(slot);
|
||||
ItemStack itemStack = abstractInventory.getItemStack(slot);
|
||||
|
||||
ClientClickWindowPacket clickWindowPacket = new ClientClickWindowPacket();
|
||||
clickWindowPacket.windowId = playerInventory ? 0 : inventory.getWindowId();
|
||||
|
@ -13,7 +13,7 @@ class ItemContainingMeta extends EntityMeta {
|
||||
|
||||
protected ItemContainingMeta(@NotNull Entity entity, @NotNull Metadata metadata, @NotNull Material defaultItemMaterial) {
|
||||
super(entity, metadata);
|
||||
this.defaultItem = new ItemStack(defaultItemMaterial, (byte) 1);
|
||||
this.defaultItem = ItemStack.of(defaultItemMaterial);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
@ -18,7 +18,7 @@ public class FireworkRocketMeta extends EntityMeta implements ProjectileMeta {
|
||||
|
||||
@NotNull
|
||||
public ItemStack getFireworkInfo() {
|
||||
return super.metadata.getIndex((byte) 7, ItemStack.getAirItem());
|
||||
return super.metadata.getIndex((byte) 7, ItemStack.AIR);
|
||||
}
|
||||
|
||||
public void setFireworkInfo(@NotNull ItemStack value) {
|
||||
|
@ -19,7 +19,7 @@ public class ItemFrameMeta extends EntityMeta implements ObjectDataProvider {
|
||||
|
||||
@NotNull
|
||||
public ItemStack getItem() {
|
||||
return super.metadata.getIndex((byte) 7, ItemStack.getAirItem());
|
||||
return super.metadata.getIndex((byte) 7, ItemStack.AIR);
|
||||
}
|
||||
|
||||
public void setItem(@NotNull ItemStack value) {
|
||||
|
@ -41,13 +41,13 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
||||
setLeftLegRotation(new Vector(-1f, 0, -1f));
|
||||
setRightLegRotation(new Vector(1, 0, 1));
|
||||
|
||||
this.mainHandItem = ItemStack.getAirItem();
|
||||
this.offHandItem = ItemStack.getAirItem();
|
||||
this.mainHandItem = ItemStack.AIR;
|
||||
this.offHandItem = ItemStack.AIR;
|
||||
|
||||
this.helmet = ItemStack.getAirItem();
|
||||
this.chestplate = ItemStack.getAirItem();
|
||||
this.leggings = ItemStack.getAirItem();
|
||||
this.boots = ItemStack.getAirItem();
|
||||
this.helmet = ItemStack.AIR;
|
||||
this.chestplate = ItemStack.AIR;
|
||||
this.leggings = ItemStack.AIR;
|
||||
this.boots = ItemStack.AIR;
|
||||
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
// FIXME: https://wiki.vg/Object_Data#Item_Frame_.28id_71.29
|
||||
// "You have to set both Orientation and Yaw/Pitch accordingly, otherwise it will not work."
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link net.minestom.server.entity.metadata.other.ItemFrameMeta} instead.
|
||||
*/
|
||||
@ -37,7 +38,7 @@ public class EntityItemFrame extends ObjectEntity {
|
||||
*/
|
||||
@NotNull
|
||||
public ItemStack getItemStack() {
|
||||
return metadata.getIndex((byte) 7, ItemStack.getAirItem());
|
||||
return metadata.getIndex((byte) 7, ItemStack.AIR);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,7 @@ public class EntityEyeOfEnder extends Entity {
|
||||
* @return the item
|
||||
*/
|
||||
public ItemStack getItemStack() {
|
||||
return metadata.getIndex((byte) 7, ItemStack.getAirItem());
|
||||
return metadata.getIndex((byte) 7, ItemStack.AIR);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,7 @@ public class EntityPotion extends Entity {
|
||||
|
||||
@NotNull
|
||||
public ItemStack getPotion() {
|
||||
return metadata.getIndex((byte) 7, ItemStack.getAirItem());
|
||||
return metadata.getIndex((byte) 7, ItemStack.AIR);
|
||||
}
|
||||
|
||||
public void setPotion(@NotNull ItemStack potion) {
|
||||
|
@ -1,51 +0,0 @@
|
||||
package net.minestom.server.event.player;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.CancellableEvent;
|
||||
import net.minestom.server.event.PlayerEvent;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Called as a result of {@link net.minestom.server.inventory.PlayerInventory#addItemStack(ItemStack)}.
|
||||
*/
|
||||
public class PlayerAddItemStackEvent extends PlayerEvent implements CancellableEvent {
|
||||
|
||||
private ItemStack itemStack;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
public PlayerAddItemStackEvent(@NotNull Player player, @NotNull ItemStack itemStack) {
|
||||
super(player);
|
||||
this.itemStack = itemStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item stack which will be added.
|
||||
*
|
||||
* @return the item stack
|
||||
*/
|
||||
@NotNull
|
||||
public ItemStack getItemStack() {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the item stack which will be added.
|
||||
*
|
||||
* @param itemStack the new item stack
|
||||
*/
|
||||
public void setItemStack(@NotNull ItemStack itemStack) {
|
||||
this.itemStack = itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
package net.minestom.server.event.player;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.CancellableEvent;
|
||||
import net.minestom.server.event.PlayerEvent;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Called as a result of {@link net.minestom.server.inventory.PlayerInventory#setItemStack(int, ItemStack)}
|
||||
* and player click in his inventory.
|
||||
*/
|
||||
public class PlayerSetItemStackEvent extends PlayerEvent implements CancellableEvent {
|
||||
|
||||
private int slot;
|
||||
private ItemStack itemStack;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
public PlayerSetItemStackEvent(@NotNull Player player, int slot, @NotNull ItemStack itemStack) {
|
||||
super(player);
|
||||
this.slot = slot;
|
||||
this.itemStack = itemStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the slot where the item will be set.
|
||||
*
|
||||
* @return the slot
|
||||
*/
|
||||
public int getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the slot where the item will be set.
|
||||
*
|
||||
* @param slot the new slot
|
||||
*/
|
||||
public void setSlot(int slot) {
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item stack which will be set.
|
||||
*
|
||||
* @return the item stack
|
||||
*/
|
||||
@NotNull
|
||||
public ItemStack getItemStack() {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the item stack which will be set.
|
||||
*
|
||||
* @param itemStack the new item stack
|
||||
*/
|
||||
public void setItemStack(@NotNull ItemStack itemStack) {
|
||||
this.itemStack = itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ public class ConditionedFunctionWrapper implements LootTableFunction {
|
||||
@Override
|
||||
public ItemStack apply(ItemStack stack, Data data) {
|
||||
for (Condition c : conditions) {
|
||||
if(!c.test(data))
|
||||
if (!c.test(data))
|
||||
return stack;
|
||||
}
|
||||
return baseFunction.apply(stack, data);
|
||||
|
@ -31,10 +31,10 @@ public class LootTable {
|
||||
}
|
||||
|
||||
public List<ItemStack> generate(Data arguments) {
|
||||
if(arguments == null)
|
||||
if (arguments == null)
|
||||
arguments = Data.EMPTY;
|
||||
List<ItemStack> output = new LinkedList<>();
|
||||
for(Pool p : pools) {
|
||||
for (Pool p : pools) {
|
||||
p.generate(output, arguments);
|
||||
}
|
||||
return output;
|
||||
@ -74,18 +74,18 @@ public class LootTable {
|
||||
}
|
||||
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
for(Condition c : conditions) {
|
||||
if(!c.test(arguments))
|
||||
for (Condition c : conditions) {
|
||||
if (!c.test(arguments))
|
||||
return;
|
||||
}
|
||||
Random rng = new Random();
|
||||
int luck = arguments.getOrDefault(LUCK_KEY, 0);
|
||||
int rollCount = rng.nextInt(maxRollCount - minRollCount +1 /*inclusive*/) + minRollCount;
|
||||
int bonusRollCount = rng.nextInt(bonusMaxRollCount - bonusMinRollCount +1 /*inclusive*/) + bonusMinRollCount;
|
||||
int rollCount = rng.nextInt(maxRollCount - minRollCount + 1 /*inclusive*/) + minRollCount;
|
||||
int bonusRollCount = rng.nextInt(bonusMaxRollCount - bonusMinRollCount + 1 /*inclusive*/) + bonusMinRollCount;
|
||||
bonusRollCount *= luck;
|
||||
// TODO: implement luck (quality/weight) weight=floor( weight + (quality * generic.luck))
|
||||
WeightedRandom<Entry> weightedRandom = new WeightedRandom<>(entries);
|
||||
for (int i = 0; i < rollCount+bonusRollCount; i++) {
|
||||
for (int i = 0; i < rollCount + bonusRollCount; i++) {
|
||||
Entry entry = weightedRandom.get(rng);
|
||||
entry.generateStacks(output, arguments);
|
||||
}
|
||||
@ -122,8 +122,8 @@ public class LootTable {
|
||||
}
|
||||
|
||||
public final void generateStacks(List<ItemStack> output, Data arguments) {
|
||||
for(Condition c : conditions) {
|
||||
if(!c.test(arguments))
|
||||
for (Condition c : conditions) {
|
||||
if (!c.test(arguments))
|
||||
return;
|
||||
}
|
||||
generate(output, arguments);
|
||||
|
@ -17,11 +17,11 @@ public class AlternativesEntry extends LootTable.Entry {
|
||||
|
||||
@Override
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
for(LootTable.Entry c : children) {
|
||||
for (LootTable.Entry c : children) {
|
||||
int previousSize = output.size();
|
||||
c.generateStacks(output, arguments);
|
||||
int newSize = output.size();
|
||||
if(newSize != previousSize) { // an entry managed to generate, stop here
|
||||
if (newSize != previousSize) { // an entry managed to generate, stop here
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,11 @@ public class ItemEntry extends LootTable.Entry {
|
||||
|
||||
@Override
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
ItemStack stack = new ItemStack(item, (byte)1);
|
||||
ItemStack stack = ItemStack.of(item);
|
||||
for (LootTableFunction function : functions) {
|
||||
stack = function.apply(stack, arguments);
|
||||
}
|
||||
if(!stack.isAir()) {
|
||||
if (!stack.isAir()) {
|
||||
output.add(stack);
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,11 @@ public class SequenceEntry extends LootTable.Entry {
|
||||
|
||||
@Override
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
for(LootTable.Entry c : children) {
|
||||
for (LootTable.Entry c : children) {
|
||||
int previousSize = output.size();
|
||||
c.generateStacks(output, arguments);
|
||||
int newSize = output.size();
|
||||
if(newSize == previousSize) { // an entry failed to generate, stop here
|
||||
if (newSize == previousSize) { // an entry failed to generate, stop here
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -27,19 +27,19 @@ public class TagEntry extends LootTable.Entry {
|
||||
@Override
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
Set<NamespaceID> values = tag.getValues();
|
||||
if(values.isEmpty())
|
||||
if (values.isEmpty())
|
||||
return;
|
||||
Material[] asArrayOfItems = new Material[values.size()];
|
||||
int ptr = 0;
|
||||
for (NamespaceID id : values) {
|
||||
asArrayOfItems[ptr++] = Registries.getMaterial(id);
|
||||
}
|
||||
if(expand) {
|
||||
Material selectedItem = asArrayOfItems[rng.nextInt(asArrayOfItems.length)];
|
||||
output.add(new ItemStack(selectedItem, (byte) 1));
|
||||
if (expand) {
|
||||
Material selectedMaterial = asArrayOfItems[rng.nextInt(asArrayOfItems.length)];
|
||||
output.add(ItemStack.of(selectedMaterial));
|
||||
} else {
|
||||
for(Material item : asArrayOfItems) {
|
||||
output.add(new ItemStack(item, (byte) 1));
|
||||
for (Material material : asArrayOfItems) {
|
||||
output.add(ItemStack.of(material));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,223 @@
|
||||
package net.minestom.server.inventory;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.data.DataContainer;
|
||||
import net.minestom.server.inventory.click.InventoryClickProcessor;
|
||||
import net.minestom.server.inventory.condition.InventoryCondition;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* Represents an inventory where items can be modified/retrieved.
|
||||
*/
|
||||
public abstract class AbstractInventory implements InventoryClickHandler, DataContainer {
|
||||
|
||||
private final int size;
|
||||
protected final ItemStack[] itemStacks;
|
||||
|
||||
// list of conditions/callbacks assigned to this inventory
|
||||
protected final List<InventoryCondition> inventoryConditions = new CopyOnWriteArrayList<>();
|
||||
// the click processor which process all the clicks in the inventory
|
||||
protected final InventoryClickProcessor clickProcessor = new InventoryClickProcessor();
|
||||
|
||||
private Data data;
|
||||
|
||||
protected AbstractInventory(int size) {
|
||||
this.size = size;
|
||||
this.itemStacks = new ItemStack[getSize()];
|
||||
|
||||
Arrays.fill(itemStacks, ItemStack.AIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an {@link ItemStack} at the specified slot and send relevant update to the viewer(s).
|
||||
*
|
||||
* @param slot the slot to set the item
|
||||
* @param itemStack the item to set
|
||||
*/
|
||||
public synchronized void setItemStack(int slot, @NotNull ItemStack itemStack) {
|
||||
Check.argCondition(!MathUtils.isBetween(slot, 0, getSize()),
|
||||
"Inventory does not have the slot " + slot);
|
||||
safeItemInsert(slot, itemStack);
|
||||
}
|
||||
|
||||
protected abstract void safeItemInsert(int slot, @NotNull ItemStack itemStack);
|
||||
|
||||
public synchronized <T> @NotNull T processItemStack(@NotNull ItemStack itemStack,
|
||||
@NotNull TransactionType type,
|
||||
@NotNull TransactionOption<T> option) {
|
||||
var pair = type.process(this, itemStack);
|
||||
return option.fill(this, pair.left(), pair.right());
|
||||
}
|
||||
|
||||
public synchronized <T> @NotNull List<@NotNull T> processItemStacks(@NotNull List<@NotNull ItemStack> itemStacks,
|
||||
@NotNull TransactionType type,
|
||||
@NotNull TransactionOption<T> option) {
|
||||
List<T> result = new ArrayList<>(itemStacks.size());
|
||||
itemStacks.forEach(itemStack -> {
|
||||
T transactionResult = processItemStack(itemStack, type, option);
|
||||
result.add(transactionResult);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link ItemStack} to the inventory and sends relevant update to the viewer(s).
|
||||
*
|
||||
* @param itemStack the item to add
|
||||
* @param option the transaction option
|
||||
* @return true if the item has been successfully added, false otherwise
|
||||
*/
|
||||
public <T> @NotNull T addItemStack(@NotNull ItemStack itemStack, @NotNull TransactionOption<T> option) {
|
||||
return processItemStack(itemStack, TransactionType.ADD, option);
|
||||
}
|
||||
|
||||
public boolean addItemStack(@NotNull ItemStack itemStack) {
|
||||
return addItemStack(itemStack, TransactionOption.ALL_OR_NOTHING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds {@link ItemStack}s to the inventory and sends relevant updates to the viewer(s).
|
||||
*
|
||||
* @param itemStacks items to add
|
||||
* @param option the transaction option
|
||||
* @return the operation results
|
||||
*/
|
||||
public <T> @NotNull List<@NotNull T> addItemStacks(@NotNull List<@NotNull ItemStack> itemStacks,
|
||||
@NotNull TransactionOption<T> option) {
|
||||
return processItemStacks(itemStacks, TransactionType.ADD, option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an {@link ItemStack} from the inventory and sends relevant update to the viewer(s).
|
||||
*
|
||||
* @param itemStack the item to take
|
||||
* @return true if the item has been successfully fully taken, false otherwise
|
||||
*/
|
||||
public <T> @NotNull T takeItemStack(@NotNull ItemStack itemStack, @NotNull TransactionOption<T> option) {
|
||||
return processItemStack(itemStack, TransactionType.TAKE, option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes {@link ItemStack}s from the inventory and sends relevant updates to the viewer(s).
|
||||
*
|
||||
* @param itemStacks items to take
|
||||
* @return the operation results
|
||||
*/
|
||||
public <T> @NotNull List<@NotNull T> takeItemStacks(@NotNull List<@NotNull ItemStack> itemStacks,
|
||||
@NotNull TransactionOption<T> option) {
|
||||
return processItemStacks(itemStacks, TransactionType.TAKE, option);
|
||||
}
|
||||
|
||||
public synchronized void replaceItemStack(int slot, @NotNull UnaryOperator<@NotNull ItemStack> operator) {
|
||||
var currentItem = getItemStack(slot);
|
||||
setItemStack(slot, operator.apply(currentItem));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the inventory and send relevant update to the viewer(s).
|
||||
*/
|
||||
public synchronized void clear() {
|
||||
// Clear the item array
|
||||
Arrays.fill(itemStacks, ItemStack.AIR);
|
||||
// Send the cleared inventory to viewers
|
||||
update();
|
||||
}
|
||||
|
||||
public abstract void update();
|
||||
|
||||
/**
|
||||
* Gets the {@link ItemStack} at the specified slot.
|
||||
*
|
||||
* @param slot the slot to check
|
||||
* @return the item in the slot {@code slot}
|
||||
*/
|
||||
public @NotNull ItemStack getItemStack(int slot) {
|
||||
return itemStacks[slot];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the {@link ItemStack} in the inventory.
|
||||
* <p>
|
||||
* Be aware that the returned array does not need to be the original one,
|
||||
* meaning that modifying it directly may not work.
|
||||
*
|
||||
* @return an array containing all the inventory's items
|
||||
*/
|
||||
public @NotNull ItemStack[] getItemStacks() {
|
||||
return itemStacks.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the inventory.
|
||||
*
|
||||
* @return the inventory's size
|
||||
*/
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the "inner inventory" (which includes only "usable" slots).
|
||||
*
|
||||
* @return inner inventory's size
|
||||
*/
|
||||
public int getInnerSize() {
|
||||
return getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the {@link InventoryCondition} of this inventory.
|
||||
*
|
||||
* @return a modifiable {@link List} containing all the inventory conditions
|
||||
*/
|
||||
public @NotNull List<@NotNull InventoryCondition> getInventoryConditions() {
|
||||
return inventoryConditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new {@link InventoryCondition} to this inventory.
|
||||
*
|
||||
* @param inventoryCondition the inventory condition to add
|
||||
*/
|
||||
public void addInventoryCondition(@NotNull InventoryCondition inventoryCondition) {
|
||||
this.inventoryConditions.add(inventoryCondition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Places all the items of {@code itemStacks} into the internal array.
|
||||
*
|
||||
* @param itemStacks the array to copy the content from
|
||||
* @throws IllegalArgumentException if the size of the array is not equal to {@link #getSize()}
|
||||
* @throws NullPointerException if {@code itemStacks} contains one null element or more
|
||||
*/
|
||||
public void copyContents(@NotNull ItemStack[] itemStacks) {
|
||||
Check.argCondition(itemStacks.length != getSize(),
|
||||
"The size of the array has to be of the same size as the inventory: " + getSize());
|
||||
|
||||
for (int i = 0; i < itemStacks.length; i++) {
|
||||
final ItemStack itemStack = itemStacks[i];
|
||||
Check.notNull(itemStack, "The item array cannot contain any null element!");
|
||||
setItemStack(i, itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Data getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(@Nullable Data data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
@ -1,33 +1,23 @@
|
||||
package net.minestom.server.inventory;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.Viewable;
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.data.DataContainer;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.inventory.click.ClickType;
|
||||
import net.minestom.server.inventory.click.InventoryClickLoopHandler;
|
||||
import net.minestom.server.inventory.click.InventoryClickProcessor;
|
||||
import net.minestom.server.inventory.click.InventoryClickResult;
|
||||
import net.minestom.server.inventory.condition.InventoryCondition;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.StackingRule;
|
||||
import net.minestom.server.network.packet.server.play.OpenWindowPacket;
|
||||
import net.minestom.server.network.packet.server.play.SetSlotPacket;
|
||||
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
|
||||
import net.minestom.server.network.packet.server.play.WindowPropertyPacket;
|
||||
import net.minestom.server.network.player.PlayerConnection;
|
||||
import net.minestom.server.utils.ArrayUtils;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import net.minestom.server.utils.inventory.PlayerInventoryUtils;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@ -37,7 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* You can create one with {@link Inventory#Inventory(InventoryType, String)} or by making your own subclass.
|
||||
* It can then be opened using {@link Player#openInventory(Inventory)}.
|
||||
*/
|
||||
public class Inventory implements InventoryModifier, InventoryClickHandler, Viewable, DataContainer {
|
||||
public class Inventory extends AbstractInventory implements Viewable {
|
||||
|
||||
// incremented each time an inventory is created (used in the window packets)
|
||||
private static final AtomicInteger LAST_INVENTORY_ID = new AtomicInteger();
|
||||
@ -46,41 +36,32 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
private final byte id;
|
||||
// the type of this inventory
|
||||
private final InventoryType inventoryType;
|
||||
// the title of this inventory)
|
||||
private String title;
|
||||
|
||||
// the size based on the inventory type
|
||||
private final int size;
|
||||
// the title of this inventory
|
||||
private Component title;
|
||||
|
||||
private final int offset;
|
||||
|
||||
// the items in this inventory
|
||||
private final ItemStack[] itemStacks;
|
||||
// the players currently viewing this inventory
|
||||
private final Set<Player> viewers = new CopyOnWriteArraySet<>();
|
||||
private final Set<Player> unmodifiableViewers = Collections.unmodifiableSet(viewers);
|
||||
// (player -> cursor item) map, used by the click listeners
|
||||
private final ConcurrentHashMap<Player, ItemStack> cursorPlayersItem = new ConcurrentHashMap<>();
|
||||
|
||||
// list of conditions/callbacks assigned to this inventory
|
||||
private final List<InventoryCondition> inventoryConditions = new CopyOnWriteArrayList<>();
|
||||
// the click processor which process all the clicks in the inventory
|
||||
private final InventoryClickProcessor clickProcessor = new InventoryClickProcessor();
|
||||
|
||||
private Data data;
|
||||
|
||||
public Inventory(@NotNull InventoryType inventoryType, @NotNull String title) {
|
||||
public Inventory(@NotNull InventoryType inventoryType, @NotNull Component title) {
|
||||
super(inventoryType.getSize());
|
||||
this.id = generateId();
|
||||
this.inventoryType = inventoryType;
|
||||
this.title = title;
|
||||
|
||||
this.size = inventoryType.getSize();
|
||||
this.offset = getSize();
|
||||
}
|
||||
|
||||
this.offset = size;
|
||||
|
||||
this.itemStacks = new ItemStack[size];
|
||||
|
||||
ArrayUtils.fill(itemStacks, ItemStack::getAirItem);
|
||||
/**
|
||||
* @deprecated use {@link Inventory#Inventory(InventoryType, Component)}
|
||||
*/
|
||||
@Deprecated
|
||||
public Inventory(@NotNull InventoryType inventoryType, @NotNull String title) {
|
||||
this(inventoryType, Component.text(title));
|
||||
}
|
||||
|
||||
private static byte generateId() {
|
||||
@ -106,7 +87,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
* @return the inventory title
|
||||
*/
|
||||
@NotNull
|
||||
public String getTitle() {
|
||||
public Component getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@ -115,7 +96,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
*
|
||||
* @param title the new inventory title
|
||||
*/
|
||||
public void setTitle(@NotNull String title) {
|
||||
public void setTitle(@NotNull Component title) {
|
||||
this.title = title;
|
||||
|
||||
OpenWindowPacket packet = new OpenWindowPacket(title);
|
||||
@ -141,85 +122,17 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemStack(int slot, @NotNull ItemStack itemStack) {
|
||||
Check.argCondition(!MathUtils.isBetween(slot, 0, getSize()),
|
||||
inventoryType.toString() + " does not have slot " + slot);
|
||||
|
||||
safeItemInsert(slot, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addItemStack(@NotNull ItemStack itemStack) {
|
||||
final StackingRule stackingRule = itemStack.getStackingRule();
|
||||
for (int i = 0; i < getSize(); i++) {
|
||||
ItemStack item = getItemStack(i);
|
||||
final StackingRule itemStackingRule = item.getStackingRule();
|
||||
if (itemStackingRule.canBeStacked(itemStack, item)) {
|
||||
final int itemAmount = itemStackingRule.getAmount(item);
|
||||
if (itemAmount == stackingRule.getMaxSize())
|
||||
continue;
|
||||
final int itemStackAmount = itemStackingRule.getAmount(itemStack);
|
||||
final int totalAmount = itemStackAmount + itemAmount;
|
||||
if (!stackingRule.canApply(itemStack, totalAmount)) {
|
||||
item = itemStackingRule.apply(item, itemStackingRule.getMaxSize());
|
||||
|
||||
sendSlotRefresh((short) i, item);
|
||||
itemStack = stackingRule.apply(itemStack, totalAmount - stackingRule.getMaxSize());
|
||||
} else {
|
||||
item.setAmount((byte) totalAmount);
|
||||
sendSlotRefresh((short) i, item);
|
||||
return true;
|
||||
}
|
||||
} else if (item.isAir()) {
|
||||
setItemStack(i, itemStack);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
// Clear the item array
|
||||
for (int i = 0; i < getSize(); i++) {
|
||||
setItemStackInternal(i, ItemStack.getAirItem());
|
||||
}
|
||||
// Send the cleared inventory to viewers
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemStack getItemStack(int slot) {
|
||||
return itemStacks[slot];
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemStack[] getItemStacks() {
|
||||
return itemStacks.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<InventoryCondition> getInventoryConditions() {
|
||||
return inventoryConditions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInventoryCondition(@NotNull InventoryCondition inventoryCondition) {
|
||||
this.inventoryConditions.add(inventoryCondition);
|
||||
public synchronized void clear() {
|
||||
super.clear();
|
||||
// Clear cursor
|
||||
getViewers().forEach(player ->
|
||||
setCursorItem(player, ItemStack.AIR));
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the inventory for all viewers.
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
sendPacketToViewers(createNewWindowItemsPacket());
|
||||
}
|
||||
@ -239,15 +152,6 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
playerConnection.sendPacket(createNewWindowItemsPacket());
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes only a specific slot with the updated item stack data.
|
||||
*
|
||||
* @param slot the slot to refresh
|
||||
*/
|
||||
public void refreshSlot(short slot) {
|
||||
sendSlotRefresh(slot, getItemStack(slot));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Player> getViewers() {
|
||||
@ -289,7 +193,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
*/
|
||||
@NotNull
|
||||
public ItemStack getCursorItem(@NotNull Player player) {
|
||||
return cursorPlayersItem.getOrDefault(player, ItemStack.getAirItem());
|
||||
return cursorPlayersItem.getOrDefault(player, ItemStack.AIR);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -323,8 +227,9 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
* @param slot the internal slot id
|
||||
* @param itemStack the item to insert
|
||||
*/
|
||||
private synchronized void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
|
||||
setItemStackInternal(slot, itemStack);
|
||||
@Override
|
||||
protected synchronized void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
|
||||
this.itemStacks[slot] = itemStack;
|
||||
SetSlotPacket setSlotPacket = new SetSlotPacket();
|
||||
setSlotPacket.windowId = getWindowId();
|
||||
setSlotPacket.slot = (short) slot;
|
||||
@ -332,19 +237,6 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
sendPacketToViewers(setSlotPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an item into the inventory without notifying viewers.
|
||||
* <p>
|
||||
* This will also warn the inventory that the cached window items packet is
|
||||
* not up-to-date.
|
||||
*
|
||||
* @param slot the internal slot
|
||||
* @param itemStack the item to insert
|
||||
*/
|
||||
protected void setItemStackInternal(int slot, @NotNull ItemStack itemStack) {
|
||||
itemStacks[slot] = itemStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a complete new {@link WindowItemsPacket}.
|
||||
*
|
||||
@ -381,7 +273,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
* @param player the player to change the cursor item
|
||||
* @param itemStack the cursor item
|
||||
*/
|
||||
private void setCursorPlayerItem(@NotNull Player player, @NotNull ItemStack itemStack) {
|
||||
private void refreshPlayerCursorItem(@NotNull Player player, @NotNull ItemStack itemStack) {
|
||||
this.cursorPlayersItem.put(player, itemStack);
|
||||
}
|
||||
|
||||
@ -408,7 +300,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
} else {
|
||||
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
||||
}
|
||||
setCursorPlayerItem(player, clickResult.getCursor());
|
||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
||||
|
||||
if (!clickResult.isCancel())
|
||||
callClickEvent(player, isInWindow ? this : null, slot, ClickType.LEFT_CLICK, clicked, cursor);
|
||||
@ -435,7 +327,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
} else {
|
||||
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
||||
}
|
||||
setCursorPlayerItem(player, clickResult.getCursor());
|
||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
||||
|
||||
if (!clickResult.isCancel())
|
||||
callClickEvent(player, isInWindow ? this : null, slot, ClickType.RIGHT_CLICK, clicked, cursor);
|
||||
@ -493,7 +385,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
updateFromClick(clickResult, player);
|
||||
}
|
||||
|
||||
setCursorPlayerItem(player, clickResult.getCursor());
|
||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
||||
playerInventory.update();
|
||||
update();
|
||||
|
||||
@ -543,7 +435,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
final boolean outsideDrop = slot == -999;
|
||||
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
||||
final ItemStack clicked = outsideDrop ?
|
||||
ItemStack.getAirItem() : (isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot));
|
||||
ItemStack.AIR : (isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot));
|
||||
final ItemStack cursor = getCursorItem(player);
|
||||
|
||||
final InventoryClickResult clickResult = clickProcessor.drop(isInWindow ? this : null, player,
|
||||
@ -562,7 +454,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
}
|
||||
}
|
||||
|
||||
setCursorPlayerItem(player, clickResult.getCursor());
|
||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
||||
|
||||
return !clickResult.isCancel();
|
||||
}
|
||||
@ -574,7 +466,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
||||
final ItemStack clicked = slot != -999 ?
|
||||
(isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot)) :
|
||||
ItemStack.getAirItem();
|
||||
ItemStack.AIR;
|
||||
final ItemStack cursor = getCursorItem(player);
|
||||
|
||||
final InventoryClickResult clickResult = clickProcessor.dragging(isInWindow ? this : null, player,
|
||||
@ -600,7 +492,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
updateFromClick(clickResult, player);
|
||||
}
|
||||
|
||||
setCursorPlayerItem(player, clickResult.getCursor());
|
||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
||||
|
||||
return !clickResult.isCancel();
|
||||
}
|
||||
@ -629,27 +521,11 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
if (clickResult.doRefresh())
|
||||
updateFromClick(clickResult, player);
|
||||
|
||||
setCursorPlayerItem(player, clickResult.getCursor());
|
||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
||||
|
||||
return !clickResult.isCancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh a slot for all viewers
|
||||
* <p>
|
||||
* WARNING: this does not update the items in the inventory, this is only visual
|
||||
*
|
||||
* @param slot the packet slot
|
||||
* @param itemStack the item stack to set at the slot
|
||||
*/
|
||||
private void sendSlotRefresh(short slot, ItemStack itemStack) {
|
||||
SetSlotPacket setSlotPacket = new SetSlotPacket();
|
||||
setSlotPacket.windowId = getWindowId();
|
||||
setSlotPacket.slot = slot;
|
||||
setSlotPacket.itemStack = itemStack;
|
||||
sendPacketToViewers(setSlotPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to update the inventory for a specific player in order to fix his cancelled actions
|
||||
*
|
||||
@ -663,15 +539,4 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
update(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Data getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(@Nullable Data data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
@ -1,97 +0,0 @@
|
||||
package net.minestom.server.inventory;
|
||||
|
||||
import net.minestom.server.inventory.condition.InventoryCondition;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents an inventory where items can be modified/retrieved.
|
||||
*/
|
||||
public interface InventoryModifier {
|
||||
|
||||
/**
|
||||
* Sets an {@link ItemStack} at the specified slot and send relevant update to the viewer(s).
|
||||
*
|
||||
* @param slot the slot to set the item
|
||||
* @param itemStack the item to set
|
||||
*/
|
||||
void setItemStack(int slot, @NotNull ItemStack itemStack);
|
||||
|
||||
/**
|
||||
* Adds an {@link ItemStack} to the inventory and send relevant update to the viewer(s).
|
||||
* <p>
|
||||
* Even the item cannot be fully added, the amount of {@code itemStack} will be updated.
|
||||
*
|
||||
* @param itemStack the item to add
|
||||
* @return true if the item has been successfully fully added, false otherwise
|
||||
*/
|
||||
boolean addItemStack(@NotNull ItemStack itemStack);
|
||||
|
||||
/**
|
||||
* Clears the inventory and send relevant update to the viewer(s).
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Gets the {@link ItemStack} at the specified slot.
|
||||
*
|
||||
* @param slot the slot to check
|
||||
* @return the item in the slot {@code slot}
|
||||
*/
|
||||
@NotNull
|
||||
ItemStack getItemStack(int slot);
|
||||
|
||||
/**
|
||||
* Gets all the {@link ItemStack} in the inventory.
|
||||
* <p>
|
||||
* Be aware that the returned array does not need to be the original one,
|
||||
* meaning that modifying it directly may not work.
|
||||
*
|
||||
* @return an array containing all the inventory's items
|
||||
*/
|
||||
@NotNull
|
||||
ItemStack[] getItemStacks();
|
||||
|
||||
/**
|
||||
* Gets the size of the inventory.
|
||||
*
|
||||
* @return the inventory's size
|
||||
*/
|
||||
int getSize();
|
||||
|
||||
/**
|
||||
* Gets all the {@link InventoryCondition} of this inventory.
|
||||
*
|
||||
* @return a modifiable {@link List} containing all the inventory conditions
|
||||
*/
|
||||
@NotNull
|
||||
List<InventoryCondition> getInventoryConditions();
|
||||
|
||||
/**
|
||||
* Adds a new {@link InventoryCondition} to this inventory.
|
||||
*
|
||||
* @param inventoryCondition the inventory condition to add
|
||||
*/
|
||||
void addInventoryCondition(@NotNull InventoryCondition inventoryCondition);
|
||||
|
||||
/**
|
||||
* Places all the items of {@code itemStacks} into the internal array.
|
||||
*
|
||||
* @param itemStacks the array to copy the content from
|
||||
* @throws IllegalArgumentException if the size of the array is not equal to {@link #getSize()}
|
||||
* @throws NullPointerException if {@code itemStacks} contains one null element or more
|
||||
*/
|
||||
default void copyContents(@NotNull ItemStack[] itemStacks) {
|
||||
Check.argCondition(itemStacks.length != getSize(),
|
||||
"The size of the array has to be of the same size as the inventory: " + getSize());
|
||||
|
||||
for (int i = 0; i < itemStacks.length; i++) {
|
||||
final ItemStack itemStack = itemStacks[i];
|
||||
Check.notNull(itemStack, "The item array cannot contain any null element!");
|
||||
setItemStack(i, itemStack);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,147 +1,60 @@
|
||||
package net.minestom.server.inventory;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.data.DataContainer;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.item.ArmorEquipEvent;
|
||||
import net.minestom.server.event.player.PlayerAddItemStackEvent;
|
||||
import net.minestom.server.event.player.PlayerSetItemStackEvent;
|
||||
import net.minestom.server.inventory.click.ClickType;
|
||||
import net.minestom.server.inventory.click.InventoryClickLoopHandler;
|
||||
import net.minestom.server.inventory.click.InventoryClickProcessor;
|
||||
import net.minestom.server.inventory.click.InventoryClickResult;
|
||||
import net.minestom.server.inventory.condition.InventoryCondition;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.StackingRule;
|
||||
import net.minestom.server.network.packet.server.play.EntityEquipmentPacket;
|
||||
import net.minestom.server.network.packet.server.play.SetSlotPacket;
|
||||
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
|
||||
import net.minestom.server.utils.ArrayUtils;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import static net.minestom.server.utils.inventory.PlayerInventoryUtils.*;
|
||||
|
||||
/**
|
||||
* Represents the inventory of a {@link Player}, retrieved with {@link Player#getInventory()}.
|
||||
*/
|
||||
public class PlayerInventory implements InventoryModifier, InventoryClickHandler, EquipmentHandler, DataContainer {
|
||||
public class PlayerInventory extends AbstractInventory implements EquipmentHandler {
|
||||
|
||||
public static final int INVENTORY_SIZE = 46;
|
||||
public static final int INNER_INVENTORY_SIZE = 36;
|
||||
|
||||
protected final Player player;
|
||||
protected final ItemStack[] items = new ItemStack[INVENTORY_SIZE];
|
||||
private ItemStack cursorItem = ItemStack.getAirItem();
|
||||
|
||||
private final List<InventoryCondition> inventoryConditions = new CopyOnWriteArrayList<>();
|
||||
private final InventoryClickProcessor clickProcessor = new InventoryClickProcessor();
|
||||
|
||||
private Data data;
|
||||
private ItemStack cursorItem = ItemStack.AIR;
|
||||
|
||||
public PlayerInventory(@NotNull Player player) {
|
||||
super(INVENTORY_SIZE);
|
||||
this.player = player;
|
||||
|
||||
ArrayUtils.fill(items, ItemStack::getAirItem);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemStack getItemStack(int slot) {
|
||||
return this.items[slot];
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemStack[] getItemStacks() {
|
||||
return items.clone();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<InventoryCondition> getInventoryConditions() {
|
||||
return inventoryConditions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInventoryCondition(@NotNull InventoryCondition inventoryCondition) {
|
||||
// fix packet slot to inventory slot conversion
|
||||
InventoryCondition condition = (p, slot, clickType, inventoryConditionResult) -> {
|
||||
final int convertedSlot = convertPlayerInventorySlot(slot, OFFSET);
|
||||
inventoryCondition.accept(p, convertedSlot, clickType, inventoryConditionResult);
|
||||
};
|
||||
|
||||
this.inventoryConditions.add(condition);
|
||||
super.addInventoryCondition(condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemStack(int slot, @NotNull ItemStack itemStack) {
|
||||
PlayerSetItemStackEvent setItemStackEvent = new PlayerSetItemStackEvent(player, slot, itemStack);
|
||||
player.callEvent(PlayerSetItemStackEvent.class, setItemStackEvent);
|
||||
if (setItemStackEvent.isCancelled())
|
||||
return;
|
||||
slot = setItemStackEvent.getSlot();
|
||||
itemStack = setItemStackEvent.getItemStack();
|
||||
|
||||
safeItemInsert(slot, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addItemStack(@NotNull ItemStack itemStack) {
|
||||
PlayerAddItemStackEvent addItemStackEvent = new PlayerAddItemStackEvent(player, itemStack);
|
||||
player.callEvent(PlayerAddItemStackEvent.class, addItemStackEvent);
|
||||
if (addItemStackEvent.isCancelled())
|
||||
return false;
|
||||
|
||||
itemStack = addItemStackEvent.getItemStack();
|
||||
|
||||
final StackingRule stackingRule = itemStack.getStackingRule();
|
||||
for (int i = 0; i < items.length - 10; i++) {
|
||||
ItemStack item = items[i];
|
||||
final StackingRule itemStackingRule = item.getStackingRule();
|
||||
if (itemStackingRule.canBeStacked(itemStack, item)) {
|
||||
final int itemAmount = itemStackingRule.getAmount(item);
|
||||
if (itemAmount == stackingRule.getMaxSize())
|
||||
continue;
|
||||
final int itemStackAmount = itemStackingRule.getAmount(itemStack);
|
||||
final int totalAmount = itemStackAmount + itemAmount;
|
||||
if (!stackingRule.canApply(itemStack, totalAmount)) {
|
||||
item = itemStackingRule.apply(item, itemStackingRule.getMaxSize());
|
||||
|
||||
sendSlotRefresh((short) convertToPacketSlot(i), item);
|
||||
itemStack = stackingRule.apply(itemStack, totalAmount - stackingRule.getMaxSize());
|
||||
} else {
|
||||
item.setAmount((byte) totalAmount);
|
||||
sendSlotRefresh((short) convertToPacketSlot(i), item);
|
||||
return true;
|
||||
}
|
||||
} else if (item.isAir()) {
|
||||
safeItemInsert(i, itemStack);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
// Clear the item array
|
||||
for (int i = 0; i < getSize(); i++) {
|
||||
setItemStackInternal(i, ItemStack.getAirItem());
|
||||
}
|
||||
// Send the cleared inventory to the inventory's owner
|
||||
update();
|
||||
|
||||
public synchronized void clear() {
|
||||
super.clear();
|
||||
// Reset cursor
|
||||
setCursorItem(ItemStack.AIR);
|
||||
// Update equipments
|
||||
this.player.sendPacketToViewersAndSelf(player.getEquipmentsPacket());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return INVENTORY_SIZE;
|
||||
public int getInnerSize() {
|
||||
return INNER_INVENTORY_SIZE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -214,20 +127,11 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
* Refreshes the player inventory by sending a {@link WindowItemsPacket} containing all.
|
||||
* the inventory items
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
player.getPlayerConnection().sendPacket(createWindowItemsPacket());
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes only a specific slot with the updated item stack data.
|
||||
*
|
||||
* @param slot the slot to refresh
|
||||
*/
|
||||
public void refreshSlot(short slot) {
|
||||
final int packetSlot = convertToPacketSlot(slot);
|
||||
sendSlotRefresh((short) packetSlot, getItemStack(slot));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item in player cursor.
|
||||
*
|
||||
@ -261,6 +165,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
* @throws IllegalArgumentException if the slot {@code slot} does not exist
|
||||
* @throws NullPointerException if {@code itemStack} is null
|
||||
*/
|
||||
@Override
|
||||
protected synchronized void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
|
||||
Check.argCondition(!MathUtils.isBetween(slot, 0, getSize()),
|
||||
"The slot " + slot + " does not exist for player");
|
||||
@ -295,7 +200,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
}
|
||||
}
|
||||
|
||||
this.items[slot] = itemStack;
|
||||
this.itemStacks[slot] = itemStack;
|
||||
|
||||
// Sync equipment
|
||||
if (equipmentSlot != null) {
|
||||
@ -308,10 +213,6 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
//refreshSlot((short) slot);
|
||||
}
|
||||
|
||||
protected void setItemStackInternal(int slot, ItemStack itemStack) {
|
||||
items[slot] = itemStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item from a packet slot.
|
||||
*
|
||||
@ -319,7 +220,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
* @param offset offset (generally 9 to ignore armor and craft slots)
|
||||
* @param itemStack the item stack to set
|
||||
*/
|
||||
protected void setItemStack(int slot, int offset, ItemStack itemStack) {
|
||||
protected void setItemStack(int slot, int offset, @NotNull ItemStack itemStack) {
|
||||
final int convertedSlot = convertPlayerInventorySlot(slot, offset);
|
||||
setItemStack(convertedSlot, itemStack);
|
||||
}
|
||||
@ -333,7 +234,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
*/
|
||||
protected ItemStack getItemStack(int slot, int offset) {
|
||||
final int convertedSlot = convertPlayerInventorySlot(slot, offset);
|
||||
return this.items[convertedSlot];
|
||||
return this.itemStacks[convertedSlot];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -359,9 +260,9 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
private WindowItemsPacket createWindowItemsPacket() {
|
||||
ItemStack[] convertedSlots = new ItemStack[INVENTORY_SIZE];
|
||||
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
for (int i = 0; i < itemStacks.length; i++) {
|
||||
final int slot = convertToPacketSlot(i);
|
||||
convertedSlots[slot] = items[i];
|
||||
convertedSlots[slot] = itemStacks[i];
|
||||
}
|
||||
|
||||
WindowItemsPacket windowItemsPacket = new WindowItemsPacket();
|
||||
@ -418,7 +319,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
public boolean drop(@NotNull Player player, int mode, int slot, int button) {
|
||||
final ItemStack cursor = getCursorItem();
|
||||
final boolean outsideDrop = slot == -999;
|
||||
final ItemStack clicked = outsideDrop ? ItemStack.getAirItem() : getItemStack(slot, OFFSET);
|
||||
final ItemStack clicked = outsideDrop ? ItemStack.AIR : getItemStack(slot, OFFSET);
|
||||
|
||||
final InventoryClickResult clickResult = clickProcessor.drop(null, player,
|
||||
mode, slot, button, clicked, cursor);
|
||||
@ -441,7 +342,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
|
||||
final boolean hotBarClick = convertToPacketSlot(slot) < 9;
|
||||
final InventoryClickResult clickResult = clickProcessor.shiftClick(null, player, slot, clicked, cursor,
|
||||
new InventoryClickLoopHandler(0, items.length, 1,
|
||||
new InventoryClickLoopHandler(0, itemStacks.length, 1,
|
||||
i -> {
|
||||
if (hotBarClick) {
|
||||
return i < 9 ? i + 9 : i - 9;
|
||||
@ -492,7 +393,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
@Override
|
||||
public boolean dragging(@NotNull Player player, int slot, int button) {
|
||||
final ItemStack cursor = getCursorItem();
|
||||
final ItemStack clicked = slot != -999 ? getItemStack(slot, OFFSET) : ItemStack.getAirItem();
|
||||
final ItemStack clicked = slot != -999 ? getItemStack(slot, OFFSET) : ItemStack.AIR;
|
||||
|
||||
final InventoryClickResult clickResult = clickProcessor.dragging(null, player,
|
||||
slot, button,
|
||||
@ -516,9 +417,9 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
final ItemStack cursor = getCursorItem();
|
||||
|
||||
final InventoryClickResult clickResult = clickProcessor.doubleClick(null, player, slot, cursor,
|
||||
new InventoryClickLoopHandler(0, items.length, 1,
|
||||
new InventoryClickLoopHandler(0, itemStacks.length, 1,
|
||||
i -> i < 9 ? i + 9 : i - 9,
|
||||
index -> items[index],
|
||||
index -> itemStacks[index],
|
||||
this::setItemStack));
|
||||
|
||||
if (clickResult == null)
|
||||
@ -531,15 +432,4 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
|
||||
|
||||
return !clickResult.isCancel();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Data getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(@Nullable Data data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
package net.minestom.server.inventory;
|
||||
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TransactionOption<T> {
|
||||
|
||||
/**
|
||||
* Place as much as the item as possible.
|
||||
* <p>
|
||||
* The remaining, can be air.
|
||||
*/
|
||||
TransactionOption<ItemStack> ALL = (inventory, result, itemChangesMap) -> {
|
||||
itemChangesMap.forEach(inventory::safeItemInsert);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Only place the item if can be fully added.
|
||||
* <p>
|
||||
* Returns true if the item has been added, false if nothing changed.
|
||||
*/
|
||||
TransactionOption<Boolean> ALL_OR_NOTHING = (inventory, result, itemChangesMap) -> {
|
||||
if (result.isAir()) {
|
||||
// Item can be fully placed inside the inventory, do so
|
||||
itemChangesMap.forEach(inventory::safeItemInsert);
|
||||
return true;
|
||||
} else {
|
||||
// Inventory cannot accept the item fully
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Loop through the inventory items without changing anything.
|
||||
* <p>
|
||||
* Returns true if the item can be fully added, false otherwise.
|
||||
*/
|
||||
TransactionOption<Boolean> DRY_RUN = (inventory, result, itemChangesMap) -> result.isAir();
|
||||
|
||||
@NotNull T fill(@NotNull AbstractInventory inventory,
|
||||
@NotNull ItemStack result,
|
||||
@NotNull Map<@NotNull Integer, @NotNull ItemStack> itemChangesMap);
|
||||
}
|
102
src/main/java/net/minestom/server/inventory/TransactionType.java
Normal file
102
src/main/java/net/minestom/server/inventory/TransactionType.java
Normal file
@ -0,0 +1,102 @@
|
||||
package net.minestom.server.inventory;
|
||||
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.StackingRule;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a type of transaction that you can apply to an {@link AbstractInventory}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface TransactionType {
|
||||
|
||||
/**
|
||||
* Adds an item to the inventory.
|
||||
* Can either take an air slot or be stacked.
|
||||
*/
|
||||
TransactionType ADD = (inventory, itemStack) -> {
|
||||
Int2ObjectMap<ItemStack> itemChangesMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
final StackingRule stackingRule = itemStack.getStackingRule();
|
||||
|
||||
// Check filled slot (not air)
|
||||
for (int i = 0; i < inventory.getInnerSize(); i++) {
|
||||
ItemStack inventoryItem = inventory.getItemStack(i);
|
||||
if (inventoryItem.isAir()) {
|
||||
continue;
|
||||
}
|
||||
if (stackingRule.canBeStacked(itemStack, inventoryItem)) {
|
||||
final int itemAmount = stackingRule.getAmount(inventoryItem);
|
||||
if (itemAmount == stackingRule.getMaxSize())
|
||||
continue;
|
||||
final int itemStackAmount = stackingRule.getAmount(itemStack);
|
||||
final int totalAmount = itemStackAmount + itemAmount;
|
||||
if (!stackingRule.canApply(itemStack, totalAmount)) {
|
||||
// Slot cannot accept the whole item, reduce amount to 'itemStack'
|
||||
itemChangesMap.put(i, stackingRule.apply(inventoryItem, stackingRule.getMaxSize()));
|
||||
itemStack = stackingRule.apply(itemStack, totalAmount - stackingRule.getMaxSize());
|
||||
} else {
|
||||
// Slot can accept the whole item
|
||||
itemChangesMap.put(i, stackingRule.apply(inventoryItem, totalAmount));
|
||||
itemStack = stackingRule.apply(itemStack, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check air slot to fill
|
||||
for (int i = 0; i < inventory.getInnerSize(); i++) {
|
||||
ItemStack inventoryItem = inventory.getItemStack(i);
|
||||
if (!inventoryItem.isAir()) {
|
||||
continue;
|
||||
}
|
||||
// Fill the slot
|
||||
itemChangesMap.put(i, itemStack);
|
||||
itemStack = stackingRule.apply(itemStack, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return Pair.of(itemStack, itemChangesMap);
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes an item from the inventory.
|
||||
* Can either transform items to air or reduce their amount.
|
||||
*/
|
||||
TransactionType TAKE = (inventory, itemStack) -> {
|
||||
Int2ObjectMap<ItemStack> itemChangesMap = new Int2ObjectOpenHashMap<>();
|
||||
final StackingRule stackingRule = itemStack.getStackingRule();
|
||||
for (int i = 0; i < inventory.getInnerSize(); i++) {
|
||||
ItemStack inventoryItem = inventory.getItemStack(i);
|
||||
if (inventoryItem.isAir()) {
|
||||
continue;
|
||||
}
|
||||
if (stackingRule.canBeStacked(itemStack, inventoryItem)) {
|
||||
final int itemAmount = stackingRule.getAmount(inventoryItem);
|
||||
final int itemStackAmount = stackingRule.getAmount(itemStack);
|
||||
if (itemStackAmount < itemAmount) {
|
||||
itemChangesMap.put(i, stackingRule.apply(inventoryItem, itemAmount - itemStackAmount));
|
||||
itemStack = stackingRule.apply(itemStack, 0);
|
||||
break;
|
||||
}
|
||||
itemChangesMap.put(i, stackingRule.apply(inventoryItem, 0));
|
||||
itemStack = stackingRule.apply(itemStack, itemStackAmount - itemAmount);
|
||||
if (stackingRule.getAmount(itemStack) == 0) {
|
||||
itemStack = stackingRule.apply(itemStack, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Pair.of(itemStack, itemChangesMap);
|
||||
};
|
||||
|
||||
@NotNull Pair<ItemStack, Map<Integer, ItemStack>> process(@NotNull AbstractInventory inventory,
|
||||
@NotNull ItemStack itemStack);
|
||||
|
||||
}
|
@ -91,29 +91,22 @@ public class InventoryClickProcessor {
|
||||
ItemStack resultClicked;
|
||||
|
||||
if (clickedRule.canBeStacked(clicked, cursor)) {
|
||||
final int amount = clicked.getAmount() + 1;
|
||||
final int amount = clickedRule.getAmount(clicked) + 1;
|
||||
if (!clickedRule.canApply(clicked, amount)) {
|
||||
return clickResult;
|
||||
} else {
|
||||
resultCursor = cursorRule.apply(cursor, cursorRule.getAmount(cursor) - 1);
|
||||
resultCursor = cursorRule.apply(cursor, operand -> operand - 1);
|
||||
resultClicked = clickedRule.apply(clicked, amount);
|
||||
}
|
||||
} else {
|
||||
if (cursor.isAir()) {
|
||||
final int amount = (int) Math.ceil((double) clicked.getAmount() / 2d);
|
||||
resultCursor = clicked.clone();
|
||||
resultCursor = cursorRule.apply(resultCursor, amount);
|
||||
|
||||
resultClicked = clicked.clone();
|
||||
resultClicked = clickedRule.apply(resultClicked, clicked.getAmount() / 2);
|
||||
final int amount = (int) Math.ceil((double) clickedRule.getAmount(clicked) / 2d);
|
||||
resultCursor = cursorRule.apply(clicked, amount);
|
||||
resultClicked = clickedRule.apply(clicked, operand -> operand / 2);
|
||||
} else {
|
||||
if (clicked.isAir()) {
|
||||
final int amount = cursor.getAmount();
|
||||
resultCursor = cursor.clone();
|
||||
resultCursor = cursorRule.apply(resultCursor, amount - 1);
|
||||
|
||||
resultClicked = cursor.clone();
|
||||
resultClicked = clickedRule.apply(resultClicked, 1);
|
||||
resultCursor = cursorRule.apply(cursor, operand -> operand - 1);
|
||||
resultClicked = clickedRule.apply(cursor, 1);
|
||||
} else {
|
||||
resultCursor = clicked;
|
||||
resultClicked = cursor;
|
||||
@ -155,11 +148,11 @@ public class InventoryClickProcessor {
|
||||
if (clicked.isAir()) {
|
||||
// Set held item [key] to slot
|
||||
resultClicked = cursor;
|
||||
resultHeld = ItemStack.getAirItem();
|
||||
resultHeld = ItemStack.AIR;
|
||||
} else {
|
||||
if (cursor.isAir()) {
|
||||
// if held item [key] is air then set clicked to held
|
||||
resultClicked = ItemStack.getAirItem();
|
||||
resultClicked = ItemStack.AIR;
|
||||
} else {
|
||||
// Otherwise replace held item and held
|
||||
resultClicked = cursor;
|
||||
@ -189,7 +182,7 @@ public class InventoryClickProcessor {
|
||||
final StackingRule clickedRule = clicked.getStackingRule();
|
||||
|
||||
boolean filled = false;
|
||||
ItemStack resultClicked = clicked.clone();
|
||||
ItemStack resultClicked = clicked;
|
||||
|
||||
for (InventoryClickLoopHandler loopHandler : loopHandlers) {
|
||||
final Int2IntFunction indexModifier = loopHandler.getIndexModifier();
|
||||
@ -242,7 +235,7 @@ public class InventoryClickProcessor {
|
||||
|
||||
// Switch
|
||||
itemSetter.accept(index, resultClicked);
|
||||
itemSetter.accept(slot, ItemStack.getAirItem());
|
||||
itemSetter.accept(slot, ItemStack.AIR);
|
||||
filled = true;
|
||||
break;
|
||||
}
|
||||
@ -287,16 +280,16 @@ public class InventoryClickProcessor {
|
||||
int finalCursorAmount = cursorAmount;
|
||||
|
||||
for (int s : slots) {
|
||||
final ItemStack draggedItem = cursor.clone();
|
||||
ItemStack slotItem = itemGetter.apply(s);
|
||||
|
||||
clickResult = startCondition(inventory, player, s, ClickType.DRAGGING, slotItem, cursor);
|
||||
if (clickResult.isCancel())
|
||||
break;
|
||||
StackingRule slotItemRule = slotItem.getStackingRule();
|
||||
|
||||
final int maxSize = stackingRule.getMaxSize();
|
||||
if (stackingRule.canBeStacked(draggedItem, slotItem)) {
|
||||
final int amount = slotItem.getAmount() + slotSize;
|
||||
if (stackingRule.canBeStacked(cursor, slotItem)) {
|
||||
final int amount = slotItemRule.getAmount(slotItem) + slotSize;
|
||||
if (stackingRule.canApply(slotItem, amount)) {
|
||||
slotItem = stackingRule.apply(slotItem, amount);
|
||||
finalCursorAmount -= slotSize;
|
||||
@ -306,7 +299,7 @@ public class InventoryClickProcessor {
|
||||
finalCursorAmount -= removedAmount;
|
||||
}
|
||||
} else if (slotItem.isAir()) {
|
||||
slotItem = stackingRule.apply(draggedItem, slotSize);
|
||||
slotItem = stackingRule.apply(cursor, slotSize);
|
||||
finalCursorAmount -= slotSize;
|
||||
}
|
||||
itemSetter.accept(s, slotItem);
|
||||
@ -327,15 +320,16 @@ public class InventoryClickProcessor {
|
||||
if (size > cursorAmount)
|
||||
return null;
|
||||
for (int s : slots) {
|
||||
ItemStack draggedItem = cursor.clone();
|
||||
ItemStack draggedItem = cursor;
|
||||
ItemStack slotItem = itemGetter.apply(s);
|
||||
|
||||
clickResult = startCondition(inventory, player, s, ClickType.DRAGGING, slotItem, cursor);
|
||||
if (clickResult.isCancel())
|
||||
break;
|
||||
|
||||
StackingRule slotItemRule = slotItem.getStackingRule();
|
||||
if (stackingRule.canBeStacked(draggedItem, slotItem)) {
|
||||
final int amount = slotItem.getAmount() + 1;
|
||||
final int amount = slotItemRule.getAmount(slotItem) + 1;
|
||||
if (stackingRule.canApply(slotItem, amount)) {
|
||||
slotItem = stackingRule.apply(slotItem, amount);
|
||||
itemSetter.accept(s, slotItem);
|
||||
@ -377,7 +371,7 @@ public class InventoryClickProcessor {
|
||||
@Nullable
|
||||
public InventoryClickResult doubleClick(@Nullable Inventory inventory, @NotNull Player player, int slot,
|
||||
@NotNull ItemStack cursor, @NotNull InventoryClickLoopHandler... loopHandlers) {
|
||||
InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.START_DOUBLE_CLICK, ItemStack.getAirItem(), cursor);
|
||||
InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.START_DOUBLE_CLICK, ItemStack.AIR, cursor);
|
||||
|
||||
if (clickResult.isCancel()) {
|
||||
return clickResult;
|
||||
@ -446,8 +440,8 @@ public class InventoryClickProcessor {
|
||||
final StackingRule clickedRule = clicked.getStackingRule();
|
||||
final StackingRule cursorRule = cursor.getStackingRule();
|
||||
|
||||
ItemStack resultClicked = clicked.clone();
|
||||
ItemStack resultCursor = cursor.clone();
|
||||
ItemStack resultClicked = clicked;
|
||||
ItemStack resultCursor = cursor;
|
||||
|
||||
|
||||
if (slot == -999) {
|
||||
@ -455,7 +449,7 @@ public class InventoryClickProcessor {
|
||||
if (button == 0) {
|
||||
// Left (drop all)
|
||||
final int amount = cursorRule.getAmount(resultCursor);
|
||||
final ItemStack dropItem = cursorRule.apply(resultCursor.clone(), amount);
|
||||
final ItemStack dropItem = cursorRule.apply(resultCursor, amount);
|
||||
final boolean dropResult = player.dropItem(dropItem);
|
||||
clickResult.setCancel(!dropResult);
|
||||
if (dropResult) {
|
||||
@ -463,7 +457,7 @@ public class InventoryClickProcessor {
|
||||
}
|
||||
} else if (button == 1) {
|
||||
// Right (drop 1)
|
||||
final ItemStack dropItem = cursorRule.apply(resultCursor.clone(), 1);
|
||||
final ItemStack dropItem = cursorRule.apply(resultCursor, 1);
|
||||
final boolean dropResult = player.dropItem(dropItem);
|
||||
clickResult.setCancel(!dropResult);
|
||||
if (dropResult) {
|
||||
@ -476,7 +470,7 @@ public class InventoryClickProcessor {
|
||||
} else if (mode == 4) {
|
||||
if (button == 0) {
|
||||
// Drop key Q (drop 1)
|
||||
final ItemStack dropItem = cursorRule.apply(resultClicked.clone(), 1);
|
||||
final ItemStack dropItem = cursorRule.apply(resultClicked, 1);
|
||||
final boolean dropResult = player.dropItem(dropItem);
|
||||
clickResult.setCancel(!dropResult);
|
||||
if (dropResult) {
|
||||
@ -487,7 +481,7 @@ public class InventoryClickProcessor {
|
||||
} else if (button == 1) {
|
||||
// Ctrl + Drop key Q (drop all)
|
||||
final int amount = cursorRule.getAmount(resultClicked);
|
||||
final ItemStack dropItem = clickedRule.apply(resultClicked.clone(), amount);
|
||||
final ItemStack dropItem = clickedRule.apply(resultClicked, amount);
|
||||
final boolean dropResult = player.dropItem(dropItem);
|
||||
clickResult.setCancel(!dropResult);
|
||||
if (dropResult) {
|
||||
@ -517,7 +511,7 @@ public class InventoryClickProcessor {
|
||||
|
||||
// Call ItemStack#onInventoryClick
|
||||
{
|
||||
clickResult.getClicked().onInventoryClick(player, clickType, slot, isPlayerInventory);
|
||||
//clickResult.getClicked().onInventoryClick(player, clickType, slot, isPlayerInventory);
|
||||
}
|
||||
|
||||
// Reset the didCloseInventory field
|
||||
@ -578,6 +572,7 @@ public class InventoryClickProcessor {
|
||||
@NotNull ClickType clickType, @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
|
||||
InventoryClickEvent inventoryClickEvent = new InventoryClickEvent(inventory, player, slot, clickType, clicked, cursor);
|
||||
player.callEvent(InventoryClickEvent.class, inventoryClickEvent);
|
||||
System.out.println("click");
|
||||
}
|
||||
|
||||
public void clearCache(@NotNull Player player) {
|
||||
|
@ -1,11 +1,11 @@
|
||||
package net.minestom.server.inventory.condition;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.inventory.InventoryModifier;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.click.ClickType;
|
||||
|
||||
/**
|
||||
* Can be added to any {@link InventoryModifier}
|
||||
* Can be added to any {@link AbstractInventory}
|
||||
* using {@link net.minestom.server.inventory.Inventory#addInventoryCondition(InventoryCondition)}
|
||||
* or {@link net.minestom.server.inventory.PlayerInventory#addInventoryCondition(InventoryCondition)}
|
||||
* in order to listen to any issued clicks.
|
||||
|
@ -1,72 +0,0 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.chat.JsonMessage;
|
||||
|
||||
public class ItemDisplay {
|
||||
|
||||
private Component displayName;
|
||||
private Component[] lore;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #ItemDisplay(Component, Component[])}
|
||||
*/
|
||||
@Deprecated
|
||||
public ItemDisplay(JsonMessage displayName, JsonMessage[] lore) {
|
||||
this.displayName = displayName.asComponent();
|
||||
this.lore = new Component[lore.length];
|
||||
|
||||
for (int i = 0; i < lore.length; i++) {
|
||||
this.lore[i] = lore[i].asComponent();
|
||||
}
|
||||
}
|
||||
|
||||
public ItemDisplay(Component displayName, Component[] lore) {
|
||||
this.displayName = displayName;
|
||||
this.lore = lore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item display name.
|
||||
*
|
||||
* @return the item display name
|
||||
* @deprecated Use {@link #getDisplayName()}
|
||||
*/
|
||||
@Deprecated
|
||||
public JsonMessage getDisplayNameJson() {
|
||||
return JsonMessage.fromComponent(displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item lore.
|
||||
*
|
||||
* @return the item lore
|
||||
* @deprecated Use {@link #getLore()}
|
||||
*/
|
||||
@Deprecated
|
||||
public JsonMessage[] getLoreJson() {
|
||||
JsonMessage[] loreOld = new JsonMessage[lore.length];
|
||||
for (int i = 0; i < lore.length; i++) {
|
||||
loreOld[i] = JsonMessage.fromComponent(lore[i]);
|
||||
}
|
||||
return loreOld;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item display name.
|
||||
*
|
||||
* @return the item display name
|
||||
*/
|
||||
public Component getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item lore.
|
||||
*
|
||||
* @return the item lore
|
||||
*/
|
||||
public Component[] getLore() {
|
||||
return lore;
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
/**
|
||||
* Represents a flag which can be applied to an {@link ItemStack} using {@link ItemStack#addItemFlags(ItemFlag...)}.
|
||||
* Represents a hide flag which can be applied to an {@link ItemStack} using {@link ItemMetaBuilder#hideFlag(int)}.
|
||||
*/
|
||||
public enum ItemFlag {
|
||||
public enum ItemHideFlag {
|
||||
HIDE_ENCHANTS(1),
|
||||
HIDE_ATTRIBUTES(2),
|
||||
HIDE_UNBREAKABLE(4),
|
||||
@ -13,7 +13,7 @@ public enum ItemFlag {
|
||||
|
||||
private final int bitFieldPart;
|
||||
|
||||
ItemFlag(int bit) {
|
||||
ItemHideFlag(int bit) {
|
||||
this.bitFieldPart = bit;
|
||||
}
|
||||
|
165
src/main/java/net/minestom/server/item/ItemMeta.java
Normal file
165
src/main/java/net/minestom/server/item/ItemMeta.java
Normal file
@ -0,0 +1,165 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.attribute.ItemAttribute;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import net.minestom.server.utils.binary.Writeable;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ItemMeta implements Writeable {
|
||||
|
||||
private final int damage;
|
||||
private final boolean unbreakable;
|
||||
private final int hideFlag;
|
||||
private final Component displayName;
|
||||
private final List<Component> lore;
|
||||
|
||||
private final Map<Enchantment, Short> enchantmentMap;
|
||||
private final List<ItemAttribute> attributes;
|
||||
|
||||
private final int customModelData;
|
||||
|
||||
private final Set<Block> canDestroy;
|
||||
private final Set<Block> canPlaceOn;
|
||||
|
||||
private final NBTCompound nbt;
|
||||
private final ItemMetaBuilder emptyBuilder;
|
||||
|
||||
private String cachedSNBT;
|
||||
private BinaryWriter cachedBuffer;
|
||||
|
||||
protected ItemMeta(@NotNull ItemMetaBuilder metaBuilder) {
|
||||
this.damage = metaBuilder.damage;
|
||||
this.unbreakable = metaBuilder.unbreakable;
|
||||
this.hideFlag = metaBuilder.hideFlag;
|
||||
this.displayName = metaBuilder.displayName;
|
||||
this.lore = new ArrayList<>(metaBuilder.lore);
|
||||
this.enchantmentMap = new HashMap<>(metaBuilder.enchantmentMap);
|
||||
this.attributes = new ArrayList<>(metaBuilder.attributes);
|
||||
this.customModelData = metaBuilder.customModelData;
|
||||
this.canDestroy = new HashSet<>(metaBuilder.canDestroy);
|
||||
this.canPlaceOn = new HashSet<>(metaBuilder.canPlaceOn);
|
||||
|
||||
this.nbt = metaBuilder.nbt;
|
||||
this.emptyBuilder = metaBuilder.getSupplier().get();
|
||||
}
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
public @NotNull ItemMeta with(@NotNull Consumer<@NotNull ItemMetaBuilder> builderConsumer) {
|
||||
var builder = builder();
|
||||
builderConsumer.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public int getDamage() {
|
||||
return damage;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public boolean isUnbreakable() {
|
||||
return unbreakable;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public int getHideFlag() {
|
||||
return hideFlag;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @Nullable Component getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull List<@NotNull Component> getLore() {
|
||||
return Collections.unmodifiableList(lore);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull Map<Enchantment, Short> getEnchantmentMap() {
|
||||
return Collections.unmodifiableMap(enchantmentMap);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull List<@NotNull ItemAttribute> getAttributes() {
|
||||
return Collections.unmodifiableList(attributes);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public int getCustomModelData() {
|
||||
return customModelData;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull Set<@NotNull Block> getCanDestroy() {
|
||||
return Collections.unmodifiableSet(canDestroy);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull Set<@NotNull Block> getCanPlaceOn() {
|
||||
return Collections.unmodifiableSet(canPlaceOn);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public <T> T getOrDefault(@NotNull ItemTag<T> tag, @Nullable T defaultValue) {
|
||||
var key = tag.getKey();
|
||||
if (nbt.containsKey(key)) {
|
||||
return tag.read(toNBT());
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public <T> @Nullable T get(@NotNull ItemTag<T> tag) {
|
||||
return tag.read(toNBT());
|
||||
}
|
||||
|
||||
public @NotNull NBTCompound toNBT() {
|
||||
return nbt.deepClone();
|
||||
}
|
||||
|
||||
public @NotNull String toSNBT() {
|
||||
if (cachedSNBT == null) {
|
||||
this.cachedSNBT = nbt.toSNBT();
|
||||
}
|
||||
return cachedSNBT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ItemMeta itemMeta = (ItemMeta) o;
|
||||
return nbt.equals(itemMeta.nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return nbt.hashCode();
|
||||
}
|
||||
|
||||
@Contract(value = "-> new", pure = true)
|
||||
protected @NotNull ItemMetaBuilder builder() {
|
||||
return ItemMetaBuilder.fromNBT(emptyBuilder, nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(@NotNull BinaryWriter writer) {
|
||||
if (cachedBuffer == null) {
|
||||
BinaryWriter w = new BinaryWriter();
|
||||
w.writeNBT("", nbt);
|
||||
this.cachedBuffer = w;
|
||||
}
|
||||
writer.write(cachedBuffer);
|
||||
cachedBuffer.getBuffer().resetReaderIndex();
|
||||
}
|
||||
}
|
271
src/main/java/net/minestom/server/item/ItemMetaBuilder.java
Normal file
271
src/main/java/net/minestom/server/item/ItemMetaBuilder.java
Normal file
@ -0,0 +1,271 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minestom.server.adventure.AdventureSerializer;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.attribute.ItemAttribute;
|
||||
import net.minestom.server.utils.NBTUtils;
|
||||
import net.minestom.server.utils.Utils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class ItemMetaBuilder {
|
||||
|
||||
protected NBTCompound nbt = new NBTCompound();
|
||||
|
||||
protected int damage;
|
||||
protected boolean unbreakable;
|
||||
protected int hideFlag;
|
||||
protected Component displayName;
|
||||
protected List<Component> lore = new ArrayList<>();
|
||||
protected Map<Enchantment, Short> enchantmentMap = new HashMap<>();
|
||||
protected List<ItemAttribute> attributes = new ArrayList<>();
|
||||
protected int customModelData;
|
||||
protected Set<Block> canDestroy = new HashSet<>();
|
||||
protected Set<Block> canPlaceOn = new HashSet<>();
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder damage(int damage) {
|
||||
this.damage = damage;
|
||||
this.nbt.setInt("Damage", damage);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder unbreakable(boolean unbreakable) {
|
||||
this.unbreakable = unbreakable;
|
||||
this.nbt.setByte("Unbreakable", (byte) (unbreakable ? 1 : 0));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder hideFlag(int hideFlag) {
|
||||
this.hideFlag = hideFlag;
|
||||
this.nbt.setInt("HideFlags", hideFlag);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder hideFlag(@NotNull ItemHideFlag... hideFlags) {
|
||||
int result = 0;
|
||||
for (ItemHideFlag hideFlag : hideFlags) {
|
||||
result |= hideFlag.getBitFieldPart();
|
||||
}
|
||||
return hideFlag(result);
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder displayName(@Nullable Component displayName) {
|
||||
this.displayName = displayName;
|
||||
handleCompound("display", nbtCompound -> {
|
||||
if (displayName != null) {
|
||||
final String name = AdventureSerializer.serialize(displayName);
|
||||
nbtCompound.setString("Name", name);
|
||||
} else {
|
||||
nbtCompound.removeTag("Name");
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder lore(@NotNull List<@NotNull Component> lore) {
|
||||
this.lore = lore;
|
||||
handleCompound("display", nbtCompound -> {
|
||||
final NBTList<NBTString> loreNBT = new NBTList<>(NBTTypes.TAG_String);
|
||||
for (Component line : lore) {
|
||||
loreNBT.add(new NBTString(GsonComponentSerializer.gson().serialize(line)));
|
||||
}
|
||||
nbtCompound.set("Lore", loreNBT);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder lore(Component... lore) {
|
||||
lore(Arrays.asList(lore));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder enchantments(@NotNull Map<Enchantment, Short> enchantments) {
|
||||
this.enchantmentMap = enchantments;
|
||||
handleMap(enchantmentMap, "Enchantments", nbt, () -> {
|
||||
NBTUtils.writeEnchant(nbt, "Enchantments", enchantmentMap);
|
||||
return nbt.get("Enchantments");
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_, _ -> this")
|
||||
public @NotNull ItemMetaBuilder enchantment(@NotNull Enchantment enchantment, short level) {
|
||||
this.enchantmentMap.put(enchantment, level);
|
||||
enchantments(enchantmentMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("-> this")
|
||||
public @NotNull ItemMetaBuilder clearEnchantment() {
|
||||
this.enchantmentMap.clear();
|
||||
enchantments(enchantmentMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder attributes(@NotNull List<@NotNull ItemAttribute> attributes) {
|
||||
this.attributes = attributes;
|
||||
|
||||
handleCollection(attributes, "AttributeModifiers", nbt, () -> {
|
||||
NBTList<NBTCompound> attributesNBT = new NBTList<>(NBTTypes.TAG_Compound);
|
||||
for (ItemAttribute itemAttribute : attributes) {
|
||||
final UUID uuid = itemAttribute.getUuid();
|
||||
attributesNBT.add(
|
||||
new NBTCompound()
|
||||
.setIntArray("UUID", Utils.uuidToIntArray(uuid))
|
||||
.setDouble("Amount", itemAttribute.getValue())
|
||||
.setString("Slot", itemAttribute.getSlot().name().toLowerCase())
|
||||
.setString("AttributeName", itemAttribute.getAttribute().getKey())
|
||||
.setInt("Operation", itemAttribute.getOperation().getId())
|
||||
.setString("Name", itemAttribute.getInternalName())
|
||||
);
|
||||
}
|
||||
return attributesNBT;
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder customModelData(int customModelData) {
|
||||
this.customModelData = customModelData;
|
||||
this.nbt.setInt("CustomModelData", customModelData);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder canPlaceOn(@NotNull Set<@NotNull Block> blocks) {
|
||||
this.canPlaceOn = blocks;
|
||||
handleCollection(canPlaceOn, "CanPlaceOn", nbt, () -> {
|
||||
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
|
||||
canPlaceOn.forEach(block -> list.add(new NBTString(block.getName())));
|
||||
nbt.set("CanPlaceOn", list);
|
||||
return list;
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder canPlaceOn(@NotNull Block... blocks) {
|
||||
return canPlaceOn(Set.of(blocks));
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder canDestroy(@NotNull Set<@NotNull Block> blocks) {
|
||||
this.canDestroy = blocks;
|
||||
handleCollection(canDestroy, "CanDestroy", nbt, () -> {
|
||||
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
|
||||
canDestroy.forEach(block -> list.add(new NBTString(block.getName())));
|
||||
nbt.set("CanDestroy", list);
|
||||
return list;
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder canDestroy(@NotNull Block... blocks) {
|
||||
return canDestroy(Set.of(blocks));
|
||||
}
|
||||
|
||||
public <T> @NotNull ItemMetaBuilder set(@NotNull ItemTag<T> tag, @Nullable T value) {
|
||||
if (value != null) {
|
||||
tag.write(nbt, value);
|
||||
} else {
|
||||
this.nbt.removeTag(tag.getKey());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("-> new")
|
||||
public abstract @NotNull ItemMeta build();
|
||||
|
||||
public abstract void read(@NotNull NBTCompound nbtCompound);
|
||||
|
||||
protected abstract @NotNull Supplier<@NotNull ItemMetaBuilder> getSupplier();
|
||||
|
||||
protected void handleCompound(@NotNull String key,
|
||||
@NotNull Consumer<@NotNull NBTCompound> consumer) {
|
||||
NBTCompound compound = null;
|
||||
boolean newNbt = false;
|
||||
if (nbt.containsKey(key)) {
|
||||
NBT dNbt = nbt.get(key);
|
||||
if (dNbt instanceof NBTCompound) {
|
||||
compound = (NBTCompound) dNbt;
|
||||
}
|
||||
} else {
|
||||
compound = new NBTCompound();
|
||||
newNbt = true;
|
||||
}
|
||||
|
||||
if (compound != null) {
|
||||
consumer.accept(compound);
|
||||
|
||||
if (newNbt && compound.getSize() > 0) {
|
||||
this.nbt.set(key, compound);
|
||||
} else if (!newNbt && compound.getSize() == 0) {
|
||||
this.nbt.removeTag(key);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleNullable(@Nullable Object value,
|
||||
@NotNull String key,
|
||||
@NotNull NBTCompound nbtCompound,
|
||||
@NotNull Supplier<@NotNull NBT> supplier) {
|
||||
if (value != null) {
|
||||
nbtCompound.set(key, supplier.get());
|
||||
} else {
|
||||
nbtCompound.removeTag(key);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleCollection(@NotNull Collection<?> objects,
|
||||
@NotNull String key,
|
||||
@NotNull NBTCompound nbtCompound,
|
||||
@NotNull Supplier<@NotNull NBT> supplier) {
|
||||
if (!objects.isEmpty()) {
|
||||
nbtCompound.set(key, supplier.get());
|
||||
} else {
|
||||
nbtCompound.removeTag(key);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleMap(@NotNull Map<?, ?> objects,
|
||||
@NotNull String key,
|
||||
@NotNull NBTCompound nbtCompound,
|
||||
@NotNull Supplier<@NotNull NBT> supplier) {
|
||||
if (!objects.isEmpty()) {
|
||||
nbtCompound.set(key, supplier.get());
|
||||
} else {
|
||||
nbtCompound.removeTag(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Contract(value = "_, _ -> new", pure = true)
|
||||
public static @NotNull ItemMetaBuilder fromNBT(@NotNull ItemMetaBuilder src, @NotNull NBTCompound nbtCompound) {
|
||||
ItemMetaBuilder dest = src.getSupplier().get();
|
||||
NBTUtils.loadDataIntoMeta(dest, nbtCompound);
|
||||
return dest;
|
||||
}
|
||||
|
||||
public interface Provider<T extends ItemMetaBuilder> {
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
122
src/main/java/net/minestom/server/item/ItemStackBuilder.java
Normal file
122
src/main/java/net/minestom/server/item/ItemStackBuilder.java
Normal file
@ -0,0 +1,122 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.item.metadata.*;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public class ItemStackBuilder {
|
||||
|
||||
private final Material material;
|
||||
private int amount;
|
||||
protected ItemMetaBuilder metaBuilder;
|
||||
|
||||
protected ItemStackBuilder(@NotNull Material material, @NotNull ItemMetaBuilder metaBuilder) {
|
||||
this.material = material;
|
||||
this.amount = 1;
|
||||
this.metaBuilder = metaBuilder;
|
||||
}
|
||||
|
||||
private static final Map<Material, Supplier<ItemMetaBuilder>> MATERIAL_SUPPLIER_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.POTION, PotionMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.LINGERING_POTION, PotionMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.SPLASH_POTION, PotionMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.TIPPED_ARROW, PotionMeta.Builder::new);
|
||||
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.FILLED_MAP, MapMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.COMPASS, CompassMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.ENCHANTED_BOOK, EnchantedBookMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.CROSSBOW, CrossbowMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.WRITABLE_BOOK, WritableBookMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.WRITTEN_BOOK, WrittenBookMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.FIREWORK_STAR, FireworkEffectMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.FIREWORK_ROCKET, FireworkMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.PLAYER_HEAD, PlayerHeadMeta.Builder::new);
|
||||
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.LEATHER_HELMET, LeatherArmorMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.LEATHER_CHESTPLATE, LeatherArmorMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.LEATHER_LEGGINGS, LeatherArmorMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.LEATHER_BOOTS, LeatherArmorMeta.Builder::new);
|
||||
}
|
||||
|
||||
protected ItemStackBuilder(@NotNull Material material) {
|
||||
this(material,
|
||||
MATERIAL_SUPPLIER_MAP.getOrDefault(material, DefaultMeta::new).get());
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public @NotNull ItemStackBuilder amount(int amount) {
|
||||
this.amount = amount;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public @NotNull ItemStackBuilder meta(@NotNull ItemMeta itemMeta) {
|
||||
this.metaBuilder = itemMeta.builder();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public @NotNull ItemStackBuilder meta(@NotNull UnaryOperator<@NotNull ItemMetaBuilder> itemMetaConsumer) {
|
||||
this.metaBuilder = itemMetaConsumer.apply(metaBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_, _ -> this")
|
||||
public <T extends ItemMetaBuilder, U extends ItemMetaBuilder.Provider<T>> @NotNull ItemStackBuilder meta(@NotNull Class<U> metaType, @NotNull Consumer<@NotNull T> itemMetaConsumer) {
|
||||
itemMetaConsumer.accept((T) metaBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public @NotNull ItemStackBuilder displayName(@Nullable Component displayName) {
|
||||
this.metaBuilder.displayName(displayName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public @NotNull ItemStackBuilder lore(@NotNull List<@NotNull Component> lore) {
|
||||
this.metaBuilder.lore(lore);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public @NotNull ItemStackBuilder lore(Component... lore) {
|
||||
this.metaBuilder.lore(lore);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "-> new", pure = true)
|
||||
public @NotNull ItemStack build() {
|
||||
return new ItemStack(material, amount, metaBuilder.build());
|
||||
}
|
||||
|
||||
private static final class DefaultMeta extends ItemMetaBuilder {
|
||||
@Override
|
||||
public @NotNull ItemMeta build() {
|
||||
return new ItemMeta(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
// Empty
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Supplier<@NotNull ItemMetaBuilder> getSupplier() {
|
||||
return DefaultMeta::new;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
97
src/main/java/net/minestom/server/item/ItemTag.java
Normal file
97
src/main/java/net/minestom/server/item/ItemTag.java
Normal file
@ -0,0 +1,97 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ItemTag<T> {
|
||||
|
||||
private final String key;
|
||||
private final Function<NBTCompound, T> readFunction;
|
||||
private final BiConsumer<NBTCompound, T> writeConsumer;
|
||||
|
||||
private ItemTag(@NotNull String key,
|
||||
@NotNull Function<NBTCompound, T> readFunction,
|
||||
@NotNull BiConsumer<NBTCompound, T> writeConsumer) {
|
||||
this.key = key;
|
||||
this.readFunction = readFunction;
|
||||
this.writeConsumer = writeConsumer;
|
||||
}
|
||||
|
||||
public @NotNull String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
protected T read(@NotNull NBTCompound nbtCompound) {
|
||||
return readFunction.apply(nbtCompound);
|
||||
}
|
||||
|
||||
protected void write(@NotNull NBTCompound nbtCompound, @NotNull T value) {
|
||||
this.writeConsumer.accept(nbtCompound, value);
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Byte> Byte(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getByte(key),
|
||||
(nbtCompound, value) -> nbtCompound.setByte(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Short> Short(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getShort(key),
|
||||
(nbtCompound, value) -> nbtCompound.setShort(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Integer> Integer(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getInt(key),
|
||||
(nbtCompound, integer) -> nbtCompound.setInt(key, integer));
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Long> Long(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getLong(key),
|
||||
(nbtCompound, value) -> nbtCompound.setLong(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Float> Float(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getFloat(key),
|
||||
(nbtCompound, value) -> nbtCompound.setFloat(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Double> Double(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getDouble(key),
|
||||
(nbtCompound, value) -> nbtCompound.setDouble(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<byte[]> ByteArray(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getByteArray(key),
|
||||
(nbtCompound, value) -> nbtCompound.setByteArray(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<String> String(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getString(key),
|
||||
(nbtCompound, value) -> nbtCompound.setString(key, value));
|
||||
}
|
||||
|
||||
// TODO List/Compound
|
||||
|
||||
public static @NotNull ItemTag<int[]> IntArray(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getIntArray(key),
|
||||
(nbtCompound, value) -> nbtCompound.setIntArray(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<long[]> LongArray(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getLongArray(key),
|
||||
(nbtCompound, value) -> nbtCompound.setLongArray(key, value));
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.IntUnaryOperator;
|
||||
|
||||
/**
|
||||
* Represents the stacking rule of an {@link ItemStack}.
|
||||
* This can be used to mimic the vanilla one (using the displayed item quantity)
|
||||
@ -40,10 +43,15 @@ public abstract class StackingRule {
|
||||
*
|
||||
* @param item the {@link ItemStack} to applies the size to
|
||||
* @param newAmount the new item size
|
||||
* @return the new {@link ItemStack} with the new amount
|
||||
* @return a new {@link ItemStack item} with the specified amount
|
||||
*/
|
||||
@NotNull
|
||||
public abstract ItemStack apply(@NotNull ItemStack item, int newAmount);
|
||||
@Contract("_, _ -> new")
|
||||
public abstract @NotNull ItemStack apply(@NotNull ItemStack item, int newAmount);
|
||||
|
||||
@Contract("_, _ -> new")
|
||||
public @NotNull ItemStack apply(@NotNull ItemStack item, @NotNull IntUnaryOperator amountOperator) {
|
||||
return apply(item, amountOperator.applyAsInt(getAmount(item)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine the current stack size of an {@link ItemStack}.
|
||||
|
@ -1,103 +1,107 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.utils.Position;
|
||||
import net.minestom.server.utils.clone.CloneUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CompassMeta extends ItemMeta {
|
||||
public class CompassMeta extends ItemMeta implements ItemMetaBuilder.Provider<CompassMeta.Builder> {
|
||||
|
||||
private boolean lodestoneTracked;
|
||||
private String lodestoneDimension;
|
||||
private final boolean lodestoneTracked;
|
||||
private final String lodestoneDimension;
|
||||
private final Position lodestonePosition;
|
||||
|
||||
private Position lodestonePosition;
|
||||
protected CompassMeta(ItemMetaBuilder metaBuilder,
|
||||
boolean lodestoneTracked,
|
||||
@Nullable String lodestoneDimension,
|
||||
@Nullable Position lodestonePosition) {
|
||||
super(metaBuilder);
|
||||
this.lodestoneTracked = lodestoneTracked;
|
||||
this.lodestoneDimension = lodestoneDimension;
|
||||
this.lodestonePosition = lodestonePosition;
|
||||
}
|
||||
|
||||
public boolean isLodestoneTracked() {
|
||||
return lodestoneTracked;
|
||||
}
|
||||
|
||||
public void setLodestoneTracked(boolean lodestoneTracked) {
|
||||
this.lodestoneTracked = lodestoneTracked;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLodestoneDimension() {
|
||||
public @Nullable String getLodestoneDimension() {
|
||||
return lodestoneDimension;
|
||||
}
|
||||
|
||||
public void setLodestoneDimension(@Nullable String lodestoneDimension) {
|
||||
this.lodestoneDimension = lodestoneDimension;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Position getLodestonePosition() {
|
||||
public @Nullable Position getLodestonePosition() {
|
||||
return lodestonePosition;
|
||||
}
|
||||
|
||||
public void setLodestonePosition(@Nullable Position lodestonePosition) {
|
||||
this.lodestonePosition = lodestonePosition;
|
||||
}
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return true;
|
||||
}
|
||||
private boolean lodestoneTracked;
|
||||
private String lodestoneDimension;
|
||||
private Position lodestonePosition;
|
||||
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
if (!(itemMeta instanceof CompassMeta))
|
||||
return false;
|
||||
CompassMeta compassMeta = (CompassMeta) itemMeta;
|
||||
return (compassMeta.lodestoneTracked == lodestoneTracked) &&
|
||||
(Objects.equals(compassMeta.lodestoneDimension, lodestoneDimension)) &&
|
||||
(Objects.equals(compassMeta.lodestonePosition, lodestonePosition));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.containsKey("LodestoneTracked")) {
|
||||
this.lodestoneTracked = compound.getByte("LodestoneTracked") == 1;
|
||||
}
|
||||
if (compound.containsKey("LodestoneDimension")) {
|
||||
this.lodestoneDimension = compound.getString("LodestoneDimension");
|
||||
}
|
||||
if (compound.containsKey("LodestonePos")) {
|
||||
final NBTCompound posCompound = compound.getCompound("LodestonePos");
|
||||
final int x = posCompound.getInt("X");
|
||||
final int y = posCompound.getInt("Y");
|
||||
final int z = posCompound.getInt("Z");
|
||||
|
||||
this.lodestonePosition = new Position(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
compound.setByte("LodestoneTracked", (byte) (lodestoneTracked ? 1 : 0));
|
||||
if (lodestoneDimension != null) {
|
||||
compound.setString("LodestoneDimension", lodestoneDimension);
|
||||
public Builder lodestoneTracked(boolean lodestoneTracked) {
|
||||
this.lodestoneTracked = lodestoneTracked;
|
||||
this.nbt.setByte("LodestoneTracked", (byte) (lodestoneTracked ? 1 : 0));
|
||||
return this;
|
||||
}
|
||||
|
||||
if (lodestonePosition != null) {
|
||||
NBTCompound posCompound = new NBTCompound();
|
||||
posCompound.setInt("X", (int) lodestonePosition.getX());
|
||||
posCompound.setInt("Y", (int) lodestonePosition.getY());
|
||||
posCompound.setInt("Z", (int) lodestonePosition.getZ());
|
||||
compound.set("LodestonePos", posCompound);
|
||||
public Builder lodestoneDimension(@Nullable String lodestoneDimension) {
|
||||
this.lodestoneDimension = lodestoneDimension;
|
||||
|
||||
if (lodestoneDimension != null) {
|
||||
this.nbt.setString("LodestoneDimension", lodestoneDimension);
|
||||
} else {
|
||||
this.nbt.removeTag("LodestoneDimension");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
CompassMeta compassMeta = (CompassMeta) super.clone();
|
||||
compassMeta.lodestoneTracked = lodestoneTracked;
|
||||
compassMeta.lodestoneDimension = lodestoneDimension;
|
||||
compassMeta.lodestonePosition = CloneUtils.optionalClone(lodestonePosition);
|
||||
public Builder lodestonePosition(@Nullable Position lodestonePosition) {
|
||||
this.lodestonePosition = lodestonePosition;
|
||||
|
||||
return compassMeta;
|
||||
if (lodestonePosition != null) {
|
||||
NBTCompound posCompound = new NBTCompound();
|
||||
posCompound.setInt("X", (int) lodestonePosition.getX());
|
||||
posCompound.setInt("Y", (int) lodestonePosition.getY());
|
||||
posCompound.setInt("Z", (int) lodestonePosition.getZ());
|
||||
this.nbt.set("LodestonePos", posCompound);
|
||||
} else {
|
||||
this.nbt.removeTag("LodestonePos");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompassMeta build() {
|
||||
return new CompassMeta(this, lodestoneTracked, lodestoneDimension, lodestonePosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey("LodestoneTracked")) {
|
||||
lodestoneTracked(nbtCompound.getByte("LodestoneTracked") == 1);
|
||||
}
|
||||
if (nbtCompound.containsKey("LodestoneDimension")) {
|
||||
lodestoneDimension(nbtCompound.getString("LodestoneDimension"));
|
||||
}
|
||||
if (nbtCompound.containsKey("LodestonePos")) {
|
||||
final NBTCompound posCompound = nbtCompound.getCompound("LodestonePos");
|
||||
final int x = posCompound.getInt("X");
|
||||
final int y = posCompound.getInt("Y");
|
||||
final int z = posCompound.getInt("Z");
|
||||
lodestonePosition(new Position(x, y, z));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||
return Builder::new;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,50 +1,37 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import net.minestom.server.utils.NBTUtils;
|
||||
import net.minestom.server.utils.clone.CloneUtils;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||
|
||||
public class CrossbowMeta extends ItemMeta {
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
private boolean triple;
|
||||
private ItemStack projectile1, projectile2, projectile3;
|
||||
public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider<SpawnEggMeta.Builder> {
|
||||
|
||||
private boolean charged;
|
||||
|
||||
/**
|
||||
* Sets the projectile of this crossbow.
|
||||
*
|
||||
* @param projectile the projectile of the crossbow
|
||||
*/
|
||||
public void setProjectile(@NotNull ItemStack projectile) {
|
||||
Check.argCondition(projectile.isAir(), "the projectile of your crossbow isn't visible");
|
||||
this.projectile1 = projectile;
|
||||
this.triple = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the triple projectiles of this crossbow.
|
||||
*
|
||||
* @param projectile1 the projectile 1
|
||||
* @param projectile2 the projectile 2
|
||||
* @param projectile3 the projectile 3
|
||||
*/
|
||||
public void setProjectiles(@NotNull ItemStack projectile1, @NotNull ItemStack projectile2, @NotNull ItemStack projectile3) {
|
||||
Check.argCondition(projectile1.isAir(), "the projectile1 of your crossbow isn't visible");
|
||||
Check.argCondition(projectile2.isAir(), "the projectile2 of your crossbow isn't visible");
|
||||
Check.argCondition(projectile3.isAir(), "the projectile3 of your crossbow isn't visible");
|
||||
private final boolean triple;
|
||||
private final ItemStack projectile1, projectile2, projectile3;
|
||||
private final boolean charged;
|
||||
|
||||
protected CrossbowMeta(@NotNull ItemMetaBuilder metaBuilder,
|
||||
boolean triple,
|
||||
ItemStack projectile1, ItemStack projectile2, ItemStack projectile3,
|
||||
boolean charged) {
|
||||
super(metaBuilder);
|
||||
this.triple = triple;
|
||||
this.projectile1 = projectile1;
|
||||
this.projectile2 = projectile2;
|
||||
this.projectile3 = projectile3;
|
||||
this.triple = true;
|
||||
this.charged = charged;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,27 +46,27 @@ public class CrossbowMeta extends ItemMeta {
|
||||
/**
|
||||
* Gets the first projectile.
|
||||
*
|
||||
* @return the first projectile, null if not present
|
||||
* @return the first projectile
|
||||
*/
|
||||
public ItemStack getProjectile1() {
|
||||
public @NotNull ItemStack getProjectile1() {
|
||||
return projectile1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the second projectile.
|
||||
*
|
||||
* @return the second projectile, null if not present
|
||||
* @return the second projectile
|
||||
*/
|
||||
public ItemStack getProjectile2() {
|
||||
public @NotNull ItemStack getProjectile2() {
|
||||
return projectile2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the third projectile.
|
||||
*
|
||||
* @return the third projectile, null if not present
|
||||
* @return the third projectile
|
||||
*/
|
||||
public ItemStack getProjectile3() {
|
||||
public @NotNull ItemStack getProjectile3() {
|
||||
return projectile3;
|
||||
}
|
||||
|
||||
@ -92,115 +79,112 @@ public class CrossbowMeta extends ItemMeta {
|
||||
return charged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the bow charged or uncharged.
|
||||
*
|
||||
* @param charged true to make the crossbow charged, false otherwise
|
||||
*/
|
||||
public void setCharged(boolean charged) {
|
||||
this.charged = charged;
|
||||
}
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return projectile1 != null && !projectile1.isAir();
|
||||
}
|
||||
private boolean triple;
|
||||
private ItemStack projectile1, projectile2, projectile3 = ItemStack.AIR;
|
||||
private boolean charged;
|
||||
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
if (!(itemMeta instanceof CrossbowMeta))
|
||||
return false;
|
||||
/**
|
||||
* Sets the projectile of this crossbow.
|
||||
*
|
||||
* @param projectile the projectile of the crossbow, air to remove
|
||||
*/
|
||||
public Builder projectile(@NotNull ItemStack projectile) {
|
||||
this.projectile1 = projectile;
|
||||
this.triple = false;
|
||||
|
||||
final CrossbowMeta crossbowMeta = (CrossbowMeta) itemMeta;
|
||||
final boolean checkCount = triple && crossbowMeta.triple;
|
||||
if (!checkCount)
|
||||
return false;
|
||||
NBTList<NBTCompound> chargedProjectiles = new NBTList<>(NBTTypes.TAG_Compound);
|
||||
if (!projectile.isAir()) {
|
||||
chargedProjectiles.add(getItemCompound(projectile));
|
||||
}
|
||||
this.nbt.set("ChargedProjectiles", chargedProjectiles);
|
||||
|
||||
if (projectile1.isSimilar(crossbowMeta.projectile1) &&
|
||||
projectile2.isSimilar(crossbowMeta.projectile2) &&
|
||||
projectile3.isSimilar(crossbowMeta.projectile3)) {
|
||||
return true;
|
||||
return this;
|
||||
}
|
||||
|
||||
return !triple && (projectile1.isSimilar(crossbowMeta.projectile1));
|
||||
}
|
||||
/**
|
||||
* Sets the triple projectiles of this crossbow.
|
||||
*
|
||||
* @param projectile1 the projectile 1
|
||||
* @param projectile2 the projectile 2
|
||||
* @param projectile3 the projectile 3
|
||||
*/
|
||||
public Builder projectiles(@NotNull ItemStack projectile1, @NotNull ItemStack projectile2, @NotNull ItemStack projectile3) {
|
||||
Check.argCondition(projectile1.isAir(), "the projectile1 of your crossbow isn't visible");
|
||||
Check.argCondition(projectile2.isAir(), "the projectile2 of your crossbow isn't visible");
|
||||
Check.argCondition(projectile3.isAir(), "the projectile3 of your crossbow isn't visible");
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.containsKey("ChargedProjectiles")) {
|
||||
final NBTList<NBTCompound> projectilesList = compound.getList("ChargedProjectiles");
|
||||
int index = 0;
|
||||
for (NBTCompound projectileCompound : projectilesList) {
|
||||
final byte count = projectileCompound.getByte("Count");
|
||||
final String id = projectileCompound.getString("id");
|
||||
final Material material = Registries.getMaterial(id);
|
||||
this.projectile1 = projectile1;
|
||||
this.projectile2 = projectile2;
|
||||
this.projectile3 = projectile3;
|
||||
this.triple = true;
|
||||
|
||||
final NBTCompound tagsCompound = projectileCompound.getCompound("tag");
|
||||
NBTList<NBTCompound> chargedProjectiles = new NBTList<>(NBTTypes.TAG_Compound);
|
||||
chargedProjectiles.add(getItemCompound(projectile1));
|
||||
chargedProjectiles.add(getItemCompound(projectile2));
|
||||
chargedProjectiles.add(getItemCompound(projectile3));
|
||||
this.nbt.set("ChargedProjectiles", chargedProjectiles);
|
||||
|
||||
ItemStack itemStack = new ItemStack(material, count);
|
||||
NBTUtils.loadDataIntoItem(itemStack, tagsCompound);
|
||||
return this;
|
||||
}
|
||||
|
||||
index++;
|
||||
/**
|
||||
* Makes the bow charged or uncharged.
|
||||
*
|
||||
* @param charged true to make the crossbow charged, false otherwise
|
||||
*/
|
||||
public Builder charged(boolean charged) {
|
||||
this.charged = charged;
|
||||
this.nbt.setByte("Charged", (byte) (charged ? 1 : 0));
|
||||
return this;
|
||||
}
|
||||
|
||||
if (index == 1) {
|
||||
projectile1 = itemStack;
|
||||
} else if (index == 2) {
|
||||
projectile2 = itemStack;
|
||||
} else if (index == 3) {
|
||||
projectile3 = itemStack;
|
||||
@Override
|
||||
public @NotNull CrossbowMeta build() {
|
||||
return new CrossbowMeta(this, triple, projectile1, projectile2, projectile3, charged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey("ChargedProjectiles")) {
|
||||
final NBTList<NBTCompound> projectilesList = nbtCompound.getList("ChargedProjectiles");
|
||||
List<ItemStack> projectiles = new ArrayList<>();
|
||||
for (NBTCompound projectileCompound : projectilesList) {
|
||||
final byte count = projectileCompound.getByte("Count");
|
||||
final String id = projectileCompound.getString("id");
|
||||
final Material material = Registries.getMaterial(id);
|
||||
|
||||
final NBTCompound tagsCompound = projectileCompound.getCompound("tag");
|
||||
|
||||
ItemStack itemStack = NBTUtils.loadItem(material, count, tagsCompound);
|
||||
|
||||
projectiles.add(itemStack);
|
||||
}
|
||||
|
||||
if (projectiles.size() == 1) {
|
||||
projectile(projectiles.get(0));
|
||||
} else if (projectiles.size() == 3) {
|
||||
projectiles(projectiles.get(0), projectiles.get(1), projectiles.get(2));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (compound.containsKey("Charged")) {
|
||||
this.charged = compound.getByte("Charged") == 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
if (projectile1 != null || projectile2 != null || projectile3 != null) {
|
||||
NBTList<NBTCompound> chargedProjectiles = new NBTList<>(NBTTypes.TAG_Compound);
|
||||
if (projectile1 != null) {
|
||||
chargedProjectiles.add(getItemCompound(projectile1));
|
||||
if (nbtCompound.containsKey("Charged")) {
|
||||
charged(nbtCompound.getByte("Charged") == 1);
|
||||
}
|
||||
if (projectile2 != null) {
|
||||
chargedProjectiles.add(getItemCompound(projectile2));
|
||||
}
|
||||
if (projectile3 != null) {
|
||||
chargedProjectiles.add(getItemCompound(projectile3));
|
||||
}
|
||||
compound.set("ChargedProjectiles", chargedProjectiles);
|
||||
}
|
||||
|
||||
if (charged) {
|
||||
compound.setByte("Charged", (byte) (charged ? 1 : 0));
|
||||
@Override
|
||||
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||
return Builder::new;
|
||||
}
|
||||
|
||||
private @NotNull NBTCompound getItemCompound(@NotNull ItemStack itemStack) {
|
||||
NBTCompound compound = itemStack.getMeta().toNBT();
|
||||
compound.setByte("Count", (byte) itemStack.getAmount());
|
||||
compound.setString("id", itemStack.getMaterial().getName());
|
||||
return compound;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
CrossbowMeta crossbowMeta = (CrossbowMeta) super.clone();
|
||||
crossbowMeta.triple = triple;
|
||||
crossbowMeta.projectile1 = CloneUtils.optionalClone(projectile1);
|
||||
crossbowMeta.projectile2 = CloneUtils.optionalClone(projectile2);
|
||||
crossbowMeta.projectile3 = CloneUtils.optionalClone(projectile3);
|
||||
|
||||
crossbowMeta.charged = charged;
|
||||
|
||||
return crossbowMeta;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private NBTCompound getItemCompound(@NotNull ItemStack itemStack) {
|
||||
NBTCompound compound = new NBTCompound();
|
||||
|
||||
compound.setByte("Count", itemStack.getAmount());
|
||||
compound.setString("id", itemStack.getMaterial().getName());
|
||||
NBTUtils.saveDataIntoNBT(itemStack, compound);
|
||||
|
||||
return compound;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +1,25 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ShortMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap;
|
||||
import net.minestom.server.item.Enchantment;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.utils.NBTUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class EnchantedBookMeta extends ItemMeta {
|
||||
public class EnchantedBookMeta extends ItemMeta implements ItemMetaBuilder.Provider<EnchantedBookMeta.Builder> {
|
||||
|
||||
private final Object2ShortMap<Enchantment> storedEnchantmentMap = new Object2ShortOpenHashMap<>();
|
||||
private final Map<Enchantment, Short> storedEnchantmentMap;
|
||||
|
||||
protected EnchantedBookMeta(@NotNull ItemMetaBuilder metaBuilder, Map<Enchantment, Short> storedEnchantmentMap) {
|
||||
super(metaBuilder);
|
||||
this.storedEnchantmentMap = new HashMap<>(storedEnchantmentMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stored enchantment map.
|
||||
@ -20,76 +27,41 @@ public class EnchantedBookMeta extends ItemMeta {
|
||||
*
|
||||
* @return an unmodifiable map containing the item stored enchantments
|
||||
*/
|
||||
@NotNull
|
||||
public Map<Enchantment, Short> getStoredEnchantmentMap() {
|
||||
public @NotNull Map<Enchantment, Short> getStoredEnchantmentMap() {
|
||||
return Collections.unmodifiableMap(storedEnchantmentMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a stored enchantment level.
|
||||
*
|
||||
* @param enchantment the enchantment type
|
||||
* @param level the enchantment level
|
||||
*/
|
||||
public void setStoredEnchantment(@NotNull Enchantment enchantment, short level) {
|
||||
if (level < 1) {
|
||||
removeStoredEnchantment(enchantment);
|
||||
return;
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private Map<Enchantment, Short> enchantments = new HashMap<>();
|
||||
|
||||
public @NotNull Builder enchantments(Map<Enchantment, Short> enchantments) {
|
||||
this.enchantments = enchantments;
|
||||
NBTUtils.writeEnchant(nbt, "StoredEnchantments", enchantments);
|
||||
return this;
|
||||
}
|
||||
|
||||
this.storedEnchantmentMap.put(enchantment, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a stored enchantment.
|
||||
*
|
||||
* @param enchantment the enchantment type
|
||||
*/
|
||||
public void removeStoredEnchantment(@NotNull Enchantment enchantment) {
|
||||
this.storedEnchantmentMap.removeShort(enchantment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a stored enchantment level.
|
||||
*
|
||||
* @param enchantment the enchantment type
|
||||
* @return the stored enchantment level, 0 if not present
|
||||
*/
|
||||
public int getStoredEnchantmentLevel(@NotNull Enchantment enchantment) {
|
||||
return this.storedEnchantmentMap.getOrDefault(enchantment, (short) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return !storedEnchantmentMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
return itemMeta instanceof EnchantedBookMeta &&
|
||||
((EnchantedBookMeta) itemMeta).storedEnchantmentMap.equals(storedEnchantmentMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.containsKey("StoredEnchantments")) {
|
||||
NBTUtils.loadEnchantments(compound.getList("StoredEnchantments"), this::setStoredEnchantment);
|
||||
public @NotNull Builder enchantment(Enchantment enchantment, short level) {
|
||||
this.enchantments.put(enchantment, level);
|
||||
enchantments(enchantments);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
if (!storedEnchantmentMap.isEmpty()) {
|
||||
NBTUtils.writeEnchant(compound, "StoredEnchantments", storedEnchantmentMap);
|
||||
@Override
|
||||
public @NotNull EnchantedBookMeta build() {
|
||||
return new EnchantedBookMeta(this, enchantments);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
EnchantedBookMeta enchantedBookMeta = (EnchantedBookMeta) super.clone();
|
||||
enchantedBookMeta.storedEnchantmentMap.putAll(storedEnchantmentMap);
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey("StoredEnchantments")) {
|
||||
NBTUtils.loadEnchantments(nbtCompound.getList("StoredEnchantments"), this::enchantment);
|
||||
}
|
||||
}
|
||||
|
||||
return enchantedBookMeta;
|
||||
@Override
|
||||
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||
return Builder::new;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,92 +1,52 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.firework.FireworkEffect;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
public class FireworkEffectMeta extends ItemMeta {
|
||||
import java.util.function.Supplier;
|
||||
|
||||
private FireworkEffect fireworkEffect;
|
||||
public class FireworkEffectMeta extends ItemMeta implements ItemMetaBuilder.Provider<FireworkEffectMeta.Builder> {
|
||||
|
||||
private final FireworkEffect fireworkEffect;
|
||||
|
||||
protected FireworkEffectMeta(@NotNull ItemMetaBuilder metaBuilder, FireworkEffect fireworkEffect) {
|
||||
super(metaBuilder);
|
||||
this.fireworkEffect = fireworkEffect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the firework effect for this meta.
|
||||
*
|
||||
* @return The current firework effect, or {@code null} if none.
|
||||
*/
|
||||
@Nullable
|
||||
public FireworkEffect getFireworkEffect() {
|
||||
return fireworkEffect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the {@link FireworkEffect} for this item meta.
|
||||
*
|
||||
* @param fireworkEffect The new firework effect, or {@code null}.
|
||||
*/
|
||||
public void setFireworkEffect(@Nullable FireworkEffect fireworkEffect) {
|
||||
this.fireworkEffect = fireworkEffect;
|
||||
}
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
/**
|
||||
* Whether if this item meta has an effect.
|
||||
*
|
||||
* @return {@code true} if this item meta has an effect, otherwise {@code false}.
|
||||
*/
|
||||
public boolean hasFireworkEffect() {
|
||||
return this.fireworkEffect != null;
|
||||
}
|
||||
private FireworkEffect fireworkEffect;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return this.hasFireworkEffect();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
if (!(itemMeta instanceof FireworkEffectMeta)) {
|
||||
return false;
|
||||
public Builder effect(@Nullable FireworkEffect fireworkEffect) {
|
||||
this.fireworkEffect = fireworkEffect;
|
||||
this.nbt.set("Explosion", this.fireworkEffect.asCompound());
|
||||
return this;
|
||||
}
|
||||
|
||||
FireworkEffectMeta fireworkEffectMeta = (FireworkEffectMeta) itemMeta;
|
||||
return fireworkEffectMeta.fireworkEffect == this.fireworkEffect;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.containsKey("Explosion")) {
|
||||
this.fireworkEffect = FireworkEffect.fromCompound(compound.getCompound("Explosion"));
|
||||
@Override
|
||||
public @NotNull FireworkEffectMeta build() {
|
||||
return new FireworkEffectMeta(this, fireworkEffect);
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey("Explosion")) {
|
||||
effect(FireworkEffect.fromCompound(nbtCompound.getCompound("Explosion")));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
if (this.fireworkEffect != null) {
|
||||
compound.set("Explosion", this.fireworkEffect.asCompound());
|
||||
@Override
|
||||
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||
return Builder::new;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
FireworkEffectMeta fireworkEffectMeta = (FireworkEffectMeta) super.clone();
|
||||
fireworkEffectMeta.fireworkEffect = this.fireworkEffect;
|
||||
return fireworkEffectMeta;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,179 +1,92 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.firework.FireworkEffect;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Represents a firework rocket meta data and its effects.
|
||||
*/
|
||||
public class FireworkMeta extends ItemMeta {
|
||||
public class FireworkMeta extends ItemMeta implements ItemMetaBuilder.Provider<FireworkMeta.Builder> {
|
||||
|
||||
private List<FireworkEffect> effects = new CopyOnWriteArrayList<>();
|
||||
private byte flightDuration;
|
||||
private final List<FireworkEffect> effects;
|
||||
private final byte flightDuration;
|
||||
|
||||
/**
|
||||
* Adds a firework effect to this firework.
|
||||
*
|
||||
* @param effect The firework effect to be added.
|
||||
*/
|
||||
public void addFireworkEffect(FireworkEffect effect) {
|
||||
this.effects.add(effect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an array of firework effects to this firework.
|
||||
*
|
||||
* @param effects An array of firework effects to be added.
|
||||
*/
|
||||
public void addFireworkEffects(FireworkEffect... effects) {
|
||||
this.effects.addAll(Arrays.asList(effects));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a firework effect from this firework.
|
||||
*
|
||||
* @param index The index of the firework effect to be removed.
|
||||
* @throws IndexOutOfBoundsException If index {@literal < 0 or index >} {@link #getEffectSize()}
|
||||
*/
|
||||
public void removeFireworkEffect(int index) throws IndexOutOfBoundsException {
|
||||
this.effects.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a firework effects from this firework.
|
||||
*
|
||||
* @param effect The effect to be removed.
|
||||
*/
|
||||
public void removeFireworkEffect(FireworkEffect effect) {
|
||||
this.effects.remove(effect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a collection with all effects in this firework.
|
||||
*
|
||||
* @return A collection with all effects in this firework.
|
||||
*/
|
||||
public List<FireworkEffect> getEffects() {
|
||||
return effects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the size of effects in this firework.
|
||||
*
|
||||
* @return The size of the effects.
|
||||
*/
|
||||
public int getEffectSize() {
|
||||
return this.effects.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all effects from this firework.
|
||||
*/
|
||||
public void clearEffects() {
|
||||
this.effects.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this firework has any effects.
|
||||
*
|
||||
* @return {@code true} if this firework has any effects, otherwise {@code false}.
|
||||
*/
|
||||
public boolean hasEffects() {
|
||||
return this.effects.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the flight duration of this firework.
|
||||
*
|
||||
* @param flightDuration The new flight duration for this firework.
|
||||
*/
|
||||
public void setFlightDuration(byte flightDuration) {
|
||||
protected FireworkMeta(@NotNull ItemMetaBuilder metaBuilder, List<FireworkEffect> effects,
|
||||
byte flightDuration) {
|
||||
super(metaBuilder);
|
||||
this.effects = new ArrayList<>(effects);
|
||||
this.flightDuration = flightDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flight duration of this firework
|
||||
* @return the flight duration of this firework
|
||||
*/
|
||||
public List<FireworkEffect> getEffects() {
|
||||
return Collections.unmodifiableList(effects);
|
||||
}
|
||||
|
||||
public byte getFlightDuration() {
|
||||
return flightDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return this.flightDuration == 0 || !this.effects.isEmpty();
|
||||
}
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
return false;
|
||||
}
|
||||
private List<FireworkEffect> effects = new CopyOnWriteArrayList<>();
|
||||
private byte flightDuration;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
this.effects.clear();
|
||||
if (compound.containsKey("Fireworks")) {
|
||||
NBTCompound fireworksCompound = compound.getCompound("Fireworks");
|
||||
public Builder effects(List<FireworkEffect> effects) {
|
||||
this.effects = effects;
|
||||
handleCompound("Fireworks", nbtCompound -> {
|
||||
NBTList<NBTCompound> explosions = new NBTList<>(NBTTypes.TAG_Compound);
|
||||
for (FireworkEffect effect : this.effects) {
|
||||
explosions.add(effect.asCompound());
|
||||
}
|
||||
nbtCompound.set("Explosions", explosions);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
if (fireworksCompound.containsKey("Flight")) {
|
||||
this.flightDuration = fireworksCompound.getAsByte("Flight");
|
||||
}
|
||||
public Builder flightDuration(byte flightDuration) {
|
||||
this.flightDuration = flightDuration;
|
||||
handleCompound("Fireworks", nbtCompound -> {
|
||||
nbtCompound.setByte("Flight", this.flightDuration);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
if (fireworksCompound.containsKey("Explosions")) {
|
||||
NBTList<NBTCompound> explosions = fireworksCompound.getList("Explosions");
|
||||
@Override
|
||||
public @NotNull FireworkMeta build() {
|
||||
return new FireworkMeta(this, effects, flightDuration);
|
||||
}
|
||||
|
||||
for (NBTCompound explosion : explosions) {
|
||||
this.effects.add(FireworkEffect.fromCompound(explosion));
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey("Fireworks")) {
|
||||
NBTCompound fireworksCompound = nbtCompound.getCompound("Fireworks");
|
||||
|
||||
if (fireworksCompound.containsKey("Flight")) {
|
||||
flightDuration(fireworksCompound.getAsByte("Flight"));
|
||||
}
|
||||
|
||||
if (fireworksCompound.containsKey("Explosions")) {
|
||||
NBTList<NBTCompound> explosions = fireworksCompound.getList("Explosions");
|
||||
|
||||
for (NBTCompound explosion : explosions) {
|
||||
this.effects.add(FireworkEffect.fromCompound(explosion));
|
||||
}
|
||||
effects(effects);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
NBTCompound fireworksCompound = new NBTCompound();
|
||||
fireworksCompound.setByte("Flight", this.flightDuration);
|
||||
|
||||
NBTList<NBTCompound> explosions = new NBTList<>(NBTTypes.TAG_Compound);
|
||||
for (FireworkEffect effect : this.effects) {
|
||||
explosions.add(effect.asCompound());
|
||||
}
|
||||
|
||||
fireworksCompound.set("Explosions", explosions);
|
||||
|
||||
compound.set("Fireworks", fireworksCompound);
|
||||
@Override
|
||||
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||
return Builder::new;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
FireworkMeta fireworkMeta = (FireworkMeta) super.clone();
|
||||
fireworkMeta.effects = this.effects;
|
||||
fireworkMeta.flightDuration = this.flightDuration;
|
||||
|
||||
return fireworkMeta;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.utils.clone.PublicCloneable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
/**
|
||||
* Represents nbt data only available for a type of item.
|
||||
*/
|
||||
public abstract class ItemMeta implements PublicCloneable<ItemMeta> {
|
||||
|
||||
/**
|
||||
* Gets if this meta object contains any useful data to send to the client.
|
||||
*
|
||||
* @return true if this item has nbt data, false otherwise
|
||||
*/
|
||||
public abstract boolean hasNbt();
|
||||
|
||||
/**
|
||||
* Gets if the two ItemMeta are similar.
|
||||
* <p>
|
||||
* It is used by {@link ItemStack#isSimilar(ItemStack)}.
|
||||
*
|
||||
* @param itemMeta the second item meta to check
|
||||
* @return true if the two meta are similar, false otherwise
|
||||
*/
|
||||
public abstract boolean isSimilar(@NotNull ItemMeta itemMeta);
|
||||
|
||||
/**
|
||||
* Reads nbt data from a compound.
|
||||
* <p>
|
||||
* WARNING: it is possible that it contains no useful data,
|
||||
* it has to be checked before getting anything.
|
||||
*
|
||||
* @param compound the compound containing the data
|
||||
*/
|
||||
public abstract void read(@NotNull NBTCompound compound);
|
||||
|
||||
/**
|
||||
* Writes nbt data to a compound.
|
||||
*
|
||||
* @param compound the compound receiving the item meta data
|
||||
*/
|
||||
public abstract void write(@NotNull NBTCompound compound);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
try {
|
||||
return (ItemMeta) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
throw new IllegalStateException("Weird thing happened");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,175 +1,61 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.chat.ChatColor;
|
||||
import net.minestom.server.color.Color;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
/**
|
||||
* Represents the item meta for leather armor parts.
|
||||
*/
|
||||
public class LeatherArmorMeta extends ItemMeta {
|
||||
private boolean modified;
|
||||
private Color color;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Sets the color of the leather armor piece.
|
||||
*
|
||||
* @param color the color of the leather armor
|
||||
* @deprecated Use {@link #setColor(Color)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setColor(ChatColor color) {
|
||||
this.setColor(color.asColor());
|
||||
}
|
||||
public class LeatherArmorMeta extends ItemMeta implements ItemMetaBuilder.Provider<LeatherArmorMeta.Builder> {
|
||||
|
||||
/**
|
||||
* Changes the color of the leather armor piece.
|
||||
*
|
||||
* @param red The red color of the leather armor piece.
|
||||
* @param green The green color of the leather armor piece.
|
||||
* @param blue The blue color of the leather armor piece.
|
||||
* @deprecated Use {@link #setColor(Color)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setColor(byte red, byte green, byte blue) {
|
||||
this.setColor(new Color(red, green, blue));
|
||||
}
|
||||
private final Color color;
|
||||
|
||||
/**
|
||||
* Sets the color of this leather armor piece.
|
||||
*
|
||||
* @param color the new color
|
||||
*/
|
||||
public void setColor(@NotNull Color color) {
|
||||
this.modified = !color.equals(this.color);
|
||||
protected LeatherArmorMeta(@NotNull ItemMetaBuilder metaBuilder, @Nullable Color color) {
|
||||
super(metaBuilder);
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color of this leather armor piece.
|
||||
*
|
||||
* @return the color
|
||||
*/
|
||||
public @NotNull Color getColor() {
|
||||
return this.color;
|
||||
public @Nullable Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the color to the default leather one.
|
||||
*/
|
||||
public void reset() {
|
||||
this.color = new Color(0, 0, 0);
|
||||
this.modified = false;
|
||||
}
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
/**
|
||||
* Gets the red component.
|
||||
*
|
||||
* @return the red component
|
||||
* @deprecated Use {@link #getColor}
|
||||
*/
|
||||
@Deprecated
|
||||
public int getRed() {
|
||||
return this.color.getRed();
|
||||
}
|
||||
private Color color;
|
||||
|
||||
/**
|
||||
* Gets the green component.
|
||||
*
|
||||
* @return the green component
|
||||
* @deprecated Use {@link #getColor}
|
||||
*/
|
||||
@Deprecated
|
||||
public int getGreen() {
|
||||
return this.color.getGreen();
|
||||
}
|
||||
public Builder color(@Nullable Color color) {
|
||||
this.color = color;
|
||||
handleCompound("display", nbtCompound -> {
|
||||
if (color != null) {
|
||||
nbtCompound.setInt("color", color.asRGB());
|
||||
} else {
|
||||
nbtCompound.removeTag("color");
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the blue component.
|
||||
*
|
||||
* @return the blue component
|
||||
* @deprecated Use {@link #getColor}
|
||||
*/
|
||||
@Deprecated
|
||||
public int getBlue() {
|
||||
return this.color.getBlue();
|
||||
}
|
||||
@Override
|
||||
public @NotNull LeatherArmorMeta build() {
|
||||
return new LeatherArmorMeta(this, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the color of this armor piece have been changed.
|
||||
*
|
||||
* @return true if the color has been changed, false otherwise
|
||||
*/
|
||||
public boolean isModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
if (!(itemMeta instanceof LeatherArmorMeta)) return false;
|
||||
final LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) itemMeta;
|
||||
return leatherArmorMeta.isModified() == isModified()
|
||||
&& leatherArmorMeta.getColor().equals(getColor());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.containsKey("display")) {
|
||||
final NBTCompound nbtCompound = compound.getCompound("display");
|
||||
if (nbtCompound.containsKey("color")) {
|
||||
final int color = nbtCompound.getInt("color");
|
||||
|
||||
// Sets the color of the leather armor piece
|
||||
// This also fixes that the armor pieces do not decolorize again when you are in creative
|
||||
// mode.
|
||||
this.setColor(new Color(color));
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey("display")) {
|
||||
final NBTCompound displayCompound = nbtCompound.getCompound("display");
|
||||
if (displayCompound.containsKey("color")) {
|
||||
color(new Color(displayCompound.getInt("color")));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
if (modified) {
|
||||
NBTCompound displayCompound;
|
||||
if (!compound.containsKey("display")) {
|
||||
displayCompound = new NBTCompound();
|
||||
} else {
|
||||
displayCompound = compound.getCompound("display");
|
||||
}
|
||||
displayCompound.setInt("color", color.asRGB());
|
||||
// Adds the color compound to the display compound
|
||||
compound.set("display", displayCompound);
|
||||
@Override
|
||||
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||
return Builder::new;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) super.clone();
|
||||
leatherArmorMeta.modified = this.isModified();
|
||||
leatherArmorMeta.color = color;
|
||||
|
||||
return leatherArmorMeta;
|
||||
}
|
||||
}
|
||||
|
@ -3,28 +3,36 @@ package net.minestom.server.item.metadata;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.chat.ChatColor;
|
||||
import net.minestom.server.color.Color;
|
||||
import net.minestom.server.utils.clone.CloneUtils;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.utils.clone.PublicCloneable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class MapMeta extends ItemMeta {
|
||||
|
||||
private int mapId;
|
||||
private int mapScaleDirection = 1;
|
||||
private List<MapDecoration> decorations = new CopyOnWriteArrayList<>();
|
||||
private Color mapColor = new Color(0, 0, 0);
|
||||
private final int mapId;
|
||||
private final int mapScaleDirection;
|
||||
private final List<MapDecoration> decorations;
|
||||
private final Color mapColor;
|
||||
|
||||
public MapMeta() {
|
||||
}
|
||||
|
||||
public MapMeta(int id) {
|
||||
this.mapId = id;
|
||||
protected MapMeta(ItemMetaBuilder metaBuilder,
|
||||
int mapId,
|
||||
int mapScaleDirection,
|
||||
@NotNull List<MapDecoration> decorations,
|
||||
@NotNull Color mapColor) {
|
||||
super(metaBuilder);
|
||||
this.mapId = mapId;
|
||||
this.mapScaleDirection = mapScaleDirection;
|
||||
this.decorations = decorations;
|
||||
this.mapColor = mapColor;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,15 +44,6 @@ public class MapMeta extends ItemMeta {
|
||||
return mapId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the map id.
|
||||
*
|
||||
* @param mapId the new map id
|
||||
*/
|
||||
public void setMapId(int mapId) {
|
||||
this.mapId = mapId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map scale direction.
|
||||
*
|
||||
@ -54,15 +53,6 @@ public class MapMeta extends ItemMeta {
|
||||
return mapScaleDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the map scale direction.
|
||||
*
|
||||
* @param mapScaleDirection the new map scale direction
|
||||
*/
|
||||
public void setMapScaleDirection(int mapScaleDirection) {
|
||||
this.mapScaleDirection = mapScaleDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map decorations.
|
||||
*
|
||||
@ -72,15 +62,6 @@ public class MapMeta extends ItemMeta {
|
||||
return decorations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the map decorations list.
|
||||
*
|
||||
* @param decorations the new map decorations list
|
||||
*/
|
||||
public void setDecorations(List<MapDecoration> decorations) {
|
||||
this.decorations = decorations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map color.
|
||||
*
|
||||
@ -101,94 +82,28 @@ public class MapMeta extends ItemMeta {
|
||||
return this.mapColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the map color.
|
||||
*
|
||||
* @param mapColor the new map color
|
||||
* @deprecated Use {@link #setMapColor(Color)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setMapColor(ChatColor mapColor) {
|
||||
this.setMapColor(mapColor.asColor());
|
||||
}
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
/**
|
||||
* Changes the map color.
|
||||
*
|
||||
* @param color the new map color
|
||||
*/
|
||||
public void setMapColor(@NotNull Color color) {
|
||||
this.mapColor = color;
|
||||
}
|
||||
private int mapId;
|
||||
private int mapScaleDirection = 1;
|
||||
private List<MapDecoration> decorations = new CopyOnWriteArrayList<>();
|
||||
private Color mapColor = new Color(0, 0, 0);
|
||||
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
if (!(itemMeta instanceof MapMeta))
|
||||
return false;
|
||||
|
||||
final MapMeta mapMeta = (MapMeta) itemMeta;
|
||||
return mapMeta.mapId == mapId &&
|
||||
mapMeta.mapScaleDirection == mapScaleDirection &&
|
||||
mapMeta.decorations.equals(decorations) &&
|
||||
mapMeta.mapColor == mapColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.containsKey("map")) {
|
||||
this.mapId = compound.getAsInt("map");
|
||||
public Builder mapId(int value) {
|
||||
this.mapId = value;
|
||||
this.nbt.setInt("map", mapId);
|
||||
return this;
|
||||
}
|
||||
|
||||
if (compound.containsKey("map_scale_direction")) {
|
||||
this.mapScaleDirection = compound.getAsInt("map_scale_direction");
|
||||
public Builder mapScaleDirection(int value) {
|
||||
this.mapScaleDirection = value;
|
||||
this.nbt.setInt("map_scale_direction", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
if (compound.containsKey("Decorations")) {
|
||||
final NBTList<NBTCompound> decorationsList = compound.getList("Decorations");
|
||||
for (NBTCompound decorationCompound : decorationsList) {
|
||||
final String id = decorationCompound.getString("id");
|
||||
final byte type = decorationCompound.getAsByte("type");
|
||||
byte x = 0;
|
||||
public Builder decorations(List<MapDecoration> value) {
|
||||
this.decorations = value;
|
||||
|
||||
if (decorationCompound.containsKey("x")) {
|
||||
x = decorationCompound.getAsByte("x");
|
||||
}
|
||||
|
||||
byte z = 0;
|
||||
if (decorationCompound.containsKey("z")) {
|
||||
z = decorationCompound.getAsByte("z");
|
||||
}
|
||||
|
||||
double rotation = 0.0;
|
||||
if (decorationCompound.containsKey("rot")) {
|
||||
rotation = decorationCompound.getAsDouble("rot");
|
||||
}
|
||||
|
||||
this.decorations.add(new MapDecoration(id, type, x, z, rotation));
|
||||
}
|
||||
}
|
||||
|
||||
if (compound.containsKey("display")) {
|
||||
final NBTCompound displayCompound = compound.getCompound("display");
|
||||
if (displayCompound.containsKey("MapColor")) {
|
||||
this.mapColor = new Color(displayCompound.getAsInt("MapColor"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
compound.setInt("map", mapId);
|
||||
|
||||
compound.setInt("map_scale_direction", mapScaleDirection);
|
||||
|
||||
if (!decorations.isEmpty()) {
|
||||
NBTList<NBTCompound> decorationsList = new NBTList<>(NBTTypes.TAG_Compound);
|
||||
for (MapDecoration decoration : decorations) {
|
||||
NBTCompound decorationCompound = new NBTCompound();
|
||||
@ -200,29 +115,80 @@ public class MapMeta extends ItemMeta {
|
||||
|
||||
decorationsList.add(decorationCompound);
|
||||
}
|
||||
compound.set("Decorations", decorationsList);
|
||||
this.nbt.set("Decorations", decorationsList);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
{
|
||||
public Builder mapColor(Color value) {
|
||||
this.mapColor = value;
|
||||
|
||||
NBTCompound displayCompound;
|
||||
if (compound.containsKey("display")) {
|
||||
displayCompound = compound.getCompound("display");
|
||||
if (nbt.containsKey("display")) {
|
||||
displayCompound = nbt.getCompound("display");
|
||||
} else {
|
||||
displayCompound = new NBTCompound();
|
||||
this.nbt.set("display", displayCompound);
|
||||
}
|
||||
displayCompound.setInt("MapColor", mapColor.asRGB());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
MapMeta mapMeta = (MapMeta) super.clone();
|
||||
mapMeta.setMapId(mapId);
|
||||
mapMeta.setMapScaleDirection(mapScaleDirection);
|
||||
mapMeta.decorations = CloneUtils.cloneCopyOnWriteArrayList(decorations);
|
||||
mapMeta.setMapColor(mapColor);
|
||||
return mapMeta;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemMeta build() {
|
||||
return new MapMeta(this, mapId, mapScaleDirection, decorations, mapColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.containsKey("map")) {
|
||||
mapId(compound.getAsInt("map"));
|
||||
}
|
||||
|
||||
if (compound.containsKey("map_scale_direction")) {
|
||||
mapScaleDirection(compound.getAsInt("map_scale_direction"));
|
||||
}
|
||||
|
||||
if (compound.containsKey("Decorations")) {
|
||||
final NBTList<NBTCompound> decorationsList = compound.getList("Decorations");
|
||||
List<MapDecoration> mapDecorations = new ArrayList<>();
|
||||
for (NBTCompound decorationCompound : decorationsList) {
|
||||
final String id = decorationCompound.getString("id");
|
||||
final byte type = decorationCompound.getAsByte("type");
|
||||
byte x = 0;
|
||||
|
||||
if (decorationCompound.containsKey("x")) {
|
||||
x = decorationCompound.getAsByte("x");
|
||||
}
|
||||
|
||||
byte z = 0;
|
||||
if (decorationCompound.containsKey("z")) {
|
||||
z = decorationCompound.getAsByte("z");
|
||||
}
|
||||
|
||||
double rotation = 0.0;
|
||||
if (decorationCompound.containsKey("rot")) {
|
||||
rotation = decorationCompound.getAsDouble("rot");
|
||||
}
|
||||
|
||||
mapDecorations.add(new MapDecoration(id, type, x, z, rotation));
|
||||
}
|
||||
decorations(mapDecorations);
|
||||
}
|
||||
|
||||
if (compound.containsKey("display")) {
|
||||
final NBTCompound displayCompound = compound.getCompound("display");
|
||||
if (displayCompound.containsKey("MapColor")) {
|
||||
mapColor(new Color(displayCompound.getAsInt("MapColor")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Supplier<@NotNull ItemMetaBuilder> getSupplier() {
|
||||
return Builder::new;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MapDecoration implements PublicCloneable<MapDecoration> {
|
||||
|
@ -1,7 +1,8 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.entity.PlayerSkin;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.utils.Utils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -10,152 +11,85 @@ import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Represents a skull that can have an owner.
|
||||
*/
|
||||
public class PlayerHeadMeta extends ItemMeta {
|
||||
public class PlayerHeadMeta extends ItemMeta implements ItemMetaBuilder.Provider<PlayerHeadMeta.Builder> {
|
||||
|
||||
private UUID skullOwner;
|
||||
private PlayerSkin playerSkin;
|
||||
private final UUID skullOwner;
|
||||
private final PlayerSkin playerSkin;
|
||||
|
||||
/**
|
||||
* Sets the owner of the skull.
|
||||
*
|
||||
* @param player The new owner of the skull.
|
||||
* @return {@code true} if the owner was successfully set, otherwise {@code false}.
|
||||
*/
|
||||
public boolean setOwningPlayer(@NotNull Player player) {
|
||||
if (player.getSkin() != null) {
|
||||
this.skullOwner = player.getUuid();
|
||||
this.playerSkin = player.getSkin();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
protected PlayerHeadMeta(@NotNull ItemMetaBuilder metaBuilder, UUID skullOwner,
|
||||
PlayerSkin playerSkin) {
|
||||
super(metaBuilder);
|
||||
this.skullOwner = skullOwner;
|
||||
this.playerSkin = playerSkin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the owner of the head.
|
||||
*
|
||||
* @return The head's owner.
|
||||
*/
|
||||
@Nullable
|
||||
public UUID getSkullOwner() {
|
||||
return skullOwner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the owner of the head.
|
||||
*
|
||||
* @param skullOwner The new head owner.
|
||||
*/
|
||||
public void setSkullOwner(@NotNull UUID skullOwner) {
|
||||
this.skullOwner = skullOwner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the skin of the head.
|
||||
*
|
||||
* @return The head's skin.
|
||||
*/
|
||||
@Nullable
|
||||
public PlayerSkin getPlayerSkin() {
|
||||
return playerSkin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the skin of the head.
|
||||
*
|
||||
* @param playerSkin The new skin for the head.
|
||||
*/
|
||||
public void setPlayerSkin(@NotNull PlayerSkin playerSkin) {
|
||||
this.playerSkin = playerSkin;
|
||||
}
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return this.skullOwner != null || playerSkin != null;
|
||||
}
|
||||
private UUID skullOwner;
|
||||
private PlayerSkin playerSkin;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
if (!(itemMeta instanceof PlayerHeadMeta))
|
||||
return false;
|
||||
final PlayerHeadMeta playerHeadMeta = (PlayerHeadMeta) itemMeta;
|
||||
return playerHeadMeta.playerSkin == playerSkin;
|
||||
}
|
||||
public Builder skullOwner(@Nullable UUID skullOwner) {
|
||||
this.skullOwner = skullOwner;
|
||||
handleCompound("SkullOwner", nbtCompound -> {
|
||||
nbtCompound.setIntArray("Id", Utils.uuidToIntArray(this.skullOwner));
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.containsKey("SkullOwner")) {
|
||||
NBTCompound skullOwnerCompound = compound.getCompound("SkullOwner");
|
||||
public Builder playerSkin(@Nullable PlayerSkin playerSkin) {
|
||||
this.playerSkin = playerSkin;
|
||||
handleCompound("SkullOwner", nbtCompound -> {
|
||||
NBTList<NBTCompound> textures = new NBTList<>(NBTTypes.TAG_Compound);
|
||||
String value = this.playerSkin.getTextures() == null ? "" : this.playerSkin.getTextures();
|
||||
String signature = this.playerSkin.getSignature() == null ? "" : this.playerSkin.getSignature();
|
||||
textures.add(new NBTCompound().setString("Value", value).setString("Signature", signature));
|
||||
nbtCompound.set("Properties", new NBTCompound().set("textures", textures));
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
if (skullOwnerCompound.containsKey("Id")) {
|
||||
this.skullOwner = Utils.intArrayToUuid(skullOwnerCompound.getIntArray("Id"));
|
||||
}
|
||||
@Override
|
||||
public @NotNull PlayerHeadMeta build() {
|
||||
return new PlayerHeadMeta(this, skullOwner, playerSkin);
|
||||
}
|
||||
|
||||
if (skullOwnerCompound.containsKey("Properties")) {
|
||||
NBTCompound propertyCompound = skullOwnerCompound.getCompound("Properties");
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey("SkullOwner")) {
|
||||
NBTCompound skullOwnerCompound = nbtCompound.getCompound("SkullOwner");
|
||||
|
||||
if (propertyCompound.containsKey("textures")) {
|
||||
NBTList<NBTCompound> textures = propertyCompound.getList("textures");
|
||||
if (textures != null) {
|
||||
NBTCompound nbt = textures.get(0);
|
||||
this.playerSkin = new PlayerSkin(nbt.getString("Value"), nbt.getString("Signature"));
|
||||
}
|
||||
if (skullOwnerCompound.containsKey("Id")) {
|
||||
skullOwner(Utils.intArrayToUuid(skullOwnerCompound.getIntArray("Id")));
|
||||
}
|
||||
|
||||
if (skullOwnerCompound.containsKey("Properties")) {
|
||||
NBTCompound propertyCompound = skullOwnerCompound.getCompound("Properties");
|
||||
|
||||
if (propertyCompound.containsKey("textures")) {
|
||||
NBTList<NBTCompound> textures = propertyCompound.getList("textures");
|
||||
if (textures != null) {
|
||||
NBTCompound nbt = textures.get(0);
|
||||
playerSkin(new PlayerSkin(nbt.getString("Value"), nbt.getString("Signature")));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
NBTCompound skullOwnerCompound = new NBTCompound();
|
||||
// Sets the identifier for the skull
|
||||
if (this.skullOwner != null)
|
||||
skullOwnerCompound.setIntArray("Id", Utils.uuidToIntArray(this.skullOwner));
|
||||
|
||||
if (this.playerSkin == null && this.skullOwner != null) {
|
||||
this.playerSkin = PlayerSkin.fromUuid(this.skullOwner.toString());
|
||||
}
|
||||
|
||||
if (this.playerSkin != null) {
|
||||
NBTList<NBTCompound> textures = new NBTList<>(NBTTypes.TAG_Compound);
|
||||
String value = this.playerSkin.getTextures() == null ? "" : this.playerSkin.getTextures();
|
||||
String signature = this.playerSkin.getSignature() == null ? "" : this.playerSkin.getSignature();
|
||||
textures.add(new NBTCompound().setString("Value", value).setString("Signature", signature));
|
||||
skullOwnerCompound.set("Properties", new NBTCompound().set("textures", textures));
|
||||
@Override
|
||||
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||
return Builder::new;
|
||||
}
|
||||
|
||||
compound.set("SkullOwner", skullOwnerCompound);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
PlayerHeadMeta playerHeadMeta = (PlayerHeadMeta) super.clone();
|
||||
playerHeadMeta.skullOwner = this.skullOwner;
|
||||
playerHeadMeta.playerSkin = this.playerSkin;
|
||||
return playerHeadMeta;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.chat.ChatColor;
|
||||
import net.minestom.server.color.Color;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.potion.CustomPotionEffect;
|
||||
import net.minestom.server.potion.PotionType;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import net.minestom.server.utils.clone.CloneUtils;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -13,124 +13,53 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Item meta for
|
||||
* {@link net.minestom.server.item.Material#POTION},
|
||||
* {@link net.minestom.server.item.Material#LINGERING_POTION},
|
||||
* {@link net.minestom.server.item.Material#SPLASH_POTION},
|
||||
* {@link net.minestom.server.item.Material#TIPPED_ARROW}.
|
||||
*/
|
||||
public class PotionMeta extends ItemMeta {
|
||||
public class PotionMeta extends ItemMeta implements ItemMetaBuilder.Provider<PotionMeta.Builder> {
|
||||
|
||||
private PotionType potionType;
|
||||
private final PotionType potionType;
|
||||
private final List<CustomPotionEffect> customPotionEffects;
|
||||
private final Color color;
|
||||
|
||||
// Not final because of #clone()
|
||||
private List<CustomPotionEffect> customPotionEffects = new CopyOnWriteArrayList<>();
|
||||
protected PotionMeta(@NotNull ItemMetaBuilder metaBuilder, @Nullable PotionType potionType,
|
||||
List<CustomPotionEffect> customPotionEffects,
|
||||
Color color) {
|
||||
super(metaBuilder);
|
||||
this.potionType = potionType;
|
||||
this.customPotionEffects = new ArrayList<>(customPotionEffects);
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
private Color color;
|
||||
|
||||
/**
|
||||
* Gets the potion type.
|
||||
*
|
||||
* @return the potion type
|
||||
*/
|
||||
@Nullable
|
||||
public PotionType getPotionType() {
|
||||
return potionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the potion type.
|
||||
*
|
||||
* @param potionType the new potion type
|
||||
*/
|
||||
public void setPotionType(@Nullable PotionType potionType) {
|
||||
this.potionType = potionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of {@link CustomPotionEffect}.
|
||||
*
|
||||
* @return the custom potion effect list
|
||||
*/
|
||||
@NotNull
|
||||
public List<CustomPotionEffect> getCustomPotionEffects() {
|
||||
return customPotionEffects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the color of the potion.
|
||||
*
|
||||
* @param color the new color of the potion
|
||||
* @deprecated Use {@link #setColor(Color)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setColor(ChatColor color) {
|
||||
this.setColor(color.asColor());
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the color of the potion.
|
||||
*
|
||||
* @param color the new color of the potion
|
||||
*/
|
||||
public void setColor(@Nullable Color color) {
|
||||
this.color = color;
|
||||
}
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return potionType != null ||
|
||||
!customPotionEffects.isEmpty();
|
||||
}
|
||||
private PotionType potionType;
|
||||
private List<CustomPotionEffect> customPotionEffects = new ArrayList<>();
|
||||
private Color color;
|
||||
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
if (!(itemMeta instanceof PotionMeta))
|
||||
return false;
|
||||
PotionMeta potionMeta = (PotionMeta) itemMeta;
|
||||
return potionMeta.potionType == potionType &&
|
||||
potionMeta.customPotionEffects.equals(customPotionEffects) &&
|
||||
potionMeta.color.equals(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.containsKey("Potion")) {
|
||||
this.potionType = Registries.getPotionType(compound.getString("Potion"));
|
||||
public Builder potionType(@NotNull PotionType potionType) {
|
||||
this.potionType = potionType;
|
||||
this.nbt.setString("Potion", potionType.getNamespaceID());
|
||||
return this;
|
||||
}
|
||||
|
||||
if (compound.containsKey("CustomPotionEffects")) {
|
||||
NBTList<NBTCompound> customEffectList = compound.getList("CustomPotionEffects");
|
||||
for (NBTCompound potionCompound : customEffectList) {
|
||||
final byte id = potionCompound.getAsByte("Id");
|
||||
final byte amplifier = potionCompound.getAsByte("Amplifier");
|
||||
final int duration = potionCompound.containsKey("Duration") ? potionCompound.getNumber("Duration").intValue() : (int) TimeUnit.SECOND.toMilliseconds(30);
|
||||
final boolean ambient = potionCompound.containsKey("Ambient") ? potionCompound.getAsByte("Ambient") == 1 : false;
|
||||
final boolean showParticles = potionCompound.containsKey("ShowParticles") ? potionCompound.getAsByte("ShowParticles") == 1 : true;
|
||||
final boolean showIcon = potionCompound.containsKey("ShowIcon") ? potionCompound.getAsByte("ShowIcon") == 1 : true;
|
||||
public Builder effects(@NotNull List<CustomPotionEffect> customPotionEffects) {
|
||||
this.customPotionEffects = customPotionEffects;
|
||||
|
||||
this.customPotionEffects.add(
|
||||
new CustomPotionEffect(id, amplifier, duration, ambient, showParticles, showIcon));
|
||||
}
|
||||
}
|
||||
|
||||
if (compound.containsKey("CustomPotionColor")) {
|
||||
this.color = new Color(compound.getInt("CustomPotionColor"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
if (potionType != null) {
|
||||
compound.setString("Potion", potionType.getNamespaceID());
|
||||
}
|
||||
if (!customPotionEffects.isEmpty()) {
|
||||
NBTList<NBTCompound> potionList = new NBTList<>(NBTTypes.TAG_Compound);
|
||||
|
||||
for (CustomPotionEffect customPotionEffect : customPotionEffects) {
|
||||
NBTCompound potionCompound = new NBTCompound();
|
||||
potionCompound.setByte("Id", customPotionEffect.getId());
|
||||
@ -142,25 +71,52 @@ public class PotionMeta extends ItemMeta {
|
||||
|
||||
potionList.add(potionCompound);
|
||||
}
|
||||
this.nbt.set("CustomPotionEffects", potionList);
|
||||
|
||||
compound.set("CustomPotionEffects", potionList);
|
||||
return this;
|
||||
}
|
||||
|
||||
if (color != null) {
|
||||
compound.setInt("CustomPotionColor", color.asRGB());
|
||||
public Builder color(@NotNull Color color) {
|
||||
this.color = color;
|
||||
this.nbt.setInt("CustomPotionColor", color.asRGB());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PotionMeta build() {
|
||||
return new PotionMeta(this, potionType, customPotionEffects, color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey("Potion")) {
|
||||
potionType(Registries.getPotionType(nbtCompound.getString("Potion")));
|
||||
}
|
||||
|
||||
if (nbtCompound.containsKey("CustomPotionEffects")) {
|
||||
NBTList<NBTCompound> customEffectList = nbtCompound.getList("CustomPotionEffects");
|
||||
for (NBTCompound potionCompound : customEffectList) {
|
||||
final byte id = potionCompound.getAsByte("Id");
|
||||
final byte amplifier = potionCompound.getAsByte("Amplifier");
|
||||
final int duration = potionCompound.containsKey("Duration") ? potionCompound.getNumber("Duration").intValue() : (int) TimeUnit.SECOND.toMilliseconds(30);
|
||||
final boolean ambient = potionCompound.containsKey("Ambient") ? potionCompound.getAsByte("Ambient") == 1 : false;
|
||||
final boolean showParticles = potionCompound.containsKey("ShowParticles") ? potionCompound.getAsByte("ShowParticles") == 1 : true;
|
||||
final boolean showIcon = potionCompound.containsKey("ShowIcon") ? potionCompound.getAsByte("ShowIcon") == 1 : true;
|
||||
|
||||
this.customPotionEffects.add(
|
||||
new CustomPotionEffect(id, amplifier, duration, ambient, showParticles, showIcon));
|
||||
}
|
||||
effects(customPotionEffects);
|
||||
}
|
||||
|
||||
if (nbtCompound.containsKey("CustomPotionColor")) {
|
||||
color(new Color(nbtCompound.getInt("CustomPotionColor")));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||
return Builder::new;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
PotionMeta potionMeta = (PotionMeta) super.clone();
|
||||
potionMeta.potionType = potionType;
|
||||
potionMeta.customPotionEffects = CloneUtils.cloneCopyOnWriteArrayList(customPotionEffects);
|
||||
|
||||
potionMeta.color = color;
|
||||
|
||||
return potionMeta;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +1,50 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
// TODO for which item
|
||||
public class SpawnEggMeta extends ItemMeta {
|
||||
import java.util.function.Supplier;
|
||||
|
||||
private EntityType entityType;
|
||||
public class SpawnEggMeta extends ItemMeta implements ItemMetaBuilder.Provider<SpawnEggMeta.Builder> {
|
||||
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return entityType != null;
|
||||
private final EntityType entityType;
|
||||
|
||||
protected SpawnEggMeta(@NotNull ItemMetaBuilder metaBuilder, @Nullable EntityType entityType) {
|
||||
super(metaBuilder);
|
||||
this.entityType = entityType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
if (!(itemMeta instanceof SpawnEggMeta))
|
||||
return false;
|
||||
final SpawnEggMeta spawnEggMeta = (SpawnEggMeta) itemMeta;
|
||||
return spawnEggMeta.entityType == entityType;
|
||||
public @Nullable EntityType getEntityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.containsKey("EntityTag")) {
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private EntityType entityType;
|
||||
|
||||
public Builder entityType(@Nullable EntityType entityType) {
|
||||
this.entityType = entityType;
|
||||
// TODO nbt
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull SpawnEggMeta build() {
|
||||
return new SpawnEggMeta(this, entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
if (!hasNbt())
|
||||
return;
|
||||
NBTCompound entityCompound = new NBTCompound();
|
||||
if (entityType != null) {
|
||||
entityCompound.setString("id", entityType.getNamespaceID());
|
||||
@Override
|
||||
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||
return Builder::new;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
SpawnEggMeta spawnEggMeta = (SpawnEggMeta) super.clone();
|
||||
spawnEggMeta.entityType = entityType;
|
||||
return spawnEggMeta;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@ package net.minestom.server.item.metadata;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minestom.server.adventure.AdventureSerializer;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
@ -11,110 +13,97 @@ import org.jglrxavpok.hephaistos.nbt.NBTString;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class WritableBookMeta extends ItemMeta {
|
||||
public class WritableBookMeta extends ItemMeta implements ItemMetaBuilder.Provider<WritableBookMeta.Builder> {
|
||||
|
||||
private String title;
|
||||
private String author;
|
||||
private List<Component> pages = new ArrayList<>();
|
||||
private final String author;
|
||||
private final String title;
|
||||
private final List<Component> pages;
|
||||
|
||||
@Nullable
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(@Nullable String title) {
|
||||
protected WritableBookMeta(@NotNull ItemMetaBuilder metaBuilder,
|
||||
@Nullable String author, @Nullable String title,
|
||||
@NotNull List<@NotNull Component> pages) {
|
||||
super(metaBuilder);
|
||||
this.author = author;
|
||||
this.title = title;
|
||||
this.pages = new ArrayList<>(pages);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getAuthor() {
|
||||
public @Nullable String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(@Nullable String author) {
|
||||
this.author = author;
|
||||
public @Nullable String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array list containing the book pages.
|
||||
* <p>
|
||||
* The list is modifiable.
|
||||
*
|
||||
* @return a modifiable {@link ArrayList} containing the book pages
|
||||
*/
|
||||
@NotNull
|
||||
public List<Component> getPages() {
|
||||
return pages;
|
||||
public @NotNull List<@NotNull Component> getPages() {
|
||||
return Collections.unmodifiableList(pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pages list of this book.
|
||||
*
|
||||
* @param pages the pages list
|
||||
*/
|
||||
public void setPages(@NotNull List<Component> pages) {
|
||||
this.pages = pages;
|
||||
}
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return !pages.isEmpty();
|
||||
}
|
||||
private String author;
|
||||
private String title;
|
||||
private List<Component> pages = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
if (!(itemMeta instanceof WritableBookMeta))
|
||||
return false;
|
||||
final WritableBookMeta writableBookMeta = (WritableBookMeta) itemMeta;
|
||||
return writableBookMeta.pages.equals(pages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
|
||||
if (compound.containsKey("title")) {
|
||||
this.title = compound.getString("title");
|
||||
public Builder author(@Nullable String author) {
|
||||
this.author = author;
|
||||
handleNullable(author, "author", nbt,
|
||||
() -> new NBTString(Objects.requireNonNull(author)));
|
||||
return this;
|
||||
}
|
||||
|
||||
if (compound.containsKey("author")) {
|
||||
this.author = compound.getString("author");
|
||||
public Builder title(@Nullable String title) {
|
||||
this.title = title;
|
||||
handleNullable(title, "title", nbt,
|
||||
() -> new NBTString(Objects.requireNonNull(title)));
|
||||
return this;
|
||||
}
|
||||
|
||||
if (compound.containsKey("pages")) {
|
||||
final NBTList<NBTString> list = compound.getList("pages");
|
||||
for (NBTString page : list) {
|
||||
this.pages.add(GsonComponentSerializer.gson().deserialize(page.getValue()));
|
||||
public Builder pages(@NotNull List<@NotNull Component> pages) {
|
||||
this.pages = pages;
|
||||
|
||||
handleCollection(pages, "pages", nbt, () -> {
|
||||
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
|
||||
for (Component page : pages) {
|
||||
list.add(new NBTString(AdventureSerializer.serialize(page)));
|
||||
}
|
||||
return list;
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull WritableBookMeta build() {
|
||||
return new WritableBookMeta(this, author, title, pages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey("author")) {
|
||||
author(nbtCompound.getString("author"));
|
||||
}
|
||||
if (nbtCompound.containsKey("title")) {
|
||||
title(nbtCompound.getString("title"));
|
||||
}
|
||||
if (nbtCompound.containsKey("pages")) {
|
||||
final NBTList<NBTString> list = nbtCompound.getList("pages");
|
||||
for (NBTString page : list) {
|
||||
this.pages.add(GsonComponentSerializer.gson().deserialize(page.getValue()));
|
||||
}
|
||||
pages(pages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
|
||||
if (title != null) {
|
||||
compound.setString("title", title);
|
||||
}
|
||||
|
||||
if (author != null) {
|
||||
compound.setString("author", author);
|
||||
}
|
||||
|
||||
if (!pages.isEmpty()) {
|
||||
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
|
||||
for (Component page : pages) {
|
||||
list.add(new NBTString(AdventureSerializer.serialize(page)));
|
||||
}
|
||||
compound.set("pages", list);
|
||||
@Override
|
||||
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||
return WritableBookMeta.Builder::new;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
WritableBookMeta writableBookMeta = (WritableBookMeta) super.clone();
|
||||
writableBookMeta.pages = new ArrayList<>(pages);
|
||||
return writableBookMeta;
|
||||
}
|
||||
}
|
||||
}
|
@ -5,192 +5,53 @@ import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minestom.server.adventure.AdventureSerializer;
|
||||
import net.minestom.server.adventure.Localizable;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTString;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class WrittenBookMeta extends ItemMeta {
|
||||
public class WrittenBookMeta extends ItemMeta implements ItemMetaBuilder.Provider<WrittenBookMeta.Builder> {
|
||||
|
||||
private boolean resolved;
|
||||
private WrittenBookGeneration generation;
|
||||
private String author;
|
||||
private String title;
|
||||
private List<Component> pages = new ArrayList<>();
|
||||
private final boolean resolved;
|
||||
private final WrittenBookGeneration generation;
|
||||
private final String author;
|
||||
private final String title;
|
||||
private final List<Component> pages;
|
||||
|
||||
protected WrittenBookMeta(@NotNull ItemMetaBuilder metaBuilder, boolean resolved,
|
||||
@Nullable WrittenBookGeneration generation,
|
||||
@Nullable String author, @Nullable String title,
|
||||
@NotNull List<@NotNull Component> pages) {
|
||||
super(metaBuilder);
|
||||
this.resolved = resolved;
|
||||
this.generation = generation;
|
||||
this.author = author;
|
||||
this.title = title;
|
||||
this.pages = new ArrayList<>(pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the book is resolved.
|
||||
*
|
||||
* @return true if the book is resolved, false otherwise
|
||||
*/
|
||||
public boolean isResolved() {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to true when the book (or a book from the stack)
|
||||
* is opened for the first time after being created.
|
||||
*
|
||||
* @param resolved true to make the book resolved, false otherwise
|
||||
*/
|
||||
public void setResolved(boolean resolved) {
|
||||
this.resolved = resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the copy tier of the book.
|
||||
*
|
||||
* @return the copy tier of the book
|
||||
*/
|
||||
public WrittenBookGeneration getGeneration() {
|
||||
public @Nullable WrittenBookGeneration getGeneration() {
|
||||
return generation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the copy tier of the book.
|
||||
*
|
||||
* @param generation the copy tier of the book
|
||||
*/
|
||||
public void setGeneration(WrittenBookGeneration generation) {
|
||||
this.generation = generation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the author of the book.
|
||||
*
|
||||
* @return the author of the book
|
||||
*/
|
||||
public String getAuthor() {
|
||||
public @Nullable String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the author of the book.
|
||||
*
|
||||
* @param author the author of the book
|
||||
*/
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the title of the book.
|
||||
*
|
||||
* @return the title of the book
|
||||
*/
|
||||
public String getTitle() {
|
||||
public @Nullable String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title of the book.
|
||||
*
|
||||
* @param title the title of the book
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link ArrayList} containing all the pages.
|
||||
* <p>
|
||||
* The list is not modifiable as it is .
|
||||
*
|
||||
* @return a modifiable {@link ArrayList} with the pages of the book
|
||||
*/
|
||||
@Deprecated
|
||||
public List<Component> getPagesJson() {
|
||||
return pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ArrayList} containing the book pages.
|
||||
*
|
||||
* @param pages the array list containing the book pages
|
||||
*/
|
||||
public void setPages(List<Component> pages) {
|
||||
this.pages = pages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNbt() {
|
||||
return resolved || generation != null ||
|
||||
author != null || title != null ||
|
||||
!pages.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemMeta itemMeta) {
|
||||
if (!(itemMeta instanceof WrittenBookMeta))
|
||||
return false;
|
||||
final WrittenBookMeta writtenBookMeta = (WrittenBookMeta) itemMeta;
|
||||
return writtenBookMeta.resolved == resolved &&
|
||||
writtenBookMeta.generation == generation &&
|
||||
writtenBookMeta.author.equals(author) &&
|
||||
writtenBookMeta.title.equals(title) &&
|
||||
writtenBookMeta.pages.equals(pages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.containsKey("resolved")) {
|
||||
this.resolved = compound.getByte("resolved") == 1;
|
||||
}
|
||||
if (compound.containsKey("generation")) {
|
||||
this.generation = WrittenBookGeneration.values()[compound.getInt("generation")];
|
||||
}
|
||||
if (compound.containsKey("author")) {
|
||||
this.author = compound.getString("author");
|
||||
}
|
||||
if (compound.containsKey("title")) {
|
||||
this.title = compound.getString("title");
|
||||
}
|
||||
if (compound.containsKey("pages")) {
|
||||
final NBTList<NBTString> list = compound.getList("pages");
|
||||
for (NBTString page : list) {
|
||||
this.pages.add(GsonComponentSerializer.gson().deserialize(page.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NBTCompound compound) {
|
||||
if (resolved) {
|
||||
compound.setByte("resolved", (byte) 1);
|
||||
}
|
||||
if (generation != null) {
|
||||
compound.setInt("generation", generation.ordinal());
|
||||
}
|
||||
if (author != null) {
|
||||
compound.setString("author", author);
|
||||
}
|
||||
if (title != null) {
|
||||
compound.setString("title", title);
|
||||
}
|
||||
if (!pages.isEmpty()) {
|
||||
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
|
||||
for (Component page : pages) {
|
||||
list.add(new NBTString(AdventureSerializer.serialize(page)));
|
||||
}
|
||||
compound.set("pages", list);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemMeta clone() {
|
||||
WrittenBookMeta writtenBookMeta = (WrittenBookMeta) super.clone();
|
||||
writtenBookMeta.resolved = resolved;
|
||||
writtenBookMeta.generation = generation;
|
||||
writtenBookMeta.author = author;
|
||||
writtenBookMeta.title = title;
|
||||
writtenBookMeta.pages.addAll(pages);
|
||||
|
||||
return writtenBookMeta;
|
||||
public @NotNull List<@NotNull Component> getPages() {
|
||||
return Collections.unmodifiableList(pages);
|
||||
}
|
||||
|
||||
public enum WrittenBookGeneration {
|
||||
@ -201,21 +62,104 @@ public class WrittenBookMeta extends ItemMeta {
|
||||
* Creates a written book meta from an Adventure book. This meta will not be
|
||||
* resolved and the generation will default to {@link WrittenBookGeneration#ORIGINAL}.
|
||||
*
|
||||
* @param book the book
|
||||
* @param book the book
|
||||
* @param localizable who the book is for
|
||||
*
|
||||
* @return the meta
|
||||
*/
|
||||
public static @NotNull WrittenBookMeta fromAdventure(@NotNull Book book, @NotNull Localizable localizable) {
|
||||
// make the book
|
||||
WrittenBookMeta meta = new WrittenBookMeta();
|
||||
meta.resolved = false;
|
||||
meta.generation = WrittenBookGeneration.ORIGINAL;
|
||||
meta.author = AdventureSerializer.translateAndSerialize(book.author(), localizable);
|
||||
meta.title = AdventureSerializer.translateAndSerialize(book.title(), localizable);
|
||||
meta.pages = new ArrayList<>();
|
||||
meta.pages.addAll(book.pages());
|
||||
return new Builder()
|
||||
.resolved(false)
|
||||
.generation(WrittenBookGeneration.ORIGINAL)
|
||||
.author(AdventureSerializer.translateAndSerialize(book.author(), localizable))
|
||||
.title(AdventureSerializer.translateAndSerialize(book.title(), localizable))
|
||||
.pages(book.pages())
|
||||
.build();
|
||||
}
|
||||
|
||||
return meta;
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private boolean resolved;
|
||||
private WrittenBookGeneration generation;
|
||||
private String author;
|
||||
private String title;
|
||||
private List<Component> pages = new ArrayList<>();
|
||||
|
||||
public Builder resolved(boolean resolved) {
|
||||
this.resolved = resolved;
|
||||
this.nbt.setByte("resolved", (byte) (resolved ? 1 : 0));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder generation(@Nullable WrittenBookGeneration generation) {
|
||||
this.generation = generation;
|
||||
handleNullable(generation, "generation", nbt,
|
||||
() -> new NBTInt(Objects.requireNonNull(generation).ordinal()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder author(@Nullable String author) {
|
||||
this.author = author;
|
||||
handleNullable(author, "author", nbt,
|
||||
() -> new NBTString(Objects.requireNonNull(author)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder title(@Nullable String title) {
|
||||
this.title = title;
|
||||
handleNullable(title, "title", nbt,
|
||||
() -> new NBTString(Objects.requireNonNull(title)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pages(@NotNull List<@NotNull Component> pages) {
|
||||
this.pages = pages;
|
||||
|
||||
handleCollection(pages, "pages", nbt, () -> {
|
||||
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
|
||||
for (Component page : pages) {
|
||||
list.add(new NBTString(AdventureSerializer.serialize(page)));
|
||||
}
|
||||
return list;
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pages(Component... pages) {
|
||||
return pages(Arrays.asList(pages));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull WrittenBookMeta build() {
|
||||
return new WrittenBookMeta(this, resolved, generation, author, title, pages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey("resolved")) {
|
||||
resolved(nbtCompound.getByte("resolved") == 1);
|
||||
}
|
||||
if (nbtCompound.containsKey("generation")) {
|
||||
generation(WrittenBookGeneration.values()[nbtCompound.getInt("generation")]);
|
||||
}
|
||||
if (nbtCompound.containsKey("author")) {
|
||||
author(nbtCompound.getString("author"));
|
||||
}
|
||||
if (nbtCompound.containsKey("title")) {
|
||||
title(nbtCompound.getString("title"));
|
||||
}
|
||||
if (nbtCompound.containsKey("pages")) {
|
||||
final NBTList<NBTString> list = nbtCompound.getList("pages");
|
||||
for (NBTString page : list) {
|
||||
this.pages.add(GsonComponentSerializer.gson().deserialize(page.getValue()));
|
||||
}
|
||||
pages(pages);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Supplier<ItemMetaBuilder> getSupplier() {
|
||||
return Builder::new;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,10 +25,9 @@ public class VanillaStackingRule extends StackingRule {
|
||||
@Override
|
||||
public ItemStack apply(@NotNull ItemStack item, int newAmount) {
|
||||
if (newAmount <= 0)
|
||||
return ItemStack.getAirItem();
|
||||
return ItemStack.AIR;
|
||||
|
||||
item.setAmount((byte) newAmount);
|
||||
return item;
|
||||
return item.withAmount(newAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -10,7 +10,7 @@ public class AnimationListener {
|
||||
public static void animationListener(ClientAnimationPacket packet, Player player) {
|
||||
final Player.Hand hand = packet.hand;
|
||||
final ItemStack itemStack = player.getItemInHand(hand);
|
||||
itemStack.onLeftClick(player, hand);
|
||||
//itemStack.onLeftClick(player, hand);
|
||||
PlayerHandAnimationEvent handAnimationEvent = new PlayerHandAnimationEvent(player, hand);
|
||||
player.callCancellableEvent(PlayerHandAnimationEvent.class, handAnimationEvent, () -> {
|
||||
switch (hand) {
|
||||
|
@ -52,7 +52,8 @@ public class BlockPlacementListener {
|
||||
final ItemStack usedItem = player.getItemInHand(hand);
|
||||
|
||||
// Interact at block
|
||||
final boolean cancel = usedItem.onUseOnBlock(player, hand, blockPosition, direction);
|
||||
// FIXME: onUseOnBlock
|
||||
final boolean cancel = false;//usedItem.onUseOnBlock(player, hand, blockPosition, direction);
|
||||
PlayerBlockInteractEvent playerBlockInteractEvent = new PlayerBlockInteractEvent(player, blockPosition, hand, blockFace);
|
||||
playerBlockInteractEvent.setCancelled(cancel);
|
||||
playerBlockInteractEvent.setBlockingItemUse(cancel);
|
||||
@ -85,7 +86,8 @@ public class BlockPlacementListener {
|
||||
canPlaceBlock = false; //Spectators can't place blocks
|
||||
} else if (player.getGameMode() == GameMode.ADVENTURE) {
|
||||
//Check if the block can placed on the block
|
||||
canPlaceBlock = usedItem.canPlaceOn(instance.getBlock(blockPosition).getName());
|
||||
Block placeBlock = instance.getBlock(blockPosition);
|
||||
canPlaceBlock = usedItem.getMeta().getCanPlaceOn().contains(placeBlock);
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,11 +168,8 @@ public class BlockPlacementListener {
|
||||
// Block consuming
|
||||
if (playerBlockPlaceEvent.doesConsumeBlock()) {
|
||||
// Consume the block in the player's hand
|
||||
final ItemStack newUsedItem = usedItem.consume(1);
|
||||
|
||||
if (newUsedItem != null) {
|
||||
playerInventory.setItemInHand(hand, newUsedItem);
|
||||
}
|
||||
final ItemStack newUsedItem = usedItem.getStackingRule().apply(usedItem, usedItem.getAmount() - 1);
|
||||
playerInventory.setItemInHand(hand, newUsedItem);
|
||||
}
|
||||
} else {
|
||||
refreshChunk = true;
|
||||
@ -196,8 +195,6 @@ public class BlockPlacementListener {
|
||||
if (refreshChunk) {
|
||||
chunk.sendChunkSectionUpdate(ChunkUtils.getSectionAt(blockPosition.getY()), player);
|
||||
}
|
||||
|
||||
player.getInventory().refreshSlot(player.getHeldSlot());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -47,7 +47,8 @@ public class PlayerDiggingListener {
|
||||
} else if (player.getGameMode() == GameMode.ADVENTURE) {
|
||||
//Check if the item can break the block with the current item
|
||||
ItemStack itemInMainHand = player.getItemInMainHand();
|
||||
if (!itemInMainHand.canDestroy(instance.getBlock(blockPosition).getName())) {
|
||||
Block destroyedBlock = instance.getBlock(blockPosition);
|
||||
if (!itemInMainHand.getMeta().getCanDestroy().contains(destroyedBlock)) {
|
||||
sendAcknowledgePacket(player, blockPosition, blockStateId,
|
||||
ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false);
|
||||
return;
|
||||
@ -111,7 +112,7 @@ public class PlayerDiggingListener {
|
||||
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM_STACK) {
|
||||
|
||||
final ItemStack droppedItemStack = player.getInventory().getItemInMainHand();
|
||||
dropItem(player, droppedItemStack, ItemStack.getAirItem());
|
||||
dropItem(player, droppedItemStack, ItemStack.AIR);
|
||||
|
||||
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM) {
|
||||
|
||||
@ -123,14 +124,11 @@ public class PlayerDiggingListener {
|
||||
|
||||
if (handAmount <= dropAmount) {
|
||||
// Drop the whole item without copy
|
||||
dropItem(player, handItem, ItemStack.getAirItem());
|
||||
dropItem(player, handItem, ItemStack.AIR);
|
||||
} else {
|
||||
// Drop a single item, need a copy
|
||||
ItemStack droppedItemStack2 = handItem.clone();
|
||||
ItemStack droppedItemStack2 = stackingRule.apply(handItem, dropAmount);
|
||||
|
||||
droppedItemStack2 = stackingRule.apply(droppedItemStack2, dropAmount);
|
||||
|
||||
handItem = handItem.clone(); // Force the copy
|
||||
handItem = stackingRule.apply(handItem, handAmount - dropAmount);
|
||||
|
||||
dropItem(player, droppedItemStack2, handItem);
|
||||
|
@ -15,7 +15,7 @@ public class UseItemListener {
|
||||
final PlayerInventory inventory = player.getInventory();
|
||||
final Player.Hand hand = packet.hand;
|
||||
ItemStack itemStack = hand == Player.Hand.MAIN ? inventory.getItemInMainHand() : inventory.getItemInOffHand();
|
||||
itemStack.onRightClick(player, hand);
|
||||
//itemStack.onRightClick(player, hand);
|
||||
PlayerUseItemEvent useItemEvent = new PlayerUseItemEvent(player, hand, itemStack);
|
||||
player.callEvent(PlayerUseItemEvent.class, useItemEvent);
|
||||
|
||||
|
@ -13,7 +13,7 @@ public class ClientClickWindowPacket extends ClientPlayPacket {
|
||||
public byte button;
|
||||
public short actionNumber;
|
||||
public int mode;
|
||||
public ItemStack item = ItemStack.getAirItem();
|
||||
public ItemStack item = ItemStack.AIR;
|
||||
|
||||
@Override
|
||||
public void read(@NotNull BinaryReader reader) {
|
||||
|
@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
public class ClientCreativeInventoryActionPacket extends ClientPlayPacket {
|
||||
|
||||
public short slot;
|
||||
public ItemStack item = ItemStack.getAirItem();
|
||||
public ItemStack item = ItemStack.AIR;
|
||||
|
||||
@Override
|
||||
public void read(@NotNull BinaryReader reader) {
|
||||
|
@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ClientEditBookPacket extends ClientPlayPacket {
|
||||
|
||||
public ItemStack book = ItemStack.getAirItem();
|
||||
public ItemStack book = ItemStack.AIR;
|
||||
public boolean isSigning;
|
||||
public Player.Hand hand = Player.Hand.MAIN;
|
||||
|
||||
|
@ -17,7 +17,7 @@ public class ClientNameItemPacket extends ClientPlayPacket {
|
||||
@Override
|
||||
public void write(@NotNull BinaryWriter writer) {
|
||||
if(itemName.length() > Short.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Item name cannot be longer than Short.MAX_VALUE characters!");
|
||||
throw new IllegalArgumentException("ItemStack name cannot be longer than Short.MAX_VALUE characters!");
|
||||
}
|
||||
writer.writeSizedString(itemName);
|
||||
}
|
||||
|
@ -25,7 +25,8 @@ public class AdvancementsPacket implements ComponentHoldingServerPacket {
|
||||
public String[] identifiersToRemove = new String[0];
|
||||
public ProgressMapping[] progressMappings = new ProgressMapping[0];
|
||||
|
||||
public AdvancementsPacket() {}
|
||||
public AdvancementsPacket() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull BinaryWriter writer) {
|
||||
@ -149,14 +150,14 @@ public class AdvancementsPacket implements ComponentHoldingServerPacket {
|
||||
@Override
|
||||
public void read(@NotNull BinaryReader reader) {
|
||||
boolean hasParent = reader.readBoolean();
|
||||
if(hasParent) {
|
||||
if (hasParent) {
|
||||
parentIdentifier = reader.readSizedString(Integer.MAX_VALUE);
|
||||
} else {
|
||||
parentIdentifier = null;
|
||||
}
|
||||
|
||||
boolean hasDisplay = reader.readBoolean();
|
||||
if(hasDisplay) {
|
||||
if (hasDisplay) {
|
||||
displayData = new DisplayData();
|
||||
displayData.read(reader);
|
||||
} else {
|
||||
@ -177,7 +178,7 @@ public class AdvancementsPacket implements ComponentHoldingServerPacket {
|
||||
public static class DisplayData implements Writeable, Readable {
|
||||
public Component title = Component.empty(); // Only text
|
||||
public Component description = Component.empty(); // Only text
|
||||
public ItemStack icon = ItemStack.getAirItem();
|
||||
public ItemStack icon = ItemStack.AIR;
|
||||
public FrameType frameType = FrameType.TASK;
|
||||
public int flags;
|
||||
public String backgroundTexture = "";
|
||||
@ -205,7 +206,7 @@ public class AdvancementsPacket implements ComponentHoldingServerPacket {
|
||||
icon = reader.readItemStack();
|
||||
frameType = FrameType.values()[reader.readVarInt()];
|
||||
flags = reader.readInt();
|
||||
if((flags & 0x1) != 0) {
|
||||
if ((flags & 0x1) != 0) {
|
||||
backgroundTexture = reader.readSizedString(Integer.MAX_VALUE);
|
||||
} else {
|
||||
backgroundTexture = null;
|
||||
@ -302,7 +303,7 @@ public class AdvancementsPacket implements ComponentHoldingServerPacket {
|
||||
@Override
|
||||
public void read(@NotNull BinaryReader reader) {
|
||||
achieved = reader.readBoolean();
|
||||
if(achieved) {
|
||||
if (achieved) {
|
||||
dateOfAchieving = reader.readLong();
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ public class DeclareRecipesPacket implements ServerPacket {
|
||||
|
||||
public DeclaredRecipe[] recipes = new DeclaredRecipe[0];
|
||||
|
||||
public DeclareRecipesPacket() {}
|
||||
public DeclareRecipesPacket() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull BinaryWriter writer) {
|
||||
@ -68,7 +69,7 @@ public class DeclareRecipesPacket implements ServerPacket {
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unrecognized type: "+type+" (id is "+id+")");
|
||||
throw new UnsupportedOperationException("Unrecognized type: " + type + " (id is " + id + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,7 +194,7 @@ public class DeclareRecipesPacket implements ServerPacket {
|
||||
width = reader.readVarInt();
|
||||
height = reader.readVarInt();
|
||||
group = reader.readSizedString(Integer.MAX_VALUE);
|
||||
ingredients = new Ingredient[width*height];
|
||||
ingredients = new Ingredient[width * height];
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
ingredients[i] = new Ingredient();
|
||||
ingredients[i].read(reader);
|
||||
@ -442,7 +443,7 @@ public class DeclareRecipesPacket implements ServerPacket {
|
||||
public void read(@NotNull BinaryReader reader) {
|
||||
group = reader.readSizedString(Integer.MAX_VALUE);
|
||||
ingredient = new Ingredient();
|
||||
ingredient.read( reader);
|
||||
ingredient.read(reader);
|
||||
result = reader.readItemStack();
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,8 @@ public class EntityEquipmentPacket implements ServerPacket {
|
||||
public Slot[] slots;
|
||||
public ItemStack[] itemStacks;
|
||||
|
||||
public EntityEquipmentPacket() {}
|
||||
public EntityEquipmentPacket() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull BinaryWriter writer) {
|
||||
@ -53,7 +54,7 @@ public class EntityEquipmentPacket implements ServerPacket {
|
||||
boolean hasRemaining = true;
|
||||
List<Slot> slots = new LinkedList<>();
|
||||
List<ItemStack> stacks = new LinkedList<>();
|
||||
while(hasRemaining) {
|
||||
while (hasRemaining) {
|
||||
byte slotEnum = reader.readByte();
|
||||
hasRemaining = (slotEnum & 0x80) == 0x80;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.minestom.server.network.packet.server.play;
|
||||
|
||||
import net.minestom.server.chat.ColoredText;
|
||||
import net.minestom.server.chat.JsonMessage;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.network.packet.server.ServerPacket;
|
||||
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
||||
import net.minestom.server.utils.binary.BinaryReader;
|
||||
@ -12,26 +11,27 @@ public class OpenWindowPacket implements ServerPacket {
|
||||
|
||||
public int windowId;
|
||||
public int windowType;
|
||||
public JsonMessage title = ColoredText.of("");
|
||||
public Component title = Component.text("");
|
||||
|
||||
public OpenWindowPacket() {}
|
||||
public OpenWindowPacket() {
|
||||
}
|
||||
|
||||
public OpenWindowPacket(String title) {
|
||||
this.title = ColoredText.of(title);
|
||||
public OpenWindowPacket(Component title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull BinaryWriter writer) {
|
||||
writer.writeVarInt(windowId);
|
||||
writer.writeVarInt(windowType);
|
||||
writer.writeJsonMessage(title);
|
||||
writer.writeComponent(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull BinaryReader reader) {
|
||||
windowId = reader.readVarInt();
|
||||
windowType = reader.readVarInt();
|
||||
title = reader.readJsonMessage(Integer.MAX_VALUE);
|
||||
title = reader.readComponent(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -14,7 +14,7 @@ public class SetSlotPacket implements ServerPacket {
|
||||
public ItemStack itemStack;
|
||||
|
||||
public SetSlotPacket() {
|
||||
itemStack = ItemStack.getAirItem();
|
||||
itemStack = ItemStack.AIR;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,7 +96,7 @@ public class TradeListPacket implements ServerPacket {
|
||||
result = reader.readItemStack();
|
||||
|
||||
boolean hasSecondItem = reader.readBoolean();
|
||||
if(hasSecondItem) {
|
||||
if (hasSecondItem) {
|
||||
inputItem2 = reader.readItemStack();
|
||||
} else {
|
||||
inputItem2 = null;
|
||||
|
@ -17,11 +17,11 @@ public abstract class ShapedRecipe extends Recipe {
|
||||
private ItemStack result;
|
||||
|
||||
protected ShapedRecipe(@NotNull String recipeId,
|
||||
int width,
|
||||
int height,
|
||||
@NotNull String group,
|
||||
@Nullable List<DeclareRecipesPacket.Ingredient> ingredients,
|
||||
@NotNull ItemStack result) {
|
||||
int width,
|
||||
int height,
|
||||
@NotNull String group,
|
||||
@Nullable List<DeclareRecipesPacket.Ingredient> ingredients,
|
||||
@NotNull ItemStack result) {
|
||||
super(RecipeType.SHAPED, recipeId);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
@ -38,7 +38,7 @@ public abstract class ThreadProvider {
|
||||
|
||||
{
|
||||
// Default thread count in the pool (cores * 2)
|
||||
setThreadCount(NettyRuntime.availableProcessors() * 2);
|
||||
setThreadCount(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,22 +5,18 @@ import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.util.Codec;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.adventure.AdventureSerializer;
|
||||
import net.minestom.server.attribute.Attribute;
|
||||
import net.minestom.server.attribute.AttributeOperation;
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.data.DataType;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.item.Enchantment;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.item.attribute.AttributeSlot;
|
||||
import net.minestom.server.item.attribute.ItemAttribute;
|
||||
import net.minestom.server.item.metadata.ItemMeta;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import net.minestom.server.utils.binary.BinaryReader;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -48,6 +44,7 @@ public final class NBTUtils {
|
||||
|
||||
/**
|
||||
* Turns an {@link NBTCompound} into an Adventure {@link BinaryTagHolder}.
|
||||
*
|
||||
* @param tag the tag, if any
|
||||
* @return the binary tag holder, or {@code null} if the tag was null
|
||||
*/
|
||||
@ -69,15 +66,17 @@ public final class NBTUtils {
|
||||
public static void loadAllItems(@NotNull NBTList<NBTCompound> items, @NotNull Inventory destination) {
|
||||
destination.clear();
|
||||
for (NBTCompound tag : items) {
|
||||
Material item = Registries.getMaterial(tag.getString("id"));
|
||||
if (item == Material.AIR) {
|
||||
item = Material.STONE;
|
||||
Material material = Registries.getMaterial(tag.getString("id"));
|
||||
if (material == Material.AIR) {
|
||||
material = Material.STONE;
|
||||
}
|
||||
ItemStack stack = new ItemStack(item, tag.getByte("Count"));
|
||||
byte count = tag.getByte("Count");
|
||||
NBTCompound nbtCompound = null;
|
||||
if (tag.containsKey("tag")) {
|
||||
loadDataIntoItem(stack, tag.getCompound("tag"));
|
||||
nbtCompound = tag.getCompound("tag");
|
||||
}
|
||||
destination.setItemStack(tag.getByte("Slot"), stack);
|
||||
ItemStack itemStack = loadItem(material, count, nbtCompound);
|
||||
destination.setItemStack(tag.getByte("Slot"), itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,12 +85,11 @@ public final class NBTUtils {
|
||||
final ItemStack stack = inventory.getItemStack(i);
|
||||
NBTCompound nbt = new NBTCompound();
|
||||
|
||||
NBTCompound tag = new NBTCompound();
|
||||
saveDataIntoNBT(stack, tag);
|
||||
NBTCompound tag = stack.getMeta().toNBT();
|
||||
|
||||
nbt.set("tag", tag);
|
||||
nbt.setByte("Slot", (byte) i);
|
||||
nbt.setByte("Count", stack.getAmount());
|
||||
nbt.setByte("Count", (byte) stack.getAmount());
|
||||
nbt.setString("id", stack.getMaterial().getName());
|
||||
|
||||
list.add(nbt);
|
||||
@ -113,48 +111,60 @@ public final class NBTUtils {
|
||||
nbt.set(listName, enchantList);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
public static ItemStack readItemStack(@NotNull BinaryReader reader) {
|
||||
final boolean present = reader.readBoolean();
|
||||
|
||||
if (!present) {
|
||||
return ItemStack.getAirItem();
|
||||
return ItemStack.AIR;
|
||||
}
|
||||
|
||||
final int id = reader.readVarInt();
|
||||
if (id == -1) {
|
||||
// Drop mode
|
||||
return ItemStack.getAirItem();
|
||||
return ItemStack.AIR;
|
||||
}
|
||||
|
||||
final Material material = Material.fromId((short) id);
|
||||
final byte count = reader.readByte();
|
||||
ItemStack item = new ItemStack(material, count);
|
||||
NBTCompound nbtCompound = null;
|
||||
|
||||
try {
|
||||
final NBT itemNBT = reader.readTag();
|
||||
if (itemNBT instanceof NBTCompound) { // can also be a TAG_End if no data
|
||||
NBTCompound nbt = (NBTCompound) itemNBT;
|
||||
loadDataIntoItem(item, nbt);
|
||||
nbtCompound = (NBTCompound) itemNBT;
|
||||
}
|
||||
} catch (IOException | NBTException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
}
|
||||
|
||||
return item;
|
||||
return loadItem(material, count, nbtCompound);
|
||||
}
|
||||
|
||||
public static @NotNull ItemStack loadItem(@NotNull Material material, int count, @Nullable NBTCompound nbtCompound) {
|
||||
return ItemStack.builder(material)
|
||||
.amount(count)
|
||||
.meta(metaBuilder -> {
|
||||
if (nbtCompound != null) {
|
||||
return ItemMetaBuilder.fromNBT(metaBuilder, nbtCompound);
|
||||
} else {
|
||||
return metaBuilder;
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static void loadDataIntoItem(@NotNull ItemStack item, @NotNull NBTCompound nbt) {
|
||||
if (nbt.containsKey("Damage")) item.setDamage(nbt.getInt("Damage"));
|
||||
if (nbt.containsKey("Unbreakable")) item.setUnbreakable(nbt.getAsByte("Unbreakable") == 1);
|
||||
if (nbt.containsKey("HideFlags")) item.setHideFlag(nbt.getInt("HideFlags"));
|
||||
public static void loadDataIntoMeta(@NotNull ItemMetaBuilder metaBuilder, @NotNull NBTCompound nbt) {
|
||||
if (nbt.containsKey("Damage")) metaBuilder.damage(nbt.getInt("Damage"));
|
||||
if (nbt.containsKey("Unbreakable")) metaBuilder.unbreakable(nbt.getAsByte("Unbreakable") == 1);
|
||||
if (nbt.containsKey("HideFlags")) metaBuilder.hideFlag(nbt.getInt("HideFlags"));
|
||||
if (nbt.containsKey("display")) {
|
||||
final NBTCompound display = nbt.getCompound("display");
|
||||
if (display.containsKey("Name")) {
|
||||
final String rawName = display.getString("Name");
|
||||
final Component displayName = GsonComponentSerializer.gson().deserialize(rawName);
|
||||
item.setDisplayName(displayName);
|
||||
metaBuilder.displayName(displayName);
|
||||
}
|
||||
if (display.containsKey("Lore")) {
|
||||
NBTList<NBTString> loreList = display.getList("Lore");
|
||||
@ -162,19 +172,20 @@ public final class NBTUtils {
|
||||
for (NBTString s : loreList) {
|
||||
lore.add(GsonComponentSerializer.gson().deserialize(s.getValue()));
|
||||
}
|
||||
item.setLore(lore);
|
||||
metaBuilder.lore(lore);
|
||||
}
|
||||
}
|
||||
|
||||
// Enchantments
|
||||
if (nbt.containsKey("Enchantments")) {
|
||||
loadEnchantments(nbt.getList("Enchantments"), item::setEnchantment);
|
||||
loadEnchantments(nbt.getList("Enchantments"), metaBuilder::enchantment);
|
||||
}
|
||||
|
||||
// Attributes
|
||||
if (nbt.containsKey("AttributeModifiers")) {
|
||||
NBTList<NBTCompound> attributes = nbt.getList("AttributeModifiers");
|
||||
for (NBTCompound attributeNBT : attributes) {
|
||||
List<ItemAttribute> attributes = new ArrayList<>();
|
||||
NBTList<NBTCompound> nbtAttributes = nbt.getList("AttributeModifiers");
|
||||
for (NBTCompound attributeNBT : nbtAttributes) {
|
||||
final UUID uuid;
|
||||
{
|
||||
final int[] uuidArray = attributeNBT.getIntArray("UUID");
|
||||
@ -208,54 +219,43 @@ public final class NBTUtils {
|
||||
// Add attribute
|
||||
final ItemAttribute itemAttribute =
|
||||
new ItemAttribute(uuid, name, attribute, attributeOperation, value, attributeSlot);
|
||||
item.addAttribute(itemAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
// Hide flags
|
||||
{
|
||||
if (nbt.containsKey("HideFlags")) {
|
||||
item.setHideFlag(nbt.getInt("HideFlags"));
|
||||
attributes.add(itemAttribute);
|
||||
}
|
||||
metaBuilder.attributes(attributes);
|
||||
}
|
||||
|
||||
// Custom model data
|
||||
{
|
||||
if (nbt.containsKey("CustomModelData")) {
|
||||
item.setCustomModelData(nbt.getInt("CustomModelData"));
|
||||
metaBuilder.customModelData(nbt.getInt("CustomModelData"));
|
||||
}
|
||||
}
|
||||
|
||||
// Meta specific field
|
||||
final ItemMeta itemMeta = item.getItemMeta();
|
||||
if (itemMeta != null) {
|
||||
itemMeta.read(nbt);
|
||||
}
|
||||
// Meta specific fields
|
||||
metaBuilder.read(nbt);
|
||||
|
||||
// Ownership
|
||||
{
|
||||
if (nbt.containsKey(ItemStack.OWNERSHIP_DATA_KEY)) {
|
||||
final String identifierString = nbt.getString(ItemStack.OWNERSHIP_DATA_KEY);
|
||||
final UUID identifier = UUID.fromString(identifierString);
|
||||
final Data data = ItemStack.DATA_OWNERSHIP.getOwnObject(identifier);
|
||||
if (data != null) {
|
||||
item.setData(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//CanPlaceOn
|
||||
// CanPlaceOn
|
||||
{
|
||||
if (nbt.containsKey("CanPlaceOn")) {
|
||||
NBTList<NBTString> canPlaceOn = nbt.getList("CanPlaceOn");
|
||||
canPlaceOn.forEach(x -> item.getCanPlaceOn().add(x.getValue()));
|
||||
Set<Block> blocks = new HashSet<>();
|
||||
for (NBTString blockNamespace : canPlaceOn) {
|
||||
Block block = Registries.getBlock(blockNamespace.getValue());
|
||||
blocks.add(block);
|
||||
}
|
||||
metaBuilder.canPlaceOn(blocks);
|
||||
}
|
||||
}
|
||||
//CanDestroy
|
||||
// CanDestroy
|
||||
{
|
||||
if (nbt.containsKey("CanDestroy")) {
|
||||
NBTList<NBTString> canPlaceOn = nbt.getList("CanDestroy");
|
||||
canPlaceOn.forEach(x -> item.getCanDestroy().add(x.getValue()));
|
||||
NBTList<NBTString> canDestroy = nbt.getList("CanDestroy");
|
||||
Set<Block> blocks = new HashSet<>();
|
||||
for (NBTString blockNamespace : canDestroy) {
|
||||
Block block = Registries.getBlock(blockNamespace.getValue());
|
||||
blocks.add(block);
|
||||
}
|
||||
metaBuilder.canDestroy(blocks);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -273,243 +273,6 @@ public final class NBTUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeItemStack(BinaryWriter packet, ItemStack itemStack) {
|
||||
if (itemStack == null || itemStack.isAir()) {
|
||||
packet.writeBoolean(false);
|
||||
} else {
|
||||
packet.writeBoolean(true);
|
||||
packet.writeVarInt(itemStack.getMaterial().getId());
|
||||
packet.writeByte(itemStack.getAmount());
|
||||
|
||||
if (!itemStack.hasNbtTag()) {
|
||||
packet.writeByte((byte) NBTTypes.TAG_End); // No nbt
|
||||
return;
|
||||
}
|
||||
|
||||
NBTCompound itemNBT = new NBTCompound();
|
||||
|
||||
// Vanilla compound
|
||||
saveDataIntoNBT(itemStack, itemNBT);
|
||||
|
||||
// End custom model data
|
||||
packet.writeNBT("", itemNBT);
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveDataIntoNBT(@NotNull ItemStack itemStack, @NotNull NBTCompound itemNBT) {
|
||||
// Unbreakable
|
||||
if (itemStack.isUnbreakable()) {
|
||||
itemNBT.setInt("Unbreakable", 1);
|
||||
}
|
||||
|
||||
// Start damage
|
||||
{
|
||||
final int damage = itemStack.getDamage();
|
||||
if (damage > 0) {
|
||||
itemNBT.setInt("Damage", damage);
|
||||
}
|
||||
}
|
||||
// End damage
|
||||
|
||||
// Display
|
||||
final boolean hasDisplayName = itemStack.hasDisplayName();
|
||||
final boolean hasLore = itemStack.hasLore();
|
||||
|
||||
if (hasDisplayName || hasLore) {
|
||||
NBTCompound displayNBT = new NBTCompound();
|
||||
if (hasDisplayName) {
|
||||
final String name = AdventureSerializer.serialize(itemStack.getDisplayName());
|
||||
displayNBT.setString("Name", name);
|
||||
}
|
||||
|
||||
if (hasLore) {
|
||||
final List<Component> lore = itemStack.getLore();
|
||||
|
||||
final NBTList<NBTString> loreNBT = new NBTList<>(NBTTypes.TAG_String);
|
||||
for (Component line : lore) {
|
||||
loreNBT.add(new NBTString(GsonComponentSerializer.gson().serialize(line)));
|
||||
}
|
||||
displayNBT.set("Lore", loreNBT);
|
||||
}
|
||||
|
||||
itemNBT.set("display", displayNBT);
|
||||
}
|
||||
// End display
|
||||
|
||||
// Start enchantment
|
||||
{
|
||||
final Map<Enchantment, Short> enchantmentMap = itemStack.getEnchantmentMap();
|
||||
if (!enchantmentMap.isEmpty()) {
|
||||
writeEnchant(itemNBT, "Enchantments", enchantmentMap);
|
||||
}
|
||||
}
|
||||
// End enchantment
|
||||
|
||||
// Start attribute
|
||||
{
|
||||
final List<ItemAttribute> itemAttributes = itemStack.getAttributes();
|
||||
if (!itemAttributes.isEmpty()) {
|
||||
NBTList<NBTCompound> attributesNBT = new NBTList<>(NBTTypes.TAG_Compound);
|
||||
|
||||
for (ItemAttribute itemAttribute : itemAttributes) {
|
||||
final UUID uuid = itemAttribute.getUuid();
|
||||
attributesNBT.add(
|
||||
new NBTCompound()
|
||||
.setIntArray("UUID", Utils.uuidToIntArray(uuid))
|
||||
.setDouble("Amount", itemAttribute.getValue())
|
||||
.setString("Slot", itemAttribute.getSlot().name().toLowerCase())
|
||||
.setString("AttributeName", itemAttribute.getAttribute().getKey())
|
||||
.setInt("Operation", itemAttribute.getOperation().getId())
|
||||
.setString("Name", itemAttribute.getInternalName())
|
||||
);
|
||||
}
|
||||
itemNBT.set("AttributeModifiers", attributesNBT);
|
||||
}
|
||||
}
|
||||
// End attribute
|
||||
|
||||
// Start hide flags
|
||||
{
|
||||
final int hideFlag = itemStack.getHideFlag();
|
||||
if (hideFlag != 0) {
|
||||
itemNBT.setInt("HideFlags", hideFlag);
|
||||
}
|
||||
}
|
||||
// End hide flags
|
||||
|
||||
// Start custom model data
|
||||
{
|
||||
final int customModelData = itemStack.getCustomModelData();
|
||||
if (customModelData != 0) {
|
||||
itemNBT.setInt("CustomModelData", customModelData);
|
||||
}
|
||||
}
|
||||
// End custom model data
|
||||
|
||||
// Start custom meta
|
||||
{
|
||||
final ItemMeta itemMeta = itemStack.getItemMeta();
|
||||
if (itemMeta != null) {
|
||||
itemMeta.write(itemNBT);
|
||||
}
|
||||
}
|
||||
// End custom meta
|
||||
|
||||
// Start ownership
|
||||
{
|
||||
final Data data = itemStack.getData();
|
||||
if (data != null && !data.isEmpty()) {
|
||||
final UUID identifier = itemStack.getIdentifier();
|
||||
itemNBT.setString(ItemStack.OWNERSHIP_DATA_KEY, identifier.toString());
|
||||
}
|
||||
}
|
||||
// End ownership
|
||||
|
||||
//CanDestroy
|
||||
{
|
||||
Set<String> canDestroy = itemStack.getCanDestroy();
|
||||
if (canDestroy.size() > 0) {
|
||||
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
|
||||
canDestroy.forEach(x -> list.add(new NBTString(x)));
|
||||
itemNBT.set("CanDestroy", list);
|
||||
}
|
||||
}
|
||||
|
||||
//CanDestroy
|
||||
{
|
||||
Set<String> canPlaceOn = itemStack.getCanPlaceOn();
|
||||
if (canPlaceOn.size() > 0) {
|
||||
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
|
||||
canPlaceOn.forEach(x -> list.add(new NBTString(x)));
|
||||
itemNBT.set("CanPlaceOn", list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object into its {@link NBT} equivalent.
|
||||
* <p>
|
||||
* If {@code type} is not a primitive type or primitive array and {@code supportDataType} is true,
|
||||
* the data will be encoded with the appropriate {@link DataType} into a byte array.
|
||||
*
|
||||
* @param value the value to convert
|
||||
* @param type the type of the value, used to know which {@link DataType} to use if {@code value} is not a primitive type
|
||||
* @param supportDataType true to allow using a {@link DataType} to encode {@code value} into a byte array if not a primitive type
|
||||
* @return the converted value, null if {@code type} is not a primitive type and {@code supportDataType} is false
|
||||
*/
|
||||
@Nullable
|
||||
public static NBT toNBT(@NotNull Object value, @NotNull Class type, boolean supportDataType) {
|
||||
type = PrimitiveConversion.getObjectClass(type);
|
||||
if (type.equals(Boolean.class)) {
|
||||
// No boolean type in NBT
|
||||
return new NBTByte((byte) (((boolean) value) ? 1 : 0));
|
||||
} else if (type.equals(Byte.class)) {
|
||||
return new NBTByte((byte) value);
|
||||
} else if (type.equals(Character.class)) {
|
||||
// No char type in NBT
|
||||
return new NBTShort((short) value);
|
||||
} else if (type.equals(Short.class)) {
|
||||
return new NBTShort((short) value);
|
||||
} else if (type.equals(Integer.class)) {
|
||||
return new NBTInt((int) value);
|
||||
} else if (type.equals(Long.class)) {
|
||||
return new NBTLong((long) value);
|
||||
} else if (type.equals(Float.class)) {
|
||||
return new NBTFloat((float) value);
|
||||
} else if (type.equals(Double.class)) {
|
||||
return new NBTDouble((double) value);
|
||||
} else if (type.equals(String.class)) {
|
||||
return new NBTString((String) value);
|
||||
} else if (type.equals(Byte[].class)) {
|
||||
return new NBTByteArray((byte[]) value);
|
||||
} else if (type.equals(Integer[].class)) {
|
||||
return new NBTIntArray((int[]) value);
|
||||
} else if (type.equals(Long[].class)) {
|
||||
return new NBTLongArray((long[]) value);
|
||||
} else {
|
||||
if (supportDataType) {
|
||||
// Custom NBT type, try to encode using the data manager
|
||||
DataType dataType = MinecraftServer.getDataManager().getDataType(type);
|
||||
Check.notNull(dataType, "The type '" + type + "' is not registered in DataManager and not a primitive type.");
|
||||
|
||||
BinaryWriter writer = new BinaryWriter();
|
||||
dataType.encode(writer, value);
|
||||
|
||||
final byte[] encodedValue = writer.toByteArray();
|
||||
|
||||
return new NBTByteArray(encodedValue);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a nbt object to its raw value.
|
||||
* <p>
|
||||
* Currently support number, string, byte/int/long array.
|
||||
*
|
||||
* @param nbt the nbt tag to convert
|
||||
* @return the value representation of a tag
|
||||
* @throws UnsupportedOperationException if the tag type is not supported
|
||||
*/
|
||||
@NotNull
|
||||
public static Object fromNBT(@NotNull NBT nbt) {
|
||||
if (nbt instanceof NBTNumber) {
|
||||
return ((NBTNumber) nbt).getValue();
|
||||
} else if (nbt instanceof NBTString) {
|
||||
return ((NBTString) nbt).getValue();
|
||||
} else if (nbt instanceof NBTByteArray) {
|
||||
return ((NBTByteArray) nbt).getValue();
|
||||
} else if (nbt instanceof NBTIntArray) {
|
||||
return ((NBTIntArray) nbt).getValue();
|
||||
} else if (nbt instanceof NBTLongArray) {
|
||||
return ((NBTLongArray) nbt).getValue();
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("NBT type " + nbt.getClass() + " is not handled properly.");
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface EnchantmentSetter {
|
||||
void applyEnchantment(Enchantment name, short level);
|
||||
|
@ -9,7 +9,6 @@ import net.minestom.server.adventure.AdventureSerializer;
|
||||
import net.minestom.server.chat.JsonMessage;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.NBTUtils;
|
||||
import net.minestom.server.utils.SerializerUtils;
|
||||
import net.minestom.server.utils.Utils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -264,7 +263,14 @@ public class BinaryWriter extends OutputStream {
|
||||
}
|
||||
|
||||
public void writeItemStack(@NotNull ItemStack itemStack) {
|
||||
NBTUtils.writeItemStack(this, itemStack);
|
||||
if (itemStack.isAir()) {
|
||||
writeBoolean(false);
|
||||
} else {
|
||||
writeBoolean(true);
|
||||
writeVarInt(itemStack.getMaterial().getId());
|
||||
writeByte((byte) itemStack.getAmount());
|
||||
write(itemStack.getMeta());
|
||||
}
|
||||
}
|
||||
|
||||
public void writeNBT(@NotNull String name, @NotNull NBT tag) {
|
||||
|
@ -1,59 +0,0 @@
|
||||
package net.minestom.server.utils.ownership;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Convenient class to keep trace of objects linked to an {@link UUID}.
|
||||
*
|
||||
* @param <T> the owned object type
|
||||
*/
|
||||
public class OwnershipHandler<T> {
|
||||
|
||||
// identifier = the object having an ownership
|
||||
private ConcurrentHashMap<UUID, T> ownershipDataMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Generates a new unique identifier.
|
||||
* <p>
|
||||
* Does call {@link UUID#randomUUID()} internally.
|
||||
*
|
||||
* @return a new generated identifier
|
||||
*/
|
||||
public UUID generateIdentifier() {
|
||||
return UUID.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the owned object based on its identifier.
|
||||
*
|
||||
* @param identifier the object identifier
|
||||
* @return the own object, null if not found
|
||||
*/
|
||||
@Nullable
|
||||
public T getOwnObject(@NotNull UUID identifier) {
|
||||
return ownershipDataMap.get(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves, replace or remove the own object of an identifier.
|
||||
*
|
||||
* @param identifier the identifier of the object
|
||||
* @param value the value of the object, can override the previous value, null means removing the identifier
|
||||
*/
|
||||
public void saveOwnObject(@NotNull UUID identifier, @Nullable T value) {
|
||||
if (value != null) {
|
||||
this.ownershipDataMap.put(identifier, value);
|
||||
} else {
|
||||
clearCache(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearCache(@NotNull UUID identifier) {
|
||||
this.ownershipDataMap.remove(identifier);
|
||||
}
|
||||
|
||||
}
|
@ -100,7 +100,7 @@ public class BiomeParticles {
|
||||
@Override
|
||||
public NBTCompound toNbt() {
|
||||
//todo test count might be wrong type
|
||||
NBTCompound nbtCompound = item.toNBT();
|
||||
NBTCompound nbtCompound = item.getMeta().toNBT();
|
||||
nbtCompound.setString("type", type);
|
||||
return nbtCompound;
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ public class Main {
|
||||
commandManager.register(new EchoCommand());
|
||||
commandManager.register(new SummonCommand());
|
||||
commandManager.register(new RemoveCommand());
|
||||
commandManager.register(new GiveCommand());
|
||||
|
||||
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage(Component.text("Unknown command", NamedTextColor.RED)));
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package demo;
|
||||
|
||||
import com.google.common.util.concurrent.AtomicDouble;
|
||||
import demo.generator.ChunkGeneratorDemo;
|
||||
import demo.generator.NoiseTestGenerator;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@ -27,18 +26,20 @@ import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.InventoryType;
|
||||
import net.minestom.server.inventory.PlayerInventory;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.ItemTag;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.item.metadata.CompassMeta;
|
||||
import net.minestom.server.monitoring.BenchmarkManager;
|
||||
import net.minestom.server.network.ConnectionManager;
|
||||
import net.minestom.server.ping.ResponseDataConsumer;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import net.minestom.server.utils.Position;
|
||||
import net.minestom.server.utils.Vector;
|
||||
import net.minestom.server.utils.inventory.PlayerInventoryUtils;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import net.minestom.server.world.DimensionType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@ -57,24 +58,40 @@ public class PlayerInit {
|
||||
instanceContainer.enableAutoChunkLoad(true);
|
||||
instanceContainer.setChunkGenerator(chunkGeneratorDemo);
|
||||
|
||||
inventory = new Inventory(InventoryType.CHEST_1_ROW, "Test inventory");
|
||||
inventory = new Inventory(InventoryType.CHEST_1_ROW, Component.text("Test inventory"));
|
||||
/*inventory.addInventoryCondition((p, slot, clickType, inventoryConditionResult) -> {
|
||||
p.sendMessage("click type inventory: " + clickType);
|
||||
System.out.println("slot inv: " + slot)0;
|
||||
inventoryConditionResult.setCancel(slot == 3);
|
||||
});*/
|
||||
//inventory.setItemStack(3, new ItemStack(Material.DIAMOND, (byte) 34));
|
||||
}
|
||||
inventory.setItemStack(3, ItemStack.of(Material.DIAMOND, 34));
|
||||
|
||||
private static final AtomicDouble LAST_TICK_TIME = new AtomicDouble();
|
||||
{
|
||||
CompassMeta compassMeta = new CompassMeta.Builder()
|
||||
.lodestonePosition(new Position(0, 0, 0))
|
||||
.build();
|
||||
|
||||
ItemStack itemStack = ItemStack.builder(Material.COMPASS)
|
||||
.meta(CompassMeta.class, builder -> {
|
||||
builder.lodestonePosition(new Position(0, 0, 0));
|
||||
builder.set(ItemTag.Integer("int"), 25);
|
||||
})
|
||||
.build();
|
||||
|
||||
itemStack = itemStack.with(itemBuilder -> itemBuilder
|
||||
.amount(10)
|
||||
.meta(CompassMeta.class, builder -> {
|
||||
builder.lodestonePosition(new Position(5, 0, 0));
|
||||
})
|
||||
.lore(Component.text("Lore")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
ConnectionManager connectionManager = MinecraftServer.getConnectionManager();
|
||||
BenchmarkManager benchmarkManager = MinecraftServer.getBenchmarkManager();
|
||||
|
||||
MinecraftServer.getUpdateManager().addTickMonitor(tickMonitor ->
|
||||
LAST_TICK_TIME.set(tickMonitor.getTickTime()));
|
||||
|
||||
MinecraftServer.getSchedulerManager().buildTask(() -> {
|
||||
|
||||
Collection<Player> players = connectionManager.getOnlinePlayers();
|
||||
@ -85,9 +102,7 @@ public class PlayerInit {
|
||||
long ramUsage = benchmarkManager.getUsedMemory();
|
||||
ramUsage /= 1e6; // bytes to MB
|
||||
|
||||
final Component header = Component.text("RAM USAGE: " + ramUsage + " MB")
|
||||
.append(Component.newline())
|
||||
.append(Component.text("TICK TIME: " + MathUtils.round(LAST_TICK_TIME.get(), 2) + "ms"));
|
||||
final Component header = Component.text("RAM USAGE: " + ramUsage + " MB");
|
||||
final Component footer = benchmarkManager.getCpuMonitoringMessage();
|
||||
Audiences.players().sendPlayerListHeaderAndFooter(header, footer);
|
||||
|
||||
@ -189,6 +204,7 @@ public class PlayerInit {
|
||||
|
||||
globalEventHandler.addEventCallback(PlayerLoginEvent.class, event -> {
|
||||
final Player player = event.getPlayer();
|
||||
player.sendMessage("test");
|
||||
|
||||
event.setSpawningInstance(instanceContainer);
|
||||
int x = Math.abs(ThreadLocalRandom.current().nextInt()) % 1000 - 250;
|
||||
@ -210,19 +226,31 @@ public class PlayerInit {
|
||||
player.setPermissionLevel(4);
|
||||
|
||||
PlayerInventory inventory = player.getInventory();
|
||||
ItemStack itemStack = new ItemStack(Material.STONE, (byte) 64);
|
||||
ItemStack itemStack = ItemStack.builder(Material.STONE)
|
||||
.amount(64)
|
||||
.meta(itemMetaBuilder ->
|
||||
itemMetaBuilder.canPlaceOn(Set.of(Block.STONE))
|
||||
.canDestroy(Set.of(Block.DIAMOND_ORE)))
|
||||
.build();
|
||||
|
||||
System.out.println(itemStack.getMeta().toSNBT());
|
||||
|
||||
//itemStack = itemStack.withStore(storeBuilder -> storeBuilder.set("key2", 25, Integer::sum));
|
||||
|
||||
inventory.addItemStack(itemStack);
|
||||
|
||||
{
|
||||
ItemStack item = new ItemStack(Material.DIAMOND_CHESTPLATE, (byte) 1);
|
||||
inventory.setChestplate(item);
|
||||
item.setDisplayName(ColoredText.of("test"));
|
||||
ItemStack item = ItemStack.builder(Material.DIAMOND_CHESTPLATE)
|
||||
.displayName(Component.text("test"))
|
||||
.lore(Component.text("lore"))
|
||||
.build();
|
||||
|
||||
inventory.refreshSlot((short) PlayerInventoryUtils.CHESTPLATE_SLOT);
|
||||
//inventory.setChestplate(item);
|
||||
|
||||
inventory.setChestplate(item.with(itemStackBuilder -> {
|
||||
itemStackBuilder.lore(Collections.emptyList());
|
||||
}));
|
||||
}
|
||||
|
||||
//player.getInventory().addItemStack(new ItemStack(Material.STONE, (byte) 32));
|
||||
});
|
||||
|
||||
globalEventHandler.addEventCallback(PlayerBlockBreakEvent.class, event -> {
|
||||
|
58
src/test/java/demo/commands/GiveCommand.java
Normal file
58
src/test/java/demo/commands/GiveCommand.java
Normal file
@ -0,0 +1,58 @@
|
||||
package demo.commands;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.inventory.PlayerInventory;
|
||||
import net.minestom.server.inventory.TransactionOption;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.utils.entity.EntityFinder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.Integer;
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.*;
|
||||
|
||||
public class GiveCommand extends Command {
|
||||
public GiveCommand() {
|
||||
super("give");
|
||||
|
||||
setDefaultExecutor((sender, context) ->
|
||||
sender.sendMessage(Component.text("Usage: /give <target> <item> [<count>]")));
|
||||
|
||||
addSyntax((sender, context) -> {
|
||||
final EntityFinder entityFinder = context.get("target");
|
||||
int count = context.get("count");
|
||||
count = Math.min(count, PlayerInventory.INVENTORY_SIZE * 64);
|
||||
ItemStack itemStack = context.get("item");
|
||||
|
||||
List<ItemStack> itemStacks;
|
||||
if (count <= 64) {
|
||||
itemStack = itemStack.withAmount(count);
|
||||
itemStacks = Collections.singletonList(itemStack);
|
||||
} else {
|
||||
itemStacks = new ArrayList<>();
|
||||
while (count > 64) {
|
||||
itemStacks.add(itemStack.withAmount(64));
|
||||
count -= 64;
|
||||
}
|
||||
itemStacks.add(itemStack.withAmount(count));
|
||||
}
|
||||
|
||||
final List<Entity> targets = entityFinder.find(sender);
|
||||
for (Entity target : targets) {
|
||||
if (target instanceof Player) {
|
||||
Player player = (Player) target;
|
||||
player.getInventory().addItemStacks(itemStacks, TransactionOption.ALL);
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMessage(Component.text("Items have been given successfully!"));
|
||||
|
||||
}, Entity("target").onlyPlayers(true), ItemStack("item"), Integer("count").setDefaultValue(() -> 1));
|
||||
|
||||
}
|
||||
}
|
@ -47,13 +47,13 @@ public class ReadWritePackets {
|
||||
private <T extends Readable & Writeable> Collection<DynamicTest> checkImplementationPresence(Class<T> packetClass) throws IOException {
|
||||
ClassPath cp = ClassPath.from(ClassLoader.getSystemClassLoader());
|
||||
List<DynamicTest> allTests = new LinkedList<>();
|
||||
for(ClassPath.ClassInfo classInfo : cp.getAllClasses()) {
|
||||
if(!classInfo.getPackageName().startsWith("net.minestom.server.network.packet"))
|
||||
for (ClassPath.ClassInfo classInfo : cp.getAllClasses()) {
|
||||
if (!classInfo.getPackageName().startsWith("net.minestom.server.network.packet"))
|
||||
continue;
|
||||
try {
|
||||
Class<?> clazz = classInfo.load();
|
||||
if(packetClass.isAssignableFrom(clazz) && !clazz.isInterface() && ((clazz.getModifiers() & Modifier.ABSTRACT) != Modifier.ABSTRACT)) {
|
||||
allTests.add(DynamicTest.dynamicTest("WriteThenRead "+clazz.getSimpleName(), () -> {
|
||||
if (packetClass.isAssignableFrom(clazz) && !clazz.isInterface() && ((clazz.getModifiers() & Modifier.ABSTRACT) != Modifier.ABSTRACT)) {
|
||||
allTests.add(DynamicTest.dynamicTest("WriteThenRead " + clazz.getSimpleName(), () -> {
|
||||
// required for managers to be loaded
|
||||
MinecraftServer.init();
|
||||
|
||||
@ -63,14 +63,13 @@ public class ReadWritePackets {
|
||||
T packet;
|
||||
|
||||
// exceptions
|
||||
if(clazz.getSimpleName().equals("EntityEquipmentPacket")) {
|
||||
if (clazz.getSimpleName().equals("EntityEquipmentPacket")) {
|
||||
// requires at least one slot and one item
|
||||
EntityEquipmentPacket p = new EntityEquipmentPacket();
|
||||
p.itemStacks = new ItemStack[] { ItemStack.getAirItem() };
|
||||
p.slots = new EntityEquipmentPacket.Slot[] { EntityEquipmentPacket.Slot.MAIN_HAND };
|
||||
p.itemStacks = new ItemStack[]{ItemStack.AIR};
|
||||
p.slots = new EntityEquipmentPacket.Slot[]{EntityEquipmentPacket.Slot.MAIN_HAND};
|
||||
packet = (T) p;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
packet = (T) constructor.newInstance();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user